]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/curl/lib/hostip.c
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / curl / lib / hostip.c
1 /***************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
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.
13  * 
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.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * $Id: hostip.c,v 1.130 2004/03/17 12:46:46 bagder Exp $
22  ***************************************************************************/
23
24 #include "setup.h"
25
26 #include <string.h>
27 #include <errno.h>
28
29 #define _REENTRANT
30
31 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
32 #include <malloc.h>
33 #else
34 #ifdef HAVE_SYS_TYPES_H
35 #include <sys/types.h>
36 #endif
37 #ifdef HAVE_SYS_SOCKET_H
38 #include <sys/socket.h>
39 #endif
40 #ifdef HAVE_NETINET_IN_H
41 #include <netinet/in.h>
42 #endif
43 #ifdef HAVE_NETDB_H
44 #include <netdb.h>
45 #endif
46 #ifdef HAVE_ARPA_INET_H
47 #include <arpa/inet.h>
48 #endif
49 #ifdef HAVE_STDLIB_H
50 #include <stdlib.h>     /* required for free() prototypes */
51 #endif
52 #ifdef  VMS
53 #include <in.h>
54 #include <inet.h>
55 #include <stdlib.h>
56 #endif
57 #endif
58
59 #ifdef HAVE_SETJMP_H
60 #include <setjmp.h>
61 #endif
62
63 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
64 #undef in_addr_t
65 #define in_addr_t unsigned long
66 #endif
67
68 #include "urldata.h"
69 #include "sendf.h"
70 #include "hostip.h"
71 #include "hash.h"
72 #include "share.h"
73 #include "url.h"
74
75 #define _MPRINTF_REPLACE /* use our functions only */
76 #include <curl/mprintf.h>
77
78 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
79 #include "inet_ntoa_r.h"
80 #endif
81
82 /* The last #include file should be: */
83 #ifdef CURLDEBUG
84 #include "memdebug.h"
85 #endif
86
87 #ifndef ARES_SUCCESS
88 #define ARES_SUCCESS CURLE_OK
89 #endif
90
91 static curl_hash hostname_cache;
92 static int host_cache_initialized;
93
94 static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
95                                      char *hostname,
96                                      int port,
97                                      int *waitp);
98 #ifndef ENABLE_IPV6
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);
102 #endif
103 #endif
104
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)
110
111 static void trace_it (const char *fmt, ...);
112 #else
113 #define TRACE(x)
114 #endif
115
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);
119 struct thread_data {
120   HANDLE thread_hnd;
121   DWORD  thread_id;
122   DWORD  thread_status;
123 };
124 #endif
125
126 void Curl_global_host_cache_init(void)
127 {
128   if (!host_cache_initialized) {
129     Curl_hash_init(&hostname_cache, 7, Curl_freednsinfo);
130     host_cache_initialized = 1;
131   }
132 }
133
134 curl_hash *Curl_global_host_cache_get(void)
135 {
136   return &hostname_cache;
137 }
138
139 void Curl_global_host_cache_dtor(void)
140 {
141   if (host_cache_initialized) {
142     Curl_hash_clean(&hostname_cache);
143     host_cache_initialized = 0;
144   }
145 }
146
147 /* count the number of characters that an integer takes up */
148 static int _num_chars(int i)
149 {
150   int chars = 0;
151
152   /* While the number divided by 10 is greater than one, 
153    * re-divide the number by 10, and increment the number of 
154    * characters by 1.
155    *
156    * this relies on the fact that for every multiple of 10, 
157    * a new digit is added onto every number
158    */
159   do {
160     chars++;
161
162     i = (int) i / 10;
163   } while (i >= 1);
164
165   return chars;
166 }
167
168 /* Create a hostcache id */
169 static char *
170 create_hostcache_id(char *server, int port, size_t *entry_len)
171 {
172   char *id = NULL;
173
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 */
178   
179   /* Allocate the new entry id */
180   id = malloc(*entry_len + 1); /* 1 extra for the zero terminator */
181   if (!id)
182     return NULL;
183
184   /* Create the new entry */
185   sprintf(id, "%s:%d", server, port);
186
187   return id; /* return pointer to the string */
188 }
189
190 struct hostcache_prune_data {
191   int cache_timeout;
192   time_t now;
193 };
194
195 static int
196 hostcache_timestamp_remove(void *datap, void *hc)
197 {
198   struct hostcache_prune_data *data = 
199     (struct hostcache_prune_data *) datap;
200   struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
201   
202   if ((data->now - c->timestamp < data->cache_timeout) ||
203       c->inuse) {
204     /* please don't remove */
205     return 0;
206   }
207   
208   /* fine, remove */
209   return 1;
210 }
211
212 static void
213 hostcache_prune(curl_hash *hostcache, int cache_timeout, time_t now)
214 {
215   struct hostcache_prune_data user;
216
217   user.cache_timeout = cache_timeout;
218   user.now = now;
219
220   Curl_hash_clean_with_criterium(hostcache, 
221                                  (void *) &user, 
222                                  hostcache_timestamp_remove);
223 }
224
225 void Curl_hostcache_prune(struct SessionHandle *data)
226 {
227   time_t now;
228
229   if(data->set.dns_cache_timeout == -1)
230     /* cache forever means never prune! */
231     return;
232
233   if(data->share)
234     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
235
236   time(&now);
237
238   /* Remove outdated and unused entries from the hostcache */
239   hostcache_prune(data->hostcache,
240                   data->set.dns_cache_timeout,
241                   now);
242
243   if(data->share)
244     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
245 }
246
247 #ifdef HAVE_SIGSETJMP
248 /* Beware this is a global and unique instance */
249 sigjmp_buf curl_jmpenv;
250 #endif
251
252
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
255    cache etc */
256
257 static struct Curl_dns_entry *
258 cache_resolv_response(struct SessionHandle *data,
259                       Curl_addrinfo *addr,
260                       char *hostname,
261                       int port)
262 {
263   char *entry_id;
264   size_t entry_len;
265   struct Curl_dns_entry *dns;
266   time_t now;
267
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 */
271   if (!entry_id)
272     return NULL;
273
274   /* Create a new cache entry */
275   dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry));
276   if (!dns) {
277     Curl_freeaddrinfo(addr);
278     free(entry_id);
279     return NULL;
280   }
281
282   dns->inuse = 0;   /* init to not used */
283   dns->addr = addr; /* this is the address(es) */
284
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);
289   if(!dns) {
290     /* Major badness, run away. When this happens, the 'dns' data has
291        already been cleared up by Curl_hash_add(). */
292     free(entry_id);
293     return NULL;
294   }
295   time(&now);
296
297   dns->timestamp = now; /* used now */
298   dns->inuse++;         /* mark entry as in-use */
299
300   /* free the allocated entry_id again */
301   free(entry_id);
302
303   return dns;
304 }
305
306 /* Resolve a name and return a pointer in the 'entry' argument if one
307    is available.
308
309    Return codes:
310
311    -1 = error, no pointer
312    0 = OK, pointer provided
313    1 = waiting for response, no pointer
314 */
315 int Curl_resolv(struct connectdata *conn,
316                 char *hostname,
317                 int port,
318                 struct Curl_dns_entry **entry)
319 {
320   char *entry_id = NULL;
321   struct Curl_dns_entry *dns = NULL;
322   size_t entry_len;
323   int wait;
324   struct SessionHandle *data = conn->data;
325   CURLcode result;
326
327   /* default to failure */
328   int rc = -1;
329   *entry = NULL;
330
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");
337     return -1;
338   }
339 #endif
340
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 */
344   if (!entry_id)
345     return -1;
346
347   if(data->share)
348     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
349
350   /* See if its already in our dns cache */
351   dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1);
352   
353   if(data->share)
354     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
355
356   /* free the allocated entry_id again */
357   free(entry_id);
358
359   if (!dns) {
360     /* The entry was not in the cache. Resolve it to IP address */
361       
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
364        call */
365     Curl_addrinfo *addr = my_getaddrinfo(conn, hostname, port, &wait);
366     
367     if (!addr) {
368       if(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 */
374           return -1;
375         if(dns)
376           rc = 0; /* pointer provided */
377         else
378           rc = 1; /* no info yet */
379       }
380     }
381     else {
382       if(data->share)
383         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
384
385       /* we got a response, store it in the cache */
386       dns = cache_resolv_response(data, addr, hostname, port);
387       
388       if(data->share)
389         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
390
391       if(!dns)
392         /* returned failure, bail out nicely */
393         Curl_freeaddrinfo(addr);
394       else
395         rc = 0;
396     }
397   }
398   else {
399     dns->inuse++; /* we use it! */
400     rc = 0;
401   }
402
403   *entry = dns;
404
405   return rc;
406 }
407
408 void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
409 {
410   if(data->share)
411     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
412
413   dns->inuse--;
414
415 #ifdef CURLDEBUG
416   if(dns->inuse < 0) {
417     infof(data, "Interal host cache screw-up!");
418     *(char **)0=NULL;
419   }
420 #endif
421
422   if(data->share)
423     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
424 }
425
426 /*
427  * This is a wrapper function for freeing name information in a protocol
428  * independent way. This takes care of using the appropriate underlaying
429  * function.
430  */
431 void Curl_freeaddrinfo(Curl_addrinfo *p)
432 {
433 #ifdef ENABLE_IPV6
434   freeaddrinfo(p);
435 #else
436   free(p); /* works fine for the ARES case too */
437 #endif
438 }
439
440 /*
441  * Free a cache dns entry.
442  */
443 void Curl_freednsinfo(void *freethis)
444 {
445   struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
446
447   Curl_freeaddrinfo(p->addr);
448
449   free(p);
450 }
451
452 /* --- resolve name or IP-number --- */
453
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!
458  */
459 #define CURL_NAMELOOKUP_SIZE 9000
460
461 #ifdef USE_ARES
462
463 CURLcode Curl_multi_ares_fdset(struct connectdata *conn,
464                                fd_set *read_fd_set,
465                                fd_set *write_fd_set,
466                                int *max_fdp)
467
468 {
469   int max = ares_fds(conn->data->state.areschannel,
470                      read_fd_set, write_fd_set);
471   *max_fdp = max;
472
473   return CURLE_OK;
474 }
475
476 /* called to check if the name is resolved now */
477 CURLcode Curl_is_resolved(struct connectdata *conn,
478                           struct Curl_dns_entry **dns)
479 {
480   fd_set read_fds, write_fds;
481   static const struct timeval tv={0,0};
482   int count;
483   struct SessionHandle *data = conn->data;
484   int nfds;
485
486   FD_ZERO(&read_fds);
487   FD_ZERO(&write_fds);
488   nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds);
489
490   count = select(nfds, &read_fds, &write_fds, NULL,
491                  (struct timeval *)&tv);
492
493   if(count)
494     ares_process(data->state.areschannel, &read_fds, &write_fds);
495
496   *dns = NULL;
497
498   if(conn->async.done) {
499     /* we're done, kill the ares handle */
500     if(!conn->async.dns)
501       return CURLE_COULDNT_RESOLVE_HOST;
502     *dns = conn->async.dns;
503   }
504
505   return CURLE_OK;
506 }
507
508 /* This is a function that locks and waits until the name resolve operation
509    has completed.
510
511    If 'entry' is non-NULL, make it point to the resolved dns entry
512
513    Return CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
514    CURLE_OPERATION_TIMEDOUT if a time-out occurred.
515 */
516 CURLcode Curl_wait_for_resolv(struct connectdata *conn,
517                               struct Curl_dns_entry **entry)
518 {
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 */
525
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;
532
533   /* Wait for the name resolve query to complete. */
534   while (1) {
535     int nfds=0;
536     fd_set read_fds, write_fds;
537     struct timeval *tvp, tv, store;
538     int count;
539
540     store.tv_sec = (int)(timeout - elapsed);
541     store.tv_usec = 0;
542     
543     FD_ZERO(&read_fds);
544     FD_ZERO(&write_fds);
545     nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds);
546     if (nfds == 0)
547       break;
548     tvp = ares_timeout(data->state.areschannel,
549                        &store, &tv);
550     count = select(nfds, &read_fds, &write_fds, NULL, tvp);
551     if (count < 0 && errno != EINVAL)
552       break;
553     else if(!count) {
554       /* timeout */
555       timedout = TRUE;
556       break;
557     }
558     ares_process(data->state.areschannel, &read_fds, &write_fds);
559
560     elapsed = Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
561   }
562
563   /* Operation complete, if the lookup was successful we now have the entry
564      in the cache. */
565     
566   if(entry)
567     *entry = conn->async.dns;
568
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;
574     }
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;
579     }
580     else
581       rc = CURLE_OPERATION_TIMEDOUT;
582
583     /* close the connection, since we can't return failure here without
584        cleaning up this connection properly */
585     Curl_disconnect(conn);
586   }
587   
588   return rc;
589 }
590 #endif
591
592 #if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME)
593
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 *" */
597                           int status,
598                           struct hostent *hostent)
599 {
600   struct connectdata *conn = (struct connectdata *)arg;
601   struct Curl_dns_entry *dns = NULL;
602
603   conn->async.done = TRUE;
604   conn->async.status = status;
605
606   if(ARES_SUCCESS == status) {
607     /* we got a resolved name in 'hostent' */
608     char *bufp = (char *)malloc(CURL_NAMELOOKUP_SIZE);
609     if(bufp) {
610
611       /* pack_hostent() copies to and shrinks the target buffer */
612       struct hostent *he = pack_hostent(&bufp, hostent);
613
614       struct SessionHandle *data = conn->data;
615
616       if(data->share)
617         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
618
619       dns = cache_resolv_response(data, he,
620                                   conn->async.hostname, conn->async.port);
621
622       if(data->share)
623         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
624     }
625   }
626
627   conn->async.dns = dns;
628
629   /* The input hostent struct will be freed by ares when we return from this
630      function */
631 }
632 #endif
633
634 #ifdef USE_ARES
635 /*
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.
640  */
641 static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
642                                      char *hostname,
643                                      int port,
644                                      int *waitp)
645 {
646   char *bufp;
647   struct SessionHandle *data = conn->data;
648
649   *waitp = FALSE;
650   
651   bufp = strdup(hostname);
652
653   if(bufp) {
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 */
660
661     /* areschannel is already setup in the Curl_open() function */
662     ares_gethostbyname(data->state.areschannel, hostname, PF_INET,
663                        host_callback, conn);
664       
665     *waitp = TRUE; /* please wait for the response */
666   }
667   return NULL; /* no struct yet */
668 }
669 #endif
670
671 #if !defined(USE_ARES) && !defined(USE_THREADING_GETHOSTBYNAME)
672
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)
678 {
679   (void)conn;
680   *entry=NULL;
681   return CURLE_COULDNT_RESOLVE_HOST;
682 }
683
684 CURLcode Curl_is_resolved(struct connectdata *conn,
685                           struct Curl_dns_entry **dns)
686 {
687   (void)conn;
688   *dns = NULL;
689
690   return CURLE_COULDNT_RESOLVE_HOST;
691 }
692 #endif
693
694 #if !defined(USE_ARES)
695 CURLcode Curl_multi_ares_fdset(struct connectdata *conn,
696                                fd_set *read_fd_set,
697                                fd_set *write_fd_set,
698                                int *max_fdp)
699 {
700   (void)conn;
701   (void)read_fd_set;
702   (void)write_fd_set;
703   (void)max_fdp;
704   return CURLE_OK;
705 }
706 #endif
707
708 #if defined(ENABLE_IPV6) && !defined(USE_ARES)
709
710 #ifdef CURLDEBUG
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
714  * in memdebug.c
715  */
716 int curl_getaddrinfo(char *hostname, char *service,
717                      struct addrinfo *hints,
718                      struct addrinfo **result,
719                      int line, const char *source)
720 {
721   int res=(getaddrinfo)(hostname, service, hints, result);
722   if(0 == res) {
723     /* success */
724     if(logfile)
725       fprintf(logfile, "ADDR %s:%d getaddrinfo() = %p\n",
726               source, line, (void *)*result);
727   }
728   else {
729     if(logfile)
730       fprintf(logfile, "ADDR %s:%d getaddrinfo() failed\n",
731               source, line);
732   }
733   return res;
734 }
735
736 void curl_freeaddrinfo(struct addrinfo *freethis,
737                        int line, const char *source)
738 {
739   (freeaddrinfo)(freethis);
740   if(logfile)
741     fprintf(logfile, "ADDR %s:%d freeaddrinfo(%p)\n",
742             source, line, (void *)freethis);
743 }
744
745 #endif
746
747 /*
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.
752  */
753 static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
754                                      char *hostname,
755                                      int port,
756                                      int *waitp)
757 {
758   struct addrinfo hints, *res;
759   int error;
760   char sbuf[NI_MAXSERV];
761   int s, pf;
762   struct SessionHandle *data = conn->data;
763
764   *waitp=0; /* don't wait, we have the response now */
765
766   /* see if we have an IPv6 stack */
767   s = socket(PF_INET6, SOCK_DGRAM, 0);
768   if (s < 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. */
772     pf = PF_INET;
773   else {
774     /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest
775      * possible checks. And close the socket again.
776      */
777     sclose(s);
778
779     /*
780      * Check if a more limited name resolve has been requested.
781      */
782     switch(data->set.ip_version) {
783     case CURL_IPRESOLVE_V4:
784       pf = PF_INET;
785       break;
786     case CURL_IPRESOLVE_V6:
787       pf = PF_INET6;
788       break;
789     default:
790       pf = PF_UNSPEC;
791       break;
792     }
793   }
794  
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);
801   if (error) {
802     infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port);    
803     return NULL;
804   }
805
806   return res;
807 }
808 #else /* following code is IPv4-only */
809
810 #if !defined(HAVE_GETHOSTBYNAME_R) || defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME)
811 static void hostcache_fixoffset(struct hostent *h, long offset);
812 /*
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!
815  */
816 static struct hostent* pack_hostent(char** buf, struct hostent* orig)
817 {
818   char *bufptr;
819   char *newbuf;
820   struct hostent* copy;
821
822   int i;
823   char *str;
824   size_t len;
825
826   bufptr = *buf;
827   copy = (struct hostent*)bufptr;
828
829   bufptr += sizeof(struct hostent);
830   copy->h_name = bufptr;
831   len = strlen(orig->h_name) + 1;
832   strncpy(bufptr, orig->h_name, len);
833   bufptr += len;
834
835   /* we align on even 64bit boundaries for safety */
836 #define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
837
838   /* This must be aligned properly to work on many CPU architectures! */
839   bufptr = MEMALIGN(bufptr);
840   
841   copy->h_aliases = (char**)bufptr;
842
843   /* Figure out how many aliases there are */
844   for (i = 0; orig->h_aliases && orig->h_aliases[i]; ++i);
845
846   /* Reserve room for the array */
847   bufptr += (i + 1) * sizeof(char*);
848
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;
855       bufptr += len;
856     }
857   }
858   /* if(!orig->h_aliases) i was already set to 0 */
859
860   /* Terminate the alias list with a NULL */
861   copy->h_aliases[i] = NULL;
862
863   copy->h_addrtype = orig->h_addrtype;
864   copy->h_length = orig->h_length;
865     
866   /* align it for (at least) 32bit accesses */
867   bufptr = MEMALIGN(bufptr);
868
869   copy->h_addr_list = (char**)bufptr;
870
871   /* Figure out how many addresses there are */
872   for (i = 0; orig->h_addr_list[i] != NULL; ++i);
873
874   /* Reserve room for the array */
875   bufptr += (i + 1) * sizeof(char*);
876
877   i = 0;
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;
883     bufptr += len;
884     str = orig->h_addr_list[++i];
885   }
886   copy->h_addr_list[i] = NULL;
887
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));
891
892   /* if the alloc moved, we need to adjust things again */
893   if(newbuf != *buf)
894     hostcache_fixoffset((struct hostent*)newbuf, (long)newbuf-(long)*buf);
895
896   /* setup the return */
897   *buf = newbuf;
898   copy = (struct hostent*)newbuf;
899
900   return copy;
901 }
902 #endif
903
904 static void hostcache_fixoffset(struct hostent *h, long offset)
905 {
906   int i=0;
907
908   h->h_name=(char *)((long)h->h_name+offset);
909   if(h->h_aliases) {
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);
914       i++;
915     }
916   }
917
918   h->h_addr_list=(char **)((long)h->h_addr_list+offset);
919   i=0;
920   while(h->h_addr_list[i]) {
921     h->h_addr_list[i]=(char *)((long)h->h_addr_list[i]+offset);
922     i++;
923   }
924 }
925
926 #ifndef USE_ARES
927
928 static char *MakeIP(unsigned long num, char *addr, int addr_len)
929 {
930 #if defined(HAVE_INET_NTOA) || defined(HAVE_INET_NTOA_R)
931   struct in_addr in;
932   in.s_addr = htonl(num);
933
934 #if defined(HAVE_INET_NTOA_R)
935   inet_ntoa_r(in,addr,addr_len);
936 #else
937   strncpy(addr,inet_ntoa(in),addr_len);
938 #endif
939 #else
940   unsigned char *paddr;
941
942   num = htonl(num);  /* htonl() added to avoid endian probs */
943   paddr = (unsigned char *)&num;
944   sprintf(addr, "%u.%u.%u.%u", paddr[0], paddr[1], paddr[2], paddr[3]);
945 #endif
946   return (addr);
947 }
948
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
951    considerably. */
952 static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
953                                      char *hostname,
954                                      int port,
955                                      int *waitp)
956 {
957   struct hostent *h = NULL;
958   in_addr_t in;
959   struct SessionHandle *data = conn->data;
960   (void)port; /* unused in IPv4 code */
961
962   *waitp = 0; /* don't wait, we act synchronously */
963
964   in=inet_addr(hostname);
965   if (in != CURL_INADDR_NONE) {
966     struct in_addr *addrentry;
967     struct namebuf {
968         struct hostent hostentry;
969         char *h_addr_list[2];
970         struct in_addr addrentry;
971         char h_name[128];
972     } *buf = (struct namebuf *)malloc(sizeof(struct namebuf));
973     if(!buf)
974       return NULL; /* major failure */
975
976     h = &buf->hostentry;
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));
986   }
987 #if defined(HAVE_GETHOSTBYNAME_R)
988   else {
989     int h_errnop;
990     int res=ERANGE;
991     int step_size=200;
992     int *buf = (int *)malloc(CURL_NAMELOOKUP_SIZE);
993     if(!buf)
994       return NULL; /* major failure */
995
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 */
1002     while(!h) {
1003       h = gethostbyname_r(hostname,
1004                           (struct hostent *)buf,
1005                           (char *)buf + sizeof(struct hostent),
1006                           step_size - sizeof(struct hostent),
1007                           &h_errnop);
1008
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. */
1013
1014       if(h || (errno != ERANGE))
1015         break;
1016       
1017       step_size+=200;
1018     }
1019
1020 #ifdef CURLDEBUG
1021     infof(data, "gethostbyname_r() uses %d bytes\n", step_size);
1022 #endif
1023
1024     if(h) {
1025       int offset;
1026       h=(struct hostent *)realloc(buf, step_size);
1027       offset=(long)h-(long)buf;
1028       hostcache_fixoffset(h, offset);
1029       buf=(int *)h;
1030     }
1031     else
1032 #endif /* HAVE_GETHOSTBYNAME_R_5 */
1033 #ifdef HAVE_GETHOSTBYNAME_R_6
1034     /* Linux */
1035     do {
1036       res=gethostbyname_r(hostname,
1037                           (struct hostent *)buf,
1038                           (char *)buf + sizeof(struct hostent),
1039                           step_size - sizeof(struct hostent),
1040                           &h, /* DIFFERENCE */
1041                           &h_errnop);
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
1045          problem.
1046
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
1050          glibc.
1051
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).
1055
1056          If anyone has a better fix, please tell us!
1057
1058          -------------------------------------------------------------------
1059
1060          On October 23rd 2003, Dan C dug up more details on the mysteries of
1061          gethostbyname_r() in glibc:
1062
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!
1067
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.
1071
1072       */
1073
1074       if(((ERANGE == res) || (EAGAIN == res)) ||
1075          ((res<0) && ((ERANGE == errno) || (EAGAIN == errno))))
1076         step_size+=200;
1077       else
1078         break;
1079     } while(step_size <= CURL_NAMELOOKUP_SIZE);
1080
1081     if(!h) /* failure */
1082       res=1;
1083     
1084 #ifdef CURLDEBUG
1085     infof(data, "gethostbyname_r() uses %d bytes\n", step_size);
1086 #endif
1087     if(!res) {
1088       int offset;
1089       h=(struct hostent *)realloc(buf, step_size);
1090       offset=(long)h-(long)buf;
1091       hostcache_fixoffset(h, offset);
1092       buf=(int *)h;
1093     }
1094     else
1095 #endif/* HAVE_GETHOSTBYNAME_R_6 */
1096 #ifdef HAVE_GETHOSTBYNAME_R_3
1097     /* AIX, Digital Unix/Tru64, HPUX 10, more? */
1098
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.
1107        
1108        This AIX 4.3 or later detection is all made in the configure script.
1109
1110        Troels Walsted Hansen helped us work this out on March 3rd, 2003. */
1111
1112     if(CURL_NAMELOOKUP_SIZE >=
1113        (sizeof(struct hostent)+sizeof(struct hostent_data))) {
1114
1115       /* August 22nd, 2000: Albert Chin-A-Young brought an updated version
1116        * that should work! September 20: Richard Prescott worked on the buffer
1117        * size dilemma. */
1118
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 */
1124     }
1125     else
1126       res = -1; /* failure, too smallish buffer size */
1127
1128     if(!res) { /* success */
1129
1130       h = (struct hostent*)buf; /* result expected in h */
1131
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. */
1139     }    
1140     else
1141 #endif /* HAVE_GETHOSTBYNAME_R_3 */
1142       {
1143       infof(data, "gethostbyname_r(2) failed for %s\n", hostname);
1144       h = NULL; /* set return code to NULL */
1145       free(buf);
1146     }
1147 #else /* HAVE_GETHOSTBYNAME_R */
1148   else {
1149
1150 #ifdef USE_THREADING_GETHOSTBYNAME
1151     if (init_gethostbyname_thread(conn,hostname,port)) {
1152        *waitp = TRUE;  /* please wait for the response */
1153        return NULL;
1154     }
1155     infof(data, "init_gethostbyname_thread() failed for %s; code %lu\n",
1156           hostname, GetLastError());
1157 #endif
1158     h = gethostbyname(hostname);
1159     if (!h)
1160       infof(data, "gethostbyname(2) failed for %s\n", hostname);
1161     else {
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
1165          that */
1166       h = pack_hostent(&buf, h);
1167     }
1168 #endif /*HAVE_GETHOSTBYNAME_R */
1169   }
1170
1171   return h;
1172 }
1173
1174 #endif /* end of IPv4-specific code */
1175
1176 #endif /* end of !USE_ARES */
1177
1178
1179 #if defined(USE_THREADING_GETHOSTBYNAME)
1180 #ifdef DEBUG_THREADING_GETHOSTBYNAME
1181 static void trace_it (const char *fmt, ...)
1182 {
1183   static int do_trace = -1;
1184   va_list args;
1185
1186   if (do_trace == -1)
1187     do_trace = getenv("CURL_TRACE") ? 1 : 0;
1188   if (!do_trace)
1189     return;
1190   va_start (args, fmt);
1191   vfprintf (stderr, fmt, args);
1192   fflush (stderr);
1193   va_end (args);
1194 }
1195 #endif
1196
1197 /* For builds without ARES/USE_IPV6, create a resolver thread and wait on it.
1198  */
1199 static DWORD WINAPI gethostbyname_thread (void *arg)
1200 {
1201   struct connectdata *conn = (struct connectdata*) arg;
1202   struct hostent *he;
1203   int    rc;
1204
1205   WSASetLastError (conn->async.status = NO_DATA); /* pending status */
1206   he = gethostbyname (conn->async.hostname);
1207   if (he) {
1208     host_callback(conn, ARES_SUCCESS, he);
1209     rc = 1;
1210   }
1211   else {
1212     host_callback(conn, (int)WSAGetLastError(), NULL);
1213     rc = 0;
1214   }
1215   TRACE(("Winsock-error %d, addr %s\n", conn->async.status,
1216          he ? inet_ntoa(*(struct in_addr*)he->h_addr) : "unknown"));
1217   return (rc);
1218   /* An implicit ExitThread() here */
1219 }
1220
1221 /* complementary of ares_destroy
1222  */
1223 static void destroy_thread_data (struct connectdata *conn)
1224 {
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;
1231 }
1232
1233 static bool init_gethostbyname_thread (struct connectdata *conn,
1234                                        const char *hostname, int port)
1235 {
1236   struct thread_data *td = malloc(sizeof(*td));
1237
1238   if (!td) {
1239     SetLastError(ENOMEM);
1240     return (0);
1241   }
1242
1243   memset (td, 0, sizeof(*td));
1244   Curl_safefree(conn->async.hostname);
1245   conn->async.hostname = strdup(hostname);
1246   if (!conn->async.hostname) {
1247     free(td);
1248     SetLastError(ENOMEM);
1249     return (0);
1250   }
1251
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;
1257
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);
1263      return (0);
1264   }
1265   return (1);
1266 }
1267
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)
1271 {
1272   struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
1273   struct SessionHandle *data = conn->data;
1274   long   timeout;
1275   DWORD  status, ticks;
1276   CURLcode rc;
1277
1278   curlassert (conn && td);
1279
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();
1286
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));
1293   }
1294   else {
1295      conn->async.done = TRUE;
1296      TerminateThread(td->thread_hnd, (DWORD)-1);
1297      td->thread_status = (DWORD)-1;
1298   }
1299
1300   TRACE(("gethostbyname_thread() retval %08lX, elapsed %lu ms\n",
1301          td->thread_status, GetTickCount()-ticks));
1302
1303   if(entry)
1304     *entry = conn->async.dns;
1305
1306   rc = CURLE_OK;
1307
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;
1313     }
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;
1317     }
1318     else
1319       rc = CURLE_OPERATION_TIMEDOUT;
1320
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);
1325   }
1326   return (rc);
1327 }
1328
1329 CURLcode Curl_is_resolved(struct connectdata *conn,
1330                           struct Curl_dns_entry **entry)
1331 {
1332   *entry = NULL;
1333
1334   if (conn->async.done) {
1335     /* we're 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;
1340     }
1341     *entry = conn->async.dns;
1342     TRACE(("resolved okay, dns %p\n", *entry));
1343   }
1344   else
1345     TRACE(("not yet\n"));
1346   return CURLE_OK;
1347 }
1348
1349 #endif