"Fossies" - the Fresh Open Source Software Archive

Member "curl-7.66.0/lib/asyn-thread.c" (9 Sep 2019, 20849 Bytes) of package /linux/www/curl-7.66.0.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "asyn-thread.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 7.65.3_vs_7.66.0.

    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 threaded name resolves builds
   27  **********************************************************************/
   28 #ifdef CURLRES_THREADED
   29 
   30 #ifdef HAVE_NETINET_IN_H
   31 #include <netinet/in.h>
   32 #endif
   33 #ifdef HAVE_NETDB_H
   34 #include <netdb.h>
   35 #endif
   36 #ifdef HAVE_ARPA_INET_H
   37 #include <arpa/inet.h>
   38 #endif
   39 #ifdef __VMS
   40 #include <in.h>
   41 #include <inet.h>
   42 #endif
   43 
   44 #if defined(USE_THREADS_POSIX)
   45 #  ifdef HAVE_PTHREAD_H
   46 #    include <pthread.h>
   47 #  endif
   48 #elif defined(USE_THREADS_WIN32)
   49 #  ifdef HAVE_PROCESS_H
   50 #    include <process.h>
   51 #  endif
   52 #endif
   53 
   54 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
   55 #undef in_addr_t
   56 #define in_addr_t unsigned long
   57 #endif
   58 
   59 #ifdef HAVE_GETADDRINFO
   60 #  define RESOLVER_ENOMEM  EAI_MEMORY
   61 #else
   62 #  define RESOLVER_ENOMEM  ENOMEM
   63 #endif
   64 
   65 #include "urldata.h"
   66 #include "sendf.h"
   67 #include "hostip.h"
   68 #include "hash.h"
   69 #include "share.h"
   70 #include "strerror.h"
   71 #include "url.h"
   72 #include "multiif.h"
   73 #include "inet_pton.h"
   74 #include "inet_ntop.h"
   75 #include "curl_threads.h"
   76 #include "connect.h"
   77 /* The last 3 #include files should be in this order */
   78 #include "curl_printf.h"
   79 #include "curl_memory.h"
   80 #include "memdebug.h"
   81 
   82 struct resdata {
   83   struct curltime start;
   84 };
   85 
   86 /*
   87  * Curl_resolver_global_init()
   88  * Called from curl_global_init() to initialize global resolver environment.
   89  * Does nothing here.
   90  */
   91 int Curl_resolver_global_init(void)
   92 {
   93   return CURLE_OK;
   94 }
   95 
   96 /*
   97  * Curl_resolver_global_cleanup()
   98  * Called from curl_global_cleanup() to destroy global resolver environment.
   99  * Does nothing here.
  100  */
  101 void Curl_resolver_global_cleanup(void)
  102 {
  103 }
  104 
  105 /*
  106  * Curl_resolver_init()
  107  * Called from curl_easy_init() -> Curl_open() to initialize resolver
  108  * URL-state specific environment ('resolver' member of the UrlState
  109  * structure).
  110  */
  111 CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
  112 {
  113   (void)easy;
  114   *resolver = calloc(1, sizeof(struct resdata));
  115   if(!*resolver)
  116     return CURLE_OUT_OF_MEMORY;
  117   return CURLE_OK;
  118 }
  119 
  120 /*
  121  * Curl_resolver_cleanup()
  122  * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
  123  * URL-state specific environment ('resolver' member of the UrlState
  124  * structure).
  125  */
  126 void Curl_resolver_cleanup(void *resolver)
  127 {
  128   free(resolver);
  129 }
  130 
  131 /*
  132  * Curl_resolver_duphandle()
  133  * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
  134  * environment ('resolver' member of the UrlState structure).
  135  */
  136 CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
  137 {
  138   (void)from;
  139   return Curl_resolver_init(easy, to);
  140 }
  141 
  142 static void destroy_async_data(struct Curl_async *);
  143 
  144 /*
  145  * Cancel all possibly still on-going resolves for this connection.
  146  */
  147 void Curl_resolver_cancel(struct connectdata *conn)
  148 {
  149   destroy_async_data(&conn->async);
  150 }
  151 
  152 /* This function is used to init a threaded resolve */
  153 static bool init_resolve_thread(struct connectdata *conn,
  154                                 const char *hostname, int port,
  155                                 const struct addrinfo *hints);
  156 
  157 
  158 /* Data for synchronization between resolver thread and its parent */
  159 struct thread_sync_data {
  160   curl_mutex_t * mtx;
  161   int done;
  162 
  163   char *hostname;        /* hostname to resolve, Curl_async.hostname
  164                             duplicate */
  165   int port;
  166 #ifdef HAVE_SOCKETPAIR
  167   struct connectdata *conn;
  168   curl_socket_t sock_pair[2]; /* socket pair */
  169 #endif
  170   int sock_error;
  171   Curl_addrinfo *res;
  172 #ifdef HAVE_GETADDRINFO
  173   struct addrinfo hints;
  174 #endif
  175   struct thread_data *td; /* for thread-self cleanup */
  176 };
  177 
  178 struct thread_data {
  179   curl_thread_t thread_hnd;
  180   unsigned int poll_interval;
  181   time_t interval_end;
  182   struct thread_sync_data tsd;
  183 };
  184 
  185 static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn)
  186 {
  187   return &(((struct thread_data *)conn->async.os_specific)->tsd);
  188 }
  189 
  190 /* Destroy resolver thread synchronization data */
  191 static
  192 void destroy_thread_sync_data(struct thread_sync_data * tsd)
  193 {
  194   if(tsd->mtx) {
  195     Curl_mutex_destroy(tsd->mtx);
  196     free(tsd->mtx);
  197   }
  198 
  199   free(tsd->hostname);
  200 
  201   if(tsd->res)
  202     Curl_freeaddrinfo(tsd->res);
  203 
  204 #ifdef HAVE_SOCKETPAIR
  205   /*
  206    * close one end of the socket pair (may be done in resolver thread);
  207    * the other end (for reading) is always closed in the parent thread.
  208    */
  209   if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
  210     sclose(tsd->sock_pair[1]);
  211   }
  212 #endif
  213   memset(tsd, 0, sizeof(*tsd));
  214 }
  215 
  216 /* Initialize resolver thread synchronization data */
  217 static
  218 int init_thread_sync_data(struct thread_data * td,
  219                            const char *hostname,
  220                            int port,
  221                            const struct addrinfo *hints)
  222 {
  223   struct thread_sync_data *tsd = &td->tsd;
  224 
  225   memset(tsd, 0, sizeof(*tsd));
  226 
  227   tsd->td = td;
  228   tsd->port = port;
  229   /* Treat the request as done until the thread actually starts so any early
  230    * cleanup gets done properly.
  231    */
  232   tsd->done = 1;
  233 #ifdef HAVE_GETADDRINFO
  234   DEBUGASSERT(hints);
  235   tsd->hints = *hints;
  236 #else
  237   (void) hints;
  238 #endif
  239 
  240   tsd->mtx = malloc(sizeof(curl_mutex_t));
  241   if(tsd->mtx == NULL)
  242     goto err_exit;
  243 
  244   Curl_mutex_init(tsd->mtx);
  245 
  246 #ifdef HAVE_SOCKETPAIR
  247   /* create socket pair */
  248   if(socketpair(AF_LOCAL, SOCK_STREAM, 0, &tsd->sock_pair[0]) < 0) {
  249     tsd->sock_pair[0] = CURL_SOCKET_BAD;
  250     tsd->sock_pair[1] = CURL_SOCKET_BAD;
  251     goto err_exit;
  252   }
  253 #endif
  254   tsd->sock_error = CURL_ASYNC_SUCCESS;
  255 
  256   /* Copying hostname string because original can be destroyed by parent
  257    * thread during gethostbyname execution.
  258    */
  259   tsd->hostname = strdup(hostname);
  260   if(!tsd->hostname)
  261     goto err_exit;
  262 
  263   return 1;
  264 
  265  err_exit:
  266   /* Memory allocation failed */
  267   destroy_thread_sync_data(tsd);
  268   return 0;
  269 }
  270 
  271 static int getaddrinfo_complete(struct connectdata *conn)
  272 {
  273   struct thread_sync_data *tsd = conn_thread_sync_data(conn);
  274   int rc;
  275 
  276   rc = Curl_addrinfo_callback(conn, tsd->sock_error, tsd->res);
  277   /* The tsd->res structure has been copied to async.dns and perhaps the DNS
  278      cache.  Set our copy to NULL so destroy_thread_sync_data doesn't free it.
  279   */
  280   tsd->res = NULL;
  281 
  282   return rc;
  283 }
  284 
  285 
  286 #ifdef HAVE_GETADDRINFO
  287 
  288 /*
  289  * getaddrinfo_thread() resolves a name and then exits.
  290  *
  291  * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
  292  * and wait on it.
  293  */
  294 static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
  295 {
  296   struct thread_sync_data *tsd = (struct thread_sync_data*)arg;
  297   struct thread_data *td = tsd->td;
  298   char service[12];
  299   int rc;
  300 #ifdef HAVE_SOCKETPAIR
  301   char buf[1];
  302 #endif
  303 
  304   msnprintf(service, sizeof(service), "%d", tsd->port);
  305 
  306   rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
  307 
  308   if(rc != 0) {
  309     tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
  310     if(tsd->sock_error == 0)
  311       tsd->sock_error = RESOLVER_ENOMEM;
  312   }
  313   else {
  314     Curl_addrinfo_set_port(tsd->res, tsd->port);
  315   }
  316 
  317   Curl_mutex_acquire(tsd->mtx);
  318   if(tsd->done) {
  319     /* too late, gotta clean up the mess */
  320     Curl_mutex_release(tsd->mtx);
  321     destroy_thread_sync_data(tsd);
  322     free(td);
  323   }
  324   else {
  325 #ifdef HAVE_SOCKETPAIR
  326     if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
  327       /* DNS has been resolved, signal client task */
  328       buf[0] = 1;
  329       if(write(tsd->sock_pair[1],  buf, sizeof(buf)) < 0) {
  330         /* update sock_erro to errno */
  331         tsd->sock_error = SOCKERRNO;
  332       }
  333     }
  334 #endif
  335     tsd->done = 1;
  336     Curl_mutex_release(tsd->mtx);
  337   }
  338 
  339   return 0;
  340 }
  341 
  342 #else /* HAVE_GETADDRINFO */
  343 
  344 /*
  345  * gethostbyname_thread() resolves a name and then exits.
  346  */
  347 static unsigned int CURL_STDCALL gethostbyname_thread(void *arg)
  348 {
  349   struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
  350   struct thread_data *td = tsd->td;
  351 
  352   tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
  353 
  354   if(!tsd->res) {
  355     tsd->sock_error = SOCKERRNO;
  356     if(tsd->sock_error == 0)
  357       tsd->sock_error = RESOLVER_ENOMEM;
  358   }
  359 
  360   Curl_mutex_acquire(tsd->mtx);
  361   if(tsd->done) {
  362     /* too late, gotta clean up the mess */
  363     Curl_mutex_release(tsd->mtx);
  364     destroy_thread_sync_data(tsd);
  365     free(td);
  366   }
  367   else {
  368     tsd->done = 1;
  369     Curl_mutex_release(tsd->mtx);
  370   }
  371 
  372   return 0;
  373 }
  374 
  375 #endif /* HAVE_GETADDRINFO */
  376 
  377 /*
  378  * destroy_async_data() cleans up async resolver data and thread handle.
  379  */
  380 static void destroy_async_data(struct Curl_async *async)
  381 {
  382   if(async->os_specific) {
  383     struct thread_data *td = (struct thread_data*) async->os_specific;
  384     int done;
  385 #ifdef HAVE_SOCKETPAIR
  386     curl_socket_t sock_rd = td->tsd.sock_pair[0];
  387     struct connectdata *conn = td->tsd.conn;
  388 #endif
  389 
  390     /*
  391      * if the thread is still blocking in the resolve syscall, detach it and
  392      * let the thread do the cleanup...
  393      */
  394     Curl_mutex_acquire(td->tsd.mtx);
  395     done = td->tsd.done;
  396     td->tsd.done = 1;
  397     Curl_mutex_release(td->tsd.mtx);
  398 
  399     if(!done) {
  400       Curl_thread_destroy(td->thread_hnd);
  401     }
  402     else {
  403       if(td->thread_hnd != curl_thread_t_null)
  404         Curl_thread_join(&td->thread_hnd);
  405 
  406       destroy_thread_sync_data(&td->tsd);
  407 
  408       free(async->os_specific);
  409     }
  410 #ifdef HAVE_SOCKETPAIR
  411     /*
  412      * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
  413      * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
  414      */
  415     if(conn)
  416       Curl_multi_closed(conn->data, sock_rd);
  417     sclose(sock_rd);
  418 #endif
  419   }
  420   async->os_specific = NULL;
  421 
  422   free(async->hostname);
  423   async->hostname = NULL;
  424 }
  425 
  426 /*
  427  * init_resolve_thread() starts a new thread that performs the actual
  428  * resolve. This function returns before the resolve is done.
  429  *
  430  * Returns FALSE in case of failure, otherwise TRUE.
  431  */
  432 static bool init_resolve_thread(struct connectdata *conn,
  433                                 const char *hostname, int port,
  434                                 const struct addrinfo *hints)
  435 {
  436   struct thread_data *td = calloc(1, sizeof(struct thread_data));
  437   int err = ENOMEM;
  438 
  439   conn->async.os_specific = (void *)td;
  440   if(!td)
  441     goto errno_exit;
  442 
  443   conn->async.port = port;
  444   conn->async.done = FALSE;
  445   conn->async.status = 0;
  446   conn->async.dns = NULL;
  447   td->thread_hnd = curl_thread_t_null;
  448 
  449   if(!init_thread_sync_data(td, hostname, port, hints)) {
  450     conn->async.os_specific = NULL;
  451     free(td);
  452     goto errno_exit;
  453   }
  454 
  455   free(conn->async.hostname);
  456   conn->async.hostname = strdup(hostname);
  457   if(!conn->async.hostname)
  458     goto err_exit;
  459 
  460   /* The thread will set this to 1 when complete. */
  461   td->tsd.done = 0;
  462 
  463 #ifdef HAVE_GETADDRINFO
  464   td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
  465 #else
  466   td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
  467 #endif
  468 
  469   if(!td->thread_hnd) {
  470     /* The thread never started, so mark it as done here for proper cleanup. */
  471     td->tsd.done = 1;
  472     err = errno;
  473     goto err_exit;
  474   }
  475 
  476   return TRUE;
  477 
  478  err_exit:
  479   destroy_async_data(&conn->async);
  480 
  481  errno_exit:
  482   errno = err;
  483   return FALSE;
  484 }
  485 
  486 /*
  487  * resolver_error() calls failf() with the appropriate message after a resolve
  488  * error
  489  */
  490 
  491 static CURLcode resolver_error(struct connectdata *conn)
  492 {
  493   const char *host_or_proxy;
  494   CURLcode result;
  495 
  496   if(conn->bits.httpproxy) {
  497     host_or_proxy = "proxy";
  498     result = CURLE_COULDNT_RESOLVE_PROXY;
  499   }
  500   else {
  501     host_or_proxy = "host";
  502     result = CURLE_COULDNT_RESOLVE_HOST;
  503   }
  504 
  505   failf(conn->data, "Could not resolve %s: %s", host_or_proxy,
  506         conn->async.hostname);
  507 
  508   return result;
  509 }
  510 
  511 static CURLcode thread_wait_resolv(struct connectdata *conn,
  512                                    struct Curl_dns_entry **entry,
  513                                    bool report)
  514 {
  515   struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
  516   CURLcode result = CURLE_OK;
  517 
  518   DEBUGASSERT(conn && td);
  519   DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
  520 
  521   /* wait for the thread to resolve the name */
  522   if(Curl_thread_join(&td->thread_hnd)) {
  523     if(entry)
  524       result = getaddrinfo_complete(conn);
  525   }
  526   else
  527     DEBUGASSERT(0);
  528 
  529   conn->async.done = TRUE;
  530 
  531   if(entry)
  532     *entry = conn->async.dns;
  533 
  534   if(!conn->async.dns && report)
  535     /* a name was not resolved, report error */
  536     result = resolver_error(conn);
  537 
  538   destroy_async_data(&conn->async);
  539 
  540   if(!conn->async.dns && report)
  541     connclose(conn, "asynch resolve failed");
  542 
  543   return result;
  544 }
  545 
  546 
  547 /*
  548  * Until we gain a way to signal the resolver threads to stop early, we must
  549  * simply wait for them and ignore their results.
  550  */
  551 void Curl_resolver_kill(struct connectdata *conn)
  552 {
  553   struct thread_data *td = (struct thread_data*) conn->async.os_specific;
  554 
  555   /* If we're still resolving, we must wait for the threads to fully clean up,
  556      unfortunately.  Otherwise, we can simply cancel to clean up any resolver
  557      data. */
  558   if(td && td->thread_hnd != curl_thread_t_null)
  559     (void)thread_wait_resolv(conn, NULL, FALSE);
  560   else
  561     Curl_resolver_cancel(conn);
  562 }
  563 
  564 /*
  565  * Curl_resolver_wait_resolv()
  566  *
  567  * Waits for a resolve to finish. This function should be avoided since using
  568  * this risk getting the multi interface to "hang".
  569  *
  570  * If 'entry' is non-NULL, make it point to the resolved dns entry
  571  *
  572  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
  573  * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
  574  *
  575  * This is the version for resolves-in-a-thread.
  576  */
  577 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
  578                                    struct Curl_dns_entry **entry)
  579 {
  580   return thread_wait_resolv(conn, entry, TRUE);
  581 }
  582 
  583 /*
  584  * Curl_resolver_is_resolved() is called repeatedly to check if a previous
  585  * name resolve request has completed. It should also make sure to time-out if
  586  * the operation seems to take too long.
  587  */
  588 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
  589                                    struct Curl_dns_entry **entry)
  590 {
  591   struct Curl_easy *data = conn->data;
  592   struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
  593   int done = 0;
  594 
  595   *entry = NULL;
  596 
  597   if(!td) {
  598     DEBUGASSERT(td);
  599     return CURLE_COULDNT_RESOLVE_HOST;
  600   }
  601 
  602   Curl_mutex_acquire(td->tsd.mtx);
  603   done = td->tsd.done;
  604   Curl_mutex_release(td->tsd.mtx);
  605 
  606   if(done) {
  607     getaddrinfo_complete(conn);
  608 
  609     if(!conn->async.dns) {
  610       CURLcode result = resolver_error(conn);
  611       destroy_async_data(&conn->async);
  612       return result;
  613     }
  614     destroy_async_data(&conn->async);
  615     *entry = conn->async.dns;
  616   }
  617   else {
  618     /* poll for name lookup done with exponential backoff up to 250ms */
  619     /* should be fine even if this converts to 32 bit */
  620     time_t elapsed = (time_t)Curl_timediff(Curl_now(),
  621                                            data->progress.t_startsingle);
  622     if(elapsed < 0)
  623       elapsed = 0;
  624 
  625     if(td->poll_interval == 0)
  626       /* Start at 1ms poll interval */
  627       td->poll_interval = 1;
  628     else if(elapsed >= td->interval_end)
  629       /* Back-off exponentially if last interval expired  */
  630       td->poll_interval *= 2;
  631 
  632     if(td->poll_interval > 250)
  633       td->poll_interval = 250;
  634 
  635     td->interval_end = elapsed + td->poll_interval;
  636     Curl_expire(conn->data, td->poll_interval, EXPIRE_ASYNC_NAME);
  637   }
  638 
  639   return CURLE_OK;
  640 }
  641 
  642 int Curl_resolver_getsock(struct connectdata *conn,
  643                           curl_socket_t *socks)
  644 {
  645   int ret_val = 0;
  646   time_t milli;
  647   timediff_t ms;
  648   struct Curl_easy *data = conn->data;
  649   struct resdata *reslv = (struct resdata *)data->state.resolver;
  650 #ifdef HAVE_SOCKETPAIR
  651   struct thread_data *td = (struct thread_data*)conn->async.os_specific;
  652 #else
  653   (void)socks;
  654 #endif
  655 
  656 #ifdef HAVE_SOCKETPAIR
  657   if(td) {
  658     /* return read fd to client for polling the DNS resolution status */
  659     socks[0] = td->tsd.sock_pair[0];
  660     DEBUGASSERT(td->tsd.conn == conn || !td->tsd.conn);
  661     td->tsd.conn = conn;
  662     ret_val = GETSOCK_READSOCK(0);
  663   }
  664   else {
  665 #endif
  666     ms = Curl_timediff(Curl_now(), reslv->start);
  667     if(ms < 3)
  668       milli = 0;
  669     else if(ms <= 50)
  670       milli = (time_t)ms/3;
  671     else if(ms <= 250)
  672       milli = 50;
  673     else
  674       milli = 200;
  675     Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
  676 #ifdef HAVE_SOCKETPAIR
  677   }
  678 #endif
  679 
  680 
  681   return ret_val;
  682 }
  683 
  684 #ifndef HAVE_GETADDRINFO
  685 /*
  686  * Curl_getaddrinfo() - for platforms without getaddrinfo
  687  */
  688 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
  689                                          const char *hostname,
  690                                          int port,
  691                                          int *waitp)
  692 {
  693   struct in_addr in;
  694   struct Curl_easy *data = conn->data;
  695   struct resdata *reslv = (struct resdata *)data->state.resolver;
  696 
  697   *waitp = 0; /* default to synchronous response */
  698 
  699   if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
  700     /* This is a dotted IP address 123.123.123.123-style */
  701     return Curl_ip2addr(AF_INET, &in, hostname, port);
  702 
  703   reslv->start = Curl_now();
  704 
  705   /* fire up a new resolver thread! */
  706   if(init_resolve_thread(conn, hostname, port, NULL)) {
  707     *waitp = 1; /* expect asynchronous response */
  708     return NULL;
  709   }
  710 
  711   failf(conn->data, "getaddrinfo() thread failed\n");
  712 
  713   return NULL;
  714 }
  715 
  716 #else /* !HAVE_GETADDRINFO */
  717 
  718 /*
  719  * Curl_resolver_getaddrinfo() - for getaddrinfo
  720  */
  721 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
  722                                          const char *hostname,
  723                                          int port,
  724                                          int *waitp)
  725 {
  726   struct addrinfo hints;
  727   char sbuf[12];
  728   int pf = PF_INET;
  729   struct Curl_easy *data = conn->data;
  730   struct resdata *reslv = (struct resdata *)data->state.resolver;
  731 
  732   *waitp = 0; /* default to synchronous response */
  733 
  734 #ifndef USE_RESOLVE_ON_IPS
  735   {
  736     struct in_addr in;
  737     /* First check if this is an IPv4 address string */
  738     if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
  739       /* This is a dotted IP address 123.123.123.123-style */
  740       return Curl_ip2addr(AF_INET, &in, hostname, port);
  741   }
  742 #ifdef CURLRES_IPV6
  743   {
  744     struct in6_addr in6;
  745     /* check if this is an IPv6 address string */
  746     if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
  747       /* This is an IPv6 address literal */
  748       return Curl_ip2addr(AF_INET6, &in6, hostname, port);
  749   }
  750 #endif /* CURLRES_IPV6 */
  751 #endif /* !USE_RESOLVE_ON_IPS */
  752 
  753 #ifdef CURLRES_IPV6
  754   /*
  755    * Check if a limited name resolve has been requested.
  756    */
  757   switch(conn->ip_version) {
  758   case CURL_IPRESOLVE_V4:
  759     pf = PF_INET;
  760     break;
  761   case CURL_IPRESOLVE_V6:
  762     pf = PF_INET6;
  763     break;
  764   default:
  765     pf = PF_UNSPEC;
  766     break;
  767   }
  768 
  769   if((pf != PF_INET) && !Curl_ipv6works())
  770     /* The stack seems to be a non-IPv6 one */
  771     pf = PF_INET;
  772 #endif /* CURLRES_IPV6 */
  773 
  774   memset(&hints, 0, sizeof(hints));
  775   hints.ai_family = pf;
  776   hints.ai_socktype = (conn->transport == TRNSPRT_TCP)?
  777     SOCK_STREAM : SOCK_DGRAM;
  778 
  779   msnprintf(sbuf, sizeof(sbuf), "%d", port);
  780 
  781   reslv->start = Curl_now();
  782   /* fire up a new resolver thread! */
  783   if(init_resolve_thread(conn, hostname, port, &hints)) {
  784     *waitp = 1; /* expect asynchronous response */
  785     return NULL;
  786   }
  787 
  788   failf(data, "getaddrinfo() thread failed to start\n");
  789   return NULL;
  790 
  791 }
  792 
  793 #endif /* !HAVE_GETADDRINFO */
  794 
  795 CURLcode Curl_set_dns_servers(struct Curl_easy *data,
  796                               char *servers)
  797 {
  798   (void)data;
  799   (void)servers;
  800   return CURLE_NOT_BUILT_IN;
  801 
  802 }
  803 
  804 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
  805                                 const char *interf)
  806 {
  807   (void)data;
  808   (void)interf;
  809   return CURLE_NOT_BUILT_IN;
  810 }
  811 
  812 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
  813                                 const char *local_ip4)
  814 {
  815   (void)data;
  816   (void)local_ip4;
  817   return CURLE_NOT_BUILT_IN;
  818 }
  819 
  820 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
  821                                 const char *local_ip6)
  822 {
  823   (void)data;
  824   (void)local_ip6;
  825   return CURLE_NOT_BUILT_IN;
  826 }
  827 
  828 #endif /* CURLRES_THREADED */