tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

socketpair.c (6341B)


      1 /* Copyright (c) 2003-2004, Roger Dingledine
      2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
      3 * Copyright (c) 2007-2021, The Tor Project, Inc. */
      4 
      5 /**
      6 * @file socketpair.c
      7 * @brief Replacement socketpair() for systems that lack it
      8 **/
      9 
     10 #include "lib/cc/torint.h"
     11 #include "lib/net/socketpair.h"
     12 #include "lib/net/inaddr_st.h"
     13 #include "lib/arch/bytes.h"
     14 
     15 #include <errno.h>
     16 #include <string.h>
     17 
     18 #ifdef HAVE_UNISTD_H
     19 #include <unistd.h>
     20 #endif
     21 #ifdef HAVE_NETINET_IN_H
     22 #include <netinet/in.h>
     23 #endif
     24 
     25 #ifdef _WIN32
     26 #include <winsock2.h>
     27 #include <windows.h>
     28 #define socket_errno() (WSAGetLastError())
     29 #define SOCKET_EPROTONOSUPPORT WSAEPROTONOSUPPORT
     30 #else /* !defined(_WIN32) */
     31 #define closesocket(x) close(x)
     32 #define socket_errno() (errno)
     33 #define SOCKET_EPROTONOSUPPORT EPROTONOSUPPORT
     34 #endif /* defined(_WIN32) */
     35 
     36 #ifdef NEED_ERSATZ_SOCKETPAIR
     37 
     38 // Avoid warning about call to memcmp.
     39 #define raw_memcmp memcmp
     40 
     41 /**
     42 * Return a new socket that is bound and listening on the loopback interface
     43 * of family <b>family</b> for a socket of type <b>type</b>. On failure return
     44 * TOR_INVALID_SOCKET.
     45 */
     46 static tor_socket_t
     47 get_local_listener(int family, int type)
     48 {
     49  struct sockaddr_in sin;
     50  struct sockaddr_in6 sin6;
     51  struct sockaddr *sa;
     52  int len;
     53 
     54  memset(&sin, 0, sizeof(sin));
     55  memset(&sin6, 0, sizeof(sin6));
     56 
     57  tor_socket_t sock = TOR_INVALID_SOCKET;
     58  sock = socket(family, type, 0);
     59  if (!SOCKET_OK(sock)) {
     60    return TOR_INVALID_SOCKET;
     61  }
     62 
     63  if (family == AF_INET) {
     64    sa = (struct sockaddr *) &sin;
     65    sin.sin_family = AF_INET;
     66    sin.sin_addr.s_addr = tor_htonl(0x7f000001);
     67    len = sizeof(sin);
     68  } else {
     69    sa = (struct sockaddr *) &sin6;
     70    sin6.sin6_family = AF_INET6;
     71    sin6.sin6_addr.s6_addr[15] = 1;
     72    len = sizeof(sin6);
     73  }
     74 
     75  if (bind(sock, sa, len) == -1)
     76    goto err;
     77  if (listen(sock, 1) == -1)
     78    goto err;
     79 
     80  return sock;
     81 err:
     82  closesocket(sock);
     83  return TOR_INVALID_SOCKET;
     84 }
     85 
     86 /**
     87 * Return true iff sa1 and sa2 are equivalent AF_INET or AF_INET6 addresses.
     88 */
     89 static int
     90 sockaddr_eq(struct sockaddr *sa1, struct sockaddr *sa2)
     91 {
     92  if (sa1->sa_family != sa2->sa_family)
     93    return 0;
     94 
     95  if (sa1->sa_family == AF_INET6) {
     96    struct sockaddr_in6 *sin6_1 = (struct sockaddr_in6 *) sa1;
     97    struct sockaddr_in6 *sin6_2 = (struct sockaddr_in6 *) sa2;
     98    return sin6_1->sin6_port == sin6_2->sin6_port &&
     99      0==raw_memcmp(sin6_1->sin6_addr.s6_addr, sin6_2->sin6_addr.s6_addr, 16);
    100  } else if (sa1->sa_family == AF_INET) {
    101    struct sockaddr_in *sin_1 = (struct sockaddr_in *) sa1;
    102    struct sockaddr_in *sin_2 = (struct sockaddr_in *) sa2;
    103    return sin_1->sin_port == sin_2->sin_port &&
    104      sin_1->sin_addr.s_addr == sin_2->sin_addr.s_addr;
    105  } else {
    106    return 0;
    107  }
    108 }
    109 
    110 /**
    111 * Helper used to implement socketpair on systems that lack it, by
    112 * making a direct connection to localhost.
    113 *
    114 * See tor_socketpair() for details.
    115 *
    116 * The direct connection defaults to IPv4, but falls back to IPv6 if
    117 * IPv4 is not supported.
    118 **/
    119 int
    120 tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
    121 {
    122  /* This socketpair does not work when localhost is down. So
    123   * it's really not the same thing at all. But it's close enough
    124   * for now, and really, when localhost is down sometimes, we
    125   * have other problems too.
    126   */
    127  tor_socket_t listener = TOR_INVALID_SOCKET;
    128  tor_socket_t connector = TOR_INVALID_SOCKET;
    129  tor_socket_t acceptor = TOR_INVALID_SOCKET;
    130  struct sockaddr_storage accepted_addr_ss;
    131  struct sockaddr_storage connect_addr_ss;
    132  struct sockaddr *connect_addr = (struct sockaddr *) &connect_addr_ss;
    133  struct sockaddr *accepted_addr = (struct sockaddr *) &accepted_addr_ss;
    134  socklen_t size;
    135  int saved_errno = -1;
    136  int ersatz_domain = AF_INET;
    137  socklen_t addrlen = sizeof(struct sockaddr_in);
    138 
    139  memset(&accepted_addr_ss, 0, sizeof(accepted_addr_ss));
    140  memset(&connect_addr_ss, 0, sizeof(connect_addr_ss));
    141 
    142  if (protocol
    143 #ifdef AF_UNIX
    144      || family != AF_UNIX
    145 #endif
    146      ) {
    147 #ifdef _WIN32
    148    return -WSAEAFNOSUPPORT;
    149 #else
    150    return -EAFNOSUPPORT;
    151 #endif
    152  }
    153  if (!fd) {
    154    return -EINVAL;
    155  }
    156 
    157  listener = get_local_listener(ersatz_domain, type);
    158  if (!SOCKET_OK(listener)) {
    159    int first_errno = socket_errno();
    160    if (first_errno == SOCKET_EPROTONOSUPPORT) {
    161      /* Assume we're on an IPv6-only system */
    162      ersatz_domain = AF_INET6;
    163      addrlen = sizeof(struct sockaddr_in6);
    164      listener = get_local_listener(ersatz_domain, type);
    165    }
    166    if (!SOCKET_OK(listener)) {
    167      /* Keep the previous behaviour, which was to return the IPv4 error.
    168       * (This may be less informative on IPv6-only systems.)
    169       * XX/teor - is there a better way to decide which errno to return?
    170       * (I doubt we care much either way, once there is an error.)
    171       */
    172      return -first_errno;
    173    }
    174  }
    175 
    176  connector = socket(ersatz_domain, type, 0);
    177  if (!SOCKET_OK(connector))
    178    goto tidy_up_and_fail;
    179  /* We want to find out the port number to connect to.  */
    180  size = sizeof(connect_addr_ss);
    181  if (getsockname(listener, connect_addr, &size) == -1)
    182    goto tidy_up_and_fail;
    183  if (size != addrlen)
    184    goto abort_tidy_up_and_fail;
    185  if (connect(connector, connect_addr, size) == -1)
    186    goto tidy_up_and_fail;
    187 
    188  size = sizeof(accepted_addr_ss);
    189  acceptor = accept(listener, accepted_addr, &size);
    190  if (!SOCKET_OK(acceptor))
    191    goto tidy_up_and_fail;
    192  if (size != addrlen)
    193    goto abort_tidy_up_and_fail;
    194  /* Now check we are talking to ourself by matching port and host on the
    195     two sockets.  */
    196  if (getsockname(connector, connect_addr, &size) == -1)
    197    goto tidy_up_and_fail;
    198  /* Set *_tor_addr and *_port to the address and port that was used */
    199  if (!sockaddr_eq(accepted_addr, connect_addr))
    200    goto abort_tidy_up_and_fail;
    201  closesocket(listener);
    202  fd[0] = connector;
    203  fd[1] = acceptor;
    204  return 0;
    205 
    206 abort_tidy_up_and_fail:
    207 #ifdef _WIN32
    208  saved_errno = WSAECONNABORTED;
    209 #else
    210  saved_errno = ECONNABORTED; /* I hope this is portable and appropriate.  */
    211 #endif
    212 tidy_up_and_fail:
    213  if (saved_errno < 0)
    214    saved_errno = errno;
    215  if (SOCKET_OK(listener))
    216    closesocket(listener);
    217  if (SOCKET_OK(connector))
    218    closesocket(connector);
    219  if (SOCKET_OK(acceptor))
    220    closesocket(acceptor);
    221  return -saved_errno;
    222 }
    223 
    224 #endif /* defined(NEED_ERSATZ_SOCKETPAIR) */