Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/curl/lib/asyn-thrdd.c
2654 views
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 /* = WSA_NOT_ENOUGH_MEMORY on Windows */
53
#else
54
# define RESOLVER_ENOMEM SOCKENOMEM
55
#endif
56
57
#include "urldata.h"
58
#include "cfilters.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 "curl_threads.h"
66
#include "select.h"
67
68
#ifdef USE_ARES
69
#include <ares.h>
70
#ifdef USE_HTTPSRR
71
#define USE_HTTPSRR_ARES /* the combo */
72
#endif
73
#endif
74
75
/* The last 2 #include files should be in this order */
76
#include "curl_memory.h"
77
#include "memdebug.h"
78
79
80
/*
81
* Curl_async_global_init()
82
* Called from curl_global_init() to initialize global resolver environment.
83
* Does nothing here.
84
*/
85
int Curl_async_global_init(void)
86
{
87
#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
88
if(ares_library_init(ARES_LIB_INIT_ALL)) {
89
return CURLE_FAILED_INIT;
90
}
91
#endif
92
return CURLE_OK;
93
}
94
95
/*
96
* Curl_async_global_cleanup()
97
* Called from curl_global_cleanup() to destroy global resolver environment.
98
* Does nothing here.
99
*/
100
void Curl_async_global_cleanup(void)
101
{
102
#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
103
ares_library_cleanup();
104
#endif
105
}
106
107
static void async_thrdd_destroy(struct Curl_easy *);
108
static void async_thrdd_shutdown(struct Curl_easy *);
109
110
CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
111
{
112
(void)data;
113
*impl = NULL;
114
return CURLE_OK;
115
}
116
117
/* Give up reference to add_ctx */
118
static void addr_ctx_unlink(struct async_thrdd_addr_ctx **paddr_ctx,
119
struct Curl_easy *data)
120
{
121
struct async_thrdd_addr_ctx *addr_ctx = *paddr_ctx;
122
bool destroy;
123
124
(void)data;
125
if(!addr_ctx)
126
return;
127
128
Curl_mutex_acquire(&addr_ctx->mutx);
129
if(!data) /* called by resolving thread */
130
addr_ctx->thrd_done = TRUE;
131
132
DEBUGASSERT(addr_ctx->ref_count);
133
--addr_ctx->ref_count;
134
destroy = !addr_ctx->ref_count;
135
Curl_mutex_release(&addr_ctx->mutx);
136
137
if(destroy) {
138
Curl_mutex_destroy(&addr_ctx->mutx);
139
free(addr_ctx->hostname);
140
if(addr_ctx->res)
141
Curl_freeaddrinfo(addr_ctx->res);
142
#ifndef CURL_DISABLE_SOCKETPAIR
143
#ifndef USE_EVENTFD
144
wakeup_close(addr_ctx->sock_pair[1]);
145
#endif
146
wakeup_close(addr_ctx->sock_pair[0]);
147
#endif
148
free(addr_ctx);
149
}
150
*paddr_ctx = NULL;
151
}
152
153
/* Initialize context for threaded resolver */
154
static struct async_thrdd_addr_ctx *
155
addr_ctx_create(struct Curl_easy *data,
156
const char *hostname, int port,
157
const struct addrinfo *hints)
158
{
159
struct async_thrdd_addr_ctx *addr_ctx = calloc(1, sizeof(*addr_ctx));
160
if(!addr_ctx)
161
return NULL;
162
163
addr_ctx->thread_hnd = curl_thread_t_null;
164
addr_ctx->port = port;
165
addr_ctx->ref_count = 1;
166
167
#ifdef HAVE_GETADDRINFO
168
DEBUGASSERT(hints);
169
addr_ctx->hints = *hints;
170
#else
171
(void)hints;
172
#endif
173
174
Curl_mutex_init(&addr_ctx->mutx);
175
176
#ifndef CURL_DISABLE_SOCKETPAIR
177
/* create socket pair or pipe */
178
if(wakeup_create(addr_ctx->sock_pair, FALSE) < 0) {
179
addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
180
addr_ctx->sock_pair[1] = CURL_SOCKET_BAD;
181
goto err_exit;
182
}
183
#endif
184
addr_ctx->sock_error = 0;
185
186
/* Copying hostname string because original can be destroyed by parent
187
* thread during gethostbyname execution.
188
*/
189
addr_ctx->hostname = strdup(hostname);
190
if(!addr_ctx->hostname)
191
goto err_exit;
192
193
return addr_ctx;
194
195
err_exit:
196
addr_ctx_unlink(&addr_ctx, data);
197
return NULL;
198
}
199
200
#ifdef HAVE_GETADDRINFO
201
202
/*
203
* getaddrinfo_thread() resolves a name and then exits.
204
*
205
* For builds without ARES, but with USE_IPV6, create a resolver thread
206
* and wait on it.
207
*/
208
static CURL_THREAD_RETURN_T CURL_STDCALL getaddrinfo_thread(void *arg)
209
{
210
struct async_thrdd_addr_ctx *addr_ctx = arg;
211
bool do_abort;
212
213
Curl_mutex_acquire(&addr_ctx->mutx);
214
do_abort = addr_ctx->do_abort;
215
Curl_mutex_release(&addr_ctx->mutx);
216
217
if(!do_abort) {
218
char service[12];
219
int rc;
220
221
curl_msnprintf(service, sizeof(service), "%d", addr_ctx->port);
222
223
rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service,
224
&addr_ctx->hints, &addr_ctx->res);
225
226
if(rc) {
227
addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc;
228
if(addr_ctx->sock_error == 0)
229
addr_ctx->sock_error = RESOLVER_ENOMEM;
230
}
231
else {
232
Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port);
233
}
234
235
Curl_mutex_acquire(&addr_ctx->mutx);
236
do_abort = addr_ctx->do_abort;
237
Curl_mutex_release(&addr_ctx->mutx);
238
#ifndef CURL_DISABLE_SOCKETPAIR
239
if(!do_abort) {
240
#ifdef USE_EVENTFD
241
const uint64_t buf[1] = { 1 };
242
#else
243
const char buf[1] = { 1 };
244
#endif
245
/* Thread is done, notify transfer */
246
if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) {
247
/* update sock_error to errno */
248
addr_ctx->sock_error = SOCKERRNO;
249
}
250
}
251
#endif
252
253
}
254
255
addr_ctx_unlink(&addr_ctx, NULL);
256
return 0;
257
}
258
259
#else /* HAVE_GETADDRINFO */
260
261
/*
262
* gethostbyname_thread() resolves a name and then exits.
263
*/
264
static CURL_THREAD_RETURN_T CURL_STDCALL gethostbyname_thread(void *arg)
265
{
266
struct async_thrdd_addr_ctx *addr_ctx = arg;
267
bool do_abort;
268
269
Curl_mutex_acquire(&addr_ctx->mutx);
270
do_abort = addr_ctx->do_abort;
271
Curl_mutex_release(&addr_ctx->mutx);
272
273
if(!do_abort) {
274
addr_ctx->res = Curl_ipv4_resolve_r(addr_ctx->hostname, addr_ctx->port);
275
if(!addr_ctx->res) {
276
addr_ctx->sock_error = SOCKERRNO;
277
if(addr_ctx->sock_error == 0)
278
addr_ctx->sock_error = RESOLVER_ENOMEM;
279
}
280
281
Curl_mutex_acquire(&addr_ctx->mutx);
282
do_abort = addr_ctx->do_abort;
283
Curl_mutex_release(&addr_ctx->mutx);
284
#ifndef CURL_DISABLE_SOCKETPAIR
285
if(!do_abort) {
286
#ifdef USE_EVENTFD
287
const uint64_t buf[1] = { 1 };
288
#else
289
const char buf[1] = { 1 };
290
#endif
291
/* Thread is done, notify transfer */
292
if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) {
293
/* update sock_error to errno */
294
addr_ctx->sock_error = SOCKERRNO;
295
}
296
}
297
#endif
298
}
299
300
addr_ctx_unlink(&addr_ctx, NULL);
301
return 0;
302
}
303
304
#endif /* HAVE_GETADDRINFO */
305
306
/*
307
* async_thrdd_destroy() cleans up async resolver data and thread handle.
308
*/
309
static void async_thrdd_destroy(struct Curl_easy *data)
310
{
311
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
312
struct async_thrdd_addr_ctx *addr = thrdd->addr;
313
314
#ifdef USE_HTTPSRR_ARES
315
if(thrdd->rr.channel) {
316
ares_destroy(thrdd->rr.channel);
317
thrdd->rr.channel = NULL;
318
}
319
Curl_httpsrr_cleanup(&thrdd->rr.hinfo);
320
#endif
321
322
if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null)) {
323
bool done;
324
325
Curl_mutex_acquire(&addr->mutx);
326
#ifndef CURL_DISABLE_SOCKETPAIR
327
if(!addr->do_abort)
328
Curl_multi_will_close(data, addr->sock_pair[0]);
329
#endif
330
addr->do_abort = TRUE;
331
done = addr->thrd_done;
332
Curl_mutex_release(&addr->mutx);
333
334
if(done) {
335
Curl_thread_join(&addr->thread_hnd);
336
CURL_TRC_DNS(data, "async_thrdd_destroy, thread joined");
337
}
338
else {
339
/* thread is still running. Detach it. */
340
Curl_thread_destroy(&addr->thread_hnd);
341
CURL_TRC_DNS(data, "async_thrdd_destroy, thread detached");
342
}
343
}
344
/* release our reference to the shared context */
345
addr_ctx_unlink(&thrdd->addr, data);
346
}
347
348
#ifdef USE_HTTPSRR_ARES
349
350
static void async_thrdd_rr_done(void *user_data, ares_status_t status,
351
size_t timeouts,
352
const ares_dns_record_t *dnsrec)
353
{
354
struct Curl_easy *data = user_data;
355
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
356
357
(void)timeouts;
358
thrdd->rr.done = TRUE;
359
if((ARES_SUCCESS != status) || !dnsrec)
360
return;
361
thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo);
362
}
363
364
static CURLcode async_rr_start(struct Curl_easy *data, int port)
365
{
366
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
367
int status;
368
char *rrname = NULL;
369
370
DEBUGASSERT(!thrdd->rr.channel);
371
if(port != 443) {
372
rrname = curl_maprintf("_%d_.https.%s", port, data->conn->host.name);
373
if(!rrname)
374
return CURLE_OUT_OF_MEMORY;
375
}
376
status = ares_init_options(&thrdd->rr.channel, NULL, 0);
377
if(status != ARES_SUCCESS) {
378
thrdd->rr.channel = NULL;
379
return CURLE_FAILED_INIT;
380
}
381
#ifdef CURLDEBUG
382
if(getenv("CURL_DNS_SERVER")) {
383
const char *servers = getenv("CURL_DNS_SERVER");
384
status = ares_set_servers_ports_csv(thrdd->rr.channel, servers);
385
if(status)
386
return CURLE_FAILED_INIT;
387
}
388
#endif
389
390
memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
391
thrdd->rr.hinfo.port = -1;
392
thrdd->rr.hinfo.rrname = rrname;
393
ares_query_dnsrec(thrdd->rr.channel,
394
rrname ? rrname : data->conn->host.name, ARES_CLASS_IN,
395
ARES_REC_TYPE_HTTPS,
396
async_thrdd_rr_done, data, NULL);
397
CURL_TRC_DNS(data, "Issued HTTPS-RR request for %s", data->conn->host.name);
398
return CURLE_OK;
399
}
400
#endif
401
402
/*
403
* async_thrdd_init() starts a new thread that performs the actual
404
* resolve. This function returns before the resolve is done.
405
*
406
* Returns FALSE in case of failure, otherwise TRUE.
407
*/
408
static bool async_thrdd_init(struct Curl_easy *data,
409
const char *hostname, int port, int ip_version,
410
const struct addrinfo *hints)
411
{
412
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
413
struct async_thrdd_addr_ctx *addr_ctx;
414
415
/* !checksrc! disable ERRNOVAR 1 */
416
int err = ENOMEM;
417
418
if(thrdd->addr
419
#ifdef USE_HTTPSRR_ARES
420
|| thrdd->rr.channel
421
#endif
422
) {
423
CURL_TRC_DNS(data, "starting new resolve, with previous not cleaned up");
424
async_thrdd_destroy(data);
425
DEBUGASSERT(!thrdd->addr);
426
#ifdef USE_HTTPSRR_ARES
427
DEBUGASSERT(!thrdd->rr.channel);
428
#endif
429
}
430
431
data->state.async.dns = NULL;
432
data->state.async.done = FALSE;
433
data->state.async.port = port;
434
data->state.async.ip_version = ip_version;
435
free(data->state.async.hostname);
436
data->state.async.hostname = strdup(hostname);
437
if(!data->state.async.hostname)
438
goto err_exit;
439
440
addr_ctx = addr_ctx_create(data, hostname, port, hints);
441
if(!addr_ctx)
442
goto err_exit;
443
thrdd->addr = addr_ctx;
444
445
/* passing addr_ctx to the thread adds a reference */
446
addr_ctx->ref_count = 2;
447
addr_ctx->start = curlx_now();
448
449
#ifdef HAVE_GETADDRINFO
450
addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx);
451
#else
452
addr_ctx->thread_hnd = Curl_thread_create(gethostbyname_thread, addr_ctx);
453
#endif
454
455
if(addr_ctx->thread_hnd == curl_thread_t_null) {
456
/* The thread never started */
457
addr_ctx->ref_count = 1;
458
addr_ctx->thrd_done = TRUE;
459
err = errno;
460
goto err_exit;
461
}
462
463
#ifdef USE_HTTPSRR_ARES
464
if(async_rr_start(data, port))
465
infof(data, "Failed HTTPS RR operation");
466
#endif
467
CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port);
468
return TRUE;
469
470
err_exit:
471
CURL_TRC_DNS(data, "resolve thread failed init: %d", err);
472
async_thrdd_destroy(data);
473
CURL_SETERRNO(err);
474
return FALSE;
475
}
476
477
static void async_thrdd_shutdown(struct Curl_easy *data)
478
{
479
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
480
struct async_thrdd_addr_ctx *addr_ctx = thrdd->addr;
481
bool done;
482
483
if(!addr_ctx)
484
return;
485
if(addr_ctx->thread_hnd == curl_thread_t_null)
486
return;
487
488
Curl_mutex_acquire(&addr_ctx->mutx);
489
#ifndef CURL_DISABLE_SOCKETPAIR
490
if(!addr_ctx->do_abort)
491
Curl_multi_will_close(data, addr_ctx->sock_pair[0]);
492
#endif
493
addr_ctx->do_abort = TRUE;
494
done = addr_ctx->thrd_done;
495
Curl_mutex_release(&addr_ctx->mutx);
496
497
/* Wait for the thread to terminate if it is already marked done. If it is
498
not done yet we cannot do anything here. We had tried pthread_cancel but
499
it caused hanging and resource leaks (#18532). */
500
if(done && (addr_ctx->thread_hnd != curl_thread_t_null)) {
501
Curl_thread_join(&addr_ctx->thread_hnd);
502
CURL_TRC_DNS(data, "async_thrdd_shutdown, thread joined");
503
}
504
}
505
506
/*
507
* 'entry' may be NULL and then no data is returned
508
*/
509
static CURLcode asyn_thrdd_await(struct Curl_easy *data,
510
struct async_thrdd_addr_ctx *addr_ctx,
511
struct Curl_dns_entry **entry)
512
{
513
CURLcode result = CURLE_OK;
514
515
if(addr_ctx->thread_hnd != curl_thread_t_null) {
516
/* not interested in result? cancel, if still running... */
517
if(!entry)
518
async_thrdd_shutdown(data);
519
520
if(addr_ctx->thread_hnd != curl_thread_t_null) {
521
CURL_TRC_DNS(data, "resolve, wait for thread to finish");
522
if(!Curl_thread_join(&addr_ctx->thread_hnd)) {
523
DEBUGASSERT(0);
524
}
525
}
526
527
if(entry)
528
result = Curl_async_is_resolved(data, entry);
529
}
530
531
data->state.async.done = TRUE;
532
if(entry)
533
*entry = data->state.async.dns;
534
535
return result;
536
}
537
538
/*
539
* Until we gain a way to signal the resolver threads to stop early, we must
540
* simply wait for them and ignore their results.
541
*/
542
void Curl_async_thrdd_shutdown(struct Curl_easy *data)
543
{
544
async_thrdd_shutdown(data);
545
}
546
547
void Curl_async_thrdd_destroy(struct Curl_easy *data)
548
{
549
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
550
551
if(thrdd->addr && !data->set.quick_exit) {
552
(void)asyn_thrdd_await(data, thrdd->addr, NULL);
553
}
554
async_thrdd_destroy(data);
555
}
556
557
/*
558
* Curl_async_await()
559
*
560
* Waits for a resolve to finish. This function should be avoided since using
561
* this risk getting the multi interface to "hang".
562
*
563
* If 'entry' is non-NULL, make it point to the resolved dns entry
564
*
565
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
566
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
567
*
568
* This is the version for resolves-in-a-thread.
569
*/
570
CURLcode Curl_async_await(struct Curl_easy *data,
571
struct Curl_dns_entry **entry)
572
{
573
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
574
if(thrdd->addr)
575
return asyn_thrdd_await(data, thrdd->addr, entry);
576
return CURLE_FAILED_INIT;
577
}
578
579
/*
580
* Curl_async_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
*/
584
CURLcode Curl_async_is_resolved(struct Curl_easy *data,
585
struct Curl_dns_entry **dns)
586
{
587
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
588
bool done = FALSE;
589
590
DEBUGASSERT(dns);
591
*dns = NULL;
592
593
if(data->state.async.done) {
594
*dns = data->state.async.dns;
595
CURL_TRC_DNS(data, "threaded: is_resolved(), already done, dns=%sfound",
596
*dns ? "" : "not ");
597
return CURLE_OK;
598
}
599
600
#ifdef USE_HTTPSRR_ARES
601
/* best effort, ignore errors */
602
if(thrdd->rr.channel)
603
(void)Curl_ares_perform(thrdd->rr.channel, 0);
604
#endif
605
606
DEBUGASSERT(thrdd->addr);
607
if(!thrdd->addr)
608
return CURLE_FAILED_INIT;
609
610
Curl_mutex_acquire(&thrdd->addr->mutx);
611
done = thrdd->addr->thrd_done;
612
Curl_mutex_release(&thrdd->addr->mutx);
613
614
if(done) {
615
CURLcode result = CURLE_OK;
616
617
data->state.async.done = TRUE;
618
Curl_resolv_unlink(data, &data->state.async.dns);
619
Curl_expire_done(data, EXPIRE_ASYNC_NAME);
620
621
if(thrdd->addr->res) {
622
data->state.async.dns =
623
Curl_dnscache_mk_entry(data, thrdd->addr->res,
624
data->state.async.hostname, 0,
625
data->state.async.port, FALSE);
626
thrdd->addr->res = NULL;
627
if(!data->state.async.dns)
628
result = CURLE_OUT_OF_MEMORY;
629
630
#ifdef USE_HTTPSRR_ARES
631
if(thrdd->rr.channel) {
632
result = thrdd->rr.result;
633
if(!result) {
634
struct Curl_https_rrinfo *lhrr;
635
lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo);
636
if(!lhrr)
637
result = CURLE_OUT_OF_MEMORY;
638
else
639
data->state.async.dns->hinfo = lhrr;
640
}
641
}
642
#endif
643
if(!result && data->state.async.dns)
644
result = Curl_dnscache_add(data, data->state.async.dns);
645
}
646
647
if(!result && !data->state.async.dns)
648
result = Curl_resolver_error(data, NULL);
649
if(result)
650
Curl_resolv_unlink(data, &data->state.async.dns);
651
*dns = data->state.async.dns;
652
CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
653
result, *dns ? "" : "not ");
654
async_thrdd_shutdown(data);
655
return result;
656
}
657
else {
658
/* poll for name lookup done with exponential backoff up to 250ms */
659
/* should be fine even if this converts to 32-bit */
660
timediff_t elapsed = curlx_timediff(curlx_now(),
661
data->progress.t_startsingle);
662
if(elapsed < 0)
663
elapsed = 0;
664
665
if(thrdd->addr->poll_interval == 0)
666
/* Start at 1ms poll interval */
667
thrdd->addr->poll_interval = 1;
668
else if(elapsed >= thrdd->addr->interval_end)
669
/* Back-off exponentially if last interval expired */
670
thrdd->addr->poll_interval *= 2;
671
672
if(thrdd->addr->poll_interval > 250)
673
thrdd->addr->poll_interval = 250;
674
675
thrdd->addr->interval_end = elapsed + thrdd->addr->poll_interval;
676
Curl_expire(data, thrdd->addr->poll_interval, EXPIRE_ASYNC_NAME);
677
return CURLE_OK;
678
}
679
}
680
681
CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
682
{
683
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
684
CURLcode result = CURLE_OK;
685
bool thrd_done;
686
687
#if !defined(USE_HTTPSRR_ARES) && defined(CURL_DISABLE_SOCKETPAIR)
688
(void)ps;
689
#endif
690
691
#ifdef USE_HTTPSRR_ARES
692
if(thrdd->rr.channel) {
693
result = Curl_ares_pollset(data, thrdd->rr.channel, ps);
694
if(result)
695
return result;
696
}
697
#endif
698
if(!thrdd->addr)
699
return result;
700
701
Curl_mutex_acquire(&thrdd->addr->mutx);
702
thrd_done = thrdd->addr->thrd_done;
703
Curl_mutex_release(&thrdd->addr->mutx);
704
705
if(!thrd_done) {
706
#ifndef CURL_DISABLE_SOCKETPAIR
707
/* return read fd to client for polling the DNS resolution status */
708
result = Curl_pollset_add_in(data, ps, thrdd->addr->sock_pair[0]);
709
#else
710
timediff_t milli;
711
timediff_t ms = curlx_timediff(curlx_now(), thrdd->addr->start);
712
if(ms < 3)
713
milli = 0;
714
else if(ms <= 50)
715
milli = ms/3;
716
else if(ms <= 250)
717
milli = 50;
718
else
719
milli = 200;
720
Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
721
#endif
722
}
723
return result;
724
}
725
726
#ifndef HAVE_GETADDRINFO
727
/*
728
* Curl_async_getaddrinfo() - for platforms without getaddrinfo
729
*/
730
struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
731
const char *hostname,
732
int port,
733
int ip_version,
734
int *waitp)
735
{
736
(void)ip_version;
737
*waitp = 0; /* default to synchronous response */
738
739
/* fire up a new resolver thread! */
740
if(async_thrdd_init(data, hostname, port, ip_version, NULL)) {
741
*waitp = 1; /* expect asynchronous response */
742
return NULL;
743
}
744
745
failf(data, "getaddrinfo() thread failed");
746
747
return NULL;
748
}
749
750
#else /* !HAVE_GETADDRINFO */
751
752
/*
753
* Curl_async_getaddrinfo() - for getaddrinfo
754
*/
755
struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
756
const char *hostname,
757
int port,
758
int ip_version,
759
int *waitp)
760
{
761
struct addrinfo hints;
762
int pf = PF_INET;
763
*waitp = 0; /* default to synchronous response */
764
765
CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port);
766
#ifdef CURLRES_IPV6
767
if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
768
/* The stack seems to be IPv6-enabled */
769
if(ip_version == CURL_IPRESOLVE_V6)
770
pf = PF_INET6;
771
else
772
pf = PF_UNSPEC;
773
}
774
#else
775
(void)ip_version;
776
#endif /* CURLRES_IPV6 */
777
778
memset(&hints, 0, sizeof(hints));
779
hints.ai_family = pf;
780
hints.ai_socktype =
781
(Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ?
782
SOCK_STREAM : SOCK_DGRAM;
783
784
/* fire up a new resolver thread! */
785
if(async_thrdd_init(data, hostname, port, ip_version, &hints)) {
786
*waitp = 1; /* expect asynchronous response */
787
return NULL;
788
}
789
790
failf(data, "getaddrinfo() thread failed to start");
791
return NULL;
792
793
}
794
795
#endif /* !HAVE_GETADDRINFO */
796
797
#endif /* CURLRES_THREADED */
798
799