• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2019, 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.haxx.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) || 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 
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 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  */
Curl_resolver_global_init(void)99 int 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  */
Curl_resolver_global_cleanup(void)115 void Curl_resolver_global_cleanup(void)
116 {
117 #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
118   ares_library_cleanup();
119 #endif
120 }
121 
122 
Curl_ares_sock_state_cb(void * data,ares_socket_t socket_fd,int readable,int writable)123 static void Curl_ares_sock_state_cb(void *data, ares_socket_t socket_fd,
124                                     int readable, int writable)
125 {
126   struct Curl_easy *easy = data;
127   if(!readable && !writable) {
128     DEBUGASSERT(easy);
129     Curl_multi_closed(easy, socket_fd);
130   }
131 }
132 
133 /*
134  * Curl_resolver_init()
135  *
136  * Called from curl_easy_init() -> Curl_open() to initialize resolver
137  * URL-state specific environment ('resolver' member of the UrlState
138  * structure).  Fills the passed pointer by the initialized ares_channel.
139  */
Curl_resolver_init(struct Curl_easy * easy,void ** resolver)140 CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
141 {
142   int status;
143   struct ares_options options;
144   int optmask = ARES_OPT_SOCK_STATE_CB;
145   options.sock_state_cb = Curl_ares_sock_state_cb;
146   options.sock_state_cb_data = easy;
147   status = ares_init_options((ares_channel*)resolver, &options, optmask);
148   if(status != ARES_SUCCESS) {
149     if(status == ARES_ENOMEM)
150       return CURLE_OUT_OF_MEMORY;
151     else
152       return CURLE_FAILED_INIT;
153   }
154   return CURLE_OK;
155   /* make sure that all other returns from this function should destroy the
156      ares channel before returning error! */
157 }
158 
159 /*
160  * Curl_resolver_cleanup()
161  *
162  * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
163  * URL-state specific environment ('resolver' member of the UrlState
164  * structure).  Destroys the ares channel.
165  */
Curl_resolver_cleanup(void * resolver)166 void Curl_resolver_cleanup(void *resolver)
167 {
168   ares_destroy((ares_channel)resolver);
169 }
170 
171 /*
172  * Curl_resolver_duphandle()
173  *
174  * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
175  * environment ('resolver' member of the UrlState structure).  Duplicates the
176  * 'from' ares channel and passes the resulting channel to the 'to' pointer.
177  */
Curl_resolver_duphandle(struct Curl_easy * easy,void ** to,void * from)178 CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
179 {
180   (void)from;
181   /*
182    * it would be better to call ares_dup instead, but right now
183    * it is not possible to set 'sock_state_cb_data' outside of
184    * ares_init_options
185    */
186   return Curl_resolver_init(easy, to);
187 }
188 
189 static void destroy_async_data(struct Curl_async *async);
190 
191 /*
192  * Cancel all possibly still on-going resolves for this connection.
193  */
Curl_resolver_cancel(struct connectdata * conn)194 void Curl_resolver_cancel(struct connectdata *conn)
195 {
196   if(conn->data && conn->data->state.resolver)
197     ares_cancel((ares_channel)conn->data->state.resolver);
198   destroy_async_data(&conn->async);
199 }
200 
201 /*
202  * We're equivalent to Curl_resolver_cancel() for the c-ares resolver.  We
203  * never block.
204  */
Curl_resolver_kill(struct connectdata * conn)205 void Curl_resolver_kill(struct connectdata *conn)
206 {
207   /* We don't need to check the resolver state because we can be called safely
208      at any time and we always do the same thing. */
209   Curl_resolver_cancel(conn);
210 }
211 
212 /*
213  * destroy_async_data() cleans up async resolver data.
214  */
destroy_async_data(struct Curl_async * async)215 static void destroy_async_data(struct Curl_async *async)
216 {
217   free(async->hostname);
218 
219   if(async->os_specific) {
220     struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
221     if(res) {
222       if(res->temp_ai) {
223         Curl_freeaddrinfo(res->temp_ai);
224         res->temp_ai = NULL;
225       }
226       free(res);
227     }
228     async->os_specific = NULL;
229   }
230 
231   async->hostname = NULL;
232 }
233 
234 /*
235  * Curl_resolver_getsock() is called when someone from the outside world
236  * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
237  * with ares. The caller must make sure that this function is only called when
238  * we have a working ares channel.
239  *
240  * Returns: sockets-in-use-bitmap
241  */
242 
Curl_resolver_getsock(struct connectdata * conn,curl_socket_t * socks,int numsocks)243 int Curl_resolver_getsock(struct connectdata *conn,
244                           curl_socket_t *socks,
245                           int numsocks)
246 
247 {
248   struct timeval maxtime;
249   struct timeval timebuf;
250   struct timeval *timeout;
251   long milli;
252   int max = ares_getsock((ares_channel)conn->data->state.resolver,
253                          (ares_socket_t *)socks, numsocks);
254 
255   maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
256   maxtime.tv_usec = 0;
257 
258   timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
259                          &timebuf);
260   milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
261   if(milli == 0)
262     milli += 10;
263   Curl_expire(conn->data, milli, EXPIRE_ASYNC_NAME);
264 
265   return max;
266 }
267 
268 /*
269  * waitperform()
270  *
271  * 1) Ask ares what sockets it currently plays with, then
272  * 2) wait for the timeout period to check for action on ares' sockets.
273  * 3) tell ares to act on all the sockets marked as "with action"
274  *
275  * return number of sockets it worked on
276  */
277 
waitperform(struct connectdata * conn,int timeout_ms)278 static int waitperform(struct connectdata *conn, int timeout_ms)
279 {
280   struct Curl_easy *data = conn->data;
281   int nfds;
282   int bitmask;
283   ares_socket_t socks[ARES_GETSOCK_MAXNUM];
284   struct pollfd pfd[ARES_GETSOCK_MAXNUM];
285   int i;
286   int num = 0;
287 
288   bitmask = ares_getsock((ares_channel)data->state.resolver, socks,
289                          ARES_GETSOCK_MAXNUM);
290 
291   for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
292     pfd[i].events = 0;
293     pfd[i].revents = 0;
294     if(ARES_GETSOCK_READABLE(bitmask, i)) {
295       pfd[i].fd = socks[i];
296       pfd[i].events |= POLLRDNORM|POLLIN;
297     }
298     if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
299       pfd[i].fd = socks[i];
300       pfd[i].events |= POLLWRNORM|POLLOUT;
301     }
302     if(pfd[i].events != 0)
303       num++;
304     else
305       break;
306   }
307 
308   if(num)
309     nfds = Curl_poll(pfd, num, timeout_ms);
310   else
311     nfds = 0;
312 
313   if(!nfds)
314     /* Call ares_process() unconditonally here, even if we simply timed out
315        above, as otherwise the ares name resolve won't timeout! */
316     ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD,
317                     ARES_SOCKET_BAD);
318   else {
319     /* move through the descriptors and ask for processing on them */
320     for(i = 0; i < num; i++)
321       ares_process_fd((ares_channel)data->state.resolver,
322                       pfd[i].revents & (POLLRDNORM|POLLIN)?
323                       pfd[i].fd:ARES_SOCKET_BAD,
324                       pfd[i].revents & (POLLWRNORM|POLLOUT)?
325                       pfd[i].fd:ARES_SOCKET_BAD);
326   }
327   return nfds;
328 }
329 
330 /*
331  * Curl_resolver_is_resolved() is called repeatedly to check if a previous
332  * name resolve request has completed. It should also make sure to time-out if
333  * the operation seems to take too long.
334  *
335  * Returns normal CURLcode errors.
336  */
Curl_resolver_is_resolved(struct connectdata * conn,struct Curl_dns_entry ** dns)337 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
338                                    struct Curl_dns_entry **dns)
339 {
340   struct Curl_easy *data = conn->data;
341   struct ResolverResults *res = (struct ResolverResults *)
342     conn->async.os_specific;
343   CURLcode result = CURLE_OK;
344 
345   if(dns)
346     *dns = NULL;
347 
348   waitperform(conn, 0);
349 
350   if(res && !res->num_pending) {
351     if(dns) {
352       (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
353       /* temp_ai ownership is moved to the connection, so we need not free-up
354          them */
355       res->temp_ai = NULL;
356     }
357     if(!conn->async.dns) {
358       failf(data, "Could not resolve: %s (%s)",
359             conn->async.hostname, ares_strerror(conn->async.status));
360       result = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
361         CURLE_COULDNT_RESOLVE_HOST;
362     }
363     else if(dns)
364       *dns = conn->async.dns;
365 
366     destroy_async_data(&conn->async);
367   }
368 
369   return result;
370 }
371 
372 /*
373  * Curl_resolver_wait_resolv()
374  *
375  * Waits for a resolve to finish. This function should be avoided since using
376  * this risk getting the multi interface to "hang".
377  *
378  * If 'entry' is non-NULL, make it point to the resolved dns entry
379  *
380  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
381  * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
382  */
Curl_resolver_wait_resolv(struct connectdata * conn,struct Curl_dns_entry ** entry)383 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
384                                    struct Curl_dns_entry **entry)
385 {
386   CURLcode result = CURLE_OK;
387   struct Curl_easy *data = conn->data;
388   timediff_t timeout;
389   struct curltime now = Curl_now();
390   struct Curl_dns_entry *temp_entry;
391 
392   if(entry)
393     *entry = NULL; /* clear on entry */
394 
395   timeout = Curl_timeleft(data, &now, TRUE);
396   if(timeout < 0) {
397     /* already expired! */
398     connclose(conn, "Timed out before name resolve started");
399     return CURLE_OPERATION_TIMEDOUT;
400   }
401   if(!timeout)
402     timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
403 
404   /* Wait for the name resolve query to complete. */
405   while(!result) {
406     struct timeval *tvp, tv, store;
407     int itimeout;
408     int timeout_ms;
409 
410     itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
411 
412     store.tv_sec = itimeout/1000;
413     store.tv_usec = (itimeout%1000)*1000;
414 
415     tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
416 
417     /* use the timeout period ares returned to us above if less than one
418        second is left, otherwise just use 1000ms to make sure the progress
419        callback gets called frequent enough */
420     if(!tvp->tv_sec)
421       timeout_ms = (int)(tvp->tv_usec/1000);
422     else
423       timeout_ms = 1000;
424 
425     waitperform(conn, timeout_ms);
426     result = Curl_resolver_is_resolved(conn, entry?&temp_entry:NULL);
427 
428     if(result || conn->async.done)
429       break;
430 
431     if(Curl_pgrsUpdate(conn))
432       result = CURLE_ABORTED_BY_CALLBACK;
433     else {
434       struct curltime now2 = Curl_now();
435       timediff_t timediff = Curl_timediff(now2, now); /* spent time */
436       if(timediff <= 0)
437         timeout -= 1; /* always deduct at least 1 */
438       else if(timediff > timeout)
439         timeout = -1;
440       else
441         timeout -= (long)timediff;
442       now = now2; /* for next loop */
443     }
444     if(timeout < 0)
445       result = CURLE_OPERATION_TIMEDOUT;
446   }
447   if(result)
448     /* failure, so we cancel the ares operation */
449     ares_cancel((ares_channel)data->state.resolver);
450 
451   /* Operation complete, if the lookup was successful we now have the entry
452      in the cache. */
453   if(entry)
454     *entry = conn->async.dns;
455 
456   if(result)
457     /* close the connection, since we can't return failure here without
458        cleaning up this connection properly.
459        TODO: remove this action from here, it is not a name resolver decision.
460     */
461     connclose(conn, "c-ares resolve failed");
462 
463   return result;
464 }
465 
466 /* Connects results to the list */
compound_results(struct ResolverResults * res,Curl_addrinfo * ai)467 static void compound_results(struct ResolverResults *res,
468                              Curl_addrinfo *ai)
469 {
470   Curl_addrinfo *ai_tail;
471   if(!ai)
472     return;
473   ai_tail = ai;
474 
475   while(ai_tail->ai_next)
476     ai_tail = ai_tail->ai_next;
477 
478   /* Add the new results to the list of old results. */
479   ai_tail->ai_next = res->temp_ai;
480   res->temp_ai = ai;
481 }
482 
483 /*
484  * ares_query_completed_cb() is the callback that ares will call when
485  * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
486  * when using ares, is completed either successfully or with failure.
487  */
query_completed_cb(void * arg,int status,int timeouts,struct hostent * hostent)488 static void query_completed_cb(void *arg,  /* (struct connectdata *) */
489                                int status,
490 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
491                                int timeouts,
492 #endif
493                                struct hostent *hostent)
494 {
495   struct connectdata *conn = (struct connectdata *)arg;
496   struct ResolverResults *res;
497 
498 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
499   (void)timeouts; /* ignored */
500 #endif
501 
502   if(ARES_EDESTRUCTION == status)
503     /* when this ares handle is getting destroyed, the 'arg' pointer may not
504        be valid so only defer it when we know the 'status' says its fine! */
505     return;
506 
507   res = (struct ResolverResults *)conn->async.os_specific;
508   if(res) {
509     res->num_pending--;
510 
511     if(CURL_ASYNC_SUCCESS == status) {
512       Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
513       if(ai) {
514         compound_results(res, ai);
515       }
516     }
517     /* A successful result overwrites any previous error */
518     if(res->last_status != ARES_SUCCESS)
519       res->last_status = status;
520   }
521 }
522 
523 /*
524  * Curl_resolver_getaddrinfo() - when using ares
525  *
526  * Returns name information about the given hostname and port number. If
527  * successful, the 'hostent' is returned and the forth argument will point to
528  * memory we need to free after use. That memory *MUST* be freed with
529  * Curl_freeaddrinfo(), nothing else.
530  */
Curl_resolver_getaddrinfo(struct connectdata * conn,const char * hostname,int port,int * waitp)531 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
532                                          const char *hostname,
533                                          int port,
534                                          int *waitp)
535 {
536   char *bufp;
537   struct Curl_easy *data = conn->data;
538   struct in_addr in;
539   int family = PF_INET;
540 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
541   struct in6_addr in6;
542 #endif /* CURLRES_IPV6 */
543 
544   *waitp = 0; /* default to synchronous response */
545 
546   /* First check if this is an IPv4 address string */
547   if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
548     /* This is a dotted IP address 123.123.123.123-style */
549     return Curl_ip2addr(AF_INET, &in, hostname, port);
550   }
551 
552 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
553   /* Otherwise, check if this is an IPv6 address string */
554   if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
555     /* This must be an IPv6 address literal.  */
556     return Curl_ip2addr(AF_INET6, &in6, hostname, port);
557 
558   switch(conn->ip_version) {
559   default:
560 #if ARES_VERSION >= 0x010601
561     family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
562                            c-ares versions this just falls through and defaults
563                            to PF_INET */
564     break;
565 #endif
566   case CURL_IPRESOLVE_V4:
567     family = PF_INET;
568     break;
569   case CURL_IPRESOLVE_V6:
570     family = PF_INET6;
571     break;
572   }
573 #endif /* CURLRES_IPV6 */
574 
575   bufp = strdup(hostname);
576   if(bufp) {
577     struct ResolverResults *res = NULL;
578     free(conn->async.hostname);
579     conn->async.hostname = bufp;
580     conn->async.port = port;
581     conn->async.done = FALSE;   /* not done */
582     conn->async.status = 0;     /* clear */
583     conn->async.dns = NULL;     /* clear */
584     res = calloc(sizeof(struct ResolverResults), 1);
585     if(!res) {
586       free(conn->async.hostname);
587       conn->async.hostname = NULL;
588       return NULL;
589     }
590     conn->async.os_specific = res;
591 
592     /* initial status - failed */
593     res->last_status = ARES_ENOTFOUND;
594 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
595     if(family == PF_UNSPEC) {
596       if(Curl_ipv6works()) {
597         res->num_pending = 2;
598 
599         /* areschannel is already setup in the Curl_open() function */
600         ares_gethostbyname((ares_channel)data->state.resolver, hostname,
601                             PF_INET, query_completed_cb, conn);
602         ares_gethostbyname((ares_channel)data->state.resolver, hostname,
603                             PF_INET6, query_completed_cb, conn);
604       }
605       else {
606         res->num_pending = 1;
607 
608         /* areschannel is already setup in the Curl_open() function */
609         ares_gethostbyname((ares_channel)data->state.resolver, hostname,
610                             PF_INET, query_completed_cb, conn);
611       }
612     }
613     else
614 #endif /* CURLRES_IPV6 */
615     {
616       res->num_pending = 1;
617 
618       /* areschannel is already setup in the Curl_open() function */
619       ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
620                          query_completed_cb, conn);
621     }
622 
623     *waitp = 1; /* expect asynchronous response */
624   }
625   return NULL; /* no struct yet */
626 }
627 
Curl_set_dns_servers(struct Curl_easy * data,char * servers)628 CURLcode Curl_set_dns_servers(struct Curl_easy *data,
629                               char *servers)
630 {
631   CURLcode result = CURLE_NOT_BUILT_IN;
632   int ares_result;
633 
634   /* If server is NULL or empty, this would purge all DNS servers
635    * from ares library, which will cause any and all queries to fail.
636    * So, just return OK if none are configured and don't actually make
637    * any changes to c-ares.  This lets c-ares use it's defaults, which
638    * it gets from the OS (for instance from /etc/resolv.conf on Linux).
639    */
640   if(!(servers && servers[0]))
641     return CURLE_OK;
642 
643 #if (ARES_VERSION >= 0x010704)
644   ares_result = ares_set_servers_csv(data->state.resolver, servers);
645   switch(ares_result) {
646   case ARES_SUCCESS:
647     result = CURLE_OK;
648     break;
649   case ARES_ENOMEM:
650     result = CURLE_OUT_OF_MEMORY;
651     break;
652   case ARES_ENOTINITIALIZED:
653   case ARES_ENODATA:
654   case ARES_EBADSTR:
655   default:
656     result = CURLE_BAD_FUNCTION_ARGUMENT;
657     break;
658   }
659 #else /* too old c-ares version! */
660   (void)data;
661   (void)(ares_result);
662 #endif
663   return result;
664 }
665 
Curl_set_dns_interface(struct Curl_easy * data,const char * interf)666 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
667                                 const char *interf)
668 {
669 #if (ARES_VERSION >= 0x010704)
670   if(!interf)
671     interf = "";
672 
673   ares_set_local_dev((ares_channel)data->state.resolver, interf);
674 
675   return CURLE_OK;
676 #else /* c-ares version too old! */
677   (void)data;
678   (void)interf;
679   return CURLE_NOT_BUILT_IN;
680 #endif
681 }
682 
Curl_set_dns_local_ip4(struct Curl_easy * data,const char * local_ip4)683 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
684                                 const char *local_ip4)
685 {
686 #if (ARES_VERSION >= 0x010704)
687   struct in_addr a4;
688 
689   if((!local_ip4) || (local_ip4[0] == 0)) {
690     a4.s_addr = 0; /* disabled: do not bind to a specific address */
691   }
692   else {
693     if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
694       return CURLE_BAD_FUNCTION_ARGUMENT;
695     }
696   }
697 
698   ares_set_local_ip4((ares_channel)data->state.resolver, ntohl(a4.s_addr));
699 
700   return CURLE_OK;
701 #else /* c-ares version too old! */
702   (void)data;
703   (void)local_ip4;
704   return CURLE_NOT_BUILT_IN;
705 #endif
706 }
707 
Curl_set_dns_local_ip6(struct Curl_easy * data,const char * local_ip6)708 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
709                                 const char *local_ip6)
710 {
711 #if (ARES_VERSION >= 0x010704) && defined(ENABLE_IPV6)
712   unsigned char a6[INET6_ADDRSTRLEN];
713 
714   if((!local_ip6) || (local_ip6[0] == 0)) {
715     /* disabled: do not bind to a specific address */
716     memset(a6, 0, sizeof(a6));
717   }
718   else {
719     if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
720       return CURLE_BAD_FUNCTION_ARGUMENT;
721     }
722   }
723 
724   ares_set_local_ip6((ares_channel)data->state.resolver, a6);
725 
726   return CURLE_OK;
727 #else /* c-ares version too old! */
728   (void)data;
729   (void)local_ip6;
730   return CURLE_NOT_BUILT_IN;
731 #endif
732 }
733 #endif /* CURLRES_ARES */
734