Tor  0.4.6.0-alpha-dev
socketpair.c
Go to the documentation of this file.
1 /* Copyright (c) 2003-2004, Roger Dingledine
2  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3  * Copyright (c) 2007-2020, 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 
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) */
TOR_INVALID_SOCKET
#define TOR_INVALID_SOCKET
Definition: nettypes.h:41
tor_htonl
static uint32_t tor_htonl(uint32_t a)
Definition: bytes.h:163
torint.h
Integer definitions used throughout Tor.
sockaddr_in6
Definition: inaddr_st.h:98
socketpair.h
Header for socketpair.c.
bytes.h
Inline functions for reading and writing multibyte values from the middle of strings,...
inaddr_st.h
Define in6_addr, its members, and related types on platforms that lack it.
tor_socket_t
#define tor_socket_t
Definition: nettypes.h:36
SOCKET_OK
#define SOCKET_OK(s)
Definition: nettypes.h:39