Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel (at) 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 /*
     83  * Curl_resolver_global_init()
     84  * Called from curl_global_init() to initialize global resolver environment.
     85  * Does nothing here.
     86  */
     87 int Curl_resolver_global_init(void)
     88 {
     89   return CURLE_OK;
     90 }
     91 
     92 /*
     93  * Curl_resolver_global_cleanup()
     94  * Called from curl_global_cleanup() to destroy global resolver environment.
     95  * Does nothing here.
     96  */
     97 void Curl_resolver_global_cleanup(void)
     98 {
     99 }
    100 
    101 /*
    102  * Curl_resolver_init()
    103  * Called from curl_easy_init() -> Curl_open() to initialize resolver
    104  * URL-state specific environment ('resolver' member of the UrlState
    105  * structure).  Does nothing here.
    106  */
    107 CURLcode Curl_resolver_init(void **resolver)
    108 {
    109   (void)resolver;
    110   return CURLE_OK;
    111 }
    112 
    113 /*
    114  * Curl_resolver_cleanup()
    115  * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
    116  * URL-state specific environment ('resolver' member of the UrlState
    117  * structure).  Does nothing here.
    118  */
    119 void Curl_resolver_cleanup(void *resolver)
    120 {
    121   (void)resolver;
    122 }
    123 
    124 /*
    125  * Curl_resolver_duphandle()
    126  * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
    127  * environment ('resolver' member of the UrlState structure).  Does nothing
    128  * here.
    129  */
    130 int Curl_resolver_duphandle(void **to, void *from)
    131 {
    132   (void)to;
    133   (void)from;
    134   return CURLE_OK;
    135 }
    136 
    137 static void destroy_async_data(struct Curl_async *);
    138 
    139 /*
    140  * Cancel all possibly still on-going resolves for this connection.
    141  */
    142 void Curl_resolver_cancel(struct connectdata *conn)
    143 {
    144   destroy_async_data(&conn->async);
    145 }
    146 
    147 /* This function is used to init a threaded resolve */
    148 static bool init_resolve_thread(struct connectdata *conn,
    149                                 const char *hostname, int port,
    150                                 const struct addrinfo *hints);
    151 
    152 
    153 /* Data for synchronization between resolver thread and its parent */
    154 struct thread_sync_data {
    155   curl_mutex_t * mtx;
    156   int done;
    157 
    158   char *hostname;        /* hostname to resolve, Curl_async.hostname
    159                             duplicate */
    160   int port;
    161   int sock_error;
    162   Curl_addrinfo *res;
    163 #ifdef HAVE_GETADDRINFO
    164   struct addrinfo hints;
    165 #endif
    166   struct thread_data *td; /* for thread-self cleanup */
    167 };
    168 
    169 struct thread_data {
    170   curl_thread_t thread_hnd;
    171   unsigned int poll_interval;
    172   time_t interval_end;
    173   struct thread_sync_data tsd;
    174 };
    175 
    176 static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn)
    177 {
    178   return &(((struct thread_data *)conn->async.os_specific)->tsd);
    179 }
    180 
    181 #define CONN_THREAD_SYNC_DATA(conn) &(((conn)->async.os_specific)->tsd);
    182 
    183 /* Destroy resolver thread synchronization data */
    184 static
    185 void destroy_thread_sync_data(struct thread_sync_data * tsd)
    186 {
    187   if(tsd->mtx) {
    188     Curl_mutex_destroy(tsd->mtx);
    189     free(tsd->mtx);
    190   }
    191 
    192   free(tsd->hostname);
    193 
    194   if(tsd->res)
    195     Curl_freeaddrinfo(tsd->res);
    196 
    197   memset(tsd, 0, sizeof(*tsd));
    198 }
    199 
    200 /* Initialize resolver thread synchronization data */
    201 static
    202 int init_thread_sync_data(struct thread_data * td,
    203                            const char *hostname,
    204                            int port,
    205                            const struct addrinfo *hints)
    206 {
    207   struct thread_sync_data *tsd = &td->tsd;
    208 
    209   memset(tsd, 0, sizeof(*tsd));
    210 
    211   tsd->td = td;
    212   tsd->port = port;
    213   /* Treat the request as done until the thread actually starts so any early
    214    * cleanup gets done properly.
    215    */
    216   tsd->done = 1;
    217 #ifdef HAVE_GETADDRINFO
    218   DEBUGASSERT(hints);
    219   tsd->hints = *hints;
    220 #else
    221   (void) hints;
    222 #endif
    223 
    224   tsd->mtx = malloc(sizeof(curl_mutex_t));
    225   if(tsd->mtx == NULL)
    226     goto err_exit;
    227 
    228   Curl_mutex_init(tsd->mtx);
    229 
    230   tsd->sock_error = CURL_ASYNC_SUCCESS;
    231 
    232   /* Copying hostname string because original can be destroyed by parent
    233    * thread during gethostbyname execution.
    234    */
    235   tsd->hostname = strdup(hostname);
    236   if(!tsd->hostname)
    237     goto err_exit;
    238 
    239   return 1;
    240 
    241  err_exit:
    242   /* Memory allocation failed */
    243   destroy_thread_sync_data(tsd);
    244   return 0;
    245 }
    246 
    247 static int getaddrinfo_complete(struct connectdata *conn)
    248 {
    249   struct thread_sync_data *tsd = conn_thread_sync_data(conn);
    250   int rc;
    251 
    252   rc = Curl_addrinfo_callback(conn, tsd->sock_error, tsd->res);
    253   /* The tsd->res structure has been copied to async.dns and perhaps the DNS
    254      cache.  Set our copy to NULL so destroy_thread_sync_data doesn't free it.
    255   */
    256   tsd->res = NULL;
    257 
    258   return rc;
    259 }
    260 
    261 
    262 #ifdef HAVE_GETADDRINFO
    263 
    264 /*
    265  * getaddrinfo_thread() resolves a name and then exits.
    266  *
    267  * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
    268  * and wait on it.
    269  */
    270 static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
    271 {
    272   struct thread_sync_data *tsd = (struct thread_sync_data*)arg;
    273   struct thread_data *td = tsd->td;
    274   char service[12];
    275   int rc;
    276 
    277   snprintf(service, sizeof(service), "%d", tsd->port);
    278 
    279   rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
    280 
    281   if(rc != 0) {
    282     tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
    283     if(tsd->sock_error == 0)
    284       tsd->sock_error = RESOLVER_ENOMEM;
    285   }
    286   else {
    287     Curl_addrinfo_set_port(tsd->res, tsd->port);
    288   }
    289 
    290   Curl_mutex_acquire(tsd->mtx);
    291   if(tsd->done) {
    292     /* too late, gotta clean up the mess */
    293     Curl_mutex_release(tsd->mtx);
    294     destroy_thread_sync_data(tsd);
    295     free(td);
    296   }
    297   else {
    298     tsd->done = 1;
    299     Curl_mutex_release(tsd->mtx);
    300   }
    301 
    302   return 0;
    303 }
    304 
    305 #else /* HAVE_GETADDRINFO */
    306 
    307 /*
    308  * gethostbyname_thread() resolves a name and then exits.
    309  */
    310 static unsigned int CURL_STDCALL gethostbyname_thread(void *arg)
    311 {
    312   struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
    313   struct thread_data *td = tsd->td;
    314 
    315   tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
    316 
    317   if(!tsd->res) {
    318     tsd->sock_error = SOCKERRNO;
    319     if(tsd->sock_error == 0)
    320       tsd->sock_error = RESOLVER_ENOMEM;
    321   }
    322 
    323   Curl_mutex_acquire(tsd->mtx);
    324   if(tsd->done) {
    325     /* too late, gotta clean up the mess */
    326     Curl_mutex_release(tsd->mtx);
    327     destroy_thread_sync_data(tsd);
    328     free(td);
    329   }
    330   else {
    331     tsd->done = 1;
    332     Curl_mutex_release(tsd->mtx);
    333   }
    334 
    335   return 0;
    336 }
    337 
    338 #endif /* HAVE_GETADDRINFO */
    339 
    340 /*
    341  * destroy_async_data() cleans up async resolver data and thread handle.
    342  */
    343 static void destroy_async_data(struct Curl_async *async)
    344 {
    345   if(async->os_specific) {
    346     struct thread_data *td = (struct thread_data*) async->os_specific;
    347     int done;
    348 
    349     /*
    350      * if the thread is still blocking in the resolve syscall, detach it and
    351      * let the thread do the cleanup...
    352      */
    353     Curl_mutex_acquire(td->tsd.mtx);
    354     done = td->tsd.done;
    355     td->tsd.done = 1;
    356     Curl_mutex_release(td->tsd.mtx);
    357 
    358     if(!done) {
    359       Curl_thread_destroy(td->thread_hnd);
    360     }
    361     else {
    362       if(td->thread_hnd != curl_thread_t_null)
    363         Curl_thread_join(&td->thread_hnd);
    364 
    365       destroy_thread_sync_data(&td->tsd);
    366 
    367       free(async->os_specific);
    368     }
    369   }
    370   async->os_specific = NULL;
    371 
    372   free(async->hostname);
    373   async->hostname = NULL;
    374 }
    375 
    376 /*
    377  * init_resolve_thread() starts a new thread that performs the actual
    378  * resolve. This function returns before the resolve is done.
    379  *
    380  * Returns FALSE in case of failure, otherwise TRUE.
    381  */
    382 static bool init_resolve_thread(struct connectdata *conn,
    383                                 const char *hostname, int port,
    384                                 const struct addrinfo *hints)
    385 {
    386   struct thread_data *td = calloc(1, sizeof(struct thread_data));
    387   int err = ENOMEM;
    388 
    389   conn->async.os_specific = (void *)td;
    390   if(!td)
    391     goto errno_exit;
    392 
    393   conn->async.port = port;
    394   conn->async.done = FALSE;
    395   conn->async.status = 0;
    396   conn->async.dns = NULL;
    397   td->thread_hnd = curl_thread_t_null;
    398 
    399   if(!init_thread_sync_data(td, hostname, port, hints)) {
    400     conn->async.os_specific = NULL;
    401     free(td);
    402     goto errno_exit;
    403   }
    404 
    405   free(conn->async.hostname);
    406   conn->async.hostname = strdup(hostname);
    407   if(!conn->async.hostname)
    408     goto err_exit;
    409 
    410   /* The thread will set this to 1 when complete. */
    411   td->tsd.done = 0;
    412 
    413 #ifdef HAVE_GETADDRINFO
    414   td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
    415 #else
    416   td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
    417 #endif
    418 
    419   if(!td->thread_hnd) {
    420     /* The thread never started, so mark it as done here for proper cleanup. */
    421     td->tsd.done = 1;
    422     err = errno;
    423     goto err_exit;
    424   }
    425 
    426   return TRUE;
    427 
    428  err_exit:
    429   destroy_async_data(&conn->async);
    430 
    431  errno_exit:
    432   errno = err;
    433   return FALSE;
    434 }
    435 
    436 /*
    437  * resolver_error() calls failf() with the appropriate message after a resolve
    438  * error
    439  */
    440 
    441 static CURLcode resolver_error(struct connectdata *conn)
    442 {
    443   const char *host_or_proxy;
    444   CURLcode result;
    445 
    446   if(conn->bits.httpproxy) {
    447     host_or_proxy = "proxy";
    448     result = CURLE_COULDNT_RESOLVE_PROXY;
    449   }
    450   else {
    451     host_or_proxy = "host";
    452     result = CURLE_COULDNT_RESOLVE_HOST;
    453   }
    454 
    455   failf(conn->data, "Could not resolve %s: %s", host_or_proxy,
    456         conn->async.hostname);
    457 
    458   return result;
    459 }
    460 
    461 /*
    462  * Curl_resolver_wait_resolv()
    463  *
    464  * waits for a resolve to finish. This function should be avoided since using
    465  * this risk getting the multi interface to "hang".
    466  *
    467  * If 'entry' is non-NULL, make it point to the resolved dns entry
    468  *
    469  * This is the version for resolves-in-a-thread.
    470  */
    471 CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
    472                                    struct Curl_dns_entry **entry)
    473 {
    474   struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
    475   CURLcode result = CURLE_OK;
    476 
    477   DEBUGASSERT(conn && td);
    478 
    479   /* wait for the thread to resolve the name */
    480   if(Curl_thread_join(&td->thread_hnd))
    481     result = getaddrinfo_complete(conn);
    482   else
    483     DEBUGASSERT(0);
    484 
    485   conn->async.done = TRUE;
    486 
    487   if(entry)
    488     *entry = conn->async.dns;
    489 
    490   if(!conn->async.dns)
    491     /* a name was not resolved, report error */
    492     result = resolver_error(conn);
    493 
    494   destroy_async_data(&conn->async);
    495 
    496   if(!conn->async.dns)
    497     connclose(conn, "asynch resolve failed");
    498 
    499   return result;
    500 }
    501 
    502 /*
    503  * Curl_resolver_is_resolved() is called repeatedly to check if a previous
    504  * name resolve request has completed. It should also make sure to time-out if
    505  * the operation seems to take too long.
    506  */
    507 CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
    508                                    struct Curl_dns_entry **entry)
    509 {
    510   struct Curl_easy *data = conn->data;
    511   struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
    512   int done = 0;
    513 
    514   *entry = NULL;
    515 
    516   if(!td) {
    517     DEBUGASSERT(td);
    518     return CURLE_COULDNT_RESOLVE_HOST;
    519   }
    520 
    521   Curl_mutex_acquire(td->tsd.mtx);
    522   done = td->tsd.done;
    523   Curl_mutex_release(td->tsd.mtx);
    524 
    525   if(done) {
    526     getaddrinfo_complete(conn);
    527 
    528     if(!conn->async.dns) {
    529       CURLcode result = resolver_error(conn);
    530       destroy_async_data(&conn->async);
    531       return result;
    532     }
    533     destroy_async_data(&conn->async);
    534     *entry = conn->async.dns;
    535   }
    536   else {
    537     /* poll for name lookup done with exponential backoff up to 250ms */
    538     timediff_t elapsed = Curl_timediff(Curl_now(),
    539                                        data->progress.t_startsingle);
    540     if(elapsed < 0)
    541       elapsed = 0;
    542 
    543     if(td->poll_interval == 0)
    544       /* Start at 1ms poll interval */
    545       td->poll_interval = 1;
    546     else if(elapsed >= td->interval_end)
    547       /* Back-off exponentially if last interval expired  */
    548       td->poll_interval *= 2;
    549 
    550     if(td->poll_interval > 250)
    551       td->poll_interval = 250;
    552 
    553     td->interval_end = elapsed + td->poll_interval;
    554     Curl_expire(conn->data, td->poll_interval, EXPIRE_ASYNC_NAME);
    555   }
    556 
    557   return CURLE_OK;
    558 }
    559 
    560 int Curl_resolver_getsock(struct connectdata *conn,
    561                           curl_socket_t *socks,
    562                           int numsocks)
    563 {
    564   (void)conn;
    565   (void)socks;
    566   (void)numsocks;
    567   return 0;
    568 }
    569 
    570 #ifndef HAVE_GETADDRINFO
    571 /*
    572  * Curl_getaddrinfo() - for platforms without getaddrinfo
    573  */
    574 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
    575                                          const char *hostname,
    576                                          int port,
    577                                          int *waitp)
    578 {
    579   struct in_addr in;
    580 
    581   *waitp = 0; /* default to synchronous response */
    582 
    583   if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
    584     /* This is a dotted IP address 123.123.123.123-style */
    585     return Curl_ip2addr(AF_INET, &in, hostname, port);
    586 
    587   /* fire up a new resolver thread! */
    588   if(init_resolve_thread(conn, hostname, port, NULL)) {
    589     *waitp = 1; /* expect asynchronous response */
    590     return NULL;
    591   }
    592 
    593   /* fall-back to blocking version */
    594   return Curl_ipv4_resolve_r(hostname, port);
    595 }
    596 
    597 #else /* !HAVE_GETADDRINFO */
    598 
    599 /*
    600  * Curl_resolver_getaddrinfo() - for getaddrinfo
    601  */
    602 Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
    603                                          const char *hostname,
    604                                          int port,
    605                                          int *waitp)
    606 {
    607   struct addrinfo hints;
    608   Curl_addrinfo *res;
    609   int error;
    610   char sbuf[12];
    611   int pf = PF_INET;
    612 
    613   *waitp = 0; /* default to synchronous response */
    614 
    615 #ifndef USE_RESOLVE_ON_IPS
    616   {
    617     struct in_addr in;
    618     /* First check if this is an IPv4 address string */
    619     if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
    620       /* This is a dotted IP address 123.123.123.123-style */
    621       return Curl_ip2addr(AF_INET, &in, hostname, port);
    622   }
    623 #ifdef CURLRES_IPV6
    624   {
    625     struct in6_addr in6;
    626     /* check if this is an IPv6 address string */
    627     if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
    628       /* This is an IPv6 address literal */
    629       return Curl_ip2addr(AF_INET6, &in6, hostname, port);
    630   }
    631 #endif /* CURLRES_IPV6 */
    632 #endif /* !USE_RESOLVE_ON_IPS */
    633 
    634 #ifdef CURLRES_IPV6
    635   /*
    636    * Check if a limited name resolve has been requested.
    637    */
    638   switch(conn->ip_version) {
    639   case CURL_IPRESOLVE_V4:
    640     pf = PF_INET;
    641     break;
    642   case CURL_IPRESOLVE_V6:
    643     pf = PF_INET6;
    644     break;
    645   default:
    646     pf = PF_UNSPEC;
    647     break;
    648   }
    649 
    650   if((pf != PF_INET) && !Curl_ipv6works())
    651     /* The stack seems to be a non-IPv6 one */
    652     pf = PF_INET;
    653 #endif /* CURLRES_IPV6 */
    654 
    655   memset(&hints, 0, sizeof(hints));
    656   hints.ai_family = pf;
    657   hints.ai_socktype = conn->socktype;
    658 
    659   snprintf(sbuf, sizeof(sbuf), "%d", port);
    660 
    661   /* fire up a new resolver thread! */
    662   if(init_resolve_thread(conn, hostname, port, &hints)) {
    663     *waitp = 1; /* expect asynchronous response */
    664     return NULL;
    665   }
    666 
    667   /* fall-back to blocking version */
    668   infof(conn->data, "init_resolve_thread() failed for %s; %s\n",
    669         hostname, Curl_strerror(conn, errno));
    670 
    671   error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
    672   if(error) {
    673     infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n",
    674           hostname, port, Curl_strerror(conn, SOCKERRNO));
    675     return NULL;
    676   }
    677   else {
    678     Curl_addrinfo_set_port(res, port);
    679   }
    680 
    681   return res;
    682 }
    683 
    684 #endif /* !HAVE_GETADDRINFO */
    685 
    686 CURLcode Curl_set_dns_servers(struct Curl_easy *data,
    687                               char *servers)
    688 {
    689   (void)data;
    690   (void)servers;
    691   return CURLE_NOT_BUILT_IN;
    692 
    693 }
    694 
    695 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
    696                                 const char *interf)
    697 {
    698   (void)data;
    699   (void)interf;
    700   return CURLE_NOT_BUILT_IN;
    701 }
    702 
    703 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
    704                                 const char *local_ip4)
    705 {
    706   (void)data;
    707   (void)local_ip4;
    708   return CURLE_NOT_BUILT_IN;
    709 }
    710 
    711 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
    712                                 const char *local_ip6)
    713 {
    714   (void)data;
    715   (void)local_ip6;
    716   return CURLE_NOT_BUILT_IN;
    717 }
    718 
    719 #endif /* CURLRES_THREADED */
    720