LCOV - code coverage report
Current view: top level - lib/net - socketpair.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 60 97 61.9 %
Date: 2021-11-24 03:28:48 Functions: 3 3 100.0 %

          Line data    Source code
       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           1 : get_local_listener(int family, int type)
      48             : {
      49           1 :   struct sockaddr_in sin;
      50           1 :   struct sockaddr_in6 sin6;
      51           1 :   struct sockaddr *sa;
      52           1 :   int len;
      53             : 
      54           1 :   memset(&sin, 0, sizeof(sin));
      55           1 :   memset(&sin6, 0, sizeof(sin6));
      56             : 
      57           1 :   tor_socket_t sock = TOR_INVALID_SOCKET;
      58           1 :   sock = socket(family, type, 0);
      59           1 :   if (!SOCKET_OK(sock)) {
      60             :     return TOR_INVALID_SOCKET;
      61             :   }
      62             : 
      63           1 :   if (family == AF_INET) {
      64           1 :     sa = (struct sockaddr *) &sin;
      65           1 :     sin.sin_family = AF_INET;
      66           1 :     sin.sin_addr.s_addr = tor_htonl(0x7f000001);
      67           1 :     len = sizeof(sin);
      68             :   } else {
      69           0 :     sa = (struct sockaddr *) &sin6;
      70           0 :     sin6.sin6_family = AF_INET6;
      71           0 :     sin6.sin6_addr.s6_addr[15] = 1;
      72           0 :     len = sizeof(sin6);
      73             :   }
      74             : 
      75           1 :   if (bind(sock, sa, len) == -1)
      76           0 :     goto err;
      77           1 :   if (listen(sock, 1) == -1)
      78           0 :     goto err;
      79             : 
      80             :   return sock;
      81           0 :  err:
      82           0 :   closesocket(sock);
      83           0 :   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           1 : sockaddr_eq(struct sockaddr *sa1, struct sockaddr *sa2)
      91             : {
      92           1 :   if (sa1->sa_family != sa2->sa_family)
      93             :     return 0;
      94             : 
      95           1 :   if (sa1->sa_family == AF_INET6) {
      96           0 :     struct sockaddr_in6 *sin6_1 = (struct sockaddr_in6 *) sa1;
      97           0 :     struct sockaddr_in6 *sin6_2 = (struct sockaddr_in6 *) sa2;
      98           0 :     return sin6_1->sin6_port == sin6_2->sin6_port &&
      99           0 :       0==raw_memcmp(sin6_1->sin6_addr.s6_addr, sin6_2->sin6_addr.s6_addr, 16);
     100           1 :   } else if (sa1->sa_family == AF_INET) {
     101           1 :     struct sockaddr_in *sin_1 = (struct sockaddr_in *) sa1;
     102           1 :     struct sockaddr_in *sin_2 = (struct sockaddr_in *) sa2;
     103           1 :     return sin_1->sin_port == sin_2->sin_port &&
     104           1 :       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           1 : 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           1 :   tor_socket_t listener = TOR_INVALID_SOCKET;
     128           1 :   tor_socket_t connector = TOR_INVALID_SOCKET;
     129           1 :   tor_socket_t acceptor = TOR_INVALID_SOCKET;
     130           1 :   struct sockaddr_storage accepted_addr_ss;
     131           1 :   struct sockaddr_storage connect_addr_ss;
     132           1 :   struct sockaddr *connect_addr = (struct sockaddr *) &connect_addr_ss;
     133           1 :   struct sockaddr *accepted_addr = (struct sockaddr *) &accepted_addr_ss;
     134           1 :   socklen_t size;
     135           1 :   int saved_errno = -1;
     136           1 :   int ersatz_domain = AF_INET;
     137           1 :   socklen_t addrlen = sizeof(struct sockaddr_in);
     138             : 
     139           1 :   memset(&accepted_addr_ss, 0, sizeof(accepted_addr_ss));
     140           1 :   memset(&connect_addr_ss, 0, sizeof(connect_addr_ss));
     141             : 
     142           1 :   if (protocol
     143             : #ifdef AF_UNIX
     144           1 :       || family != AF_UNIX
     145             : #endif
     146             :       ) {
     147             : #ifdef _WIN32
     148             :     return -WSAEAFNOSUPPORT;
     149             : #else
     150             :     return -EAFNOSUPPORT;
     151             : #endif
     152             :   }
     153           1 :   if (!fd) {
     154             :     return -EINVAL;
     155             :   }
     156             : 
     157           1 :   listener = get_local_listener(ersatz_domain, type);
     158           1 :   if (!SOCKET_OK(listener)) {
     159           0 :     int first_errno = socket_errno();
     160           0 :     if (first_errno == SOCKET_EPROTONOSUPPORT) {
     161             :       /* Assume we're on an IPv6-only system */
     162           0 :       ersatz_domain = AF_INET6;
     163           0 :       addrlen = sizeof(struct sockaddr_in6);
     164           0 :       listener = get_local_listener(ersatz_domain, type);
     165             :     }
     166           0 :     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           0 :       return -first_errno;
     173             :     }
     174             :   }
     175             : 
     176           1 :   connector = socket(ersatz_domain, type, 0);
     177           1 :   if (!SOCKET_OK(connector))
     178           0 :     goto tidy_up_and_fail;
     179             :   /* We want to find out the port number to connect to.  */
     180           1 :   size = sizeof(connect_addr_ss);
     181           1 :   if (getsockname(listener, connect_addr, &size) == -1)
     182           0 :     goto tidy_up_and_fail;
     183           1 :   if (size != addrlen)
     184           0 :     goto abort_tidy_up_and_fail;
     185           1 :   if (connect(connector, connect_addr, size) == -1)
     186           0 :     goto tidy_up_and_fail;
     187             : 
     188           1 :   size = sizeof(accepted_addr_ss);
     189           1 :   acceptor = accept(listener, accepted_addr, &size);
     190           1 :   if (!SOCKET_OK(acceptor))
     191           0 :     goto tidy_up_and_fail;
     192           1 :   if (size != addrlen)
     193           0 :     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           1 :   if (getsockname(connector, connect_addr, &size) == -1)
     197           0 :     goto tidy_up_and_fail;
     198             :   /* Set *_tor_addr and *_port to the address and port that was used */
     199           1 :   if (!sockaddr_eq(accepted_addr, connect_addr))
     200           0 :     goto abort_tidy_up_and_fail;
     201           1 :   closesocket(listener);
     202           1 :   fd[0] = connector;
     203           1 :   fd[1] = acceptor;
     204           1 :   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           0 :   if (saved_errno < 0)
     214           0 :     saved_errno = errno;
     215           0 :   if (SOCKET_OK(listener))
     216           0 :     closesocket(listener);
     217           0 :   if (SOCKET_OK(connector))
     218           0 :     closesocket(connector);
     219           0 :   if (SOCKET_OK(acceptor))
     220           0 :     closesocket(acceptor);
     221           0 :   return -saved_errno;
     222             : }
     223             : 
     224             : #endif /* defined(NEED_ERSATZ_SOCKETPAIR) */

Generated by: LCOV version 1.14