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
27 /***********************************************************************
28 * Only for ares-enabled builds
29 * And only for functions that fulfill the asynch resolver backend API
30 * as defined in asyn.h, nothing else belongs in this file!
31 **********************************************************************/
32
33 #ifdef CURLRES_ARES
34
35 #include <limits.h>
36 #ifdef HAVE_NETINET_IN_H
37 #include <netinet/in.h>
38 #endif
39 #ifdef HAVE_NETDB_H
40 #include <netdb.h>
41 #endif
42 #ifdef HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
44 #endif
45 #ifdef __VMS
46 #include <in.h>
47 #include <inet.h>
48 #endif
49
50 #include "urldata.h"
51 #include "sendf.h"
52 #include "hostip.h"
53 #include "hash.h"
54 #include "share.h"
55 #include "url.h"
56 #include "multiif.h"
57 #include "inet_pton.h"
58 #include "connect.h"
59 #include "select.h"
60 #include "progress.h"
61 #include "timediff.h"
62
63 #if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
64 defined(_WIN32)
65 # define CARES_STATICLIB
66 #endif
67 #include <ares.h>
68 #include <ares_version.h> /* really old c-ares didn't include this by
69 itself */
70
71 #if ARES_VERSION >= 0x010500
72 /* c-ares 1.5.0 or later, the callback proto is modified */
73 #define HAVE_CARES_CALLBACK_TIMEOUTS 1
74 #endif
75
76 #if ARES_VERSION >= 0x010601
77 /* IPv6 supported since 1.6.1 */
78 #define HAVE_CARES_IPV6 1
79 #endif
80
81 #if ARES_VERSION >= 0x010704
82 #define HAVE_CARES_SERVERS_CSV 1
83 #define HAVE_CARES_LOCAL_DEV 1
84 #define HAVE_CARES_SET_LOCAL 1
85 #endif
86
87 #if ARES_VERSION >= 0x010b00
88 #define HAVE_CARES_PORTS_CSV 1
89 #endif
90
91 #if ARES_VERSION >= 0x011000
92 /* 1.16.0 or later has ares_getaddrinfo */
93 #define HAVE_CARES_GETADDRINFO 1
94 #endif
95
96 /* The last 3 #include files should be in this order */
97 #include "curl_printf.h"
98 #include "curl_memory.h"
99 #include "memdebug.h"
100
101 struct thread_data {
102 int num_pending; /* number of outstanding c-ares requests */
103 struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
104 parts */
105 int last_status;
106 #ifndef HAVE_CARES_GETADDRINFO
107 struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
108 #endif
109 char hostname[1];
110 };
111
112 /* How long we are willing to wait for additional parallel responses after
113 obtaining a "definitive" one. For old c-ares without getaddrinfo.
114
115 This is intended to equal the c-ares default timeout. cURL always uses that
116 default value. Unfortunately, c-ares doesn't expose its default timeout in
117 its API, but it is officially documented as 5 seconds.
118
119 See query_completed_cb() for an explanation of how this is used.
120 */
121 #define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
122
123 #define CARES_TIMEOUT_PER_ATTEMPT 2000
124
125 static int ares_ver = 0;
126
127 /*
128 * Curl_resolver_global_init() - the generic low-level asynchronous name
129 * resolve API. Called from curl_global_init() to initialize global resolver
130 * environment. Initializes ares library.
131 */
Curl_resolver_global_init(void)132 int Curl_resolver_global_init(void)
133 {
134 #ifdef CARES_HAVE_ARES_LIBRARY_INIT
135 if(ares_library_init(ARES_LIB_INIT_ALL)) {
136 return CURLE_FAILED_INIT;
137 }
138 #endif
139 ares_version(&ares_ver);
140 return CURLE_OK;
141 }
142
143 /*
144 * Curl_resolver_global_cleanup()
145 *
146 * Called from curl_global_cleanup() to destroy global resolver environment.
147 * Deinitializes ares library.
148 */
Curl_resolver_global_cleanup(void)149 void Curl_resolver_global_cleanup(void)
150 {
151 #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
152 ares_library_cleanup();
153 #endif
154 }
155
156
sock_state_cb(void * data,ares_socket_t socket_fd,int readable,int writable)157 static void sock_state_cb(void *data, ares_socket_t socket_fd,
158 int readable, int writable)
159 {
160 struct Curl_easy *easy = data;
161 if(!readable && !writable) {
162 DEBUGASSERT(easy);
163 Curl_multi_closed(easy, socket_fd);
164 }
165 }
166
167 #ifdef HTTP_HANDOVER_FEATURE
168 int32_t BindSocket(int32_t fd, uint32_t netId);
bindohosnetid(ares_socket_t socket_fd,int type,void * data)169 static int bindohosnetid(ares_socket_t socket_fd, int type, void *data)
170 {
171 struct Curl_easy *easy = (struct Curl_easy *)data;
172 if (!easy || socket_fd <= 0) {
173 return -1;
174 }
175 unsigned int netid = easy->set.socket_bind_netid;
176 if (netid > 0) {
177 int ret = BindSocket(socket_fd, netid);
178 if (ret == 0) {
179 return 0;
180 } else {
181 // try bind but failed
182 return -1;
183 }
184 }
185 return 0;
186 }
187 #endif
188
189 /*
190 * Curl_resolver_init()
191 *
192 * Called from curl_easy_init() -> Curl_open() to initialize resolver
193 * URL-state specific environment ('resolver' member of the UrlState
194 * structure). Fills the passed pointer by the initialized ares_channel.
195 */
Curl_resolver_init(struct Curl_easy * easy,void ** resolver)196 CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
197 {
198 int status;
199 struct ares_options options;
200 int optmask = ARES_OPT_SOCK_STATE_CB;
201 options.sock_state_cb = sock_state_cb;
202 options.sock_state_cb_data = easy;
203
204 /*
205 if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s)
206
207 if c-ares >= 1.20.0 it already has the timeout to 2s, curl does not need
208 to set the timeout value;
209
210 if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to
211 overwrite c-ares' timeout.
212 */
213 DEBUGASSERT(ares_ver);
214 if(ares_ver < 0x011400) {
215 options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
216 optmask |= ARES_OPT_TIMEOUTMS;
217 }
218
219 status = ares_init_options((ares_channel*)resolver, &options, optmask);
220 if(status != ARES_SUCCESS) {
221 if(status == ARES_ENOMEM)
222 return CURLE_OUT_OF_MEMORY;
223 else
224 return CURLE_FAILED_INIT;
225 }
226 #ifdef HTTP_HANDOVER_FEATURE
227 if (easy->set.socket_bind_netid > 0) {
228 ares_set_socket_configure_callback(resolver, bindohosnetid, easy);
229 }
230 #endif
231 return CURLE_OK;
232 /* make sure that all other returns from this function should destroy the
233 ares channel before returning error! */
234 }
235
236 /*
237 * Curl_resolver_cleanup()
238 *
239 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
240 * URL-state specific environment ('resolver' member of the UrlState
241 * structure). Destroys the ares channel.
242 */
Curl_resolver_cleanup(void * resolver)243 void Curl_resolver_cleanup(void *resolver)
244 {
245 ares_destroy((ares_channel)resolver);
246 }
247
248 /*
249 * Curl_resolver_duphandle()
250 *
251 * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
252 * environment ('resolver' member of the UrlState structure). Duplicates the
253 * 'from' ares channel and passes the resulting channel to the 'to' pointer.
254 */
Curl_resolver_duphandle(struct Curl_easy * easy,void ** to,void * from)255 CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
256 {
257 (void)from;
258 /*
259 * it would be better to call ares_dup instead, but right now
260 * it is not possible to set 'sock_state_cb_data' outside of
261 * ares_init_options
262 */
263 return Curl_resolver_init(easy, to);
264 }
265
266 static void destroy_async_data(struct Curl_async *async);
267
268 /*
269 * Cancel all possibly still on-going resolves for this connection.
270 */
Curl_resolver_cancel(struct Curl_easy * data)271 void Curl_resolver_cancel(struct Curl_easy *data)
272 {
273 DEBUGASSERT(data);
274 if(data->state.async.resolver)
275 ares_cancel((ares_channel)data->state.async.resolver);
276 destroy_async_data(&data->state.async);
277 }
278
279 /*
280 * We're equivalent to Curl_resolver_cancel() for the c-ares resolver. We
281 * never block.
282 */
Curl_resolver_kill(struct Curl_easy * data)283 void Curl_resolver_kill(struct Curl_easy *data)
284 {
285 /* We don't need to check the resolver state because we can be called safely
286 at any time and we always do the same thing. */
287 Curl_resolver_cancel(data);
288 }
289
290 /*
291 * destroy_async_data() cleans up async resolver data.
292 */
destroy_async_data(struct Curl_async * async)293 static void destroy_async_data(struct Curl_async *async)
294 {
295 if(async->tdata) {
296 struct thread_data *res = async->tdata;
297 if(res) {
298 if(res->temp_ai) {
299 Curl_freeaddrinfo(res->temp_ai);
300 res->temp_ai = NULL;
301 }
302 free(res);
303 }
304 async->tdata = NULL;
305 }
306 }
307
308 /*
309 * Curl_resolver_getsock() is called when someone from the outside world
310 * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
311 * with ares. The caller must make sure that this function is only called when
312 * we have a working ares channel.
313 *
314 * Returns: sockets-in-use-bitmap
315 */
316
Curl_resolver_getsock(struct Curl_easy * data,curl_socket_t * socks)317 int Curl_resolver_getsock(struct Curl_easy *data,
318 curl_socket_t *socks)
319 {
320 struct timeval maxtime;
321 struct timeval timebuf;
322 struct timeval *timeout;
323 long milli;
324 int max = ares_getsock((ares_channel)data->state.async.resolver,
325 (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
326
327 maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
328 maxtime.tv_usec = 0;
329
330 timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime,
331 &timebuf);
332 milli = (long)curlx_tvtoms(timeout);
333 if(milli == 0)
334 milli += 10;
335 Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
336
337 return max;
338 }
339
340 /*
341 * waitperform()
342 *
343 * 1) Ask ares what sockets it currently plays with, then
344 * 2) wait for the timeout period to check for action on ares' sockets.
345 * 3) tell ares to act on all the sockets marked as "with action"
346 *
347 * return number of sockets it worked on, or -1 on error
348 */
349
waitperform(struct Curl_easy * data,timediff_t timeout_ms)350 static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
351 {
352 int nfds;
353 int bitmask;
354 ares_socket_t socks[ARES_GETSOCK_MAXNUM];
355 struct pollfd pfd[ARES_GETSOCK_MAXNUM];
356 int i;
357 int num = 0;
358
359 bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks,
360 ARES_GETSOCK_MAXNUM);
361
362 for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
363 pfd[i].events = 0;
364 pfd[i].revents = 0;
365 if(ARES_GETSOCK_READABLE(bitmask, i)) {
366 pfd[i].fd = socks[i];
367 pfd[i].events |= POLLRDNORM|POLLIN;
368 }
369 if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
370 pfd[i].fd = socks[i];
371 pfd[i].events |= POLLWRNORM|POLLOUT;
372 }
373 if(pfd[i].events)
374 num++;
375 else
376 break;
377 }
378
379 if(num) {
380 nfds = Curl_poll(pfd, num, timeout_ms);
381 if(nfds < 0)
382 return -1;
383 }
384 else
385 nfds = 0;
386
387 if(!nfds)
388 /* Call ares_process() unconditionally here, even if we simply timed out
389 above, as otherwise the ares name resolve won't timeout! */
390 ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD,
391 ARES_SOCKET_BAD);
392 else {
393 /* move through the descriptors and ask for processing on them */
394 for(i = 0; i < num; i++)
395 ares_process_fd((ares_channel)data->state.async.resolver,
396 (pfd[i].revents & (POLLRDNORM|POLLIN))?
397 pfd[i].fd:ARES_SOCKET_BAD,
398 (pfd[i].revents & (POLLWRNORM|POLLOUT))?
399 pfd[i].fd:ARES_SOCKET_BAD);
400 }
401 return nfds;
402 }
403
404 /*
405 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
406 * name resolve request has completed. It should also make sure to time-out if
407 * the operation seems to take too long.
408 *
409 * Returns normal CURLcode errors.
410 */
Curl_resolver_is_resolved(struct Curl_easy * data,struct Curl_dns_entry ** dns)411 CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
412 struct Curl_dns_entry **dns)
413 {
414 struct thread_data *res = data->state.async.tdata;
415 CURLcode result = CURLE_OK;
416
417 DEBUGASSERT(dns);
418 *dns = NULL;
419
420 if(waitperform(data, 0) < 0)
421 return CURLE_UNRECOVERABLE_POLL;
422
423 #ifndef HAVE_CARES_GETADDRINFO
424 /* Now that we've checked for any last minute results above, see if there are
425 any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
426 expires. */
427 if(res
428 && res->num_pending
429 /* This is only set to non-zero if the timer was started. */
430 && (res->happy_eyeballs_dns_time.tv_sec
431 || res->happy_eyeballs_dns_time.tv_usec)
432 && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time)
433 >= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
434 /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
435 running. */
436 memset(
437 &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time));
438
439 /* Cancel the raw c-ares request, which will fire query_completed_cb() with
440 ARES_ECANCELLED synchronously for all pending responses. This will
441 leave us with res->num_pending == 0, which is perfect for the next
442 block. */
443 ares_cancel((ares_channel)data->state.async.resolver);
444 DEBUGASSERT(res->num_pending == 0);
445 }
446 #endif
447
448 if(res && !res->num_pending) {
449 (void)Curl_addrinfo_callback(data, res->last_status, res->temp_ai);
450 /* temp_ai ownership is moved to the connection, so we need not free-up
451 them */
452 res->temp_ai = NULL;
453
454 if(!data->state.async.dns)
455 result = Curl_resolver_error(data);
456 else
457 *dns = data->state.async.dns;
458
459 destroy_async_data(&data->state.async);
460 }
461
462 return result;
463 }
464
465 /*
466 * Curl_resolver_wait_resolv()
467 *
468 * Waits for a resolve to finish. This function should be avoided since using
469 * this risk getting the multi interface to "hang".
470 *
471 * 'entry' MUST be non-NULL.
472 *
473 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
474 * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
475 */
Curl_resolver_wait_resolv(struct Curl_easy * data,struct Curl_dns_entry ** entry)476 CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
477 struct Curl_dns_entry **entry)
478 {
479 CURLcode result = CURLE_OK;
480 timediff_t timeout;
481 struct curltime now = Curl_now();
482
483 DEBUGASSERT(entry);
484 *entry = NULL; /* clear on entry */
485
486 timeout = Curl_timeleft(data, &now, TRUE);
487 if(timeout < 0) {
488 /* already expired! */
489 connclose(data->conn, "Timed out before name resolve started");
490 return CURLE_OPERATION_TIMEDOUT;
491 }
492 if(!timeout)
493 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
494
495 /* Wait for the name resolve query to complete. */
496 while(!result) {
497 struct timeval *tvp, tv, store;
498 int itimeout;
499 timediff_t timeout_ms;
500
501 #if TIMEDIFF_T_MAX > INT_MAX
502 itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout;
503 #else
504 itimeout = (int)timeout;
505 #endif
506
507 store.tv_sec = itimeout/1000;
508 store.tv_usec = (itimeout%1000)*1000;
509
510 tvp = ares_timeout((ares_channel)data->state.async.resolver, &store, &tv);
511
512 /* use the timeout period ares returned to us above if less than one
513 second is left, otherwise just use 1000ms to make sure the progress
514 callback gets called frequent enough */
515 if(!tvp->tv_sec)
516 timeout_ms = (timediff_t)(tvp->tv_usec/1000);
517 else
518 timeout_ms = 1000;
519
520 if(waitperform(data, timeout_ms) < 0)
521 return CURLE_UNRECOVERABLE_POLL;
522 result = Curl_resolver_is_resolved(data, entry);
523
524 if(result || data->state.async.done)
525 break;
526
527 if(Curl_pgrsUpdate(data))
528 result = CURLE_ABORTED_BY_CALLBACK;
529 else {
530 struct curltime now2 = Curl_now();
531 timediff_t timediff = Curl_timediff(now2, now); /* spent time */
532 if(timediff <= 0)
533 timeout -= 1; /* always deduct at least 1 */
534 else if(timediff > timeout)
535 timeout = -1;
536 else
537 timeout -= timediff;
538 now = now2; /* for next loop */
539 }
540 if(timeout < 0)
541 result = CURLE_OPERATION_TIMEDOUT;
542 }
543 if(result)
544 /* failure, so we cancel the ares operation */
545 ares_cancel((ares_channel)data->state.async.resolver);
546
547 /* Operation complete, if the lookup was successful we now have the entry
548 in the cache. */
549 if(entry)
550 *entry = data->state.async.dns;
551
552 if(result)
553 /* close the connection, since we can't return failure here without
554 cleaning up this connection properly. */
555 connclose(data->conn, "c-ares resolve failed");
556
557 return result;
558 }
559
560 #ifndef HAVE_CARES_GETADDRINFO
561
562 /* Connects results to the list */
compound_results(struct thread_data * res,struct Curl_addrinfo * ai)563 static void compound_results(struct thread_data *res,
564 struct Curl_addrinfo *ai)
565 {
566 if(!ai)
567 return;
568
569 #ifdef USE_IPV6 /* CURLRES_IPV6 */
570 if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) {
571 /* We have results already, put the new IPv6 entries at the head of the
572 list. */
573 struct Curl_addrinfo *temp_ai_tail = res->temp_ai;
574
575 while(temp_ai_tail->ai_next)
576 temp_ai_tail = temp_ai_tail->ai_next;
577
578 temp_ai_tail->ai_next = ai;
579 }
580 else
581 #endif /* CURLRES_IPV6 */
582 {
583 /* Add the new results to the list of old results. */
584 struct Curl_addrinfo *ai_tail = ai;
585 while(ai_tail->ai_next)
586 ai_tail = ai_tail->ai_next;
587
588 ai_tail->ai_next = res->temp_ai;
589 res->temp_ai = ai;
590 }
591 }
592
593 /*
594 * ares_query_completed_cb() is the callback that ares will call when
595 * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
596 * when using ares, is completed either successfully or with failure.
597 */
query_completed_cb(void * arg,int status,int timeouts,struct hostent * hostent)598 static void query_completed_cb(void *arg, /* (struct connectdata *) */
599 int status,
600 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
601 int timeouts,
602 #endif
603 struct hostent *hostent)
604 {
605 struct Curl_easy *data = (struct Curl_easy *)arg;
606 struct thread_data *res;
607
608 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
609 (void)timeouts; /* ignored */
610 #endif
611
612 if(ARES_EDESTRUCTION == status)
613 /* when this ares handle is getting destroyed, the 'arg' pointer may not
614 be valid so only defer it when we know the 'status' says its fine! */
615 return;
616
617 res = data->state.async.tdata;
618 if(res) {
619 res->num_pending--;
620
621 if(CURL_ASYNC_SUCCESS == status) {
622 struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port);
623 if(ai) {
624 compound_results(res, ai);
625 }
626 }
627 /* A successful result overwrites any previous error */
628 if(res->last_status != ARES_SUCCESS)
629 res->last_status = status;
630
631 /* If there are responses still pending, we presume they must be the
632 complementary IPv4 or IPv6 lookups that we started in parallel in
633 Curl_resolver_getaddrinfo() (for Happy Eyeballs). If we've got a
634 "definitive" response from one of a set of parallel queries, we need to
635 think about how long we're willing to wait for more responses. */
636 if(res->num_pending
637 /* Only these c-ares status values count as "definitive" for these
638 purposes. For example, ARES_ENODATA is what we expect when there is
639 no IPv6 entry for a domain name, and that's not a reason to get more
640 aggressive in our timeouts for the other response. Other errors are
641 either a result of bad input (which should affect all parallel
642 requests), local or network conditions, non-definitive server
643 responses, or us cancelling the request. */
644 && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
645 /* Right now, there can only be up to two parallel queries, so don't
646 bother handling any other cases. */
647 DEBUGASSERT(res->num_pending == 1);
648
649 /* It's possible that one of these parallel queries could succeed
650 quickly, but the other could always fail or timeout (when we're
651 talking to a pool of DNS servers that can only successfully resolve
652 IPv4 address, for example).
653
654 It's also possible that the other request could always just take
655 longer because it needs more time or only the second DNS server can
656 fulfill it successfully. But, to align with the philosophy of Happy
657 Eyeballs, we don't want to wait _too_ long or users will think
658 requests are slow when IPv6 lookups don't actually work (but IPv4 ones
659 do).
660
661 So, now that we have a usable answer (some IPv4 addresses, some IPv6
662 addresses, or "no such domain"), we start a timeout for the remaining
663 pending responses. Even though it is typical that this resolved
664 request came back quickly, that needn't be the case. It might be that
665 this completing request didn't get a result from the first DNS server
666 or even the first round of the whole DNS server pool. So it could
667 already be quite some time after we issued the DNS queries in the
668 first place. Without modifying c-ares, we can't know exactly where in
669 its retry cycle we are. We could guess based on how much time has
670 gone by, but it doesn't really matter. Happy Eyeballs tells us that,
671 given usable information in hand, we simply don't want to wait "too
672 much longer" after we get a result.
673
674 We simply wait an additional amount of time equal to the default
675 c-ares query timeout. That is enough time for a typical parallel
676 response to arrive without being "too long". Even on a network
677 where one of the two types of queries is failing or timing out
678 constantly, this will usually mean we wait a total of the default
679 c-ares timeout (5 seconds) plus the round trip time for the successful
680 request, which seems bearable. The downside is that c-ares might race
681 with us to issue one more retry just before we give up, but it seems
682 better to "waste" that request instead of trying to guess the perfect
683 timeout to prevent it. After all, we don't even know where in the
684 c-ares retry cycle each request is.
685 */
686 res->happy_eyeballs_dns_time = Curl_now();
687 Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT,
688 EXPIRE_HAPPY_EYEBALLS_DNS);
689 }
690 }
691 }
692 #else
693 /* c-ares 1.16.0 or later */
694
695 /*
696 * ares2addr() converts an address list provided by c-ares to an internal
697 * libcurl compatible list
698 */
ares2addr(struct ares_addrinfo_node * node)699 static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node)
700 {
701 /* traverse the ares_addrinfo_node list */
702 struct ares_addrinfo_node *ai;
703 struct Curl_addrinfo *cafirst = NULL;
704 struct Curl_addrinfo *calast = NULL;
705 int error = 0;
706
707 for(ai = node; ai != NULL; ai = ai->ai_next) {
708 size_t ss_size;
709 struct Curl_addrinfo *ca;
710 /* ignore elements with unsupported address family, */
711 /* settle family-specific sockaddr structure size. */
712 if(ai->ai_family == AF_INET)
713 ss_size = sizeof(struct sockaddr_in);
714 #ifdef USE_IPV6
715 else if(ai->ai_family == AF_INET6)
716 ss_size = sizeof(struct sockaddr_in6);
717 #endif
718 else
719 continue;
720
721 /* ignore elements without required address info */
722 if(!ai->ai_addr || !(ai->ai_addrlen > 0))
723 continue;
724
725 /* ignore elements with bogus address size */
726 if((size_t)ai->ai_addrlen < ss_size)
727 continue;
728
729 ca = malloc(sizeof(struct Curl_addrinfo) + ss_size);
730 if(!ca) {
731 error = EAI_MEMORY;
732 break;
733 }
734
735 /* copy each structure member individually, member ordering, */
736 /* size, or padding might be different for each platform. */
737
738 ca->ai_flags = ai->ai_flags;
739 ca->ai_family = ai->ai_family;
740 ca->ai_socktype = ai->ai_socktype;
741 ca->ai_protocol = ai->ai_protocol;
742 ca->ai_addrlen = (curl_socklen_t)ss_size;
743 ca->ai_addr = NULL;
744 ca->ai_canonname = NULL;
745 ca->ai_next = NULL;
746
747 ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
748 memcpy(ca->ai_addr, ai->ai_addr, ss_size);
749
750 /* if the return list is empty, this becomes the first element */
751 if(!cafirst)
752 cafirst = ca;
753
754 /* add this element last in the return list */
755 if(calast)
756 calast->ai_next = ca;
757 calast = ca;
758 }
759
760 /* if we failed, destroy the Curl_addrinfo list */
761 if(error) {
762 Curl_freeaddrinfo(cafirst);
763 cafirst = NULL;
764 }
765
766 return cafirst;
767 }
768
addrinfo_cb(void * arg,int status,int timeouts,struct ares_addrinfo * result)769 static void addrinfo_cb(void *arg, int status, int timeouts,
770 struct ares_addrinfo *result)
771 {
772 struct Curl_easy *data = (struct Curl_easy *)arg;
773 struct thread_data *res = data->state.async.tdata;
774 (void)timeouts;
775 if(ARES_SUCCESS == status) {
776 res->temp_ai = ares2addr(result->nodes);
777 res->last_status = CURL_ASYNC_SUCCESS;
778 ares_freeaddrinfo(result);
779 }
780 res->num_pending--;
781 }
782
783 #endif
784 /*
785 * Curl_resolver_getaddrinfo() - when using ares
786 *
787 * Returns name information about the given hostname and port number. If
788 * successful, the 'hostent' is returned and the fourth argument will point to
789 * memory we need to free after use. That memory *MUST* be freed with
790 * Curl_freeaddrinfo(), nothing else.
791 */
Curl_resolver_getaddrinfo(struct Curl_easy * data,const char * hostname,int port,int * waitp)792 struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
793 const char *hostname,
794 int port,
795 int *waitp)
796 {
797 struct thread_data *res = NULL;
798 size_t namelen = strlen(hostname);
799 *waitp = 0; /* default to synchronous response */
800
801 res = calloc(1, sizeof(struct thread_data) + namelen);
802 if(res) {
803 strcpy(res->hostname, hostname);
804 data->state.async.hostname = res->hostname;
805 data->state.async.port = port;
806 data->state.async.done = FALSE; /* not done */
807 data->state.async.status = 0; /* clear */
808 data->state.async.dns = NULL; /* clear */
809 data->state.async.tdata = res;
810
811 /* initial status - failed */
812 res->last_status = ARES_ENOTFOUND;
813
814 #ifdef HAVE_CARES_GETADDRINFO
815 {
816 struct ares_addrinfo_hints hints;
817 char service[12];
818 int pf = PF_INET;
819 memset(&hints, 0, sizeof(hints));
820 #ifdef CURLRES_IPV6
821 if((data->conn->ip_version != CURL_IPRESOLVE_V4) &&
822 Curl_ipv6works(data)) {
823 /* The stack seems to be IPv6-enabled */
824 if(data->conn->ip_version == CURL_IPRESOLVE_V6)
825 pf = PF_INET6;
826 else
827 pf = PF_UNSPEC;
828 }
829 #endif /* CURLRES_IPV6 */
830 hints.ai_family = pf;
831 hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
832 SOCK_STREAM : SOCK_DGRAM;
833 /* Since the service is a numerical one, set the hint flags
834 * accordingly to save a call to getservbyname in inside C-Ares
835 */
836 hints.ai_flags = ARES_AI_NUMERICSERV;
837 msnprintf(service, sizeof(service), "%d", port);
838 res->num_pending = 1;
839 ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
840 service, &hints, addrinfo_cb, data);
841 }
842 #else
843
844 #ifdef HAVE_CARES_IPV6
845 if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
846 /* The stack seems to be IPv6-enabled */
847 res->num_pending = 2;
848
849 /* areschannel is already setup in the Curl_open() function */
850 ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
851 PF_INET, query_completed_cb, data);
852 ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
853 PF_INET6, query_completed_cb, data);
854 }
855 else
856 #endif
857 {
858 res->num_pending = 1;
859
860 /* areschannel is already setup in the Curl_open() function */
861 ares_gethostbyname((ares_channel)data->state.async.resolver,
862 hostname, PF_INET,
863 query_completed_cb, data);
864 }
865 #endif
866 *waitp = 1; /* expect asynchronous response */
867 }
868 return NULL; /* no struct yet */
869 }
870
Curl_set_dns_servers(struct Curl_easy * data,char * servers)871 CURLcode Curl_set_dns_servers(struct Curl_easy *data,
872 char *servers)
873 {
874 CURLcode result = CURLE_NOT_BUILT_IN;
875 int ares_result;
876
877 /* If server is NULL or empty, this would purge all DNS servers
878 * from ares library, which will cause any and all queries to fail.
879 * So, just return OK if none are configured and don't actually make
880 * any changes to c-ares. This lets c-ares use its defaults, which
881 * it gets from the OS (for instance from /etc/resolv.conf on Linux).
882 */
883 if(!(servers && servers[0]))
884 return CURLE_OK;
885
886 #ifdef HAVE_CARES_SERVERS_CSV
887 #ifdef HAVE_CARES_PORTS_CSV
888 ares_result = ares_set_servers_ports_csv(data->state.async.resolver,
889 servers);
890 #else
891 ares_result = ares_set_servers_csv(data->state.async.resolver, servers);
892 #endif
893 switch(ares_result) {
894 case ARES_SUCCESS:
895 result = CURLE_OK;
896 break;
897 case ARES_ENOMEM:
898 result = CURLE_OUT_OF_MEMORY;
899 break;
900 case ARES_ENOTINITIALIZED:
901 case ARES_ENODATA:
902 case ARES_EBADSTR:
903 default:
904 DEBUGF(infof(data, "bad servers set"));
905 result = CURLE_BAD_FUNCTION_ARGUMENT;
906 break;
907 }
908 #else /* too old c-ares version! */
909 (void)data;
910 (void)(ares_result);
911 #endif
912 return result;
913 }
914
Curl_set_dns_interface(struct Curl_easy * data,const char * interf)915 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
916 const char *interf)
917 {
918 #ifdef HAVE_CARES_LOCAL_DEV
919 if(!interf)
920 interf = "";
921
922 ares_set_local_dev((ares_channel)data->state.async.resolver, interf);
923
924 return CURLE_OK;
925 #else /* c-ares version too old! */
926 (void)data;
927 (void)interf;
928 return CURLE_NOT_BUILT_IN;
929 #endif
930 }
931
Curl_set_dns_local_ip4(struct Curl_easy * data,const char * local_ip4)932 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
933 const char *local_ip4)
934 {
935 #ifdef HAVE_CARES_SET_LOCAL
936 struct in_addr a4;
937
938 if((!local_ip4) || (local_ip4[0] == 0)) {
939 a4.s_addr = 0; /* disabled: do not bind to a specific address */
940 }
941 else {
942 if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
943 DEBUGF(infof(data, "bad DNS IPv4 address"));
944 return CURLE_BAD_FUNCTION_ARGUMENT;
945 }
946 }
947
948 ares_set_local_ip4((ares_channel)data->state.async.resolver,
949 ntohl(a4.s_addr));
950
951 return CURLE_OK;
952 #else /* c-ares version too old! */
953 (void)data;
954 (void)local_ip4;
955 return CURLE_NOT_BUILT_IN;
956 #endif
957 }
958
Curl_set_dns_local_ip6(struct Curl_easy * data,const char * local_ip6)959 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
960 const char *local_ip6)
961 {
962 #if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6)
963 unsigned char a6[INET6_ADDRSTRLEN];
964
965 if((!local_ip6) || (local_ip6[0] == 0)) {
966 /* disabled: do not bind to a specific address */
967 memset(a6, 0, sizeof(a6));
968 }
969 else {
970 if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
971 DEBUGF(infof(data, "bad DNS IPv6 address"));
972 return CURLE_BAD_FUNCTION_ARGUMENT;
973 }
974 }
975
976 ares_set_local_ip6((ares_channel)data->state.async.resolver, a6);
977
978 return CURLE_OK;
979 #else /* c-ares version too old! */
980 (void)data;
981 (void)local_ip6;
982 return CURLE_NOT_BUILT_IN;
983 #endif
984 }
985 #endif /* CURLRES_ARES */
986