asyn-thread.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_NETINET_IN_H
00026 #include <netinet/in.h>
00027 #endif
00028 #ifdef HAVE_NETDB_H
00029 #include <netdb.h>
00030 #endif
00031 #ifdef HAVE_ARPA_INET_H
00032 #include <arpa/inet.h>
00033 #endif
00034 #ifdef __VMS
00035 #include <in.h>
00036 #include <inet.h>
00037 #endif
00038 
00039 #if defined(USE_THREADS_POSIX)
00040 #  ifdef HAVE_PTHREAD_H
00041 #    include <pthread.h>
00042 #  endif
00043 #elif defined(USE_THREADS_WIN32)
00044 #  ifdef HAVE_PROCESS_H
00045 #    include <process.h>
00046 #  endif
00047 #endif
00048 
00049 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
00050 #undef in_addr_t
00051 #define in_addr_t unsigned long
00052 #endif
00053 
00054 #ifdef HAVE_GETADDRINFO
00055 #  define RESOLVER_ENOMEM  EAI_MEMORY
00056 #else
00057 #  define RESOLVER_ENOMEM  ENOMEM
00058 #endif
00059 
00060 #include "urldata.h"
00061 #include "sendf.h"
00062 #include "hostip.h"
00063 #include "hash.h"
00064 #include "share.h"
00065 #include "strerror.h"
00066 #include "url.h"
00067 #include "multiif.h"
00068 #include "inet_pton.h"
00069 #include "inet_ntop.h"
00070 #include "curl_threads.h"
00071 #include "connect.h"
00072 /* The last 3 #include files should be in this order */
00073 #include "curl_printf.h"
00074 #include "curl_memory.h"
00075 #include "memdebug.h"
00076 
00077 /***********************************************************************
00078  * Only for threaded name resolves builds
00079  **********************************************************************/
00080 #ifdef CURLRES_THREADED
00081 
00082 /*
00083  * Curl_resolver_global_init()
00084  * Called from curl_global_init() to initialize global resolver environment.
00085  * Does nothing here.
00086  */
00087 int Curl_resolver_global_init(void)
00088 {
00089   return CURLE_OK;
00090 }
00091 
00092 /*
00093  * Curl_resolver_global_cleanup()
00094  * Called from curl_global_cleanup() to destroy global resolver environment.
00095  * Does nothing here.
00096  */
00097 void Curl_resolver_global_cleanup(void)
00098 {
00099 }
00100 
00101 /*
00102  * Curl_resolver_init()
00103  * Called from curl_easy_init() -> Curl_open() to initialize resolver
00104  * URL-state specific environment ('resolver' member of the UrlState
00105  * structure).  Does nothing here.
00106  */
00107 CURLcode Curl_resolver_init(void **resolver)
00108 {
00109   (void)resolver;
00110   return CURLE_OK;
00111 }
00112 
00113 /*
00114  * Curl_resolver_cleanup()
00115  * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
00116  * URL-state specific environment ('resolver' member of the UrlState
00117  * structure).  Does nothing here.
00118  */
00119 void Curl_resolver_cleanup(void *resolver)
00120 {
00121   (void)resolver;
00122 }
00123 
00124 /*
00125  * Curl_resolver_duphandle()
00126  * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
00127  * environment ('resolver' member of the UrlState structure).  Does nothing
00128  * here.
00129  */
00130 int Curl_resolver_duphandle(void **to, void *from)
00131 {
00132   (void)to;
00133   (void)from;
00134   return CURLE_OK;
00135 }
00136 
00137 static void destroy_async_data(struct Curl_async *);
00138 
00139 /*
00140  * Cancel all possibly still on-going resolves for this connection.
00141  */
00142 void Curl_resolver_cancel(struct connectdata *conn)
00143 {
00144   destroy_async_data(&conn->async);
00145 }
00146 
00147 /* This function is used to init a threaded resolve */
00148 static bool init_resolve_thread(struct connectdata *conn,
00149                                 const char *hostname, int port,
00150                                 const struct addrinfo *hints);
00151 
00152 
00153 /* Data for synchronization between resolver thread and its parent */
00154 struct thread_sync_data {
00155   curl_mutex_t * mtx;
00156   int done;
00157 
00158   char *hostname;        /* hostname to resolve, Curl_async.hostname
00159                             duplicate */
00160   int port;
00161   int sock_error;
00162   Curl_addrinfo *res;
00163 #ifdef HAVE_GETADDRINFO
00164   struct addrinfo hints;
00165 #endif
00166   struct thread_data *td; /* for thread-self cleanup */
00167 };
00168 
00169 struct thread_data {
00170   curl_thread_t thread_hnd;
00171   unsigned int poll_interval;
00172   time_t interval_end;
00173   struct thread_sync_data tsd;
00174 };
00175 
00176 static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn)
00177 {
00178   return &(((struct thread_data *)conn->async.os_specific)->tsd);
00179 }
00180 
00181 #define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd);
00182 
00183 /* Destroy resolver thread synchronization data */
00184 static
00185 void destroy_thread_sync_data(struct thread_sync_data * tsd)
00186 {
00187   if(tsd->mtx) {
00188     Curl_mutex_destroy(tsd->mtx);
00189     free(tsd->mtx);
00190   }
00191 
00192   free(tsd->hostname);
00193 
00194   if(tsd->res)
00195     Curl_freeaddrinfo(tsd->res);
00196 
00197   memset(tsd, 0, sizeof(*tsd));
00198 }
00199 
00200 /* Initialize resolver thread synchronization data */
00201 static
00202 int init_thread_sync_data(struct thread_data * td,
00203                            const char *hostname,
00204                            int port,
00205                            const struct addrinfo *hints)
00206 {
00207   struct thread_sync_data *tsd = &td->tsd;
00208 
00209   memset(tsd, 0, sizeof(*tsd));
00210 
00211   tsd->td = td;
00212   tsd->port = port;
00213 #ifdef HAVE_GETADDRINFO
00214   DEBUGASSERT(hints);
00215   tsd->hints = *hints;
00216 #else
00217   (void) hints;
00218 #endif
00219 
00220   tsd->mtx = malloc(sizeof(curl_mutex_t));
00221   if(tsd->mtx == NULL)
00222     goto err_exit;
00223 
00224   Curl_mutex_init(tsd->mtx);
00225 
00226   tsd->sock_error = CURL_ASYNC_SUCCESS;
00227 
00228   /* Copying hostname string because original can be destroyed by parent
00229    * thread during gethostbyname execution.
00230    */
00231   tsd->hostname = strdup(hostname);
00232   if(!tsd->hostname)
00233     goto err_exit;
00234 
00235   return 1;
00236 
00237  err_exit:
00238   /* Memory allocation failed */
00239   destroy_thread_sync_data(tsd);
00240   return 0;
00241 }
00242 
00243 static int getaddrinfo_complete(struct connectdata *conn)
00244 {
00245   struct thread_sync_data *tsd = conn_thread_sync_data(conn);
00246   int rc;
00247 
00248   rc = Curl_addrinfo_callback(conn, tsd->sock_error, tsd->res);
00249   /* The tsd->res structure has been copied to async.dns and perhaps the DNS
00250      cache.  Set our copy to NULL so destroy_thread_sync_data doesn't free it.
00251   */
00252   tsd->res = NULL;
00253 
00254   return rc;
00255 }
00256 
00257 
00258 #ifdef HAVE_GETADDRINFO
00259 
00260 /*
00261  * getaddrinfo_thread() resolves a name and then exits.
00262  *
00263  * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
00264  * and wait on it.
00265  */
00266 static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
00267 {
00268   struct thread_sync_data *tsd = (struct thread_sync_data*)arg;
00269   struct thread_data *td = tsd->td;
00270   char service[12];
00271   int rc;
00272 
00273   snprintf(service, sizeof(service), "%d", tsd->port);
00274 
00275   rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
00276 
00277   if(rc != 0) {
00278     tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
00279     if(tsd->sock_error == 0)
00280       tsd->sock_error = RESOLVER_ENOMEM;
00281   }
00282   else {
00283     Curl_addrinfo_set_port(tsd->res, tsd->port);
00284   }
00285 
00286   Curl_mutex_acquire(tsd->mtx);
00287   if(tsd->done) {
00288     /* too late, gotta clean up the mess */
00289     Curl_mutex_release(tsd->mtx);
00290     destroy_thread_sync_data(tsd);
00291     free(td);
00292   }
00293   else {
00294     tsd->done = 1;
00295     Curl_mutex_release(tsd->mtx);
00296   }
00297 
00298   return 0;
00299 }
00300 
00301 #else /* HAVE_GETADDRINFO */
00302 
00303 /*
00304  * gethostbyname_thread() resolves a name and then exits.
00305  */
00306 static unsigned int CURL_STDCALL gethostbyname_thread(void *arg)
00307 {
00308   struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
00309   struct thread_data *td = tsd->td;
00310 
00311   tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
00312 
00313   if(!tsd->res) {
00314     tsd->sock_error = SOCKERRNO;
00315     if(tsd->sock_error == 0)
00316       tsd->sock_error = RESOLVER_ENOMEM;
00317   }
00318 
00319   Curl_mutex_acquire(tsd->mtx);
00320   if(tsd->done) {
00321     /* too late, gotta clean up the mess */
00322     Curl_mutex_release(tsd->mtx);
00323     destroy_thread_sync_data(tsd);
00324     free(td);
00325   }
00326   else {
00327     tsd->done = 1;
00328     Curl_mutex_release(tsd->mtx);
00329   }
00330 
00331   return 0;
00332 }
00333 
00334 #endif /* HAVE_GETADDRINFO */
00335 
00336 /*
00337  * destroy_async_data() cleans up async resolver data and thread handle.
00338  */
00339 static void destroy_async_data(struct Curl_async *async)
00340 {
00341   if(async->os_specific) {
00342     struct thread_data *td = (struct thread_data*) async->os_specific;
00343     int done;
00344 
00345     /*
00346      * if the thread is still blocking in the resolve syscall, detach it and
00347      * let the thread do the cleanup...
00348      */
00349     Curl_mutex_acquire(td->tsd.mtx);
00350     done = td->tsd.done;
00351     td->tsd.done = 1;
00352     Curl_mutex_release(td->tsd.mtx);
00353 
00354     if(!done) {
00355       Curl_thread_destroy(td->thread_hnd);
00356     }
00357     else {
00358       if(td->thread_hnd != curl_thread_t_null)
00359         Curl_thread_join(&td->thread_hnd);
00360 
00361       destroy_thread_sync_data(&td->tsd);
00362 
00363       free(async->os_specific);
00364     }
00365   }
00366   async->os_specific = NULL;
00367 
00368   free(async->hostname);
00369   async->hostname = NULL;
00370 }
00371 
00372 /*
00373  * init_resolve_thread() starts a new thread that performs the actual
00374  * resolve. This function returns before the resolve is done.
00375  *
00376  * Returns FALSE in case of failure, otherwise TRUE.
00377  */
00378 static bool init_resolve_thread(struct connectdata *conn,
00379                                 const char *hostname, int port,
00380                                 const struct addrinfo *hints)
00381 {
00382   struct thread_data *td = calloc(1, sizeof(struct thread_data));
00383   int err = RESOLVER_ENOMEM;
00384 
00385   conn->async.os_specific = (void *)td;
00386   if(!td)
00387     goto err_exit;
00388 
00389   conn->async.port = port;
00390   conn->async.done = FALSE;
00391   conn->async.status = 0;
00392   conn->async.dns = NULL;
00393   td->thread_hnd = curl_thread_t_null;
00394 
00395   if(!init_thread_sync_data(td, hostname, port, hints))
00396     goto err_exit;
00397 
00398   free(conn->async.hostname);
00399   conn->async.hostname = strdup(hostname);
00400   if(!conn->async.hostname)
00401     goto err_exit;
00402 
00403 #ifdef HAVE_GETADDRINFO
00404   td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
00405 #else
00406   td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
00407 #endif
00408 
00409   if(!td->thread_hnd) {
00410 #ifndef _WIN32_WCE
00411     err = errno;
00412 #endif
00413     goto err_exit;
00414   }
00415 
00416   return TRUE;
00417 
00418  err_exit:
00419   destroy_async_data(&conn->async);
00420 
00421   SET_ERRNO(err);
00422 
00423   return FALSE;
00424 }
00425 
00426 /*
00427  * resolver_error() calls failf() with the appropriate message after a resolve
00428  * error
00429  */
00430 
00431 static CURLcode resolver_error(struct connectdata *conn)
00432 {
00433   const char *host_or_proxy;
00434   CURLcode result;
00435 
00436   if(conn->bits.httpproxy) {
00437     host_or_proxy = "proxy";
00438     result = CURLE_COULDNT_RESOLVE_PROXY;
00439   }
00440   else {
00441     host_or_proxy = "host";
00442     result = CURLE_COULDNT_RESOLVE_HOST;
00443   }
00444 
00445   failf(conn->data, "Could not resolve %s: %s", host_or_proxy,
00446         conn->async.hostname);
00447 
00448   return result;
00449 }
00450 
00451 /*
00452  * Curl_resolver_wait_resolv()
00453  *
00454  * waits for a resolve to finish. This function should be avoided since using
00455  * this risk getting the multi interface to "hang".
00456  *
00457  * If 'entry' is non-NULL, make it point to the resolved dns entry
00458  *
00459  * This is the version for resolves-in-a-thread.
00460  */
00461 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
00462                                    struct Curl_dns_entry **entry)
00463 {
00464   struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
00465   CURLcode result = CURLE_OK;
00466 
00467   DEBUGASSERT(conn && td);
00468 
00469   /* wait for the thread to resolve the name */
00470   if(Curl_thread_join(&td->thread_hnd))
00471     result = getaddrinfo_complete(conn);
00472   else
00473     DEBUGASSERT(0);
00474 
00475   conn->async.done = TRUE;
00476 
00477   if(entry)
00478     *entry = conn->async.dns;
00479 
00480   if(!conn->async.dns)
00481     /* a name was not resolved, report error */
00482     result = resolver_error(conn);
00483 
00484   destroy_async_data(&conn->async);
00485 
00486   if(!conn->async.dns)
00487     connclose(conn, "asynch resolve failed");
00488 
00489   return result;
00490 }
00491 
00492 /*
00493  * Curl_resolver_is_resolved() is called repeatedly to check if a previous
00494  * name resolve request has completed. It should also make sure to time-out if
00495  * the operation seems to take too long.
00496  */
00497 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
00498                                    struct Curl_dns_entry **entry)
00499 {
00500   struct Curl_easy *data = conn->data;
00501   struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
00502   int done = 0;
00503 
00504   *entry = NULL;
00505 
00506   if(!td) {
00507     DEBUGASSERT(td);
00508     return CURLE_COULDNT_RESOLVE_HOST;
00509   }
00510 
00511   Curl_mutex_acquire(td->tsd.mtx);
00512   done = td->tsd.done;
00513   Curl_mutex_release(td->tsd.mtx);
00514 
00515   if(done) {
00516     getaddrinfo_complete(conn);
00517 
00518     if(!conn->async.dns) {
00519       CURLcode result = resolver_error(conn);
00520       destroy_async_data(&conn->async);
00521       return result;
00522     }
00523     destroy_async_data(&conn->async);
00524     *entry = conn->async.dns;
00525   }
00526   else {
00527     /* poll for name lookup done with exponential backoff up to 250ms */
00528     time_t elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
00529     if(elapsed < 0)
00530       elapsed = 0;
00531 
00532     if(td->poll_interval == 0)
00533       /* Start at 1ms poll interval */
00534       td->poll_interval = 1;
00535     else if(elapsed >= td->interval_end)
00536       /* Back-off exponentially if last interval expired  */
00537       td->poll_interval *= 2;
00538 
00539     if(td->poll_interval > 250)
00540       td->poll_interval = 250;
00541 
00542     td->interval_end = elapsed + td->poll_interval;
00543     Curl_expire(conn->data, td->poll_interval);
00544   }
00545 
00546   return CURLE_OK;
00547 }
00548 
00549 int Curl_resolver_getsock(struct connectdata *conn,
00550                           curl_socket_t *socks,
00551                           int numsocks)
00552 {
00553   (void)conn;
00554   (void)socks;
00555   (void)numsocks;
00556   return 0;
00557 }
00558 
00559 #ifndef HAVE_GETADDRINFO
00560 /*
00561  * Curl_getaddrinfo() - for platforms without getaddrinfo
00562  */
00563 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
00564                                          const char *hostname,
00565                                          int port,
00566                                          int *waitp)
00567 {
00568   struct in_addr in;
00569 
00570   *waitp = 0; /* default to synchronous response */
00571 
00572   if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
00573     /* This is a dotted IP address 123.123.123.123-style */
00574     return Curl_ip2addr(AF_INET, &in, hostname, port);
00575 
00576   /* fire up a new resolver thread! */
00577   if(init_resolve_thread(conn, hostname, port, NULL)) {
00578     *waitp = 1; /* expect asynchronous response */
00579     return NULL;
00580   }
00581 
00582   /* fall-back to blocking version */
00583   return Curl_ipv4_resolve_r(hostname, port);
00584 }
00585 
00586 #else /* !HAVE_GETADDRINFO */
00587 
00588 /*
00589  * Curl_resolver_getaddrinfo() - for getaddrinfo
00590  */
00591 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
00592                                          const char *hostname,
00593                                          int port,
00594                                          int *waitp)
00595 {
00596   struct addrinfo hints;
00597   struct in_addr in;
00598   Curl_addrinfo *res;
00599   int error;
00600   char sbuf[12];
00601   int pf = PF_INET;
00602 #ifdef CURLRES_IPV6
00603   struct in6_addr in6;
00604 #endif /* CURLRES_IPV6 */
00605 
00606   *waitp = 0; /* default to synchronous response */
00607 
00608 #ifndef USE_RESOLVE_ON_IPS
00609   /* First check if this is an IPv4 address string */
00610   if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
00611     /* This is a dotted IP address 123.123.123.123-style */
00612     return Curl_ip2addr(AF_INET, &in, hostname, port);
00613 
00614 #ifdef CURLRES_IPV6
00615   /* check if this is an IPv6 address string */
00616   if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
00617     /* This is an IPv6 address literal */
00618     return Curl_ip2addr(AF_INET6, &in6, hostname, port);
00619 #endif /* CURLRES_IPV6 */
00620 #endif /* !USE_RESOLVE_ON_IPS */
00621 
00622 #ifdef CURLRES_IPV6
00623   /*
00624    * Check if a limited name resolve has been requested.
00625    */
00626   switch(conn->ip_version) {
00627   case CURL_IPRESOLVE_V4:
00628     pf = PF_INET;
00629     break;
00630   case CURL_IPRESOLVE_V6:
00631     pf = PF_INET6;
00632     break;
00633   default:
00634     pf = PF_UNSPEC;
00635     break;
00636   }
00637 
00638   if((pf != PF_INET) && !Curl_ipv6works())
00639     /* The stack seems to be a non-IPv6 one */
00640     pf = PF_INET;
00641 #endif /* CURLRES_IPV6 */
00642 
00643   memset(&hints, 0, sizeof(hints));
00644   hints.ai_family = pf;
00645   hints.ai_socktype = conn->socktype;
00646 
00647   snprintf(sbuf, sizeof(sbuf), "%d", port);
00648 
00649   /* fire up a new resolver thread! */
00650   if(init_resolve_thread(conn, hostname, port, &hints)) {
00651     *waitp = 1; /* expect asynchronous response */
00652     return NULL;
00653   }
00654 
00655   /* fall-back to blocking version */
00656   infof(conn->data, "init_resolve_thread() failed for %s; %s\n",
00657         hostname, Curl_strerror(conn, ERRNO));
00658 
00659   error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
00660   if(error) {
00661     infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n",
00662           hostname, port, Curl_strerror(conn, SOCKERRNO));
00663     return NULL;
00664   }
00665   else {
00666     Curl_addrinfo_set_port(res, port);
00667   }
00668 
00669   return res;
00670 }
00671 
00672 #endif /* !HAVE_GETADDRINFO */
00673 
00674 CURLcode Curl_set_dns_servers(struct Curl_easy *data,
00675                               char *servers)
00676 {
00677   (void)data;
00678   (void)servers;
00679   return CURLE_NOT_BUILT_IN;
00680 
00681 }
00682 
00683 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
00684                                 const char *interf)
00685 {
00686   (void)data;
00687   (void)interf;
00688   return CURLE_NOT_BUILT_IN;
00689 }
00690 
00691 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
00692                                 const char *local_ip4)
00693 {
00694   (void)data;
00695   (void)local_ip4;
00696   return CURLE_NOT_BUILT_IN;
00697 }
00698 
00699 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
00700                                 const char *local_ip6)
00701 {
00702   (void)data;
00703   (void)local_ip6;
00704   return CURLE_NOT_BUILT_IN;
00705 }
00706 
00707 #endif /* CURLRES_THREADED */


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