diff --git a/changes/ticket26526 b/changes/ticket26526 new file mode 100644 index 0000000000..447b581df8 --- /dev/null +++ b/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. diff --git a/changes/ticket26526_extra b/changes/ticket26526_extra new file mode 100644 index 0000000000..5495962ff7 --- /dev/null +++ b/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. diff --git a/src/app/config/config.c b/src/app/config/config.c index 665732ea56..1be1803f29 100644 --- a/src/app/config/config.c +++ b/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 #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; diff --git a/src/app/config/statefile.c b/src/app/config/statefile.c index 8eeef45026..656dc2eec3 100644 --- a/src/app/config/statefile.c +++ b/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" diff --git a/src/core/mainloop/main.c b/src/core/mainloop/main.c index c5773ddfc1..048397a2da 100644 --- a/src/core/mainloop/main.c +++ b/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" diff --git a/src/core/or/or.h b/src/core/or/or.h index 6edfd21dfb..2e419eefd5 100644 --- a/src/core/or/or.h +++ b/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" diff --git a/src/feature/nodelist/routerlist.c b/src/feature/nodelist/routerlist.c index 12226fee64..8b54329da9 100644 --- a/src/feature/nodelist/routerlist.c +++ b/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" diff --git a/src/feature/rend/rendservice.c b/src/feature/rend/rendservice.c index da4a98b3d1..1a99bd56ed 100644 --- a/src/feature/rend/rendservice.c +++ b/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" diff --git a/src/lib/net/address.c b/src/lib/net/address.c index f3eddca7bb..3b624da096 100644 --- a/src/lib/net/address.c +++ b/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 name, and set - * *addr to the proper IP address and family. The family - * argument (which must be AF_INET, AF_INET6, or AF_UNSPEC) declares a - * preferred 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 ip 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 s, resolve the - * address as needed, and put the result in addr_out and (optionally) - * port_out. 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 addrport. If - * address is provided, set *address to a copy of the - * host portion of the string. If addr is provided, try to - * resolve the host portion of the string and store it into - * *addr (in host byte order). If port_out is provided, - * store the port number into *port_out, or 0 if no port is given. - * If port_out is NULL, then there must be no port number in - * addrport. - * 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; diff --git a/src/lib/net/address.h b/src/lib/net/address.h index f8ea573c30..e857b4068b 100644 --- a/src/lib/net/address.h +++ b/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); diff --git a/src/lib/net/ipv6.c b/src/lib/net/inaddr.c similarity index 82% rename from src/lib/net/ipv6.c rename to src/lib/net/inaddr.c index 630d6f1db4..dcd8fcdd65 100644 --- a/src/lib/net/ipv6.c +++ b/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 @@ -26,6 +26,45 @@ #include #include +#ifdef _WIN32 +#include +#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 *in (in network order, as usual), + * write it as a string into the buf_len-byte buffer in + * buf. 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 af==AF_INET and src a struct in_addr, or * af==AF_INET6 and src a struct in6_addr, try to format the * address and store it in the len-byte buffer dst. Returns diff --git a/src/lib/net/ipv4.h b/src/lib/net/inaddr.h similarity index 66% rename from src/lib/net/ipv4.h rename to src/lib/net/inaddr.h index 0127f09e09..121025a126 100644 --- a/src/lib/net/ipv4.h +++ b/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 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 diff --git a/src/lib/net/ipv6.h b/src/lib/net/inaddr_st.h similarity index 89% rename from src/lib/net/ipv6.h rename to src/lib/net/inaddr_st.h index 4e5ef4da88..cc72621e99 100644 --- a/src/lib/net/ipv6.h +++ b/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 #ifdef HAVE_NETINET_IN6_H #include #endif + #ifdef _WIN32 #include #include #include #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 */ diff --git a/src/lib/net/include.am b/src/lib/net/include.am index 6fda173614..67db0d5af2 100644 --- a/src/lib/net/include.am +++ b/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 \ diff --git a/src/lib/net/ipv4.c b/src/lib/net/ipv4.c deleted file mode 100644 index db1429f49c..0000000000 --- a/src/lib/net/ipv4.c +++ /dev/null @@ -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 -#endif -#ifdef _WIN32 -#include -#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 *in (in network order, as usual), - * write it as a string into the buf_len-byte buffer in - * buf. 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)); -} diff --git a/src/lib/net/resolve.c b/src/lib/net/resolve.c index c620d4f6ab..ff9c93989a 100644 --- a/src/lib/net/resolve.c +++ b/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 name, and set + * *addr to the proper IP address and family. The family + * argument (which must be AF_INET, AF_INET6, or AF_UNSPEC) declares a + * preferred 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 s, resolve the + * address as needed, and put the result in addr_out and (optionally) + * port_out. 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; diff --git a/src/lib/net/resolve.h b/src/lib/net/resolve.h index c91aecfee4..bf870c44c4 100644 --- a/src/lib/net/resolve.h +++ b/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 diff --git a/src/test/test_addr.c b/src/test/test_addr.c index 9ab921c5b6..a9004048a5 100644 --- a/src/test/test_addr.c +++ b/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 #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) \ diff --git a/src/test/test_config.c b/src/test/test_config.c index af3a8a7cfe..393378b4c8 100644 --- a/src/test/test_config.c +++ b/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" diff --git a/src/test/test_connection.c b/src/test/test_connection.c index c423c6573f..e716c83fe1 100644 --- a/src/test/test_connection.c +++ b/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 }; - diff --git a/src/test/test_controller.c b/src/test/test_controller.c index 2ded04619c..d0aa868448 100644 --- a/src/test/test_controller.c +++ b/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" diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index c666bca59a..c9138611d8 100644 --- a/src/test/test_helpers.c +++ b/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" diff --git a/src/test/test_options.c b/src/test/test_options.c index 396965401e..4e890205b2 100644 --- a/src/test/test_options.c +++ b/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 diff --git a/src/test/test_pt.c b/src/test/test_pt.c index dea3791da2..d0160d1148 100644 --- a/src/test/test_pt.c +++ b/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" diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c index ce032ed643..efae621d09 100644 --- a/src/tools/tor-gencert.c +++ b/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")) { diff --git a/src/tools/tor-resolve.c b/src/tools/tor-resolve.c index 1532d5f201..9358cc8a6e 100644 --- a/src/tools/tor-resolve.c +++ b/src/tools/tor-resolve.c @@ -197,12 +197,14 @@ socks5_reason_to_string(char reason) * address (in host order) into *result_addr. */ 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;