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