Adding comprehensive test cases for resolve_my_address.

Also, improve comments on resolve_my_address to explain what it
actually does.
This commit is contained in:
rl1987 2014-04-28 23:20:58 +03:00 committed by Nick Mathewson
parent a56511e594
commit 28217b969e
6 changed files with 628 additions and 13 deletions

View File

@ -1354,8 +1354,8 @@ tor_addr_is_multicast(const tor_addr_t *a)
* connects to the Internet. This address should only be used in checking
* whether our address has changed. Return 0 on success, -1 on failure.
*/
int
get_interface_address6(int severity, sa_family_t family, tor_addr_t *addr)
MOCK_IMPL(int,
get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
{
/* XXX really, this function should yield a smartlist of addresses. */
smartlist_t *addrs;
@ -1684,8 +1684,8 @@ tor_dup_ip(uint32_t addr)
* checking whether our address has changed. Return 0 on success, -1 on
* failure.
*/
int
get_interface_address(int severity, uint32_t *addr)
MOCK_IMPL(int,
get_interface_address,(int severity, uint32_t *addr))
{
tor_addr_t local_addr;
int r;

View File

@ -148,7 +148,8 @@ char *tor_dup_addr(const tor_addr_t *addr) ATTR_MALLOC;
const char *fmt_addr_impl(const tor_addr_t *addr, int decorate);
const char *fmt_addrport(const tor_addr_t *addr, uint16_t port);
const char * fmt_addr32(uint32_t addr);
int get_interface_address6(int severity, sa_family_t family, tor_addr_t *addr);
MOCK_DECL(int,get_interface_address6,(int severity, sa_family_t family,
tor_addr_t *addr));
/** Flag to specify how to do a comparison between addresses. In an "exact"
* comparison, addresses are equivalent only if they are in the same family
@ -225,7 +226,7 @@ int addr_mask_get_bits(uint32_t mask);
#define INET_NTOA_BUF_LEN 16
int tor_inet_ntoa(const struct in_addr *in, char *buf, size_t buf_len);
char *tor_dup_ip(uint32_t addr) ATTR_MALLOC;
int get_interface_address(int severity, uint32_t *addr);
MOCK_DECL(int,get_interface_address,(int severity, uint32_t *addr));
tor_addr_port_t *tor_addr_port_new(const tor_addr_t *addr, uint16_t port);

View File

@ -2061,9 +2061,20 @@ get_environment(void)
#endif
}
/** Get name of current host and write it to <b>name</b> array, whose
* length is specified by <b>namelen</b> argument. Return 0 upon
* successfull completion; otherwise return return -1. (Currently,
* this function is merely a mockable wrapper for POSIX gethostname().)
*/
MOCK_IMPL(int,
tor_gethostname,(char *name, size_t namelen))
{
return gethostname(name,namelen);
}
/** 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.)
* 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)
@ -2281,8 +2292,9 @@ tor_inet_pton(int af, const char *src, void *dst)
* (This function exists because standard windows gethostbyname
* doesn't treat raw IP addresses properly.)
*/
int
tor_lookup_hostname(const char *name, uint32_t *addr)
MOCK_IMPL(int,
tor_lookup_hostname,(const char *name, uint32_t *addr))
{
tor_addr_t myaddr;
int ret;

View File

@ -537,10 +537,11 @@ struct sockaddr_in6 {
};
#endif
MOCK_DECL(int,tor_gethostname,(char *name, size_t namelen));
int tor_inet_aton(const char *cp, struct in_addr *addr) ATTR_NONNULL((1,2));
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);
int tor_lookup_hostname(const char *name, uint32_t *addr) ATTR_NONNULL((1,2));
MOCK_DECL(int,tor_lookup_hostname,(const char *name, uint32_t *addr));
int set_socket_nonblocking(tor_socket_t socket);
int tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2]);
int network_init(void);

View File

@ -11,6 +11,7 @@
#define CONFIG_PRIVATE
#include "or.h"
#include "compat.h"
#include "addressmap.h"
#include "channel.h"
#include "circuitbuild.h"
@ -2051,7 +2052,33 @@ get_last_resolved_addr(void)
}
/**
* Use <b>options-\>Address</b> to guess our public IP address.
* Attempt getting our non-local (as judged by tor_addr_is_internal()
* function) IP address using following techniques, listed in
* order from best (most desirable, try first) to worst (least
* desirable, try if everything else fails).
*
* First, attempt using <b>options-\>Address</b> to get our
* non-local IP address.
*
* If <b>options-\>Address</b> represents a non-local IP address,
* consider it ours.
*
* If <b>options-\>Address</b> is a DNS name that resolves to
* a non-local IP address, consider this IP address ours.
*
* If <b>options-\>Address</b> is NULL, fall back to getting local
* hostname and using it in above-described ways to try and
* get our IP address.
*
* In case local hostname cannot be resolved to a non-local IP
* address, try getting an IP address of network interface
* in hopes it will be non-local one.
*
* Fail if one or more of the following is true:
* - DNS name in <b>options-\>Address</b> cannot be resolved.
* - <b>options-\>Address</b> is a local host address.
* - Attempt to getting local hostname fails.
* - Attempt to getting network interface address fails.
*
* Return 0 if all is well, or -1 if we can't find a suitable
* public IP address.
@ -2060,6 +2087,11 @@ get_last_resolved_addr(void)
* - Put our public IP address (in host order) into *<b>addr_out</b>.
* - If <b>method_out</b> is non-NULL, set *<b>method_out</b> to a static
* string describing how we arrived at our answer.
* - "CONFIGURED" - parsed from IP address string in
* <b>options-\>Address</b>
* - "RESOLVED" - resolved from DNS name in <b>options-\>Address</b>
* - "GETHOSTNAME" - resolved from a local hostname.
* - "INTERFACE" - retrieved from a network interface.
* - If <b>hostname_out</b> is non-NULL, and we resolved a hostname to
* get our address, set *<b>hostname_out</b> to a newly allocated string
* holding that hostname. (If we didn't get our address by resolving a
@ -2098,7 +2130,7 @@ resolve_my_address(int warn_severity, const or_options_t *options,
explicit_ip = 0; /* it's implicit */
explicit_hostname = 0; /* it's implicit */
if (gethostname(hostname, sizeof(hostname)) < 0) {
if (tor_gethostname(hostname, sizeof(hostname)) < 0) {
log_fn(warn_severity, LD_NET,"Error obtaining local hostname");
return -1;
}

View File

@ -578,10 +578,579 @@ test_config_fix_my_family(void *arg)
or_options_free(defaults);
}
static int n_hostname_01010101 = 0;
/** This mock function is meant to replace tor_lookup_hostname().
* It answers with 1.1.1.1 as IP adddress that resulted from lookup.
* This function increments <b>n_hostname_01010101</b> counter by one
* every time it is called.
*/
static int
tor_lookup_hostname_01010101(const char *name, uint32_t *addr)
{
n_hostname_01010101++;
if (name && addr) {
*addr = ntohl(0x01010101);
}
return 0;
}
static int n_hostname_localhost = 0;
/** This mock function is meant to replace tor_lookup_hostname().
* It answers with 127.0.0.1 as IP adddress that resulted from lookup.
* This function increments <b>n_hostname_localhost</b> counter by one
* every time it is called.
*/
static int
tor_lookup_hostname_localhost(const char *name, uint32_t *addr)
{
n_hostname_localhost++;
if (name && addr) {
*addr = 0x7f000001;
}
return 0;
}
static int n_hostname_failure = 0;
/** This mock function is meant to replace tor_lookup_hostname().
* It pretends to fail by returning -1 to caller. Also, this function
* increments <b>n_hostname_failure</b> every time it is called.
*/
static int
tor_lookup_hostname_failure(const char *name, uint32_t *addr)
{
(void)name;
(void)addr;
n_hostname_failure++;
return -1;
}
static int n_gethostname_replacement = 0;
/** This mock function is meant to replace tor_gethostname(). It
* responds with string "onionrouter" as hostname. This function
* increments <b>n_gethostname_replacement</b> by one every time
* it is called.
*/
static int
tor_gethostname_replacement(char *name, size_t namelen)
{
n_gethostname_replacement++;
if (name && namelen) {
strlcpy(name,"onionrouter",namelen);
}
return 0;
}
static int n_gethostname_localhost = 0;
/** This mock function is meant to replace tor_gethostname(). It
* responds with string "127.0.0.1" as hostname. This function
* increments <b>n_gethostname_localhost</b> by one every time
* it is called.
*/
static int
tor_gethostname_localhost(char *name, size_t namelen)
{
n_gethostname_localhost++;
if (name && namelen) {
strlcpy(name,"127.0.0.1",namelen);
}
return 0;
}
static int n_gethostname_failure = 0;
/** This mock function is meant to replace tor_gethostname.
* It pretends to fail by returning -1. This function increments
* <b>n_gethostname_failure</b> by one every time it is called.
*/
static int
tor_gethostname_failure(char *name, size_t namelen)
{
n_gethostname_failure++;
return -1;
}
static int n_get_interface_address = 0;
/** This mock function is meant to replace get_interface_address().
* It answers with address 8.8.8.8. This function increments
* <b>n_get_interface_address</b> by one every time it is called.
*/
static int
get_interface_address_08080808(int severity, uint32_t *addr)
{
(void)severity;
n_get_interface_address++;
if (addr) {
*addr = ntohl(0x08080808);
}
return 0;
}
static int n_get_interface_address6 = 0;
static sa_family_t last_address6_family;
/** This mock function is meant to replace get_interface_address6().
* It answers with IP address 9.9.9.9 iff both of the following are true:
* - <b>family</b> is AF_INET
* - <b>addr</b> pointer is not NULL.
* This function increments <b>n_get_interface_address6</b> by one every
* time it is called.
*/
static int
get_interface_address6_replacement(int severity, sa_family_t family,
tor_addr_t *addr)
{
(void)severity;
last_address6_family = family;
n_get_interface_address6++;
if ((family != AF_INET) || !addr) {
return -1;
}
tor_addr_from_ipv4h(addr,0x09090909);
return 0;
}
static int n_get_interface_address_failure = 0;
/**
* This mock function is meant to replace get_interface_address().
* It pretends to fail getting interface address by returning -1.
* <b>n_get_interface_address_failure</b> is incremented by one
* every time this function is called.
*/
static int
get_interface_address_failure(int severity, uint32_t *addr)
{
(void)severity;
(void)addr;
n_get_interface_address_failure++;
return -1;
}
static int n_get_interface_address6_failure = 0;
/**
* This mock function is meant to replace get_interface_addres6().
* It will pretent to fail by return -1.
* <b>n_get_interface_address6_failure</b> is incremented by one
* every time this function is called and <b>last_address6_family</b>
* is assigned the value of <b>family</b> argument.
*/
static int
get_interface_address6_failure(int severity, sa_family_t family,
tor_addr_t *addr)
{
n_get_interface_address6_failure++;
last_address6_family = family;
return -1;
}
static void
test_config_resolve_my_address(void *arg)
{
(void)arg;
or_options_t *options;
uint32_t resolved_addr;
const char *method_used;
char *hostname_out;
int retval;
int prev_n_hostname_01010101;
int prev_n_hostname_localhost;
int prev_n_hostname_failure;
int prev_n_gethostname_replacement;
int prev_n_gethostname_failure;
int prev_n_gethostname_localhost;
int prev_n_get_interface_address;
int prev_n_get_interface_address_failure;
int prev_n_get_interface_address6;
int prev_n_get_interface_address6_failure;
options = options_new();
options_init(options);
/*
* CASE 1:
* If options->Address is a valid IPv4 address string, we want
* the corresponding address to be parsed and returned.
*/
options->Address = tor_strdup("128.52.128.105");
retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr,
&method_used,&hostname_out);
tt_want(retval == 0);
tt_want_str_op(method_used,==,"CONFIGURED");
tt_want(hostname_out == NULL);
tt_assert(htonl(resolved_addr) == 0x69803480);
tor_free(options->Address);
/*
* CASE 2:
* If options->Address is a valid DNS address, we want resolve_my_address()
* function to ask tor_lookup_hostname() for help with resolving it
* and return the address that was resolved (in host order).
*/
MOCK(tor_lookup_hostname,tor_lookup_hostname_01010101);
tor_free(options->Address);
options->Address = tor_strdup("www.torproject.org");
prev_n_hostname_01010101 = n_hostname_01010101;
retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr,
&method_used,&hostname_out);
tt_want(retval == 0);
tt_want(n_hostname_01010101 == prev_n_hostname_01010101 + 1);
tt_want_str_op(method_used,==,"RESOLVED");
tt_want_str_op(hostname_out,==,"www.torproject.org");
tt_assert(htonl(resolved_addr) == 0x01010101);
UNMOCK(tor_lookup_hostname);
tor_free(options->Address);
/*
* CASE 3:
* Given that options->Address is NULL, we want resolve_my_address()
* to try and use tor_gethostname() to get hostname AND use
* tor_lookup_hostname() to get IP address.
*/
resolved_addr = 0;
tor_free(options->Address);
options->Address = NULL;
MOCK(tor_gethostname,tor_gethostname_replacement);
MOCK(tor_lookup_hostname,tor_lookup_hostname_01010101);
prev_n_gethostname_replacement = n_gethostname_replacement;
prev_n_hostname_01010101 = n_hostname_01010101;
retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr,
&method_used,&hostname_out);
tt_want(retval == 0);
tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1);
tt_want(n_hostname_01010101 == prev_n_hostname_01010101 + 1);
tt_want_str_op(method_used,==,"GETHOSTNAME");
tt_want_str_op(hostname_out,==,"onionrouter");
tt_assert(htonl(resolved_addr) == 0x01010101);
UNMOCK(tor_gethostname);
UNMOCK(tor_lookup_hostname);
/*
* CASE 4:
* Given that options->Address is a local host address, we want
* resolve_my_address() function to fail.
*/
resolved_addr = 0;
tor_free(options->Address);
options->Address = tor_strdup("127.0.0.1");
retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr,
&method_used,&hostname_out);
tt_want(resolved_addr == 0);
tt_assert(retval == -1);
tor_free(options->Address);
/*
* CASE 5:
* We want resolve_my_address() to fail if DNS address in options->Address
* cannot be resolved.
*/
MOCK(tor_lookup_hostname,tor_lookup_hostname_failure);
prev_n_hostname_failure = n_hostname_failure;
tor_free(options->Address);
options->Address = tor_strdup("www.tor-project.org");
retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr,
&method_used,&hostname_out);
tt_want(n_hostname_failure == prev_n_hostname_failure + 1);
tt_assert(retval == -1);
UNMOCK(tor_lookup_hostname);
tor_free(options->Address);
options->Address = NULL;
/*
* CASE 6:
* If options->Address is NULL AND gettting local hostname fails, we want
* resolve_my_address() to fail as well.
*/
MOCK(tor_gethostname,tor_gethostname_failure);
prev_n_gethostname_failure = n_gethostname_failure;
retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr,
&method_used,&hostname_out);
tt_want(n_gethostname_failure == prev_n_gethostname_failure + 1);
tt_assert(retval == -1);
UNMOCK(tor_gethostname);
/*
* CASE 7:
* We want resolve_my_address() to try and get network interface address via
* get_interface_address() if hostname returned by tor_gethostname() cannot be
* resolved into IP address.
*/
MOCK(tor_gethostname,tor_gethostname_replacement);
MOCK(get_interface_address,get_interface_address_08080808);
prev_n_gethostname_replacement = n_gethostname_replacement;
prev_n_get_interface_address = n_get_interface_address;
retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr,
&method_used,&hostname_out);
tt_want(retval == 0);
tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1);
tt_want(n_get_interface_address == prev_n_get_interface_address + 1);
tt_want_str_op(method_used,==,"INTERFACE");
tt_want(hostname_out == NULL);
tt_assert(resolved_addr == ntohl(0x08080808));
UNMOCK(get_interface_address);
/*
* CASE 8:
* Suppose options->Address is NULL AND hostname returned by tor_gethostname()
* is unresolvable. We want resolve_my_address to fail if
* get_interface_address() fails.
*/
MOCK(get_interface_address,get_interface_address_failure);
prev_n_get_interface_address_failure = n_get_interface_address_failure;
prev_n_gethostname_replacement = n_gethostname_replacement;
retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr,
&method_used,&hostname_out);
tt_want(n_get_interface_address_failure ==
prev_n_get_interface_address_failure + 1);
tt_want(n_gethostname_replacement ==
prev_n_gethostname_replacement + 1);
tt_assert(retval == -1);
UNMOCK(get_interface_address);
/*
* CASE 9:
* Given that options->Address is NULL AND tor_lookup_hostname()
* fails AND hostname returned by gethostname() resolves
* to local IP address, we want resolve_my_address() function to
* call get_interface_address6(.,AF_INET,.) and return IP address
* the latter function has found.
*/
MOCK(tor_lookup_hostname,tor_lookup_hostname_failure);
MOCK(tor_gethostname,tor_gethostname_replacement);
MOCK(get_interface_address6,get_interface_address6_replacement);
prev_n_gethostname_replacement = n_gethostname_replacement;
prev_n_hostname_failure = n_hostname_failure;
prev_n_get_interface_address6 = n_get_interface_address6;
retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr,
&method_used,&hostname_out);
tt_want(last_address6_family == AF_INET);
tt_want(n_get_interface_address6 == prev_n_get_interface_address6 + 1);
tt_want(n_hostname_failure == prev_n_hostname_failure + 1);
tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1);
tt_want(retval == 0);
tt_want_str_op(method_used,==,"INTERFACE");
tt_assert(htonl(resolved_addr) == 0x09090909);
UNMOCK(tor_lookup_hostname);
UNMOCK(tor_gethostname);
UNMOCK(get_interface_address6);
/*
* CASE 10: We want resolve_my_address() to fail if all of the following
* are true:
* 1. options->Address is not NULL
* 2. ... but it cannot be converted to struct in_addr by
* tor_inet_aton()
* 3. ... and tor_lookup_hostname() fails to resolve the
* options->Address
*/
MOCK(tor_lookup_hostname,tor_lookup_hostname_failure);
prev_n_hostname_failure = n_hostname_failure;
tor_free(options->Address);
options->Address = tor_strdup("some_hostname");
retval = resolve_my_address(LOG_NOTICE, options, &resolved_addr,
&method_used,&hostname_out);
tt_want(n_hostname_failure == prev_n_hostname_failure + 1);
tt_assert(retval == -1);
UNMOCK(tor_gethostname);
UNMOCK(tor_lookup_hostname);
/*
* CASE 11:
* Suppose the following sequence of events:
* 1. options->Address is NULL
* 2. tor_gethostname() succeeds to get hostname of machine Tor
* if running on.
* 3. Hostname from previous step cannot be converted to
* address by using tor_inet_aton() function.
* 4. However, tor_lookup_hostname() succeds in resolving the
* hostname from step 2.
* 5. Unfortunately, tor_addr_is_internal() deems this address
* to be internal.
* 6. get_interface_address6(.,AF_INET,.) returns non-internal
* IPv4
*
* We want resolve_my_addr() to succeed with method "INTERFACE"
* and address from step 6.
*/
tor_free(options->Address);
options->Address = NULL;
MOCK(tor_gethostname,tor_gethostname_replacement);
MOCK(tor_lookup_hostname,tor_lookup_hostname_localhost);
MOCK(get_interface_address6,get_interface_address6_replacement);
prev_n_gethostname_replacement = n_gethostname_replacement;
prev_n_hostname_localhost = n_hostname_localhost;
prev_n_get_interface_address6 = n_get_interface_address6;
retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr,
&method_used,&hostname_out);
tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1);
tt_want(n_hostname_localhost == prev_n_hostname_localhost + 1);
tt_want(n_get_interface_address6 == prev_n_get_interface_address6 + 1);
tt_str_op(method_used,==,"INTERFACE");
tt_assert(!hostname_out);
tt_assert(retval == 0);
/*
* CASE 11b:
* 1-5 as above.
* 6. get_interface_address6() fails.
*
* In this subcase, we want resolve_my_address() to fail.
*/
UNMOCK(get_interface_address6);
MOCK(get_interface_address6,get_interface_address6_failure);
prev_n_gethostname_replacement = n_gethostname_replacement;
prev_n_hostname_localhost = n_hostname_localhost;
prev_n_get_interface_address6_failure = n_get_interface_address6_failure;
retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr,
&method_used,&hostname_out);
tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1);
tt_want(n_hostname_localhost == prev_n_hostname_localhost + 1);
tt_want(n_get_interface_address6_failure ==
prev_n_get_interface_address6_failure + 1);
tt_assert(retval == -1);
UNMOCK(tor_gethostname);
UNMOCK(tor_lookup_hostname);
UNMOCK(get_interface_address6);
/* CASE 12:
* Suppose the following happens:
* 1. options->Address is NULL AND options->DirAuthorities is 1.
* 2. tor_gethostname() succeeds in getting hostname of a machine ...
* 3. ... which is successfully parsed by tor_inet_aton() ...
* 4. into IPv4 address that tor_addr_is_inernal() considers to be
* internal.
*
* In this case, we want resolve_my_address() to fail.
*/
tor_free(options->Address);
options->Address = NULL;
options->DirAuthorities = tor_malloc_zero(sizeof(config_line_t));
MOCK(tor_gethostname,tor_gethostname_localhost);
prev_n_gethostname_localhost = n_gethostname_localhost;
retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr,
&method_used,&hostname_out);
tt_want(n_gethostname_localhost == prev_n_gethostname_localhost + 1);
tt_assert(retval == -1);
UNMOCK(tor_gethostname);
done:
tor_free(options->Address);
tor_free(options->DirAuthorities);
or_options_free(options);
UNMOCK(tor_gethostname);
UNMOCK(tor_lookup_hostname);
UNMOCK(get_interface_address);
UNMOCK(get_interface_address6);
UNMOCK(tor_gethostname);
}
#define CONFIG_TEST(name, flags) \
{ #name, test_config_ ## name, flags, NULL, NULL }
struct testcase_t config_tests[] = {
CONFIG_TEST(resolve_my_address, TT_FORK),
CONFIG_TEST(addressmap, 0),
CONFIG_TEST(parse_bridge_line, 0),
CONFIG_TEST(parse_transport_options_line, 0),