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