[tor-commits] [tor/master] Make tor_ersatz_socketpair work on IPv6-only systems

nickm at torproject.org nickm at torproject.org
Thu Nov 19 15:48:53 UTC 2015


commit 53ec840bdfa3dfa421b3cecd7a90a3cd5e39d15c
Author: teor (Tim Wilson-Brown) <teor2345 at gmail.com>
Date:   Thu Nov 19 19:08:22 2015 +1100

    Make tor_ersatz_socketpair work on IPv6-only systems
    
    (But it won't work on some systems without IPv4/IPv6 localhost
    (some BSD jails) by design, to avoid creating sockets on routable
    IP addresses. However, those systems likely have the AF_UNIX socketpair,
    which tor prefers.)
    
    Fixes bug #17638; bugfix on a very early tor version,
    earlier than 22dba27d8dd5 (23 Nov 2004) / svn:r2943.
    
    Patch by "teor".
---
 changes/bug17638-ipv6-ersatz-socketpair |    5 ++
 src/common/compat.c                     |   88 ++++++++++++++++++++++---------
 src/test/test_util.c                    |   16 ++----
 3 files changed, 74 insertions(+), 35 deletions(-)

diff --git a/changes/bug17638-ipv6-ersatz-socketpair b/changes/bug17638-ipv6-ersatz-socketpair
new file mode 100644
index 0000000..6193065
--- /dev/null
+++ b/changes/bug17638-ipv6-ersatz-socketpair
@@ -0,0 +1,5 @@
+  o Minor bugfix (IPv6 compatibility, unit tests):
+    - Make tor_ersatz_socketpair work on IPv6-only systems.
+      Fixes bug #17638; bugfix on a very early tor version,
+      earlier than 22dba27d8dd5 (23 Nov 2004) / svn:r2943.
+      Patch by "teor".
diff --git a/src/common/compat.c b/src/common/compat.c
index 7d72b4b..24698c3 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -1486,6 +1486,12 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
 }
 
 #ifdef NEED_ERSATZ_SOCKETPAIR
+
+#define SIZEOF_SOCKADDR(domain) \
+  (domain == AF_INET ? sizeof(struct sockaddr_in) : \
+   (domain == AF_INET6 ? sizeof(struct sockaddr_in6) : \
+    ((size_t)0) /* unsupported, don't match any valid size */))
+
 /**
  * Helper used to implement socketpair on systems that lack it, by
  * making a direct connection to localhost.
@@ -1501,12 +1507,19 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
     tor_socket_t listener = TOR_INVALID_SOCKET;
     tor_socket_t connector = TOR_INVALID_SOCKET;
     tor_socket_t acceptor = TOR_INVALID_SOCKET;
-    struct sockaddr_in listen_addr;
-    struct sockaddr_in connect_addr;
+    tor_addr_t listen_tor_addr;
+    struct sockaddr listen_addr;
+    in_port_t listen_port = 0;
+    tor_addr_t connect_tor_addr;
+    in_port_t connect_port = 0;
+    struct sockaddr  connect_addr;
     socklen_t size;
     int saved_errno = -1;
+    int ersatz_domain = AF_INET;
 
+    memset(&connect_tor_addr, 0, sizeof(connect_tor_addr));
     memset(&connect_addr, 0, sizeof(connect_addr));
+    memset(&listen_tor_addr, 0, sizeof(listen_tor_addr));
     memset(&listen_addr, 0, sizeof(listen_addr));
 
     if (protocol
@@ -1524,47 +1537,71 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
       return -EINVAL;
     }
 
-    listener = tor_open_socket(AF_INET, type, 0);
-    if (!SOCKET_OK(listener))
-      return -tor_socket_errno(-1);
-    memset(&listen_addr, 0, sizeof(listen_addr));
-    listen_addr.sin_family = AF_INET;
-    listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-    listen_addr.sin_port = 0;   /* kernel chooses port.  */
-    if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr))
-        == -1)
+    listener = tor_open_socket(ersatz_domain, type, 0);
+    if (!SOCKET_OK(listener)) {
+      int first_errno = tor_socket_errno(-1);
+      if (first_errno == SOCK_ERRNO(EPROTONOSUPPORT)
+          && ersatz_domain == AF_INET) {
+        /* Assume we're on an IPv6-only system */
+        ersatz_domain = AF_INET6;
+        listener = tor_open_socket(ersatz_domain, type, 0);
+        if (!SOCKET_OK(listener)) {
+          /* Keep the previous behaviour, which was to return the IPv4 error.
+           * (This may be less informative on IPv6-only systems.)
+           * XX/teor - is there a better way to decide which errno to return?
+           * (I doubt we care much either way, once there is an error.)
+           */
+          return -first_errno;
+        }
+      }
+    }
+    /* If there is no 127.0.0.1 or ::1, this will and must fail. Otherwise, we
+     * risk exposing a socketpair on a routable IP address. (Some BSD jails
+     * use a routable address for localhost. Fortunately, they have the real
+     * AF_UNIX socketpair.) */
+    if (ersatz_domain == AF_INET) {
+      tor_addr_from_ipv4h(&listen_tor_addr, INADDR_LOOPBACK);
+    } else {
+      tor_addr_parse(&listen_tor_addr, "[::1]");
+    }
+    tor_assert(tor_addr_is_loopback(&listen_tor_addr));
+    tor_addr_to_sockaddr(&listen_tor_addr,
+                         0 /* kernel chooses port.  */,
+                         &listen_addr,
+                         sizeof (listen_addr));
+    if (bind(listener, &listen_addr, sizeof (listen_addr)) == -1)
       goto tidy_up_and_fail;
     if (listen(listener, 1) == -1)
       goto tidy_up_and_fail;
 
-    connector = tor_open_socket(AF_INET, type, 0);
+    connector = tor_open_socket(ersatz_domain, type, 0);
     if (!SOCKET_OK(connector))
       goto tidy_up_and_fail;
     /* We want to find out the port number to connect to.  */
     size = sizeof(connect_addr);
-    if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1)
+    if (getsockname(listener, &connect_addr, &size) == -1)
       goto tidy_up_and_fail;
-    if (size != sizeof (connect_addr))
+    if (size != SIZEOF_SOCKADDR (connect_addr.sa_family))
       goto abort_tidy_up_and_fail;
-    if (connect(connector, (struct sockaddr *) &connect_addr,
-                sizeof(connect_addr)) == -1)
+    if (connect(connector, &connect_addr, sizeof(connect_addr)) == -1)
       goto tidy_up_and_fail;
 
     size = sizeof(listen_addr);
-    acceptor = tor_accept_socket(listener,
-                                 (struct sockaddr *) &listen_addr, &size);
+    acceptor = tor_accept_socket(listener, &listen_addr, &size);
     if (!SOCKET_OK(acceptor))
       goto tidy_up_and_fail;
-    if (size != sizeof(listen_addr))
+    if (size != SIZEOF_SOCKADDR(listen_addr.sa_family))
       goto abort_tidy_up_and_fail;
     /* Now check we are talking to ourself by matching port and host on the
        two sockets.  */
-    if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1)
+    if (getsockname(connector, &connect_addr, &size) == -1)
       goto tidy_up_and_fail;
-    if (size != sizeof (connect_addr)
-        || listen_addr.sin_family != connect_addr.sin_family
-        || listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
-        || listen_addr.sin_port != connect_addr.sin_port) {
+    /* Set *_tor_addr and *_port to the address and port that was used */
+    tor_addr_from_sockaddr(&listen_tor_addr, &listen_addr, &listen_port);
+    tor_addr_from_sockaddr(&connect_tor_addr, &connect_addr, &connect_port);
+    if (size != SIZEOF_SOCKADDR (connect_addr.sa_family)
+        || tor_addr_compare(&listen_tor_addr, &connect_tor_addr, CMP_SEMANTIC)
+        || listen_port != connect_port) {
       goto abort_tidy_up_and_fail;
     }
     tor_close_socket(listener);
@@ -1590,6 +1627,9 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
       tor_close_socket(acceptor);
     return -saved_errno;
 }
+
+#undef SIZEOF_SOCKADDR
+
 #endif
 
 /* Return the maximum number of allowed sockets. */
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 187cb23..eecc50a 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -4342,9 +4342,6 @@ fd_is_nonblocking(tor_socket_t fd)
 }
 #endif
 
-#define ERRNO_IS_EPROTO(e)    (e == SOCK_ERRNO(EPROTONOSUPPORT))
-#define SOCK_ERR_IS_EPROTO(s) ERRNO_IS_EPROTO(tor_socket_errno(s))
-
 /* Test for tor_open_socket*, using IPv4 or IPv6 depending on arg. */
 static void
 test_util_socket(void *arg)
@@ -4361,7 +4358,7 @@ test_util_socket(void *arg)
   (void)arg;
 
   fd1 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 0, 0);
-  if (SOCK_ERR_IS_EPROTO(fd1)) {
+  if (tor_socket_errno(fd1) == SOCK_ERRNO(EPROTONOSUPPORT)) {
     /* Assume we're on an IPv4-only or IPv6-only system, and give up now. */
     goto done;
   }
@@ -4424,11 +4421,10 @@ test_util_socketpair(void *arg)
   int socketpair_result = 0;
 
   socketpair_result = tor_socketpair_fn(family, SOCK_STREAM, 0, fds);
-  if (ersatz && ERRNO_IS_EPROTO(-socketpair_result)) {
-    /* Assume we're on an IPv6-only system, and give up now.
-     * (tor_ersatz_socketpair uses IPv4.) */
-    goto done;
-  }
+  /* If there is no 127.0.0.1 or ::1, tor_ersatz_socketpair will and must fail.
+   * Otherwise, we risk exposing a socketpair on a routable IP address. (Some
+   * BSD jails use a routable address for localhost. Fortunately, they have
+   * the real AF_UNIX socketpair.) */
   tt_int_op(0, OP_EQ, socketpair_result);
   tt_assert(SOCKET_OK(fds[0]));
   tt_assert(SOCKET_OK(fds[1]));
@@ -4449,8 +4445,6 @@ test_util_socketpair(void *arg)
     tor_close_socket(fds[1]);
 }
 
-#undef SOCKET_EPROTO
-
 static void
 test_util_max_mem(void *arg)
 {





More information about the tor-commits mailing list