LCOV - code coverage report
Current view: top level - lib/net - resolve.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 123 180 68.3 %
Date: 2021-11-24 03:28:48 Functions: 17 22 77.3 %

          Line data    Source code
       1             : /* Copyright (c) 2003-2004, Roger Dingledine
       2             :  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       3             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       4             : /* See LICENSE for licensing information */
       5             : 
       6             : /**
       7             :  * \file resolve.c
       8             :  * \brief Use the libc DNS resolver to convert hostnames into addresses.
       9             :  **/
      10             : 
      11             : #define RESOLVE_PRIVATE
      12             : #include "lib/net/resolve.h"
      13             : 
      14             : #include "lib/net/address.h"
      15             : #include "lib/net/inaddr.h"
      16             : #include "lib/malloc/malloc.h"
      17             : #include "lib/string/parse_int.h"
      18             : #include "lib/string/util_string.h"
      19             : 
      20             : #include "ext/siphash.h"
      21             : #include "ext/ht.h"
      22             : 
      23             : #ifdef HAVE_SYS_TYPES_H
      24             : #include <sys/types.h>
      25             : #endif
      26             : #ifdef HAVE_SYS_SOCKET_H
      27             : #include <sys/socket.h>
      28             : #endif
      29             : #ifdef HAVE_NETDB_H
      30             : #include <netdb.h>
      31             : #endif
      32             : 
      33             : #include <string.h>
      34             : 
      35             : /** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set
      36             :  * *<b>addr</b> to the proper IP address, in host byte order.  Returns 0
      37             :  * on success, -1 on failure; 1 on transient failure.
      38             :  *
      39             :  * This function only accepts IPv4 addresses.
      40             :  *
      41             :  * (This function exists because standard windows gethostbyname
      42             :  * doesn't treat raw IP addresses properly.)
      43             :  */
      44             : 
      45          59 : MOCK_IMPL(int,
      46             : tor_lookup_hostname,(const char *name, uint32_t *addr))
      47             : {
      48          59 :   tor_addr_t myaddr;
      49          59 :   int ret;
      50             : 
      51          59 :   if (BUG(!addr))
      52           0 :     return -1;
      53             : 
      54          59 :   *addr = 0;
      55             : 
      56          59 :   if ((ret = tor_addr_lookup(name, AF_INET, &myaddr)))
      57             :     return ret;
      58             : 
      59           4 :   if (tor_addr_family(&myaddr) == AF_INET) {
      60           4 :     *addr = tor_addr_to_ipv4h(&myaddr);
      61           4 :     return ret;
      62             :   }
      63             : 
      64             :   return -1;
      65             : }
      66             : 
      67             : #ifdef HAVE_GETADDRINFO
      68             : 
      69             : /* Host lookup helper for tor_addr_lookup(), when getaddrinfo() is
      70             :  * available on this system.
      71             :  *
      72             :  * See tor_addr_lookup() for details.
      73             :  */
      74           3 : MOCK_IMPL(STATIC int,
      75             : tor_addr_lookup_host_impl,(const char *name,
      76             :                           uint16_t family,
      77             :                           tor_addr_t *addr))
      78             : {
      79           3 :   int err;
      80           3 :   struct addrinfo *res=NULL, *res_p;
      81           3 :   struct addrinfo *best=NULL;
      82           3 :   struct addrinfo hints;
      83           3 :   int result = -1;
      84           3 :   memset(&hints, 0, sizeof(hints));
      85           3 :   hints.ai_family = family;
      86           3 :   hints.ai_socktype = SOCK_STREAM;
      87           3 :   err = tor_getaddrinfo(name, NULL, &hints, &res);
      88             :   /* The check for 'res' here shouldn't be necessary, but it makes static
      89             :    * analysis tools happy. */
      90           3 :   if (!err && res) {
      91             :     best = NULL;
      92           0 :     for (res_p = res; res_p; res_p = res_p->ai_next) {
      93           0 :       if (family == AF_UNSPEC) {
      94           0 :         if (res_p->ai_family == AF_INET) {
      95             :           best = res_p;
      96             :           break;
      97           0 :         } else if (res_p->ai_family == AF_INET6 && !best) {
      98           0 :           best = res_p;
      99             :         }
     100           0 :       } else if (family == res_p->ai_family) {
     101             :         best = res_p;
     102             :         break;
     103             :       }
     104             :     }
     105           0 :     if (!best)
     106           0 :       best = res;
     107           0 :     if (best->ai_family == AF_INET) {
     108           0 :       tor_addr_from_in(addr,
     109           0 :                        &((struct sockaddr_in*)best->ai_addr)->sin_addr);
     110           0 :       result = 0;
     111           0 :     } else if (best->ai_family == AF_INET6) {
     112           0 :       tor_addr_from_in6(addr,
     113           0 :                         &((struct sockaddr_in6*)best->ai_addr)->sin6_addr);
     114           0 :       result = 0;
     115             :     }
     116           0 :     tor_freeaddrinfo(res);
     117           0 :     return result;
     118             :   }
     119           3 :   return (err == EAI_AGAIN) ? 1 : -1;
     120             : }
     121             : 
     122             : #else /* !defined(HAVE_GETADDRINFO) */
     123             : 
     124             : /* Host lookup helper for tor_addr_lookup(), which calls gethostbyname().
     125             :  * Used when getaddrinfo() is not available on this system.
     126             :  *
     127             :  * See tor_addr_lookup() for details.
     128             :  */
     129             : MOCK_IMPL(STATIC int,
     130             : tor_addr_lookup_host_impl,(const char *name,
     131             :                           uint16_t family,
     132             :                            tor_addr_t *addr))
     133             : {
     134             :   (void) family;
     135             :   struct hostent *ent;
     136             :   int err;
     137             : #ifdef HAVE_GETHOSTBYNAME_R_6_ARG
     138             :   char buf[2048];
     139             :   struct hostent hostent;
     140             :   int r;
     141             :   r = gethostbyname_r(name, &hostent, buf, sizeof(buf), &ent, &err);
     142             : #elif defined(HAVE_GETHOSTBYNAME_R_5_ARG)
     143             :   char buf[2048];
     144             :   struct hostent hostent;
     145             :   ent = gethostbyname_r(name, &hostent, buf, sizeof(buf), &err);
     146             : #elif defined(HAVE_GETHOSTBYNAME_R_3_ARG)
     147             :   struct hostent_data data;
     148             :   struct hostent hent;
     149             :   memset(&data, 0, sizeof(data));
     150             :   err = gethostbyname_r(name, &hent, &data);
     151             :   ent = err ? NULL : &hent;
     152             : #else
     153             :   ent = gethostbyname(name);
     154             : #ifdef _WIN32
     155             :   err = WSAGetLastError();
     156             : #else
     157             :   err = h_errno;
     158             : #endif /* defined(_WIN32) */
     159             : #endif /* defined(HAVE_GETHOSTBYNAME_R_6_ARG) || ... */
     160             :   if (ent) {
     161             :     if (ent->h_addrtype == AF_INET) {
     162             :       tor_addr_from_in(addr, (struct in_addr*) ent->h_addr);
     163             :     } else if (ent->h_addrtype == AF_INET6) {
     164             :       tor_addr_from_in6(addr, (struct in6_addr*) ent->h_addr);
     165             :     } else {
     166             :       tor_assert(0); // LCOV_EXCL_LINE: gethostbyname() returned bizarre type
     167             :     }
     168             :     return 0;
     169             :   }
     170             : #ifdef _WIN32
     171             :   return (err == WSATRY_AGAIN) ? 1 : -1;
     172             : #else
     173             :   return (err == TRY_AGAIN) ? 1 : -1;
     174             : #endif
     175             : }
     176             : #endif /* defined(HAVE_GETADDRINFO) */
     177             : 
     178             : /** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set
     179             :  * *<b>addr</b> to the proper IP address and family. The <b>family</b>
     180             :  * argument (which must be AF_INET, AF_INET6, or AF_UNSPEC) declares a
     181             :  * <i>preferred</i> family, though another one may be returned if only one
     182             :  * family is implemented for this address.
     183             :  *
     184             :  * Like tor_addr_parse(), this function accepts IPv6 addresses with or without
     185             :  * square brackets.
     186             :  *
     187             :  * Return 0 on success, -1 on failure; 1 on transient failure.
     188             :  */
     189        2527 : MOCK_IMPL(int,
     190             : tor_addr_lookup,(const char *name, uint16_t family, tor_addr_t *addr))
     191             : {
     192             :   /* Perhaps eventually this should be replaced by a tor_getaddrinfo or
     193             :    * something.
     194             :    */
     195        2527 :   int parsed_family = 0;
     196        2527 :   int result = -1;
     197             : 
     198        2527 :   tor_assert(name);
     199        2527 :   tor_assert(addr);
     200        2527 :   tor_assert(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC);
     201             : 
     202        2527 :   if (!*name) {
     203             :     /* Empty address is an error. */
     204           5 :     goto permfail;
     205             :   }
     206             : 
     207             :   /* Is it an IP address? */
     208        2522 :   parsed_family = tor_addr_parse(addr, name);
     209             : 
     210        2522 :   if (parsed_family >= 0) {
     211             :     /* If the IP address family matches, or was unspecified */
     212        2333 :     if (parsed_family == family || family == AF_UNSPEC) {
     213        2303 :       goto success;
     214             :     } else {
     215          30 :       goto permfail;
     216             :     }
     217             :   } else {
     218             :     /* Clear the address after a failed tor_addr_parse(). */
     219         189 :     memset(addr, 0, sizeof(tor_addr_t));
     220         189 :     result = tor_addr_lookup_host_impl(name, family, addr);
     221         189 :     goto done;
     222             :   }
     223             : 
     224             :  /* If we weren't successful, and haven't already set the result,
     225             :   * assume it's a permanent failure */
     226          35 :  permfail:
     227          35 :   result = -1;
     228          35 :   goto done;
     229        2303 :  success:
     230        2303 :   result = 0;
     231             : 
     232             :  /* We have set the result, now it's time to clean up */
     233         189 :  done:
     234        2527 :   if (result) {
     235             :     /* Clear the address on error */
     236         211 :     memset(addr, 0, sizeof(tor_addr_t));
     237             :   }
     238        2527 :   return result;
     239             : }
     240             : 
     241             : /** Parse an address or address-port combination from <b>s</b>, resolve the
     242             :  * address as needed, and put the result in <b>addr_out</b> and (optionally)
     243             :  * <b>port_out</b>.
     244             :  *
     245             :  * Like tor_addr_port_parse(), this function accepts:
     246             :  *  - IPv6 address and port, when the IPv6 address is in square brackets,
     247             :  *  - IPv6 address with square brackets,
     248             :  *  - IPv6 address without square brackets.
     249             :  *
     250             :  * Return 0 on success, negative on failure. */
     251             : int
     252         275 : tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out)
     253             : {
     254         275 :   tor_addr_t addr;
     255         275 :   uint16_t portval = 0;
     256         275 :   char *tmp = NULL;
     257         275 :   int rv = 0;
     258         275 :   int result;
     259             : 
     260         275 :   tor_assert(s);
     261         275 :   tor_assert(addr_out);
     262             : 
     263         275 :   s = eat_whitespace(s);
     264             : 
     265             :   /* Try parsing s as an address:port first, so we don't have to duplicate
     266             :    * the logic that rejects IPv6:Port with no square brackets. */
     267         275 :   rv = tor_addr_port_parse(LOG_WARN, s, &addr, &portval, 0);
     268             :   /* That was easy, no DNS required. */
     269         275 :   if (rv == 0)
     270         220 :     goto success;
     271             : 
     272             :   /* Now let's check for malformed IPv6 addresses and ports:
     273             :    * tor_addr_port_parse() requires squared brackes if there is a port,
     274             :    * and we want tor_addr_port_lookup() to have the same requirement.
     275             :    * But we strip the port using tor_addr_port_split(), so tor_addr_lookup()
     276             :    * only sees the address, and will accept it without square brackets. */
     277          55 :   int family = tor_addr_parse(&addr, s);
     278             :   /* If tor_addr_parse() succeeds where tor_addr_port_parse() failed, we need
     279             :    * to reject this address as malformed. */
     280          55 :   if (family >= 0) {
     281             :     /* Double-check it's an IPv6 address. If not, we have a parsing bug.
     282             :      */
     283           0 :     tor_assertf_nonfatal(family == AF_INET6,
     284             :                          "Wrong family: %d (should be IPv6: %d) which "
     285             :                          "failed IP:port parsing, but passed IP parsing. "
     286             :                          "input string: '%s'; parsed address: '%s'.",
     287             :                          family, AF_INET6, s, fmt_addr(&addr));
     288           0 :     goto err;
     289             :   }
     290             : 
     291             :   /* Now we have a hostname. Let's split off the port, if any. */
     292          55 :   rv = tor_addr_port_split(LOG_WARN, s, &tmp, &portval);
     293          55 :   if (rv < 0)
     294          22 :     goto err;
     295             : 
     296             :   /* And feed the hostname to the lookup function. */
     297          33 :   if (tor_addr_lookup(tmp, AF_UNSPEC, &addr) != 0)
     298          24 :     goto err;
     299             : 
     300           9 :  success:
     301         229 :   if (port_out)
     302         229 :     *port_out = portval;
     303         229 :   tor_addr_copy(addr_out, &addr);
     304         229 :   result = 0;
     305         229 :   goto done;
     306             : 
     307          46 :  err:
     308             :   /* Clear the address and port on error */
     309          46 :   memset(addr_out, 0, sizeof(tor_addr_t));
     310          46 :   if (port_out)
     311          46 :     *port_out = 0;
     312             :   result = -1;
     313             : 
     314             :  /* We have set the result, now it's time to clean up */
     315         275 :  done:
     316         275 :   tor_free(tmp);
     317         275 :   return result;
     318             : }
     319             : 
     320             : #ifdef USE_SANDBOX_GETADDRINFO
     321             : /** True if we should only return cached values */
     322             : static int sandbox_getaddrinfo_is_active = 0;
     323             : 
     324             : /** Cache entry for getaddrinfo results; used when sandboxing is implemented
     325             :  * so that we can consult the cache when the sandbox prevents us from doing
     326             :  * getaddrinfo.
     327             :  *
     328             :  * We support only a limited range of getaddrinfo calls, where servname is null
     329             :  * and hints contains only socktype=SOCK_STREAM, family in INET,INET6,UNSPEC.
     330             :  */
     331             : typedef struct cached_getaddrinfo_item_t {
     332             :   HT_ENTRY(cached_getaddrinfo_item_t) node;
     333             :   char *name;
     334             :   int family;
     335             :   /** set if no error; otherwise NULL */
     336             :   struct addrinfo *res;
     337             :   /** 0 for no error; otherwise an EAI_* value */
     338             :   int err;
     339             : } cached_getaddrinfo_item_t;
     340             : 
     341             : static unsigned
     342           6 : cached_getaddrinfo_item_hash(const cached_getaddrinfo_item_t *item)
     343             : {
     344           6 :   return (unsigned)siphash24g(item->name, strlen(item->name)) + item->family;
     345             : }
     346             : 
     347             : static unsigned
     348           0 : cached_getaddrinfo_items_eq(const cached_getaddrinfo_item_t *a,
     349             :                             const cached_getaddrinfo_item_t *b)
     350             : {
     351           0 :   return (a->family == b->family) && 0 == strcmp(a->name, b->name);
     352             : }
     353             : 
     354             : #define cached_getaddrinfo_item_free(item)              \
     355             :   FREE_AND_NULL(cached_getaddrinfo_item_t,              \
     356             :                 cached_getaddrinfo_item_free_, (item))
     357             : 
     358             : static void
     359           2 : cached_getaddrinfo_item_free_(cached_getaddrinfo_item_t *item)
     360             : {
     361           2 :   if (item == NULL)
     362             :     return;
     363             : 
     364           2 :   tor_free(item->name);
     365           2 :   if (item->res)
     366           0 :     freeaddrinfo(item->res);
     367           2 :   tor_free(item);
     368             : }
     369             : 
     370             : static HT_HEAD(getaddrinfo_cache, cached_getaddrinfo_item_t)
     371             :      getaddrinfo_cache = HT_INITIALIZER();
     372             : 
     373         585 : HT_PROTOTYPE(getaddrinfo_cache, cached_getaddrinfo_item_t, node,
     374             :              cached_getaddrinfo_item_hash,
     375             :              cached_getaddrinfo_items_eq);
     376         238 : HT_GENERATE2(getaddrinfo_cache, cached_getaddrinfo_item_t, node,
     377             :              cached_getaddrinfo_item_hash,
     378             :              cached_getaddrinfo_items_eq,
     379             :              0.6, tor_reallocarray_, tor_free_);
     380             : 
     381             : /** If true, don't try to cache getaddrinfo results. */
     382             : static int sandbox_getaddrinfo_cache_disabled = 0;
     383             : 
     384             : /** Tell the sandbox layer not to try to cache getaddrinfo results. Used as in
     385             :  * tor-resolve, when we have no intention of initializing crypto or of
     386             :  * installing the sandbox.*/
     387             : void
     388          22 : sandbox_disable_getaddrinfo_cache(void)
     389             : {
     390          22 :   sandbox_getaddrinfo_cache_disabled = 1;
     391          22 : }
     392             : 
     393             : void
     394           0 : tor_freeaddrinfo(struct addrinfo *ai)
     395             : {
     396           0 :   if (sandbox_getaddrinfo_cache_disabled)
     397           0 :     freeaddrinfo(ai);
     398           0 : }
     399             : 
     400             : int
     401           3 : tor_getaddrinfo(const char *name, const char *servname,
     402             :                 const struct addrinfo *hints,
     403             :                 struct addrinfo **res)
     404             : {
     405           3 :   int err;
     406           3 :   struct cached_getaddrinfo_item_t search, *item;
     407             : 
     408           3 :   if (sandbox_getaddrinfo_cache_disabled) {
     409           0 :     return getaddrinfo(name, NULL, hints, res);
     410             :   }
     411             : 
     412           3 :   if (servname != NULL) {
     413           0 :     log_warn(LD_BUG, "called with non-NULL servname");
     414           0 :     return EAI_NONAME;
     415             :   }
     416           3 :   if (name == NULL) {
     417           0 :     log_warn(LD_BUG, "called with NULL name");
     418           0 :     return EAI_NONAME;
     419             :   }
     420             : 
     421           3 :   *res = NULL;
     422             : 
     423           3 :   memset(&search, 0, sizeof(search));
     424           3 :   search.name = (char *) name;
     425           3 :   search.family = hints ? hints->ai_family : AF_UNSPEC;
     426           3 :   item = HT_FIND(getaddrinfo_cache, &getaddrinfo_cache, &search);
     427             : 
     428           3 :   if (! sandbox_getaddrinfo_is_active) {
     429             :     /* If the sandbox is not turned on yet, then getaddrinfo and store the
     430             :        result. */
     431             : 
     432           3 :     err = getaddrinfo(name, NULL, hints, res);
     433           3 :     log_info(LD_NET,"(Sandbox) getaddrinfo %s.", err ? "failed" : "succeeded");
     434             : 
     435           3 :     if (! item) {
     436           3 :       item = tor_malloc_zero(sizeof(*item));
     437           3 :       item->name = tor_strdup(name);
     438           3 :       item->family = hints ? hints->ai_family : AF_UNSPEC;
     439           3 :       HT_INSERT(getaddrinfo_cache, &getaddrinfo_cache, item);
     440             :     }
     441             : 
     442           3 :     if (item->res) {
     443           0 :       freeaddrinfo(item->res);
     444           0 :       item->res = NULL;
     445             :     }
     446           3 :     item->res = *res;
     447           3 :     item->err = err;
     448           3 :     return err;
     449             :   }
     450             : 
     451             :   /* Otherwise, the sandbox is on.  If we have an item, yield its cached
     452             :      result. */
     453           0 :   if (item) {
     454           0 :     *res = item->res;
     455           0 :     return item->err;
     456             :   }
     457             : 
     458             :   /* getting here means something went wrong */
     459           0 :   log_err(LD_BUG,"(Sandbox) failed to get address %s!", name);
     460           0 :   return EAI_NONAME;
     461             : }
     462             : 
     463             : int
     464           0 : tor_add_addrinfo(const char *name)
     465             : {
     466           0 :   struct addrinfo *res;
     467           0 :   struct addrinfo hints;
     468           0 :   int i;
     469           0 :   static const int families[] = { AF_INET, AF_INET6, AF_UNSPEC };
     470             : 
     471           0 :   memset(&hints, 0, sizeof(hints));
     472           0 :   hints.ai_socktype = SOCK_STREAM;
     473           0 :   for (i = 0; i < 3; ++i) {
     474           0 :     hints.ai_family = families[i];
     475             : 
     476           0 :     res = NULL;
     477           0 :     (void) tor_getaddrinfo(name, NULL, &hints, &res);
     478           0 :     if (res)
     479           0 :       tor_freeaddrinfo(res);
     480             :   }
     481             : 
     482           0 :   return 0;
     483             : }
     484             : 
     485             : void
     486         235 : tor_free_getaddrinfo_cache(void)
     487             : {
     488         235 :   cached_getaddrinfo_item_t **next, **item, *this;
     489             : 
     490         235 :   for (item = HT_START(getaddrinfo_cache, &getaddrinfo_cache);
     491         237 :        item;
     492           2 :        item = next) {
     493           2 :     this = *item;
     494           2 :     next = HT_NEXT_RMV(getaddrinfo_cache, &getaddrinfo_cache, item);
     495           2 :     cached_getaddrinfo_item_free(this);
     496             :   }
     497             : 
     498         235 :   HT_CLEAR(getaddrinfo_cache, &getaddrinfo_cache);
     499         235 : }
     500             : 
     501             : void
     502           0 : tor_make_getaddrinfo_cache_active(void)
     503             : {
     504           0 :   sandbox_getaddrinfo_is_active = 1;
     505           0 : }
     506             : #else /* !defined(USE_SANDBOX_GETADDRINFO) */
     507             : void
     508             : sandbox_disable_getaddrinfo_cache(void)
     509             : {
     510             : }
     511             : void
     512             : tor_make_getaddrinfo_cache_active(void)
     513             : {
     514             : }
     515             : #endif /* defined(USE_SANDBOX_GETADDRINFO) */

Generated by: LCOV version 1.14