1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: hostip.c,v 1.2 2007/03/15 19:22:13 andy Exp $
22 ***************************************************************************/
31 #ifdef HAVE_SYS_TYPES_H
32 #include <sys/types.h>
34 #ifdef HAVE_SYS_SOCKET_H
35 #include <sys/socket.h>
37 #ifdef HAVE_NETINET_IN_H
38 #include <netinet/in.h>
43 #ifdef HAVE_ARPA_INET_H
44 #include <arpa/inet.h>
47 #include <stdlib.h> /* required for free() prototypes */
50 #include <unistd.h> /* for the close() proto */
73 #include "inet_ntop.h"
75 #define _MPRINTF_REPLACE /* use our functions only */
76 #include <curl/mprintf.h>
78 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
79 #include "inet_ntoa_r.h"
83 /* The last #include file should be: */
90 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
91 * source file are these:
93 * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
94 * that. The host may not be able to resolve IPv6, but we don't really have to
95 * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
98 * CURLRES_ARES - is defined if libcurl is built to use c-ares for
99 * asynchronous name resolves. This can be Windows or *nix.
101 * CURLRES_THREADED - is defined if libcurl is built to run under (native)
102 * Windows, and then the name resolve will be done in a new thread, and the
103 * supported API will be the same as for ares-builds.
105 * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
106 * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
109 * The host*.c sources files are split up like this:
111 * hostip.c - method-independent resolver functions and utility functions
112 * hostasyn.c - functions for asynchronous name resolves
113 * hostsyn.c - functions for synchronous name resolves
114 * hostares.c - functions for ares-using name resolves
115 * hostthre.c - functions for threaded name resolves
116 * hostip4.c - ipv4-specific functions
117 * hostip6.c - ipv6-specific functions
119 * The hostip.h is the united header file for all this. It defines the
120 * CURLRES_* defines based on the config*.h and setup.h defines.
123 /* These two symbols are for the global DNS cache */
124 static struct curl_hash hostname_cache
;
125 static int host_cache_initialized
;
127 static void freednsentry(void *freethis
);
130 * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
131 * Global DNS cache is general badness. Do not use. This will be removed in
132 * a future version. Use the share interface instead!
134 void Curl_global_host_cache_init(void)
136 if (!host_cache_initialized
) {
137 Curl_hash_init(&hostname_cache
, 7, freednsentry
);
138 host_cache_initialized
= 1;
143 * Return a pointer to the global cache
145 struct curl_hash
*Curl_global_host_cache_get(void)
147 return &hostname_cache
;
151 * Destroy and cleanup the global DNS cache
153 void Curl_global_host_cache_dtor(void)
155 if (host_cache_initialized
) {
156 Curl_hash_clean(&hostname_cache
);
157 host_cache_initialized
= 0;
162 * Return # of adresses in a Curl_addrinfo struct
164 int Curl_num_addresses(const Curl_addrinfo
*addr
)
167 for (i
= 0; addr
; addr
= addr
->ai_next
, i
++)
173 * Curl_printable_address() returns a printable version of the 1st address
174 * given in the 'ip' argument. The result will be stored in the buf that is
177 * If the conversion fails, it returns NULL.
179 const char *Curl_printable_address(const Curl_addrinfo
*ip
,
180 char *buf
, size_t bufsize
)
182 const void *ip4
= &((const struct sockaddr_in
*)ip
->ai_addr
)->sin_addr
;
183 int af
= ip
->ai_family
;
185 const void *ip6
= &((const struct sockaddr_in6
*)ip
->ai_addr
)->sin6_addr
;
187 const void *ip6
= NULL
;
190 return Curl_inet_ntop(af
, af
== AF_INET
? ip4
: ip6
, buf
, bufsize
);
194 * Return a hostcache id string for the providing host + port, to be used by
198 create_hostcache_id(const char *server
, int port
)
200 /* create and return the new allocated entry */
201 return aprintf("%s:%d", server
, port
);
204 struct hostcache_prune_data
{
210 * This function is set as a callback to be called for every entry in the DNS
211 * cache when we want to prune old unused entries.
213 * Returning non-zero means remove the entry, return 0 to keep it in the
217 hostcache_timestamp_remove(void *datap
, void *hc
)
219 struct hostcache_prune_data
*data
=
220 (struct hostcache_prune_data
*) datap
;
221 struct Curl_dns_entry
*c
= (struct Curl_dns_entry
*) hc
;
223 if ((data
->now
- c
->timestamp
< data
->cache_timeout
) ||
225 /* please don't remove */
234 * Prune the DNS cache. This assumes that a lock has already been taken.
237 hostcache_prune(struct curl_hash
*hostcache
, int cache_timeout
, time_t now
)
239 struct hostcache_prune_data user
;
241 user
.cache_timeout
= cache_timeout
;
244 Curl_hash_clean_with_criterium(hostcache
,
246 hostcache_timestamp_remove
);
250 * Library-wide function for pruning the DNS cache. This function takes and
251 * returns the appropriate locks.
253 void Curl_hostcache_prune(struct SessionHandle
*data
)
257 if((data
->set
.dns_cache_timeout
== -1) || !data
->dns
.hostcache
)
258 /* cache forever means never prune, and NULL hostcache means
263 Curl_share_lock(data
, CURL_LOCK_DATA_DNS
, CURL_LOCK_ACCESS_SINGLE
);
267 /* Remove outdated and unused entries from the hostcache */
268 hostcache_prune(data
->dns
.hostcache
,
269 data
->set
.dns_cache_timeout
,
273 Curl_share_unlock(data
, CURL_LOCK_DATA_DNS
);
277 remove_entry_if_stale(struct SessionHandle
*data
, struct Curl_dns_entry
*dns
)
279 struct hostcache_prune_data user
;
281 if( !dns
|| (data
->set
.dns_cache_timeout
== -1) || !data
->dns
.hostcache
)
282 /* cache forever means never prune, and NULL hostcache means
287 user
.cache_timeout
= data
->set
.dns_cache_timeout
;
289 if ( !hostcache_timestamp_remove(&user
,dns
) )
292 /* ok, we do need to clear the cache. although we need to remove just a
293 single entry we clean the entire hash, as no explicit delete function
296 Curl_share_lock(data
, CURL_LOCK_DATA_DNS
, CURL_LOCK_ACCESS_SINGLE
);
298 Curl_hash_clean_with_criterium(data
->dns
.hostcache
,
300 hostcache_timestamp_remove
);
303 Curl_share_unlock(data
, CURL_LOCK_DATA_DNS
);
309 #ifdef HAVE_SIGSETJMP
310 /* Beware this is a global and unique instance. This is used to store the
311 return address that we can jump back to from inside a signal handler. This
312 is not thread-safe stuff. */
313 sigjmp_buf curl_jmpenv
;
318 * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
320 * When calling Curl_resolv() has resulted in a response with a returned
321 * address, we call this function to store the information in the dns
324 * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
326 struct Curl_dns_entry
*
327 Curl_cache_addr(struct SessionHandle
*data
,
329 const char *hostname
,
334 struct Curl_dns_entry
*dns
;
335 struct Curl_dns_entry
*dns2
;
338 /* Create an entry id, based upon the hostname and port */
339 entry_id
= create_hostcache_id(hostname
, port
);
340 /* If we can't create the entry id, fail */
343 entry_len
= strlen(entry_id
);
345 /* Create a new cache entry */
346 dns
= (struct Curl_dns_entry
*) calloc(sizeof(struct Curl_dns_entry
), 1);
352 dns
->inuse
= 0; /* init to not used */
353 dns
->addr
= addr
; /* this is the address(es) */
355 /* Store the resolved data in our DNS cache. This function may return a
356 pointer to an existing struct already present in the hash, and it may
357 return the same argument we pass in. Make no assumptions. */
358 dns2
= Curl_hash_add(data
->dns
.hostcache
, entry_id
, entry_len
+1,
361 /* Major badness, run away. */
369 dns
->timestamp
= now
; /* used now */
370 dns
->inuse
++; /* mark entry as in-use */
372 /* free the allocated entry_id again */
379 * Curl_resolv() is the main name resolve function within libcurl. It resolves
380 * a name and returns a pointer to the entry in the 'entry' argument (if one
381 * is provided). This function might return immediately if we're using asynch
382 * resolves. See the return codes.
384 * The cache entry we return will get its 'inuse' counter increased when this
385 * function is used. You MUST call Curl_resolv_unlock() later (when you're
386 * done using this struct) to decrease the counter again.
390 * CURLRESOLV_ERROR (-1) = error, no pointer
391 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
392 * CURLRESOLV_PENDING (1) = waiting for response, no pointer
395 int Curl_resolv(struct connectdata
*conn
,
396 const char *hostname
,
398 struct Curl_dns_entry
**entry
)
400 char *entry_id
= NULL
;
401 struct Curl_dns_entry
*dns
= NULL
;
404 struct SessionHandle
*data
= conn
->data
;
409 #ifdef HAVE_SIGSETJMP
410 /* this allows us to time-out from the name resolver, as the timeout
411 will generate a signal and we will siglongjmp() from that here */
412 if(!data
->set
.no_signal
) {
413 if (sigsetjmp(curl_jmpenv
, 1)) {
414 /* this is coming from a siglongjmp() */
415 failf(data
, "name lookup timed out");
416 return CURLRESOLV_ERROR
;
421 /* Create an entry id, based upon the hostname and port */
422 entry_id
= create_hostcache_id(hostname
, port
);
423 /* If we can't create the entry id, fail */
425 return CURLRESOLV_ERROR
;
427 entry_len
= strlen(entry_id
);
430 Curl_share_lock(data
, CURL_LOCK_DATA_DNS
, CURL_LOCK_ACCESS_SINGLE
);
432 /* See if its already in our dns cache */
433 dns
= Curl_hash_pick(data
->dns
.hostcache
, entry_id
, entry_len
+1);
436 Curl_share_unlock(data
, CURL_LOCK_DATA_DNS
);
438 /* free the allocated entry_id again */
441 /* See whether the returned entry is stale. Deliberately done after the
443 if ( remove_entry_if_stale(data
,dns
) )
444 dns
= NULL
; /* the memory deallocation is being handled by the hash */
446 rc
= CURLRESOLV_ERROR
; /* default to failure */
449 /* The entry was not in the cache. Resolve it to IP address */
453 /* Check what IP specifics the app has requested and if we can provide it.
454 * If not, bail out. */
455 if(!Curl_ipvalid(data
))
456 return CURLRESOLV_ERROR
;
458 /* If Curl_getaddrinfo() returns NULL, 'wait' might be set to a non-zero
459 value indicating that we need to wait for the response to the resolve
461 addr
= Curl_getaddrinfo(conn
, hostname
, port
, &wait
);
465 /* the response to our resolve call will come asynchronously at
466 a later time, good or bad */
467 /* First, check that we haven't received the info by now */
468 result
= Curl_is_resolved(conn
, &dns
);
469 if(result
) /* error detected */
470 return CURLRESOLV_ERROR
;
472 rc
= CURLRESOLV_RESOLVED
; /* pointer provided */
474 rc
= CURLRESOLV_PENDING
; /* no info yet */
479 Curl_share_lock(data
, CURL_LOCK_DATA_DNS
, CURL_LOCK_ACCESS_SINGLE
);
481 /* we got a response, store it in the cache */
482 dns
= Curl_cache_addr(data
, addr
, hostname
, port
);
485 Curl_share_unlock(data
, CURL_LOCK_DATA_DNS
);
488 /* returned failure, bail out nicely */
489 Curl_freeaddrinfo(addr
);
491 rc
= CURLRESOLV_RESOLVED
;
496 Curl_share_lock(data
, CURL_LOCK_DATA_DNS
, CURL_LOCK_ACCESS_SINGLE
);
497 dns
->inuse
++; /* we use it! */
499 Curl_share_unlock(data
, CURL_LOCK_DATA_DNS
);
500 rc
= CURLRESOLV_RESOLVED
;
509 * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
510 * made, the struct may be destroyed due to pruning. It is important that only
511 * one unlock is made for each Curl_resolv() call.
513 void Curl_resolv_unlock(struct SessionHandle
*data
, struct Curl_dns_entry
*dns
)
515 curlassert(dns
&& (dns
->inuse
>0));
518 Curl_share_lock(data
, CURL_LOCK_DATA_DNS
, CURL_LOCK_ACCESS_SINGLE
);
523 Curl_share_unlock(data
, CURL_LOCK_DATA_DNS
);
527 * File-internal: free a cache dns entry.
529 static void freednsentry(void *freethis
)
531 struct Curl_dns_entry
*p
= (struct Curl_dns_entry
*) freethis
;
533 Curl_freeaddrinfo(p
->addr
);
539 * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
541 struct curl_hash
*Curl_mk_dnscache(void)
543 return Curl_hash_alloc(7, freednsentry
);
546 #ifdef CURLRES_ADDRINFO_COPY
548 /* align on even 64bit boundaries */
549 #define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
552 * Curl_addrinfo_copy() performs a "deep" copy of a hostent into a buffer and
553 * returns a pointer to the malloc()ed copy. You need to call free() on the
554 * returned buffer when you're done with it.
556 Curl_addrinfo
*Curl_addrinfo_copy(const void *org
, int port
)
558 const struct hostent
*orig
= org
;
560 return Curl_he2ai(orig
, port
);
562 #endif /* CURLRES_ADDRINFO_COPY */
564 /***********************************************************************
565 * Only for plain-ipv4 and c-ares builds
566 **********************************************************************/
568 #if defined(CURLRES_IPV4) || defined(CURLRES_ARES)
570 * This is a function for freeing name information in a protocol independent
573 void Curl_freeaddrinfo(Curl_addrinfo
*ai
)
577 /* walk over the list and free all entries */
586 struct hostent hostentry
;
587 char *h_addr_list
[2];
588 struct in_addr addrentry
;
589 char h_name
[16]; /* 123.123.123.123 = 15 letters is maximum */
593 * Curl_ip2addr() takes a 32bit ipv4 internet address as input parameter
594 * together with a pointer to the string version of the address, and it
595 * returns a Curl_addrinfo chain filled in correctly with information for this
598 * The input parameters ARE NOT checked for validity but they are expected
599 * to have been checked already when this is called.
601 Curl_addrinfo
*Curl_ip2addr(in_addr_t num
, const char *hostname
, int port
)
605 struct in_addr
*addrentry
;
606 struct namebuf buffer
;
607 struct namebuf
*buf
= &buffer
;
610 h
->h_addr_list
= &buf
->h_addr_list
[0];
611 addrentry
= &buf
->addrentry
;
613 /* On UNICOS, s_addr is a bit field and for some reason assigning to it
614 * doesn't work. There must be a better fix than this ugly hack.
616 memcpy(addrentry
, &num
, SIZEOF_in_addr
);
618 addrentry
->s_addr
= num
;
620 h
->h_addr_list
[0] = (char*)addrentry
;
621 h
->h_addr_list
[1] = NULL
;
622 h
->h_addrtype
= AF_INET
;
623 h
->h_length
= sizeof(*addrentry
);
624 h
->h_name
= &buf
->h_name
[0];
627 /* Now store the dotted version of the address */
628 snprintf((char *)h
->h_name
, 16, "%s", hostname
);
630 ai
= Curl_he2ai(h
, port
);