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