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