aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2015-03-31 14:37:09 -0400
committerNick Mathewson <nickm@torproject.org>2015-03-31 14:37:09 -0400
commit30e933b13660e62d10997f4809b3f13235b30325 (patch)
tree51047f6ec5b72865e0a5fbb0cac60fde115c916a
parent732322b710d515a55ebbaa984fa12f19ccb333e8 (diff)
parentb49ffb2a21ccbcf62c3e38b822043b4302d8c26f (diff)
downloadtor-30e933b13660e62d10997f4809b3f13235b30325.tar.gz
tor-30e933b13660e62d10997f4809b3f13235b30325.zip
Merge branch 'ticket14710_squashed'
-rw-r--r--changes/ticket1471010
-rw-r--r--src/common/address.c99
-rw-r--r--src/common/address.h3
-rw-r--r--src/common/compat.c20
-rw-r--r--src/common/compat.h10
-rw-r--r--src/test/test_address.c175
6 files changed, 274 insertions, 43 deletions
diff --git a/changes/ticket14710 b/changes/ticket14710
new file mode 100644
index 0000000000..2b6cb2060f
--- /dev/null
+++ b/changes/ticket14710
@@ -0,0 +1,10 @@
+ o Code simplification and refactoring:
+ - Move the hacky fallback code out of get_interface_address6()
+ into separate function and get it covered with unit-tests. Resolves
+ ticket 14710.
+
+ o Minor bugfixes:
+ - When attempting to use fallback technique for network interface
+ lookup, disregard loopback and multicast addresses since they are
+ unsuitable for public communications.
+
diff --git a/src/common/address.c b/src/common/address.c
index 42a116a91e..6bd107889a 100644
--- a/src/common/address.c
+++ b/src/common/address.c
@@ -1504,47 +1504,22 @@ tor_addr_is_multicast(const tor_addr_t *a)
return 0;
}
-/** Set *<b>addr</b> to the IP address (if any) of whatever interface
- * connects to the Internet. This address should only be used in checking
- * whether our address has changed. Return 0 on success, -1 on failure.
+/** Attempt to retrieve IP address of current host by utilizing some
+ * UDP socket trickery. Only look for address of given <b>family</b>.
+ * Set result to *<b>addr</b>. Return 0 on success, -1 on failure.
*/
-MOCK_IMPL(int,
-get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
+STATIC int
+get_interface_address6_via_udp_socket_hack(int severity,
+ sa_family_t family,
+ tor_addr_t *addr)
{
- /* XXX really, this function should yield a smartlist of addresses. */
- smartlist_t *addrs;
- int sock=-1, r=-1;
struct sockaddr_storage my_addr, target_addr;
+ int sock=-1, r=-1;
socklen_t addr_len;
- tor_assert(addr);
-
- /* Try to do this the smart way if possible. */
- if ((addrs = get_interface_addresses_raw(severity))) {
- int rv = -1;
- SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) {
- if (family != AF_UNSPEC && family != tor_addr_family(a))
- continue;
- if (tor_addr_is_loopback(a) ||
- tor_addr_is_multicast(a))
- continue;
- tor_addr_copy(addr, a);
- rv = 0;
-
- /* If we found a non-internal address, declare success. Otherwise,
- * keep looking. */
- if (!tor_addr_is_internal(a, 0))
- break;
- } SMARTLIST_FOREACH_END(a);
-
- SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a));
- smartlist_free(addrs);
- return rv;
- }
-
- /* Okay, the smart way is out. */
memset(addr, 0, sizeof(tor_addr_t));
memset(&target_addr, 0, sizeof(target_addr));
+
/* Don't worry: no packets are sent. We just need to use a real address
* on the actual Internet. */
if (family == AF_INET6) {
@@ -1566,6 +1541,7 @@ get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
} else {
return -1;
}
+
if (sock < 0) {
int e = tor_socket_errno(-1);
log_fn(severity, LD_NET, "unable to create socket: %s",
@@ -1573,27 +1549,74 @@ get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
goto err;
}
- if (connect(sock,(struct sockaddr *)&target_addr, addr_len) < 0) {
+ if (tor_connect_socket(sock,(struct sockaddr *)&target_addr,
+ addr_len) < 0) {
int e = tor_socket_errno(sock);
log_fn(severity, LD_NET, "connect() failed: %s", tor_socket_strerror(e));
goto err;
}
- if (getsockname(sock,(struct sockaddr*)&my_addr, &addr_len)) {
+ if (tor_getsockname(sock,(struct sockaddr*)&my_addr, &addr_len)) {
int e = tor_socket_errno(sock);
log_fn(severity, LD_NET, "getsockname() to determine interface failed: %s",
tor_socket_strerror(e));
goto err;
}
- tor_addr_from_sockaddr(addr, (struct sockaddr*)&my_addr, NULL);
- r=0;
+ if (tor_addr_from_sockaddr(addr, (struct sockaddr*)&my_addr, NULL) == 0) {
+ if (tor_addr_is_loopback(addr) || tor_addr_is_multicast(addr)) {
+ log_fn(severity, LD_NET, "Address that we determined via UDP socket"
+ " magic is unsuitable for public comms.");
+ } else {
+ r=0;
+ }
+ }
+
err:
if (sock >= 0)
tor_close_socket(sock);
return r;
}
+/** Set *<b>addr</b> to the IP address (if any) of whatever interface
+ * connects to the Internet. This address should only be used in checking
+ * whether our address has changed. Return 0 on success, -1 on failure.
+ */
+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;
+ tor_assert(addr);
+
+ /* Try to do this the smart way if possible. */
+ if ((addrs = get_interface_addresses_raw(severity))) {
+ int rv = -1;
+ SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) {
+ if (family != AF_UNSPEC && family != tor_addr_family(a))
+ continue;
+ if (tor_addr_is_loopback(a) ||
+ tor_addr_is_multicast(a))
+ continue;
+
+ tor_addr_copy(addr, a);
+ rv = 0;
+
+ /* If we found a non-internal address, declare success. Otherwise,
+ * keep looking. */
+ if (!tor_addr_is_internal(a, 0))
+ break;
+ } SMARTLIST_FOREACH_END(a);
+
+ SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a));
+ smartlist_free(addrs);
+ return rv;
+ }
+
+ /* Okay, the smart way is out. */
+ return get_interface_address6_via_udp_socket_hack(severity,family,addr);
+}
+
/* ======
* IPv4 helpers
* XXXX024 IPv6 deprecate some of these.
diff --git a/src/common/address.h b/src/common/address.h
index df835e917a..cd80615f93 100644
--- a/src/common/address.h
+++ b/src/common/address.h
@@ -274,6 +274,9 @@ tor_addr_port_t *tor_addr_port_new(const tor_addr_t *addr, uint16_t port);
#ifdef ADDRESS_PRIVATE
STATIC smartlist_t *get_interface_addresses_raw(int severity);
+STATIC int get_interface_address6_via_udp_socket_hack(int severity,
+ sa_family_t family,
+ tor_addr_t *addr);
#ifdef HAVE_IFADDRS_TO_SMARTLIST
STATIC smartlist_t *ifaddrs_to_smartlist(const struct ifaddrs *ifa);
diff --git a/src/common/compat.c b/src/common/compat.c
index 1788e32ee3..8da7ef3f69 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -1156,12 +1156,20 @@ mark_socket_open(tor_socket_t s)
/** @} */
/** As socket(), but counts the number of open sockets. */
-tor_socket_t
-tor_open_socket(int domain, int type, int protocol)
+MOCK_IMPL(tor_socket_t,
+tor_open_socket,(int domain, int type, int protocol))
{
return tor_open_socket_with_extensions(domain, type, protocol, 1, 0);
}
+/** Mockable wrapper for connect(). */
+MOCK_IMPL(tor_socket_t,
+tor_connect_socket,(tor_socket_t socket,const struct sockaddr *address,
+ socklen_t address_len))
+{
+ return connect(socket,address,address_len);
+}
+
/** As socket(), but creates a nonblocking socket and
* counts the number of open sockets. */
tor_socket_t
@@ -1308,6 +1316,14 @@ get_n_open_sockets(void)
return n;
}
+/** Mockable wrapper for getsockname(). */
+MOCK_IMPL(int,
+tor_getsockname,(tor_socket_t socket, struct sockaddr *address,
+ socklen_t *address_len))
+{
+ return getsockname(socket, address, address_len);
+}
+
/** Turn <b>socket</b> into a nonblocking socket. Return 0 on success, -1
* on failure.
*/
diff --git a/src/common/compat.h b/src/common/compat.h
index 11b41cded9..5189b7e056 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -463,7 +463,8 @@ int tor_close_socket(tor_socket_t s);
tor_socket_t tor_open_socket_with_extensions(
int domain, int type, int protocol,
int cloexec, int nonblock);
-tor_socket_t tor_open_socket(int domain, int type, int protocol);
+MOCK_DECL(tor_socket_t,
+tor_open_socket,(int domain, int type, int protocol));
tor_socket_t tor_open_socket_nonblocking(int domain, int type, int protocol);
tor_socket_t tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr,
socklen_t *len);
@@ -474,8 +475,15 @@ tor_socket_t tor_accept_socket_with_extensions(tor_socket_t sockfd,
struct sockaddr *addr,
socklen_t *len,
int cloexec, int nonblock);
+MOCK_DECL(tor_socket_t,
+tor_connect_socket,(tor_socket_t socket,const struct sockaddr *address,
+ socklen_t address_len));
int get_n_open_sockets(void);
+MOCK_DECL(int,
+tor_getsockname,(tor_socket_t socket, struct sockaddr *address,
+ socklen_t *address_len));
+
#define tor_socket_send(s, buf, len, flags) send(s, buf, len, flags)
#define tor_socket_recv(s, buf, len, flags) recv(s, buf, len, flags)
diff --git a/src/test/test_address.c b/src/test/test_address.c
index 424a6352b0..317b370912 100644
--- a/src/test/test_address.c
+++ b/src/test/test_address.c
@@ -130,8 +130,8 @@ test_address_ifaddrs_to_smartlist(void *arg)
ipv6_sockaddr = tor_malloc(sizeof(struct sockaddr_in6));
ipv6_sockaddr->sin6_family = AF_INET6;
ipv6_sockaddr->sin6_port = 0;
- inet_pton(AF_INET6, "2001:db8:8714:3a90::12",
- &(ipv6_sockaddr->sin6_addr));
+ 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));
@@ -452,10 +452,181 @@ test_address_get_if_addrs_ioctl(void *arg)
#endif
+#define FAKE_SOCKET_FD (42)
+
+tor_socket_t
+fake_open_socket(int domain, int type, int protocol)
+{
+ (void)domain;
+ (void)type;
+ (void)protocol;
+
+ return FAKE_SOCKET_FD;
+}
+
+static int last_connected_socket_fd = 0;
+
+static int connect_retval = 0;
+
+tor_socket_t
+pretend_to_connect(tor_socket_t socket, const struct sockaddr *address,
+ socklen_t address_len)
+{
+ (void)address;
+ (void)address_len;
+
+ last_connected_socket_fd = socket;
+
+ return connect_retval;
+}
+
+static struct sockaddr *mock_addr = NULL;
+
+int
+fake_getsockname(tor_socket_t socket, struct sockaddr *address,
+ socklen_t *address_len)
+{
+ socklen_t bytes_to_copy = 0;
+
+ 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_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,==,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,==,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));
+
+ UNMOCK(tor_open_socket);
+ UNMOCK(tor_connect_socket);
+ UNMOCK(tor_getsockname);
+
+ done:
+ 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;
+
+ 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,==,retval_reference);
+ tt_assert( (retval == -1 && retval_reference == -1) ||
+ (tor_addr_compare(&addr4,&addr4_to_check,CMP_EXACT) == 0) );
+
+ //[XXX: Skipping the AF_INET6 case because bug #12377 makes it fail.]
+ (void)addr6_to_check;
+ (void)addr6;
+#if 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,==,retval_reference);
+ tt_assert( (retval == -1 && retval_reference == -1) ||
+ (tor_addr_compare(&addr6,&addr6_to_check,CMP_EXACT) == 0) );
+
+#endif
+
+ /* 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_CCITT,&addr4);
+
+ tt_assert(retval == -1);
+
+ done:
+ return;
+}
+
#define ADDRESS_TEST(name, flags) \
{ #name, test_address_ ## name, flags, NULL, NULL }
struct testcase_t address_tests[] = {
+ ADDRESS_TEST(udp_socket_trick_whitebox, TT_FORK),
+ ADDRESS_TEST(udp_socket_trick_blackbox, TT_FORK),
#ifdef HAVE_IFADDRS_TO_SMARTLIST
ADDRESS_TEST(get_if_addrs_ifaddrs, TT_FORK),
ADDRESS_TEST(ifaddrs_to_smartlist, 0),