VirtualBox

source: vbox/trunk/src/libs/curl-8.11.1/lib/asyn-thread.c@ 108333

最後變更 在這個檔案從108333是 108048,由 vboxsync 提交於 7 週 前

curl-8.11.1: Applied and adjusted our curl changes to 8.7.1. jiraref:VBP-1535

  • 屬性 svn:eol-style 設為 native
檔案大小: 19.0 KB
 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <[email protected]>, 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 "urldata.h"
58#include "sendf.h"
59#include "hostip.h"
60#include "hash.h"
61#include "share.h"
62#include "url.h"
63#include "multiif.h"
64#include "inet_ntop.h"
65#include "curl_threads.h"
66#include "connect.h"
67/* The last 3 #include files should be in this order */
68#include "curl_printf.h"
69#include "curl_memory.h"
70#include "memdebug.h"
71
72struct resdata {
73 struct curltime start;
74};
75
76/*
77 * Curl_resolver_global_init()
78 * Called from curl_global_init() to initialize global resolver environment.
79 * Does nothing here.
80 */
81int Curl_resolver_global_init(void)
82{
83 return CURLE_OK;
84}
85
86/*
87 * Curl_resolver_global_cleanup()
88 * Called from curl_global_cleanup() to destroy global resolver environment.
89 * Does nothing here.
90 */
91void Curl_resolver_global_cleanup(void)
92{
93}
94
95/*
96 * Curl_resolver_init()
97 * Called from curl_easy_init() -> Curl_open() to initialize resolver
98 * URL-state specific environment ('resolver' member of the UrlState
99 * structure).
100 */
101CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
102{
103 (void)easy;
104 *resolver = calloc(1, sizeof(struct resdata));
105 if(!*resolver)
106 return CURLE_OUT_OF_MEMORY;
107 return CURLE_OK;
108}
109
110/*
111 * Curl_resolver_cleanup()
112 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
113 * URL-state specific environment ('resolver' member of the UrlState
114 * structure).
115 */
116void Curl_resolver_cleanup(void *resolver)
117{
118 free(resolver);
119}
120
121/*
122 * Curl_resolver_duphandle()
123 * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
124 * environment ('resolver' member of the UrlState structure).
125 */
126CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
127{
128 (void)from;
129 return Curl_resolver_init(easy, to);
130}
131
132static void destroy_async_data(struct Curl_async *);
133
134/*
135 * Cancel all possibly still on-going resolves for this connection.
136 */
137void Curl_resolver_cancel(struct Curl_easy *data)
138{
139 destroy_async_data(&data->state.async);
140}
141
142/* This function is used to init a threaded resolve */
143static bool init_resolve_thread(struct Curl_easy *data,
144 const char *hostname, int port,
145 const struct addrinfo *hints);
146
147
148/* Data for synchronization between resolver thread and its parent */
149struct thread_sync_data {
150 curl_mutex_t *mtx;
151 int done;
152 int port;
153 char *hostname; /* hostname to resolve, Curl_async.hostname
154 duplicate */
155#ifndef CURL_DISABLE_SOCKETPAIR
156 struct Curl_easy *data;
157 curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */
158#endif
159 int sock_error;
160 struct Curl_addrinfo *res;
161#ifdef HAVE_GETADDRINFO
162 struct addrinfo hints;
163#endif
164 struct thread_data *td; /* for thread-self cleanup */
165};
166
167struct thread_data {
168 curl_thread_t thread_hnd;
169 unsigned int poll_interval;
170 timediff_t interval_end;
171 struct thread_sync_data tsd;
172};
173
174static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
175{
176 return &(data->state.async.tdata->tsd);
177}
178
179/* Destroy resolver thread synchronization data */
180static
181void destroy_thread_sync_data(struct thread_sync_data *tsd)
182{
183 if(tsd->mtx) {
184 Curl_mutex_destroy(tsd->mtx);
185 free(tsd->mtx);
186 }
187
188 free(tsd->hostname);
189
190 if(tsd->res)
191 Curl_freeaddrinfo(tsd->res);
192
193#ifndef CURL_DISABLE_SOCKETPAIR
194 /*
195 * close one end of the socket pair (may be done in resolver thread);
196 * the other end (for reading) is always closed in the parent thread.
197 */
198 if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
199 wakeup_close(tsd->sock_pair[1]);
200 }
201#endif
202 memset(tsd, 0, sizeof(*tsd));
203}
204
205/* Initialize resolver thread synchronization data */
206static
207int init_thread_sync_data(struct thread_data *td,
208 const char *hostname,
209 int port,
210 const struct addrinfo *hints)
211{
212 struct thread_sync_data *tsd = &td->tsd;
213
214 memset(tsd, 0, sizeof(*tsd));
215
216 tsd->td = td;
217 tsd->port = port;
218 /* Treat the request as done until the thread actually starts so any early
219 * cleanup gets done properly.
220 */
221 tsd->done = 1;
222#ifdef HAVE_GETADDRINFO
223 DEBUGASSERT(hints);
224 tsd->hints = *hints;
225#else
226 (void) hints;
227#endif
228
229 tsd->mtx = malloc(sizeof(curl_mutex_t));
230 if(!tsd->mtx)
231 goto err_exit;
232
233 Curl_mutex_init(tsd->mtx);
234
235#ifndef CURL_DISABLE_SOCKETPAIR
236 /* create socket pair or pipe */
237 if(wakeup_create(tsd->sock_pair, FALSE) < 0) {
238 tsd->sock_pair[0] = CURL_SOCKET_BAD;
239 tsd->sock_pair[1] = CURL_SOCKET_BAD;
240 goto err_exit;
241 }
242#endif
243 tsd->sock_error = CURL_ASYNC_SUCCESS;
244
245 /* Copying hostname string because original can be destroyed by parent
246 * thread during gethostbyname execution.
247 */
248 tsd->hostname = strdup(hostname);
249 if(!tsd->hostname)
250 goto err_exit;
251
252 return 1;
253
254err_exit:
255#ifndef CURL_DISABLE_SOCKETPAIR
256 if(tsd->sock_pair[0] != CURL_SOCKET_BAD) {
257 wakeup_close(tsd->sock_pair[0]);
258 tsd->sock_pair[0] = CURL_SOCKET_BAD;
259 }
260#endif
261 destroy_thread_sync_data(tsd);
262 return 0;
263}
264
265static CURLcode getaddrinfo_complete(struct Curl_easy *data)
266{
267 struct thread_sync_data *tsd = conn_thread_sync_data(data);
268 CURLcode result;
269
270 result = Curl_addrinfo_callback(data, tsd->sock_error, tsd->res);
271 /* The tsd->res structure has been copied to async.dns and perhaps the DNS
272 cache. Set our copy to NULL so destroy_thread_sync_data does not free it.
273 */
274 tsd->res = NULL;
275
276 return result;
277}
278
279
280#ifdef HAVE_GETADDRINFO
281
282/*
283 * getaddrinfo_thread() resolves a name and then exits.
284 *
285 * For builds without ARES, but with USE_IPV6, create a resolver thread
286 * and wait on it.
287 */
288static
289#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
290DWORD
291#else
292unsigned int
293#endif
294CURL_STDCALL getaddrinfo_thread(void *arg)
295{
296 struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
297 struct thread_data *td = tsd->td;
298 char service[12];
299 int rc;
300#ifndef CURL_DISABLE_SOCKETPAIR
301#ifdef USE_EVENTFD
302 const void *buf;
303 const uint64_t val = 1;
304#else
305 char buf[1];
306#endif
307#endif
308
309 msnprintf(service, sizeof(service), "%d", tsd->port);
310
311 rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
312
313 if(rc) {
314 tsd->sock_error = SOCKERRNO ? SOCKERRNO : rc;
315 if(tsd->sock_error == 0)
316 tsd->sock_error = RESOLVER_ENOMEM;
317 }
318 else {
319 Curl_addrinfo_set_port(tsd->res, tsd->port);
320 }
321
322 Curl_mutex_acquire(tsd->mtx);
323 if(tsd->done) {
324 /* too late, gotta clean up the mess */
325 Curl_mutex_release(tsd->mtx);
326 destroy_thread_sync_data(tsd);
327 free(td);
328 }
329 else {
330#ifndef CURL_DISABLE_SOCKETPAIR
331 if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
332#ifdef USE_EVENTFD
333 buf = &val;
334#else
335 buf[0] = 1;
336#endif
337 /* DNS has been resolved, signal client task */
338 if(wakeup_write(tsd->sock_pair[1], buf, sizeof(buf)) < 0) {
339 /* update sock_erro to errno */
340 tsd->sock_error = SOCKERRNO;
341 }
342 }
343#endif
344 tsd->done = 1;
345 Curl_mutex_release(tsd->mtx);
346 }
347
348 return 0;
349}
350
351#else /* HAVE_GETADDRINFO */
352
353/*
354 * gethostbyname_thread() resolves a name and then exits.
355 */
356static
357#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
358DWORD
359#else
360unsigned int
361#endif
362CURL_STDCALL gethostbyname_thread(void *arg)
363{
364 struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
365 struct thread_data *td = tsd->td;
366
367 tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
368
369 if(!tsd->res) {
370 tsd->sock_error = SOCKERRNO;
371 if(tsd->sock_error == 0)
372 tsd->sock_error = RESOLVER_ENOMEM;
373 }
374
375 Curl_mutex_acquire(tsd->mtx);
376 if(tsd->done) {
377 /* too late, gotta clean up the mess */
378 Curl_mutex_release(tsd->mtx);
379 destroy_thread_sync_data(tsd);
380 free(td);
381 }
382 else {
383 tsd->done = 1;
384 Curl_mutex_release(tsd->mtx);
385 }
386
387 return 0;
388}
389
390#endif /* HAVE_GETADDRINFO */
391
392/*
393 * destroy_async_data() cleans up async resolver data and thread handle.
394 */
395static void destroy_async_data(struct Curl_async *async)
396{
397 if(async->tdata) {
398 struct thread_data *td = async->tdata;
399 int done;
400#ifndef CURL_DISABLE_SOCKETPAIR
401 curl_socket_t sock_rd = td->tsd.sock_pair[0];
402 struct Curl_easy *data = td->tsd.data;
403#endif
404
405 /*
406 * if the thread is still blocking in the resolve syscall, detach it and
407 * let the thread do the cleanup...
408 */
409 Curl_mutex_acquire(td->tsd.mtx);
410 done = td->tsd.done;
411 td->tsd.done = 1;
412 Curl_mutex_release(td->tsd.mtx);
413
414 if(!done) {
415 Curl_thread_destroy(td->thread_hnd);
416 }
417 else {
418 if(td->thread_hnd != curl_thread_t_null)
419 Curl_thread_join(&td->thread_hnd);
420
421 destroy_thread_sync_data(&td->tsd);
422
423 free(async->tdata);
424 }
425#ifndef CURL_DISABLE_SOCKETPAIR
426 /*
427 * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
428 * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
429 */
430 Curl_multi_closed(data, sock_rd);
431 wakeup_close(sock_rd);
432#endif
433 }
434 async->tdata = NULL;
435
436 free(async->hostname);
437 async->hostname = NULL;
438}
439
440/*
441 * init_resolve_thread() starts a new thread that performs the actual
442 * resolve. This function returns before the resolve is done.
443 *
444 * Returns FALSE in case of failure, otherwise TRUE.
445 */
446static bool init_resolve_thread(struct Curl_easy *data,
447 const char *hostname, int port,
448 const struct addrinfo *hints)
449{
450 struct thread_data *td = calloc(1, sizeof(struct thread_data));
451 int err = ENOMEM;
452 struct Curl_async *asp = &data->state.async;
453
454 data->state.async.tdata = td;
455 if(!td)
456 goto errno_exit;
457
458 asp->port = port;
459 asp->done = FALSE;
460 asp->status = 0;
461 asp->dns = NULL;
462 td->thread_hnd = curl_thread_t_null;
463
464 if(!init_thread_sync_data(td, hostname, port, hints)) {
465 asp->tdata = NULL;
466 free(td);
467 goto errno_exit;
468 }
469
470 free(asp->hostname);
471 asp->hostname = strdup(hostname);
472 if(!asp->hostname)
473 goto err_exit;
474
475 /* The thread will set this to 1 when complete. */
476 td->tsd.done = 0;
477
478#ifdef HAVE_GETADDRINFO
479 td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
480#else
481 td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
482#endif
483
484 if(td->thread_hnd == curl_thread_t_null) {
485 /* The thread never started, so mark it as done here for proper cleanup. */
486 td->tsd.done = 1;
487 err = errno;
488 goto err_exit;
489 }
490
491 return TRUE;
492
493err_exit:
494 destroy_async_data(asp);
495
496errno_exit:
497 errno = err;
498 return FALSE;
499}
500
501/*
502 * 'entry' may be NULL and then no data is returned
503 */
504static CURLcode thread_wait_resolv(struct Curl_easy *data,
505 struct Curl_dns_entry **entry,
506 bool report)
507{
508 struct thread_data *td;
509 CURLcode result = CURLE_OK;
510
511 DEBUGASSERT(data);
512 td = data->state.async.tdata;
513 DEBUGASSERT(td);
514 DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
515
516 /* wait for the thread to resolve the name */
517 if(Curl_thread_join(&td->thread_hnd)) {
518 if(entry)
519 result = getaddrinfo_complete(data);
520 }
521 else
522 DEBUGASSERT(0);
523
524 data->state.async.done = TRUE;
525
526 if(entry)
527 *entry = data->state.async.dns;
528
529 if(!data->state.async.dns && report)
530 /* a name was not resolved, report error */
531 result = Curl_resolver_error(data);
532
533 destroy_async_data(&data->state.async);
534
535 if(!data->state.async.dns && report)
536 connclose(data->conn, "asynch resolve failed");
537
538 return result;
539}
540
541
542/*
543 * Until we gain a way to signal the resolver threads to stop early, we must
544 * simply wait for them and ignore their results.
545 */
546void Curl_resolver_kill(struct Curl_easy *data)
547{
548 struct thread_data *td = data->state.async.tdata;
549
550 /* If we are still resolving, we must wait for the threads to fully clean up,
551 unfortunately. Otherwise, we can simply cancel to clean up any resolver
552 data. */
553 if(td && td->thread_hnd != curl_thread_t_null
554 && (data->set.quick_exit != 1L))
555 (void)thread_wait_resolv(data, NULL, FALSE);
556 else
557 Curl_resolver_cancel(data);
558}
559
560/*
561 * Curl_resolver_wait_resolv()
562 *
563 * Waits for a resolve to finish. This function should be avoided since using
564 * this risk getting the multi interface to "hang".
565 *
566 * If 'entry' is non-NULL, make it point to the resolved dns entry
567 *
568 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
569 * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
570 *
571 * This is the version for resolves-in-a-thread.
572 */
573CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
574 struct Curl_dns_entry **entry)
575{
576 return thread_wait_resolv(data, entry, TRUE);
577}
578
579/*
580 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
581 * name resolve request has completed. It should also make sure to time-out if
582 * the operation seems to take too long.
583 */
584CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
585 struct Curl_dns_entry **entry)
586{
587 struct thread_data *td = data->state.async.tdata;
588 int done = 0;
589
590 DEBUGASSERT(entry);
591 *entry = NULL;
592
593 if(!td) {
594 DEBUGASSERT(td);
595 return CURLE_COULDNT_RESOLVE_HOST;
596 }
597
598 Curl_mutex_acquire(td->tsd.mtx);
599 done = td->tsd.done;
600 Curl_mutex_release(td->tsd.mtx);
601
602 if(done) {
603 getaddrinfo_complete(data);
604
605 if(!data->state.async.dns) {
606 CURLcode result = Curl_resolver_error(data);
607 destroy_async_data(&data->state.async);
608 return result;
609 }
610 destroy_async_data(&data->state.async);
611 *entry = data->state.async.dns;
612 }
613 else {
614 /* poll for name lookup done with exponential backoff up to 250ms */
615 /* should be fine even if this converts to 32-bit */
616 timediff_t elapsed = Curl_timediff(Curl_now(),
617 data->progress.t_startsingle);
618 if(elapsed < 0)
619 elapsed = 0;
620
621 if(td->poll_interval == 0)
622 /* Start at 1ms poll interval */
623 td->poll_interval = 1;
624 else if(elapsed >= td->interval_end)
625 /* Back-off exponentially if last interval expired */
626 td->poll_interval *= 2;
627
628 if(td->poll_interval > 250)
629 td->poll_interval = 250;
630
631 td->interval_end = elapsed + td->poll_interval;
632 Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME);
633 }
634
635 return CURLE_OK;
636}
637
638int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
639{
640 int ret_val = 0;
641 timediff_t milli;
642 timediff_t ms;
643 struct resdata *reslv = (struct resdata *)data->state.async.resolver;
644#ifndef CURL_DISABLE_SOCKETPAIR
645 struct thread_data *td = data->state.async.tdata;
646#else
647 (void)socks;
648#endif
649
650#ifndef CURL_DISABLE_SOCKETPAIR
651 if(td) {
652 /* return read fd to client for polling the DNS resolution status */
653 socks[0] = td->tsd.sock_pair[0];
654 td->tsd.data = data;
655 ret_val = GETSOCK_READSOCK(0);
656 }
657 else {
658#endif
659 ms = Curl_timediff(Curl_now(), reslv->start);
660 if(ms < 3)
661 milli = 0;
662 else if(ms <= 50)
663 milli = ms/3;
664 else if(ms <= 250)
665 milli = 50;
666 else
667 milli = 200;
668 Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
669#ifndef CURL_DISABLE_SOCKETPAIR
670 }
671#endif
672
673
674 return ret_val;
675}
676
677#ifndef HAVE_GETADDRINFO
678/*
679 * Curl_getaddrinfo() - for platforms without getaddrinfo
680 */
681struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
682 const char *hostname,
683 int port,
684 int *waitp)
685{
686 struct resdata *reslv = (struct resdata *)data->state.async.resolver;
687
688 *waitp = 0; /* default to synchronous response */
689
690 reslv->start = Curl_now();
691
692 /* fire up a new resolver thread! */
693 if(init_resolve_thread(data, hostname, port, NULL)) {
694 *waitp = 1; /* expect asynchronous response */
695 return NULL;
696 }
697
698 failf(data, "getaddrinfo() thread failed");
699
700 return NULL;
701}
702
703#else /* !HAVE_GETADDRINFO */
704
705/*
706 * Curl_resolver_getaddrinfo() - for getaddrinfo
707 */
708struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
709 const char *hostname,
710 int port,
711 int *waitp)
712{
713 struct addrinfo hints;
714 int pf = PF_INET;
715 struct resdata *reslv = (struct resdata *)data->state.async.resolver;
716
717 *waitp = 0; /* default to synchronous response */
718
719#ifdef CURLRES_IPV6
720 if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
721 /* The stack seems to be IPv6-enabled */
722 if(data->conn->ip_version == CURL_IPRESOLVE_V6)
723 pf = PF_INET6;
724 else
725 pf = PF_UNSPEC;
726 }
727#endif /* CURLRES_IPV6 */
728
729 memset(&hints, 0, sizeof(hints));
730 hints.ai_family = pf;
731 hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ?
732 SOCK_STREAM : SOCK_DGRAM;
733
734 reslv->start = Curl_now();
735 /* fire up a new resolver thread! */
736 if(init_resolve_thread(data, hostname, port, &hints)) {
737 *waitp = 1; /* expect asynchronous response */
738 return NULL;
739 }
740
741 failf(data, "getaddrinfo() thread failed to start");
742 return NULL;
743
744}
745
746#endif /* !HAVE_GETADDRINFO */
747
748CURLcode Curl_set_dns_servers(struct Curl_easy *data,
749 char *servers)
750{
751 (void)data;
752 (void)servers;
753 return CURLE_NOT_BUILT_IN;
754
755}
756
757CURLcode Curl_set_dns_interface(struct Curl_easy *data,
758 const char *interf)
759{
760 (void)data;
761 (void)interf;
762 return CURLE_NOT_BUILT_IN;
763}
764
765CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
766 const char *local_ip4)
767{
768 (void)data;
769 (void)local_ip4;
770 return CURLE_NOT_BUILT_IN;
771}
772
773CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
774 const char *local_ip6)
775{
776 (void)data;
777 (void)local_ip6;
778 return CURLE_NOT_BUILT_IN;
779}
780
781#endif /* CURLRES_THREADED */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette