Browse Source

Merge branch 'ticket26526_26532'

Nick Mathewson 5 years ago
parent
commit
422abd4fa3

+ 4 - 0
changes/ticket26526

@@ -0,0 +1,4 @@
+  o Code simplification and refactoring:
+    - Utility functions that can perform a DNS lookup are now wholly
+      separated from those that can't, in separate headers and C
+      modules. Closes ticket 26526.

+ 3 - 0
changes/ticket26526_extra

@@ -0,0 +1,3 @@
+  o Minor features (tor-resolve):
+    - The tor-resolve utility can now be used with IPv6 SOCKS proxies.
+      Side-effect of the refactoring for ticket 26526.

+ 3 - 11
src/app/config/config.c

@@ -106,6 +106,7 @@
 #include "feature/client/transports.h"
 #include "feature/relay/ext_orport.h"
 #include "feature/dircommon/voting_schedule.h"
+#include "lib/net/resolve.h"
 #ifdef _WIN32
 #include <shlobj.h>
 #endif
@@ -6459,26 +6460,17 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
   addrport = smartlist_get(items, 0);
   smartlist_del_keeporder(items, 0);
 
-  const char *addrport_sep = strchr(addrport, ':');
-  if (!addrport_sep) {
-    log_warn(LD_CONFIG, "Error parsing DirAuthority address '%s' "
-                        "(':' not found)", addrport);
+  if (tor_addr_port_split(LOG_WARN, addrport, &address, &dir_port) < 0) {
+    log_warn(LD_CONFIG, "Error parsing DirAuthority address '%s'.", addrport);
     goto err;
   }
 
-  address = tor_strndup(addrport, addrport_sep - addrport);
   if (!string_is_valid_ipv4_address(address)) {
     log_warn(LD_CONFIG, "Error parsing DirAuthority address '%s' "
                         "(invalid IPv4 address)", address);
     goto err;
   }
 
-  tor_free(address);
-
-  if (addr_port_lookup(LOG_WARN, addrport, &address, NULL, &dir_port)<0) {
-    log_warn(LD_CONFIG, "Error parsing DirAuthority address '%s'", addrport);
-    goto err;
-  }
   if (!dir_port) {
     log_warn(LD_CONFIG, "Missing port in DirAuthority address '%s'",addrport);
     goto err;

+ 1 - 0
src/app/config/statefile.c

@@ -43,6 +43,7 @@
 #include "lib/sandbox/sandbox.h"
 #include "app/config/statefile.h"
 #include "lib/encoding/confline.h"
+#include "lib/net/resolve.h"
 
 #include "app/config/or_state_st.h"
 

+ 1 - 0
src/core/mainloop/main.c

@@ -116,6 +116,7 @@
 #include "lib/sandbox/sandbox.h"
 #include "lib/fs/lockfile.h"
 #include "lib/net/buffers_net.h"
+#include "lib/net/resolve.h"
 #include "lib/tls/tortls.h"
 #include "lib/evloop/compat_libevent.h"
 #include "lib/encoding/confline.h"

+ 1 - 3
src/core/or/or.h

@@ -49,9 +49,7 @@
 #include "lib/log/util_bug.h"
 #include "lib/malloc/util_malloc.h"
 #include "lib/net/address.h"
-#include "lib/net/ipv4.h"
-#include "lib/net/ipv6.h"
-#include "lib/net/resolve.h"
+#include "lib/net/inaddr.h"
 #include "lib/net/socket.h"
 #include "lib/string/compat_ctype.h"
 #include "lib/string/compat_string.h"

+ 1 - 0
src/feature/nodelist/routerlist.c

@@ -123,6 +123,7 @@
 #include "lib/sandbox/sandbox.h"
 #include "feature/nodelist/torcert.h"
 #include "lib/math/fp.h"
+#include "lib/net/resolve.h"
 
 #include "feature/dirauth/dirvote.h"
 #include "feature/dirauth/mode.h"

+ 1 - 0
src/feature/rend/rendservice.c

@@ -37,6 +37,7 @@
 #include "feature/nodelist/routerparse.h"
 #include "feature/nodelist/routerset.h"
 #include "lib/encoding/confline.h"
+#include "lib/net/resolve.h"
 
 #include "core/or/cpath_build_state_st.h"
 #include "core/or/crypt_path_st.h"

+ 9 - 214
src/lib/net/address.c

@@ -6,6 +6,9 @@
 /**
  * \file address.c
  * \brief Functions to use and manipulate the tor_addr_t structure.
+ *
+ * This module doesn't have any support for the libc resolver: that is all in
+ * resolve.c.
  **/
 
 #define ADDRESS_PRIVATE
@@ -37,13 +40,12 @@
 
 #include "lib/net/address.h"
 #include "lib/net/socket.h"
-#include "lib/net/resolve.h"
 #include "lib/container/smartlist.h"
 #include "lib/ctime/di_ops.h"
 #include "lib/log/torlog.h"
 #include "lib/log/escape.h"
 #include "lib/malloc/util_malloc.h"
-#include "lib/net/ipv4.h"
+#include "lib/net/inaddr.h"
 #include "lib/string/compat_ctype.h"
 #include "lib/string/compat_string.h"
 #include "lib/string/parse_int.h"
@@ -234,127 +236,6 @@ tor_addr_make_null(tor_addr_t *a, sa_family_t family)
   a->family = family;
 }
 
-/** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set
- * *<b>addr</b> to the proper IP address and family. The <b>family</b>
- * argument (which must be AF_INET, AF_INET6, or AF_UNSPEC) declares a
- * <i>preferred</i> family, though another one may be returned if only one
- * family is implemented for this address.
- *
- * Return 0 on success, -1 on failure; 1 on transient failure.
- */
-MOCK_IMPL(int,
-tor_addr_lookup,(const char *name, uint16_t family, tor_addr_t *addr))
-{
-  /* Perhaps eventually this should be replaced by a tor_getaddrinfo or
-   * something.
-   */
-  struct in_addr iaddr;
-  struct in6_addr iaddr6;
-  tor_assert(name);
-  tor_assert(addr);
-  tor_assert(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC);
-  if (!*name) {
-    /* Empty address is an error. */
-    return -1;
-  } else if (tor_inet_pton(AF_INET, name, &iaddr)) {
-    /* It's an IPv4 IP. */
-    if (family == AF_INET6)
-      return -1;
-    tor_addr_from_in(addr, &iaddr);
-    return 0;
-  } else if (tor_inet_pton(AF_INET6, name, &iaddr6)) {
-    if (family == AF_INET)
-      return -1;
-    tor_addr_from_in6(addr, &iaddr6);
-    return 0;
-  } else {
-#ifdef HAVE_GETADDRINFO
-    int err;
-    struct addrinfo *res=NULL, *res_p;
-    struct addrinfo *best=NULL;
-    struct addrinfo hints;
-    int result = -1;
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_family = family;
-    hints.ai_socktype = SOCK_STREAM;
-    err = tor_getaddrinfo(name, NULL, &hints, &res);
-    /* The check for 'res' here shouldn't be necessary, but it makes static
-     * analysis tools happy. */
-    if (!err && res) {
-      best = NULL;
-      for (res_p = res; res_p; res_p = res_p->ai_next) {
-        if (family == AF_UNSPEC) {
-          if (res_p->ai_family == AF_INET) {
-            best = res_p;
-            break;
-          } else if (res_p->ai_family == AF_INET6 && !best) {
-            best = res_p;
-          }
-        } else if (family == res_p->ai_family) {
-          best = res_p;
-          break;
-        }
-      }
-      if (!best)
-        best = res;
-      if (best->ai_family == AF_INET) {
-        tor_addr_from_in(addr,
-                         &((struct sockaddr_in*)best->ai_addr)->sin_addr);
-        result = 0;
-      } else if (best->ai_family == AF_INET6) {
-        tor_addr_from_in6(addr,
-                          &((struct sockaddr_in6*)best->ai_addr)->sin6_addr);
-        result = 0;
-      }
-      tor_freeaddrinfo(res);
-      return result;
-    }
-    return (err == EAI_AGAIN) ? 1 : -1;
-#else /* !(defined(HAVE_GETADDRINFO)) */
-    struct hostent *ent;
-    int err;
-#ifdef HAVE_GETHOSTBYNAME_R_6_ARG
-    char buf[2048];
-    struct hostent hostent;
-    int r;
-    r = gethostbyname_r(name, &hostent, buf, sizeof(buf), &ent, &err);
-#elif defined(HAVE_GETHOSTBYNAME_R_5_ARG)
-    char buf[2048];
-    struct hostent hostent;
-    ent = gethostbyname_r(name, &hostent, buf, sizeof(buf), &err);
-#elif defined(HAVE_GETHOSTBYNAME_R_3_ARG)
-    struct hostent_data data;
-    struct hostent hent;
-    memset(&data, 0, sizeof(data));
-    err = gethostbyname_r(name, &hent, &data);
-    ent = err ? NULL : &hent;
-#else
-    ent = gethostbyname(name);
-#ifdef _WIN32
-    err = WSAGetLastError();
-#else
-    err = h_errno;
-#endif
-#endif /* defined(HAVE_GETHOSTBYNAME_R_6_ARG) || ... */
-    if (ent) {
-      if (ent->h_addrtype == AF_INET) {
-        tor_addr_from_in(addr, (struct in_addr*) ent->h_addr);
-      } else if (ent->h_addrtype == AF_INET6) {
-        tor_addr_from_in6(addr, (struct in6_addr*) ent->h_addr);
-      } else {
-        tor_assert(0); // LCOV_EXCL_LINE: gethostbyname() returned bizarre type
-      }
-      return 0;
-    }
-#ifdef _WIN32
-    return (err == WSATRY_AGAIN) ? 1 : -1;
-#else
-    return (err == TRY_AGAIN) ? 1 : -1;
-#endif
-#endif /* defined(HAVE_GETADDRINFO) */
-  }
-}
-
 /** Return true iff <b>ip</b> is an IP reserved to localhost or local networks
  * in RFC1918 or RFC4193 or RFC4291. (fec0::/10, deprecated by RFC3879, is
  * also treated as internal for now.)
@@ -1324,64 +1205,6 @@ tor_addr_parse(tor_addr_t *addr, const char *src)
   return result;
 }
 
-/** Parse an address or address-port combination from <b>s</b>, resolve the
- * address as needed, and put the result in <b>addr_out</b> and (optionally)
- * <b>port_out</b>.  Return 0 on success, negative on failure. */
-int
-tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out)
-{
-  const char *port;
-  tor_addr_t addr;
-  uint16_t portval;
-  char *tmp = NULL;
-
-  tor_assert(s);
-  tor_assert(addr_out);
-
-  s = eat_whitespace(s);
-
-  if (*s == '[') {
-    port = strstr(s, "]");
-    if (!port)
-      goto err;
-    tmp = tor_strndup(s+1, port-(s+1));
-    port = port+1;
-    if (*port == ':')
-      port++;
-    else
-      port = NULL;
-  } else {
-    port = strchr(s, ':');
-    if (port)
-      tmp = tor_strndup(s, port-s);
-    else
-      tmp = tor_strdup(s);
-    if (port)
-      ++port;
-  }
-
-  if (tor_addr_lookup(tmp, AF_UNSPEC, &addr) != 0)
-    goto err;
-  tor_free(tmp);
-
-  if (port) {
-    portval = (int) tor_parse_long(port, 10, 1, 65535, NULL, NULL);
-    if (!portval)
-      goto err;
-  } else {
-    portval = 0;
-  }
-
-  if (port_out)
-    *port_out = portval;
-  tor_addr_copy(addr_out, &addr);
-
-  return 0;
- err:
-  tor_free(tmp);
-  return -1;
-}
-
 #ifdef _WIN32
 typedef ULONG (WINAPI *GetAdaptersAddresses_fn_t)(
               ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG);
@@ -1927,7 +1750,7 @@ tor_addr_port_split(int severity, const char *addrport,
   tor_assert(addrport);
   tor_assert(address_out);
   tor_assert(port_out);
-  /* We need to check for IPv6 manually because addr_port_lookup() doesn't
+  /* We need to check for IPv6 manually because the logic below doesn't
    * do a good job on IPv6 addresses that lack a port. */
   if (tor_addr_parse(&a_tmp, addrport) == AF_INET6) {
     *port_out = 0;
@@ -1935,30 +1758,11 @@ tor_addr_port_split(int severity, const char *addrport,
     return 0;
   }
 
-  return addr_port_lookup(severity, addrport, address_out, NULL, port_out);
-}
-
-/** Parse a string of the form "host[:port]" from <b>addrport</b>.  If
- * <b>address</b> is provided, set *<b>address</b> to a copy of the
- * host portion of the string.  If <b>addr</b> is provided, try to
- * resolve the host portion of the string and store it into
- * *<b>addr</b> (in host byte order).  If <b>port_out</b> is provided,
- * store the port number into *<b>port_out</b>, or 0 if no port is given.
- * If <b>port_out</b> is NULL, then there must be no port number in
- * <b>addrport</b>.
- * Return 0 on success, -1 on failure.
- */
-int
-addr_port_lookup(int severity, const char *addrport, char **address,
-                uint32_t *addr, uint16_t *port_out)
-{
   const char *colon;
   char *address_ = NULL;
   int port_;
   int ok = 1;
 
-  tor_assert(addrport);
-
   colon = strrchr(addrport, ':');
   if (colon) {
     address_ = tor_strndup(addrport, colon-addrport);
@@ -1980,22 +1784,13 @@ addr_port_lookup(int severity, const char *addrport, char **address,
     port_ = 0;
   }
 
-  if (addr) {
-    /* There's an addr pointer, so we need to resolve the hostname. */
-    if (tor_lookup_hostname(address_,addr)) {
-      log_fn(severity, LD_NET, "Couldn't look up %s", escaped(address_));
-      ok = 0;
-      *addr = 0;
-    }
-  }
-
-  if (address && ok) {
-    *address = address_;
+  if (ok) {
+    *address_out = address_;
   } else {
-    if (address)
-      *address = NULL;
+    *address_out = NULL;
     tor_free(address_);
   }
+
   if (port_out)
     *port_out = ok ? ((uint16_t) port_) : 0;
 

+ 1 - 8
src/lib/net/address.h

@@ -14,7 +14,7 @@
 #include "orconfig.h"
 #include "lib/cc/torint.h"
 #include "lib/log/util_bug.h"
-#include "lib/net/ipv6.h"
+#include "lib/net/inaddr_st.h"
 #include "lib/net/nettypes.h"
 
 #ifdef HAVE_NETINET_IN_H
@@ -204,8 +204,6 @@ tor_addr_eq_ipv4h(const tor_addr_t *a, uint32_t u)
  */
 #define TOR_ADDR_BUF_LEN 48
 
-MOCK_DECL(int, tor_addr_lookup,(const char *name, uint16_t family,
-                                tor_addr_t *addr_out));
 char *tor_addr_to_str_dup(const tor_addr_t *addr) ATTR_MALLOC;
 
 /** Wrapper function of fmt_addr_impl(). It does not decorate IPv6
@@ -263,9 +261,6 @@ int tor_addr_to_PTR_name(char *out, size_t outlen,
 int tor_addr_parse_PTR_name(tor_addr_t *result, const char *address,
                                        int family, int accept_regular);
 
-int tor_addr_port_lookup(const char *s, tor_addr_t *addr_out,
-                        uint16_t *port_out);
-
 /* Does the address * yield an AF_UNSPEC wildcard address (1),
  * which expands to corresponding wildcard IPv4 and IPv6 rules, and do we
  * allow *4 and *6 for IPv4 and IPv6 wildcards, respectively;
@@ -330,8 +325,6 @@ int tor_addr_port_parse(int severity, const char *addrport,
 int tor_addr_hostname_is_local(const char *name);
 
 /* IPv4 helpers */
-int addr_port_lookup(int severity, const char *addrport, char **address,
-                    uint32_t *addr, uint16_t *port_out);
 int parse_port_range(const char *port, uint16_t *port_min_out,
                      uint16_t *port_max_out);
 int addr_mask_get_bits(uint32_t mask);

+ 48 - 9
src/lib/net/ipv6.c → src/lib/net/inaddr.c

@@ -4,20 +4,20 @@
 /* See LICENSE for licensing information */
 
 /**
- * \file ipv6.c
- * \brief Functions for encoding and decoding IPv6 addresses
- *
- * (Because these functions are generic, they can also handle IPv4 addresses).
+ * \file inaddr.c
+ * \brief Convert in_addr and in6_addr to and from strings.
  **/
 
-#include "lib/net/ipv6.h"
-#include "lib/net/ipv4.h"
-#include "lib/string/util_string.h"
-#include "lib/string/compat_string.h"
+#include "lib/net/inaddr.h"
+
+#include "lib/cc/torint.h"
+#include "lib/log/util_bug.h"
+#include "lib/net/inaddr_st.h"
 #include "lib/string/compat_ctype.h"
+#include "lib/string/compat_string.h"
 #include "lib/string/printf.h"
 #include "lib/string/scanf.h"
-#include "lib/log/util_bug.h"
+#include "lib/string/util_string.h"
 
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
@@ -26,6 +26,45 @@
 #include <stdlib.h>
 #include <string.h>
 
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+
+/** Set *addr to the IP address (in dotted-quad notation) stored in *str.
+ * Return 1 on success, 0 if *str is badly formatted.
+ * (Like inet_aton(str,addr), but works on Windows and Solaris.)
+ */
+int
+tor_inet_aton(const char *str, struct in_addr* addr)
+{
+  unsigned a,b,c,d;
+  char more;
+  if (tor_sscanf(str, "%3u.%3u.%3u.%3u%c", &a,&b,&c,&d,&more) != 4)
+    return 0;
+  if (a > 255) return 0;
+  if (b > 255) return 0;
+  if (c > 255) return 0;
+  if (d > 255) return 0;
+  addr->s_addr = htonl((a<<24) | (b<<16) | (c<<8) | d);
+  return 1;
+}
+
+/** Given an IPv4 in_addr struct *<b>in</b> (in network order, as usual),
+ *  write it as a string into the <b>buf_len</b>-byte buffer in
+ *  <b>buf</b>. Returns a non-negative integer on success.
+ *  Returns -1 on failure.
+ */
+int
+tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len)
+{
+  uint32_t a = ntohl(in->s_addr);
+  return tor_snprintf(buf, buf_len, "%d.%d.%d.%d",
+                      (int)(uint8_t)((a>>24)&0xff),
+                      (int)(uint8_t)((a>>16)&0xff),
+                      (int)(uint8_t)((a>>8 )&0xff),
+                      (int)(uint8_t)((a    )&0xff));
+}
+
 /** Given <b>af</b>==AF_INET and <b>src</b> a struct in_addr, or
  * <b>af</b>==AF_INET6 and <b>src</b> a struct in6_addr, try to format the
  * address and store it in the <b>len</b>-byte buffer <b>dst</b>.  Returns

+ 10 - 4
src/lib/net/ipv4.h → src/lib/net/inaddr.h

@@ -4,18 +4,24 @@
 /* See LICENSE for licensing information */
 
 /**
- * \file ipv4.h
- * \brief Header for ipv4.c
+ * \file inaddr.h
+ * \brief Header for inaddr.c.
  **/
-#ifndef TOR_IPV4_H
-#define TOR_IPV4_H
 
+#ifndef TOR_INADDR_H
+#define TOR_INADDR_H
+
+#include "orconfig.h"
 #include <stddef.h>
 
 struct in_addr;
+
 int tor_inet_aton(const char *str, struct in_addr *addr);
 /** Length of a buffer to allocate to hold the results of tor_inet_ntoa.*/
 #define INET_NTOA_BUF_LEN 16
 int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len);
 
+const char *tor_inet_ntop(int af, const void *src, char *dst, size_t len);
+int tor_inet_pton(int af, const char *src, void *dst);
+
 #endif

+ 11 - 10
src/lib/net/ipv6.h → src/lib/net/inaddr_st.h

@@ -4,25 +4,29 @@
 /* See LICENSE for licensing information */
 
 /**
- * \file ipv6.h
- * \brief Header for ipv6.c
+ * \file inaddr_st.h
+ *
+ * \brief Define in6_addr, its members, and related types on platforms that
+ *    lack it.
  **/
 
-#ifndef TOR_IPV6_H
-#define TOR_IPV6_H
+#ifndef TOR_INADDR_ST_H
+#define TOR_INADDR_ST_H
 
-#include "orconfig.h"
-#include <stddef.h>
 #ifdef HAVE_NETINET_IN6_H
 #include <netinet/in6.h>
 #endif
+
 #ifdef _WIN32
 #include <winsock2.h>
 #include <ws2tcpip.h>
 #include <windows.h>
 #endif
+
 #include "lib/cc/torint.h"
 
+struct in_addr;
+
 /** Implementation of struct in6_addr for platforms that do not have it.
  * Generally, these platforms are ones without IPv6 support, but we want to
  * have a working in6_addr there anyway, so we can use it to parse IPv6
@@ -85,7 +89,4 @@ struct sockaddr_in6 {
 };
 #endif /* !defined(HAVE_STRUCT_SOCKADDR_IN6) */
 
-const char *tor_inet_ntop(int af, const void *src, char *dst, size_t len);
-int tor_inet_pton(int af, const char *src, void *dst);
-
-#endif
+#endif /* TOR_INADDR_ST_H */

+ 3 - 4
src/lib/net/include.am

@@ -10,8 +10,7 @@ src_lib_libtor_net_a_SOURCES =			\
 	src/lib/net/alertsock.c                 \
 	src/lib/net/buffers_net.c		\
 	src/lib/net/gethostname.c		\
-	src/lib/net/ipv4.c			\
-	src/lib/net/ipv6.c			\
+	src/lib/net/inaddr.c			\
 	src/lib/net/resolve.c			\
 	src/lib/net/socket.c
 
@@ -25,8 +24,8 @@ noinst_HEADERS +=				\
 	src/lib/net/alertsock.h                 \
 	src/lib/net/buffers_net.h		\
 	src/lib/net/gethostname.h		\
-	src/lib/net/ipv4.h			\
-	src/lib/net/ipv6.h			\
+	src/lib/net/inaddr.h			\
+	src/lib/net/inaddr_st.h			\
 	src/lib/net/nettypes.h			\
 	src/lib/net/resolve.h			\
 	src/lib/net/socket.h			\

+ 0 - 57
src/lib/net/ipv4.c

@@ -1,57 +0,0 @@
-/* Copyright (c) 2003-2004, Roger Dingledine
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2018, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * \file ipv4.c
- * \brief Functions for encoding and decoding IPv4 addresses into strings
- **/
-
-#include "orconfig.h"
-#include "lib/cc/torint.h"
-#include "lib/net/ipv4.h"
-#include "lib/string/printf.h"
-#include "lib/string/scanf.h"
-
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef _WIN32
-#include <winsock2.h>
-#endif
-
-/** Set *addr to the IP address (in dotted-quad notation) stored in *str.
- * Return 1 on success, 0 if *str is badly formatted.
- * (Like inet_aton(str,addr), but works on Windows and Solaris.)
- */
-int
-tor_inet_aton(const char *str, struct in_addr* addr)
-{
-  unsigned a,b,c,d;
-  char more;
-  if (tor_sscanf(str, "%3u.%3u.%3u.%3u%c", &a,&b,&c,&d,&more) != 4)
-    return 0;
-  if (a > 255) return 0;
-  if (b > 255) return 0;
-  if (c > 255) return 0;
-  if (d > 255) return 0;
-  addr->s_addr = htonl((a<<24) | (b<<16) | (c<<8) | d);
-  return 1;
-}
-
-/** Given an IPv4 in_addr struct *<b>in</b> (in network order, as usual),
- *  write it as a string into the <b>buf_len</b>-byte buffer in
- *  <b>buf</b>. Returns a non-negative integer on success.
- *  Returns -1 on failure.
- */
-int
-tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len)
-{
-  uint32_t a = ntohl(in->s_addr);
-  return tor_snprintf(buf, buf_len, "%d.%d.%d.%d",
-                      (int)(uint8_t)((a>>24)&0xff),
-                      (int)(uint8_t)((a>>16)&0xff),
-                      (int)(uint8_t)((a>>8 )&0xff),
-                      (int)(uint8_t)((a    )&0xff));
-}

+ 183 - 0
src/lib/net/resolve.c

@@ -9,8 +9,12 @@
  **/
 
 #include "lib/net/resolve.h"
+
 #include "lib/net/address.h"
+#include "lib/net/inaddr.h"
 #include "lib/malloc/util_malloc.h"
+#include "lib/string/parse_int.h"
+#include "lib/string/util_string.h"
 
 #include "siphash.h"
 #include "ht.h"
@@ -52,6 +56,185 @@ tor_lookup_hostname,(const char *name, uint32_t *addr))
   return -1;
 }
 
+/** Similar behavior to Unix gethostbyname: resolve <b>name</b>, and set
+ * *<b>addr</b> to the proper IP address and family. The <b>family</b>
+ * argument (which must be AF_INET, AF_INET6, or AF_UNSPEC) declares a
+ * <i>preferred</i> family, though another one may be returned if only one
+ * family is implemented for this address.
+ *
+ * Return 0 on success, -1 on failure; 1 on transient failure.
+ */
+MOCK_IMPL(int,
+tor_addr_lookup,(const char *name, uint16_t family, tor_addr_t *addr))
+{
+  /* Perhaps eventually this should be replaced by a tor_getaddrinfo or
+   * something.
+   */
+  struct in_addr iaddr;
+  struct in6_addr iaddr6;
+  tor_assert(name);
+  tor_assert(addr);
+  tor_assert(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC);
+  if (!*name) {
+    /* Empty address is an error. */
+    return -1;
+  } else if (tor_inet_pton(AF_INET, name, &iaddr)) {
+    /* It's an IPv4 IP. */
+    if (family == AF_INET6)
+      return -1;
+    tor_addr_from_in(addr, &iaddr);
+    return 0;
+  } else if (tor_inet_pton(AF_INET6, name, &iaddr6)) {
+    if (family == AF_INET)
+      return -1;
+    tor_addr_from_in6(addr, &iaddr6);
+    return 0;
+  } else {
+#ifdef HAVE_GETADDRINFO
+    int err;
+    struct addrinfo *res=NULL, *res_p;
+    struct addrinfo *best=NULL;
+    struct addrinfo hints;
+    int result = -1;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = family;
+    hints.ai_socktype = SOCK_STREAM;
+    err = tor_getaddrinfo(name, NULL, &hints, &res);
+    /* The check for 'res' here shouldn't be necessary, but it makes static
+     * analysis tools happy. */
+    if (!err && res) {
+      best = NULL;
+      for (res_p = res; res_p; res_p = res_p->ai_next) {
+        if (family == AF_UNSPEC) {
+          if (res_p->ai_family == AF_INET) {
+            best = res_p;
+            break;
+          } else if (res_p->ai_family == AF_INET6 && !best) {
+            best = res_p;
+          }
+        } else if (family == res_p->ai_family) {
+          best = res_p;
+          break;
+        }
+      }
+      if (!best)
+        best = res;
+      if (best->ai_family == AF_INET) {
+        tor_addr_from_in(addr,
+                         &((struct sockaddr_in*)best->ai_addr)->sin_addr);
+        result = 0;
+      } else if (best->ai_family == AF_INET6) {
+        tor_addr_from_in6(addr,
+                          &((struct sockaddr_in6*)best->ai_addr)->sin6_addr);
+        result = 0;
+      }
+      tor_freeaddrinfo(res);
+      return result;
+    }
+    return (err == EAI_AGAIN) ? 1 : -1;
+#else /* !(defined(HAVE_GETADDRINFO)) */
+    struct hostent *ent;
+    int err;
+#ifdef HAVE_GETHOSTBYNAME_R_6_ARG
+    char buf[2048];
+    struct hostent hostent;
+    int r;
+    r = gethostbyname_r(name, &hostent, buf, sizeof(buf), &ent, &err);
+#elif defined(HAVE_GETHOSTBYNAME_R_5_ARG)
+    char buf[2048];
+    struct hostent hostent;
+    ent = gethostbyname_r(name, &hostent, buf, sizeof(buf), &err);
+#elif defined(HAVE_GETHOSTBYNAME_R_3_ARG)
+    struct hostent_data data;
+    struct hostent hent;
+    memset(&data, 0, sizeof(data));
+    err = gethostbyname_r(name, &hent, &data);
+    ent = err ? NULL : &hent;
+#else
+    ent = gethostbyname(name);
+#ifdef _WIN32
+    err = WSAGetLastError();
+#else
+    err = h_errno;
+#endif
+#endif /* defined(HAVE_GETHOSTBYNAME_R_6_ARG) || ... */
+    if (ent) {
+      if (ent->h_addrtype == AF_INET) {
+        tor_addr_from_in(addr, (struct in_addr*) ent->h_addr);
+      } else if (ent->h_addrtype == AF_INET6) {
+        tor_addr_from_in6(addr, (struct in6_addr*) ent->h_addr);
+      } else {
+        tor_assert(0); // LCOV_EXCL_LINE: gethostbyname() returned bizarre type
+      }
+      return 0;
+    }
+#ifdef _WIN32
+    return (err == WSATRY_AGAIN) ? 1 : -1;
+#else
+    return (err == TRY_AGAIN) ? 1 : -1;
+#endif
+#endif /* defined(HAVE_GETADDRINFO) */
+  }
+}
+
+/** Parse an address or address-port combination from <b>s</b>, resolve the
+ * address as needed, and put the result in <b>addr_out</b> and (optionally)
+ * <b>port_out</b>.  Return 0 on success, negative on failure. */
+int
+tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out)
+{
+  const char *port;
+  tor_addr_t addr;
+  uint16_t portval;
+  char *tmp = NULL;
+
+  tor_assert(s);
+  tor_assert(addr_out);
+
+  s = eat_whitespace(s);
+
+  if (*s == '[') {
+    port = strstr(s, "]");
+    if (!port)
+      goto err;
+    tmp = tor_strndup(s+1, port-(s+1));
+    port = port+1;
+    if (*port == ':')
+      port++;
+    else
+      port = NULL;
+  } else {
+    port = strchr(s, ':');
+    if (port)
+      tmp = tor_strndup(s, port-s);
+    else
+      tmp = tor_strdup(s);
+    if (port)
+      ++port;
+  }
+
+  if (tor_addr_lookup(tmp, AF_UNSPEC, &addr) != 0)
+    goto err;
+  tor_free(tmp);
+
+  if (port) {
+    portval = (int) tor_parse_long(port, 10, 1, 65535, NULL, NULL);
+    if (!portval)
+      goto err;
+  } else {
+    portval = 0;
+  }
+
+  if (port_out)
+    *port_out = portval;
+  tor_addr_copy(addr_out, &addr);
+
+  return 0;
+ err:
+  tor_free(tmp);
+  return -1;
+}
+
 #ifdef USE_SANDBOX_GETADDRINFO
 /** True if we should only return cached values */
 static int sandbox_getaddrinfo_is_active = 0;

+ 7 - 1
src/lib/net/resolve.h

@@ -22,7 +22,13 @@
 #define USE_SANDBOX_GETADDRINFO
 #endif
 
-MOCK_DECL(int,tor_lookup_hostname,(const char *name, uint32_t *addr));
+struct tor_addr_t;
+
+MOCK_DECL(int, tor_lookup_hostname,(const char *name, uint32_t *addr));
+MOCK_DECL(int, tor_addr_lookup,(const char *name, uint16_t family,
+                                struct tor_addr_t *addr_out));
+int tor_addr_port_lookup(const char *s, struct tor_addr_t *addr_out,
+                         uint16_t *port_out);
 
 struct addrinfo;
 #ifdef USE_SANDBOX_GETADDRINFO

+ 3 - 59
src/test/test_addr.c

@@ -10,71 +10,16 @@
 #include "test/test.h"
 #include "feature/client/addressmap.h"
 #include "test/log_test_helpers.h"
+#include "lib/net/resolve.h"
 
 #ifdef HAVE_SYS_UN_H
 #include <sys/un.h>
 #endif
 
-/** Mocking replacement: only handles localhost. */
-static int
-mock_tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr_out)
-{
-  if (!strcmp(name, "localhost")) {
-    if (family == AF_INET || family == AF_UNSPEC) {
-      tor_addr_from_ipv4h(addr_out, 0x7f000001);
-      return 0;
-    } else if (family == AF_INET6) {
-      char bytes[16] = { 0, 0, 0, 0, 0, 0, 0, 0,
-                         0, 0, 0, 0, 0, 0, 0, 1 };
-      tor_addr_from_ipv6_bytes(addr_out, bytes);
-      return 0;
-    }
-  }
-  return -1;
-}
-
 static void
 test_addr_basic(void *arg)
 {
-  uint32_t u32;
-  uint16_t u16;
-  char *cp;
-
-  /* Test addr_port_lookup */
-  (void)arg;
-  cp = NULL; u32 = 3; u16 = 3;
-  tt_assert(!addr_port_lookup(LOG_WARN, "1.2.3.4", &cp, &u32, &u16));
-  tt_str_op(cp,OP_EQ, "1.2.3.4");
-  tt_int_op(u32,OP_EQ, 0x01020304u);
-  tt_int_op(u16,OP_EQ, 0);
-  tor_free(cp);
-  tt_assert(!addr_port_lookup(LOG_WARN, "4.3.2.1:99", &cp, &u32, &u16));
-  tt_str_op(cp,OP_EQ, "4.3.2.1");
-  tt_int_op(u32,OP_EQ, 0x04030201u);
-  tt_int_op(u16,OP_EQ, 99);
-  tor_free(cp);
-
-  MOCK(tor_addr_lookup, mock_tor_addr_lookup);
-
-  tt_assert(!addr_port_lookup(LOG_WARN, "nonexistent.address:4040",
-                               &cp, NULL, &u16));
-  tt_str_op(cp,OP_EQ, "nonexistent.address");
-  tt_int_op(u16,OP_EQ, 4040);
-  tor_free(cp);
-  tt_assert(!addr_port_lookup(LOG_WARN, "localhost:9999", &cp, &u32, &u16));
-  tt_str_op(cp,OP_EQ, "localhost");
-  tt_int_op(u16,OP_EQ, 9999);
-  tt_int_op(u32,OP_EQ, 0x7f000001u);
-  tor_free(cp);
-  u32 = 3;
-  tt_assert(!addr_port_lookup(LOG_WARN, "localhost", NULL, &u32, &u16));
-  tt_ptr_op(cp,OP_EQ, NULL);
-  tt_int_op(u32,OP_EQ, 0x7f000001u);
-  tt_int_op(u16,OP_EQ, 0);
-  tor_free(cp);
-
-  tt_assert(addr_port_lookup(LOG_WARN, "localhost:3", &cp, &u32, NULL));
-  tor_free(cp);
+  (void) arg;
 
   tt_int_op(0,OP_EQ, addr_mask_get_bits(0x0u));
   tt_int_op(32,OP_EQ, addr_mask_get_bits(0xFFFFFFFFu));
@@ -102,8 +47,7 @@ test_addr_basic(void *arg)
   }
 
  done:
-  UNMOCK(tor_addr_lookup);
-  tor_free(cp);
+  ;
 }
 
 #define test_op_ip6_(a,op,b,e1,e2)                               \

+ 1 - 0
src/test/test_config.c

@@ -10,6 +10,7 @@
 #define ROUTERSET_PRIVATE
 #include "core/or/or.h"
 #include "lib/net/address.h"
+#include "lib/net/resolve.h"
 #include "feature/client/addressmap.h"
 #include "feature/client/bridges.h"
 #include "core/or/circuitmux_ewma.h"

+ 1 - 1
src/test/test_connection.c

@@ -20,6 +20,7 @@
 #include "feature/rend/rendcache.h"
 #include "feature/dircache/directory.h"
 #include "core/or/connection_or.h"
+#include "lib/net/resolve.h"
 
 #include "test/test_connection.h"
 #include "test/test_helpers.h"
@@ -899,4 +900,3 @@ struct testcase_t connection_tests[] = {
   { "failed_orconn_tracker", test_failed_orconn_tracker, TT_FORK, NULL, NULL },
   END_OF_TESTCASES
 };
-

+ 1 - 0
src/test/test_controller.c

@@ -14,6 +14,7 @@
 #include "feature/nodelist/nodelist.h"
 #include "test/test.h"
 #include "test/test_helpers.h"
+#include "lib/net/resolve.h"
 
 #include "feature/control/control_connection_st.h"
 #include "feature/dirclient/download_status_st.h"

+ 1 - 0
src/test/test_helpers.c

@@ -24,6 +24,7 @@
 #include "core/or/relay.h"
 #include "feature/nodelist/routerlist.h"
 #include "lib/encoding/confline.h"
+#include "lib/net/resolve.h"
 
 #include "core/or/cell_st.h"
 #include "core/or/connection_st.h"

+ 1 - 0
src/test/test_options.c

@@ -21,6 +21,7 @@
 #include "lib/encoding/confline.h"
 #include "core/or/policies.h"
 #include "test/test_helpers.h"
+#include "lib/net/resolve.h"
 
 #define NS_MODULE test_options
 

+ 1 - 0
src/test/test_pt.c

@@ -19,6 +19,7 @@
 #include "test/test.h"
 #include "lib/process/subprocess.h"
 #include "lib/encoding/confline.h"
+#include "lib/net/resolve.h"
 
 #include "app/config/or_state_st.h"
 

+ 12 - 8
src/tools/tor-gencert.c

@@ -41,7 +41,8 @@ ENABLE_GCC_WARNING(redundant-decls)
 #include "lib/log/torlog.h"
 #include "lib/malloc/util_malloc.h"
 #include "lib/net/address.h"
-#include "lib/net/ipv4.h"
+#include "lib/net/inaddr.h"
+#include "lib/net/resolve.h"
 #include "lib/string/compat_string.h"
 #include "lib/string/printf.h"
 
@@ -170,19 +171,22 @@ parse_commandline(int argc, char **argv)
     } else if (!strcmp(argv[i], "-v")) {
       verbose = 1;
     } else if (!strcmp(argv[i], "-a")) {
-      uint32_t addr;
+      tor_addr_t addr;
       uint16_t port;
-      char b[INET_NTOA_BUF_LEN];
-      struct in_addr in;
       if (i+1>=argc) {
         fprintf(stderr, "No argument to -a\n");
         return 1;
       }
-      if (addr_port_lookup(LOG_ERR, argv[++i], NULL, &addr, &port)<0)
+      const char *addr_arg = argv[++i];
+      if (tor_addr_port_lookup(addr_arg, &addr, &port)<0) {
+        fprintf(stderr, "Can't resolve address/port for %s", addr_arg);
         return 1;
-      in.s_addr = htonl(addr);
-      tor_inet_ntoa(&in, b, sizeof(b));
-      tor_asprintf(&address, "%s:%d", b, (int)port);
+      }
+      if (tor_addr_family(&addr) != AF_INET) {
+        fprintf(stderr, "%s must resolve to an IPv4 address", addr_arg);
+        return 1;
+      }
+      address = tor_strdup(fmt_addrport(&addr, port));
     } else if (!strcmp(argv[i], "--create-identity-key")) {
       make_new_id = 1;
     } else if (!strcmp(argv[i], "--passphrase-fd")) {

+ 12 - 11
src/tools/tor-resolve.c

@@ -197,12 +197,14 @@ socks5_reason_to_string(char reason)
  * address (in host order) into *<b>result_addr</b>.
  */
 static int
-do_resolve(const char *hostname, uint32_t sockshost, uint16_t socksport,
+do_resolve(const char *hostname,
+           const tor_addr_t *sockshost, uint16_t socksport,
            int reverse, int version,
            tor_addr_t *result_addr, char **result_hostname)
 {
   int s = -1;
-  struct sockaddr_in socksaddr;
+  struct sockaddr_storage ss;
+  socklen_t socklen;
   char *req = NULL;
   ssize_t len = 0;
 
@@ -219,11 +221,10 @@ do_resolve(const char *hostname, uint32_t sockshost, uint16_t socksport,
     return -1;
   }
 
-  memset(&socksaddr, 0, sizeof(socksaddr));
-  socksaddr.sin_family = AF_INET;
-  socksaddr.sin_port = htons(socksport);
-  socksaddr.sin_addr.s_addr = htonl(sockshost);
-  if (connect(s, (struct sockaddr*)&socksaddr, sizeof(socksaddr))) {
+  socklen = tor_addr_to_sockaddr(sockshost, socksport,
+                                 (struct sockaddr *)&ss, sizeof(ss));
+
+  if (connect(s, (struct sockaddr*)&ss, sizeof(socklen))) {
     log_sock_error("connecting to SOCKS host", s);
     goto err;
   }
@@ -346,7 +347,7 @@ usage(void)
 int
 main(int argc, char **argv)
 {
-  uint32_t sockshost;
+  tor_addr_t sockshost;
   uint16_t socksport = 0, port_option = 0;
   int isSocks4 = 0, isVerbose = 0, isReverse = 0;
   char **arg;
@@ -414,7 +415,7 @@ main(int argc, char **argv)
 
   if (n_args == 1) {
     log_debug(LD_CONFIG, "defaulting to localhost");
-    sockshost = 0x7f000001u; /* localhost */
+    tor_addr_from_ipv4h(&sockshost, 0x7f000001u); /* localhost */
     if (port_option) {
       log_debug(LD_CONFIG, "Using port %d", (int)port_option);
       socksport = port_option;
@@ -423,7 +424,7 @@ main(int argc, char **argv)
       socksport = 9050; /* 9050 */
     }
   } else if (n_args == 2) {
-    if (addr_port_lookup(LOG_WARN, arg[1], NULL, &sockshost, &socksport)<0) {
+    if (tor_addr_port_lookup(arg[1], &sockshost, &socksport)<0) {
       fprintf(stderr, "Couldn't parse/resolve address %s", arg[1]);
       return 1;
     }
@@ -445,7 +446,7 @@ main(int argc, char **argv)
     return 1;
   }
 
-  if (do_resolve(arg[0], sockshost, socksport, isReverse,
+  if (do_resolve(arg[0], &sockshost, socksport, isReverse,
                  isSocks4 ? 4 : 5, &result,
                  &result_hostname))
     return 1;