blob: aa581a49a0a2dbf4805cdcc2772068b99c3138e2 [file] [log] [blame]
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
Elliott Hughes82be86d2017-09-20 17:00:17 -07008 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07009 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
Alex Deymod15eaac2016-06-28 14:49:26 -070012 * are also available at https://curl.haxx.se/docs/copyright.html.
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070013 *
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
Alex Deymo486467e2017-12-19 19:04:07 +010025/***********************************************************************
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
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070033#include <limits.h>
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070034#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
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070057#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"
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070069
70# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
71 (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
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
Alex Deymod15eaac2016-06-28 14:49:26 -070083/* The last 3 #include files should be in this order */
84#include "curl_printf.h"
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070085#include "curl_memory.h"
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070086#include "memdebug.h"
87
88struct ResolverResults {
89 int num_pending; /* number of ares_gethostbyname() requests */
90 Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
91 int last_status;
92};
93
94/*
95 * Curl_resolver_global_init() - the generic low-level asynchronous name
96 * resolve API. Called from curl_global_init() to initialize global resolver
97 * environment. Initializes ares library.
98 */
99int Curl_resolver_global_init(void)
100{
101#ifdef CARES_HAVE_ARES_LIBRARY_INIT
102 if(ares_library_init(ARES_LIB_INIT_ALL)) {
103 return CURLE_FAILED_INIT;
104 }
105#endif
106 return CURLE_OK;
107}
108
109/*
110 * Curl_resolver_global_cleanup()
111 *
112 * Called from curl_global_cleanup() to destroy global resolver environment.
113 * Deinitializes ares library.
114 */
115void Curl_resolver_global_cleanup(void)
116{
117#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
118 ares_library_cleanup();
119#endif
120}
121
122/*
123 * Curl_resolver_init()
124 *
125 * Called from curl_easy_init() -> Curl_open() to initialize resolver
126 * URL-state specific environment ('resolver' member of the UrlState
127 * structure). Fills the passed pointer by the initialized ares_channel.
128 */
129CURLcode Curl_resolver_init(void **resolver)
130{
131 int status = ares_init((ares_channel*)resolver);
132 if(status != ARES_SUCCESS) {
133 if(status == ARES_ENOMEM)
134 return CURLE_OUT_OF_MEMORY;
135 else
136 return CURLE_FAILED_INIT;
137 }
138 return CURLE_OK;
139 /* make sure that all other returns from this function should destroy the
140 ares channel before returning error! */
141}
142
143/*
144 * Curl_resolver_cleanup()
145 *
146 * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
147 * URL-state specific environment ('resolver' member of the UrlState
148 * structure). Destroys the ares channel.
149 */
150void Curl_resolver_cleanup(void *resolver)
151{
152 ares_destroy((ares_channel)resolver);
153}
154
155/*
156 * Curl_resolver_duphandle()
157 *
158 * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
159 * environment ('resolver' member of the UrlState structure). Duplicates the
160 * 'from' ares channel and passes the resulting channel to the 'to' pointer.
161 */
162int Curl_resolver_duphandle(void **to, void *from)
163{
164 /* Clone the ares channel for the new handle */
165 if(ARES_SUCCESS != ares_dup((ares_channel*)to, (ares_channel)from))
166 return CURLE_FAILED_INIT;
167 return CURLE_OK;
168}
169
Elliott Hughes82be86d2017-09-20 17:00:17 -0700170static void destroy_async_data(struct Curl_async *async);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700171
172/*
173 * Cancel all possibly still on-going resolves for this connection.
174 */
175void Curl_resolver_cancel(struct connectdata *conn)
176{
177 if(conn->data && conn->data->state.resolver)
178 ares_cancel((ares_channel)conn->data->state.resolver);
179 destroy_async_data(&conn->async);
180}
181
182/*
183 * destroy_async_data() cleans up async resolver data.
184 */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700185static void destroy_async_data(struct Curl_async *async)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700186{
187 free(async->hostname);
188
189 if(async->os_specific) {
190 struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
191 if(res) {
192 if(res->temp_ai) {
193 Curl_freeaddrinfo(res->temp_ai);
194 res->temp_ai = NULL;
195 }
196 free(res);
197 }
198 async->os_specific = NULL;
199 }
200
201 async->hostname = NULL;
202}
203
204/*
205 * Curl_resolver_getsock() is called when someone from the outside world
206 * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
207 * with ares. The caller must make sure that this function is only called when
208 * we have a working ares channel.
209 *
210 * Returns: sockets-in-use-bitmap
211 */
212
213int Curl_resolver_getsock(struct connectdata *conn,
214 curl_socket_t *socks,
215 int numsocks)
216
217{
218 struct timeval maxtime;
219 struct timeval timebuf;
220 struct timeval *timeout;
221 long milli;
222 int max = ares_getsock((ares_channel)conn->data->state.resolver,
223 (ares_socket_t *)socks, numsocks);
224
225 maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
226 maxtime.tv_usec = 0;
227
228 timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
229 &timebuf);
230 milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
231 if(milli == 0)
232 milli += 10;
Elliott Hughes82be86d2017-09-20 17:00:17 -0700233 Curl_expire(conn->data, milli, EXPIRE_ASYNC_NAME);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700234
235 return max;
236}
237
238/*
239 * waitperform()
240 *
241 * 1) Ask ares what sockets it currently plays with, then
242 * 2) wait for the timeout period to check for action on ares' sockets.
243 * 3) tell ares to act on all the sockets marked as "with action"
244 *
245 * return number of sockets it worked on
246 */
247
248static int waitperform(struct connectdata *conn, int timeout_ms)
249{
Alex Deymoe3149cc2016-10-05 11:18:42 -0700250 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700251 int nfds;
252 int bitmask;
253 ares_socket_t socks[ARES_GETSOCK_MAXNUM];
254 struct pollfd pfd[ARES_GETSOCK_MAXNUM];
255 int i;
256 int num = 0;
257
258 bitmask = ares_getsock((ares_channel)data->state.resolver, socks,
259 ARES_GETSOCK_MAXNUM);
260
Alex Deymo486467e2017-12-19 19:04:07 +0100261 for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700262 pfd[i].events = 0;
263 pfd[i].revents = 0;
264 if(ARES_GETSOCK_READABLE(bitmask, i)) {
265 pfd[i].fd = socks[i];
266 pfd[i].events |= POLLRDNORM|POLLIN;
267 }
268 if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
269 pfd[i].fd = socks[i];
270 pfd[i].events |= POLLWRNORM|POLLOUT;
271 }
272 if(pfd[i].events != 0)
273 num++;
274 else
275 break;
276 }
277
278 if(num)
279 nfds = Curl_poll(pfd, num, timeout_ms);
280 else
281 nfds = 0;
282
283 if(!nfds)
284 /* Call ares_process() unconditonally here, even if we simply timed out
285 above, as otherwise the ares name resolve won't timeout! */
286 ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD,
287 ARES_SOCKET_BAD);
288 else {
289 /* move through the descriptors and ask for processing on them */
Alex Deymo486467e2017-12-19 19:04:07 +0100290 for(i = 0; i < num; i++)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700291 ares_process_fd((ares_channel)data->state.resolver,
292 pfd[i].revents & (POLLRDNORM|POLLIN)?
293 pfd[i].fd:ARES_SOCKET_BAD,
294 pfd[i].revents & (POLLWRNORM|POLLOUT)?
295 pfd[i].fd:ARES_SOCKET_BAD);
296 }
297 return nfds;
298}
299
300/*
301 * Curl_resolver_is_resolved() is called repeatedly to check if a previous
302 * name resolve request has completed. It should also make sure to time-out if
303 * the operation seems to take too long.
304 *
305 * Returns normal CURLcode errors.
306 */
307CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
308 struct Curl_dns_entry **dns)
309{
Alex Deymoe3149cc2016-10-05 11:18:42 -0700310 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700311 struct ResolverResults *res = (struct ResolverResults *)
312 conn->async.os_specific;
313 CURLcode result = CURLE_OK;
314
315 *dns = NULL;
316
317 waitperform(conn, 0);
318
319 if(res && !res->num_pending) {
320 (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
321 /* temp_ai ownership is moved to the connection, so we need not free-up
322 them */
323 res->temp_ai = NULL;
324 if(!conn->async.dns) {
325 failf(data, "Could not resolve: %s (%s)",
326 conn->async.hostname, ares_strerror(conn->async.status));
327 result = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
328 CURLE_COULDNT_RESOLVE_HOST;
329 }
330 else
331 *dns = conn->async.dns;
332
333 destroy_async_data(&conn->async);
334 }
335
336 return result;
337}
338
339/*
340 * Curl_resolver_wait_resolv()
341 *
342 * waits for a resolve to finish. This function should be avoided since using
343 * this risk getting the multi interface to "hang".
344 *
345 * If 'entry' is non-NULL, make it point to the resolved dns entry
346 *
347 * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
348 * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
349 */
350CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
351 struct Curl_dns_entry **entry)
352{
353 CURLcode result = CURLE_OK;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700354 struct Curl_easy *data = conn->data;
Alex Deymo486467e2017-12-19 19:04:07 +0100355 timediff_t timeout;
356 struct curltime now = Curl_now();
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700357 struct Curl_dns_entry *temp_entry;
358
Elliott Hughes82be86d2017-09-20 17:00:17 -0700359 if(entry)
360 *entry = NULL; /* clear on entry */
361
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700362 timeout = Curl_timeleft(data, &now, TRUE);
Elliott Hughes82be86d2017-09-20 17:00:17 -0700363 if(timeout < 0) {
364 /* already expired! */
365 connclose(conn, "Timed out before name resolve started");
366 return CURLE_OPERATION_TIMEDOUT;
367 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700368 if(!timeout)
369 timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
370
371 /* Wait for the name resolve query to complete. */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700372 while(!result) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700373 struct timeval *tvp, tv, store;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700374 int itimeout;
375 int timeout_ms;
376
377 itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
378
379 store.tv_sec = itimeout/1000;
380 store.tv_usec = (itimeout%1000)*1000;
381
382 tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
383
384 /* use the timeout period ares returned to us above if less than one
385 second is left, otherwise just use 1000ms to make sure the progress
386 callback gets called frequent enough */
387 if(!tvp->tv_sec)
388 timeout_ms = (int)(tvp->tv_usec/1000);
389 else
390 timeout_ms = 1000;
391
392 waitperform(conn, timeout_ms);
Elliott Hughes82be86d2017-09-20 17:00:17 -0700393 result = Curl_resolver_is_resolved(conn, &temp_entry);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700394
Elliott Hughes82be86d2017-09-20 17:00:17 -0700395 if(result || conn->async.done)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700396 break;
397
Elliott Hughes82be86d2017-09-20 17:00:17 -0700398 if(Curl_pgrsUpdate(conn))
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700399 result = CURLE_ABORTED_BY_CALLBACK;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700400 else {
Alex Deymo486467e2017-12-19 19:04:07 +0100401 struct curltime now2 = Curl_now();
402 timediff_t timediff = Curl_timediff(now2, now); /* spent time */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700403 if(timediff <= 0)
404 timeout -= 1; /* always deduct at least 1 */
405 else if(timediff > timeout)
406 timeout = -1;
407 else
408 timeout -= (long)timediff;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700409 now = now2; /* for next loop */
410 }
Elliott Hughes82be86d2017-09-20 17:00:17 -0700411 if(timeout < 0)
412 result = CURLE_OPERATION_TIMEDOUT;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700413 }
Elliott Hughes82be86d2017-09-20 17:00:17 -0700414 if(result)
415 /* failure, so we cancel the ares operation */
416 ares_cancel((ares_channel)data->state.resolver);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700417
418 /* Operation complete, if the lookup was successful we now have the entry
419 in the cache. */
420 if(entry)
421 *entry = conn->async.dns;
422
423 if(result)
424 /* close the connection, since we can't return failure here without
425 cleaning up this connection properly.
426 TODO: remove this action from here, it is not a name resolver decision.
427 */
428 connclose(conn, "c-ares resolve failed");
429
430 return result;
431}
432
433/* Connects results to the list */
434static void compound_results(struct ResolverResults *res,
435 Curl_addrinfo *ai)
436{
437 Curl_addrinfo *ai_tail;
438 if(!ai)
439 return;
440 ai_tail = ai;
441
442 while(ai_tail->ai_next)
443 ai_tail = ai_tail->ai_next;
444
445 /* Add the new results to the list of old results. */
446 ai_tail->ai_next = res->temp_ai;
447 res->temp_ai = ai;
448}
449
450/*
451 * ares_query_completed_cb() is the callback that ares will call when
452 * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
453 * when using ares, is completed either successfully or with failure.
454 */
455static void query_completed_cb(void *arg, /* (struct connectdata *) */
456 int status,
457#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
458 int timeouts,
459#endif
460 struct hostent *hostent)
461{
462 struct connectdata *conn = (struct connectdata *)arg;
463 struct ResolverResults *res;
464
465#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
466 (void)timeouts; /* ignored */
467#endif
468
469 if(ARES_EDESTRUCTION == status)
470 /* when this ares handle is getting destroyed, the 'arg' pointer may not
471 be valid so only defer it when we know the 'status' says its fine! */
472 return;
473
474 res = (struct ResolverResults *)conn->async.os_specific;
475 res->num_pending--;
476
477 if(CURL_ASYNC_SUCCESS == status) {
478 Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
479 if(ai) {
480 compound_results(res, ai);
481 }
482 }
483 /* A successful result overwrites any previous error */
484 if(res->last_status != ARES_SUCCESS)
485 res->last_status = status;
486}
487
488/*
489 * Curl_resolver_getaddrinfo() - when using ares
490 *
491 * Returns name information about the given hostname and port number. If
492 * successful, the 'hostent' is returned and the forth argument will point to
493 * memory we need to free after use. That memory *MUST* be freed with
494 * Curl_freeaddrinfo(), nothing else.
495 */
496Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
497 const char *hostname,
498 int port,
499 int *waitp)
500{
501 char *bufp;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700502 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700503 struct in_addr in;
504 int family = PF_INET;
505#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
506 struct in6_addr in6;
507#endif /* CURLRES_IPV6 */
508
509 *waitp = 0; /* default to synchronous response */
510
511 /* First check if this is an IPv4 address string */
512 if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
513 /* This is a dotted IP address 123.123.123.123-style */
514 return Curl_ip2addr(AF_INET, &in, hostname, port);
515 }
516
517#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
518 /* Otherwise, check if this is an IPv6 address string */
519 if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
520 /* This must be an IPv6 address literal. */
521 return Curl_ip2addr(AF_INET6, &in6, hostname, port);
522
523 switch(conn->ip_version) {
524 default:
525#if ARES_VERSION >= 0x010601
526 family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
527 c-ares versions this just falls through and defaults
528 to PF_INET */
529 break;
530#endif
531 case CURL_IPRESOLVE_V4:
532 family = PF_INET;
533 break;
534 case CURL_IPRESOLVE_V6:
535 family = PF_INET6;
536 break;
537 }
538#endif /* CURLRES_IPV6 */
539
540 bufp = strdup(hostname);
541 if(bufp) {
542 struct ResolverResults *res = NULL;
543 free(conn->async.hostname);
544 conn->async.hostname = bufp;
545 conn->async.port = port;
546 conn->async.done = FALSE; /* not done */
547 conn->async.status = 0; /* clear */
548 conn->async.dns = NULL; /* clear */
549 res = calloc(sizeof(struct ResolverResults), 1);
550 if(!res) {
551 free(conn->async.hostname);
552 conn->async.hostname = NULL;
553 return NULL;
554 }
555 conn->async.os_specific = res;
556
557 /* initial status - failed */
558 res->last_status = ARES_ENOTFOUND;
559#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
560 if(family == PF_UNSPEC) {
561 if(Curl_ipv6works()) {
562 res->num_pending = 2;
563
564 /* areschannel is already setup in the Curl_open() function */
565 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
566 PF_INET, query_completed_cb, conn);
567 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
568 PF_INET6, query_completed_cb, conn);
569 }
570 else {
571 res->num_pending = 1;
572
573 /* areschannel is already setup in the Curl_open() function */
574 ares_gethostbyname((ares_channel)data->state.resolver, hostname,
575 PF_INET, query_completed_cb, conn);
576 }
577 }
578 else
579#endif /* CURLRES_IPV6 */
580 {
581 res->num_pending = 1;
582
583 /* areschannel is already setup in the Curl_open() function */
584 ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
585 query_completed_cb, conn);
586 }
587
588 *waitp = 1; /* expect asynchronous response */
589 }
590 return NULL; /* no struct yet */
591}
592
Alex Deymoe3149cc2016-10-05 11:18:42 -0700593CURLcode Curl_set_dns_servers(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700594 char *servers)
595{
596 CURLcode result = CURLE_NOT_BUILT_IN;
597 int ares_result;
598
599 /* If server is NULL or empty, this would purge all DNS servers
600 * from ares library, which will cause any and all queries to fail.
601 * So, just return OK if none are configured and don't actually make
602 * any changes to c-ares. This lets c-ares use it's defaults, which
603 * it gets from the OS (for instance from /etc/resolv.conf on Linux).
604 */
605 if(!(servers && servers[0]))
606 return CURLE_OK;
607
608#if (ARES_VERSION >= 0x010704)
609 ares_result = ares_set_servers_csv(data->state.resolver, servers);
610 switch(ares_result) {
611 case ARES_SUCCESS:
612 result = CURLE_OK;
613 break;
614 case ARES_ENOMEM:
615 result = CURLE_OUT_OF_MEMORY;
616 break;
617 case ARES_ENOTINITIALIZED:
618 case ARES_ENODATA:
619 case ARES_EBADSTR:
620 default:
621 result = CURLE_BAD_FUNCTION_ARGUMENT;
622 break;
623 }
624#else /* too old c-ares version! */
625 (void)data;
626 (void)(ares_result);
627#endif
628 return result;
629}
630
Alex Deymoe3149cc2016-10-05 11:18:42 -0700631CURLcode Curl_set_dns_interface(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700632 const char *interf)
633{
634#if (ARES_VERSION >= 0x010704)
635 if(!interf)
636 interf = "";
637
638 ares_set_local_dev((ares_channel)data->state.resolver, interf);
639
640 return CURLE_OK;
641#else /* c-ares version too old! */
642 (void)data;
643 (void)interf;
644 return CURLE_NOT_BUILT_IN;
645#endif
646}
647
Alex Deymoe3149cc2016-10-05 11:18:42 -0700648CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700649 const char *local_ip4)
650{
651#if (ARES_VERSION >= 0x010704)
652 struct in_addr a4;
653
654 if((!local_ip4) || (local_ip4[0] == 0)) {
655 a4.s_addr = 0; /* disabled: do not bind to a specific address */
656 }
657 else {
658 if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
659 return CURLE_BAD_FUNCTION_ARGUMENT;
660 }
661 }
662
663 ares_set_local_ip4((ares_channel)data->state.resolver, ntohl(a4.s_addr));
664
665 return CURLE_OK;
666#else /* c-ares version too old! */
667 (void)data;
668 (void)local_ip4;
669 return CURLE_NOT_BUILT_IN;
670#endif
671}
672
Alex Deymoe3149cc2016-10-05 11:18:42 -0700673CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700674 const char *local_ip6)
675{
676#if (ARES_VERSION >= 0x010704) && defined(ENABLE_IPV6)
677 unsigned char a6[INET6_ADDRSTRLEN];
678
679 if((!local_ip6) || (local_ip6[0] == 0)) {
680 /* disabled: do not bind to a specific address */
681 memset(a6, 0, sizeof(a6));
682 }
683 else {
684 if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
685 return CURLE_BAD_FUNCTION_ARGUMENT;
686 }
687 }
688
689 ares_set_local_ip6((ares_channel)data->state.resolver, a6);
690
691 return CURLE_OK;
692#else /* c-ares version too old! */
693 (void)data;
694 (void)local_ip6;
695 return CURLE_NOT_BUILT_IN;
696#endif
697}
698#endif /* CURLRES_ARES */