Annotation of embedaddon/curl/lib/hostip.c, revision 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>