1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2008, 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.1.1.1 2008-09-23 16:32:05 hoffman Exp $
22 ***************************************************************************/
31 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
44 #include <stdlib.h> /* required for free() prototypes */
47 #include <unistd.h> /* for the close() proto */
70 #include "inet_ntop.h"
72 #define _MPRINTF_REPLACE /* use our functions only */
73 #include <curl/mprintf.h>
75 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
76 #include "inet_ntoa_r.h"
80 /* The last #include file should be: */
87 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
88 * source file are these:
90 * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
91 * that. The host may not be able to resolve IPv6, but we don't really have to
92 * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
95 * CURLRES_ARES - is defined if libcurl is built to use c-ares for
96 * asynchronous name resolves. This can be Windows or *nix.
98 * CURLRES_THREADED - is defined if libcurl is built to run under (native)
99 * Windows, and then the name resolve will be done in a new thread, and the
100 * supported API will be the same as for ares-builds.
102 * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
103 * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
106 * The host*.c sources files are split up like this:
108 * hostip.c - method-independent resolver functions and utility functions
109 * hostasyn.c - functions for asynchronous name resolves
110 * hostsyn.c - functions for synchronous name resolves
111 * hostares.c - functions for ares-using name resolves
112 * hostthre.c - functions for threaded name resolves
113 * hostip4.c - ipv4-specific functions
114 * hostip6.c - ipv6-specific functions
116 * The hostip.h is the united header file for all this. It defines the
117 * CURLRES_* defines based on the config*.h and setup.h defines.
120 /* These two symbols are for the global DNS cache */
121 static struct curl_hash hostname_cache
;
122 static int host_cache_initialized
;
124 static void freednsentry(void *freethis
);
127 * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
128 * Global DNS cache is general badness. Do not use. This will be removed in
129 * a future version. Use the share interface instead!
131 * Returns a struct curl_hash pointer on success, NULL on failure.
133 struct curl_hash
*Curl_global_host_cache_init(void)
136 if(!host_cache_initialized
) {
137 rc
= Curl_hash_init(&hostname_cache
, 7, Curl_hash_str
,
138 Curl_str_key_compare
, freednsentry
);
140 host_cache_initialized
= 1;
142 return rc
?NULL
:&hostname_cache
;
146 * Destroy and cleanup the global DNS cache
148 void Curl_global_host_cache_dtor(void)
150 if(host_cache_initialized
) {
151 Curl_hash_clean(&hostname_cache
);
152 host_cache_initialized
= 0;
157 * Return # of adresses in a Curl_addrinfo struct
159 int Curl_num_addresses(const Curl_addrinfo
*addr
)
162 for (i
= 0; addr
; addr
= addr
->ai_next
, i
++)
168 * Curl_printable_address() returns a printable version of the 1st address
169 * given in the 'ip' argument. The result will be stored in the buf that is
172 * If the conversion fails, it returns NULL.
174 const char *Curl_printable_address(const Curl_addrinfo
*ip
,
175 char *buf
, size_t bufsize
)
177 const void *ip4
= &((const struct sockaddr_in
*)ip
->ai_addr
)->sin_addr
;
178 int af
= ip
->ai_family
;
180 const void *ip6
= &((const struct sockaddr_in6
*)ip
->ai_addr
)->sin6_addr
;
182 const void *ip6
= NULL
;
185 return Curl_inet_ntop(af
, af
== AF_INET
? ip4
: ip6
, buf
, bufsize
);
189 * Return a hostcache id string for the providing host + port, to be used by
193 create_hostcache_id(const char *server
, int port
)
195 /* create and return the new allocated entry */
196 return aprintf("%s:%d", server
, port
);
199 struct hostcache_prune_data
{
205 * This function is set as a callback to be called for every entry in the DNS
206 * cache when we want to prune old unused entries.
208 * Returning non-zero means remove the entry, return 0 to keep it in the
212 hostcache_timestamp_remove(void *datap
, void *hc
)
214 struct hostcache_prune_data
*data
=
215 (struct hostcache_prune_data
*) datap
;
216 struct Curl_dns_entry
*c
= (struct Curl_dns_entry
*) hc
;
218 if((data
->now
- c
->timestamp
< data
->cache_timeout
) ||
220 /* please don't remove */
229 * Prune the DNS cache. This assumes that a lock has already been taken.
232 hostcache_prune(struct curl_hash
*hostcache
, long cache_timeout
, time_t now
)
234 struct hostcache_prune_data user
;
236 user
.cache_timeout
= cache_timeout
;
239 Curl_hash_clean_with_criterium(hostcache
,
241 hostcache_timestamp_remove
);
245 * Library-wide function for pruning the DNS cache. This function takes and
246 * returns the appropriate locks.
248 void Curl_hostcache_prune(struct SessionHandle
*data
)
252 if((data
->set
.dns_cache_timeout
== -1) || !data
->dns
.hostcache
)
253 /* cache forever means never prune, and NULL hostcache means
258 Curl_share_lock(data
, CURL_LOCK_DATA_DNS
, CURL_LOCK_ACCESS_SINGLE
);
262 /* Remove outdated and unused entries from the hostcache */
263 hostcache_prune(data
->dns
.hostcache
,
264 data
->set
.dns_cache_timeout
,
268 Curl_share_unlock(data
, CURL_LOCK_DATA_DNS
);
272 * Check if the entry should be pruned. Assumes a locked cache.
275 remove_entry_if_stale(struct SessionHandle
*data
, struct Curl_dns_entry
*dns
)
277 struct hostcache_prune_data user
;
279 if( !dns
|| (data
->set
.dns_cache_timeout
== -1) || !data
->dns
.hostcache
)
280 /* cache forever means never prune, and NULL hostcache means
285 user
.cache_timeout
= data
->set
.dns_cache_timeout
;
287 if( !hostcache_timestamp_remove(&user
,dns
) )
290 Curl_hash_clean_with_criterium(data
->dns
.hostcache
,
292 hostcache_timestamp_remove
);
298 #ifdef HAVE_SIGSETJMP
299 /* Beware this is a global and unique instance. This is used to store the
300 return address that we can jump back to from inside a signal handler. This
301 is not thread-safe stuff. */
302 sigjmp_buf curl_jmpenv
;
307 * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
309 * When calling Curl_resolv() has resulted in a response with a returned
310 * address, we call this function to store the information in the dns
313 * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
315 struct Curl_dns_entry
*
316 Curl_cache_addr(struct SessionHandle
*data
,
318 const char *hostname
,
323 struct Curl_dns_entry
*dns
;
324 struct Curl_dns_entry
*dns2
;
327 /* Create an entry id, based upon the hostname and port */
328 entry_id
= create_hostcache_id(hostname
, port
);
329 /* If we can't create the entry id, fail */
332 entry_len
= strlen(entry_id
);
334 /* Create a new cache entry */
335 dns
= (struct Curl_dns_entry
*) calloc(sizeof(struct Curl_dns_entry
), 1);
341 dns
->inuse
= 0; /* init to not used */
342 dns
->addr
= addr
; /* this is the address(es) */
344 /* Store the resolved data in our DNS cache. This function may return a
345 pointer to an existing struct already present in the hash, and it may
346 return the same argument we pass in. Make no assumptions. */
347 dns2
= Curl_hash_add(data
->dns
.hostcache
, entry_id
, entry_len
+1,
350 /* Major badness, run away. */
358 dns
->timestamp
= now
; /* used now */
359 dns
->inuse
++; /* mark entry as in-use */
361 /* free the allocated entry_id again */
368 * Curl_resolv() is the main name resolve function within libcurl. It resolves
369 * a name and returns a pointer to the entry in the 'entry' argument (if one
370 * is provided). This function might return immediately if we're using asynch
371 * resolves. See the return codes.
373 * The cache entry we return will get its 'inuse' counter increased when this
374 * function is used. You MUST call Curl_resolv_unlock() later (when you're
375 * done using this struct) to decrease the counter again.
379 * CURLRESOLV_ERROR (-1) = error, no pointer
380 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
381 * CURLRESOLV_PENDING (1) = waiting for response, no pointer
384 int Curl_resolv(struct connectdata
*conn
,
385 const char *hostname
,
387 struct Curl_dns_entry
**entry
)
389 char *entry_id
= NULL
;
390 struct Curl_dns_entry
*dns
= NULL
;
392 struct SessionHandle
*data
= conn
->data
;
394 int rc
= CURLRESOLV_ERROR
; /* default to failure */
397 #ifdef HAVE_SIGSETJMP
398 /* this allows us to time-out from the name resolver, as the timeout
399 will generate a signal and we will siglongjmp() from that here */
400 if(!data
->set
.no_signal
) {
401 if(sigsetjmp(curl_jmpenv
, 1)) {
402 /* this is coming from a siglongjmp() */
403 failf(data
, "name lookup timed out");
409 /* Create an entry id, based upon the hostname and port */
410 entry_id
= create_hostcache_id(hostname
, port
);
411 /* If we can't create the entry id, fail */
415 entry_len
= strlen(entry_id
);
418 Curl_share_lock(data
, CURL_LOCK_DATA_DNS
, CURL_LOCK_ACCESS_SINGLE
);
420 /* See if its already in our dns cache */
421 dns
= Curl_hash_pick(data
->dns
.hostcache
, entry_id
, entry_len
+1);
423 /* See whether the returned entry is stale. Done before we release lock */
424 if( remove_entry_if_stale(data
, dns
) )
425 dns
= NULL
; /* the memory deallocation is being handled by the hash */
428 dns
->inuse
++; /* we use it! */
429 rc
= CURLRESOLV_RESOLVED
;
433 Curl_share_unlock(data
, CURL_LOCK_DATA_DNS
);
435 /* free the allocated entry_id again */
439 /* The entry was not in the cache. Resolve it to IP address */
444 /* Check what IP specifics the app has requested and if we can provide it.
445 * If not, bail out. */
446 if(!Curl_ipvalid(data
))
447 return CURLRESOLV_ERROR
;
449 /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
450 non-zero value indicating that we need to wait for the response to the
452 addr
= Curl_getaddrinfo(conn
, hostname
, port
, &respwait
);
456 /* the response to our resolve call will come asynchronously at
457 a later time, good or bad */
458 /* First, check that we haven't received the info by now */
459 result
= Curl_is_resolved(conn
, &dns
);
460 if(result
) /* error detected */
461 return CURLRESOLV_ERROR
;
463 rc
= CURLRESOLV_RESOLVED
; /* pointer provided */
465 rc
= CURLRESOLV_PENDING
; /* no info yet */
470 Curl_share_lock(data
, CURL_LOCK_DATA_DNS
, CURL_LOCK_ACCESS_SINGLE
);
472 /* we got a response, store it in the cache */
473 dns
= Curl_cache_addr(data
, addr
, hostname
, port
);
476 Curl_share_unlock(data
, CURL_LOCK_DATA_DNS
);
479 /* returned failure, bail out nicely */
480 Curl_freeaddrinfo(addr
);
482 rc
= CURLRESOLV_RESOLVED
;
492 * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
493 * made, the struct may be destroyed due to pruning. It is important that only
494 * one unlock is made for each Curl_resolv() call.
496 void Curl_resolv_unlock(struct SessionHandle
*data
, struct Curl_dns_entry
*dns
)
498 DEBUGASSERT(dns
&& (dns
->inuse
>0));
501 Curl_share_lock(data
, CURL_LOCK_DATA_DNS
, CURL_LOCK_ACCESS_SINGLE
);
506 Curl_share_unlock(data
, CURL_LOCK_DATA_DNS
);
510 * File-internal: free a cache dns entry.
512 static void freednsentry(void *freethis
)
514 struct Curl_dns_entry
*p
= (struct Curl_dns_entry
*) freethis
;
516 Curl_freeaddrinfo(p
->addr
);
522 * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
524 struct curl_hash
*Curl_mk_dnscache(void)
526 return Curl_hash_alloc(7, Curl_hash_str
, Curl_str_key_compare
, freednsentry
);
529 #ifdef CURLRES_ADDRINFO_COPY
531 /* align on even 64bit boundaries */
532 #define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
535 * Curl_addrinfo_copy() performs a "deep" copy of a hostent into a buffer and
536 * returns a pointer to the malloc()ed copy. You need to call free() on the
537 * returned buffer when you're done with it.
539 Curl_addrinfo
*Curl_addrinfo_copy(const void *org
, int port
)
541 const struct hostent
*orig
= org
;
543 return Curl_he2ai(orig
, port
);
545 #endif /* CURLRES_ADDRINFO_COPY */
547 /***********************************************************************
548 * Only for plain-ipv4 and c-ares builds (NOTE: c-ares builds can be IPv6
550 **********************************************************************/
552 #if defined(CURLRES_IPV4) || defined(CURLRES_ARES)
554 * This is a function for freeing name information in a protocol independent
557 void Curl_freeaddrinfo(Curl_addrinfo
*ai
)
561 /* walk over the list and free all entries */
565 free(ai
->ai_canonname
);
572 struct hostent hostentry
;
573 char *h_addr_list
[2];
574 struct in_addr addrentry
;
575 char h_name
[16]; /* 123.123.123.123 = 15 letters is maximum */
579 * Curl_ip2addr() takes a 32bit ipv4 internet address as input parameter
580 * together with a pointer to the string version of the address, and it
581 * returns a Curl_addrinfo chain filled in correctly with information for this
584 * The input parameters ARE NOT checked for validity but they are expected
585 * to have been checked already when this is called.
587 Curl_addrinfo
*Curl_ip2addr(in_addr_t num
, const char *hostname
, int port
)
591 #if defined(VMS) && \
592 defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64)
593 #pragma pointer_size save
594 #pragma pointer_size short
595 #pragma message disable PTRMISMATCH
599 struct in_addr
*addrentry
;
600 struct namebuf buffer
;
601 struct namebuf
*buf
= &buffer
;
604 h
->h_addr_list
= &buf
->h_addr_list
[0];
605 addrentry
= &buf
->addrentry
;
607 /* On UNICOS, s_addr is a bit field and for some reason assigning to it
608 * doesn't work. There must be a better fix than this ugly hack.
610 memcpy(addrentry
, &num
, SIZEOF_in_addr
);
612 addrentry
->s_addr
= num
;
614 h
->h_addr_list
[0] = (char*)addrentry
;
615 h
->h_addr_list
[1] = NULL
;
616 h
->h_addrtype
= AF_INET
;
617 h
->h_length
= sizeof(*addrentry
);
618 h
->h_name
= &buf
->h_name
[0];
621 /* Now store the dotted version of the address */
622 snprintf((char *)h
->h_name
, 16, "%s", hostname
);
624 #if defined(VMS) && \
625 defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64)
626 #pragma pointer_size restore
627 #pragma message enable PTRMISMATCH
630 ai
= Curl_he2ai(h
, port
);
636 * Curl_he2ai() translates from a hostent struct to a Curl_addrinfo struct.
637 * The Curl_addrinfo is meant to work like the addrinfo struct does for IPv6
638 * stacks, but for all hosts and environments.
640 * Curl_addrinfo defined in "lib/hostip.h"
642 * struct Curl_addrinfo {
647 * socklen_t ai_addrlen; * Follow rfc3493 struct addrinfo *
648 * char *ai_canonname;
649 * struct sockaddr *ai_addr;
650 * struct Curl_addrinfo *ai_next;
653 * hostent defined in <netdb.h>
660 * char **h_addr_list;
663 * for backward compatibility:
665 * #define h_addr h_addr_list[0]
668 Curl_addrinfo
*Curl_he2ai(const struct hostent
*he
, int port
)
671 Curl_addrinfo
*prevai
= NULL
;
672 Curl_addrinfo
*firstai
= NULL
;
673 struct sockaddr_in
*addr
;
675 struct sockaddr_in6
*addr6
;
676 #endif /* CURLRES_IPV6 */
678 struct in_addr
*curr
;
681 /* no input == no output! */
684 for(i
=0; (curr
= (struct in_addr
*)he
->h_addr_list
[i
]) != NULL
; i
++) {
688 if (he
->h_addrtype
== AF_INET6
)
689 ss_size
= sizeof (struct sockaddr_in6
);
691 #endif /* CURLRES_IPV6 */
692 ss_size
= sizeof (struct sockaddr_in
);
694 ai
= calloc(1, sizeof(Curl_addrinfo
) + ss_size
);
700 /* store the pointer we want to return from this function */
704 /* make the previous entry point to this */
705 prevai
->ai_next
= ai
;
707 ai
->ai_family
= he
->h_addrtype
;
709 /* we return all names as STREAM, so when using this address for TFTP
710 the type must be ignored and conn->socktype be used instead! */
711 ai
->ai_socktype
= SOCK_STREAM
;
713 ai
->ai_addrlen
= ss_size
;
714 /* make the ai_addr point to the address immediately following this struct
715 and use that area to store the address */
716 ai
->ai_addr
= (struct sockaddr
*) ((char*)ai
+ sizeof(Curl_addrinfo
));
718 /* need to free this eventually */
719 ai
->ai_canonname
= strdup(he
->h_name
);
721 /* leave the rest of the struct filled with zero */
723 switch (ai
->ai_family
) {
725 addr
= (struct sockaddr_in
*)ai
->ai_addr
; /* storage area for this info */
727 memcpy((char *)&(addr
->sin_addr
), curr
, sizeof(struct in_addr
));
728 addr
->sin_family
= (unsigned short)(he
->h_addrtype
);
729 addr
->sin_port
= htons((unsigned short)port
);
734 addr6
= (struct sockaddr_in6
*)ai
->ai_addr
; /* storage area for this info */
736 memcpy((char *)&(addr6
->sin6_addr
), curr
, sizeof(struct in6_addr
));
737 addr6
->sin6_family
= (unsigned short)(he
->h_addrtype
);
738 addr6
->sin6_port
= htons((unsigned short)port
);
740 #endif /* CURLRES_IPV6 */
748 #endif /* CURLRES_IPV4 || CURLRES_ARES */