/src/curl/lib/asyn-thrdd.c
Line | Count | Source |
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 | | #include "curl_setup.h" |
25 | | |
26 | | /*********************************************************************** |
27 | | * Only for threaded name resolves builds |
28 | | **********************************************************************/ |
29 | | #ifdef USE_RESOLV_THREADED |
30 | | |
31 | | #ifdef HAVE_NETINET_IN_H |
32 | | #include <netinet/in.h> |
33 | | #endif |
34 | | #ifdef HAVE_NETDB_H |
35 | | #include <netdb.h> |
36 | | #endif |
37 | | #ifdef HAVE_ARPA_INET_H |
38 | | #include <arpa/inet.h> |
39 | | #endif |
40 | | #ifdef __VMS |
41 | | #include <in.h> |
42 | | #include <inet.h> |
43 | | #endif |
44 | | |
45 | | #ifdef HAVE_GETADDRINFO |
46 | 0 | #define RESOLVER_ENOMEM EAI_MEMORY /* = WSA_NOT_ENOUGH_MEMORY on Windows */ |
47 | | #else |
48 | | #define RESOLVER_ENOMEM SOCKENOMEM |
49 | | #endif |
50 | | |
51 | | #include "urldata.h" |
52 | | #include "cfilters.h" |
53 | | #include "curl_addrinfo.h" |
54 | | #include "curl_trc.h" |
55 | | #include "hostip.h" |
56 | | #include "httpsrr.h" |
57 | | #include "url.h" |
58 | | #include "multiif.h" |
59 | | #include "curl_threads.h" |
60 | | #include "progress.h" |
61 | | #include "rand.h" |
62 | | #include "select.h" |
63 | | #include "thrdqueue.h" |
64 | | #include "curlx/strparse.h" |
65 | | #include "curlx/wait.h" |
66 | | |
67 | | #ifdef USE_ARES |
68 | | #include <ares.h> |
69 | | #ifdef USE_HTTPSRR |
70 | | #define USE_HTTPSRR_ARES /* the combo */ |
71 | | #endif |
72 | | #endif |
73 | | |
74 | | |
75 | | /* |
76 | | * Curl_async_global_init() |
77 | | * Called from curl_global_init() to initialize global resolver environment. |
78 | | * Does nothing here. |
79 | | */ |
80 | | int Curl_async_global_init(void) |
81 | 1 | { |
82 | | #if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT) |
83 | | if(ares_library_init(ARES_LIB_INIT_ALL)) { |
84 | | return CURLE_FAILED_INIT; |
85 | | } |
86 | | #endif |
87 | 1 | return CURLE_OK; |
88 | 1 | } |
89 | | |
90 | | /* |
91 | | * Curl_async_global_cleanup() |
92 | | * Called from curl_global_cleanup() to destroy global resolver environment. |
93 | | * Does nothing here. |
94 | | */ |
95 | | void Curl_async_global_cleanup(void) |
96 | 0 | { |
97 | | #if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT) |
98 | | ares_library_cleanup(); |
99 | | #endif |
100 | 0 | } |
101 | | |
102 | | #ifdef CURLVERBOSE |
103 | | #define CURL_ASYN_ITEM_DESC_LEN 64 |
104 | 0 | #define async_item_description(x) (x)->description |
105 | | #else |
106 | | #define async_item_description(x) NULL |
107 | | #endif |
108 | | |
109 | | struct async_thrdd_item { |
110 | | struct Curl_addrinfo *res; |
111 | | #ifdef CURLVERBOSE |
112 | | char description[CURL_ASYN_ITEM_DESC_LEN]; |
113 | | #endif |
114 | | int sock_error; |
115 | | uint32_t mid; |
116 | | uint32_t resolv_id; |
117 | | uint16_t port; |
118 | | uint8_t transport; |
119 | | uint8_t dns_queries; |
120 | | #ifdef DEBUGBUILD |
121 | | uint32_t delay_ms; |
122 | | uint32_t delay_fail_ms; |
123 | | #endif |
124 | | char hostname[1]; |
125 | | }; |
126 | | |
127 | | /* Give up reference to add_ctx */ |
128 | | static void async_thrdd_item_destroy(struct async_thrdd_item *item) |
129 | 0 | { |
130 | 0 | if(item) { |
131 | 0 | if(item->res) |
132 | 0 | Curl_freeaddrinfo(item->res); |
133 | 0 | curlx_free(item); |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | | /* Initialize context for threaded resolver */ |
138 | | static struct async_thrdd_item *async_thrdd_item_create( |
139 | | struct Curl_easy *data, |
140 | | uint32_t resolv_id, uint8_t dns_queries, |
141 | | const char *hostname, uint16_t port, |
142 | | uint8_t transport) |
143 | 0 | { |
144 | 0 | size_t hostlen = strlen(hostname); |
145 | 0 | struct async_thrdd_item *item; |
146 | |
|
147 | 0 | item = curlx_calloc(1, sizeof(*item) + hostlen); |
148 | 0 | if(!item) |
149 | 0 | return NULL; |
150 | | |
151 | 0 | if(hostlen) /* NUL byte of name already in struct size */ |
152 | 0 | memcpy(item->hostname, hostname, hostlen); |
153 | 0 | item->mid = data->mid; |
154 | 0 | item->resolv_id = resolv_id; |
155 | 0 | item->dns_queries = dns_queries; |
156 | 0 | item->port = port; |
157 | 0 | item->transport = transport; |
158 | |
|
159 | 0 | #ifdef CURLVERBOSE |
160 | 0 | curl_msnprintf(item->description, sizeof(item->description), |
161 | 0 | "[%" FMT_OFF_T "/%u] %s %s:%u", |
162 | 0 | data->id, item->resolv_id, |
163 | 0 | Curl_resolv_query_str(dns_queries), |
164 | 0 | item->hostname, item->port); |
165 | 0 | #endif |
166 | |
|
167 | 0 | #ifdef DEBUGBUILD |
168 | 0 | { |
169 | 0 | const char *p = getenv("CURL_DBG_RESOLV_DELAY"); |
170 | 0 | if(p) { |
171 | 0 | curl_off_t l; |
172 | 0 | if(!curlx_str_number(&p, &l, UINT32_MAX)) { |
173 | 0 | item->delay_ms = (uint32_t)l; |
174 | 0 | } |
175 | 0 | } |
176 | 0 | p = getenv("CURL_DBG_RESOLV_FAIL_DELAY"); |
177 | 0 | if(p) { |
178 | 0 | curl_off_t l; |
179 | 0 | if(!curlx_str_number(&p, &l, UINT32_MAX)) { |
180 | 0 | unsigned char c = 0; |
181 | 0 | Curl_rand_bytes(data, FALSE, &c, 1); |
182 | 0 | item->delay_fail_ms = (uint32_t)l + c; |
183 | 0 | } |
184 | 0 | } |
185 | 0 | } |
186 | 0 | #endif |
187 | |
|
188 | 0 | return item; |
189 | 0 | } |
190 | | |
191 | | #ifdef USE_HTTPSRR_ARES |
192 | | |
193 | | static void async_thrdd_rr_done(void *user_data, ares_status_t status, |
194 | | size_t timeouts, |
195 | | const ares_dns_record_t *dnsrec) |
196 | | { |
197 | | struct Curl_resolv_async *async = user_data; |
198 | | struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL; |
199 | | |
200 | | (void)timeouts; |
201 | | if(!thrdd) |
202 | | return; |
203 | | |
204 | | async->dns_responses |= CURL_DNSQ_HTTPS; |
205 | | async->queries_ongoing--; |
206 | | async->done = !async->queries_ongoing; |
207 | | if((ARES_SUCCESS == status) && dnsrec) |
208 | | async->result = Curl_httpsrr_from_ares(dnsrec, &thrdd->rr.hinfo); |
209 | | } |
210 | | |
211 | | static CURLcode async_rr_start(struct Curl_easy *data, |
212 | | struct Curl_resolv_async *async) |
213 | | { |
214 | | struct async_thrdd_ctx *thrdd = &async->thrdd; |
215 | | int status; |
216 | | char *rrname = NULL; |
217 | | |
218 | | DEBUGASSERT(!thrdd->rr.channel); |
219 | | if(async->port != 443) { |
220 | | rrname = curl_maprintf("_%d_.https.%s", async->port, async->hostname); |
221 | | if(!rrname) |
222 | | return CURLE_OUT_OF_MEMORY; |
223 | | } |
224 | | status = ares_init_options(&thrdd->rr.channel, NULL, 0); |
225 | | if(status != ARES_SUCCESS) { |
226 | | thrdd->rr.channel = NULL; |
227 | | curlx_free(rrname); |
228 | | return CURLE_FAILED_INIT; |
229 | | } |
230 | | #ifdef DEBUGBUILD |
231 | | if(getenv("CURL_DNS_SERVER")) { |
232 | | const char *servers = getenv("CURL_DNS_SERVER"); |
233 | | status = ares_set_servers_ports_csv(thrdd->rr.channel, servers); |
234 | | if(status) { |
235 | | curlx_free(rrname); |
236 | | return CURLE_FAILED_INIT; |
237 | | } |
238 | | } |
239 | | #endif |
240 | | |
241 | | memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo)); |
242 | | thrdd->rr.hinfo.rrname = rrname; |
243 | | async->queries_ongoing++; |
244 | | ares_query_dnsrec(thrdd->rr.channel, |
245 | | rrname ? rrname : async->hostname, ARES_CLASS_IN, |
246 | | ARES_REC_TYPE_HTTPS, |
247 | | async_thrdd_rr_done, async, NULL); |
248 | | CURL_TRC_DNS(data, "[HTTPS-RR] initiated request for %s", |
249 | | rrname ? rrname : async->hostname); |
250 | | return CURLE_OK; |
251 | | } |
252 | | #endif |
253 | | |
254 | | void Curl_async_thrdd_shutdown(struct Curl_easy *data, |
255 | | struct Curl_resolv_async *async) |
256 | 0 | { |
257 | 0 | Curl_async_thrdd_destroy(data, async); |
258 | 0 | } |
259 | | |
260 | | struct async_thrdd_match_ctx { |
261 | | uint32_t mid; |
262 | | uint32_t resolv_id; |
263 | | }; |
264 | | |
265 | | static bool async_thrdd_match_item(void *qitem, void *match_data) |
266 | 0 | { |
267 | 0 | const struct async_thrdd_match_ctx *ctx = match_data; |
268 | 0 | struct async_thrdd_item *item = qitem; |
269 | 0 | return (item->mid == ctx->mid) && (item->resolv_id == ctx->resolv_id); |
270 | 0 | } |
271 | | |
272 | | void Curl_async_thrdd_destroy(struct Curl_easy *data, |
273 | | struct Curl_resolv_async *async) |
274 | 0 | { |
275 | 0 | (void)data; |
276 | 0 | if(async->queries_ongoing && !async->done && |
277 | 0 | data->multi && data->multi->resolv_thrdq) { |
278 | | /* Remove any resolve items still queued */ |
279 | 0 | struct async_thrdd_match_ctx mctx; |
280 | 0 | mctx.mid = data->mid; |
281 | 0 | mctx.resolv_id = async->id; |
282 | 0 | Curl_thrdq_clear(data->multi->resolv_thrdq, |
283 | 0 | async_thrdd_match_item, &mctx); |
284 | 0 | } |
285 | | #ifdef USE_HTTPSRR_ARES |
286 | | if(async->thrdd.rr.channel) { |
287 | | ares_destroy(async->thrdd.rr.channel); |
288 | | async->thrdd.rr.channel = NULL; |
289 | | } |
290 | | Curl_httpsrr_cleanup(&async->thrdd.rr.hinfo); |
291 | | #endif |
292 | 0 | async_thrdd_item_destroy(async->thrdd.res_A); |
293 | 0 | async->thrdd.res_A = NULL; |
294 | 0 | async_thrdd_item_destroy(async->thrdd.res_AAAA); |
295 | 0 | async->thrdd.res_AAAA = NULL; |
296 | 0 | } |
297 | | |
298 | | /* |
299 | | * Waits for a resolve to finish. This function should be avoided since using |
300 | | * this risk getting the multi interface to "hang". |
301 | | */ |
302 | | CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, |
303 | | struct Curl_dns_entry **pdns) |
304 | 0 | { |
305 | 0 | struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); |
306 | 0 | struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL; |
307 | 0 | timediff_t milli, ms; |
308 | |
|
309 | 0 | if(!thrdd) |
310 | 0 | return CURLE_FAILED_INIT; |
311 | | |
312 | 0 | while(async->queries_ongoing && !async->done) { |
313 | 0 | Curl_async_thrdd_multi_process(data->multi); |
314 | 0 | if(async->done) |
315 | 0 | break; |
316 | | |
317 | 0 | ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); |
318 | 0 | if(ms < 3) |
319 | 0 | milli = 0; |
320 | 0 | else if(ms <= 50) |
321 | 0 | milli = ms / 3; |
322 | 0 | else if(ms <= 250) |
323 | 0 | milli = 50; |
324 | 0 | else |
325 | 0 | milli = 200; |
326 | 0 | CURL_TRC_DNS(data, "await, waiting %" FMT_TIMEDIFF_T "ms", milli); |
327 | 0 | curlx_wait_ms(milli); |
328 | 0 | } |
329 | 0 | return Curl_async_take_result(data, async, pdns); |
330 | 0 | } |
331 | | |
332 | | #ifdef HAVE_GETADDRINFO |
333 | | |
334 | | /* Process the item, using Curl_getaddrinfo_ex() */ |
335 | | static void async_thrdd_item_process(void *arg) |
336 | 0 | { |
337 | 0 | struct async_thrdd_item *item = arg; |
338 | 0 | struct addrinfo hints; |
339 | 0 | char service[12]; |
340 | 0 | int pf = PF_INET; |
341 | 0 | int rc; |
342 | |
|
343 | 0 | #ifdef DEBUGBUILD |
344 | 0 | if(item->delay_ms) { |
345 | 0 | curlx_wait_ms(item->delay_ms); |
346 | 0 | } |
347 | 0 | if(item->delay_fail_ms) { |
348 | 0 | curlx_wait_ms(item->delay_fail_ms); |
349 | 0 | return; |
350 | 0 | } |
351 | 0 | #endif |
352 | | |
353 | 0 | memset(&hints, 0, sizeof(hints)); |
354 | 0 | #ifdef CURLRES_IPV6 |
355 | 0 | if(item->dns_queries & CURL_DNSQ_AAAA) { |
356 | 0 | pf = (item->dns_queries & CURL_DNSQ_A) ? PF_UNSPEC : PF_INET6; |
357 | 0 | } |
358 | 0 | #endif |
359 | 0 | hints.ai_family = pf; |
360 | 0 | hints.ai_socktype = Curl_socktype_for_transport(item->transport); |
361 | 0 | hints.ai_protocol = Curl_protocol_for_transport(item->transport); |
362 | | #ifdef __APPLE__ |
363 | | /* If we leave `ai_flags == 0` then macOS is looking for IPV4MAPPED |
364 | | * when doing AAAA queries. We do not want this "help". */ |
365 | | hints.ai_flags = AI_ADDRCONFIG; |
366 | | #endif |
367 | |
|
368 | 0 | curl_msnprintf(service, sizeof(service), "%u", item->port); |
369 | 0 | #ifdef AI_NUMERICSERV |
370 | 0 | hints.ai_flags |= AI_NUMERICSERV; |
371 | 0 | #endif |
372 | |
|
373 | 0 | rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res); |
374 | 0 | if(rc) { |
375 | 0 | item->sock_error = SOCKERRNO ? SOCKERRNO : rc; |
376 | 0 | if(item->sock_error == 0) |
377 | 0 | item->sock_error = RESOLVER_ENOMEM; |
378 | 0 | } |
379 | 0 | else { |
380 | 0 | Curl_addrinfo_set_port(item->res, item->port); |
381 | 0 | } |
382 | 0 | } |
383 | | |
384 | | #else /* HAVE_GETADDRINFO */ |
385 | | |
386 | | /* Process the item, using Curl_ipv4_resolve_r() */ |
387 | | static void async_thrdd_item_process(void *arg) |
388 | | { |
389 | | struct async_thrdd_item *item = arg; |
390 | | |
391 | | #ifdef DEBUGBUILD |
392 | | if(item->delay_ms) { |
393 | | curlx_wait_ms(item->delay_ms); |
394 | | } |
395 | | if(item->delay_fail_ms) { |
396 | | curlx_wait_ms(item->delay_fail_ms); |
397 | | return; |
398 | | } |
399 | | #endif |
400 | | item->res = Curl_ipv4_resolve_r(item->hostname, item->port); |
401 | | if(!item->res) { |
402 | | item->sock_error = SOCKERRNO; |
403 | | if(item->sock_error == 0) |
404 | | item->sock_error = RESOLVER_ENOMEM; |
405 | | } |
406 | | } |
407 | | |
408 | | #endif /* HAVE_GETADDRINFO */ |
409 | | |
410 | | #ifdef ENABLE_WAKEUP |
411 | | static void async_thrdd_event(const struct curl_thrdq *tqueue, |
412 | | Curl_thrdq_event ev, |
413 | | void *user_data) |
414 | 0 | { |
415 | 0 | struct Curl_multi *multi = user_data; |
416 | 0 | (void)tqueue; |
417 | 0 | switch(ev) { |
418 | 0 | case CURL_THRDQ_EV_ITEM_DONE: |
419 | 0 | (void)curl_multi_wakeup(multi); |
420 | 0 | break; |
421 | 0 | default: |
422 | 0 | break; |
423 | 0 | } |
424 | 0 | } |
425 | | #else |
426 | | #define async_thrdd_event NULL |
427 | | #endif |
428 | | |
429 | | static void async_thrdd_item_free(void *item) |
430 | 0 | { |
431 | 0 | async_thrdd_item_destroy(item); |
432 | 0 | } |
433 | | |
434 | | /* Create a thread queue for processing resolv items */ |
435 | | CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi, |
436 | | uint32_t min_threads, |
437 | | uint32_t max_threads, |
438 | | uint32_t idle_time_ms) |
439 | 0 | { |
440 | 0 | CURLcode result; |
441 | 0 | DEBUGASSERT(!multi->resolv_thrdq); |
442 | 0 | result = Curl_thrdq_create(&multi->resolv_thrdq, "DNS", 0, |
443 | 0 | min_threads, max_threads, idle_time_ms, |
444 | 0 | async_thrdd_item_free, |
445 | 0 | async_thrdd_item_process, |
446 | 0 | async_thrdd_event, |
447 | 0 | multi); |
448 | 0 | #ifdef DEBUGBUILD |
449 | 0 | if(!result) { |
450 | 0 | const char *p = getenv("CURL_DBG_RESOLV_MAX_THREADS"); |
451 | 0 | if(p) { |
452 | 0 | curl_off_t l; |
453 | 0 | if(!curlx_str_number(&p, &l, UINT32_MAX)) { |
454 | 0 | result = Curl_async_thrdd_multi_set_props( |
455 | 0 | multi, min_threads, (uint32_t)l, idle_time_ms); |
456 | 0 | } |
457 | 0 | } |
458 | 0 | } |
459 | 0 | #endif |
460 | 0 | return result; |
461 | 0 | } |
462 | | |
463 | | /* Tear down the thread queue, joining active threads or detaching them */ |
464 | | void Curl_async_thrdd_multi_destroy(struct Curl_multi *multi, bool join) |
465 | 0 | { |
466 | 0 | if(multi->resolv_thrdq) { |
467 | 0 | #ifdef CURLVERBOSE |
468 | 0 | CURL_TRC_DNS(multi->admin, "destroy thread queue+pool, join=%d", join); |
469 | 0 | Curl_thrdq_trace(multi->resolv_thrdq, multi->admin); |
470 | 0 | #endif |
471 | 0 | Curl_thrdq_destroy(multi->resolv_thrdq, join); |
472 | 0 | multi->resolv_thrdq = NULL; |
473 | 0 | } |
474 | 0 | } |
475 | | |
476 | | #ifdef CURLVERBOSE |
477 | | static void async_thrdd_report_item(struct Curl_easy *data, |
478 | | struct async_thrdd_item *item) |
479 | 0 | { |
480 | 0 | char buf[MAX_IPADR_LEN]; |
481 | 0 | struct dynbuf tmp; |
482 | 0 | const char *sep = ""; |
483 | 0 | const struct Curl_addrinfo *ai = item->res; |
484 | 0 | int ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET; |
485 | 0 | CURLcode result; |
486 | |
|
487 | 0 | if(!CURL_TRC_DNS_is_verbose(data)) |
488 | 0 | return; |
489 | | |
490 | 0 | curlx_dyn_init(&tmp, 1024); |
491 | 0 | for(; ai; ai = ai->ai_next) { |
492 | 0 | if(ai->ai_family == ai_family) { |
493 | 0 | Curl_printable_address(ai, buf, sizeof(buf)); |
494 | 0 | result = curlx_dyn_addf(&tmp, "%s%s", sep, buf); |
495 | 0 | if(result) { |
496 | 0 | CURL_TRC_DNS(data, "too many IP, cannot show"); |
497 | 0 | goto out; |
498 | 0 | } |
499 | 0 | sep = ", "; |
500 | 0 | } |
501 | 0 | } |
502 | | |
503 | 0 | CURL_TRC_DNS(data, "Host %s:%u resolved IPv%c: %s", |
504 | 0 | item->hostname, item->port, |
505 | 0 | (item->dns_queries & CURL_DNSQ_AAAA) ? '6' : '4', |
506 | 0 | (curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)")); |
507 | 0 | out: |
508 | 0 | curlx_dyn_free(&tmp); |
509 | 0 | } |
510 | | #endif /* CURLVERBOSE */ |
511 | | |
512 | | /* Process the receiving end of the thread queue, dispatching |
513 | | * processed items to their transfer when it can still be found |
514 | | * and has an `async` state present. Otherwise, destroy the item. */ |
515 | | void Curl_async_thrdd_multi_process(struct Curl_multi *multi) |
516 | 0 | { |
517 | 0 | struct Curl_easy *data; |
518 | 0 | void *qitem; |
519 | |
|
520 | 0 | while(!Curl_thrdq_recv(multi->resolv_thrdq, &qitem)) { |
521 | | /* dispatch resolve result */ |
522 | 0 | struct async_thrdd_item *item = qitem; |
523 | 0 | struct Curl_resolv_async *async = NULL; |
524 | |
|
525 | 0 | data = Curl_multi_get_easy(multi, item->mid); |
526 | 0 | if(data) |
527 | 0 | async = Curl_async_get(data, item->resolv_id); |
528 | 0 | if(async) { |
529 | 0 | struct async_thrdd_item **pdest = &async->thrdd.res_A; |
530 | |
|
531 | 0 | async->dns_responses |= item->dns_queries; |
532 | 0 | --async->queries_ongoing; |
533 | 0 | async->done = !async->queries_ongoing; |
534 | |
|
535 | 0 | #ifdef CURLRES_IPV6 |
536 | 0 | if(item->dns_queries & CURL_DNSQ_AAAA) |
537 | 0 | pdest = &async->thrdd.res_AAAA; |
538 | 0 | #endif |
539 | 0 | if(!*pdest) { |
540 | 0 | VERBOSE(async_thrdd_report_item(data, item)); |
541 | 0 | *pdest = item; |
542 | 0 | item = NULL; |
543 | 0 | } |
544 | 0 | else |
545 | 0 | DEBUGASSERT(0); /* should not receive duplicates here */ |
546 | 0 | Curl_multi_mark_dirty(data); |
547 | 0 | } |
548 | 0 | async_thrdd_item_free(item); |
549 | 0 | } |
550 | 0 | #ifdef CURLVERBOSE |
551 | 0 | Curl_thrdq_trace(multi->resolv_thrdq, multi->admin); |
552 | 0 | #endif |
553 | 0 | } |
554 | | |
555 | | CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi, |
556 | | uint32_t min_threads, |
557 | | uint32_t max_threads, |
558 | | uint32_t idle_time_ms) |
559 | 0 | { |
560 | 0 | return Curl_thrdq_set_props(multi->resolv_thrdq, 0, |
561 | 0 | min_threads, max_threads, idle_time_ms); |
562 | 0 | } |
563 | | |
564 | | static CURLcode async_thrdd_query(struct Curl_easy *data, |
565 | | struct Curl_resolv_async *async, |
566 | | uint8_t dns_queries) |
567 | 0 | { |
568 | 0 | struct async_thrdd_item *item; |
569 | 0 | CURLcode result; |
570 | |
|
571 | 0 | item = async_thrdd_item_create(data, async->id, dns_queries, |
572 | 0 | async->hostname, async->port, |
573 | 0 | async->transport); |
574 | 0 | if(!item) { |
575 | 0 | result = CURLE_OUT_OF_MEMORY; |
576 | 0 | goto out; |
577 | 0 | } |
578 | 0 | CURL_TRC_DNS(data, "queueing query %s", item->description); |
579 | 0 | result = Curl_thrdq_send(data->multi->resolv_thrdq, item, |
580 | 0 | async_item_description(item), async->timeout_ms); |
581 | 0 | if(result) |
582 | 0 | goto out; |
583 | 0 | item = NULL; |
584 | 0 | async->queries_ongoing++; |
585 | |
|
586 | 0 | out: |
587 | 0 | if(item) |
588 | 0 | async_thrdd_item_free(item); |
589 | 0 | return result; |
590 | 0 | } |
591 | | |
592 | | CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, |
593 | | struct Curl_resolv_async *async) |
594 | 0 | { |
595 | 0 | CURLcode result = CURLE_FAILED_INIT; |
596 | 0 | void *resolver = NULL; |
597 | |
|
598 | 0 | if(async->queries_ongoing || async->done) |
599 | 0 | return CURLE_FAILED_INIT; |
600 | | |
601 | | #ifdef USE_HTTPSRR_ARES |
602 | | DEBUGASSERT(!async->thrdd.rr.channel); |
603 | | if((async->dns_queries & CURL_DNSQ_HTTPS) && !async->is_ipaddr) { |
604 | | result = async_rr_start(data, async); |
605 | | if(result) |
606 | | goto out; |
607 | | resolver = async->thrdd.rr.channel; |
608 | | } |
609 | | #endif |
610 | | |
611 | 0 | result = Curl_resolv_announce_start(data, resolver); |
612 | 0 | if(result) |
613 | 0 | return result; |
614 | | |
615 | 0 | #ifdef CURLRES_IPV6 |
616 | | /* Do not start an AAAA query for an ipv4 address when |
617 | | * we will start an A query for it. */ |
618 | 0 | if((async->dns_queries & CURL_DNSQ_AAAA) && |
619 | 0 | !(async->is_ipv4addr && (async->dns_queries & CURL_DNSQ_A))) { |
620 | 0 | result = async_thrdd_query(data, async, CURL_DNSQ_AAAA); |
621 | 0 | if(result) |
622 | 0 | goto out; |
623 | 0 | } |
624 | 0 | #endif |
625 | 0 | if(async->dns_queries & CURL_DNSQ_A) { |
626 | 0 | result = async_thrdd_query(data, async, CURL_DNSQ_A); |
627 | 0 | if(result) |
628 | 0 | goto out; |
629 | 0 | } |
630 | | |
631 | 0 | #ifdef CURLVERBOSE |
632 | 0 | Curl_thrdq_trace(data->multi->resolv_thrdq, data); |
633 | 0 | #endif |
634 | |
|
635 | 0 | out: |
636 | 0 | if(result) |
637 | 0 | CURL_TRC_DNS(data, "error queueing query %s:%d -> %d", |
638 | 0 | async->hostname, async->port, result); |
639 | 0 | return result; |
640 | 0 | } |
641 | | |
642 | | CURLcode Curl_async_pollset(struct Curl_easy *data, |
643 | | struct Curl_resolv_async *async, |
644 | | struct easy_pollset *ps) |
645 | 0 | { |
646 | 0 | timediff_t timeout_ms; |
647 | |
|
648 | 0 | timeout_ms = Curl_async_timeleft_ms(data, async); |
649 | | #ifdef USE_HTTPSRR_ARES |
650 | | if(async->thrdd.rr.channel) { |
651 | | CURLcode result = Curl_ares_pollset(data, async->thrdd.rr.channel, ps); |
652 | | if(result) |
653 | | return result; |
654 | | timeout_ms = Curl_ares_timeout_ms(data, async, async->thrdd.rr.channel); |
655 | | } |
656 | | #else |
657 | 0 | (void)ps; |
658 | 0 | #endif |
659 | |
|
660 | 0 | if(!async->done) { |
661 | | #ifndef ENABLE_WAKEUP |
662 | | timediff_t stutter_ms, elapsed_ms; |
663 | | elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); |
664 | | if(elapsed_ms < 3) |
665 | | stutter_ms = 1; |
666 | | else if(elapsed_ms <= 50) |
667 | | stutter_ms = elapsed_ms / 3; |
668 | | else if(elapsed_ms <= 250) |
669 | | stutter_ms = 50; |
670 | | else |
671 | | stutter_ms = 200; |
672 | | timeout_ms = CURLMIN(stutter_ms, timeout_ms); |
673 | | #endif |
674 | 0 | Curl_expire(data, timeout_ms, EXPIRE_ASYNC_NAME); |
675 | 0 | } |
676 | 0 | return CURLE_OK; |
677 | 0 | } |
678 | | |
679 | | /* |
680 | | * Curl_async_take_result() is called repeatedly to check if a previous |
681 | | * name resolve request has completed. It should also make sure to time-out if |
682 | | * the operation seems to take too long. |
683 | | */ |
684 | | CURLcode Curl_async_take_result(struct Curl_easy *data, |
685 | | struct Curl_resolv_async *async, |
686 | | struct Curl_dns_entry **pdns) |
687 | 0 | { |
688 | 0 | struct async_thrdd_ctx *thrdd = &async->thrdd; |
689 | 0 | struct Curl_dns_entry *dns = NULL; |
690 | 0 | CURLcode result = CURLE_OK; |
691 | |
|
692 | 0 | DEBUGASSERT(pdns); |
693 | 0 | *pdns = NULL; |
694 | 0 | if(!async->queries_ongoing && !async->done) { |
695 | 0 | DEBUGASSERT(0); |
696 | 0 | return CURLE_FAILED_INIT; |
697 | 0 | } |
698 | | |
699 | | #ifdef USE_HTTPSRR_ARES |
700 | | /* best effort, ignore errors */ |
701 | | if(thrdd->rr.channel) |
702 | | (void)Curl_ares_perform(thrdd->rr.channel, 0); |
703 | | #endif |
704 | | #ifndef ENABLE_WAKEUP |
705 | | Curl_async_thrdd_multi_process(data->multi); |
706 | | #endif |
707 | | |
708 | 0 | if(!async->done) |
709 | 0 | return CURLE_AGAIN; |
710 | | |
711 | 0 | Curl_expire_done(data, EXPIRE_ASYNC_NAME); |
712 | 0 | if(async->result) { |
713 | 0 | result = async->result; |
714 | 0 | goto out; |
715 | 0 | } |
716 | | |
717 | 0 | if((thrdd->res_A && thrdd->res_A->res) || |
718 | 0 | (thrdd->res_AAAA && thrdd->res_AAAA->res)) { |
719 | 0 | dns = Curl_dnscache_mk_entry2( |
720 | 0 | data, async->dns_queries, |
721 | 0 | thrdd->res_A ? &thrdd->res_A->res : NULL, |
722 | 0 | thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL, |
723 | 0 | async->hostname, async->port); |
724 | 0 | if(!dns) { |
725 | 0 | result = CURLE_OUT_OF_MEMORY; |
726 | 0 | goto out; |
727 | 0 | } |
728 | |
|
729 | | #ifdef USE_HTTPSRR_ARES |
730 | | if(thrdd->rr.channel) { |
731 | | struct Curl_https_rrinfo *lhrr = NULL; |
732 | | if(thrdd->rr.hinfo.complete) { |
733 | | lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo); |
734 | | if(!lhrr) { |
735 | | result = CURLE_OUT_OF_MEMORY; |
736 | | goto out; |
737 | | } |
738 | | } |
739 | | Curl_httpsrr_trace(data, lhrr); |
740 | | Curl_dns_entry_set_https_rr(dns, lhrr); |
741 | | } |
742 | | #endif |
743 | 0 | } |
744 | | |
745 | 0 | if(dns) { |
746 | 0 | *pdns = dns; |
747 | 0 | dns = NULL; |
748 | 0 | } |
749 | 0 | #ifdef CURLVERBOSE |
750 | 0 | Curl_thrdq_trace(data->multi->resolv_thrdq, data); |
751 | 0 | #endif |
752 | |
|
753 | 0 | out: |
754 | 0 | Curl_dns_entry_unlink(data, &dns); |
755 | 0 | Curl_async_thrdd_shutdown(data, async); |
756 | 0 | if(!result && !*pdns) |
757 | 0 | result = Curl_async_failed(data, async, NULL); |
758 | 0 | if(result && |
759 | 0 | (result != CURLE_COULDNT_RESOLVE_HOST) && |
760 | 0 | (result != CURLE_COULDNT_RESOLVE_PROXY)) { |
761 | 0 | CURL_TRC_DNS(data, "Error %d resolving %s:%d", |
762 | 0 | result, async->hostname, async->port); |
763 | 0 | } |
764 | 0 | return result; |
765 | 0 | } |
766 | | |
767 | | static const struct Curl_addrinfo *async_thrdd_get_ai( |
768 | | const struct Curl_addrinfo *ai, |
769 | | int ai_family, unsigned int index) |
770 | 0 | { |
771 | 0 | unsigned int i = 0; |
772 | 0 | for(i = 0; ai; ai = ai->ai_next) { |
773 | 0 | if(ai->ai_family == ai_family) { |
774 | 0 | if(i == index) |
775 | 0 | return ai; |
776 | 0 | ++i; |
777 | 0 | } |
778 | 0 | } |
779 | 0 | return NULL; |
780 | 0 | } |
781 | | |
782 | | const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data, |
783 | | struct Curl_resolv_async *async, |
784 | | int ai_family, |
785 | | unsigned int index) |
786 | 0 | { |
787 | 0 | struct async_thrdd_ctx *thrdd = &async->thrdd; |
788 | |
|
789 | 0 | (void)data; |
790 | 0 | switch(ai_family) { |
791 | 0 | case AF_INET: |
792 | 0 | if(thrdd->res_A) |
793 | 0 | return async_thrdd_get_ai(thrdd->res_A->res, ai_family, index); |
794 | 0 | break; |
795 | 0 | case AF_INET6: |
796 | 0 | if(thrdd->res_AAAA) |
797 | 0 | return async_thrdd_get_ai(thrdd->res_AAAA->res, ai_family, index); |
798 | 0 | break; |
799 | 0 | default: |
800 | 0 | break; |
801 | 0 | } |
802 | 0 | return NULL; |
803 | 0 | } |
804 | | |
805 | | #ifdef USE_HTTPSRR |
806 | | const struct Curl_https_rrinfo *Curl_async_get_https( |
807 | | struct Curl_easy *data, |
808 | | struct Curl_resolv_async *async) |
809 | | { |
810 | | #ifdef USE_HTTPSRR_ARES |
811 | | if(Curl_async_knows_https(data, async)) |
812 | | return &async->thrdd.rr.hinfo; |
813 | | #else |
814 | | (void)data; |
815 | | (void)async; |
816 | | #endif |
817 | | return NULL; |
818 | | } |
819 | | |
820 | | bool Curl_async_knows_https(struct Curl_easy *data, |
821 | | struct Curl_resolv_async *async) |
822 | | { |
823 | | (void)data; |
824 | | if(async->dns_queries & CURL_DNSQ_HTTPS) |
825 | | return ((async->dns_responses & CURL_DNSQ_HTTPS) || async->done); |
826 | | return TRUE; /* we know it will never come */ |
827 | | } |
828 | | |
829 | | #endif /* USE_HTTPSRR */ |
830 | | |
831 | | #endif /* USE_RESOLV_THREADED */ |