asyn-ares.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  ***************************************************************************/
00022 
00023 #include "curl_setup.h"
00024 
00025 #ifdef HAVE_LIMITS_H
00026 #include <limits.h>
00027 #endif
00028 #ifdef HAVE_NETINET_IN_H
00029 #include <netinet/in.h>
00030 #endif
00031 #ifdef HAVE_NETDB_H
00032 #include <netdb.h>
00033 #endif
00034 #ifdef HAVE_ARPA_INET_H
00035 #include <arpa/inet.h>
00036 #endif
00037 #ifdef __VMS
00038 #include <in.h>
00039 #include <inet.h>
00040 #endif
00041 
00042 #ifdef HAVE_PROCESS_H
00043 #include <process.h>
00044 #endif
00045 
00046 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
00047 #undef in_addr_t
00048 #define in_addr_t unsigned long
00049 #endif
00050 
00051 /***********************************************************************
00052  * Only for ares-enabled builds
00053  * And only for functions that fulfill the asynch resolver backend API
00054  * as defined in asyn.h, nothing else belongs in this file!
00055  **********************************************************************/
00056 
00057 #ifdef CURLRES_ARES
00058 
00059 #include "urldata.h"
00060 #include "sendf.h"
00061 #include "hostip.h"
00062 #include "hash.h"
00063 #include "share.h"
00064 #include "strerror.h"
00065 #include "url.h"
00066 #include "multiif.h"
00067 #include "inet_pton.h"
00068 #include "connect.h"
00069 #include "select.h"
00070 #include "progress.h"
00071 
00072 #  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
00073      (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
00074 #    define CARES_STATICLIB
00075 #  endif
00076 #  include <ares.h>
00077 #  include <ares_version.h> /* really old c-ares didn't include this by
00078                                itself */
00079 
00080 #if ARES_VERSION >= 0x010500
00081 /* c-ares 1.5.0 or later, the callback proto is modified */
00082 #define HAVE_CARES_CALLBACK_TIMEOUTS 1
00083 #endif
00084 
00085 /* The last 3 #include files should be in this order */
00086 #include "curl_printf.h"
00087 #include "curl_memory.h"
00088 #include "memdebug.h"
00089 
00090 struct ResolverResults {
00091   int num_pending; /* number of ares_gethostbyname() requests */
00092   Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
00093   int last_status;
00094 };
00095 
00096 /*
00097  * Curl_resolver_global_init() - the generic low-level asynchronous name
00098  * resolve API.  Called from curl_global_init() to initialize global resolver
00099  * environment.  Initializes ares library.
00100  */
00101 int Curl_resolver_global_init(void)
00102 {
00103 #ifdef CARES_HAVE_ARES_LIBRARY_INIT
00104   if(ares_library_init(ARES_LIB_INIT_ALL)) {
00105     return CURLE_FAILED_INIT;
00106   }
00107 #endif
00108   return CURLE_OK;
00109 }
00110 
00111 /*
00112  * Curl_resolver_global_cleanup()
00113  *
00114  * Called from curl_global_cleanup() to destroy global resolver environment.
00115  * Deinitializes ares library.
00116  */
00117 void Curl_resolver_global_cleanup(void)
00118 {
00119 #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
00120   ares_library_cleanup();
00121 #endif
00122 }
00123 
00124 /*
00125  * Curl_resolver_init()
00126  *
00127  * Called from curl_easy_init() -> Curl_open() to initialize resolver
00128  * URL-state specific environment ('resolver' member of the UrlState
00129  * structure).  Fills the passed pointer by the initialized ares_channel.
00130  */
00131 CURLcode Curl_resolver_init(void **resolver)
00132 {
00133   int status = ares_init((ares_channel*)resolver);
00134   if(status != ARES_SUCCESS) {
00135     if(status == ARES_ENOMEM)
00136       return CURLE_OUT_OF_MEMORY;
00137     else
00138       return CURLE_FAILED_INIT;
00139   }
00140   return CURLE_OK;
00141   /* make sure that all other returns from this function should destroy the
00142      ares channel before returning error! */
00143 }
00144 
00145 /*
00146  * Curl_resolver_cleanup()
00147  *
00148  * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
00149  * URL-state specific environment ('resolver' member of the UrlState
00150  * structure).  Destroys the ares channel.
00151  */
00152 void Curl_resolver_cleanup(void *resolver)
00153 {
00154   ares_destroy((ares_channel)resolver);
00155 }
00156 
00157 /*
00158  * Curl_resolver_duphandle()
00159  *
00160  * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
00161  * environment ('resolver' member of the UrlState structure).  Duplicates the
00162  * 'from' ares channel and passes the resulting channel to the 'to' pointer.
00163  */
00164 int Curl_resolver_duphandle(void **to, void *from)
00165 {
00166   /* Clone the ares channel for the new handle */
00167   if(ARES_SUCCESS != ares_dup((ares_channel*)to, (ares_channel)from))
00168     return CURLE_FAILED_INIT;
00169   return CURLE_OK;
00170 }
00171 
00172 static void destroy_async_data(struct Curl_async *async);
00173 
00174 /*
00175  * Cancel all possibly still on-going resolves for this connection.
00176  */
00177 void Curl_resolver_cancel(struct connectdata *conn)
00178 {
00179   if(conn->data && conn->data->state.resolver)
00180     ares_cancel((ares_channel)conn->data->state.resolver);
00181   destroy_async_data(&conn->async);
00182 }
00183 
00184 /*
00185  * destroy_async_data() cleans up async resolver data.
00186  */
00187 static void destroy_async_data(struct Curl_async *async)
00188 {
00189   free(async->hostname);
00190 
00191   if(async->os_specific) {
00192     struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
00193     if(res) {
00194       if(res->temp_ai) {
00195         Curl_freeaddrinfo(res->temp_ai);
00196         res->temp_ai = NULL;
00197       }
00198       free(res);
00199     }
00200     async->os_specific = NULL;
00201   }
00202 
00203   async->hostname = NULL;
00204 }
00205 
00206 /*
00207  * Curl_resolver_getsock() is called when someone from the outside world
00208  * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
00209  * with ares. The caller must make sure that this function is only called when
00210  * we have a working ares channel.
00211  *
00212  * Returns: sockets-in-use-bitmap
00213  */
00214 
00215 int Curl_resolver_getsock(struct connectdata *conn,
00216                           curl_socket_t *socks,
00217                           int numsocks)
00218 
00219 {
00220   struct timeval maxtime;
00221   struct timeval timebuf;
00222   struct timeval *timeout;
00223   long milli;
00224   int max = ares_getsock((ares_channel)conn->data->state.resolver,
00225                          (ares_socket_t *)socks, numsocks);
00226 
00227   maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
00228   maxtime.tv_usec = 0;
00229 
00230   timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
00231                          &timebuf);
00232   milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
00233   if(milli == 0)
00234     milli += 10;
00235   Curl_expire_latest(conn->data, milli);
00236 
00237   return max;
00238 }
00239 
00240 /*
00241  * waitperform()
00242  *
00243  * 1) Ask ares what sockets it currently plays with, then
00244  * 2) wait for the timeout period to check for action on ares' sockets.
00245  * 3) tell ares to act on all the sockets marked as "with action"
00246  *
00247  * return number of sockets it worked on
00248  */
00249 
00250 static int waitperform(struct connectdata *conn, int timeout_ms)
00251 {
00252   struct Curl_easy *data = conn->data;
00253   int nfds;
00254   int bitmask;
00255   ares_socket_t socks[ARES_GETSOCK_MAXNUM];
00256   struct pollfd pfd[ARES_GETSOCK_MAXNUM];
00257   int i;
00258   int num = 0;
00259 
00260   bitmask = ares_getsock((ares_channel)data->state.resolver, socks,
00261                          ARES_GETSOCK_MAXNUM);
00262 
00263   for(i=0; i < ARES_GETSOCK_MAXNUM; i++) {
00264     pfd[i].events = 0;
00265     pfd[i].revents = 0;
00266     if(ARES_GETSOCK_READABLE(bitmask, i)) {
00267       pfd[i].fd = socks[i];
00268       pfd[i].events |= POLLRDNORM|POLLIN;
00269     }
00270     if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
00271       pfd[i].fd = socks[i];
00272       pfd[i].events |= POLLWRNORM|POLLOUT;
00273     }
00274     if(pfd[i].events != 0)
00275       num++;
00276     else
00277       break;
00278   }
00279 
00280   if(num)
00281     nfds = Curl_poll(pfd, num, timeout_ms);
00282   else
00283     nfds = 0;
00284 
00285   if(!nfds)
00286     /* Call ares_process() unconditonally here, even if we simply timed out
00287        above, as otherwise the ares name resolve won't timeout! */
00288     ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD,
00289                     ARES_SOCKET_BAD);
00290   else {
00291     /* move through the descriptors and ask for processing on them */
00292     for(i=0; i < num; i++)
00293       ares_process_fd((ares_channel)data->state.resolver,
00294                       pfd[i].revents & (POLLRDNORM|POLLIN)?
00295                       pfd[i].fd:ARES_SOCKET_BAD,
00296                       pfd[i].revents & (POLLWRNORM|POLLOUT)?
00297                       pfd[i].fd:ARES_SOCKET_BAD);
00298   }
00299   return nfds;
00300 }
00301 
00302 /*
00303  * Curl_resolver_is_resolved() is called repeatedly to check if a previous
00304  * name resolve request has completed. It should also make sure to time-out if
00305  * the operation seems to take too long.
00306  *
00307  * Returns normal CURLcode errors.
00308  */
00309 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
00310                                    struct Curl_dns_entry **dns)
00311 {
00312   struct Curl_easy *data = conn->data;
00313   struct ResolverResults *res = (struct ResolverResults *)
00314     conn->async.os_specific;
00315   CURLcode result = CURLE_OK;
00316 
00317   *dns = NULL;
00318 
00319   waitperform(conn, 0);
00320 
00321   if(res && !res->num_pending) {
00322     (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
00323     /* temp_ai ownership is moved to the connection, so we need not free-up
00324        them */
00325     res->temp_ai = NULL;
00326     if(!conn->async.dns) {
00327       failf(data, "Could not resolve: %s (%s)",
00328             conn->async.hostname, ares_strerror(conn->async.status));
00329       result = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
00330         CURLE_COULDNT_RESOLVE_HOST;
00331     }
00332     else
00333       *dns = conn->async.dns;
00334 
00335     destroy_async_data(&conn->async);
00336   }
00337 
00338   return result;
00339 }
00340 
00341 /*
00342  * Curl_resolver_wait_resolv()
00343  *
00344  * waits for a resolve to finish. This function should be avoided since using
00345  * this risk getting the multi interface to "hang".
00346  *
00347  * If 'entry' is non-NULL, make it point to the resolved dns entry
00348  *
00349  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
00350  * CURLE_OPERATION_TIMEDOUT if a time-out occurred.
00351  */
00352 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
00353                                    struct Curl_dns_entry **entry)
00354 {
00355   CURLcode result = CURLE_OK;
00356   struct Curl_easy *data = conn->data;
00357   long timeout;
00358   struct timeval now = Curl_tvnow();
00359   struct Curl_dns_entry *temp_entry;
00360 
00361   timeout = Curl_timeleft(data, &now, TRUE);
00362   if(!timeout)
00363     timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
00364 
00365   /* Wait for the name resolve query to complete. */
00366   for(;;) {
00367     struct timeval *tvp, tv, store;
00368     long timediff;
00369     int itimeout;
00370     int timeout_ms;
00371 
00372     itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
00373 
00374     store.tv_sec = itimeout/1000;
00375     store.tv_usec = (itimeout%1000)*1000;
00376 
00377     tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
00378 
00379     /* use the timeout period ares returned to us above if less than one
00380        second is left, otherwise just use 1000ms to make sure the progress
00381        callback gets called frequent enough */
00382     if(!tvp->tv_sec)
00383       timeout_ms = (int)(tvp->tv_usec/1000);
00384     else
00385       timeout_ms = 1000;
00386 
00387     waitperform(conn, timeout_ms);
00388     Curl_resolver_is_resolved(conn, &temp_entry);
00389 
00390     if(conn->async.done)
00391       break;
00392 
00393     if(Curl_pgrsUpdate(conn)) {
00394       result = CURLE_ABORTED_BY_CALLBACK;
00395       timeout = -1; /* trigger the cancel below */
00396     }
00397     else {
00398       struct timeval now2 = Curl_tvnow();
00399       timediff = Curl_tvdiff(now2, now); /* spent time */
00400       timeout -= timediff?timediff:1; /* always deduct at least 1 */
00401       now = now2; /* for next loop */
00402     }
00403 
00404     if(timeout < 0) {
00405       /* our timeout, so we cancel the ares operation */
00406       ares_cancel((ares_channel)data->state.resolver);
00407       break;
00408     }
00409   }
00410 
00411   /* Operation complete, if the lookup was successful we now have the entry
00412      in the cache. */
00413   if(entry)
00414     *entry = conn->async.dns;
00415 
00416   if(result)
00417     /* close the connection, since we can't return failure here without
00418        cleaning up this connection properly.
00419        TODO: remove this action from here, it is not a name resolver decision.
00420     */
00421     connclose(conn, "c-ares resolve failed");
00422 
00423   return result;
00424 }
00425 
00426 /* Connects results to the list */
00427 static void compound_results(struct ResolverResults *res,
00428                              Curl_addrinfo *ai)
00429 {
00430   Curl_addrinfo *ai_tail;
00431   if(!ai)
00432     return;
00433   ai_tail = ai;
00434 
00435   while(ai_tail->ai_next)
00436     ai_tail = ai_tail->ai_next;
00437 
00438   /* Add the new results to the list of old results. */
00439   ai_tail->ai_next = res->temp_ai;
00440   res->temp_ai = ai;
00441 }
00442 
00443 /*
00444  * ares_query_completed_cb() is the callback that ares will call when
00445  * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
00446  * when using ares, is completed either successfully or with failure.
00447  */
00448 static void query_completed_cb(void *arg,  /* (struct connectdata *) */
00449                                int status,
00450 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
00451                                int timeouts,
00452 #endif
00453                                struct hostent *hostent)
00454 {
00455   struct connectdata *conn = (struct connectdata *)arg;
00456   struct ResolverResults *res;
00457 
00458 #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
00459   (void)timeouts; /* ignored */
00460 #endif
00461 
00462   if(ARES_EDESTRUCTION == status)
00463     /* when this ares handle is getting destroyed, the 'arg' pointer may not
00464        be valid so only defer it when we know the 'status' says its fine! */
00465     return;
00466 
00467   res = (struct ResolverResults *)conn->async.os_specific;
00468   res->num_pending--;
00469 
00470   if(CURL_ASYNC_SUCCESS == status) {
00471     Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
00472     if(ai) {
00473       compound_results(res, ai);
00474     }
00475   }
00476   /* A successful result overwrites any previous error */
00477   if(res->last_status != ARES_SUCCESS)
00478     res->last_status = status;
00479 }
00480 
00481 /*
00482  * Curl_resolver_getaddrinfo() - when using ares
00483  *
00484  * Returns name information about the given hostname and port number. If
00485  * successful, the 'hostent' is returned and the forth argument will point to
00486  * memory we need to free after use. That memory *MUST* be freed with
00487  * Curl_freeaddrinfo(), nothing else.
00488  */
00489 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
00490                                          const char *hostname,
00491                                          int port,
00492                                          int *waitp)
00493 {
00494   char *bufp;
00495   struct Curl_easy *data = conn->data;
00496   struct in_addr in;
00497   int family = PF_INET;
00498 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
00499   struct in6_addr in6;
00500 #endif /* CURLRES_IPV6 */
00501 
00502   *waitp = 0; /* default to synchronous response */
00503 
00504   /* First check if this is an IPv4 address string */
00505   if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
00506     /* This is a dotted IP address 123.123.123.123-style */
00507     return Curl_ip2addr(AF_INET, &in, hostname, port);
00508   }
00509 
00510 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
00511   /* Otherwise, check if this is an IPv6 address string */
00512   if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
00513     /* This must be an IPv6 address literal.  */
00514     return Curl_ip2addr(AF_INET6, &in6, hostname, port);
00515 
00516   switch(conn->ip_version) {
00517   default:
00518 #if ARES_VERSION >= 0x010601
00519     family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
00520                            c-ares versions this just falls through and defaults
00521                            to PF_INET */
00522     break;
00523 #endif
00524   case CURL_IPRESOLVE_V4:
00525     family = PF_INET;
00526     break;
00527   case CURL_IPRESOLVE_V6:
00528     family = PF_INET6;
00529     break;
00530   }
00531 #endif /* CURLRES_IPV6 */
00532 
00533   bufp = strdup(hostname);
00534   if(bufp) {
00535     struct ResolverResults *res = NULL;
00536     free(conn->async.hostname);
00537     conn->async.hostname = bufp;
00538     conn->async.port = port;
00539     conn->async.done = FALSE;   /* not done */
00540     conn->async.status = 0;     /* clear */
00541     conn->async.dns = NULL;     /* clear */
00542     res = calloc(sizeof(struct ResolverResults), 1);
00543     if(!res) {
00544       free(conn->async.hostname);
00545       conn->async.hostname = NULL;
00546       return NULL;
00547     }
00548     conn->async.os_specific = res;
00549 
00550     /* initial status - failed */
00551     res->last_status = ARES_ENOTFOUND;
00552 #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
00553     if(family == PF_UNSPEC) {
00554       if(Curl_ipv6works()) {
00555         res->num_pending = 2;
00556 
00557         /* areschannel is already setup in the Curl_open() function */
00558         ares_gethostbyname((ares_channel)data->state.resolver, hostname,
00559                             PF_INET, query_completed_cb, conn);
00560         ares_gethostbyname((ares_channel)data->state.resolver, hostname,
00561                             PF_INET6, query_completed_cb, conn);
00562       }
00563       else {
00564         res->num_pending = 1;
00565 
00566         /* areschannel is already setup in the Curl_open() function */
00567         ares_gethostbyname((ares_channel)data->state.resolver, hostname,
00568                             PF_INET, query_completed_cb, conn);
00569       }
00570     }
00571     else
00572 #endif /* CURLRES_IPV6 */
00573     {
00574       res->num_pending = 1;
00575 
00576       /* areschannel is already setup in the Curl_open() function */
00577       ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
00578                          query_completed_cb, conn);
00579     }
00580 
00581     *waitp = 1; /* expect asynchronous response */
00582   }
00583   return NULL; /* no struct yet */
00584 }
00585 
00586 CURLcode Curl_set_dns_servers(struct Curl_easy *data,
00587                               char *servers)
00588 {
00589   CURLcode result = CURLE_NOT_BUILT_IN;
00590   int ares_result;
00591 
00592   /* If server is NULL or empty, this would purge all DNS servers
00593    * from ares library, which will cause any and all queries to fail.
00594    * So, just return OK if none are configured and don't actually make
00595    * any changes to c-ares.  This lets c-ares use it's defaults, which
00596    * it gets from the OS (for instance from /etc/resolv.conf on Linux).
00597    */
00598   if(!(servers && servers[0]))
00599     return CURLE_OK;
00600 
00601 #if (ARES_VERSION >= 0x010704)
00602   ares_result = ares_set_servers_csv(data->state.resolver, servers);
00603   switch(ares_result) {
00604   case ARES_SUCCESS:
00605     result = CURLE_OK;
00606     break;
00607   case ARES_ENOMEM:
00608     result = CURLE_OUT_OF_MEMORY;
00609     break;
00610   case ARES_ENOTINITIALIZED:
00611   case ARES_ENODATA:
00612   case ARES_EBADSTR:
00613   default:
00614     result = CURLE_BAD_FUNCTION_ARGUMENT;
00615     break;
00616   }
00617 #else /* too old c-ares version! */
00618   (void)data;
00619   (void)(ares_result);
00620 #endif
00621   return result;
00622 }
00623 
00624 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
00625                                 const char *interf)
00626 {
00627 #if (ARES_VERSION >= 0x010704)
00628   if(!interf)
00629     interf = "";
00630 
00631   ares_set_local_dev((ares_channel)data->state.resolver, interf);
00632 
00633   return CURLE_OK;
00634 #else /* c-ares version too old! */
00635   (void)data;
00636   (void)interf;
00637   return CURLE_NOT_BUILT_IN;
00638 #endif
00639 }
00640 
00641 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
00642                                 const char *local_ip4)
00643 {
00644 #if (ARES_VERSION >= 0x010704)
00645   struct in_addr a4;
00646 
00647   if((!local_ip4) || (local_ip4[0] == 0)) {
00648     a4.s_addr = 0; /* disabled: do not bind to a specific address */
00649   }
00650   else {
00651     if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
00652       return CURLE_BAD_FUNCTION_ARGUMENT;
00653     }
00654   }
00655 
00656   ares_set_local_ip4((ares_channel)data->state.resolver, ntohl(a4.s_addr));
00657 
00658   return CURLE_OK;
00659 #else /* c-ares version too old! */
00660   (void)data;
00661   (void)local_ip4;
00662   return CURLE_NOT_BUILT_IN;
00663 #endif
00664 }
00665 
00666 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
00667                                 const char *local_ip6)
00668 {
00669 #if (ARES_VERSION >= 0x010704) && defined(ENABLE_IPV6)
00670   unsigned char a6[INET6_ADDRSTRLEN];
00671 
00672   if((!local_ip6) || (local_ip6[0] == 0)) {
00673     /* disabled: do not bind to a specific address */
00674     memset(a6, 0, sizeof(a6));
00675   }
00676   else {
00677     if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
00678       return CURLE_BAD_FUNCTION_ARGUMENT;
00679     }
00680   }
00681 
00682   ares_set_local_ip6((ares_channel)data->state.resolver, a6);
00683 
00684   return CURLE_OK;
00685 #else /* c-ares version too old! */
00686   (void)data;
00687   (void)local_ip6;
00688   return CURLE_NOT_BUILT_IN;
00689 #endif
00690 }
00691 #endif /* CURLRES_ARES */


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:01