tor/src/test/test_address.c
c 6ada3be8f1 net: Reject invalid characters in port ranges
Fixes issue #22469 where port strings such as '0x00' get accepted, not
because the string gets converted to hex, but because the string is
silently truncated past the invalid character 'x'. This also causes
issues for strings such as '0x01-0x02' which look like a hex port range,
but in reality gets truncated to '0', which is definitely not what a
user intends.

Warn and reject such port strings as invalid.

Also, since we're throwing that "malformed port" warning a lot in the
function, wrap it up in a nice goto.

Fixes #22469
2021-10-04 15:19:16 -04:00

1406 lines
39 KiB
C

/* Copyright (c) 2014-2021, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define ADDRESS_PRIVATE
#include "orconfig.h"
#ifdef _WIN32
#include <winsock2.h>
/* For access to structs needed by GetAdaptersAddresses */
#include <iphlpapi.h>
#endif
#ifdef HAVE_IFADDRS_TO_SMARTLIST
#include <net/if.h>
#include <ifaddrs.h>
#endif
#ifdef HAVE_IFCONF_TO_SMARTLIST
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#include <net/if.h>
#endif /* defined(HAVE_IFCONF_TO_SMARTLIST) */
#include "core/or/or.h"
#include "app/config/config.h"
#include "feature/dirauth/process_descs.h"
#include "feature/nodelist/routerinfo_st.h"
#include "feature/nodelist/node_st.h"
#include "feature/nodelist/nodelist.h"
#include "lib/net/address.h"
#include "test/test.h"
#include "test/log_test_helpers.h"
/** Return 1 iff <b>sockaddr1</b> and <b>sockaddr2</b> represent
* the same IP address and port combination. Otherwise, return 0.
*/
static uint8_t
sockaddr_in_are_equal(struct sockaddr_in *sockaddr1,
struct sockaddr_in *sockaddr2)
{
return ((sockaddr1->sin_family == sockaddr2->sin_family) &&
(sockaddr1->sin_port == sockaddr2->sin_port) &&
(sockaddr1->sin_addr.s_addr == sockaddr2->sin_addr.s_addr));
}
/** Return 1 iff <b>sockaddr1</b> and <b>sockaddr2</b> represent
* the same IP address and port combination. Otherwise, return 0.
*/
static uint8_t
sockaddr_in6_are_equal(struct sockaddr_in6 *sockaddr1,
struct sockaddr_in6 *sockaddr2)
{
return ((sockaddr1->sin6_family == sockaddr2->sin6_family) &&
(sockaddr1->sin6_port == sockaddr2->sin6_port) &&
(tor_memeq(sockaddr1->sin6_addr.s6_addr,
sockaddr2->sin6_addr.s6_addr,16)));
}
/** Create a sockaddr_in structure from IP address string <b>ip_str</b>.
*
* If <b>out</b> is not NULL, write the result
* to the memory address in <b>out</b>. Otherwise, allocate the memory
* for result. On success, return pointer to result. Otherwise, return
* NULL.
*/
static struct sockaddr_in *
sockaddr_in_from_string(const char *ip_str, struct sockaddr_in *out)
{
// [FIXME: add some error checking?]
if (!out)
out = tor_malloc_zero(sizeof(struct sockaddr_in));
out->sin_family = AF_INET;
out->sin_port = 0;
tor_inet_pton(AF_INET,ip_str,&(out->sin_addr));
return out;
}
/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
* that is an IPv4 or IPv6 localhost address. Otherwise, return 0.
*/
static int
smartlist_contains_localhost_tor_addr(smartlist_t *smartlist)
{
SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
if (tor_addr_is_loopback(tor_addr)) {
return 1;
}
} SMARTLIST_FOREACH_END(tor_addr);
return 0;
}
/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
* that is an IPv4 or IPv6 multicast address. Otherwise, return 0.
*/
static int
smartlist_contains_multicast_tor_addr(smartlist_t *smartlist)
{
SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
if (tor_addr_is_multicast(tor_addr)) {
return 1;
}
} SMARTLIST_FOREACH_END(tor_addr);
return 0;
}
/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
* that is an IPv4 or IPv6 internal address. Otherwise, return 0.
*/
static int
smartlist_contains_internal_tor_addr(smartlist_t *smartlist)
{
SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
if (tor_addr_is_internal(tor_addr, 0)) {
return 1;
}
} SMARTLIST_FOREACH_END(tor_addr);
return 0;
}
/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
* that is NULL or the null tor_addr_t. Otherwise, return 0.
*/
static int
smartlist_contains_null_tor_addr(smartlist_t *smartlist)
{
SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
if (tor_addr == NULL || tor_addr_is_null(tor_addr)) {
return 1;
}
} SMARTLIST_FOREACH_END(tor_addr);
return 0;
}
/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
* that is an IPv4 address. Otherwise, return 0.
*/
static int
smartlist_contains_ipv4_tor_addr(smartlist_t *smartlist)
{
SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
if (tor_addr_is_v4(tor_addr)) {
return 1;
}
} SMARTLIST_FOREACH_END(tor_addr);
return 0;
}
/** Return 1 iff <b>smartlist</b> contains a tor_addr_t structure
* that is an IPv6 address. Otherwise, return 0.
*/
static int
smartlist_contains_ipv6_tor_addr(smartlist_t *smartlist)
{
SMARTLIST_FOREACH_BEGIN(smartlist, tor_addr_t *, tor_addr) {
/* Since there's no tor_addr_is_v6, assume all non-v4s are v6 */
if (!tor_addr_is_v4(tor_addr)) {
return 1;
}
} SMARTLIST_FOREACH_END(tor_addr);
return 0;
}
#ifdef HAVE_IFADDRS_TO_SMARTLIST
static void
test_address_ifaddrs_to_smartlist(void *arg)
{
struct ifaddrs *ifa = NULL;
struct ifaddrs *ifa_ipv4 = NULL;
struct ifaddrs *ifa_ipv6 = NULL;
struct sockaddr_in *ipv4_sockaddr_local = NULL;
struct sockaddr_in *netmask_slash8 = NULL;
struct sockaddr_in *ipv4_sockaddr_remote = NULL;
struct sockaddr_in6 *ipv6_sockaddr = NULL;
smartlist_t *smartlist = NULL;
tor_addr_t *tor_addr = NULL;
struct sockaddr *sockaddr_to_check = NULL;
socklen_t addr_len;
(void)arg;
netmask_slash8 = sockaddr_in_from_string("255.0.0.0",NULL);
ipv4_sockaddr_local = sockaddr_in_from_string("127.0.0.1",NULL);
ipv4_sockaddr_remote = sockaddr_in_from_string("128.52.160.20",NULL);
ipv6_sockaddr = tor_malloc(sizeof(struct sockaddr_in6));
ipv6_sockaddr->sin6_family = AF_INET6;
ipv6_sockaddr->sin6_port = 0;
tor_inet_pton(AF_INET6, "2001:db8:8714:3a90::12",
&(ipv6_sockaddr->sin6_addr));
ifa = tor_malloc(sizeof(struct ifaddrs));
ifa_ipv4 = tor_malloc(sizeof(struct ifaddrs));
ifa_ipv6 = tor_malloc(sizeof(struct ifaddrs));
ifa->ifa_next = ifa_ipv4;
ifa->ifa_name = tor_strdup("eth0");
ifa->ifa_flags = IFF_UP | IFF_RUNNING;
ifa->ifa_addr = (struct sockaddr *)ipv4_sockaddr_local;
ifa->ifa_netmask = (struct sockaddr *)netmask_slash8;
ifa->ifa_dstaddr = NULL;
ifa->ifa_data = NULL;
ifa_ipv4->ifa_next = ifa_ipv6;
ifa_ipv4->ifa_name = tor_strdup("eth1");
ifa_ipv4->ifa_flags = IFF_UP | IFF_RUNNING;
ifa_ipv4->ifa_addr = (struct sockaddr *)ipv4_sockaddr_remote;
ifa_ipv4->ifa_netmask = (struct sockaddr *)netmask_slash8;
ifa_ipv4->ifa_dstaddr = NULL;
ifa_ipv4->ifa_data = NULL;
ifa_ipv6->ifa_next = NULL;
ifa_ipv6->ifa_name = tor_strdup("eth2");
ifa_ipv6->ifa_flags = IFF_UP | IFF_RUNNING;
ifa_ipv6->ifa_addr = (struct sockaddr *)ipv6_sockaddr;
ifa_ipv6->ifa_netmask = NULL;
ifa_ipv6->ifa_dstaddr = NULL;
ifa_ipv6->ifa_data = NULL;
smartlist = ifaddrs_to_smartlist(ifa, AF_UNSPEC);
tt_assert(smartlist);
tt_int_op(smartlist_len(smartlist), OP_EQ, 3);
sockaddr_to_check = tor_malloc(sizeof(struct sockaddr_in6));
tor_addr = smartlist_get(smartlist,0);
addr_len =
tor_addr_to_sockaddr(tor_addr,0,sockaddr_to_check,
sizeof(struct sockaddr_in));
tt_int_op(addr_len,OP_EQ,sizeof(struct sockaddr_in));
tt_assert(sockaddr_in_are_equal((struct sockaddr_in *)sockaddr_to_check,
ipv4_sockaddr_local));
tor_addr = smartlist_get(smartlist,1);
addr_len =
tor_addr_to_sockaddr(tor_addr,0,sockaddr_to_check,
sizeof(struct sockaddr_in));
tt_int_op(addr_len,OP_EQ,sizeof(struct sockaddr_in));
tt_assert(sockaddr_in_are_equal((struct sockaddr_in *)sockaddr_to_check,
ipv4_sockaddr_remote));
tor_addr = smartlist_get(smartlist,2);
addr_len =
tor_addr_to_sockaddr(tor_addr,0,sockaddr_to_check,
sizeof(struct sockaddr_in6));
tt_int_op(addr_len,OP_EQ,sizeof(struct sockaddr_in6));
tt_assert(sockaddr_in6_are_equal((struct sockaddr_in6*)sockaddr_to_check,
ipv6_sockaddr));
done:
tor_free(netmask_slash8);
tor_free(ipv4_sockaddr_local);
tor_free(ipv4_sockaddr_remote);
tor_free(ipv6_sockaddr);
tor_free(ifa->ifa_name);
tor_free(ifa_ipv4->ifa_name);
tor_free(ifa_ipv6->ifa_name);
tor_free(ifa);
tor_free(ifa_ipv4);
tor_free(ifa_ipv6);
tor_free(sockaddr_to_check);
if (smartlist) {
SMARTLIST_FOREACH(smartlist, tor_addr_t *, t, tor_free(t));
smartlist_free(smartlist);
}
return;
}
static void
test_address_get_if_addrs_ifaddrs(void *arg)
{
smartlist_t *results = NULL;
(void)arg;
results = get_interface_addresses_ifaddrs(LOG_ERR, AF_UNSPEC);
tt_assert(results);
/* Some FreeBSD jails don't have localhost IP address. Instead, they only
* have the address assigned to the jail (whatever that may be).
* And a jail without a network connection might not have any addresses at
* all. */
tt_assert(!smartlist_contains_null_tor_addr(results));
/* If there are addresses, they must be IPv4 or IPv6 */
if (smartlist_len(results) > 0) {
tt_assert(smartlist_contains_ipv4_tor_addr(results)
|| smartlist_contains_ipv6_tor_addr(results));
}
done:
if (results) {
SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t));
}
smartlist_free(results);
return;
}
#endif /* defined(HAVE_IFADDRS_TO_SMARTLIST) */
#ifdef HAVE_IP_ADAPTER_TO_SMARTLIST
static void
test_address_get_if_addrs_win32(void *arg)
{
smartlist_t *results = NULL;
(void)arg;
results = get_interface_addresses_win32(LOG_ERR, AF_UNSPEC);
tt_int_op(smartlist_len(results),OP_GE,1);
tt_assert(smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_null_tor_addr(results));
/* If there are addresses, they must be IPv4 or IPv6 */
if (smartlist_len(results) > 0) {
tt_assert(smartlist_contains_ipv4_tor_addr(results)
|| smartlist_contains_ipv6_tor_addr(results));
}
done:
SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t));
tor_free(results);
return;
}
static void
test_address_ip_adapter_addresses_to_smartlist(void *arg)
{
IP_ADAPTER_ADDRESSES *addrs1;
IP_ADAPTER_ADDRESSES *addrs2;
IP_ADAPTER_UNICAST_ADDRESS *unicast11;
IP_ADAPTER_UNICAST_ADDRESS *unicast12;
IP_ADAPTER_UNICAST_ADDRESS *unicast21;
smartlist_t *result = NULL;
struct sockaddr_in *sockaddr_test1;
struct sockaddr_in *sockaddr_test2;
struct sockaddr_in *sockaddr_localhost;
struct sockaddr_in *sockaddr_to_check;
tor_addr_t *tor_addr;
(void)arg;
(void)sockaddr_in6_are_equal;
sockaddr_to_check = tor_malloc_zero(sizeof(struct sockaddr_in));
addrs1 =
tor_malloc_zero(sizeof(IP_ADAPTER_ADDRESSES));
addrs1->FirstUnicastAddress =
unicast11 = tor_malloc_zero(sizeof(IP_ADAPTER_UNICAST_ADDRESS));
sockaddr_test1 = sockaddr_in_from_string("86.59.30.40",NULL);
unicast11->Address.lpSockaddr = (LPSOCKADDR)sockaddr_test1;
unicast11->Next = unicast12 =
tor_malloc_zero(sizeof(IP_ADAPTER_UNICAST_ADDRESS));
sockaddr_test2 = sockaddr_in_from_string("93.95.227.222", NULL);
unicast12->Address.lpSockaddr = (LPSOCKADDR)sockaddr_test2;
addrs1->Next = addrs2 =
tor_malloc_zero(sizeof(IP_ADAPTER_ADDRESSES));
addrs2->FirstUnicastAddress =
unicast21 = tor_malloc_zero(sizeof(IP_ADAPTER_UNICAST_ADDRESS));
sockaddr_localhost = sockaddr_in_from_string("127.0.0.1", NULL);
unicast21->Address.lpSockaddr = (LPSOCKADDR)sockaddr_localhost;
result = ip_adapter_addresses_to_smartlist(addrs1);
tt_assert(result);
tt_int_op(smartlist_len(result), OP_EQ, 3);
tor_addr = smartlist_get(result,0);
tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
sizeof(struct sockaddr_in));
tt_assert(sockaddr_in_are_equal(sockaddr_test1,sockaddr_to_check));
tor_addr = smartlist_get(result,1);
tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
sizeof(struct sockaddr_in));
tt_assert(sockaddr_in_are_equal(sockaddr_test2,sockaddr_to_check));
tor_addr = smartlist_get(result,2);
tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
sizeof(struct sockaddr_in));
tt_assert(sockaddr_in_are_equal(sockaddr_localhost,sockaddr_to_check));
done:
SMARTLIST_FOREACH(result, tor_addr_t *, t, tor_free(t));
smartlist_free(result);
tor_free(addrs1);
tor_free(addrs2);
tor_free(unicast11->Address.lpSockaddr);
tor_free(unicast11);
tor_free(unicast12->Address.lpSockaddr);
tor_free(unicast12);
tor_free(unicast21->Address.lpSockaddr);
tor_free(unicast21);
tor_free(sockaddr_to_check);
return;
}
#endif /* defined(HAVE_IP_ADAPTER_TO_SMARTLIST) */
#ifdef HAVE_IFCONF_TO_SMARTLIST
static void
test_address_ifreq_to_smartlist(void *arg)
{
smartlist_t *results = NULL;
const tor_addr_t *tor_addr = NULL;
struct sockaddr_in *sockaddr = NULL;
struct sockaddr_in *sockaddr_eth1 = NULL;
struct sockaddr_in *sockaddr_to_check = NULL;
struct ifconf *ifc;
struct ifreq *ifr;
struct ifreq *ifr_next;
socklen_t addr_len;
(void)arg;
sockaddr_to_check = tor_malloc(sizeof(struct sockaddr_in));
ifr = tor_malloc(sizeof(struct ifreq));
memset(ifr,0,sizeof(struct ifreq));
strlcpy(ifr->ifr_name,"lo",3);
sockaddr = (struct sockaddr_in *) &(ifr->ifr_ifru.ifru_addr);
sockaddr_in_from_string("127.0.0.1",sockaddr);
ifc = tor_malloc(sizeof(struct ifconf));
memset(ifc,0,sizeof(struct ifconf));
ifc->ifc_len = sizeof(struct ifreq);
ifc->ifc_ifcu.ifcu_req = ifr;
results = ifreq_to_smartlist((const uint8_t *)ifc->ifc_buf,ifc->ifc_len);
tt_int_op(smartlist_len(results),OP_EQ,1);
tor_addr = smartlist_get(results, 0);
addr_len =
tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
sizeof(struct sockaddr_in));
tt_int_op(addr_len,OP_EQ,sizeof(struct sockaddr_in));
tt_assert(sockaddr_in_are_equal(sockaddr,sockaddr_to_check));
ifr = tor_realloc(ifr,2*sizeof(struct ifreq));
ifr_next = ifr+1;
strlcpy(ifr_next->ifr_name,"eth1",5);
ifc->ifc_len = 2*sizeof(struct ifreq);
ifc->ifc_ifcu.ifcu_req = ifr;
sockaddr = (struct sockaddr_in *) &(ifr->ifr_ifru.ifru_addr);
sockaddr_eth1 = (struct sockaddr_in *) &(ifr_next->ifr_ifru.ifru_addr);
sockaddr_in_from_string("192.168.10.55",sockaddr_eth1);
SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t));
smartlist_free(results);
results = ifreq_to_smartlist((const uint8_t *)ifc->ifc_buf,ifc->ifc_len);
tt_int_op(smartlist_len(results),OP_EQ,2);
tor_addr = smartlist_get(results, 0);
addr_len =
tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
sizeof(struct sockaddr_in));
tt_int_op(addr_len,OP_EQ,sizeof(struct sockaddr_in));
tt_assert(sockaddr_in_are_equal(sockaddr,sockaddr_to_check));
tor_addr = smartlist_get(results, 1);
addr_len =
tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
sizeof(struct sockaddr_in));
tt_int_op(addr_len,OP_EQ,sizeof(struct sockaddr_in));
tt_assert(sockaddr_in_are_equal(sockaddr_eth1,sockaddr_to_check));
done:
tor_free(sockaddr_to_check);
SMARTLIST_FOREACH(results, tor_addr_t *, t, tor_free(t));
smartlist_free(results);
tor_free(ifc);
tor_free(ifr);
return;
}
static void
test_address_get_if_addrs_ioctl(void *arg)
{
smartlist_t *result = NULL;
(void)arg;
result = get_interface_addresses_ioctl(LOG_ERR, AF_INET);
/* On an IPv6-only system, this will fail and return NULL
tt_assert(result);
*/
/* Some FreeBSD jails don't have localhost IP address. Instead, they only
* have the address assigned to the jail (whatever that may be).
* And a jail without a network connection might not have any addresses at
* all. */
if (result) {
tt_assert(!smartlist_contains_null_tor_addr(result));
/* If there are addresses, they must be IPv4 or IPv6.
* (AIX supports IPv6 from SIOCGIFCONF.) */
if (smartlist_len(result) > 0) {
tt_assert(smartlist_contains_ipv4_tor_addr(result)
|| smartlist_contains_ipv6_tor_addr(result));
}
}
done:
if (result) {
SMARTLIST_FOREACH(result, tor_addr_t *, t, tor_free(t));
smartlist_free(result);
}
return;
}
#endif /* defined(HAVE_IFCONF_TO_SMARTLIST) */
#define FAKE_SOCKET_FD (42)
static tor_socket_t
fake_open_socket(int domain, int type, int protocol)
{
(void)domain;
(void)type;
(void)protocol;
return FAKE_SOCKET_FD;
}
static int
fake_close_socket(tor_socket_t s)
{
(void)s;
return 0;
}
static int last_connected_socket_fd = 0;
static int connect_retval = 0;
static tor_socket_t
pretend_to_connect(tor_socket_t sock, const struct sockaddr *address,
socklen_t address_len)
{
(void)address;
(void)address_len;
last_connected_socket_fd = sock;
return connect_retval;
}
static struct sockaddr *mock_addr = NULL;
static int
fake_getsockname(tor_socket_t sock, struct sockaddr *address,
socklen_t *address_len)
{
socklen_t bytes_to_copy = 0;
(void) sock;
if (!mock_addr)
return -1;
if (mock_addr->sa_family == AF_INET) {
bytes_to_copy = sizeof(struct sockaddr_in);
} else if (mock_addr->sa_family == AF_INET6) {
bytes_to_copy = sizeof(struct sockaddr_in6);
} else {
return -1;
}
if (*address_len < bytes_to_copy) {
return -1;
}
memcpy(address,mock_addr,bytes_to_copy);
*address_len = bytes_to_copy;
return 0;
}
static void
test_address_udp_socket_trick_whitebox(void *arg)
{
int hack_retval;
tor_addr_t *addr_from_hack = tor_malloc_zero(sizeof(tor_addr_t));
struct sockaddr_in6 *mock_addr6;
struct sockaddr_in6 *ipv6_to_check =
tor_malloc_zero(sizeof(struct sockaddr_in6));
(void)arg;
MOCK(tor_open_socket,fake_open_socket);
MOCK(tor_connect_socket,pretend_to_connect);
MOCK(tor_getsockname,fake_getsockname);
MOCK(tor_close_socket,fake_close_socket);
mock_addr = tor_malloc_zero(sizeof(struct sockaddr_storage));
sockaddr_in_from_string("23.32.246.118",(struct sockaddr_in *)mock_addr);
hack_retval =
get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
AF_INET, addr_from_hack);
tt_int_op(hack_retval,OP_EQ,0);
tt_assert(tor_addr_eq_ipv4h(addr_from_hack, 0x1720f676));
/* Now, lets do an IPv6 case. */
memset(mock_addr,0,sizeof(struct sockaddr_storage));
mock_addr6 = (struct sockaddr_in6 *)mock_addr;
mock_addr6->sin6_family = AF_INET6;
mock_addr6->sin6_port = 0;
tor_inet_pton(AF_INET6,"2001:cdba::3257:9652",&(mock_addr6->sin6_addr));
hack_retval =
get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
AF_INET6, addr_from_hack);
tt_int_op(hack_retval,OP_EQ,0);
tor_addr_to_sockaddr(addr_from_hack,0,(struct sockaddr *)ipv6_to_check,
sizeof(struct sockaddr_in6));
tt_assert(sockaddr_in6_are_equal(mock_addr6,ipv6_to_check));
done:
UNMOCK(tor_open_socket);
UNMOCK(tor_connect_socket);
UNMOCK(tor_getsockname);
UNMOCK(tor_close_socket);
tor_free(ipv6_to_check);
tor_free(mock_addr);
tor_free(addr_from_hack);
return;
}
static void
test_address_udp_socket_trick_blackbox(void *arg)
{
/* We want get_interface_address6_via_udp_socket_hack() to yield
* the same valid address that get_interface_address6() returns.
* If the latter is unable to find a valid address, we want
* _hack() to fail and return-1.
*
* Furthermore, we want _hack() never to crash, even if
* get_interface_addresses_raw() is returning NULL.
*/
tor_addr_t addr4;
tor_addr_t addr4_to_check;
tor_addr_t addr6;
tor_addr_t addr6_to_check;
int retval, retval_reference;
(void)arg;
#if 0
retval_reference = get_interface_address6(LOG_DEBUG,AF_INET,&addr4);
retval = get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
AF_INET,
&addr4_to_check);
tt_int_op(retval,OP_EQ,retval_reference);
tt_assert( (retval == -1 && retval_reference == -1) ||
(tor_addr_compare(&addr4,&addr4_to_check,CMP_EXACT) == 0) );
retval_reference = get_interface_address6(LOG_DEBUG,AF_INET6,&addr6);
retval = get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
AF_INET6,
&addr6_to_check);
tt_int_op(retval,OP_EQ,retval_reference);
tt_assert( (retval == -1 && retval_reference == -1) ||
(tor_addr_compare(&addr6,&addr6_to_check,CMP_EXACT) == 0) );
#else /* !(0) */
/* Both of the blackbox test cases fail horribly if:
* * The host has no external addresses.
* * There are multiple interfaces with either AF_INET or AF_INET6.
* * The last address isn't the one associated with the default route.
*
* The tests SHOULD be re-enabled when #12377 is fixed correctly, but till
* then this fails a lot, in situations we expect failures due to knowing
* about the code being broken.
*/
(void)addr4_to_check;
(void)addr6_to_check;
(void)addr6;
(void) retval_reference;
#endif /* 0 */
/* When family is neither AF_INET nor AF_INET6, we want _hack to
* fail and return -1.
*/
retval = get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
AF_INET+AF_INET6,&addr4);
tt_int_op(retval, OP_EQ, -1);
done:
return;
}
static void
test_address_get_if_addrs_list_internal(void *arg)
{
smartlist_t *results = NULL;
(void)arg;
results = get_interface_address_list(LOG_WARN, 1);
tt_ptr_op(results, OP_NE, NULL);
/* When the network is down, a system might not have any non-local
* non-multicast addresseses, not even internal ones.
* Unit tests shouldn't fail because of this. */
tt_int_op(smartlist_len(results),OP_GE,0);
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
/* The list may or may not contain internal addresses */
tt_assert(!smartlist_contains_null_tor_addr(results));
/* if there are any addresses, they must be IPv4 */
if (smartlist_len(results) > 0) {
tt_assert(smartlist_contains_ipv4_tor_addr(results));
}
tt_assert(!smartlist_contains_ipv6_tor_addr(results));
done:
interface_address_list_free(results);
return;
}
static void
test_address_get_if_addrs_list_no_internal(void *arg)
{
smartlist_t *results = NULL;
(void)arg;
results = get_interface_address_list(LOG_WARN, 0);
tt_ptr_op(results, OP_NE, NULL);
/* Work even on systems with only internal IPv4 addresses */
tt_int_op(smartlist_len(results),OP_GE,0);
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
tt_assert(!smartlist_contains_internal_tor_addr(results));
tt_assert(!smartlist_contains_null_tor_addr(results));
/* if there are any addresses, they must be IPv4 */
if (smartlist_len(results) > 0) {
tt_assert(smartlist_contains_ipv4_tor_addr(results));
}
tt_assert(!smartlist_contains_ipv6_tor_addr(results));
done:
interface_address_list_free(results);
return;
}
static void
test_address_get_if_addrs6_list_internal(void *arg)
{
smartlist_t *results = NULL;
(void)arg;
/* We might drop a log_err */
setup_full_capture_of_logs(LOG_ERR);
results = get_interface_address6_list(LOG_ERR, AF_INET6, 1);
tt_int_op(smartlist_len(mock_saved_logs()), OP_LE, 1);
if (smartlist_len(mock_saved_logs()) == 1) {
expect_log_msg_containing_either4("connect() failed",
"unable to create socket",
"Address that we determined via UDP "
"socket magic is unsuitable for public "
"comms.",
"getsockname() to determine interface "
"failed");
}
teardown_capture_of_logs();
tt_ptr_op(results, OP_NE, NULL);
/* Work even on systems without IPv6 interfaces */
tt_int_op(smartlist_len(results),OP_GE,0);
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
/* The list may or may not contain internal addresses */
tt_assert(!smartlist_contains_null_tor_addr(results));
/* if there are any addresses, they must be IPv6 */
tt_assert(!smartlist_contains_ipv4_tor_addr(results));
if (smartlist_len(results) > 0) {
tt_assert(smartlist_contains_ipv6_tor_addr(results));
}
done:
interface_address6_list_free(results);
teardown_capture_of_logs();
return;
}
static void
test_address_get_if_addrs6_list_no_internal(void *arg)
{
smartlist_t *results = NULL;
(void)arg;
/* We might drop a log_err */
setup_full_capture_of_logs(LOG_ERR);
results = get_interface_address6_list(LOG_ERR, AF_INET6, 0);
tt_int_op(smartlist_len(mock_saved_logs()), OP_LE, 1);
if (smartlist_len(mock_saved_logs()) == 1) {
expect_log_msg_containing_either4("connect() failed",
"unable to create socket",
"Address that we determined via UDP "
"socket magic is unsuitable for public "
"comms.",
"getsockname() to determine interface "
"failed");
}
teardown_capture_of_logs();
tt_ptr_op(results, OP_NE, NULL);
/* Work even on systems without IPv6 interfaces */
tt_int_op(smartlist_len(results),OP_GE,0);
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
tt_assert(!smartlist_contains_internal_tor_addr(results));
tt_assert(!smartlist_contains_null_tor_addr(results));
/* if there are any addresses, they must be IPv6 */
tt_assert(!smartlist_contains_ipv4_tor_addr(results));
if (smartlist_len(results) > 0) {
tt_assert(smartlist_contains_ipv6_tor_addr(results));
}
done:
teardown_capture_of_logs();
interface_address6_list_free(results);
return;
}
static int called_get_interface_addresses_raw = 0;
static smartlist_t *
mock_get_interface_addresses_raw_fail(int severity, sa_family_t family)
{
(void)severity;
(void)family;
called_get_interface_addresses_raw++;
return smartlist_new();
}
static int called_get_interface_address6_via_udp_socket_hack = 0;
static int
mock_get_interface_address6_via_udp_socket_hack_fail(int severity,
sa_family_t family,
tor_addr_t *addr)
{
(void)severity;
(void)family;
(void)addr;
called_get_interface_address6_via_udp_socket_hack++;
return -1;
}
static void
test_address_get_if_addrs_internal_fail(void *arg)
{
smartlist_t *results1 = NULL, *results2 = NULL;
int rv = 0;
uint32_t ipv4h_addr = 0;
tor_addr_t ipv6_addr;
memset(&ipv6_addr, 0, sizeof(tor_addr_t));
(void)arg;
MOCK(get_interface_addresses_raw,
mock_get_interface_addresses_raw_fail);
MOCK(get_interface_address6_via_udp_socket_hack,
mock_get_interface_address6_via_udp_socket_hack_fail);
results1 = get_interface_address6_list(LOG_ERR, AF_INET6, 1);
tt_ptr_op(results1, OP_NE, NULL);
tt_int_op(smartlist_len(results1),OP_EQ,0);
results2 = get_interface_address_list(LOG_ERR, 1);
tt_ptr_op(results2, OP_NE, NULL);
tt_int_op(smartlist_len(results2),OP_EQ,0);
rv = get_interface_address6(LOG_ERR, AF_INET6, &ipv6_addr);
tt_int_op(rv, OP_EQ, -1);
rv = get_interface_address(LOG_ERR, &ipv4h_addr);
tt_int_op(rv, OP_EQ, -1);
done:
UNMOCK(get_interface_addresses_raw);
UNMOCK(get_interface_address6_via_udp_socket_hack);
interface_address6_list_free(results1);
interface_address6_list_free(results2);
return;
}
static void
test_address_get_if_addrs_no_internal_fail(void *arg)
{
smartlist_t *results1 = NULL, *results2 = NULL;
(void)arg;
MOCK(get_interface_addresses_raw,
mock_get_interface_addresses_raw_fail);
MOCK(get_interface_address6_via_udp_socket_hack,
mock_get_interface_address6_via_udp_socket_hack_fail);
results1 = get_interface_address6_list(LOG_ERR, AF_INET6, 0);
tt_ptr_op(results1, OP_NE, NULL);
tt_int_op(smartlist_len(results1),OP_EQ,0);
results2 = get_interface_address_list(LOG_ERR, 0);
tt_ptr_op(results2, OP_NE, NULL);
tt_int_op(smartlist_len(results2),OP_EQ,0);
done:
UNMOCK(get_interface_addresses_raw);
UNMOCK(get_interface_address6_via_udp_socket_hack);
interface_address6_list_free(results1);
interface_address6_list_free(results2);
return;
}
static void
test_address_get_if_addrs(void *arg)
{
int rv;
uint32_t addr_h = 0;
tor_addr_t tor_addr;
(void)arg;
rv = get_interface_address(LOG_WARN, &addr_h);
/* When the network is down, a system might not have any non-local
* non-multicast IPv4 addresses, not even internal ones.
* Unit tests shouldn't fail because of this. */
if (rv == 0) {
tor_addr_from_ipv4h(&tor_addr, addr_h);
tt_assert(!tor_addr_is_loopback(&tor_addr));
tt_assert(!tor_addr_is_multicast(&tor_addr));
/* The address may or may not be an internal address */
tt_assert(tor_addr_is_v4(&tor_addr));
}
done:
return;
}
static void
test_address_get_if_addrs6(void *arg)
{
int rv;
tor_addr_t tor_addr;
(void)arg;
rv = get_interface_address6(LOG_WARN, AF_INET6, &tor_addr);
/* Work even on systems without IPv6 interfaces */
if (rv == 0) {
tt_assert(!tor_addr_is_loopback(&tor_addr));
tt_assert(!tor_addr_is_multicast(&tor_addr));
/* The address may or may not be an internal address */
tt_assert(!tor_addr_is_v4(&tor_addr));
}
done:
return;
}
static void
test_address_tor_addr_to_in6(void *ignored)
{
(void)ignored;
tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
const struct in6_addr *res;
uint8_t expected[16] = {42, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15};
a->family = AF_INET;
res = tor_addr_to_in6(a);
tt_assert(!res);
a->family = AF_INET6;
memcpy(a->addr.in6_addr.s6_addr, expected, 16);
res = tor_addr_to_in6(a);
tt_assert(res);
tt_mem_op(res->s6_addr, OP_EQ, expected, 16);
done:
tor_free(a);
}
static void
test_address_tor_addr_to_in(void *ignored)
{
(void)ignored;
tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
const struct in_addr *res;
a->family = AF_INET6;
res = tor_addr_to_in(a);
tt_assert(!res);
a->family = AF_INET;
a->addr.in_addr.s_addr = 44;
res = tor_addr_to_in(a);
tt_assert(res);
tt_int_op(res->s_addr, OP_EQ, 44);
done:
tor_free(a);
}
static void
test_address_tor_addr_to_ipv4n(void *ignored)
{
(void)ignored;
tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
uint32_t res;
a->family = AF_INET6;
res = tor_addr_to_ipv4n(a);
tt_assert(!res);
a->family = AF_INET;
a->addr.in_addr.s_addr = 43;
res = tor_addr_to_ipv4n(a);
tt_assert(res);
tt_int_op(res, OP_EQ, 43);
done:
tor_free(a);
}
static void
test_address_tor_addr_to_mapped_ipv4h(void *ignored)
{
(void)ignored;
tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
uint32_t res;
uint8_t toset[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 42};
a->family = AF_INET;
res = tor_addr_to_mapped_ipv4h(a);
tt_assert(!res);
a->family = AF_INET6;
memcpy(a->addr.in6_addr.s6_addr, toset, 16);
res = tor_addr_to_mapped_ipv4h(a);
tt_assert(res);
tt_int_op(res, OP_EQ, 42);
done:
tor_free(a);
}
static void
test_address_tor_addr_eq_ipv4h(void *ignored)
{
(void)ignored;
tor_addr_t *a = tor_malloc_zero(sizeof(tor_addr_t));
int res;
a->family = AF_INET6;
res = tor_addr_eq_ipv4h(a, 42);
tt_assert(!res);
a->family = AF_INET;
a->addr.in_addr.s_addr = 52;
res = tor_addr_eq_ipv4h(a, 42);
tt_assert(!res);
a->addr.in_addr.s_addr = 52;
res = tor_addr_eq_ipv4h(a, ntohl(52));
tt_assert(res);
done:
tor_free(a);
}
static void
test_address_tor_addr_in_same_network_family(void *ignored)
{
(void)ignored;
tor_addr_t a, b;
tor_addr_parse(&a, "8.8.8.8");
tor_addr_parse(&b, "8.8.4.4");
tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 1);
tor_addr_parse(&a, "8.8.8.8");
tor_addr_parse(&b, "1.1.1.1");
tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 0);
tor_addr_parse(&a, "8.8.8.8");
tor_addr_parse(&b, "2001:4860:4860::8844");
tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 0);
tor_addr_parse(&a, "2001:4860:4860::8888");
tor_addr_parse(&b, "2001:4860:4860::8844");
tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 1);
tor_addr_parse(&a, "2001:4860:4860::8888");
tor_addr_parse(&b, "2001:470:20::2");
tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 0);
done:
return;
}
static node_t *
helper_create_mock_node(char id_char)
{
node_t *node = tor_malloc_zero(sizeof(node_t));
routerinfo_t *ri = tor_malloc_zero(sizeof(routerinfo_t));
tor_addr_make_null(&ri->ipv6_addr, AF_INET6);
node->ri = ri;
memset(node->identity, id_char, sizeof(node->identity));
return node;
}
static void
helper_free_mock_node(node_t *node)
{
if (!node)
return;
tor_free(node->ri);
tor_free(node);
}
#define NODE_SET_IPV4(node, ipv4_addr_str, ipv4_port) { \
tor_addr_parse(&(node)->ri->ipv4_addr, ipv4_addr_str); \
node->ri->ipv4_orport = ipv4_port; \
}
#define NODE_CLEAR_IPV4(node) { \
tor_addr_make_unspec(&node->ri->ipv4_addr); \
node->ri->ipv4_orport = 0; \
}
#define NODE_SET_IPV6(node, ipv6_addr_str, ipv6_port) { \
tor_addr_parse(&node->ri->ipv6_addr, ipv6_addr_str); \
node->ri->ipv6_orport = ipv6_port; \
}
static void
test_address_tor_node_in_same_network_family(void *ignored)
{
(void)ignored;
node_t *node_a = helper_create_mock_node('a');
node_t *node_b = helper_create_mock_node('b');
NODE_SET_IPV4(node_a, "8.8.8.8", 1);
NODE_SET_IPV4(node_b, "8.8.4.4", 1);
tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 1);
NODE_SET_IPV4(node_a, "8.8.8.8", 1);
NODE_SET_IPV4(node_b, "1.1.1.1", 1);
tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 0);
NODE_CLEAR_IPV4(node_a);
NODE_SET_IPV6(node_a, "2001:470:20::2", 1);
tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 0);
NODE_CLEAR_IPV4(node_b);
NODE_SET_IPV6(node_b, "2606:4700:4700::1111", 1);
tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 0);
NODE_SET_IPV6(node_a, "2606:4700:4700::1001", 1);
tt_int_op(nodes_in_same_family(node_a, node_b), OP_EQ, 1);
done:
helper_free_mock_node(node_a);
helper_free_mock_node(node_b);
}
static or_options_t mock_options;
static const or_options_t *
mock_get_options(void)
{
return &mock_options;
}
/* Test dirserv_router_has_valid_address() on a stub routerinfo, with only its
* address fields set. Use IPv4 ipv4_addr_str and IPv6 ipv6_addr_str.
* Fail if it does not return rv. */
#define TEST_ROUTER_VALID_ADDRESS_HELPER(ipv4_addr_str, ipv6_addr_str, rv) \
STMT_BEGIN \
ri = tor_malloc_zero(sizeof(routerinfo_t)); \
tor_addr_parse(&ri->ipv4_addr, (ipv4_addr_str)); \
tor_addr_parse(&ri->ipv6_addr, (ipv6_addr_str)); \
tt_int_op(dirserv_router_has_valid_address(ri), OP_EQ, (rv)); \
tor_free(ri); \
STMT_END
/* Like TEST_ROUTER_VALID_ADDRESS_HELPER(), but always passes a null
* IPv6 address. */
#define CHECK_RI_ADDR(ipv4_addr_str, rv) \
TEST_ROUTER_VALID_ADDRESS_HELPER(ipv4_addr_str, "::", rv)
/* Like TEST_ROUTER_VALID_ADDRESS_HELPER(), but always passes a non-internal
* IPv4 address, so that the IPv6 check is reached. */
#define CHECK_RI_ADDR6(ipv6_addr_str, rv) \
TEST_ROUTER_VALID_ADDRESS_HELPER("1.0.0.1", ipv6_addr_str, rv)
static void
test_address_dirserv_router_addr_private(void *opt_dir_allow_private)
{
/* A stub routerinfo structure, with only its address fields set. */
routerinfo_t *ri = NULL;
/* The expected return value for private addresses.
* Modified if DirAllowPrivateAddresses is 1. */
int private_rv = -1;
memset(&mock_options, 0, sizeof(or_options_t));
MOCK(get_options, mock_get_options);
if (opt_dir_allow_private) {
mock_options.DirAllowPrivateAddresses = 1;
private_rv = 0;
}
CHECK_RI_ADDR("1.0.0.1", 0);
CHECK_RI_ADDR("10.0.0.1", private_rv);
CHECK_RI_ADDR6("2600::1", 0);
CHECK_RI_ADDR6("fe80::1", private_rv);
/* Null addresses */
/* IPv4 null fails, regardless of IPv6 */
CHECK_RI_ADDR("0.0.0.0", private_rv);
TEST_ROUTER_VALID_ADDRESS_HELPER("0.0.0.0", "::", private_rv);
/* IPv6 null succeeds, because IPv4 is not null */
CHECK_RI_ADDR6("::", 0);
/* Byte-zeroed null addresses */
/* IPv4 null fails, regardless of IPv6 */
{
ri = tor_malloc_zero(sizeof(routerinfo_t));
tt_int_op(dirserv_router_has_valid_address(ri), OP_EQ, private_rv);
tor_free(ri);
}
/* IPv6 null succeeds, because IPv4 is not internal */
{
ri = tor_malloc_zero(sizeof(routerinfo_t));
tor_addr_parse(&ri->ipv4_addr, "1.0.0.1");
tt_int_op(dirserv_router_has_valid_address(ri), OP_EQ, 0);
tor_free(ri);
}
done:
tor_free(ri);
UNMOCK(get_options);
}
static void
test_address_parse_port_range(void *arg)
{
int ret;
uint16_t min_out = 0;
uint16_t max_out = 0;
(void) arg;
/* Invalid. */
ret = parse_port_range("0x00", &min_out, &max_out);
tt_int_op(ret, OP_EQ, -1);
ret = parse_port_range("0x01", &min_out, &max_out);
tt_int_op(ret, OP_EQ, -1);
ret = parse_port_range("1817161", &min_out, &max_out);
tt_int_op(ret, OP_EQ, -1);
ret = parse_port_range("65536", &min_out, &max_out);
tt_int_op(ret, OP_EQ, -1);
ret = parse_port_range("1-65536", &min_out, &max_out);
tt_int_op(ret, OP_EQ, -1);
/* Valid. */
ret = parse_port_range("65535", &min_out, &max_out);
tt_int_op(ret, OP_EQ, 0);
tt_int_op(min_out, OP_EQ, 65535);
tt_int_op(max_out, OP_EQ, 65535);
ret = parse_port_range("1-65535", &min_out, &max_out);
tt_int_op(ret, OP_EQ, 0);
tt_int_op(min_out, OP_EQ, 1);
tt_int_op(max_out, OP_EQ, 65535);
done:
;
}
#define ADDRESS_TEST(name, flags) \
{ #name, test_address_ ## name, flags, NULL, NULL }
#define ADDRESS_TEST_STR_ARG(name, flags, str_arg) \
{ #name "/" str_arg, test_address_ ## name, flags, &passthrough_setup, \
(void *)(str_arg) }
struct testcase_t address_tests[] = {
ADDRESS_TEST(udp_socket_trick_whitebox, TT_FORK),
ADDRESS_TEST(udp_socket_trick_blackbox, TT_FORK),
ADDRESS_TEST(get_if_addrs_list_internal, 0),
ADDRESS_TEST(get_if_addrs_list_no_internal, 0),
ADDRESS_TEST(get_if_addrs6_list_internal, 0),
ADDRESS_TEST(get_if_addrs6_list_no_internal, TT_FORK),
ADDRESS_TEST(get_if_addrs_internal_fail, 0),
ADDRESS_TEST(get_if_addrs_no_internal_fail, 0),
ADDRESS_TEST(get_if_addrs, 0),
ADDRESS_TEST(get_if_addrs6, 0),
#ifdef HAVE_IFADDRS_TO_SMARTLIST
ADDRESS_TEST(get_if_addrs_ifaddrs, TT_FORK),
ADDRESS_TEST(ifaddrs_to_smartlist, 0),
#endif
#ifdef HAVE_IP_ADAPTER_TO_SMARTLIST
ADDRESS_TEST(get_if_addrs_win32, TT_FORK),
ADDRESS_TEST(ip_adapter_addresses_to_smartlist, 0),
#endif
#ifdef HAVE_IFCONF_TO_SMARTLIST
ADDRESS_TEST(get_if_addrs_ioctl, TT_FORK),
ADDRESS_TEST(ifreq_to_smartlist, 0),
#endif
ADDRESS_TEST(tor_addr_to_in6, 0),
ADDRESS_TEST(tor_addr_to_in, 0),
ADDRESS_TEST(tor_addr_to_ipv4n, 0),
ADDRESS_TEST(tor_addr_to_mapped_ipv4h, 0),
ADDRESS_TEST(tor_addr_eq_ipv4h, 0),
ADDRESS_TEST(tor_addr_in_same_network_family, 0),
ADDRESS_TEST(tor_node_in_same_network_family, 0),
ADDRESS_TEST(dirserv_router_addr_private, 0),
ADDRESS_TEST_STR_ARG(dirserv_router_addr_private, 0, "allow_private"),
ADDRESS_TEST(parse_port_range, 0),
END_OF_TESTCASES
};