hostip.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 #ifdef HAVE_SETJMP_H
00040 #include <setjmp.h>
00041 #endif
00042 #ifdef HAVE_SIGNAL_H
00043 #include <signal.h>
00044 #endif
00045 
00046 #ifdef HAVE_PROCESS_H
00047 #include <process.h>
00048 #endif
00049 
00050 #include "urldata.h"
00051 #include "sendf.h"
00052 #include "hostip.h"
00053 #include "hash.h"
00054 #include "share.h"
00055 #include "strerror.h"
00056 #include "url.h"
00057 #include "inet_ntop.h"
00058 #include "warnless.h"
00059 /* The last 3 #include files should be in this order */
00060 #include "curl_printf.h"
00061 #include "curl_memory.h"
00062 #include "memdebug.h"
00063 
00064 #if defined(CURLRES_SYNCH) && \
00065     defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
00066 /* alarm-based timeouts can only be used with all the dependencies satisfied */
00067 #define USE_ALARM_TIMEOUT
00068 #endif
00069 
00070 /*
00071  * hostip.c explained
00072  * ==================
00073  *
00074  * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
00075  * source file are these:
00076  *
00077  * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
00078  * that. The host may not be able to resolve IPv6, but we don't really have to
00079  * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
00080  * defined.
00081  *
00082  * CURLRES_ARES - is defined if libcurl is built to use c-ares for
00083  * asynchronous name resolves. This can be Windows or *nix.
00084  *
00085  * CURLRES_THREADED - is defined if libcurl is built to run under (native)
00086  * Windows, and then the name resolve will be done in a new thread, and the
00087  * supported API will be the same as for ares-builds.
00088  *
00089  * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
00090  * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
00091  * defined.
00092  *
00093  * The host*.c sources files are split up like this:
00094  *
00095  * hostip.c   - method-independent resolver functions and utility functions
00096  * hostasyn.c - functions for asynchronous name resolves
00097  * hostsyn.c  - functions for synchronous name resolves
00098  * hostip4.c  - IPv4 specific functions
00099  * hostip6.c  - IPv6 specific functions
00100  *
00101  * The two asynchronous name resolver backends are implemented in:
00102  * asyn-ares.c   - functions for ares-using name resolves
00103  * asyn-thread.c - functions for threaded name resolves
00104 
00105  * The hostip.h is the united header file for all this. It defines the
00106  * CURLRES_* defines based on the config*.h and curl_setup.h defines.
00107  */
00108 
00109 /* These two symbols are for the global DNS cache */
00110 static struct curl_hash hostname_cache;
00111 static int host_cache_initialized;
00112 
00113 static void freednsentry(void *freethis);
00114 
00115 /*
00116  * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
00117  * Global DNS cache is general badness. Do not use. This will be removed in
00118  * a future version. Use the share interface instead!
00119  *
00120  * Returns a struct curl_hash pointer on success, NULL on failure.
00121  */
00122 struct curl_hash *Curl_global_host_cache_init(void)
00123 {
00124   int rc = 0;
00125   if(!host_cache_initialized) {
00126     rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
00127                         Curl_str_key_compare, freednsentry);
00128     if(!rc)
00129       host_cache_initialized = 1;
00130   }
00131   return rc?NULL:&hostname_cache;
00132 }
00133 
00134 /*
00135  * Destroy and cleanup the global DNS cache
00136  */
00137 void Curl_global_host_cache_dtor(void)
00138 {
00139   if(host_cache_initialized) {
00140     Curl_hash_destroy(&hostname_cache);
00141     host_cache_initialized = 0;
00142   }
00143 }
00144 
00145 /*
00146  * Return # of adresses in a Curl_addrinfo struct
00147  */
00148 int Curl_num_addresses(const Curl_addrinfo *addr)
00149 {
00150   int i = 0;
00151   while(addr) {
00152     addr = addr->ai_next;
00153     i++;
00154   }
00155   return i;
00156 }
00157 
00158 /*
00159  * Curl_printable_address() returns a printable version of the 1st address
00160  * given in the 'ai' argument. The result will be stored in the buf that is
00161  * bufsize bytes big.
00162  *
00163  * If the conversion fails, it returns NULL.
00164  */
00165 const char *
00166 Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
00167 {
00168   const struct sockaddr_in *sa4;
00169   const struct in_addr *ipaddr4;
00170 #ifdef ENABLE_IPV6
00171   const struct sockaddr_in6 *sa6;
00172   const struct in6_addr *ipaddr6;
00173 #endif
00174 
00175   switch(ai->ai_family) {
00176     case AF_INET:
00177       sa4 = (const void *)ai->ai_addr;
00178       ipaddr4 = &sa4->sin_addr;
00179       return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf,
00180                             bufsize);
00181 #ifdef ENABLE_IPV6
00182     case AF_INET6:
00183       sa6 = (const void *)ai->ai_addr;
00184       ipaddr6 = &sa6->sin6_addr;
00185       return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf,
00186                             bufsize);
00187 #endif
00188     default:
00189       break;
00190   }
00191   return NULL;
00192 }
00193 
00194 /*
00195  * Return a hostcache id string for the provided host + port, to be used by
00196  * the DNS caching.
00197  */
00198 static char *
00199 create_hostcache_id(const char *name, int port)
00200 {
00201   /* create and return the new allocated entry */
00202   char *id = aprintf("%s:%d", name, port);
00203   char *ptr = id;
00204   if(ptr) {
00205     /* lower case the name part */
00206     while(*ptr && (*ptr != ':')) {
00207       *ptr = (char)TOLOWER(*ptr);
00208       ptr++;
00209     }
00210   }
00211   return id;
00212 }
00213 
00214 struct hostcache_prune_data {
00215   long cache_timeout;
00216   time_t now;
00217 };
00218 
00219 /*
00220  * This function is set as a callback to be called for every entry in the DNS
00221  * cache when we want to prune old unused entries.
00222  *
00223  * Returning non-zero means remove the entry, return 0 to keep it in the
00224  * cache.
00225  */
00226 static int
00227 hostcache_timestamp_remove(void *datap, void *hc)
00228 {
00229   struct hostcache_prune_data *data =
00230     (struct hostcache_prune_data *) datap;
00231   struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
00232 
00233   return (0 != c->timestamp)
00234     && (data->now - c->timestamp >= data->cache_timeout);
00235 }
00236 
00237 /*
00238  * Prune the DNS cache. This assumes that a lock has already been taken.
00239  */
00240 static void
00241 hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
00242 {
00243   struct hostcache_prune_data user;
00244 
00245   user.cache_timeout = cache_timeout;
00246   user.now = now;
00247 
00248   Curl_hash_clean_with_criterium(hostcache,
00249                                  (void *) &user,
00250                                  hostcache_timestamp_remove);
00251 }
00252 
00253 /*
00254  * Library-wide function for pruning the DNS cache. This function takes and
00255  * returns the appropriate locks.
00256  */
00257 void Curl_hostcache_prune(struct Curl_easy *data)
00258 {
00259   time_t now;
00260 
00261   if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
00262     /* cache forever means never prune, and NULL hostcache means
00263        we can't do it */
00264     return;
00265 
00266   if(data->share)
00267     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00268 
00269   time(&now);
00270 
00271   /* Remove outdated and unused entries from the hostcache */
00272   hostcache_prune(data->dns.hostcache,
00273                   data->set.dns_cache_timeout,
00274                   now);
00275 
00276   if(data->share)
00277     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00278 }
00279 
00280 #ifdef HAVE_SIGSETJMP
00281 /* Beware this is a global and unique instance. This is used to store the
00282    return address that we can jump back to from inside a signal handler. This
00283    is not thread-safe stuff. */
00284 sigjmp_buf curl_jmpenv;
00285 #endif
00286 
00287 /* lookup address, returns entry if found and not stale */
00288 static struct Curl_dns_entry *
00289 fetch_addr(struct connectdata *conn,
00290                 const char *hostname,
00291                 int port)
00292 {
00293   char *entry_id = NULL;
00294   struct Curl_dns_entry *dns = NULL;
00295   size_t entry_len;
00296   struct Curl_easy *data = conn->data;
00297 
00298   /* Create an entry id, based upon the hostname and port */
00299   entry_id = create_hostcache_id(hostname, port);
00300   /* If we can't create the entry id, fail */
00301   if(!entry_id)
00302     return dns;
00303 
00304   entry_len = strlen(entry_id);
00305 
00306   /* See if its already in our dns cache */
00307   dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
00308 
00309   if(dns && (data->set.dns_cache_timeout != -1))  {
00310     /* See whether the returned entry is stale. Done before we release lock */
00311     struct hostcache_prune_data user;
00312 
00313     time(&user.now);
00314     user.cache_timeout = data->set.dns_cache_timeout;
00315 
00316     if(hostcache_timestamp_remove(&user, dns)) {
00317       infof(data, "Hostname in DNS cache was stale, zapped\n");
00318       dns = NULL; /* the memory deallocation is being handled by the hash */
00319       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len+1);
00320     }
00321   }
00322 
00323   /* free the allocated entry_id again */
00324   free(entry_id);
00325 
00326   return dns;
00327 }
00328 
00329 /*
00330  * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
00331  *
00332  * Curl_resolv() checks initially and multi_runsingle() checks each time
00333  * it discovers the handle in the state WAITRESOLVE whether the hostname
00334  * has already been resolved and the address has already been stored in
00335  * the DNS cache. This short circuits waiting for a lot of pending
00336  * lookups for the same hostname requested by different handles.
00337  *
00338  * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
00339  *
00340  * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
00341  * use, or we'll leak memory!
00342  */
00343 struct Curl_dns_entry *
00344 Curl_fetch_addr(struct connectdata *conn,
00345                 const char *hostname,
00346                 int port)
00347 {
00348   struct Curl_easy *data = conn->data;
00349   struct Curl_dns_entry *dns = NULL;
00350 
00351   if(data->share)
00352     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00353 
00354   dns = fetch_addr(conn, hostname, port);
00355 
00356   if(dns)
00357     dns->inuse++; /* we use it! */
00358 
00359   if(data->share)
00360     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00361 
00362   return dns;
00363 }
00364 
00365 /*
00366  * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
00367  *
00368  * When calling Curl_resolv() has resulted in a response with a returned
00369  * address, we call this function to store the information in the dns
00370  * cache etc
00371  *
00372  * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
00373  */
00374 struct Curl_dns_entry *
00375 Curl_cache_addr(struct Curl_easy *data,
00376                 Curl_addrinfo *addr,
00377                 const char *hostname,
00378                 int port)
00379 {
00380   char *entry_id;
00381   size_t entry_len;
00382   struct Curl_dns_entry *dns;
00383   struct Curl_dns_entry *dns2;
00384 
00385   /* Create an entry id, based upon the hostname and port */
00386   entry_id = create_hostcache_id(hostname, port);
00387   /* If we can't create the entry id, fail */
00388   if(!entry_id)
00389     return NULL;
00390   entry_len = strlen(entry_id);
00391 
00392   /* Create a new cache entry */
00393   dns = calloc(1, sizeof(struct Curl_dns_entry));
00394   if(!dns) {
00395     free(entry_id);
00396     return NULL;
00397   }
00398 
00399   dns->inuse = 1;   /* the cache has the first reference */
00400   dns->addr = addr; /* this is the address(es) */
00401   time(&dns->timestamp);
00402   if(dns->timestamp == 0)
00403     dns->timestamp = 1;   /* zero indicates CURLOPT_RESOLVE entry */
00404 
00405   /* Store the resolved data in our DNS cache. */
00406   dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
00407                        (void *)dns);
00408   if(!dns2) {
00409     free(dns);
00410     free(entry_id);
00411     return NULL;
00412   }
00413 
00414   dns = dns2;
00415   dns->inuse++;         /* mark entry as in-use */
00416 
00417   /* free the allocated entry_id */
00418   free(entry_id);
00419 
00420   return dns;
00421 }
00422 
00423 /*
00424  * Curl_resolv() is the main name resolve function within libcurl. It resolves
00425  * a name and returns a pointer to the entry in the 'entry' argument (if one
00426  * is provided). This function might return immediately if we're using asynch
00427  * resolves. See the return codes.
00428  *
00429  * The cache entry we return will get its 'inuse' counter increased when this
00430  * function is used. You MUST call Curl_resolv_unlock() later (when you're
00431  * done using this struct) to decrease the counter again.
00432  *
00433  * In debug mode, we specifically test for an interface name "LocalHost"
00434  * and resolve "localhost" instead as a means to permit test cases
00435  * to connect to a local test server with any host name.
00436  *
00437  * Return codes:
00438  *
00439  * CURLRESOLV_ERROR   (-1) = error, no pointer
00440  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
00441  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
00442  */
00443 
00444 int Curl_resolv(struct connectdata *conn,
00445                 const char *hostname,
00446                 int port,
00447                 struct Curl_dns_entry **entry)
00448 {
00449   struct Curl_dns_entry *dns = NULL;
00450   struct Curl_easy *data = conn->data;
00451   CURLcode result;
00452   int rc = CURLRESOLV_ERROR; /* default to failure */
00453 
00454   *entry = NULL;
00455 
00456   if(data->share)
00457     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00458 
00459   dns = fetch_addr(conn, hostname, port);
00460 
00461   if(dns) {
00462     infof(data, "Hostname %s was found in DNS cache\n", hostname);
00463     dns->inuse++; /* we use it! */
00464     rc = CURLRESOLV_RESOLVED;
00465   }
00466 
00467   if(data->share)
00468     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00469 
00470   if(!dns) {
00471     /* The entry was not in the cache. Resolve it to IP address */
00472 
00473     Curl_addrinfo *addr;
00474     int respwait;
00475 
00476     /* Check what IP specifics the app has requested and if we can provide it.
00477      * If not, bail out. */
00478     if(!Curl_ipvalid(conn))
00479       return CURLRESOLV_ERROR;
00480 
00481     /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
00482        non-zero value indicating that we need to wait for the response to the
00483        resolve call */
00484     addr = Curl_getaddrinfo(conn,
00485 #ifdef DEBUGBUILD
00486                             (data->set.str[STRING_DEVICE]
00487                              && !strcmp(data->set.str[STRING_DEVICE],
00488                                         "LocalHost"))?"localhost":
00489 #endif
00490                             hostname, port, &respwait);
00491 
00492     if(!addr) {
00493       if(respwait) {
00494         /* the response to our resolve call will come asynchronously at
00495            a later time, good or bad */
00496         /* First, check that we haven't received the info by now */
00497         result = Curl_resolver_is_resolved(conn, &dns);
00498         if(result) /* error detected */
00499           return CURLRESOLV_ERROR;
00500         if(dns)
00501           rc = CURLRESOLV_RESOLVED; /* pointer provided */
00502         else
00503           rc = CURLRESOLV_PENDING; /* no info yet */
00504       }
00505     }
00506     else {
00507       if(data->share)
00508         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00509 
00510       /* we got a response, store it in the cache */
00511       dns = Curl_cache_addr(data, addr, hostname, port);
00512 
00513       if(data->share)
00514         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00515 
00516       if(!dns)
00517         /* returned failure, bail out nicely */
00518         Curl_freeaddrinfo(addr);
00519       else
00520         rc = CURLRESOLV_RESOLVED;
00521     }
00522   }
00523 
00524   *entry = dns;
00525 
00526   return rc;
00527 }
00528 
00529 #ifdef USE_ALARM_TIMEOUT
00530 /*
00531  * This signal handler jumps back into the main libcurl code and continues
00532  * execution.  This effectively causes the remainder of the application to run
00533  * within a signal handler which is nonportable and could lead to problems.
00534  */
00535 static
00536 RETSIGTYPE alarmfunc(int sig)
00537 {
00538   /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
00539   (void)sig;
00540   siglongjmp(curl_jmpenv, 1);
00541   return;
00542 }
00543 #endif /* USE_ALARM_TIMEOUT */
00544 
00545 /*
00546  * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
00547  * timeout.  This function might return immediately if we're using asynch
00548  * resolves. See the return codes.
00549  *
00550  * The cache entry we return will get its 'inuse' counter increased when this
00551  * function is used. You MUST call Curl_resolv_unlock() later (when you're
00552  * done using this struct) to decrease the counter again.
00553  *
00554  * If built with a synchronous resolver and use of signals is not
00555  * disabled by the application, then a nonzero timeout will cause a
00556  * timeout after the specified number of milliseconds. Otherwise, timeout
00557  * is ignored.
00558  *
00559  * Return codes:
00560  *
00561  * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
00562  * CURLRESOLV_ERROR   (-1) = error, no pointer
00563  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
00564  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
00565  */
00566 
00567 int Curl_resolv_timeout(struct connectdata *conn,
00568                         const char *hostname,
00569                         int port,
00570                         struct Curl_dns_entry **entry,
00571                         time_t timeoutms)
00572 {
00573 #ifdef USE_ALARM_TIMEOUT
00574 #ifdef HAVE_SIGACTION
00575   struct sigaction keep_sigact;   /* store the old struct here */
00576   volatile bool keep_copysig = FALSE; /* wether old sigact has been saved */
00577   struct sigaction sigact;
00578 #else
00579 #ifdef HAVE_SIGNAL
00580   void (*keep_sigact)(int);       /* store the old handler here */
00581 #endif /* HAVE_SIGNAL */
00582 #endif /* HAVE_SIGACTION */
00583   volatile long timeout;
00584   volatile unsigned int prev_alarm = 0;
00585   struct Curl_easy *data = conn->data;
00586 #endif /* USE_ALARM_TIMEOUT */
00587   int rc;
00588 
00589   *entry = NULL;
00590 
00591   if(timeoutms < 0)
00592     /* got an already expired timeout */
00593     return CURLRESOLV_TIMEDOUT;
00594 
00595 #ifdef USE_ALARM_TIMEOUT
00596   if(data->set.no_signal)
00597     /* Ignore the timeout when signals are disabled */
00598     timeout = 0;
00599   else
00600     timeout = timeoutms;
00601 
00602   if(!timeout)
00603     /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
00604     return Curl_resolv(conn, hostname, port, entry);
00605 
00606   if(timeout < 1000) {
00607     /* The alarm() function only provides integer second resolution, so if
00608        we want to wait less than one second we must bail out already now. */
00609     failf(data,
00610         "remaining timeout of %ld too small to resolve via SIGALRM method",
00611         timeout);
00612     return CURLRESOLV_TIMEDOUT;
00613   }
00614   /* This allows us to time-out from the name resolver, as the timeout
00615      will generate a signal and we will siglongjmp() from that here.
00616      This technique has problems (see alarmfunc).
00617      This should be the last thing we do before calling Curl_resolv(),
00618      as otherwise we'd have to worry about variables that get modified
00619      before we invoke Curl_resolv() (and thus use "volatile"). */
00620   if(sigsetjmp(curl_jmpenv, 1)) {
00621     /* this is coming from a siglongjmp() after an alarm signal */
00622     failf(data, "name lookup timed out");
00623     rc = CURLRESOLV_ERROR;
00624     goto clean_up;
00625   }
00626   else {
00627     /*************************************************************
00628      * Set signal handler to catch SIGALRM
00629      * Store the old value to be able to set it back later!
00630      *************************************************************/
00631 #ifdef HAVE_SIGACTION
00632     sigaction(SIGALRM, NULL, &sigact);
00633     keep_sigact = sigact;
00634     keep_copysig = TRUE; /* yes, we have a copy */
00635     sigact.sa_handler = alarmfunc;
00636 #ifdef SA_RESTART
00637     /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
00638     sigact.sa_flags &= ~SA_RESTART;
00639 #endif
00640     /* now set the new struct */
00641     sigaction(SIGALRM, &sigact, NULL);
00642 #else /* HAVE_SIGACTION */
00643     /* no sigaction(), revert to the much lamer signal() */
00644 #ifdef HAVE_SIGNAL
00645     keep_sigact = signal(SIGALRM, alarmfunc);
00646 #endif
00647 #endif /* HAVE_SIGACTION */
00648 
00649     /* alarm() makes a signal get sent when the timeout fires off, and that
00650        will abort system calls */
00651     prev_alarm = alarm(curlx_sltoui(timeout/1000L));
00652   }
00653 
00654 #else
00655 #ifndef CURLRES_ASYNCH
00656   if(timeoutms)
00657     infof(conn->data, "timeout on name lookup is not supported\n");
00658 #else
00659   (void)timeoutms; /* timeoutms not used with an async resolver */
00660 #endif
00661 #endif /* USE_ALARM_TIMEOUT */
00662 
00663   /* Perform the actual name resolution. This might be interrupted by an
00664    * alarm if it takes too long.
00665    */
00666   rc = Curl_resolv(conn, hostname, port, entry);
00667 
00668 #ifdef USE_ALARM_TIMEOUT
00669 clean_up:
00670 
00671   if(!prev_alarm)
00672     /* deactivate a possibly active alarm before uninstalling the handler */
00673     alarm(0);
00674 
00675 #ifdef HAVE_SIGACTION
00676   if(keep_copysig) {
00677     /* we got a struct as it looked before, now put that one back nice
00678        and clean */
00679     sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
00680   }
00681 #else
00682 #ifdef HAVE_SIGNAL
00683   /* restore the previous SIGALRM handler */
00684   signal(SIGALRM, keep_sigact);
00685 #endif
00686 #endif /* HAVE_SIGACTION */
00687 
00688   /* switch back the alarm() to either zero or to what it was before minus
00689      the time we spent until now! */
00690   if(prev_alarm) {
00691     /* there was an alarm() set before us, now put it back */
00692     unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
00693 
00694     /* the alarm period is counted in even number of seconds */
00695     unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
00696 
00697     if(!alarm_set ||
00698        ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
00699       /* if the alarm time-left reached zero or turned "negative" (counted
00700          with unsigned values), we should fire off a SIGALRM here, but we
00701          won't, and zero would be to switch it off so we never set it to
00702          less than 1! */
00703       alarm(1);
00704       rc = CURLRESOLV_TIMEDOUT;
00705       failf(data, "Previous alarm fired off!");
00706     }
00707     else
00708       alarm((unsigned int)alarm_set);
00709   }
00710 #endif /* USE_ALARM_TIMEOUT */
00711 
00712   return rc;
00713 }
00714 
00715 /*
00716  * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
00717  * made, the struct may be destroyed due to pruning. It is important that only
00718  * one unlock is made for each Curl_resolv() call.
00719  *
00720  * May be called with 'data' == NULL for global cache.
00721  */
00722 void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
00723 {
00724   if(data && data->share)
00725     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00726 
00727   freednsentry(dns);
00728 
00729   if(data && data->share)
00730     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00731 }
00732 
00733 /*
00734  * File-internal: release cache dns entry reference, free if inuse drops to 0
00735  */
00736 static void freednsentry(void *freethis)
00737 {
00738   struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
00739   DEBUGASSERT(dns && (dns->inuse>0));
00740 
00741   dns->inuse--;
00742   if(dns->inuse == 0) {
00743     Curl_freeaddrinfo(dns->addr);
00744     free(dns);
00745   }
00746 }
00747 
00748 /*
00749  * Curl_mk_dnscache() inits a new DNS cache and returns success/failure.
00750  */
00751 int Curl_mk_dnscache(struct curl_hash *hash)
00752 {
00753   return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
00754                         freednsentry);
00755 }
00756 
00757 /*
00758  * Curl_hostcache_clean()
00759  *
00760  * This _can_ be called with 'data' == NULL but then of course no locking
00761  * can be done!
00762  */
00763 
00764 void Curl_hostcache_clean(struct Curl_easy *data,
00765                           struct curl_hash *hash)
00766 {
00767   if(data && data->share)
00768     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00769 
00770   Curl_hash_clean(hash);
00771 
00772   if(data && data->share)
00773     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00774 }
00775 
00776 
00777 CURLcode Curl_loadhostpairs(struct Curl_easy *data)
00778 {
00779   struct curl_slist *hostp;
00780   char hostname[256];
00781   char address[256];
00782   int port;
00783 
00784   for(hostp = data->change.resolve; hostp; hostp = hostp->next) {
00785     if(!hostp->data)
00786       continue;
00787     if(hostp->data[0] == '-') {
00788       char *entry_id;
00789       size_t entry_len;
00790 
00791       if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
00792         infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n",
00793               hostp->data);
00794         continue;
00795       }
00796 
00797       /* Create an entry id, based upon the hostname and port */
00798       entry_id = create_hostcache_id(hostname, port);
00799       /* If we can't create the entry id, fail */
00800       if(!entry_id) {
00801         return CURLE_OUT_OF_MEMORY;
00802       }
00803 
00804       entry_len = strlen(entry_id);
00805 
00806       if(data->share)
00807         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00808 
00809       /* delete entry, ignore if it didn't exist */
00810       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len+1);
00811 
00812       if(data->share)
00813         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00814 
00815       /* free the allocated entry_id again */
00816       free(entry_id);
00817     }
00818     else {
00819       struct Curl_dns_entry *dns;
00820       Curl_addrinfo *addr;
00821       char *entry_id;
00822       size_t entry_len;
00823 
00824       if(3 != sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port,
00825                      address)) {
00826         infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
00827               hostp->data);
00828         continue;
00829       }
00830 
00831       addr = Curl_str2addr(address, port);
00832       if(!addr) {
00833         infof(data, "Address in '%s' found illegal!\n", hostp->data);
00834         continue;
00835       }
00836 
00837       /* Create an entry id, based upon the hostname and port */
00838       entry_id = create_hostcache_id(hostname, port);
00839       /* If we can't create the entry id, fail */
00840       if(!entry_id) {
00841         Curl_freeaddrinfo(addr);
00842         return CURLE_OUT_OF_MEMORY;
00843       }
00844 
00845       entry_len = strlen(entry_id);
00846 
00847       if(data->share)
00848         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
00849 
00850       /* See if its already in our dns cache */
00851       dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
00852 
00853       /* free the allocated entry_id again */
00854       free(entry_id);
00855 
00856       if(!dns) {
00857         /* if not in the cache already, put this host in the cache */
00858         dns = Curl_cache_addr(data, addr, hostname, port);
00859         if(dns) {
00860           dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */
00861           /* release the returned reference; the cache itself will keep the
00862            * entry alive: */
00863           dns->inuse--;
00864         }
00865       }
00866       else
00867         /* this is a duplicate, free it again */
00868         Curl_freeaddrinfo(addr);
00869 
00870       if(data->share)
00871         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
00872 
00873       if(!dns) {
00874         Curl_freeaddrinfo(addr);
00875         return CURLE_OUT_OF_MEMORY;
00876       }
00877       infof(data, "Added %s:%d:%s to DNS cache\n",
00878             hostname, port, address);
00879     }
00880   }
00881   data->change.resolve = NULL; /* dealt with now */
00882 
00883   return CURLE_OK;
00884 }


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