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