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) */