Annotation of embedaddon/curl/lib/hostip.c, revision 1.1.1.1

1.1       misho       1: /***************************************************************************
                      2:  *                                  _   _ ____  _
                      3:  *  Project                     ___| | | |  _ \| |
                      4:  *                             / __| | | | |_) | |
                      5:  *                            | (__| |_| |  _ <| |___
                      6:  *                             \___|\___/|_| \_\_____|
                      7:  *
                      8:  * Copyright (C) 1998 - 2020, 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: #ifdef HAVE_NETINET_IN_H
                     26: #include <netinet/in.h>
                     27: #endif
                     28: #ifdef HAVE_NETINET_IN6_H
                     29: #include <netinet/in6.h>
                     30: #endif
                     31: #ifdef HAVE_NETDB_H
                     32: #include <netdb.h>
                     33: #endif
                     34: #ifdef HAVE_ARPA_INET_H
                     35: #include <arpa/inet.h>
                     36: #endif
                     37: #ifdef __VMS
                     38: #include <in.h>
                     39: #include <inet.h>
                     40: #endif
                     41: 
                     42: #ifdef HAVE_SETJMP_H
                     43: #include <setjmp.h>
                     44: #endif
                     45: #ifdef HAVE_SIGNAL_H
                     46: #include <signal.h>
                     47: #endif
                     48: 
                     49: #ifdef HAVE_PROCESS_H
                     50: #include <process.h>
                     51: #endif
                     52: 
                     53: #include "urldata.h"
                     54: #include "sendf.h"
                     55: #include "hostip.h"
                     56: #include "hash.h"
                     57: #include "rand.h"
                     58: #include "share.h"
                     59: #include "strerror.h"
                     60: #include "url.h"
                     61: #include "inet_ntop.h"
                     62: #include "inet_pton.h"
                     63: #include "multiif.h"
                     64: #include "doh.h"
                     65: #include "warnless.h"
                     66: /* The last 3 #include files should be in this order */
                     67: #include "curl_printf.h"
                     68: #include "curl_memory.h"
                     69: #include "memdebug.h"
                     70: 
                     71: #if defined(CURLRES_SYNCH) && \
                     72:     defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
                     73: /* alarm-based timeouts can only be used with all the dependencies satisfied */
                     74: #define USE_ALARM_TIMEOUT
                     75: #endif
                     76: 
                     77: #define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
                     78: 
                     79: /*
                     80:  * hostip.c explained
                     81:  * ==================
                     82:  *
                     83:  * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
                     84:  * source file are these:
                     85:  *
                     86:  * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
                     87:  * that. The host may not be able to resolve IPv6, but we don't really have to
                     88:  * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
                     89:  * defined.
                     90:  *
                     91:  * CURLRES_ARES - is defined if libcurl is built to use c-ares for
                     92:  * asynchronous name resolves. This can be Windows or *nix.
                     93:  *
                     94:  * CURLRES_THREADED - is defined if libcurl is built to run under (native)
                     95:  * Windows, and then the name resolve will be done in a new thread, and the
                     96:  * supported API will be the same as for ares-builds.
                     97:  *
                     98:  * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
                     99:  * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
                    100:  * defined.
                    101:  *
                    102:  * The host*.c sources files are split up like this:
                    103:  *
                    104:  * hostip.c   - method-independent resolver functions and utility functions
                    105:  * hostasyn.c - functions for asynchronous name resolves
                    106:  * hostsyn.c  - functions for synchronous name resolves
                    107:  * hostip4.c  - IPv4 specific functions
                    108:  * hostip6.c  - IPv6 specific functions
                    109:  *
                    110:  * The two asynchronous name resolver backends are implemented in:
                    111:  * asyn-ares.c   - functions for ares-using name resolves
                    112:  * asyn-thread.c - functions for threaded name resolves
                    113: 
                    114:  * The hostip.h is the united header file for all this. It defines the
                    115:  * CURLRES_* defines based on the config*.h and curl_setup.h defines.
                    116:  */
                    117: 
                    118: static void freednsentry(void *freethis);
                    119: 
                    120: /*
                    121:  * Return # of addresses in a Curl_addrinfo struct
                    122:  */
                    123: int Curl_num_addresses(const Curl_addrinfo *addr)
                    124: {
                    125:   int i = 0;
                    126:   while(addr) {
                    127:     addr = addr->ai_next;
                    128:     i++;
                    129:   }
                    130:   return i;
                    131: }
                    132: 
                    133: /*
                    134:  * Curl_printable_address() returns a printable version of the 1st address
                    135:  * given in the 'ai' argument. The result will be stored in the buf that is
                    136:  * bufsize bytes big.
                    137:  *
                    138:  * If the conversion fails, it returns NULL.
                    139:  */
                    140: const char *
                    141: Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
                    142: {
                    143:   const struct sockaddr_in *sa4;
                    144:   const struct in_addr *ipaddr4;
                    145: #ifdef ENABLE_IPV6
                    146:   const struct sockaddr_in6 *sa6;
                    147:   const struct in6_addr *ipaddr6;
                    148: #endif
                    149: 
                    150:   switch(ai->ai_family) {
                    151:     case AF_INET:
                    152:       sa4 = (const void *)ai->ai_addr;
                    153:       ipaddr4 = &sa4->sin_addr;
                    154:       return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf,
                    155:                             bufsize);
                    156: #ifdef ENABLE_IPV6
                    157:     case AF_INET6:
                    158:       sa6 = (const void *)ai->ai_addr;
                    159:       ipaddr6 = &sa6->sin6_addr;
                    160:       return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf,
                    161:                             bufsize);
                    162: #endif
                    163:     default:
                    164:       break;
                    165:   }
                    166:   return NULL;
                    167: }
                    168: 
                    169: /*
                    170:  * Create a hostcache id string for the provided host + port, to be used by
                    171:  * the DNS caching. Without alloc.
                    172:  */
                    173: static void
                    174: create_hostcache_id(const char *name, int port, char *ptr, size_t buflen)
                    175: {
                    176:   size_t len = strlen(name);
                    177:   if(len > (buflen - 7))
                    178:     len = buflen - 7;
                    179:   /* store and lower case the name */
                    180:   while(len--)
                    181:     *ptr++ = (char)TOLOWER(*name++);
                    182:   msnprintf(ptr, 7, ":%u", port);
                    183: }
                    184: 
                    185: struct hostcache_prune_data {
                    186:   long cache_timeout;
                    187:   time_t now;
                    188: };
                    189: 
                    190: /*
                    191:  * This function is set as a callback to be called for every entry in the DNS
                    192:  * cache when we want to prune old unused entries.
                    193:  *
                    194:  * Returning non-zero means remove the entry, return 0 to keep it in the
                    195:  * cache.
                    196:  */
                    197: static int
                    198: hostcache_timestamp_remove(void *datap, void *hc)
                    199: {
                    200:   struct hostcache_prune_data *data =
                    201:     (struct hostcache_prune_data *) datap;
                    202:   struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
                    203: 
                    204:   return (0 != c->timestamp)
                    205:     && (data->now - c->timestamp >= data->cache_timeout);
                    206: }
                    207: 
                    208: /*
                    209:  * Prune the DNS cache. This assumes that a lock has already been taken.
                    210:  */
                    211: static void
                    212: hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
                    213: {
                    214:   struct hostcache_prune_data user;
                    215: 
                    216:   user.cache_timeout = cache_timeout;
                    217:   user.now = now;
                    218: 
                    219:   Curl_hash_clean_with_criterium(hostcache,
                    220:                                  (void *) &user,
                    221:                                  hostcache_timestamp_remove);
                    222: }
                    223: 
                    224: /*
                    225:  * Library-wide function for pruning the DNS cache. This function takes and
                    226:  * returns the appropriate locks.
                    227:  */
                    228: void Curl_hostcache_prune(struct Curl_easy *data)
                    229: {
                    230:   time_t now;
                    231: 
                    232:   if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
                    233:     /* cache forever means never prune, and NULL hostcache means
                    234:        we can't do it */
                    235:     return;
                    236: 
                    237:   if(data->share)
                    238:     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
                    239: 
                    240:   time(&now);
                    241: 
                    242:   /* Remove outdated and unused entries from the hostcache */
                    243:   hostcache_prune(data->dns.hostcache,
                    244:                   data->set.dns_cache_timeout,
                    245:                   now);
                    246: 
                    247:   if(data->share)
                    248:     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
                    249: }
                    250: 
                    251: #ifdef HAVE_SIGSETJMP
                    252: /* Beware this is a global and unique instance. This is used to store the
                    253:    return address that we can jump back to from inside a signal handler. This
                    254:    is not thread-safe stuff. */
                    255: sigjmp_buf curl_jmpenv;
                    256: #endif
                    257: 
                    258: /* lookup address, returns entry if found and not stale */
                    259: static struct Curl_dns_entry *
                    260: fetch_addr(struct connectdata *conn,
                    261:                 const char *hostname,
                    262:                 int port)
                    263: {
                    264:   struct Curl_dns_entry *dns = NULL;
                    265:   size_t entry_len;
                    266:   struct Curl_easy *data = conn->data;
                    267:   char entry_id[MAX_HOSTCACHE_LEN];
                    268: 
                    269:   /* Create an entry id, based upon the hostname and port */
                    270:   create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
                    271:   entry_len = strlen(entry_id);
                    272: 
                    273:   /* See if its already in our dns cache */
                    274:   dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
                    275: 
                    276:   /* No entry found in cache, check if we might have a wildcard entry */
                    277:   if(!dns && data->change.wildcard_resolve) {
                    278:     create_hostcache_id("*", port, entry_id, sizeof(entry_id));
                    279:     entry_len = strlen(entry_id);
                    280: 
                    281:     /* See if it's already in our dns cache */
                    282:     dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
                    283:   }
                    284: 
                    285:   if(dns && (data->set.dns_cache_timeout != -1)) {
                    286:     /* See whether the returned entry is stale. Done before we release lock */
                    287:     struct hostcache_prune_data user;
                    288: 
                    289:     time(&user.now);
                    290:     user.cache_timeout = data->set.dns_cache_timeout;
                    291: 
                    292:     if(hostcache_timestamp_remove(&user, dns)) {
                    293:       infof(data, "Hostname in DNS cache was stale, zapped\n");
                    294:       dns = NULL; /* the memory deallocation is being handled by the hash */
                    295:       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
                    296:     }
                    297:   }
                    298: 
                    299:   return dns;
                    300: }
                    301: 
                    302: /*
                    303:  * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
                    304:  *
                    305:  * Curl_resolv() checks initially and multi_runsingle() checks each time
                    306:  * it discovers the handle in the state WAITRESOLVE whether the hostname
                    307:  * has already been resolved and the address has already been stored in
                    308:  * the DNS cache. This short circuits waiting for a lot of pending
                    309:  * lookups for the same hostname requested by different handles.
                    310:  *
                    311:  * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
                    312:  *
                    313:  * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
                    314:  * use, or we'll leak memory!
                    315:  */
                    316: struct Curl_dns_entry *
                    317: Curl_fetch_addr(struct connectdata *conn,
                    318:                 const char *hostname,
                    319:                 int port)
                    320: {
                    321:   struct Curl_easy *data = conn->data;
                    322:   struct Curl_dns_entry *dns = NULL;
                    323: 
                    324:   if(data->share)
                    325:     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
                    326: 
                    327:   dns = fetch_addr(conn, hostname, port);
                    328: 
                    329:   if(dns)
                    330:     dns->inuse++; /* we use it! */
                    331: 
                    332:   if(data->share)
                    333:     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
                    334: 
                    335:   return dns;
                    336: }
                    337: 
                    338: #ifndef CURL_DISABLE_SHUFFLE_DNS
                    339: UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
                    340:                                     Curl_addrinfo **addr);
                    341: /*
                    342:  * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
                    343:  * struct by re-linking its linked list.
                    344:  *
                    345:  * The addr argument should be the address of a pointer to the head node of a
                    346:  * `Curl_addrinfo` list and it will be modified to point to the new head after
                    347:  * shuffling.
                    348:  *
                    349:  * Not declared static only to make it easy to use in a unit test!
                    350:  *
                    351:  * @unittest: 1608
                    352:  */
                    353: UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
                    354:                                     Curl_addrinfo **addr)
                    355: {
                    356:   CURLcode result = CURLE_OK;
                    357:   const int num_addrs = Curl_num_addresses(*addr);
                    358: 
                    359:   if(num_addrs > 1) {
                    360:     Curl_addrinfo **nodes;
                    361:     infof(data, "Shuffling %i addresses", num_addrs);
                    362: 
                    363:     nodes = malloc(num_addrs*sizeof(*nodes));
                    364:     if(nodes) {
                    365:       int i;
                    366:       unsigned int *rnd;
                    367:       const size_t rnd_size = num_addrs * sizeof(*rnd);
                    368: 
                    369:       /* build a plain array of Curl_addrinfo pointers */
                    370:       nodes[0] = *addr;
                    371:       for(i = 1; i < num_addrs; i++) {
                    372:         nodes[i] = nodes[i-1]->ai_next;
                    373:       }
                    374: 
                    375:       rnd = malloc(rnd_size);
                    376:       if(rnd) {
                    377:         /* Fisher-Yates shuffle */
                    378:         if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
                    379:           Curl_addrinfo *swap_tmp;
                    380:           for(i = num_addrs - 1; i > 0; i--) {
                    381:             swap_tmp = nodes[rnd[i] % (i + 1)];
                    382:             nodes[rnd[i] % (i + 1)] = nodes[i];
                    383:             nodes[i] = swap_tmp;
                    384:           }
                    385: 
                    386:           /* relink list in the new order */
                    387:           for(i = 1; i < num_addrs; i++) {
                    388:             nodes[i-1]->ai_next = nodes[i];
                    389:           }
                    390: 
                    391:           nodes[num_addrs-1]->ai_next = NULL;
                    392:           *addr = nodes[0];
                    393:         }
                    394:         free(rnd);
                    395:       }
                    396:       else
                    397:         result = CURLE_OUT_OF_MEMORY;
                    398:       free(nodes);
                    399:     }
                    400:     else
                    401:       result = CURLE_OUT_OF_MEMORY;
                    402:   }
                    403:   return result;
                    404: }
                    405: #endif
                    406: 
                    407: /*
                    408:  * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
                    409:  *
                    410:  * When calling Curl_resolv() has resulted in a response with a returned
                    411:  * address, we call this function to store the information in the dns
                    412:  * cache etc
                    413:  *
                    414:  * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
                    415:  */
                    416: struct Curl_dns_entry *
                    417: Curl_cache_addr(struct Curl_easy *data,
                    418:                 Curl_addrinfo *addr,
                    419:                 const char *hostname,
                    420:                 int port)
                    421: {
                    422:   char entry_id[MAX_HOSTCACHE_LEN];
                    423:   size_t entry_len;
                    424:   struct Curl_dns_entry *dns;
                    425:   struct Curl_dns_entry *dns2;
                    426: 
                    427: #ifndef CURL_DISABLE_SHUFFLE_DNS
                    428:   /* shuffle addresses if requested */
                    429:   if(data->set.dns_shuffle_addresses) {
                    430:     CURLcode result = Curl_shuffle_addr(data, &addr);
                    431:     if(result)
                    432:       return NULL;
                    433:   }
                    434: #endif
                    435: 
                    436:   /* Create a new cache entry */
                    437:   dns = calloc(1, sizeof(struct Curl_dns_entry));
                    438:   if(!dns) {
                    439:     return NULL;
                    440:   }
                    441: 
                    442:   /* Create an entry id, based upon the hostname and port */
                    443:   create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
                    444:   entry_len = strlen(entry_id);
                    445: 
                    446:   dns->inuse = 1;   /* the cache has the first reference */
                    447:   dns->addr = addr; /* this is the address(es) */
                    448:   time(&dns->timestamp);
                    449:   if(dns->timestamp == 0)
                    450:     dns->timestamp = 1;   /* zero indicates CURLOPT_RESOLVE entry */
                    451: 
                    452:   /* Store the resolved data in our DNS cache. */
                    453:   dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
                    454:                        (void *)dns);
                    455:   if(!dns2) {
                    456:     free(dns);
                    457:     return NULL;
                    458:   }
                    459: 
                    460:   dns = dns2;
                    461:   dns->inuse++;         /* mark entry as in-use */
                    462:   return dns;
                    463: }
                    464: 
                    465: /*
                    466:  * Curl_resolv() is the main name resolve function within libcurl. It resolves
                    467:  * a name and returns a pointer to the entry in the 'entry' argument (if one
                    468:  * is provided). This function might return immediately if we're using asynch
                    469:  * resolves. See the return codes.
                    470:  *
                    471:  * The cache entry we return will get its 'inuse' counter increased when this
                    472:  * function is used. You MUST call Curl_resolv_unlock() later (when you're
                    473:  * done using this struct) to decrease the counter again.
                    474:  *
                    475:  * In debug mode, we specifically test for an interface name "LocalHost"
                    476:  * and resolve "localhost" instead as a means to permit test cases
                    477:  * to connect to a local test server with any host name.
                    478:  *
                    479:  * Return codes:
                    480:  *
                    481:  * CURLRESOLV_ERROR   (-1) = error, no pointer
                    482:  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
                    483:  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
                    484:  */
                    485: 
                    486: enum resolve_t Curl_resolv(struct connectdata *conn,
                    487:                            const char *hostname,
                    488:                            int port,
                    489:                            bool allowDOH,
                    490:                            struct Curl_dns_entry **entry)
                    491: {
                    492:   struct Curl_dns_entry *dns = NULL;
                    493:   struct Curl_easy *data = conn->data;
                    494:   CURLcode result;
                    495:   enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
                    496: 
                    497:   *entry = NULL;
                    498: 
                    499:   if(data->share)
                    500:     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
                    501: 
                    502:   dns = fetch_addr(conn, hostname, port);
                    503: 
                    504:   if(dns) {
                    505:     infof(data, "Hostname %s was found in DNS cache\n", hostname);
                    506:     dns->inuse++; /* we use it! */
                    507:     rc = CURLRESOLV_RESOLVED;
                    508:   }
                    509: 
                    510:   if(data->share)
                    511:     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
                    512: 
                    513:   if(!dns) {
                    514:     /* The entry was not in the cache. Resolve it to IP address */
                    515: 
                    516:     Curl_addrinfo *addr = NULL;
                    517:     int respwait = 0;
                    518: #ifndef USE_RESOLVE_ON_IPS
                    519:     struct in_addr in;
                    520: #endif
                    521: 
                    522:     /* notify the resolver start callback */
                    523:     if(data->set.resolver_start) {
                    524:       int st;
                    525:       Curl_set_in_callback(data, true);
                    526:       st = data->set.resolver_start(data->state.resolver, NULL,
                    527:                                     data->set.resolver_start_client);
                    528:       Curl_set_in_callback(data, false);
                    529:       if(st)
                    530:         return CURLRESOLV_ERROR;
                    531:     }
                    532: 
                    533: #ifndef USE_RESOLVE_ON_IPS
                    534:     /* First check if this is an IPv4 address string */
                    535:     if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
                    536:       /* This is a dotted IP address 123.123.123.123-style */
                    537:       addr = Curl_ip2addr(AF_INET, &in, hostname, port);
                    538: #ifdef ENABLE_IPV6
                    539:     if(!addr) {
                    540:       struct in6_addr in6;
                    541:       /* check if this is an IPv6 address string */
                    542:       if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
                    543:         /* This is an IPv6 address literal */
                    544:         addr = Curl_ip2addr(AF_INET6, &in6, hostname, port);
                    545:     }
                    546: #endif /* ENABLE_IPV6 */
                    547: #endif /* !USE_RESOLVE_ON_IPS */
                    548: 
                    549:     if(!addr) {
                    550:       /* Check what IP specifics the app has requested and if we can provide
                    551:        * it. If not, bail out. */
                    552:       if(!Curl_ipvalid(conn))
                    553:         return CURLRESOLV_ERROR;
                    554: 
                    555:       if(allowDOH && data->set.doh) {
                    556:         addr = Curl_doh(conn, hostname, port, &respwait);
                    557:       }
                    558:       else {
                    559:         /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
                    560:            non-zero value indicating that we need to wait for the response to
                    561:            the resolve call */
                    562:         addr = Curl_getaddrinfo(conn,
                    563: #ifdef DEBUGBUILD
                    564:                                 (data->set.str[STRING_DEVICE]
                    565:                                  && !strcmp(data->set.str[STRING_DEVICE],
                    566:                                             "LocalHost"))?"localhost":
                    567: #endif
                    568:                                 hostname, port, &respwait);
                    569:       }
                    570:     }
                    571:     if(!addr) {
                    572:       if(respwait) {
                    573:         /* the response to our resolve call will come asynchronously at
                    574:            a later time, good or bad */
                    575:         /* First, check that we haven't received the info by now */
                    576:         result = Curl_resolv_check(conn, &dns);
                    577:         if(result) /* error detected */
                    578:           return CURLRESOLV_ERROR;
                    579:         if(dns)
                    580:           rc = CURLRESOLV_RESOLVED; /* pointer provided */
                    581:         else
                    582:           rc = CURLRESOLV_PENDING; /* no info yet */
                    583:       }
                    584:     }
                    585:     else {
                    586:       if(data->share)
                    587:         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
                    588: 
                    589:       /* we got a response, store it in the cache */
                    590:       dns = Curl_cache_addr(data, addr, hostname, port);
                    591: 
                    592:       if(data->share)
                    593:         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
                    594: 
                    595:       if(!dns)
                    596:         /* returned failure, bail out nicely */
                    597:         Curl_freeaddrinfo(addr);
                    598:       else
                    599:         rc = CURLRESOLV_RESOLVED;
                    600:     }
                    601:   }
                    602: 
                    603:   *entry = dns;
                    604: 
                    605:   return rc;
                    606: }
                    607: 
                    608: #ifdef USE_ALARM_TIMEOUT
                    609: /*
                    610:  * This signal handler jumps back into the main libcurl code and continues
                    611:  * execution.  This effectively causes the remainder of the application to run
                    612:  * within a signal handler which is nonportable and could lead to problems.
                    613:  */
                    614: static
                    615: RETSIGTYPE alarmfunc(int sig)
                    616: {
                    617:   /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
                    618:   (void)sig;
                    619:   siglongjmp(curl_jmpenv, 1);
                    620: }
                    621: #endif /* USE_ALARM_TIMEOUT */
                    622: 
                    623: /*
                    624:  * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
                    625:  * timeout.  This function might return immediately if we're using asynch
                    626:  * resolves. See the return codes.
                    627:  *
                    628:  * The cache entry we return will get its 'inuse' counter increased when this
                    629:  * function is used. You MUST call Curl_resolv_unlock() later (when you're
                    630:  * done using this struct) to decrease the counter again.
                    631:  *
                    632:  * If built with a synchronous resolver and use of signals is not
                    633:  * disabled by the application, then a nonzero timeout will cause a
                    634:  * timeout after the specified number of milliseconds. Otherwise, timeout
                    635:  * is ignored.
                    636:  *
                    637:  * Return codes:
                    638:  *
                    639:  * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
                    640:  * CURLRESOLV_ERROR   (-1) = error, no pointer
                    641:  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
                    642:  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
                    643:  */
                    644: 
                    645: enum resolve_t Curl_resolv_timeout(struct connectdata *conn,
                    646:                                    const char *hostname,
                    647:                                    int port,
                    648:                                    struct Curl_dns_entry **entry,
                    649:                                    timediff_t timeoutms)
                    650: {
                    651: #ifdef USE_ALARM_TIMEOUT
                    652: #ifdef HAVE_SIGACTION
                    653:   struct sigaction keep_sigact;   /* store the old struct here */
                    654:   volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
                    655:   struct sigaction sigact;
                    656: #else
                    657: #ifdef HAVE_SIGNAL
                    658:   void (*keep_sigact)(int);       /* store the old handler here */
                    659: #endif /* HAVE_SIGNAL */
                    660: #endif /* HAVE_SIGACTION */
                    661:   volatile long timeout;
                    662:   volatile unsigned int prev_alarm = 0;
                    663:   struct Curl_easy *data = conn->data;
                    664: #endif /* USE_ALARM_TIMEOUT */
                    665:   enum resolve_t rc;
                    666: 
                    667:   *entry = NULL;
                    668: 
                    669:   if(timeoutms < 0)
                    670:     /* got an already expired timeout */
                    671:     return CURLRESOLV_TIMEDOUT;
                    672: 
                    673: #ifdef USE_ALARM_TIMEOUT
                    674:   if(data->set.no_signal)
                    675:     /* Ignore the timeout when signals are disabled */
                    676:     timeout = 0;
                    677:   else
                    678:     timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
                    679: 
                    680:   if(!timeout)
                    681:     /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
                    682:     return Curl_resolv(conn, hostname, port, TRUE, entry);
                    683: 
                    684:   if(timeout < 1000) {
                    685:     /* The alarm() function only provides integer second resolution, so if
                    686:        we want to wait less than one second we must bail out already now. */
                    687:     failf(data,
                    688:         "remaining timeout of %ld too small to resolve via SIGALRM method",
                    689:         timeout);
                    690:     return CURLRESOLV_TIMEDOUT;
                    691:   }
                    692:   /* This allows us to time-out from the name resolver, as the timeout
                    693:      will generate a signal and we will siglongjmp() from that here.
                    694:      This technique has problems (see alarmfunc).
                    695:      This should be the last thing we do before calling Curl_resolv(),
                    696:      as otherwise we'd have to worry about variables that get modified
                    697:      before we invoke Curl_resolv() (and thus use "volatile"). */
                    698:   if(sigsetjmp(curl_jmpenv, 1)) {
                    699:     /* this is coming from a siglongjmp() after an alarm signal */
                    700:     failf(data, "name lookup timed out");
                    701:     rc = CURLRESOLV_ERROR;
                    702:     goto clean_up;
                    703:   }
                    704:   else {
                    705:     /*************************************************************
                    706:      * Set signal handler to catch SIGALRM
                    707:      * Store the old value to be able to set it back later!
                    708:      *************************************************************/
                    709: #ifdef HAVE_SIGACTION
                    710:     sigaction(SIGALRM, NULL, &sigact);
                    711:     keep_sigact = sigact;
                    712:     keep_copysig = TRUE; /* yes, we have a copy */
                    713:     sigact.sa_handler = alarmfunc;
                    714: #ifdef SA_RESTART
                    715:     /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
                    716:     sigact.sa_flags &= ~SA_RESTART;
                    717: #endif
                    718:     /* now set the new struct */
                    719:     sigaction(SIGALRM, &sigact, NULL);
                    720: #else /* HAVE_SIGACTION */
                    721:     /* no sigaction(), revert to the much lamer signal() */
                    722: #ifdef HAVE_SIGNAL
                    723:     keep_sigact = signal(SIGALRM, alarmfunc);
                    724: #endif
                    725: #endif /* HAVE_SIGACTION */
                    726: 
                    727:     /* alarm() makes a signal get sent when the timeout fires off, and that
                    728:        will abort system calls */
                    729:     prev_alarm = alarm(curlx_sltoui(timeout/1000L));
                    730:   }
                    731: 
                    732: #else
                    733: #ifndef CURLRES_ASYNCH
                    734:   if(timeoutms)
                    735:     infof(conn->data, "timeout on name lookup is not supported\n");
                    736: #else
                    737:   (void)timeoutms; /* timeoutms not used with an async resolver */
                    738: #endif
                    739: #endif /* USE_ALARM_TIMEOUT */
                    740: 
                    741:   /* Perform the actual name resolution. This might be interrupted by an
                    742:    * alarm if it takes too long.
                    743:    */
                    744:   rc = Curl_resolv(conn, hostname, port, TRUE, entry);
                    745: 
                    746: #ifdef USE_ALARM_TIMEOUT
                    747: clean_up:
                    748: 
                    749:   if(!prev_alarm)
                    750:     /* deactivate a possibly active alarm before uninstalling the handler */
                    751:     alarm(0);
                    752: 
                    753: #ifdef HAVE_SIGACTION
                    754:   if(keep_copysig) {
                    755:     /* we got a struct as it looked before, now put that one back nice
                    756:        and clean */
                    757:     sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
                    758:   }
                    759: #else
                    760: #ifdef HAVE_SIGNAL
                    761:   /* restore the previous SIGALRM handler */
                    762:   signal(SIGALRM, keep_sigact);
                    763: #endif
                    764: #endif /* HAVE_SIGACTION */
                    765: 
                    766:   /* switch back the alarm() to either zero or to what it was before minus
                    767:      the time we spent until now! */
                    768:   if(prev_alarm) {
                    769:     /* there was an alarm() set before us, now put it back */
                    770:     timediff_t elapsed_secs = Curl_timediff(Curl_now(),
                    771:                                             conn->created) / 1000;
                    772: 
                    773:     /* the alarm period is counted in even number of seconds */
                    774:     unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs);
                    775: 
                    776:     if(!alarm_set ||
                    777:        ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
                    778:       /* if the alarm time-left reached zero or turned "negative" (counted
                    779:          with unsigned values), we should fire off a SIGALRM here, but we
                    780:          won't, and zero would be to switch it off so we never set it to
                    781:          less than 1! */
                    782:       alarm(1);
                    783:       rc = CURLRESOLV_TIMEDOUT;
                    784:       failf(data, "Previous alarm fired off!");
                    785:     }
                    786:     else
                    787:       alarm((unsigned int)alarm_set);
                    788:   }
                    789: #endif /* USE_ALARM_TIMEOUT */
                    790: 
                    791:   return rc;
                    792: }
                    793: 
                    794: /*
                    795:  * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
                    796:  * made, the struct may be destroyed due to pruning. It is important that only
                    797:  * one unlock is made for each Curl_resolv() call.
                    798:  *
                    799:  * May be called with 'data' == NULL for global cache.
                    800:  */
                    801: void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
                    802: {
                    803:   if(data && data->share)
                    804:     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
                    805: 
                    806:   freednsentry(dns);
                    807: 
                    808:   if(data && data->share)
                    809:     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
                    810: }
                    811: 
                    812: /*
                    813:  * File-internal: release cache dns entry reference, free if inuse drops to 0
                    814:  */
                    815: static void freednsentry(void *freethis)
                    816: {
                    817:   struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
                    818:   DEBUGASSERT(dns && (dns->inuse>0));
                    819: 
                    820:   dns->inuse--;
                    821:   if(dns->inuse == 0) {
                    822:     Curl_freeaddrinfo(dns->addr);
                    823:     free(dns);
                    824:   }
                    825: }
                    826: 
                    827: /*
                    828:  * Curl_mk_dnscache() inits a new DNS cache and returns success/failure.
                    829:  */
                    830: int Curl_mk_dnscache(struct curl_hash *hash)
                    831: {
                    832:   return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
                    833:                         freednsentry);
                    834: }
                    835: 
                    836: /*
                    837:  * Curl_hostcache_clean()
                    838:  *
                    839:  * This _can_ be called with 'data' == NULL but then of course no locking
                    840:  * can be done!
                    841:  */
                    842: 
                    843: void Curl_hostcache_clean(struct Curl_easy *data,
                    844:                           struct curl_hash *hash)
                    845: {
                    846:   if(data && data->share)
                    847:     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
                    848: 
                    849:   Curl_hash_clean(hash);
                    850: 
                    851:   if(data && data->share)
                    852:     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
                    853: }
                    854: 
                    855: 
                    856: CURLcode Curl_loadhostpairs(struct Curl_easy *data)
                    857: {
                    858:   struct curl_slist *hostp;
                    859:   char hostname[256];
                    860:   int port = 0;
                    861: 
                    862:   /* Default is no wildcard found */
                    863:   data->change.wildcard_resolve = false;
                    864: 
                    865:   for(hostp = data->change.resolve; hostp; hostp = hostp->next) {
                    866:     char entry_id[MAX_HOSTCACHE_LEN];
                    867:     if(!hostp->data)
                    868:       continue;
                    869:     if(hostp->data[0] == '-') {
                    870:       size_t entry_len;
                    871: 
                    872:       if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
                    873:         infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n",
                    874:               hostp->data);
                    875:         continue;
                    876:       }
                    877: 
                    878:       /* Create an entry id, based upon the hostname and port */
                    879:       create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
                    880:       entry_len = strlen(entry_id);
                    881: 
                    882:       if(data->share)
                    883:         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
                    884: 
                    885:       /* delete entry, ignore if it didn't exist */
                    886:       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
                    887: 
                    888:       if(data->share)
                    889:         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
                    890:     }
                    891:     else {
                    892:       struct Curl_dns_entry *dns;
                    893:       Curl_addrinfo *head = NULL, *tail = NULL;
                    894:       size_t entry_len;
                    895:       char address[64];
                    896: #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
                    897:       char *addresses = NULL;
                    898: #endif
                    899:       char *addr_begin;
                    900:       char *addr_end;
                    901:       char *port_ptr;
                    902:       char *end_ptr;
                    903:       char *host_end;
                    904:       unsigned long tmp_port;
                    905:       bool error = true;
                    906: 
                    907:       host_end = strchr(hostp->data, ':');
                    908:       if(!host_end ||
                    909:          ((host_end - hostp->data) >= (ptrdiff_t)sizeof(hostname)))
                    910:         goto err;
                    911: 
                    912:       memcpy(hostname, hostp->data, host_end - hostp->data);
                    913:       hostname[host_end - hostp->data] = '\0';
                    914: 
                    915:       port_ptr = host_end + 1;
                    916:       tmp_port = strtoul(port_ptr, &end_ptr, 10);
                    917:       if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':')
                    918:         goto err;
                    919: 
                    920:       port = (int)tmp_port;
                    921: #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
                    922:       addresses = end_ptr + 1;
                    923: #endif
                    924: 
                    925:       while(*end_ptr) {
                    926:         size_t alen;
                    927:         Curl_addrinfo *ai;
                    928: 
                    929:         addr_begin = end_ptr + 1;
                    930:         addr_end = strchr(addr_begin, ',');
                    931:         if(!addr_end)
                    932:           addr_end = addr_begin + strlen(addr_begin);
                    933:         end_ptr = addr_end;
                    934: 
                    935:         /* allow IP(v6) address within [brackets] */
                    936:         if(*addr_begin == '[') {
                    937:           if(addr_end == addr_begin || *(addr_end - 1) != ']')
                    938:             goto err;
                    939:           ++addr_begin;
                    940:           --addr_end;
                    941:         }
                    942: 
                    943:         alen = addr_end - addr_begin;
                    944:         if(!alen)
                    945:           continue;
                    946: 
                    947:         if(alen >= sizeof(address))
                    948:           goto err;
                    949: 
                    950:         memcpy(address, addr_begin, alen);
                    951:         address[alen] = '\0';
                    952: 
                    953: #ifndef ENABLE_IPV6
                    954:         if(strchr(address, ':')) {
                    955:           infof(data, "Ignoring resolve address '%s', missing IPv6 support.\n",
                    956:                 address);
                    957:           continue;
                    958:         }
                    959: #endif
                    960: 
                    961:         ai = Curl_str2addr(address, port);
                    962:         if(!ai) {
                    963:           infof(data, "Resolve address '%s' found illegal!\n", address);
                    964:           goto err;
                    965:         }
                    966: 
                    967:         if(tail) {
                    968:           tail->ai_next = ai;
                    969:           tail = tail->ai_next;
                    970:         }
                    971:         else {
                    972:           head = tail = ai;
                    973:         }
                    974:       }
                    975: 
                    976:       if(!head)
                    977:         goto err;
                    978: 
                    979:       error = false;
                    980:    err:
                    981:       if(error) {
                    982:         infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
                    983:               hostp->data);
                    984:         Curl_freeaddrinfo(head);
                    985:         continue;
                    986:       }
                    987: 
                    988:       /* Create an entry id, based upon the hostname and port */
                    989:       create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
                    990:       entry_len = strlen(entry_id);
                    991: 
                    992:       if(data->share)
                    993:         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
                    994: 
                    995:       /* See if its already in our dns cache */
                    996:       dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
                    997: 
                    998:       if(dns) {
                    999:         infof(data, "RESOLVE %s:%d is - old addresses discarded!\n",
                   1000:                 hostname, port);
                   1001:         /* delete old entry entry, there are two reasons for this
                   1002:          1. old entry may have different addresses.
                   1003:          2. even if entry with correct addresses is already in the cache,
                   1004:             but if it is close to expire, then by the time next http
                   1005:             request is made, it can get expired and pruned because old
                   1006:             entry is not necessarily marked as added by CURLOPT_RESOLVE. */
                   1007: 
                   1008:         Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
                   1009:       }
                   1010: 
                   1011:       /* put this new host in the cache */
                   1012:       dns = Curl_cache_addr(data, head, hostname, port);
                   1013:       if(dns) {
                   1014:         dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */
                   1015:         /* release the returned reference; the cache itself will keep the
                   1016:          * entry alive: */
                   1017:             dns->inuse--;
                   1018:       }
                   1019: 
                   1020:       if(data->share)
                   1021:         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
                   1022: 
                   1023:       if(!dns) {
                   1024:         Curl_freeaddrinfo(head);
                   1025:         return CURLE_OUT_OF_MEMORY;
                   1026:       }
                   1027:       infof(data, "Added %s:%d:%s to DNS cache\n",
                   1028:             hostname, port, addresses);
                   1029: 
                   1030:       /* Wildcard hostname */
                   1031:       if(hostname[0] == '*' && hostname[1] == '\0') {
                   1032:         infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks\n",
                   1033:               hostname, port);
                   1034:         data->change.wildcard_resolve = true;
                   1035:       }
                   1036:     }
                   1037:   }
                   1038:   data->change.resolve = NULL; /* dealt with now */
                   1039: 
                   1040:   return CURLE_OK;
                   1041: }
                   1042: 
                   1043: CURLcode Curl_resolv_check(struct connectdata *conn,
                   1044:                            struct Curl_dns_entry **dns)
                   1045: {
                   1046: #if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH)
                   1047:   (void)dns;
                   1048: #endif
                   1049: 
                   1050:   if(conn->data->set.doh)
                   1051:     return Curl_doh_is_resolved(conn, dns);
                   1052:   return Curl_resolver_is_resolved(conn, dns);
                   1053: }
                   1054: 
                   1055: int Curl_resolv_getsock(struct connectdata *conn,
                   1056:                         curl_socket_t *socks)
                   1057: {
                   1058: #ifdef CURLRES_ASYNCH
                   1059:   if(conn->data->set.doh)
                   1060:     /* nothing to wait for during DOH resolve, those handles have their own
                   1061:        sockets */
                   1062:     return GETSOCK_BLANK;
                   1063:   return Curl_resolver_getsock(conn, socks);
                   1064: #else
                   1065:   (void)conn;
                   1066:   (void)socks;
                   1067:   return GETSOCK_BLANK;
                   1068: #endif
                   1069: }
                   1070: 
                   1071: /* Call this function after Curl_connect() has returned async=TRUE and
                   1072:    then a successful name resolve has been received.
                   1073: 
                   1074:    Note: this function disconnects and frees the conn data in case of
                   1075:    resolve failure */
                   1076: CURLcode Curl_once_resolved(struct connectdata *conn,
                   1077:                             bool *protocol_done)
                   1078: {
                   1079:   CURLcode result;
                   1080: 
                   1081:   if(conn->async.dns) {
                   1082:     conn->dns_entry = conn->async.dns;
                   1083:     conn->async.dns = NULL;
                   1084:   }
                   1085: 
                   1086:   result = Curl_setup_conn(conn, protocol_done);
                   1087: 
                   1088:   if(result)
                   1089:     /* We're not allowed to return failure with memory left allocated
                   1090:        in the connectdata struct, free those here */
                   1091:     Curl_disconnect(conn->data, conn, TRUE); /* close the connection */
                   1092: 
                   1093:   return result;
                   1094: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>