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