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