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