xref: /curl/lib/asyn-thread.c (revision e411c98f)
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       else
560 #endif
561       Curl_thread_destroy(td->thread_hnd);
562     }
563     else {
564 #ifdef _WIN32
565       if(td->complete_ev) {
566         Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
567         WaitForSingleObject(td->complete_ev, INFINITE);
568         CloseHandle(td->complete_ev);
569       }
570 #endif
571       if(td->thread_hnd != curl_thread_t_null)
572         Curl_thread_join(&td->thread_hnd);
573 
574       destroy_thread_sync_data(&td->tsd);
575 
576       free(async->tdata);
577     }
578 #ifndef CURL_DISABLE_SOCKETPAIR
579     /*
580      * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
581      * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
582      */
583     Curl_multi_closed(data, sock_rd);
584     wakeup_close(sock_rd);
585 #endif
586   }
587   async->tdata = NULL;
588 
589   free(async->hostname);
590   async->hostname = NULL;
591 }
592 
593 /*
594  * init_resolve_thread() starts a new thread that performs the actual
595  * resolve. This function returns before the resolve is done.
596  *
597  * Returns FALSE in case of failure, otherwise TRUE.
598  */
init_resolve_thread(struct Curl_easy * data,const char * hostname,int port,const struct addrinfo * hints)599 static bool init_resolve_thread(struct Curl_easy *data,
600                                 const char *hostname, int port,
601                                 const struct addrinfo *hints)
602 {
603   struct thread_data *td = calloc(1, sizeof(struct thread_data));
604   int err = ENOMEM;
605   struct Curl_async *asp = &data->state.async;
606 
607   data->state.async.tdata = td;
608   if(!td)
609     goto errno_exit;
610 
611   asp->port = port;
612   asp->done = FALSE;
613   asp->status = 0;
614   asp->dns = NULL;
615   td->thread_hnd = curl_thread_t_null;
616 #ifdef _WIN32
617   td->complete_ev = NULL;
618 #endif
619 
620   if(!init_thread_sync_data(td, hostname, port, hints)) {
621     asp->tdata = NULL;
622     free(td);
623     goto errno_exit;
624   }
625 
626   free(asp->hostname);
627   asp->hostname = strdup(hostname);
628   if(!asp->hostname)
629     goto err_exit;
630 
631   /* The thread will set this to 1 when complete. */
632   td->tsd.done = 0;
633 
634 #ifdef _WIN32
635   if(Curl_isWindows8OrGreater && Curl_FreeAddrInfoExW &&
636      Curl_GetAddrInfoExCancel && Curl_GetAddrInfoExW) {
637 #define MAX_NAME_LEN 256 /* max domain name is 253 chars */
638 #define MAX_PORT_LEN 8
639     WCHAR namebuf[MAX_NAME_LEN];
640     WCHAR portbuf[MAX_PORT_LEN];
641     /* calculate required length */
642     int w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, hostname,
643                                     -1, NULL, 0);
644     if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
645       /* do utf8 conversion */
646       w_len = MultiByteToWideChar(CP_UTF8, 0, hostname, -1, namebuf, w_len);
647       if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
648         swprintf(portbuf, MAX_PORT_LEN, L"%d", port);
649         td->tsd.w8.hints.ai_family = hints->ai_family;
650         td->tsd.w8.hints.ai_socktype = hints->ai_socktype;
651         td->complete_ev = CreateEvent(NULL, TRUE, FALSE, NULL);
652         if(!td->complete_ev) {
653           /* failed to start, mark it as done here for proper cleanup. */
654           td->tsd.done = 1;
655           goto err_exit;
656         }
657         err = Curl_GetAddrInfoExW(namebuf, portbuf, NS_DNS,
658                                   NULL, &td->tsd.w8.hints, &td->tsd.w8.res,
659                                   NULL, &td->tsd.w8.overlapped,
660                                   &query_complete, &td->tsd.w8.cancel_ev);
661         if(err != WSA_IO_PENDING)
662           query_complete(err, 0, &td->tsd.w8.overlapped);
663         return TRUE;
664       }
665     }
666   }
667 #endif
668 
669 #ifdef HAVE_GETADDRINFO
670   td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
671 #else
672   td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
673 #endif
674 
675   if(!td->thread_hnd) {
676     /* The thread never started, so mark it as done here for proper cleanup. */
677     td->tsd.done = 1;
678     err = errno;
679     goto err_exit;
680   }
681 
682   return TRUE;
683 
684 err_exit:
685   destroy_async_data(asp);
686 
687 errno_exit:
688   errno = err;
689   return FALSE;
690 }
691 
692 /*
693  * 'entry' may be NULL and then no data is returned
694  */
thread_wait_resolv(struct Curl_easy * data,struct Curl_dns_entry ** entry,bool report)695 static CURLcode thread_wait_resolv(struct Curl_easy *data,
696                                    struct Curl_dns_entry **entry,
697                                    bool report)
698 {
699   struct thread_data *td;
700   CURLcode result = CURLE_OK;
701 
702   DEBUGASSERT(data);
703   td = data->state.async.tdata;
704   DEBUGASSERT(td);
705 #ifdef _WIN32
706   DEBUGASSERT(td->complete_ev || td->thread_hnd != curl_thread_t_null);
707 #else
708   DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
709 #endif
710 
711   /* wait for the thread to resolve the name */
712 #ifdef _WIN32
713   if(td->complete_ev) {
714     WaitForSingleObject(td->complete_ev, INFINITE);
715     CloseHandle(td->complete_ev);
716     if(entry)
717       result = getaddrinfo_complete(data);
718   }
719   else
720 #endif
721   if(Curl_thread_join(&td->thread_hnd)) {
722     if(entry)
723       result = getaddrinfo_complete(data);
724   }
725   else
726     DEBUGASSERT(0);
727 
728   data->state.async.done = TRUE;
729 
730   if(entry)
731     *entry = data->state.async.dns;
732 
733   if(!data->state.async.dns && report)
734     /* a name was not resolved, report error */
735     result = Curl_resolver_error(data);
736 
737   destroy_async_data(&data->state.async);
738 
739   if(!data->state.async.dns && report)
740     connclose(data->conn, "asynch resolve failed");
741 
742   return result;
743 }
744 
745 
746 /*
747  * Until we gain a way to signal the resolver threads to stop early, we must
748  * simply wait for them and ignore their results.
749  */
Curl_resolver_kill(struct Curl_easy * data)750 void Curl_resolver_kill(struct Curl_easy *data)
751 {
752   struct thread_data *td = data->state.async.tdata;
753 
754   /* If we're still resolving, we must wait for the threads to fully clean up,
755      unfortunately.  Otherwise, we can simply cancel to clean up any resolver
756      data. */
757   if(td && td->thread_hnd != curl_thread_t_null
758      && (data->set.quick_exit != 1L))
759     (void)thread_wait_resolv(data, NULL, FALSE);
760   else
761     Curl_resolver_cancel(data);
762 }
763 
764 /*
765  * Curl_resolver_wait_resolv()
766  *
767  * Waits for a resolve to finish. This function should be avoided since using
768  * this risk getting the multi interface to "hang".
769  *
770  * If 'entry' is non-NULL, make it point to the resolved dns entry
771  *
772  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
773  * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
774  *
775  * This is the version for resolves-in-a-thread.
776  */
Curl_resolver_wait_resolv(struct Curl_easy * data,struct Curl_dns_entry ** entry)777 CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
778                                    struct Curl_dns_entry **entry)
779 {
780   return thread_wait_resolv(data, entry, TRUE);
781 }
782 
783 /*
784  * Curl_resolver_is_resolved() is called repeatedly to check if a previous
785  * name resolve request has completed. It should also make sure to time-out if
786  * the operation seems to take too long.
787  */
Curl_resolver_is_resolved(struct Curl_easy * data,struct Curl_dns_entry ** entry)788 CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
789                                    struct Curl_dns_entry **entry)
790 {
791   struct thread_data *td = data->state.async.tdata;
792   int done = 0;
793 
794   DEBUGASSERT(entry);
795   *entry = NULL;
796 
797   if(!td) {
798     DEBUGASSERT(td);
799     return CURLE_COULDNT_RESOLVE_HOST;
800   }
801 
802   Curl_mutex_acquire(td->tsd.mtx);
803   done = td->tsd.done;
804   Curl_mutex_release(td->tsd.mtx);
805 
806   if(done) {
807     getaddrinfo_complete(data);
808 
809     if(!data->state.async.dns) {
810       CURLcode result = Curl_resolver_error(data);
811       destroy_async_data(&data->state.async);
812       return result;
813     }
814     destroy_async_data(&data->state.async);
815     *entry = data->state.async.dns;
816   }
817   else {
818     /* poll for name lookup done with exponential backoff up to 250ms */
819     /* should be fine even if this converts to 32 bit */
820     timediff_t elapsed = Curl_timediff(Curl_now(),
821                                        data->progress.t_startsingle);
822     if(elapsed < 0)
823       elapsed = 0;
824 
825     if(td->poll_interval == 0)
826       /* Start at 1ms poll interval */
827       td->poll_interval = 1;
828     else if(elapsed >= td->interval_end)
829       /* Back-off exponentially if last interval expired  */
830       td->poll_interval *= 2;
831 
832     if(td->poll_interval > 250)
833       td->poll_interval = 250;
834 
835     td->interval_end = elapsed + td->poll_interval;
836     Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME);
837   }
838 
839   return CURLE_OK;
840 }
841 
Curl_resolver_getsock(struct Curl_easy * data,curl_socket_t * socks)842 int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
843 {
844   int ret_val = 0;
845   timediff_t milli;
846   timediff_t ms;
847   struct resdata *reslv = (struct resdata *)data->state.async.resolver;
848 #ifndef CURL_DISABLE_SOCKETPAIR
849   struct thread_data *td = data->state.async.tdata;
850 #else
851   (void)socks;
852 #endif
853 
854 #ifndef CURL_DISABLE_SOCKETPAIR
855   if(td) {
856     /* return read fd to client for polling the DNS resolution status */
857     socks[0] = td->tsd.sock_pair[0];
858     td->tsd.data = data;
859     ret_val = GETSOCK_READSOCK(0);
860   }
861   else {
862 #endif
863     ms = Curl_timediff(Curl_now(), reslv->start);
864     if(ms < 3)
865       milli = 0;
866     else if(ms <= 50)
867       milli = ms/3;
868     else if(ms <= 250)
869       milli = 50;
870     else
871       milli = 200;
872     Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
873 #ifndef CURL_DISABLE_SOCKETPAIR
874   }
875 #endif
876 
877 
878   return ret_val;
879 }
880 
881 #ifndef HAVE_GETADDRINFO
882 /*
883  * Curl_getaddrinfo() - for platforms without getaddrinfo
884  */
Curl_resolver_getaddrinfo(struct Curl_easy * data,const char * hostname,int port,int * waitp)885 struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
886                                                 const char *hostname,
887                                                 int port,
888                                                 int *waitp)
889 {
890   struct resdata *reslv = (struct resdata *)data->state.async.resolver;
891 
892   *waitp = 0; /* default to synchronous response */
893 
894   reslv->start = Curl_now();
895 
896   /* fire up a new resolver thread! */
897   if(init_resolve_thread(data, hostname, port, NULL)) {
898     *waitp = 1; /* expect asynchronous response */
899     return NULL;
900   }
901 
902   failf(data, "getaddrinfo() thread failed");
903 
904   return NULL;
905 }
906 
907 #else /* !HAVE_GETADDRINFO */
908 
909 /*
910  * Curl_resolver_getaddrinfo() - for getaddrinfo
911  */
Curl_resolver_getaddrinfo(struct Curl_easy * data,const char * hostname,int port,int * waitp)912 struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
913                                                 const char *hostname,
914                                                 int port,
915                                                 int *waitp)
916 {
917   struct addrinfo hints;
918   int pf = PF_INET;
919   struct resdata *reslv = (struct resdata *)data->state.async.resolver;
920 
921   *waitp = 0; /* default to synchronous response */
922 
923 #ifdef CURLRES_IPV6
924   if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
925     /* The stack seems to be IPv6-enabled */
926     if(data->conn->ip_version == CURL_IPRESOLVE_V6)
927       pf = PF_INET6;
928     else
929       pf = PF_UNSPEC;
930   }
931 #endif /* CURLRES_IPV6 */
932 
933   memset(&hints, 0, sizeof(hints));
934   hints.ai_family = pf;
935   hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
936     SOCK_STREAM : SOCK_DGRAM;
937 
938   reslv->start = Curl_now();
939   /* fire up a new resolver thread! */
940   if(init_resolve_thread(data, hostname, port, &hints)) {
941     *waitp = 1; /* expect asynchronous response */
942     return NULL;
943   }
944 
945   failf(data, "getaddrinfo() thread failed to start");
946   return NULL;
947 
948 }
949 
950 #endif /* !HAVE_GETADDRINFO */
951 
Curl_set_dns_servers(struct Curl_easy * data,char * servers)952 CURLcode Curl_set_dns_servers(struct Curl_easy *data,
953                               char *servers)
954 {
955   (void)data;
956   (void)servers;
957   return CURLE_NOT_BUILT_IN;
958 
959 }
960 
Curl_set_dns_interface(struct Curl_easy * data,const char * interf)961 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
962                                 const char *interf)
963 {
964   (void)data;
965   (void)interf;
966   return CURLE_NOT_BUILT_IN;
967 }
968 
Curl_set_dns_local_ip4(struct Curl_easy * data,const char * local_ip4)969 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
970                                 const char *local_ip4)
971 {
972   (void)data;
973   (void)local_ip4;
974   return CURLE_NOT_BUILT_IN;
975 }
976 
Curl_set_dns_local_ip6(struct Curl_easy * data,const char * local_ip6)977 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
978                                 const char *local_ip6)
979 {
980   (void)data;
981   (void)local_ip6;
982   return CURLE_NOT_BUILT_IN;
983 }
984 
985 #endif /* CURLRES_THREADED */
986