1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2004, 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.130 2004/03/17 12:46:46 bagder Exp $
22 ***************************************************************************/
31 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
34 #ifdef HAVE_SYS_TYPES_H
35 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 #include <sys/socket.h>
40 #ifdef HAVE_NETINET_IN_H
41 #include <netinet/in.h>
46 #ifdef HAVE_ARPA_INET_H
47 #include <arpa/inet.h>
50 #include <stdlib.h> /* required for free() prototypes */
63 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
65 #define in_addr_t unsigned long
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"
82 /* The last #include file should be: */
88 #define ARES_SUCCESS CURLE_OK
91 static curl_hash hostname_cache;
92 static int host_cache_initialized;
94 static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
99 #if !defined(HAVE_GETHOSTBYNAME_R) || defined(USE_ARES) || \
100 defined(USE_THREADING_GETHOSTBYNAME)
101 static struct hostent* pack_hostent(char** buf, struct hostent* orig);
105 #ifdef USE_THREADING_GETHOSTBYNAME
106 #ifdef DEBUG_THREADING_GETHOSTBYNAME
107 /* If this is defined, provide tracing */
108 #define TRACE(args) \
109 do { trace_it("%u: ", __LINE__); trace_it args; } while (0)
111 static void trace_it (const char *fmt, ...);
116 static struct hostent* pack_hostent (char** buf, struct hostent* orig);
117 static bool init_gethostbyname_thread (struct connectdata *conn,
118 const char *hostname, int port);
126 void Curl_global_host_cache_init(void)
128 if (!host_cache_initialized) {
129 Curl_hash_init(&hostname_cache, 7, Curl_freednsinfo);
130 host_cache_initialized = 1;
134 curl_hash *Curl_global_host_cache_get(void)
136 return &hostname_cache;
139 void Curl_global_host_cache_dtor(void)
141 if (host_cache_initialized) {
142 Curl_hash_clean(&hostname_cache);
143 host_cache_initialized = 0;
147 /* count the number of characters that an integer takes up */
148 static int _num_chars(int i)
152 /* While the number divided by 10 is greater than one,
153 * re-divide the number by 10, and increment the number of
156 * this relies on the fact that for every multiple of 10,
157 * a new digit is added onto every number
168 /* Create a hostcache id */
170 create_hostcache_id(char *server, int port, size_t *entry_len)
174 /* Get the length of the new entry id */
175 *entry_len = strlen(server) + /* Hostname length */
176 1 + /* ':' seperator */
177 _num_chars(port); /* number of characters the port will take up */
179 /* Allocate the new entry id */
180 id = malloc(*entry_len + 1); /* 1 extra for the zero terminator */
184 /* Create the new entry */
185 sprintf(id, "%s:%d", server, port);
187 return id; /* return pointer to the string */
190 struct hostcache_prune_data {
196 hostcache_timestamp_remove(void *datap, void *hc)
198 struct hostcache_prune_data *data =
199 (struct hostcache_prune_data *) datap;
200 struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
202 if ((data->now - c->timestamp < data->cache_timeout) ||
204 /* please don't remove */
213 hostcache_prune(curl_hash *hostcache, int cache_timeout, time_t now)
215 struct hostcache_prune_data user;
217 user.cache_timeout = cache_timeout;
220 Curl_hash_clean_with_criterium(hostcache,
222 hostcache_timestamp_remove);
225 void Curl_hostcache_prune(struct SessionHandle *data)
229 if(data->set.dns_cache_timeout == -1)
230 /* cache forever means never prune! */
234 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
238 /* Remove outdated and unused entries from the hostcache */
239 hostcache_prune(data->hostcache,
240 data->set.dns_cache_timeout,
244 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
247 #ifdef HAVE_SIGSETJMP
248 /* Beware this is a global and unique instance */
249 sigjmp_buf curl_jmpenv;
253 /* When calling Curl_resolv() has resulted in a response with a returned
254 address, we call this function to store the information in the dns
257 static struct Curl_dns_entry *
258 cache_resolv_response(struct SessionHandle *data,
265 struct Curl_dns_entry *dns;
268 /* Create an entry id, based upon the hostname and port */
269 entry_id = create_hostcache_id(hostname, port, &entry_len);
270 /* If we can't create the entry id, fail */
274 /* Create a new cache entry */
275 dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry));
277 Curl_freeaddrinfo(addr);
282 dns->inuse = 0; /* init to not used */
283 dns->addr = addr; /* this is the address(es) */
285 /* Store the resolved data in our DNS cache. This function may return a
286 pointer to an existing struct already present in the hash, and it may
287 return the same argument we pass in. Make no assumptions. */
288 dns = Curl_hash_add(data->hostcache, entry_id, entry_len+1, (void *)dns);
290 /* Major badness, run away. When this happens, the 'dns' data has
291 already been cleared up by Curl_hash_add(). */
297 dns->timestamp = now; /* used now */
298 dns->inuse++; /* mark entry as in-use */
300 /* free the allocated entry_id again */
306 /* Resolve a name and return a pointer in the 'entry' argument if one
311 -1 = error, no pointer
312 0 = OK, pointer provided
313 1 = waiting for response, no pointer
315 int Curl_resolv(struct connectdata *conn,
318 struct Curl_dns_entry **entry)
320 char *entry_id = NULL;
321 struct Curl_dns_entry *dns = NULL;
324 struct SessionHandle *data = conn->data;
327 /* default to failure */
331 #ifdef HAVE_SIGSETJMP
332 /* this allows us to time-out from the name resolver, as the timeout
333 will generate a signal and we will siglongjmp() from that here */
334 if(!data->set.no_signal && sigsetjmp(curl_jmpenv, 1)) {
335 /* this is coming from a siglongjmp() */
336 failf(data, "name lookup timed out");
341 /* Create an entry id, based upon the hostname and port */
342 entry_id = create_hostcache_id(hostname, port, &entry_len);
343 /* If we can't create the entry id, fail */
348 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
350 /* See if its already in our dns cache */
351 dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1);
354 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
356 /* free the allocated entry_id again */
360 /* The entry was not in the cache. Resolve it to IP address */
362 /* If my_getaddrinfo() returns NULL, 'wait' might be set to a non-zero
363 value indicating that we need to wait for the response to the resolve
365 Curl_addrinfo *addr = my_getaddrinfo(conn, hostname, port, &wait);
369 /* the response to our resolve call will come asynchronously at
370 a later time, good or bad */
371 /* First, check that we haven't received the info by now */
372 result = Curl_is_resolved(conn, &dns);
373 if(result) /* error detected */
376 rc = 0; /* pointer provided */
378 rc = 1; /* no info yet */
383 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
385 /* we got a response, store it in the cache */
386 dns = cache_resolv_response(data, addr, hostname, port);
389 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
392 /* returned failure, bail out nicely */
393 Curl_freeaddrinfo(addr);
399 dns->inuse++; /* we use it! */
408 void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
411 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
417 infof(data, "Interal host cache screw-up!");
423 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
427 * This is a wrapper function for freeing name information in a protocol
428 * independent way. This takes care of using the appropriate underlaying
431 void Curl_freeaddrinfo(Curl_addrinfo *p)
436 free(p); /* works fine for the ARES case too */
441 * Free a cache dns entry.
443 void Curl_freednsinfo(void *freethis)
445 struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
447 Curl_freeaddrinfo(p->addr);
452 /* --- resolve name or IP-number --- */
454 /* Allocate enough memory to hold the full name information structs and
455 * everything. OSF1 is known to require at least 8872 bytes. The buffer
456 * required for storing all possible aliases and IP numbers is according to
457 * Stevens' Unix Network Programming 2nd edition, p. 304: 8192 bytes!
459 #define CURL_NAMELOOKUP_SIZE 9000
463 CURLcode Curl_multi_ares_fdset(struct connectdata *conn,
465 fd_set *write_fd_set,
469 int max = ares_fds(conn->data->state.areschannel,
470 read_fd_set, write_fd_set);
476 /* called to check if the name is resolved now */
477 CURLcode Curl_is_resolved(struct connectdata *conn,
478 struct Curl_dns_entry **dns)
480 fd_set read_fds, write_fds;
481 static const struct timeval tv={0,0};
483 struct SessionHandle *data = conn->data;
488 nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds);
490 count = select(nfds, &read_fds, &write_fds, NULL,
491 (struct timeval *)&tv);
494 ares_process(data->state.areschannel, &read_fds, &write_fds);
498 if(conn->async.done) {
499 /* we're done, kill the ares handle */
501 return CURLE_COULDNT_RESOLVE_HOST;
502 *dns = conn->async.dns;
508 /* This is a function that locks and waits until the name resolve operation
511 If 'entry' is non-NULL, make it point to the resolved dns entry
513 Return CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
514 CURLE_OPERATION_TIMEDOUT if a time-out occurred.
516 CURLcode Curl_wait_for_resolv(struct connectdata *conn,
517 struct Curl_dns_entry **entry)
519 CURLcode rc=CURLE_OK;
520 struct SessionHandle *data = conn->data;
521 struct timeval now = Curl_tvnow();
522 bool timedout = FALSE;
523 long timeout = 300; /* default name resolve timeout in seconds */
524 long elapsed = 0; /* time taken so far */
526 /* now, see if there's a connect timeout or a regular timeout to
527 use instead of the default one */
528 if(conn->data->set.connecttimeout)
529 timeout = conn->data->set.connecttimeout;
530 else if(conn->data->set.timeout)
531 timeout = conn->data->set.timeout;
533 /* Wait for the name resolve query to complete. */
536 fd_set read_fds, write_fds;
537 struct timeval *tvp, tv, store;
540 store.tv_sec = (int)(timeout - elapsed);
545 nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds);
548 tvp = ares_timeout(data->state.areschannel,
550 count = select(nfds, &read_fds, &write_fds, NULL, tvp);
551 if (count < 0 && errno != EINVAL)
558 ares_process(data->state.areschannel, &read_fds, &write_fds);
560 elapsed = Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
563 /* Operation complete, if the lookup was successful we now have the entry
567 *entry = conn->async.dns;
569 if(!conn->async.dns) {
570 /* a name was not resolved */
571 if(timedout || (conn->async.status == ARES_ETIMEOUT)) {
572 failf(data, "Resolving host timed out: %s", conn->name);
573 rc = CURLE_OPERATION_TIMEDOUT;
575 else if(conn->async.done) {
576 failf(data, "Could not resolve host: %s (%s)", conn->name,
577 ares_strerror(conn->async.status));
578 rc = CURLE_COULDNT_RESOLVE_HOST;
581 rc = CURLE_OPERATION_TIMEDOUT;
583 /* close the connection, since we can't return failure here without
584 cleaning up this connection properly */
585 Curl_disconnect(conn);
592 #if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME)
594 /* this function gets called by ares/gethostbyname_thread() when we got
595 the name resolved or not */
596 static void host_callback(void *arg, /* "struct connectdata *" */
598 struct hostent *hostent)
600 struct connectdata *conn = (struct connectdata *)arg;
601 struct Curl_dns_entry *dns = NULL;
603 conn->async.done = TRUE;
604 conn->async.status = status;
606 if(ARES_SUCCESS == status) {
607 /* we got a resolved name in 'hostent' */
608 char *bufp = (char *)malloc(CURL_NAMELOOKUP_SIZE);
611 /* pack_hostent() copies to and shrinks the target buffer */
612 struct hostent *he = pack_hostent(&bufp, hostent);
614 struct SessionHandle *data = conn->data;
617 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
619 dns = cache_resolv_response(data, he,
620 conn->async.hostname, conn->async.port);
623 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
627 conn->async.dns = dns;
629 /* The input hostent struct will be freed by ares when we return from this
636 * Return name information about the given hostname and port number. If
637 * successful, the 'hostent' is returned and the forth argument will point to
638 * memory we need to free after use. That meory *MUST* be freed with
639 * Curl_freeaddrinfo(), nothing else.
641 static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
647 struct SessionHandle *data = conn->data;
651 bufp = strdup(hostname);
654 Curl_safefree(conn->async.hostname);
655 conn->async.hostname = bufp;
656 conn->async.port = port;
657 conn->async.done = FALSE; /* not done */
658 conn->async.status = 0; /* clear */
659 conn->async.dns = NULL; /* clear */
661 /* areschannel is already setup in the Curl_open() function */
662 ares_gethostbyname(data->state.areschannel, hostname, PF_INET,
663 host_callback, conn);
665 *waitp = TRUE; /* please wait for the response */
667 return NULL; /* no struct yet */
671 #if !defined(USE_ARES) && !defined(USE_THREADING_GETHOSTBYNAME)
673 /* For builds without ARES and threaded gethostbyname, Curl_resolv() can never
674 return wait==TRUE, so this function will never be called. If it still gets
675 called, we return failure at once. */
676 CURLcode Curl_wait_for_resolv(struct connectdata *conn,
677 struct Curl_dns_entry **entry)
681 return CURLE_COULDNT_RESOLVE_HOST;
684 CURLcode Curl_is_resolved(struct connectdata *conn,
685 struct Curl_dns_entry **dns)
690 return CURLE_COULDNT_RESOLVE_HOST;
694 #if !defined(USE_ARES)
695 CURLcode Curl_multi_ares_fdset(struct connectdata *conn,
697 fd_set *write_fd_set,
708 #if defined(ENABLE_IPV6) && !defined(USE_ARES)
711 /* These two are strictly for memory tracing and are using the same
712 * style as the family otherwise present in memdebug.c. I put these ones
713 * here since they require a bunch of struct types I didn't wanna include
716 int curl_getaddrinfo(char *hostname, char *service,
717 struct addrinfo *hints,
718 struct addrinfo **result,
719 int line, const char *source)
721 int res=(getaddrinfo)(hostname, service, hints, result);
725 fprintf(logfile, "ADDR %s:%d getaddrinfo() = %p\n",
726 source, line, (void *)*result);
730 fprintf(logfile, "ADDR %s:%d getaddrinfo() failed\n",
736 void curl_freeaddrinfo(struct addrinfo *freethis,
737 int line, const char *source)
739 (freeaddrinfo)(freethis);
741 fprintf(logfile, "ADDR %s:%d freeaddrinfo(%p)\n",
742 source, line, (void *)freethis);
748 * Return name information about the given hostname and port number. If
749 * successful, the 'addrinfo' is returned and the forth argument will point to
750 * memory we need to free after use. That meory *MUST* be freed with
751 * Curl_freeaddrinfo(), nothing else.
753 static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
758 struct addrinfo hints, *res;
760 char sbuf[NI_MAXSERV];
762 struct SessionHandle *data = conn->data;
764 *waitp=0; /* don't wait, we have the response now */
766 /* see if we have an IPv6 stack */
767 s = socket(PF_INET6, SOCK_DGRAM, 0);
769 /* Some non-IPv6 stacks have been found to make very slow name resolves
770 * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if
771 * the stack seems to be a non-ipv6 one. */
774 /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest
775 * possible checks. And close the socket again.
780 * Check if a more limited name resolve has been requested.
782 switch(data->set.ip_version) {
783 case CURL_IPRESOLVE_V4:
786 case CURL_IPRESOLVE_V6:
795 memset(&hints, 0, sizeof(hints));
796 hints.ai_family = pf;
797 hints.ai_socktype = SOCK_STREAM;
798 hints.ai_flags = AI_CANONNAME;
799 snprintf(sbuf, sizeof(sbuf), "%d", port);
800 error = getaddrinfo(hostname, sbuf, &hints, &res);
802 infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port);
808 #else /* following code is IPv4-only */
810 #if !defined(HAVE_GETHOSTBYNAME_R) || defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME)
811 static void hostcache_fixoffset(struct hostent *h, long offset);
813 * Performs a "deep" copy of a hostent into a buffer (returns a pointer to the
814 * copy). Make absolutely sure the destination buffer is big enough!
816 static struct hostent* pack_hostent(char** buf, struct hostent* orig)
820 struct hostent* copy;
827 copy = (struct hostent*)bufptr;
829 bufptr += sizeof(struct hostent);
830 copy->h_name = bufptr;
831 len = strlen(orig->h_name) + 1;
832 strncpy(bufptr, orig->h_name, len);
835 /* we align on even 64bit boundaries for safety */
836 #define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
838 /* This must be aligned properly to work on many CPU architectures! */
839 bufptr = MEMALIGN(bufptr);
841 copy->h_aliases = (char**)bufptr;
843 /* Figure out how many aliases there are */
844 for (i = 0; orig->h_aliases && orig->h_aliases[i]; ++i);
846 /* Reserve room for the array */
847 bufptr += (i + 1) * sizeof(char*);
849 /* Clone all known aliases */
850 if(orig->h_aliases) {
851 for(i = 0; (str = orig->h_aliases[i]); i++) {
852 len = strlen(str) + 1;
853 strncpy(bufptr, str, len);
854 copy->h_aliases[i] = bufptr;
858 /* if(!orig->h_aliases) i was already set to 0 */
860 /* Terminate the alias list with a NULL */
861 copy->h_aliases[i] = NULL;
863 copy->h_addrtype = orig->h_addrtype;
864 copy->h_length = orig->h_length;
866 /* align it for (at least) 32bit accesses */
867 bufptr = MEMALIGN(bufptr);
869 copy->h_addr_list = (char**)bufptr;
871 /* Figure out how many addresses there are */
872 for (i = 0; orig->h_addr_list[i] != NULL; ++i);
874 /* Reserve room for the array */
875 bufptr += (i + 1) * sizeof(char*);
878 len = orig->h_length;
879 str = orig->h_addr_list[i];
880 while (str != NULL) {
881 memcpy(bufptr, str, len);
882 copy->h_addr_list[i] = bufptr;
884 str = orig->h_addr_list[++i];
886 copy->h_addr_list[i] = NULL;
888 /* now, shrink the allocated buffer to the size we actually need, which
889 most often is only a fraction of the original alloc */
890 newbuf=(char *)realloc(*buf, (long)bufptr-(long)(*buf));
892 /* if the alloc moved, we need to adjust things again */
894 hostcache_fixoffset((struct hostent*)newbuf, (long)newbuf-(long)*buf);
896 /* setup the return */
898 copy = (struct hostent*)newbuf;
904 static void hostcache_fixoffset(struct hostent *h, long offset)
908 h->h_name=(char *)((long)h->h_name+offset);
910 /* only relocate aliases if there are any! */
911 h->h_aliases=(char **)((long)h->h_aliases+offset);
912 while(h->h_aliases[i]) {
913 h->h_aliases[i]=(char *)((long)h->h_aliases[i]+offset);
918 h->h_addr_list=(char **)((long)h->h_addr_list+offset);
920 while(h->h_addr_list[i]) {
921 h->h_addr_list[i]=(char *)((long)h->h_addr_list[i]+offset);
928 static char *MakeIP(unsigned long num, char *addr, int addr_len)
930 #if defined(HAVE_INET_NTOA) || defined(HAVE_INET_NTOA_R)
932 in.s_addr = htonl(num);
934 #if defined(HAVE_INET_NTOA_R)
935 inet_ntoa_r(in,addr,addr_len);
937 strncpy(addr,inet_ntoa(in),addr_len);
940 unsigned char *paddr;
942 num = htonl(num); /* htonl() added to avoid endian probs */
943 paddr = (unsigned char *)#
944 sprintf(addr, "%u.%u.%u.%u", paddr[0], paddr[1], paddr[2], paddr[3]);
949 /* The original code to this function was once stolen from the Dancer source
950 code, written by Bjorn Reese, it has since been patched and modified
952 static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
957 struct hostent *h = NULL;
959 struct SessionHandle *data = conn->data;
960 (void)port; /* unused in IPv4 code */
962 *waitp = 0; /* don't wait, we act synchronously */
964 in=inet_addr(hostname);
965 if (in != CURL_INADDR_NONE) {
966 struct in_addr *addrentry;
968 struct hostent hostentry;
969 char *h_addr_list[2];
970 struct in_addr addrentry;
972 } *buf = (struct namebuf *)malloc(sizeof(struct namebuf));
974 return NULL; /* major failure */
977 h->h_addr_list = &buf->h_addr_list[0];
978 addrentry = &buf->addrentry;
979 addrentry->s_addr = in;
980 h->h_addr_list[0] = (char*)addrentry;
981 h->h_addr_list[1] = NULL;
982 h->h_addrtype = AF_INET;
983 h->h_length = sizeof(*addrentry);
984 h->h_name = &buf->h_name[0];
985 MakeIP(ntohl(in), (char *)h->h_name, sizeof(buf->h_name));
987 #if defined(HAVE_GETHOSTBYNAME_R)
992 int *buf = (int *)malloc(CURL_NAMELOOKUP_SIZE);
994 return NULL; /* major failure */
996 /* Workaround for gethostbyname_r bug in qnx nto. It is also _required_
997 for some of these functions. */
998 memset(buf, 0, CURL_NAMELOOKUP_SIZE);
999 #ifdef HAVE_GETHOSTBYNAME_R_5
1000 /* Solaris, IRIX and more */
1001 (void)res; /* prevent compiler warning */
1003 h = gethostbyname_r(hostname,
1004 (struct hostent *)buf,
1005 (char *)buf + sizeof(struct hostent),
1006 step_size - sizeof(struct hostent),
1009 /* If the buffer is too small, it returns NULL and sets errno to
1010 ERANGE. The errno is thread safe if this is compiled with
1011 -D_REENTRANT as then the 'errno' variable is a macro defined to
1012 get used properly for threads. */
1014 if(h || (errno != ERANGE))
1021 infof(data, "gethostbyname_r() uses %d bytes\n", step_size);
1026 h=(struct hostent *)realloc(buf, step_size);
1027 offset=(long)h-(long)buf;
1028 hostcache_fixoffset(h, offset);
1032 #endif /* HAVE_GETHOSTBYNAME_R_5 */
1033 #ifdef HAVE_GETHOSTBYNAME_R_6
1036 res=gethostbyname_r(hostname,
1037 (struct hostent *)buf,
1038 (char *)buf + sizeof(struct hostent),
1039 step_size - sizeof(struct hostent),
1040 &h, /* DIFFERENCE */
1042 /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a
1043 sudden this function returns EAGAIN if the given buffer size is too
1044 small. Previous versions are known to return ERANGE for the same
1047 This wouldn't be such a big problem if older versions wouldn't
1048 sometimes return EAGAIN on a common failure case. Alas, we can't
1049 assume that EAGAIN *or* ERANGE means ERANGE for any given version of
1052 For now, we do that and thus we may call the function repeatedly and
1053 fail for older glibc versions that return EAGAIN, until we run out
1054 of buffer size (step_size grows beyond CURL_NAMELOOKUP_SIZE).
1056 If anyone has a better fix, please tell us!
1058 -------------------------------------------------------------------
1060 On October 23rd 2003, Dan C dug up more details on the mysteries of
1061 gethostbyname_r() in glibc:
1063 In glibc 2.2.5 the interface is different (this has also been
1064 discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I can't
1065 explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32
1066 (shipped/upgraded by Redhat 7.2) don't show this behavior!
1068 In this "buggy" version, the return code is -1 on error and 'errno'
1069 is set to the ERANGE or EAGAIN code. Note that 'errno' is not a
1070 thread-safe variable.
1074 if(((ERANGE == res) || (EAGAIN == res)) ||
1075 ((res<0) && ((ERANGE == errno) || (EAGAIN == errno))))
1079 } while(step_size <= CURL_NAMELOOKUP_SIZE);
1081 if(!h) /* failure */
1085 infof(data, "gethostbyname_r() uses %d bytes\n", step_size);
1089 h=(struct hostent *)realloc(buf, step_size);
1090 offset=(long)h-(long)buf;
1091 hostcache_fixoffset(h, offset);
1095 #endif/* HAVE_GETHOSTBYNAME_R_6 */
1096 #ifdef HAVE_GETHOSTBYNAME_R_3
1097 /* AIX, Digital Unix/Tru64, HPUX 10, more? */
1099 /* For AIX 4.3 or later, we don't use gethostbyname_r() at all, because of
1100 the plain fact that it does not return unique full buffers on each
1101 call, but instead several of the pointers in the hostent structs will
1102 point to the same actual data! This have the unfortunate down-side that
1103 our caching system breaks down horribly. Luckily for us though, AIX 4.3
1104 and more recent versions have a completely thread-safe libc where all
1105 the data is stored in thread-specific memory areas making calls to the
1106 plain old gethostbyname() work fine even for multi-threaded programs.
1108 This AIX 4.3 or later detection is all made in the configure script.
1110 Troels Walsted Hansen helped us work this out on March 3rd, 2003. */
1112 if(CURL_NAMELOOKUP_SIZE >=
1113 (sizeof(struct hostent)+sizeof(struct hostent_data))) {
1115 /* August 22nd, 2000: Albert Chin-A-Young brought an updated version
1116 * that should work! September 20: Richard Prescott worked on the buffer
1119 res = gethostbyname_r(hostname,
1120 (struct hostent *)buf,
1121 (struct hostent_data *)((char *)buf +
1122 sizeof(struct hostent)));
1123 h_errnop= errno; /* we don't deal with this, but set it anyway */
1126 res = -1; /* failure, too smallish buffer size */
1128 if(!res) { /* success */
1130 h = (struct hostent*)buf; /* result expected in h */
1132 /* This is the worst kind of the different gethostbyname_r() interfaces.
1133 Since we don't know how big buffer this particular lookup required,
1134 we can't realloc down the huge alloc without doing closer analysis of
1135 the returned data. Thus, we always use CURL_NAMELOOKUP_SIZE for every
1136 name lookup. Fixing this would require an extra malloc() and then
1137 calling pack_hostent() that subsequent realloc()s down the new memory
1138 area to the actually used amount. */
1141 #endif /* HAVE_GETHOSTBYNAME_R_3 */
1143 infof(data, "gethostbyname_r(2) failed for %s\n", hostname);
1144 h = NULL; /* set return code to NULL */
1147 #else /* HAVE_GETHOSTBYNAME_R */
1150 #ifdef USE_THREADING_GETHOSTBYNAME
1151 if (init_gethostbyname_thread(conn,hostname,port)) {
1152 *waitp = TRUE; /* please wait for the response */
1155 infof(data, "init_gethostbyname_thread() failed for %s; code %lu\n",
1156 hostname, GetLastError());
1158 h = gethostbyname(hostname);
1160 infof(data, "gethostbyname(2) failed for %s\n", hostname);
1162 char *buf=(char *)malloc(CURL_NAMELOOKUP_SIZE);
1163 /* we make a copy of the hostent right now, right here, as the static
1164 one we got a pointer to might get removed when we don't want/expect
1166 h = pack_hostent(&buf, h);
1168 #endif /*HAVE_GETHOSTBYNAME_R */
1174 #endif /* end of IPv4-specific code */
1176 #endif /* end of !USE_ARES */
1179 #if defined(USE_THREADING_GETHOSTBYNAME)
1180 #ifdef DEBUG_THREADING_GETHOSTBYNAME
1181 static void trace_it (const char *fmt, ...)
1183 static int do_trace = -1;
1187 do_trace = getenv("CURL_TRACE") ? 1 : 0;
1190 va_start (args, fmt);
1191 vfprintf (stderr, fmt, args);
1197 /* For builds without ARES/USE_IPV6, create a resolver thread and wait on it.
1199 static DWORD WINAPI gethostbyname_thread (void *arg)
1201 struct connectdata *conn = (struct connectdata*) arg;
1205 WSASetLastError (conn->async.status = NO_DATA); /* pending status */
1206 he = gethostbyname (conn->async.hostname);
1208 host_callback(conn, ARES_SUCCESS, he);
1212 host_callback(conn, (int)WSAGetLastError(), NULL);
1215 TRACE(("Winsock-error %d, addr %s\n", conn->async.status,
1216 he ? inet_ntoa(*(struct in_addr*)he->h_addr) : "unknown"));
1218 /* An implicit ExitThread() here */
1221 /* complementary of ares_destroy
1223 static void destroy_thread_data (struct connectdata *conn)
1225 if (conn->async.hostname)
1226 free(conn->async.hostname);
1227 if (conn->async.os_specific)
1228 free(conn->async.os_specific);
1229 conn->async.hostname = NULL;
1230 conn->async.os_specific = NULL;
1233 static bool init_gethostbyname_thread (struct connectdata *conn,
1234 const char *hostname, int port)
1236 struct thread_data *td = malloc(sizeof(*td));
1239 SetLastError(ENOMEM);
1243 memset (td, 0, sizeof(*td));
1244 Curl_safefree(conn->async.hostname);
1245 conn->async.hostname = strdup(hostname);
1246 if (!conn->async.hostname) {
1248 SetLastError(ENOMEM);
1252 conn->async.port = port;
1253 conn->async.done = FALSE;
1254 conn->async.status = 0;
1255 conn->async.dns = NULL;
1256 conn->async.os_specific = (void*) td;
1258 td->thread_hnd = CreateThread(NULL, 0, gethostbyname_thread,
1259 conn, 0, &td->thread_id);
1260 if (!td->thread_hnd) {
1261 TRACE(("CreateThread() failed; %lu\n", GetLastError()));
1262 destroy_thread_data(conn);
1268 /* called to check if the name is resolved now */
1269 CURLcode Curl_wait_for_resolv(struct connectdata *conn,
1270 struct Curl_dns_entry **entry)
1272 struct thread_data *td = (struct thread_data*) conn->async.os_specific;
1273 struct SessionHandle *data = conn->data;
1275 DWORD status, ticks;
1278 curlassert (conn && td);
1280 /* now, see if there's a connect timeout or a regular timeout to
1281 use instead of the default one */
1282 timeout = conn->data->set.connecttimeout ? conn->data->set.connecttimeout :
1283 conn->data->set.timeout ? conn->data->set.timeout :
1284 300; /* default name resolve timeout in seconds */
1285 ticks = GetTickCount();
1287 status = WaitForSingleObject(td->thread_hnd, 1000UL*timeout);
1288 if (status == WAIT_OBJECT_0 || status == WAIT_ABANDONED) {
1289 /* Thread finished before timeout; propagate Winsock error to this thread */
1290 WSASetLastError(conn->async.status);
1291 GetExitCodeThread(td->thread_hnd, &td->thread_status);
1292 TRACE(("status %lu, thread-status %08lX\n", status, td->thread_status));
1295 conn->async.done = TRUE;
1296 TerminateThread(td->thread_hnd, (DWORD)-1);
1297 td->thread_status = (DWORD)-1;
1300 TRACE(("gethostbyname_thread() retval %08lX, elapsed %lu ms\n",
1301 td->thread_status, GetTickCount()-ticks));
1304 *entry = conn->async.dns;
1308 if (!conn->async.dns) {
1309 /* a name was not resolved */
1310 if (td->thread_status == (DWORD)-1 || conn->async.status == NO_DATA) {
1311 failf(data, "Resolving host timed out: %s", conn->name);
1312 rc = CURLE_OPERATION_TIMEDOUT;
1314 else if(conn->async.done) {
1315 failf(data, "Could not resolve host: %s (code %lu)", conn->name, conn->async.status);
1316 rc = CURLE_COULDNT_RESOLVE_HOST;
1319 rc = CURLE_OPERATION_TIMEDOUT;
1321 destroy_thread_data(conn);
1322 /* close the connection, since we can't return failure here without
1323 cleaning up this connection properly */
1324 Curl_disconnect(conn);
1329 CURLcode Curl_is_resolved(struct connectdata *conn,
1330 struct Curl_dns_entry **entry)
1334 if (conn->async.done) {
1336 destroy_thread_data(conn);
1337 if (!conn->async.dns) {
1338 TRACE(("Curl_is_resolved(): CURLE_COULDNT_RESOLVE_HOST\n"));
1339 return CURLE_COULDNT_RESOLVE_HOST;
1341 *entry = conn->async.dns;
1342 TRACE(("resolved okay, dns %p\n", *entry));
1345 TRACE(("not yet\n"));