1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2008, 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: hostthre.c,v 1.57 2008-11-06 17:19:57 yangtse Exp $
22  ***************************************************************************/
23 
24 #include "setup.h"
25 
26 #include <string.h>
27 #include <errno.h>
28 
29 #ifdef NEED_MALLOC_H
30 #include <malloc.h>
31 #endif
32 #ifdef HAVE_SYS_SOCKET_H
33 #include <sys/socket.h>
34 #endif
35 #ifdef HAVE_NETINET_IN_H
36 #include <netinet/in.h>
37 #endif
38 #ifdef HAVE_NETDB_H
39 #include <netdb.h>
40 #endif
41 #ifdef HAVE_ARPA_INET_H
42 #include <arpa/inet.h>
43 #endif
44 #ifdef HAVE_STDLIB_H
45 #include <stdlib.h>     /* required for free() prototypes */
46 #endif
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>     /* for the close() proto */
49 #endif
50 #ifdef  VMS
51 #include <in.h>
52 #include <inet.h>
53 #include <stdlib.h>
54 #endif
55 
56 #ifdef HAVE_PROCESS_H
57 #include <process.h>
58 #endif
59 
60 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
61 #undef in_addr_t
62 #define in_addr_t unsigned long
63 #endif
64 
65 #include "urldata.h"
66 #include "sendf.h"
67 #include "hostip.h"
68 #include "hash.h"
69 #include "share.h"
70 #include "strerror.h"
71 #include "url.h"
72 #include "multiif.h"
73 #include "inet_pton.h"
74 
75 #define _MPRINTF_REPLACE /* use our functions only */
76 #include <curl/mprintf.h>
77 
78 #include "inet_ntop.h"
79 
80 #include "memory.h"
81 /* The last #include file should be: */
82 #include "memdebug.h"
83 
84 #if defined(_MSC_VER) && defined(CURL_NO__BEGINTHREADEX)
85 #pragma message ("No _beginthreadex() available in this RTL")
86 #endif
87 
88 /***********************************************************************
89  * Only for Windows threaded name resolves builds
90  **********************************************************************/
91 #ifdef CURLRES_THREADED
92 
93 /* This function is used to init a threaded resolve */
94 static bool init_resolve_thread(struct connectdata *conn,
95                                 const char *hostname, int port,
96                                 const struct addrinfo *hints);
97 
98 #ifdef CURLRES_IPV4
99   #define THREAD_FUNC  gethostbyname_thread
100   #define THREAD_NAME "gethostbyname_thread"
101 #else
102   #define THREAD_FUNC  getaddrinfo_thread
103   #define THREAD_NAME "getaddrinfo_thread"
104 #endif
105 
106 struct thread_data {
107   HANDLE thread_hnd;
108   unsigned thread_id;
109   DWORD  thread_status;
110   curl_socket_t dummy_sock;   /* dummy for Curl_resolv_fdset() */
111   HANDLE mutex_waiting;  /* marks that we are still waiting for a resolve */
112   HANDLE event_resolved; /* marks that the thread obtained the information */
113   HANDLE event_thread_started; /* marks that the thread has initialized and
114                                   started */
115   HANDLE mutex_terminate; /* serializes access to flag_terminate */
116   HANDLE event_terminate; /* flag for thread to terminate instead of calling
117                              callbacks */
118 #ifdef CURLRES_IPV6
119   struct addrinfo hints;
120 #endif
121 };
122 
123 /* Data for synchronization between resolver thread and its parent */
124 struct thread_sync_data {
125   HANDLE mutex_waiting;   /* thread_data.mutex_waiting duplicate */
126   HANDLE mutex_terminate; /* thread_data.mutex_terminate duplicate */
127   HANDLE event_terminate; /* thread_data.event_terminate duplicate */
128   char * hostname;        /* hostname to resolve, Curl_async.hostname
129                              duplicate */
130 };
131 
132 /* Destroy resolver thread synchronization data */
133 static
destroy_thread_sync_data(struct thread_sync_data * tsd)134 void destroy_thread_sync_data(struct thread_sync_data * tsd)
135 {
136   if(tsd->hostname)
137     free(tsd->hostname);
138   if(tsd->event_terminate)
139     CloseHandle(tsd->event_terminate);
140   if(tsd->mutex_terminate)
141     CloseHandle(tsd->mutex_terminate);
142   if(tsd->mutex_waiting)
143     CloseHandle(tsd->mutex_waiting);
144   memset(tsd,0,sizeof(*tsd));
145 }
146 
147 /* Initialize resolver thread synchronization data */
148 static
init_thread_sync_data(struct thread_data * td,const char * hostname,struct thread_sync_data * tsd)149 BOOL init_thread_sync_data(struct thread_data * td,
150                            const char * hostname,
151                            struct thread_sync_data * tsd)
152 {
153   HANDLE curr_proc = GetCurrentProcess();
154 
155   memset(tsd, 0, sizeof(*tsd));
156   if(!DuplicateHandle(curr_proc, td->mutex_waiting,
157                        curr_proc, &tsd->mutex_waiting, 0, FALSE,
158                        DUPLICATE_SAME_ACCESS)) {
159     /* failed to duplicate the mutex, no point in continuing */
160     destroy_thread_sync_data(tsd);
161     return FALSE;
162   }
163   if(!DuplicateHandle(curr_proc, td->mutex_terminate,
164                        curr_proc, &tsd->mutex_terminate, 0, FALSE,
165                        DUPLICATE_SAME_ACCESS)) {
166     /* failed to duplicate the mutex, no point in continuing */
167     destroy_thread_sync_data(tsd);
168     return FALSE;
169   }
170   if(!DuplicateHandle(curr_proc, td->event_terminate,
171                        curr_proc, &tsd->event_terminate, 0, FALSE,
172                        DUPLICATE_SAME_ACCESS)) {
173     /* failed to duplicate the event, no point in continuing */
174     destroy_thread_sync_data(tsd);
175     return FALSE;
176   }
177   /* Copying hostname string because original can be destroyed by parent
178    * thread during gethostbyname execution.
179    */
180   tsd->hostname = strdup(hostname);
181   if(!tsd->hostname) {
182     /* Memory allocation failed */
183     destroy_thread_sync_data(tsd);
184     return FALSE;
185   }
186   return TRUE;
187 }
188 
189 /* acquire resolver thread synchronization */
190 static
acquire_thread_sync(struct thread_sync_data * tsd)191 BOOL acquire_thread_sync(struct thread_sync_data * tsd)
192 {
193   /* is the thread initiator still waiting for us ? */
194   if(WaitForSingleObject(tsd->mutex_waiting, 0) == WAIT_TIMEOUT) {
195     /* yes, it is */
196 
197     /* Waiting access to event_terminate */
198     if(WaitForSingleObject(tsd->mutex_terminate, INFINITE) != WAIT_OBJECT_0) {
199       /* Something went wrong - now just ignoring */
200     }
201     else {
202       if(WaitForSingleObject(tsd->event_terminate, 0) != WAIT_TIMEOUT) {
203         /* Parent thread signaled us to terminate.
204          * This means that all data in conn->async is now destroyed
205          * and we cannot use it.
206          */
207       }
208       else {
209         return TRUE;
210       }
211     }
212   }
213   return FALSE;
214 }
215 
216 /* release resolver thread synchronization */
217 static
release_thread_sync(struct thread_sync_data * tsd)218 void release_thread_sync(struct thread_sync_data * tsd)
219 {
220   ReleaseMutex(tsd->mutex_terminate);
221 }
222 
223 #if defined(CURLRES_IPV4)
224 /*
225  * gethostbyname_thread() resolves a name, calls the Curl_addrinfo4_callback
226  * and then exits.
227  *
228  * For builds without ARES/ENABLE_IPV6, create a resolver thread and wait on
229  * it.
230  */
gethostbyname_thread(void * arg)231 static unsigned __stdcall gethostbyname_thread (void *arg)
232 {
233   struct connectdata *conn = (struct connectdata*) arg;
234   struct thread_data *td = (struct thread_data*) conn->async.os_specific;
235   struct hostent *he;
236   int    rc = 0;
237 
238   /* Duplicate the passed mutex and event handles.
239    * This allows us to use it even after the container gets destroyed
240    * due to a resolver timeout.
241    */
242   struct thread_sync_data tsd = { 0,0,0,NULL };
243 
244   if(!init_thread_sync_data(td, conn->async.hostname, &tsd)) {
245     /* thread synchronization data initialization failed */
246     return (unsigned)-1;
247   }
248 
249   conn->async.status = NO_DATA;  /* pending status */
250   SET_SOCKERRNO(conn->async.status);
251 
252   /* Signaling that we have initialized all copies of data and handles we
253      need */
254   SetEvent(td->event_thread_started);
255 
256   he = gethostbyname (tsd.hostname);
257 
258   /* is parent thread waiting for us and are we able to access conn members? */
259   if(acquire_thread_sync(&tsd)) {
260     /* Mark that we have obtained the information, and that we are calling
261      * back with it. */
262     SetEvent(td->event_resolved);
263     if(he) {
264       rc = Curl_addrinfo4_callback(conn, CURL_ASYNC_SUCCESS, he);
265     }
266     else {
267       rc = Curl_addrinfo4_callback(conn, SOCKERRNO, NULL);
268     }
269     release_thread_sync(&tsd);
270   }
271 
272   /* clean up */
273   destroy_thread_sync_data(&tsd);
274 
275   return (rc);
276   /* An implicit _endthreadex() here */
277 }
278 
279 #elif defined(CURLRES_IPV6)
280 
281 /*
282  * getaddrinfo_thread() resolves a name, calls Curl_addrinfo6_callback and then
283  * exits.
284  *
285  * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
286  * and wait on it.
287  */
getaddrinfo_thread(void * arg)288 static unsigned __stdcall getaddrinfo_thread (void *arg)
289 {
290   struct connectdata *conn = (struct connectdata*) arg;
291   struct thread_data *td   = (struct thread_data*) conn->async.os_specific;
292   Curl_addrinfo      *res;
293   char   service [NI_MAXSERV];
294   int    rc;
295   struct addrinfo hints = td->hints;
296 
297   /* Duplicate the passed mutex handle.
298    * This allows us to use it even after the container gets destroyed
299    * due to a resolver timeout.
300    */
301   struct thread_sync_data tsd = { 0,0,0,NULL };
302 
303   if(!init_thread_sync_data(td, conn->async.hostname, &tsd)) {
304     /* thread synchronization data initialization failed */
305     return -1;
306   }
307 
308   itoa(conn->async.port, service, 10);
309 
310   conn->async.status = NO_DATA;  /* pending status */
311   SET_SOCKERRNO(conn->async.status);
312 
313   /* Signaling that we have initialized all copies of data and handles we
314      need */
315   SetEvent(td->event_thread_started);
316 
317   rc = Curl_getaddrinfo_ex(tsd.hostname, service, &hints, &res);
318 
319   /* is parent thread waiting for us and are we able to access conn members? */
320   if(acquire_thread_sync(&tsd)) {
321     /* Mark that we have obtained the information, and that we are calling
322        back with it. */
323     SetEvent(td->event_resolved);
324 
325     if(rc == 0) {
326       rc = Curl_addrinfo6_callback(conn, CURL_ASYNC_SUCCESS, res);
327     }
328     else {
329       rc = Curl_addrinfo6_callback(conn, SOCKERRNO, NULL);
330     }
331     release_thread_sync(&tsd);
332   }
333 
334   /* clean up */
335   destroy_thread_sync_data(&tsd);
336 
337   return (rc);
338   /* An implicit _endthreadex() here */
339 }
340 #endif
341 
342 /*
343  * Curl_destroy_thread_data() cleans up async resolver data and thread handle.
344  * Complementary of ares_destroy.
345  */
Curl_destroy_thread_data(struct Curl_async * async)346 void Curl_destroy_thread_data (struct Curl_async *async)
347 {
348   if(async->hostname)
349     free(async->hostname);
350 
351   if(async->os_specific) {
352     struct thread_data *td = (struct thread_data*) async->os_specific;
353     curl_socket_t sock = td->dummy_sock;
354 
355     if(td->mutex_terminate && td->event_terminate) {
356       /* Signaling resolver thread to terminate */
357       if(WaitForSingleObject(td->mutex_terminate, INFINITE) == WAIT_OBJECT_0) {
358         SetEvent(td->event_terminate);
359         ReleaseMutex(td->mutex_terminate);
360       }
361       else {
362         /* Something went wrong - just ignoring it */
363       }
364     }
365 
366     if(td->mutex_terminate)
367       CloseHandle(td->mutex_terminate);
368     if(td->event_terminate)
369       CloseHandle(td->event_terminate);
370     if(td->event_thread_started)
371       CloseHandle(td->event_thread_started);
372 
373     if(sock != CURL_SOCKET_BAD)
374       sclose(sock);
375 
376     /* destroy the synchronization objects */
377     if(td->mutex_waiting)
378       CloseHandle(td->mutex_waiting);
379     td->mutex_waiting = NULL;
380     if(td->event_resolved)
381       CloseHandle(td->event_resolved);
382 
383     if(td->thread_hnd)
384       CloseHandle(td->thread_hnd);
385 
386     free(async->os_specific);
387   }
388   async->hostname = NULL;
389   async->os_specific = NULL;
390 }
391 
392 /*
393  * init_resolve_thread() starts a new thread that performs the actual
394  * resolve. This function returns before the resolve is done.
395  *
396  * Returns FALSE in case of failure, otherwise TRUE.
397  */
init_resolve_thread(struct connectdata * conn,const char * hostname,int port,const struct addrinfo * hints)398 static bool init_resolve_thread (struct connectdata *conn,
399                                  const char *hostname, int port,
400                                  const struct addrinfo *hints)
401 {
402   struct thread_data *td = calloc(sizeof(*td), 1);
403   HANDLE thread_and_event[2] = {0};
404 
405   if(!td) {
406     SET_ERRNO(ENOMEM);
407     return FALSE;
408   }
409 
410   Curl_safefree(conn->async.hostname);
411   conn->async.hostname = strdup(hostname);
412   if(!conn->async.hostname) {
413     free(td);
414     SET_ERRNO(ENOMEM);
415     return FALSE;
416   }
417 
418   conn->async.port = port;
419   conn->async.done = FALSE;
420   conn->async.status = 0;
421   conn->async.dns = NULL;
422   conn->async.os_specific = (void*) td;
423   td->dummy_sock = CURL_SOCKET_BAD;
424 
425   /* Create the mutex used to inform the resolver thread that we're
426    * still waiting, and take initial ownership.
427    */
428   td->mutex_waiting = CreateMutex(NULL, TRUE, NULL);
429   if(td->mutex_waiting == NULL) {
430     Curl_destroy_thread_data(&conn->async);
431     SET_ERRNO(EAGAIN);
432     return FALSE;
433   }
434 
435   /* Create the event that the thread uses to inform us that it's
436    * done resolving. Do not signal it.
437    */
438   td->event_resolved = CreateEvent(NULL, TRUE, FALSE, NULL);
439   if(td->event_resolved == NULL) {
440     Curl_destroy_thread_data(&conn->async);
441     SET_ERRNO(EAGAIN);
442     return FALSE;
443   }
444   /* Create the mutex used to serialize access to event_terminated
445    * between us and resolver thread.
446    */
447   td->mutex_terminate = CreateMutex(NULL, FALSE, NULL);
448   if(td->mutex_terminate == NULL) {
449     Curl_destroy_thread_data(&conn->async);
450     SET_ERRNO(EAGAIN);
451     return FALSE;
452   }
453   /* Create the event used to signal thread that it should terminate.
454    */
455   td->event_terminate = CreateEvent(NULL, TRUE, FALSE, NULL);
456   if(td->event_terminate == NULL) {
457     Curl_destroy_thread_data(&conn->async);
458     SET_ERRNO(EAGAIN);
459     return FALSE;
460   }
461   /* Create the event used by thread to inform it has initialized its own data.
462    */
463   td->event_thread_started = CreateEvent(NULL, TRUE, FALSE, NULL);
464   if(td->event_thread_started == NULL) {
465     Curl_destroy_thread_data(&conn->async);
466     SET_ERRNO(EAGAIN);
467     return FALSE;
468   }
469 
470 #ifdef _WIN32_WCE
471   td->thread_hnd = (HANDLE) CreateThread(NULL, 0,
472                                          (LPTHREAD_START_ROUTINE) THREAD_FUNC,
473                                          conn, 0, &td->thread_id);
474 #else
475   td->thread_hnd = (HANDLE) _beginthreadex(NULL, 0, THREAD_FUNC,
476                                            conn, 0, &td->thread_id);
477 #endif
478 
479 #ifdef CURLRES_IPV6
480   DEBUGASSERT(hints);
481   td->hints = *hints;
482 #else
483   (void) hints;
484 #endif
485 
486   if(!td->thread_hnd) {
487 #ifndef _WIN32_WCE
488      SET_ERRNO(errno);
489 #endif
490      Curl_destroy_thread_data(&conn->async);
491      return FALSE;
492   }
493   /* Waiting until the thread will initialize its data or it will exit due errors.
494    */
495   thread_and_event[0] = td->thread_hnd;
496   thread_and_event[1] = td->event_thread_started;
497   if(WaitForMultipleObjects(sizeof(thread_and_event) /
498                              sizeof(thread_and_event[0]),
499                              (const HANDLE*)thread_and_event, FALSE,
500                              INFINITE) == WAIT_FAILED) {
501     /* The resolver thread has been created,
502      * most probably it works now - ignoring this "minor" error
503      */
504   }
505   /* This socket is only to keep Curl_resolv_fdset() and select() happy;
506    * should never become signalled for read/write since it's unbound but
507    * Windows needs atleast 1 socket in select().
508    */
509   td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0);
510   return TRUE;
511 }
512 
513 
514 /*
515  * Curl_wait_for_resolv() waits for a resolve to finish. This function should
516  * be avoided since using this risk getting the multi interface to "hang".
517  *
518  * If 'entry' is non-NULL, make it point to the resolved dns entry
519  *
520  * This is the version for resolves-in-a-thread.
521  */
Curl_wait_for_resolv(struct connectdata * conn,struct Curl_dns_entry ** entry)522 CURLcode Curl_wait_for_resolv(struct connectdata *conn,
523                               struct Curl_dns_entry **entry)
524 {
525   struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
526   struct SessionHandle *data = conn->data;
527   long   timeout;
528   DWORD  status;
529   CURLcode rc;
530 
531   DEBUGASSERT(conn && td);
532 
533   /* now, see if there's a connect timeout or a regular timeout to
534      use instead of the default one */
535   timeout =
536     conn->data->set.connecttimeout ? conn->data->set.connecttimeout :
537     conn->data->set.timeout ? conn->data->set.timeout :
538     CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
539 
540   /* wait for the thread to resolve the name */
541   status = WaitForSingleObject(td->event_resolved, timeout);
542 
543   /* mark that we are now done waiting */
544   ReleaseMutex(td->mutex_waiting);
545 
546   /* close our handle to the mutex, no point in hanging on to it */
547   CloseHandle(td->mutex_waiting);
548   td->mutex_waiting = NULL;
549 
550   /* close the event handle, it's useless now */
551   CloseHandle(td->event_resolved);
552   td->event_resolved = NULL;
553 
554   /* has the resolver thread succeeded in resolving our query ? */
555   if(status == WAIT_OBJECT_0) {
556     /* wait for the thread to exit, it's in the callback sequence */
557     if(WaitForSingleObject(td->thread_hnd, 5000) == WAIT_TIMEOUT) {
558       TerminateThread(td->thread_hnd, 0);
559       conn->async.done = TRUE;
560       td->thread_status = (DWORD)-1;
561     }
562     else {
563       /* Thread finished before timeout; propagate Winsock error to this
564        * thread.  'conn->async.done = TRUE' is set in
565        * Curl_addrinfo4/6_callback().
566        */
567       SET_SOCKERRNO(conn->async.status);
568       GetExitCodeThread(td->thread_hnd, &td->thread_status);
569     }
570   }
571   else {
572     conn->async.done = TRUE;
573     td->thread_status = (DWORD)-1;
574   }
575 
576   if(entry)
577     *entry = conn->async.dns;
578 
579   rc = CURLE_OK;
580 
581   if(!conn->async.dns) {
582     /* a name was not resolved */
583     if(td->thread_status == CURLE_OUT_OF_MEMORY) {
584       rc = CURLE_OUT_OF_MEMORY;
585       failf(data, "Could not resolve host: %s", curl_easy_strerror(rc));
586     }
587     else if(conn->async.done) {
588       if(conn->bits.httpproxy) {
589         failf(data, "Could not resolve proxy: %s; %s",
590               conn->proxy.dispname, Curl_strerror(conn, conn->async.status));
591         rc = CURLE_COULDNT_RESOLVE_PROXY;
592       }
593       else {
594         failf(data, "Could not resolve host: %s; %s",
595               conn->host.name, Curl_strerror(conn, conn->async.status));
596         rc = CURLE_COULDNT_RESOLVE_HOST;
597       }
598     }
599     else if(td->thread_status == (DWORD)-1 || conn->async.status == NO_DATA) {
600       failf(data, "Resolving host timed out: %s", conn->host.name);
601       rc = CURLE_OPERATION_TIMEDOUT;
602     }
603     else
604       rc = CURLE_OPERATION_TIMEDOUT;
605   }
606 
607   Curl_destroy_thread_data(&conn->async);
608 
609   if(!conn->async.dns)
610     conn->bits.close = TRUE;
611 
612   return (rc);
613 }
614 
615 /*
616  * Curl_is_resolved() is called repeatedly to check if a previous name resolve
617  * request has completed. It should also make sure to time-out if the
618  * operation seems to take too long.
619  */
Curl_is_resolved(struct connectdata * conn,struct Curl_dns_entry ** entry)620 CURLcode Curl_is_resolved(struct connectdata *conn,
621                           struct Curl_dns_entry **entry)
622 {
623   struct SessionHandle *data = conn->data;
624 
625   *entry = NULL;
626 
627   if(conn->async.done) {
628     /* we're done */
629     Curl_destroy_thread_data(&conn->async);
630     if(!conn->async.dns) {
631       failf(data, "Could not resolve host: %s; %s",
632             conn->host.name, Curl_strerror(conn, conn->async.status));
633       return CURLE_COULDNT_RESOLVE_HOST;
634     }
635     *entry = conn->async.dns;
636   }
637   return CURLE_OK;
638 }
639 
Curl_resolv_getsock(struct connectdata * conn,curl_socket_t * socks,int numsocks)640 int Curl_resolv_getsock(struct connectdata *conn,
641                         curl_socket_t *socks,
642                         int numsocks)
643 {
644   const struct thread_data *td =
645     (const struct thread_data *) conn->async.os_specific;
646 
647   if(td && td->dummy_sock != CURL_SOCKET_BAD) {
648     if(numsocks) {
649       /* return one socket waiting for writable, even though this is just
650          a dummy */
651       socks[0] = td->dummy_sock;
652       return GETSOCK_WRITESOCK(0);
653     }
654   }
655   return 0;
656 }
657 
658 #ifdef CURLRES_IPV4
659 /*
660  * Curl_getaddrinfo() - for Windows threading without ENABLE_IPV6.
661  */
Curl_getaddrinfo(struct connectdata * conn,const char * hostname,int port,int * waitp)662 Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
663                                 const char *hostname,
664                                 int port,
665                                 int *waitp)
666 {
667   struct hostent *h = NULL;
668   struct SessionHandle *data = conn->data;
669   struct in_addr in;
670 
671   *waitp = 0; /* don't wait, we act synchronously */
672 
673   if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
674     /* This is a dotted IP address 123.123.123.123-style */
675     return Curl_ip2addr(AF_INET, &in, hostname, port);
676 
677   /* fire up a new resolver thread! */
678   if(init_resolve_thread(conn, hostname, port, NULL)) {
679     *waitp = TRUE;  /* please wait for the response */
680     return NULL;
681   }
682 
683   /* fall-back to blocking version */
684   infof(data, "init_resolve_thread() failed for %s; %s\n",
685         hostname, Curl_strerror(conn, ERRNO));
686 
687   h = gethostbyname(hostname);
688   if(!h) {
689     infof(data, "gethostbyname(2) failed for %s:%d; %s\n",
690           hostname, port, Curl_strerror(conn, SOCKERRNO));
691     return NULL;
692   }
693   return Curl_he2ai(h, port);
694 }
695 #endif /* CURLRES_IPV4 */
696 
697 #ifdef CURLRES_IPV6
698 /*
699  * Curl_getaddrinfo() - for Windows threading IPv6 enabled
700  */
Curl_getaddrinfo(struct connectdata * conn,const char * hostname,int port,int * waitp)701 Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
702                                 const char *hostname,
703                                 int port,
704                                 int *waitp)
705 {
706   struct addrinfo hints;
707   Curl_addrinfo *res;
708   int error;
709   char sbuf[NI_MAXSERV];
710   int pf;
711   struct SessionHandle *data = conn->data;
712 
713   *waitp = FALSE; /* default to synch response */
714 
715   /*
716    * Check if a limited name resolve has been requested.
717    */
718   switch(data->set.ip_version) {
719   case CURL_IPRESOLVE_V4:
720     pf = PF_INET;
721     break;
722   case CURL_IPRESOLVE_V6:
723     pf = PF_INET6;
724     break;
725   default:
726     pf = PF_UNSPEC;
727     break;
728   }
729 
730   if (pf != PF_INET) {
731     /* see if we have an IPv6 stack */
732     curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
733     if(s == CURL_SOCKET_BAD) {
734       /* Some non-IPv6 stacks have been found to make very slow name resolves
735        * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if
736        * the stack seems to be a non-ipv6 one. */
737 
738       pf = PF_INET;
739     }
740     else {
741       /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest
742        * possible checks. And close the socket again.
743        */
744       sclose(s);
745     }
746   }
747 
748   memset(&hints, 0, sizeof(hints));
749   hints.ai_family = pf;
750   hints.ai_socktype = conn->socktype;
751 #if 0 /* removed nov 8 2005 before 7.15.1 */
752   hints.ai_flags = AI_CANONNAME;
753 #endif
754   itoa(port, sbuf, 10);
755 
756   /* fire up a new resolver thread! */
757   if(init_resolve_thread(conn, hostname, port, &hints)) {
758     *waitp = TRUE;  /* please wait for the response */
759     return NULL;
760   }
761 
762   /* fall-back to blocking version */
763   infof(data, "init_resolve_thread() failed for %s; %s\n",
764         hostname, Curl_strerror(conn, ERRNO));
765 
766   error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
767   if(error) {
768     infof(data, "getaddrinfo() failed for %s:%d; %s\n",
769           hostname, port, Curl_strerror(conn, SOCKERRNO));
770     return NULL;
771   }
772   return res;
773 }
774 #endif /* CURLRES_IPV6 */
775 #endif /* CURLRES_THREADED */
776