• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 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 https://curl.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  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25 #include "curl_setup.h"
26 #include "socketpair.h"
27 
28 /***********************************************************************
29  * Only for threaded name resolves builds
30  **********************************************************************/
31 #ifdef CURLRES_THREADED
32 
33 #ifdef HAVE_NETINET_IN_H
34 #include <netinet/in.h>
35 #endif
36 #ifdef HAVE_NETDB_H
37 #include <netdb.h>
38 #endif
39 #ifdef HAVE_ARPA_INET_H
40 #include <arpa/inet.h>
41 #endif
42 #ifdef __VMS
43 #include <in.h>
44 #include <inet.h>
45 #endif
46 
47 #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
48 #  include <pthread.h>
49 #endif
50 
51 #ifdef HAVE_GETADDRINFO
52 #  define RESOLVER_ENOMEM  EAI_MEMORY
53 #else
54 #  define RESOLVER_ENOMEM  ENOMEM
55 #endif
56 
57 #include "system_win32.h"
58 #include "urldata.h"
59 #include "sendf.h"
60 #include "hostip.h"
61 #include "hash.h"
62 #include "share.h"
63 #include "url.h"
64 #include "multiif.h"
65 #include "inet_ntop.h"
66 #include "curl_threads.h"
67 #include "connect.h"
68 /* The last 3 #include files should be in this order */
69 #include "curl_printf.h"
70 #include "curl_memory.h"
71 #include "memdebug.h"
72 
73 struct resdata {
74   struct curltime start;
75 };
76 
77 /*
78  * Curl_resolver_global_init()
79  * Called from curl_global_init() to initialize global resolver environment.
80  * Does nothing here.
81  */
Curl_resolver_global_init(void)82 int Curl_resolver_global_init(void)
83 {
84   return CURLE_OK;
85 }
86 
87 /*
88  * Curl_resolver_global_cleanup()
89  * Called from curl_global_cleanup() to destroy global resolver environment.
90  * Does nothing here.
91  */
Curl_resolver_global_cleanup(void)92 void Curl_resolver_global_cleanup(void)
93 {
94 }
95 
96 /*
97  * Curl_resolver_init()
98  * Called from curl_easy_init() -> Curl_open() to initialize resolver
99  * URL-state specific environment ('resolver' member of the UrlState
100  * structure).
101  */
Curl_resolver_init(struct Curl_easy * easy,void ** resolver)102 CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
103 {
104   (void)easy;
105   *resolver = calloc(1, sizeof(struct resdata));
106   if(!*resolver)
107     return CURLE_OUT_OF_MEMORY;
108   return CURLE_OK;
109 }
110 
111 /*
112  * Curl_resolver_cleanup()
113  * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
114  * URL-state specific environment ('resolver' member of the UrlState
115  * structure).
116  */
Curl_resolver_cleanup(void * resolver)117 void Curl_resolver_cleanup(void *resolver)
118 {
119   free(resolver);
120 }
121 
122 /*
123  * Curl_resolver_duphandle()
124  * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
125  * environment ('resolver' member of the UrlState structure).
126  */
Curl_resolver_duphandle(struct Curl_easy * easy,void ** to,void * from)127 CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
128 {
129   (void)from;
130   return Curl_resolver_init(easy, to);
131 }
132 
133 static void destroy_async_data(struct Curl_async *);
134 
135 /*
136  * Cancel all possibly still on-going resolves for this connection.
137  */
Curl_resolver_cancel(struct Curl_easy * data)138 void Curl_resolver_cancel(struct Curl_easy *data)
139 {
140   destroy_async_data(&data->state.async);
141 }
142 
143 /* This function is used to init a threaded resolve */
144 static bool init_resolve_thread(struct Curl_easy *data,
145                                 const char *hostname, int port,
146                                 const struct addrinfo *hints);
147 
148 #ifdef _WIN32
149 /* Thread sync data used by GetAddrInfoExW for win8+ */
150 struct thread_sync_data_w8
151 {
152   OVERLAPPED overlapped;
153   ADDRINFOEXW_ *res;
154   HANDLE cancel_ev;
155   ADDRINFOEXW_ hints;
156 };
157 #endif
158 
159 /* Data for synchronization between resolver thread and its parent */
160 struct thread_sync_data {
161 #ifdef _WIN32
162   struct thread_sync_data_w8 w8;
163 #endif
164   curl_mutex_t *mtx;
165   int done;
166   int port;
167   char *hostname;        /* hostname to resolve, Curl_async.hostname
168                             duplicate */
169 #ifndef CURL_DISABLE_SOCKETPAIR
170   struct Curl_easy *data;
171   curl_socket_t sock_pair[2]; /* socket pair */
172 #endif
173   int sock_error;
174   struct Curl_addrinfo *res;
175 #ifdef HAVE_GETADDRINFO
176   struct addrinfo hints;
177 #endif
178   struct thread_data *td; /* for thread-self cleanup */
179 };
180 
181 struct thread_data {
182 #ifdef _WIN32
183   HANDLE complete_ev;
184 #endif
185   curl_thread_t thread_hnd;
186   unsigned int poll_interval;
187   timediff_t interval_end;
188   struct thread_sync_data tsd;
189 };
190 
conn_thread_sync_data(struct Curl_easy * data)191 static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
192 {
193   return &(data->state.async.tdata->tsd);
194 }
195 
196 /* Destroy resolver thread synchronization data */
197 static
destroy_thread_sync_data(struct thread_sync_data * tsd)198 void destroy_thread_sync_data(struct thread_sync_data *tsd)
199 {
200   if(tsd->mtx) {
201     Curl_mutex_destroy(tsd->mtx);
202     free(tsd->mtx);
203   }
204 
205   free(tsd->hostname);
206 
207   if(tsd->res)
208     Curl_freeaddrinfo(tsd->res);
209 
210 #ifndef CURL_DISABLE_SOCKETPAIR
211   /*
212    * close one end of the socket pair (may be done in resolver thread);
213    * the other end (for reading) is always closed in the parent thread.
214    */
215   if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
216     wakeup_close(tsd->sock_pair[1]);
217   }
218 #endif
219   memset(tsd, 0, sizeof(*tsd));
220 }
221 
222 /* Initialize resolver thread synchronization data */
223 static
init_thread_sync_data(struct thread_data * td,const char * hostname,int port,const struct addrinfo * hints)224 int init_thread_sync_data(struct thread_data *td,
225                            const char *hostname,
226                            int port,
227                            const struct addrinfo *hints)
228 {
229   struct thread_sync_data *tsd = &td->tsd;
230 
231   memset(tsd, 0, sizeof(*tsd));
232 
233   tsd->td = td;
234   tsd->port = port;
235   /* Treat the request as done until the thread actually starts so any early
236    * cleanup gets done properly.
237    */
238   tsd->done = 1;
239 #ifdef HAVE_GETADDRINFO
240   DEBUGASSERT(hints);
241   tsd->hints = *hints;
242 #else
243   (void) hints;
244 #endif
245 
246   tsd->mtx = malloc(sizeof(curl_mutex_t));
247   if(!tsd->mtx)
248     goto err_exit;
249 
250   Curl_mutex_init(tsd->mtx);
251 
252 #ifndef CURL_DISABLE_SOCKETPAIR
253   /* create socket pair or pipe */
254   if(wakeup_create(&tsd->sock_pair[0]) < 0) {
255     tsd->sock_pair[0] = CURL_SOCKET_BAD;
256     tsd->sock_pair[1] = CURL_SOCKET_BAD;
257     goto err_exit;
258   }
259 #endif
260   tsd->sock_error = CURL_ASYNC_SUCCESS;
261 
262   /* Copying hostname string because original can be destroyed by parent
263    * thread during gethostbyname execution.
264    */
265   tsd->hostname = strdup(hostname);
266   if(!tsd->hostname)
267     goto err_exit;
268 
269   return 1;
270 
271 err_exit:
272 #ifndef CURL_DISABLE_SOCKETPAIR
273   if(tsd->sock_pair[0] != CURL_SOCKET_BAD) {
274     wakeup_close(tsd->sock_pair[0]);
275     tsd->sock_pair[0] = CURL_SOCKET_BAD;
276   }
277 #endif
278   destroy_thread_sync_data(tsd);
279   return 0;
280 }
281 
getaddrinfo_complete(struct Curl_easy * data)282 static CURLcode getaddrinfo_complete(struct Curl_easy *data)
283 {
284   struct thread_sync_data *tsd = conn_thread_sync_data(data);
285   CURLcode result;
286 
287   result = Curl_addrinfo_callback(data, tsd->sock_error, tsd->res);
288   /* The tsd->res structure has been copied to async.dns and perhaps the DNS
289      cache.  Set our copy to NULL so destroy_thread_sync_data doesn't free it.
290   */
291   tsd->res = NULL;
292 
293   return result;
294 }
295 
296 #ifdef _WIN32
297 static VOID WINAPI
query_complete(DWORD err,DWORD bytes,LPWSAOVERLAPPED overlapped)298 query_complete(DWORD err, DWORD bytes, LPWSAOVERLAPPED overlapped)
299 {
300   size_t ss_size;
301   const ADDRINFOEXW_ *ai;
302   struct Curl_addrinfo *ca;
303   struct Curl_addrinfo *cafirst = NULL;
304   struct Curl_addrinfo *calast = NULL;
305 #ifdef __clang__
306 #pragma clang diagnostic push
307 #pragma clang diagnostic ignored "-Wcast-align"
308 #endif
309   struct thread_sync_data *tsd =
310     CONTAINING_RECORD(overlapped, struct thread_sync_data, w8.overlapped);
311 #ifdef __clang__
312 #pragma clang diagnostic pop
313 #endif
314   struct thread_data *td = tsd->td;
315   const ADDRINFOEXW_ *res = tsd->w8.res;
316   int error = (int)err;
317   (void)bytes;
318 
319   if(error == ERROR_SUCCESS) {
320     /* traverse the addrinfo list */
321 
322     for(ai = res; ai != NULL; ai = ai->ai_next) {
323       size_t namelen = ai->ai_canonname ? wcslen(ai->ai_canonname) + 1 : 0;
324       /* ignore elements with unsupported address family, */
325       /* settle family-specific sockaddr structure size.  */
326       if(ai->ai_family == AF_INET)
327         ss_size = sizeof(struct sockaddr_in);
328 #ifdef USE_IPV6
329       else if(ai->ai_family == AF_INET6)
330         ss_size = sizeof(struct sockaddr_in6);
331 #endif
332       else
333         continue;
334 
335       /* ignore elements without required address info */
336       if(!ai->ai_addr || !(ai->ai_addrlen > 0))
337         continue;
338 
339       /* ignore elements with bogus address size */
340       if((size_t)ai->ai_addrlen < ss_size)
341         continue;
342 
343       ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen);
344       if(!ca) {
345         error = EAI_MEMORY;
346         break;
347       }
348 
349       /* copy each structure member individually, member ordering, */
350       /* size, or padding might be different for each platform.    */
351       ca->ai_flags     = ai->ai_flags;
352       ca->ai_family    = ai->ai_family;
353       ca->ai_socktype  = ai->ai_socktype;
354       ca->ai_protocol  = ai->ai_protocol;
355       ca->ai_addrlen   = (curl_socklen_t)ss_size;
356       ca->ai_addr      = NULL;
357       ca->ai_canonname = NULL;
358       ca->ai_next      = NULL;
359 
360       ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
361       memcpy(ca->ai_addr, ai->ai_addr, ss_size);
362 
363       if(namelen) {
364         size_t i;
365         ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size);
366         for(i = 0; i < namelen; ++i) /* convert wide string to ascii */
367           ca->ai_canonname[i] = (char)ai->ai_canonname[i];
368         ca->ai_canonname[namelen] = '\0';
369       }
370 
371       /* if the return list is empty, this becomes the first element */
372       if(!cafirst)
373         cafirst = ca;
374 
375       /* add this element last in the return list */
376       if(calast)
377         calast->ai_next = ca;
378       calast = ca;
379     }
380 
381     /* if we failed, also destroy the Curl_addrinfo list */
382     if(error) {
383       Curl_freeaddrinfo(cafirst);
384       cafirst = NULL;
385     }
386     else if(!cafirst) {
387 #ifdef EAI_NONAME
388       /* rfc3493 conformant */
389       error = EAI_NONAME;
390 #else
391       /* rfc3493 obsoleted */
392       error = EAI_NODATA;
393 #endif
394 #ifdef USE_WINSOCK
395       SET_SOCKERRNO(error);
396 #endif
397     }
398     tsd->res = cafirst;
399   }
400 
401   if(tsd->w8.res) {
402     Curl_FreeAddrInfoExW(tsd->w8.res);
403     tsd->w8.res = NULL;
404   }
405 
406   if(error) {
407     tsd->sock_error = SOCKERRNO?SOCKERRNO:error;
408     if(tsd->sock_error == 0)
409       tsd->sock_error = RESOLVER_ENOMEM;
410   }
411   else {
412     Curl_addrinfo_set_port(tsd->res, tsd->port);
413   }
414 
415   Curl_mutex_acquire(tsd->mtx);
416   if(tsd->done) {
417     /* too late, gotta clean up the mess */
418     Curl_mutex_release(tsd->mtx);
419     destroy_thread_sync_data(tsd);
420     free(td);
421   }
422   else {
423 #ifndef CURL_DISABLE_SOCKETPAIR
424     char buf[1];
425     if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
426       /* DNS has been resolved, signal client task */
427       buf[0] = 1;
428       if(swrite(tsd->sock_pair[1],  buf, sizeof(buf)) < 0) {
429         /* update sock_erro to errno */
430         tsd->sock_error = SOCKERRNO;
431       }
432     }
433 #endif
434     tsd->done = 1;
435     Curl_mutex_release(tsd->mtx);
436     if(td->complete_ev)
437       SetEvent(td->complete_ev); /* Notify caller that the query completed */
438   }
439 }
440 #endif
441 
442 #ifdef HAVE_GETADDRINFO
443 
444 /*
445  * getaddrinfo_thread() resolves a name and then exits.
446  *
447  * For builds without ARES, but with USE_IPV6, create a resolver thread
448  * and wait on it.
449  */
getaddrinfo_thread(void * arg)450 static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
451 {
452   struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
453   struct thread_data *td = tsd->td;
454   char service[12];
455   int rc;
456 #ifndef CURL_DISABLE_SOCKETPAIR
457   char buf[1];
458 #endif
459 
460   msnprintf(service, sizeof(service), "%d", tsd->port);
461 
462   rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
463 
464   if(rc) {
465     tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
466     if(tsd->sock_error == 0)
467       tsd->sock_error = RESOLVER_ENOMEM;
468   }
469   else {
470     Curl_addrinfo_set_port(tsd->res, tsd->port);
471   }
472 
473   Curl_mutex_acquire(tsd->mtx);
474   if(tsd->done) {
475     /* too late, gotta clean up the mess */
476     Curl_mutex_release(tsd->mtx);
477     destroy_thread_sync_data(tsd);
478     free(td);
479   }
480   else {
481 #ifndef CURL_DISABLE_SOCKETPAIR
482     if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
483       /* DNS has been resolved, signal client task */
484       buf[0] = 1;
485       if(wakeup_write(tsd->sock_pair[1],  buf, sizeof(buf)) < 0) {
486         /* update sock_erro to errno */
487         tsd->sock_error = SOCKERRNO;
488       }
489     }
490 #endif
491     tsd->done = 1;
492     Curl_mutex_release(tsd->mtx);
493   }
494 
495   return 0;
496 }
497 
498 #else /* HAVE_GETADDRINFO */
499 
500 /*
501  * gethostbyname_thread() resolves a name and then exits.
502  */
gethostbyname_thread(void * arg)503 static unsigned int CURL_STDCALL gethostbyname_thread(void *arg)
504 {
505   struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
506   struct thread_data *td = tsd->td;
507 
508   tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
509 
510   if(!tsd->res) {
511     tsd->sock_error = SOCKERRNO;
512     if(tsd->sock_error == 0)
513       tsd->sock_error = RESOLVER_ENOMEM;
514   }
515 
516   Curl_mutex_acquire(tsd->mtx);
517   if(tsd->done) {
518     /* too late, gotta clean up the mess */
519     Curl_mutex_release(tsd->mtx);
520     destroy_thread_sync_data(tsd);
521     free(td);
522   }
523   else {
524     tsd->done = 1;
525     Curl_mutex_release(tsd->mtx);
526   }
527 
528   return 0;
529 }
530 
531 #endif /* HAVE_GETADDRINFO */
532 
533 /*
534  * destroy_async_data() cleans up async resolver data and thread handle.
535  */
destroy_async_data(struct Curl_async * async)536 static void destroy_async_data(struct Curl_async *async)
537 {
538   if(async->tdata) {
539     struct thread_data *td = async->tdata;
540     int done;
541 #ifndef CURL_DISABLE_SOCKETPAIR
542     curl_socket_t sock_rd = td->tsd.sock_pair[0];
543     struct Curl_easy *data = td->tsd.data;
544 #endif
545 
546     /*
547      * if the thread is still blocking in the resolve syscall, detach it and
548      * let the thread do the cleanup...
549      */
550     Curl_mutex_acquire(td->tsd.mtx);
551     done = td->tsd.done;
552     td->tsd.done = 1;
553     Curl_mutex_release(td->tsd.mtx);
554 
555     if(!done) {
556 #ifdef _WIN32
557       if(td->complete_ev) {
558         CloseHandle(td->complete_ev);
559         td->complete_ev = NULL;
560       }
561 #endif
562       if(td->thread_hnd != curl_thread_t_null) {
563         Curl_thread_destroy(td->thread_hnd);
564         td->thread_hnd = curl_thread_t_null;
565       }
566     }
567     else {
568 #ifdef _WIN32
569       if(td->complete_ev) {
570         Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
571         WaitForSingleObject(td->complete_ev, INFINITE);
572         CloseHandle(td->complete_ev);
573         td->complete_ev = NULL;
574       }
575 #endif
576       if(td->thread_hnd != curl_thread_t_null)
577         Curl_thread_join(&td->thread_hnd);
578 
579       destroy_thread_sync_data(&td->tsd);
580 
581       free(async->tdata);
582     }
583 #ifndef CURL_DISABLE_SOCKETPAIR
584     /*
585      * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
586      * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
587      */
588     Curl_multi_closed(data, sock_rd);
589     wakeup_close(sock_rd);
590 #endif
591   }
592   async->tdata = NULL;
593 
594   free(async->hostname);
595   async->hostname = NULL;
596 }
597 
598 /*
599  * init_resolve_thread() starts a new thread that performs the actual
600  * resolve. This function returns before the resolve is done.
601  *
602  * Returns FALSE in case of failure, otherwise TRUE.
603  */
init_resolve_thread(struct Curl_easy * data,const char * hostname,int port,const struct addrinfo * hints)604 static bool init_resolve_thread(struct Curl_easy *data,
605                                 const char *hostname, int port,
606                                 const struct addrinfo *hints)
607 {
608   struct thread_data *td = calloc(1, sizeof(struct thread_data));
609   int err = ENOMEM;
610   struct Curl_async *asp = &data->state.async;
611 
612   data->state.async.tdata = td;
613   if(!td)
614     goto errno_exit;
615 
616   asp->port = port;
617   asp->done = FALSE;
618   asp->status = 0;
619   asp->dns = NULL;
620   td->thread_hnd = curl_thread_t_null;
621 #ifdef _WIN32
622   td->complete_ev = NULL;
623 #endif
624 
625   if(!init_thread_sync_data(td, hostname, port, hints)) {
626     asp->tdata = NULL;
627     free(td);
628     goto errno_exit;
629   }
630 
631   free(asp->hostname);
632   asp->hostname = strdup(hostname);
633   if(!asp->hostname)
634     goto err_exit;
635 
636   /* The thread will set this to 1 when complete. */
637   td->tsd.done = 0;
638 
639 #ifdef _WIN32
640   if(Curl_isWindows8OrGreater && Curl_FreeAddrInfoExW &&
641      Curl_GetAddrInfoExCancel && Curl_GetAddrInfoExW) {
642 #define MAX_NAME_LEN 256 /* max domain name is 253 chars */
643 #define MAX_PORT_LEN 8
644     WCHAR namebuf[MAX_NAME_LEN];
645     WCHAR portbuf[MAX_PORT_LEN];
646     /* calculate required length */
647     int w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, hostname,
648                                     -1, NULL, 0);
649     if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
650       /* do utf8 conversion */
651       w_len = MultiByteToWideChar(CP_UTF8, 0, hostname, -1, namebuf, w_len);
652       if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
653         swprintf(portbuf, MAX_PORT_LEN, L"%d", port);
654         td->tsd.w8.hints.ai_family = hints->ai_family;
655         td->tsd.w8.hints.ai_socktype = hints->ai_socktype;
656         td->complete_ev = CreateEvent(NULL, TRUE, FALSE, NULL);
657         if(!td->complete_ev) {
658           /* failed to start, mark it as done here for proper cleanup. */
659           td->tsd.done = 1;
660           goto err_exit;
661         }
662         err = Curl_GetAddrInfoExW(namebuf, portbuf, NS_DNS,
663                                   NULL, &td->tsd.w8.hints, &td->tsd.w8.res,
664                                   NULL, &td->tsd.w8.overlapped,
665                                   &query_complete, &td->tsd.w8.cancel_ev);
666         if(err != WSA_IO_PENDING)
667           query_complete(err, 0, &td->tsd.w8.overlapped);
668         return TRUE;
669       }
670     }
671   }
672 #endif
673 
674 #ifdef HAVE_GETADDRINFO
675   td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
676 #else
677   td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
678 #endif
679 
680   if(td->thread_hnd == curl_thread_t_null) {
681     /* The thread never started, so mark it as done here for proper cleanup. */
682     td->tsd.done = 1;
683     err = errno;
684     goto err_exit;
685   }
686 
687   return TRUE;
688 
689 err_exit:
690   destroy_async_data(asp);
691 
692 errno_exit:
693   errno = err;
694   return FALSE;
695 }
696 
697 /*
698  * 'entry' may be NULL and then no data is returned
699  */
thread_wait_resolv(struct Curl_easy * data,struct Curl_dns_entry ** entry,bool report)700 static CURLcode thread_wait_resolv(struct Curl_easy *data,
701                                    struct Curl_dns_entry **entry,
702                                    bool report)
703 {
704   struct thread_data *td;
705   CURLcode result = CURLE_OK;
706 
707   DEBUGASSERT(data);
708   td = data->state.async.tdata;
709   DEBUGASSERT(td);
710 #ifdef _WIN32
711   DEBUGASSERT(td->complete_ev || td->thread_hnd != curl_thread_t_null);
712 #else
713   DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
714 #endif
715 
716   /* wait for the thread to resolve the name */
717 #ifdef _WIN32
718   if(td->complete_ev) {
719     WaitForSingleObject(td->complete_ev, INFINITE);
720     CloseHandle(td->complete_ev);
721     td->complete_ev = NULL;
722     if(entry)
723       result = getaddrinfo_complete(data);
724   }
725   else
726 #endif
727   if(Curl_thread_join(&td->thread_hnd)) {
728     if(entry)
729       result = getaddrinfo_complete(data);
730   }
731   else
732     DEBUGASSERT(0);
733 
734   data->state.async.done = TRUE;
735 
736   if(entry)
737     *entry = data->state.async.dns;
738 
739   if(!data->state.async.dns && report)
740     /* a name was not resolved, report error */
741     result = Curl_resolver_error(data);
742 
743   destroy_async_data(&data->state.async);
744 
745   if(!data->state.async.dns && report)
746     connclose(data->conn, "asynch resolve failed");
747 
748   return result;
749 }
750 
751 
752 /*
753  * Until we gain a way to signal the resolver threads to stop early, we must
754  * simply wait for them and ignore their results.
755  */
Curl_resolver_kill(struct Curl_easy * data)756 void Curl_resolver_kill(struct Curl_easy *data)
757 {
758   struct thread_data *td = data->state.async.tdata;
759 
760   /* If we're still resolving, we must wait for the threads to fully clean up,
761      unfortunately.  Otherwise, we can simply cancel to clean up any resolver
762      data. */
763 #ifdef _WIN32
764   if(td && td->complete_ev) {
765     Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
766     (void)thread_wait_resolv(data, NULL, FALSE);
767   }
768   else
769 #endif
770   if(td && td->thread_hnd != curl_thread_t_null
771      && (data->set.quick_exit != 1L))
772     (void)thread_wait_resolv(data, NULL, FALSE);
773   else
774     Curl_resolver_cancel(data);
775 }
776 
777 /*
778  * Curl_resolver_wait_resolv()
779  *
780  * Waits for a resolve to finish. This function should be avoided since using
781  * this risk getting the multi interface to "hang".
782  *
783  * If 'entry' is non-NULL, make it point to the resolved dns entry
784  *
785  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
786  * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
787  *
788  * This is the version for resolves-in-a-thread.
789  */
Curl_resolver_wait_resolv(struct Curl_easy * data,struct Curl_dns_entry ** entry)790 CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
791                                    struct Curl_dns_entry **entry)
792 {
793   return thread_wait_resolv(data, entry, TRUE);
794 }
795 
796 /*
797  * Curl_resolver_is_resolved() is called repeatedly to check if a previous
798  * name resolve request has completed. It should also make sure to time-out if
799  * the operation seems to take too long.
800  */
Curl_resolver_is_resolved(struct Curl_easy * data,struct Curl_dns_entry ** entry)801 CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
802                                    struct Curl_dns_entry **entry)
803 {
804   struct thread_data *td = data->state.async.tdata;
805   int done = 0;
806 
807   DEBUGASSERT(entry);
808   *entry = NULL;
809 
810   if(!td) {
811     DEBUGASSERT(td);
812     return CURLE_COULDNT_RESOLVE_HOST;
813   }
814 
815   Curl_mutex_acquire(td->tsd.mtx);
816   done = td->tsd.done;
817   Curl_mutex_release(td->tsd.mtx);
818 
819   if(done) {
820     getaddrinfo_complete(data);
821 
822     if(!data->state.async.dns) {
823       CURLcode result = Curl_resolver_error(data);
824       destroy_async_data(&data->state.async);
825       return result;
826     }
827     destroy_async_data(&data->state.async);
828     *entry = data->state.async.dns;
829   }
830   else {
831     /* poll for name lookup done with exponential backoff up to 250ms */
832     /* should be fine even if this converts to 32 bit */
833     timediff_t elapsed = Curl_timediff(Curl_now(),
834                                        data->progress.t_startsingle);
835     if(elapsed < 0)
836       elapsed = 0;
837 
838     if(td->poll_interval == 0)
839       /* Start at 1ms poll interval */
840       td->poll_interval = 1;
841     else if(elapsed >= td->interval_end)
842       /* Back-off exponentially if last interval expired  */
843       td->poll_interval *= 2;
844 
845     if(td->poll_interval > 250)
846       td->poll_interval = 250;
847 
848     td->interval_end = elapsed + td->poll_interval;
849     Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME);
850   }
851 
852   return CURLE_OK;
853 }
854 
Curl_resolver_getsock(struct Curl_easy * data,curl_socket_t * socks)855 int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
856 {
857   int ret_val = 0;
858   timediff_t milli;
859   timediff_t ms;
860   struct resdata *reslv = (struct resdata *)data->state.async.resolver;
861 #ifndef CURL_DISABLE_SOCKETPAIR
862   struct thread_data *td = data->state.async.tdata;
863 #else
864   (void)socks;
865 #endif
866 
867 #ifndef CURL_DISABLE_SOCKETPAIR
868   if(td) {
869     /* return read fd to client for polling the DNS resolution status */
870     socks[0] = td->tsd.sock_pair[0];
871     td->tsd.data = data;
872     ret_val = GETSOCK_READSOCK(0);
873   }
874   else {
875 #endif
876     ms = Curl_timediff(Curl_now(), reslv->start);
877     if(ms < 3)
878       milli = 0;
879     else if(ms <= 50)
880       milli = ms/3;
881     else if(ms <= 250)
882       milli = 50;
883     else
884       milli = 200;
885     Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
886 #ifndef CURL_DISABLE_SOCKETPAIR
887   }
888 #endif
889 
890 
891   return ret_val;
892 }
893 
894 #ifndef HAVE_GETADDRINFO
895 /*
896  * Curl_getaddrinfo() - for platforms without getaddrinfo
897  */
Curl_resolver_getaddrinfo(struct Curl_easy * data,const char * hostname,int port,int * waitp)898 struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
899                                                 const char *hostname,
900                                                 int port,
901                                                 int *waitp)
902 {
903   struct resdata *reslv = (struct resdata *)data->state.async.resolver;
904 
905   *waitp = 0; /* default to synchronous response */
906 
907   reslv->start = Curl_now();
908 
909   /* fire up a new resolver thread! */
910   if(init_resolve_thread(data, hostname, port, NULL)) {
911     *waitp = 1; /* expect asynchronous response */
912     return NULL;
913   }
914 
915   failf(data, "getaddrinfo() thread failed");
916 
917   return NULL;
918 }
919 
920 #else /* !HAVE_GETADDRINFO */
921 
922 /*
923  * Curl_resolver_getaddrinfo() - for getaddrinfo
924  */
Curl_resolver_getaddrinfo(struct Curl_easy * data,const char * hostname,int port,int * waitp)925 struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
926                                                 const char *hostname,
927                                                 int port,
928                                                 int *waitp)
929 {
930   struct addrinfo hints;
931   int pf = PF_INET;
932   struct resdata *reslv = (struct resdata *)data->state.async.resolver;
933 
934   *waitp = 0; /* default to synchronous response */
935 
936 #ifdef CURLRES_IPV6
937   if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
938     /* The stack seems to be IPv6-enabled */
939     if(data->conn->ip_version == CURL_IPRESOLVE_V6)
940       pf = PF_INET6;
941     else
942       pf = PF_UNSPEC;
943   }
944 #endif /* CURLRES_IPV6 */
945 
946   memset(&hints, 0, sizeof(hints));
947   hints.ai_family = pf;
948   hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
949     SOCK_STREAM : SOCK_DGRAM;
950 
951   reslv->start = Curl_now();
952   /* fire up a new resolver thread! */
953   if(init_resolve_thread(data, hostname, port, &hints)) {
954     *waitp = 1; /* expect asynchronous response */
955     return NULL;
956   }
957 
958   failf(data, "getaddrinfo() thread failed to start");
959   return NULL;
960 
961 }
962 
963 #endif /* !HAVE_GETADDRINFO */
964 
Curl_set_dns_servers(struct Curl_easy * data,char * servers)965 CURLcode Curl_set_dns_servers(struct Curl_easy *data,
966                               char *servers)
967 {
968   (void)data;
969   (void)servers;
970   return CURLE_NOT_BUILT_IN;
971 
972 }
973 
Curl_set_dns_interface(struct Curl_easy * data,const char * interf)974 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
975                                 const char *interf)
976 {
977   (void)data;
978   (void)interf;
979   return CURLE_NOT_BUILT_IN;
980 }
981 
Curl_set_dns_local_ip4(struct Curl_easy * data,const char * local_ip4)982 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
983                                 const char *local_ip4)
984 {
985   (void)data;
986   (void)local_ip4;
987   return CURLE_NOT_BUILT_IN;
988 }
989 
Curl_set_dns_local_ip6(struct Curl_easy * data,const char * local_ip6)990 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
991                                 const char *local_ip6)
992 {
993   (void)data;
994   (void)local_ip6;
995   return CURLE_NOT_BUILT_IN;
996 }
997 
998 #endif /* CURLRES_THREADED */
999