blob: 6561f3d64e810ba2307f281ef213dfced44fe3be [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
Adam Tkac8aee1a82009-09-04 12:08:56 +000019#ifdef HAVE_CONFIG_H
20#include <config.h>
Adam Tkacad1cbd92008-10-06 14:08:00 +000021#endif
22
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000023#ifdef WIN32
24//#include <io.h>
25#include <winsock2.h>
Pierre Ossman0153e232011-03-08 13:05:27 +000026#include <ws2tcpip.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000027#define errorNumber WSAGetLastError()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000028#else
29#define errorNumber errno
30#define closesocket close
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <arpa/inet.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000034#include <netinet/tcp.h>
35#include <netdb.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000036#include <errno.h>
37#include <string.h>
38#include <signal.h>
39#include <fcntl.h>
40#endif
41
Adam Tkac04b7fd22008-03-19 16:14:48 +000042#include <stdlib.h>
Tim Waugh892d10a2015-03-11 13:12:07 +000043#include <unistd.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000044#include <network/TcpSocket.h>
45#include <rfb/util.h>
46#include <rfb/LogWriter.h>
Pierre Ossman39b3b8f2015-01-29 17:35:45 +010047#include <rfb/Configuration.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000048
Pierre Ossman2f744172015-03-17 13:38:21 +010049#ifdef WIN32
50#include <os/winerrno.h>
51#endif
52
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000053#ifndef INADDR_NONE
54#define INADDR_NONE ((unsigned long)-1)
55#endif
56#ifndef INADDR_LOOPBACK
57#define INADDR_LOOPBACK ((unsigned long)0x7F000001)
58#endif
59
Pierre Ossmancfb21162015-03-17 14:02:11 +010060#ifndef IN6_ARE_ADDR_EQUAL
Pierre Ossman8b6aa202012-12-13 13:55:22 +000061#define IN6_ARE_ADDR_EQUAL(a,b) \
62 (memcmp ((const void*)(a), (const void*)(b), sizeof (struct in6_addr)) == 0)
63#endif
64
Pierre Ossmana6570c52015-03-17 13:38:59 +010065// Missing on older Windows and OS X
66#ifndef AI_NUMERICSERV
67#define AI_NUMERICSERV 0
68#endif
69
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000070using namespace network;
71using namespace rdr;
72
73static rfb::LogWriter vlog("TcpSocket");
74
Pierre Ossman39b3b8f2015-01-29 17:35:45 +010075static rfb::BoolParameter UseIPv4("UseIPv4", "Use IPv4 for incoming and outgoing connections.", true);
Pierre Ossman39b3b8f2015-01-29 17:35:45 +010076static rfb::BoolParameter UseIPv6("UseIPv6", "Use IPv6 for incoming and outgoing connections.", true);
Pierre Ossman39b3b8f2015-01-29 17:35:45 +010077
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000078/* Tunnelling support. */
79int network::findFreeTcpPort (void)
80{
DRCb2618e52011-02-21 13:43:44 +000081 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000082 struct sockaddr_in addr;
83 memset(&addr, 0, sizeof(addr));
84 addr.sin_family = AF_INET;
85 addr.sin_addr.s_addr = INADDR_ANY;
86
87 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
88 throw SocketException ("unable to create socket", errorNumber);
89
DRCb2618e52011-02-21 13:43:44 +000090 addr.sin_port = 0;
91 if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) < 0)
92 throw SocketException ("unable to find free port", errorNumber);
93
94 socklen_t n = sizeof(addr);
95 if (getsockname (sock, (struct sockaddr *)&addr, &n) < 0)
96 throw SocketException ("unable to get port number", errorNumber);
97
98 closesocket (sock);
99 return ntohs(addr.sin_port);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000100}
101
102
103// -=- Socket initialisation
104static bool socketsInitialised = false;
105static void initSockets() {
106 if (socketsInitialised)
107 return;
108#ifdef WIN32
109 WORD requiredVersion = MAKEWORD(2,0);
110 WSADATA initResult;
111
112 if (WSAStartup(requiredVersion, &initResult) != 0)
113 throw SocketException("unable to initialise Winsock2", errorNumber);
114#else
115 signal(SIGPIPE, SIG_IGN);
116#endif
117 socketsInitialised = true;
118}
119
120
121// -=- TcpSocket
122
123TcpSocket::TcpSocket(int sock, bool close)
124 : Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close)
125{
126}
127
128TcpSocket::TcpSocket(const char *host, int port)
129 : closeFd(true)
130{
Pierre Ossman5a126662015-07-30 12:24:36 +0200131 int sock, err, result;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000132 struct addrinfo *ai, *current, hints;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000133
134 // - Create a socket
135 initSockets();
Adam Tkac9cb6a422008-11-14 15:56:15 +0000136
Adam Tkac9cb6a422008-11-14 15:56:15 +0000137 memset(&hints, 0, sizeof(struct addrinfo));
138 hints.ai_family = AF_UNSPEC;
139 hints.ai_socktype = SOCK_STREAM;
140 hints.ai_canonname = NULL;
141 hints.ai_addr = NULL;
142 hints.ai_next = NULL;
143
144 if ((result = getaddrinfo(host, NULL, &hints, &ai)) != 0) {
145 throw Exception("unable to resolve host by name: %s",
Tim Waugha85363d2015-03-11 13:07:48 +0000146 gai_strerror(result));
Adam Tkac9cb6a422008-11-14 15:56:15 +0000147 }
148
Pierre Ossmanf1a35012015-03-03 16:02:42 +0100149 sock = -1;
Pierre Ossman5a126662015-07-30 12:24:36 +0200150 err = 0;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000151 for (current = ai; current != NULL; current = current->ai_next) {
Pierre Ossman5a126662015-07-30 12:24:36 +0200152 int family;
153 vnc_sockaddr_t sa;
154 socklen_t salen;
Pierre Ossmanb7e55742015-07-30 12:25:52 +0200155 char ntop[NI_MAXHOST];
Pierre Ossman5a126662015-07-30 12:24:36 +0200156
Adam Tkac9cb6a422008-11-14 15:56:15 +0000157 family = current->ai_family;
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100158
159 switch (family) {
160 case AF_INET:
161 if (!UseIPv4)
162 continue;
163 break;
164 case AF_INET6:
165 if (!UseIPv6)
166 continue;
167 break;
168 default:
Adam Tkac9cb6a422008-11-14 15:56:15 +0000169 continue;
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100170 }
Adam Tkac9cb6a422008-11-14 15:56:15 +0000171
172 salen = current->ai_addrlen;
173 memcpy(&sa, current->ai_addr, salen);
174
175 if (family == AF_INET)
176 sa.u.sin.sin_port = htons(port);
177 else
178 sa.u.sin6.sin6_port = htons(port);
179
Pierre Ossmanb7e55742015-07-30 12:25:52 +0200180 getnameinfo(&sa.u.sa, salen, ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST);
181 vlog.debug("Connecting to %s [%s] port %d", host, ntop, port);
182
Adam Tkac9cb6a422008-11-14 15:56:15 +0000183 sock = socket (family, SOCK_STREAM, 0);
184 if (sock == -1) {
185 err = errorNumber;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000186 freeaddrinfo(ai);
Adam Tkac9cb6a422008-11-14 15:56:15 +0000187 throw SocketException("unable to create socket", err);
188 }
189
190 /* Attempt to connect to the remote host */
Adam Tkacc9cda3b2009-10-30 11:13:34 +0000191 while ((result = connect(sock, &sa.u.sa, salen)) == -1) {
Adam Tkac9cb6a422008-11-14 15:56:15 +0000192 err = errorNumber;
193#ifndef WIN32
194 if (err == EINTR)
Tim Waugha85363d2015-03-11 13:07:48 +0000195 continue;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000196#endif
Pierre Ossmanb7e55742015-07-30 12:25:52 +0200197 vlog.debug("Failed to connect to address %s port %d: %d",
198 ntop, port, err);
Adam Tkac9cb6a422008-11-14 15:56:15 +0000199 closesocket(sock);
Pierre Ossman5a126662015-07-30 12:24:36 +0200200 sock = -1;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000201 break;
202 }
203
Adam Tkac9cb6a422008-11-14 15:56:15 +0000204 if (result == 0)
205 break;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000206 }
207
208 freeaddrinfo(ai);
Pierre Ossmanda9a38d2015-03-03 16:03:32 +0100209
Pierre Ossman5a126662015-07-30 12:24:36 +0200210 if (sock == -1) {
211 if (err == 0)
212 throw Exception("No useful address for host");
213 else
214 throw SocketException("unable connect to socket", err);
215 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000216
217#ifndef WIN32
218 // - By default, close the socket on exec()
219 fcntl(sock, F_SETFD, FD_CLOEXEC);
220#endif
221
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000222 // Disable Nagle's algorithm, to reduce latency
223 enableNagles(sock, false);
224
225 // Create the input and output streams
226 instream = new FdInStream(sock);
227 outstream = new FdOutStream(sock);
228 ownStreams = true;
229}
230
231TcpSocket::~TcpSocket() {
232 if (closeFd)
233 closesocket(getFd());
234}
235
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000236int TcpSocket::getMyPort() {
237 return getSockPort(getFd());
238}
239
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000240char* TcpSocket::getPeerAddress() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000241 vnc_sockaddr_t sa;
242 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000243
Tim Waugh6ae42df2014-11-17 17:07:07 +0000244 if (getpeername(getFd(), &sa.u.sa, &sa_size) != 0) {
245 vlog.error("unable to get peer name for socket");
246 return rfb::strDup("");
247 }
248
Pierre Ossman14263e12014-11-19 11:14:49 +0100249 if (sa.u.sa.sa_family == AF_INET6) {
Pierre Ossman07cd2292014-11-19 11:16:04 +0100250 char buffer[INET6_ADDRSTRLEN + 2];
Tim Waugh892d10a2015-03-11 13:12:07 +0000251 int ret;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000252
Pierre Ossman07cd2292014-11-19 11:16:04 +0100253 buffer[0] = '[';
254
Tim Waugh892d10a2015-03-11 13:12:07 +0000255 ret = getnameinfo(&sa.u.sa, sizeof(sa.u.sin6),
256 buffer + 1, sizeof(buffer) - 2, NULL, 0,
257 NI_NUMERICHOST);
258 if (ret != 0) {
Pierre Ossman14263e12014-11-19 11:14:49 +0100259 vlog.error("unable to convert peer name to a string");
260 return rfb::strDup("");
261 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000262
Pierre Ossman07cd2292014-11-19 11:16:04 +0100263 strcat(buffer, "]");
264
265 return rfb::strDup(buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000266 }
Pierre Ossman14263e12014-11-19 11:14:49 +0100267
268 if (sa.u.sa.sa_family == AF_INET) {
269 char *name;
270
271 name = inet_ntoa(sa.u.sin.sin_addr);
272 if (name == NULL) {
273 vlog.error("unable to convert peer name to a string");
274 return rfb::strDup("");
275 }
276
277 return rfb::strDup(name);
278 }
279
280 vlog.error("unknown address family for socket");
281 return rfb::strDup("");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000282}
283
284int TcpSocket::getPeerPort() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000285 vnc_sockaddr_t sa;
286 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000287
Tim Waugh6ae42df2014-11-17 17:07:07 +0000288 getpeername(getFd(), &sa.u.sa, &sa_size);
289
290 switch (sa.u.sa.sa_family) {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000291 case AF_INET6:
292 return ntohs(sa.u.sin6.sin6_port);
Pierre Ossman14263e12014-11-19 11:14:49 +0100293 case AF_INET:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000294 return ntohs(sa.u.sin.sin_port);
Pierre Ossman14263e12014-11-19 11:14:49 +0100295 default:
296 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000297 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000298}
299
300char* TcpSocket::getPeerEndpoint() {
301 rfb::CharArray address; address.buf = getPeerAddress();
302 int port = getPeerPort();
303
304 int buflen = strlen(address.buf) + 32;
305 char* buffer = new char[buflen];
306 sprintf(buffer, "%s::%d", address.buf, port);
307 return buffer;
308}
309
310bool TcpSocket::sameMachine() {
Adam Tkac897814f2009-11-12 10:32:43 +0000311 vnc_sockaddr_t peeraddr, myaddr;
312 socklen_t addrlen;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000313
Adam Tkac897814f2009-11-12 10:32:43 +0000314 addrlen = sizeof(peeraddr);
315 if (getpeername(getFd(), &peeraddr.u.sa, &addrlen) < 0)
316 throw SocketException ("unable to get peer address", errorNumber);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000317
Adam Tkac897814f2009-11-12 10:32:43 +0000318 addrlen = sizeof(myaddr); /* need to reset, since getpeername overwrote */
319 if (getsockname(getFd(), &myaddr.u.sa, &addrlen) < 0)
320 throw SocketException ("unable to get my address", errorNumber);
321
322 if (peeraddr.u.sa.sa_family != myaddr.u.sa.sa_family)
323 return false;
324
Adam Tkac897814f2009-11-12 10:32:43 +0000325 if (peeraddr.u.sa.sa_family == AF_INET6)
326 return IN6_ARE_ADDR_EQUAL(&peeraddr.u.sin6.sin6_addr,
Tim Waugha85363d2015-03-11 13:07:48 +0000327 &myaddr.u.sin6.sin6_addr);
Pierre Ossman14263e12014-11-19 11:14:49 +0100328 if (peeraddr.u.sa.sa_family == AF_INET)
329 return (peeraddr.u.sin.sin_addr.s_addr == myaddr.u.sin.sin_addr.s_addr);
330
331 // No idea what this is. Assume we're on different machines.
332 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000333}
334
335void TcpSocket::shutdown()
336{
337 Socket::shutdown();
338 ::shutdown(getFd(), 2);
339}
340
341bool TcpSocket::enableNagles(int sock, bool enable) {
342 int one = enable ? 0 : 1;
343 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
Tim Waugha85363d2015-03-11 13:07:48 +0000344 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000345 int e = errorNumber;
346 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
347 return false;
348 }
349 return true;
350}
351
Pierre Ossman64069a92011-11-08 12:10:55 +0000352bool TcpSocket::cork(int sock, bool enable) {
353#ifndef TCP_CORK
354 return false;
355#else
356 int one = enable ? 1 : 0;
357 if (setsockopt(sock, IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0)
358 return false;
359 return true;
360#endif
361}
362
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000363bool TcpSocket::isSocket(int sock)
364{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000365 vnc_sockaddr_t sa;
366 socklen_t sa_size = sizeof(sa);
367 return getsockname(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000368}
369
370bool TcpSocket::isConnected(int sock)
371{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000372 vnc_sockaddr_t sa;
373 socklen_t sa_size = sizeof(sa);
374 return getpeername(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000375}
376
377int TcpSocket::getSockPort(int sock)
378{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000379 vnc_sockaddr_t sa;
380 socklen_t sa_size = sizeof(sa);
381 if (getsockname(sock, &sa.u.sa, &sa_size) < 0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000382 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000383
384 switch (sa.u.sa.sa_family) {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000385 case AF_INET6:
386 return ntohs(sa.u.sin6.sin6_port);
Tim Waugh6ae42df2014-11-17 17:07:07 +0000387 default:
388 return ntohs(sa.u.sin.sin_port);
389 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000390}
391
Tim Waugh892d10a2015-03-11 13:12:07 +0000392TcpListener::TcpListener(int sock)
Tim Waughe4d97262014-11-21 16:07:34 +0000393{
Tim Waugh892d10a2015-03-11 13:12:07 +0000394 fd = sock;
Tim Waughe4d97262014-11-21 16:07:34 +0000395}
396
Tim Waugh892d10a2015-03-11 13:12:07 +0000397TcpListener::TcpListener(const struct sockaddr *listenaddr,
398 socklen_t listenaddrlen)
399{
400 int one = 1;
401 vnc_sockaddr_t sa;
402 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000403
404 initSockets();
Tim Waugh892d10a2015-03-11 13:12:07 +0000405
406 if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0)
407 throw SocketException("unable to create listening socket", errorNumber);
408
409 memcpy (&sa, listenaddr, listenaddrlen);
410#ifdef IPV6_V6ONLY
411 if (listenaddr->sa_family == AF_INET6) {
Pierre Ossman467df2a2015-09-29 09:42:03 +0200412 if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one))) {
413 int e = errorNumber;
414 closesocket(sock);
415 throw SocketException("unable to set IPV6_V6ONLY", e);
416 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000417 }
418#endif /* defined(IPV6_V6ONLY) */
419
Pierre Ossman056c1532015-04-23 11:30:59 +0200420#ifdef FD_CLOEXEC
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000421 // - By default, close the socket on exec()
Tim Waugh892d10a2015-03-11 13:12:07 +0000422 fcntl(sock, F_SETFD, FD_CLOEXEC);
Pierre Ossman056c1532015-04-23 11:30:59 +0200423#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000424
Pierre Ossman056c1532015-04-23 11:30:59 +0200425 // SO_REUSEADDR is broken on Windows. It allows binding to a port
426 // that already has a listening socket on it. SO_EXCLUSIVEADDRUSE
427 // might do what we want, but requires investigation.
428#ifndef WIN32
Tim Waugh892d10a2015-03-11 13:12:07 +0000429 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
430 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000431 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000432 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000433 throw SocketException("unable to create listening socket", e);
434 }
435#endif
436
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200437 if (bind(sock, &sa.u.sa, listenaddrlen) == -1) {
Pierre Ossman7ebce752015-09-29 09:42:36 +0200438 int e = errorNumber;
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200439 closesocket(sock);
Pierre Ossman7ebce752015-09-29 09:42:36 +0200440 throw SocketException("failed to bind socket", e);
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200441 }
442
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000443 // - Set it to be a listening socket
Tim Waugh892d10a2015-03-11 13:12:07 +0000444 if (listen(sock, 5) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000445 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000446 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000447 throw SocketException("unable to set socket to listening mode", e);
448 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000449
450 fd = sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000451}
452
453TcpListener::~TcpListener() {
Tim Waugh892d10a2015-03-11 13:12:07 +0000454 closesocket(fd);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000455}
456
457void TcpListener::shutdown()
458{
459#ifdef WIN32
460 closesocket(getFd());
461#else
462 ::shutdown(getFd(), 2);
463#endif
464}
465
466
467Socket*
468TcpListener::accept() {
469 int new_sock = -1;
470
471 // Accept an incoming connection
472 if ((new_sock = ::accept(fd, 0, 0)) < 0)
473 throw SocketException("unable to accept new connection", errorNumber);
474
475#ifndef WIN32
476 // - By default, close the socket on exec()
477 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
478#endif
479
480 // Disable Nagle's algorithm, to reduce latency
481 TcpSocket::enableNagles(new_sock, false);
482
483 // Create the socket object & check connection is allowed
484 TcpSocket* s = new TcpSocket(new_sock);
485 if (filter && !filter->verifyConnection(s)) {
486 delete s;
487 return 0;
488 }
489 return s;
490}
491
Pierre Ossman57cab512015-03-17 13:39:39 +0100492void TcpListener::getMyAddresses(std::list<char*>* result) {
Pierre Ossman57cab512015-03-17 13:39:39 +0100493 struct addrinfo *ai, *current, hints;
494
495 initSockets();
496
497 memset(&hints, 0, sizeof(struct addrinfo));
498 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
499 hints.ai_family = AF_UNSPEC;
500 hints.ai_socktype = SOCK_STREAM;
501 hints.ai_canonname = NULL;
502 hints.ai_addr = NULL;
503 hints.ai_next = NULL;
504
505 // Windows doesn't like NULL for service, so specify something
506 if ((getaddrinfo(NULL, "1", &hints, &ai)) != 0)
507 return;
508
509 for (current= ai; current != NULL; current = current->ai_next) {
510 switch (current->ai_family) {
511 case AF_INET:
512 if (!UseIPv4)
513 continue;
514 break;
515 case AF_INET6:
516 if (!UseIPv6)
517 continue;
518 break;
519 default:
520 continue;
521 }
522
523 char *addr = new char[INET6_ADDRSTRLEN];
524
525 getnameinfo(current->ai_addr, current->ai_addrlen, addr, INET6_ADDRSTRLEN,
526 NULL, 0, NI_NUMERICHOST);
527
528 result->push_back(addr);
529 }
530
531 freeaddrinfo(ai);
Pierre Ossman57cab512015-03-17 13:39:39 +0100532}
533
Tim Waugh892d10a2015-03-11 13:12:07 +0000534int TcpListener::getMyPort() {
535 return TcpSocket::getSockPort(getFd());
536}
537
538
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200539void network::createLocalTcpListeners(std::list<TcpListener*> *listeners,
Tim Waugh892d10a2015-03-11 13:12:07 +0000540 int port)
541{
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200542 struct addrinfo ai[2];
543 vnc_sockaddr_t sa[2];
Pierre Ossman9d784402015-03-17 17:10:10 +0100544
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200545 memset(ai, 0, sizeof(ai));
546 memset(sa, 0, sizeof(sa));
Pierre Ossman9d784402015-03-17 17:10:10 +0100547
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200548 sa[0].u.sin.sin_family = AF_INET;
549 sa[0].u.sin.sin_port = htons (port);
550 sa[0].u.sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
Tim Waugh892d10a2015-03-11 13:12:07 +0000551
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200552 ai[0].ai_family = sa[0].u.sin.sin_family;
553 ai[0].ai_addr = &sa[0].u.sa;
554 ai[0].ai_addrlen = sizeof(sa[0].u.sin);
555 ai[0].ai_next = &ai[1];
Tim Waugh892d10a2015-03-11 13:12:07 +0000556
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200557 sa[1].u.sin6.sin6_family = AF_INET6;
558 sa[1].u.sin6.sin6_port = htons (port);
559 sa[1].u.sin6.sin6_addr = in6addr_loopback;
560
561 ai[1].ai_family = sa[1].u.sin6.sin6_family;
562 ai[1].ai_addr = &sa[1].u.sa;
563 ai[1].ai_addrlen = sizeof(sa[1].u.sin6);
564 ai[1].ai_next = NULL;
565
566 createTcpListeners(listeners, ai);
Tim Waugh892d10a2015-03-11 13:12:07 +0000567}
568
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200569void network::createTcpListeners(std::list<TcpListener*> *listeners,
Tim Waugh892d10a2015-03-11 13:12:07 +0000570 const char *addr,
571 int port)
572{
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200573 struct addrinfo *ai, hints;
Tim Waugh892d10a2015-03-11 13:12:07 +0000574 char service[16];
Pierre Ossmanf7d15002015-03-17 17:10:56 +0100575 int result;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000576
Pierre Ossman9d784402015-03-17 17:10:10 +0100577 initSockets();
578
Tim Waugh6ae42df2014-11-17 17:07:07 +0000579 memset(&hints, 0, sizeof(struct addrinfo));
Tim Waugh892d10a2015-03-11 13:12:07 +0000580 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000581 hints.ai_family = AF_UNSPEC;
582 hints.ai_socktype = SOCK_STREAM;
583 hints.ai_canonname = NULL;
584 hints.ai_addr = NULL;
585 hints.ai_next = NULL;
586
Tim Waugh892d10a2015-03-11 13:12:07 +0000587 snprintf (service, sizeof (service) - 1, "%d", port);
588 service[sizeof (service) - 1] = '\0';
Pierre Ossmanf7d15002015-03-17 17:10:56 +0100589 if ((result = getaddrinfo(addr, service, &hints, &ai)) != 0)
590 throw rdr::Exception("unable to resolve listening address: %s",
591 gai_strerror(result));
Tim Waugh6ae42df2014-11-17 17:07:07 +0000592
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200593 try {
594 createTcpListeners(listeners, ai);
595 } catch(...) {
596 freeaddrinfo(ai);
597 throw;
598 }
599}
600
601void network::createTcpListeners(std::list<TcpListener*> *listeners,
602 const struct addrinfo *ai)
603{
604 const struct addrinfo *current;
605 std::list<TcpListener*> new_listeners;
606
607 initSockets();
608
Tim Waugh892d10a2015-03-11 13:12:07 +0000609 for (current = ai; current != NULL; current = current->ai_next) {
610 switch (current->ai_family) {
611 case AF_INET:
612 if (!UseIPv4)
613 continue;
614 break;
615
616 case AF_INET6:
617 if (!UseIPv6)
618 continue;
619 break;
620
621 default:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000622 continue;
Tim Waugh892d10a2015-03-11 13:12:07 +0000623 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000624
Tim Waugh892d10a2015-03-11 13:12:07 +0000625 try {
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200626 new_listeners.push_back(new TcpListener(current->ai_addr,
627 current->ai_addrlen));
Tim Waugh892d10a2015-03-11 13:12:07 +0000628 } catch (SocketException& e) {
629 // Ignore this if it is due to lack of address family support on
630 // the interface or on the system
631 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) {
632 // Otherwise, report the error
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200633 while (!new_listeners.empty()) {
634 delete new_listeners.back();
635 new_listeners.pop_back();
636 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000637 throw;
638 }
639 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000640 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000641
Tim Waugh892d10a2015-03-11 13:12:07 +0000642 if (new_listeners.empty ())
643 throw SocketException("createTcpListeners: no addresses available",
644 EADDRNOTAVAIL);
645
646 listeners->splice (listeners->end(), new_listeners);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000647}
648
649
650TcpFilter::TcpFilter(const char* spec) {
651 rfb::CharArray tmp;
Adam Tkacd36b6262009-09-04 10:57:20 +0000652 tmp.buf = rfb::strDup(spec);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000653 while (tmp.buf) {
654 rfb::CharArray first;
655 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
656 if (strlen(first.buf))
657 filter.push_back(parsePattern(first.buf));
658 }
659}
660
661TcpFilter::~TcpFilter() {
662}
663
664
665static bool
Tim Waughc24a64d2015-03-13 16:07:29 +0000666patternMatchIP(const TcpFilter::Pattern& pattern, vnc_sockaddr_t *sa) {
667 switch (pattern.address.u.sa.sa_family) {
668 unsigned long address;
669
670 case AF_INET:
671 if (sa->u.sa.sa_family != AF_INET)
672 return false;
673
674 address = sa->u.sin.sin_addr.s_addr;
675 if (address == htonl (INADDR_NONE)) return false;
676 return ((pattern.address.u.sin.sin_addr.s_addr &
677 pattern.mask.u.sin.sin_addr.s_addr) ==
678 (address & pattern.mask.u.sin.sin_addr.s_addr));
679
680 case AF_INET6:
681 if (sa->u.sa.sa_family != AF_INET6)
682 return false;
683
684 for (unsigned int n = 0; n < 16; n++) {
685 unsigned int bits = (n + 1) * 8;
686 unsigned int mask;
687 if (pattern.prefixlen > bits)
688 mask = 0xff;
689 else {
690 unsigned int lastbits = 0xff;
691 lastbits <<= bits - pattern.prefixlen;
692 mask = lastbits & 0xff;
693 }
694
695 if ((pattern.address.u.sin6.sin6_addr.s6_addr[n] & mask) !=
696 (sa->u.sin6.sin6_addr.s6_addr[n] & mask))
697 return false;
698
699 if (mask < 0xff)
700 break;
701 }
702
703 return true;
704
705 case AF_UNSPEC:
706 // Any address matches
707 return true;
708
709 default:
710 break;
711 }
712
713 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000714}
715
716bool
717TcpFilter::verifyConnection(Socket* s) {
718 rfb::CharArray name;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000719 vnc_sockaddr_t sa;
720 socklen_t sa_size = sizeof(sa);
Tim Waughc24a64d2015-03-13 16:07:29 +0000721
722 if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0)
Tim Waugh6ae42df2014-11-17 17:07:07 +0000723 return false;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000724
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000725 name.buf = s->getPeerAddress();
726 std::list<TcpFilter::Pattern>::iterator i;
727 for (i=filter.begin(); i!=filter.end(); i++) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000728 if (patternMatchIP(*i, &sa)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000729 switch ((*i).action) {
730 case Accept:
731 vlog.debug("ACCEPT %s", name.buf);
732 return true;
733 case Query:
734 vlog.debug("QUERY %s", name.buf);
735 s->setRequiresQuery();
736 return true;
737 case Reject:
738 vlog.debug("REJECT %s", name.buf);
739 return false;
740 }
741 }
742 }
743
744 vlog.debug("[REJECT] %s", name.buf);
745 return false;
746}
747
748
749TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
750 TcpFilter::Pattern pattern;
751
Tim Waughc24a64d2015-03-13 16:07:29 +0000752 rfb::CharArray addr, pref;
753 bool prefix_specified;
754 int family;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000755
Pierre Ossman398a6f42015-12-07 12:13:20 +0100756 initSockets();
757
Tim Waughc24a64d2015-03-13 16:07:29 +0000758 prefix_specified = rfb::strSplit(&p[1], '/', &addr.buf, &pref.buf);
759 if (addr.buf[0] == '\0') {
760 // Match any address
761 memset (&pattern.address, 0, sizeof (pattern.address));
762 pattern.address.u.sa.sa_family = AF_UNSPEC;
763 pattern.prefixlen = 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000764 } else {
Tim Waughc24a64d2015-03-13 16:07:29 +0000765 struct addrinfo hints;
766 struct addrinfo *ai;
767 char *p = addr.buf;
768 int result;
769 memset (&hints, 0, sizeof (hints));
770 hints.ai_family = AF_UNSPEC;
771 hints.ai_flags = AI_NUMERICHOST;
772
773 // Take out brackets, if present
774 if (*p == '[') {
775 size_t len;
776 p++;
777 len = strlen (p);
778 if (len > 0 && p[len - 1] == ']')
779 p[len - 1] = '\0';
780 }
781
782 if ((result = getaddrinfo (p, NULL, &hints, &ai)) != 0) {
783 throw Exception("unable to resolve host by name: %s",
784 gai_strerror(result));
785 }
786
787 memcpy (&pattern.address.u.sa, ai->ai_addr, ai->ai_addrlen);
788 freeaddrinfo (ai);
Tim Waughc24a64d2015-03-13 16:07:29 +0000789
790 family = pattern.address.u.sa.sa_family;
791
792 if (prefix_specified) {
793 if (family == AF_INET &&
794 rfb::strContains(pref.buf, '.')) {
795 throw Exception("mask no longer supported for filter, "
796 "use prefix instead");
797 }
798
799 pattern.prefixlen = (unsigned int) atoi(pref.buf);
800 } else {
801 switch (family) {
802 case AF_INET:
803 pattern.prefixlen = 32;
804 break;
805 case AF_INET6:
806 pattern.prefixlen = 128;
807 break;
808 default:
809 throw Exception("unknown address family");
810 }
811 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000812 }
813
Pierre Ossmanfdc55e52015-03-17 12:56:31 +0100814 family = pattern.address.u.sa.sa_family;
815
Tim Waughc24a64d2015-03-13 16:07:29 +0000816 if (pattern.prefixlen > (family == AF_INET ? 32: 128))
817 throw Exception("invalid prefix length for filter address: %u",
818 pattern.prefixlen);
819
820 // Compute mask from address and prefix length
821 memset (&pattern.mask, 0, sizeof (pattern.mask));
822 switch (family) {
823 unsigned long mask;
824 case AF_INET:
825 mask = 0;
826 for (unsigned int i=0; i<pattern.prefixlen; i++)
827 mask |= 1<<(31-i);
828 pattern.mask.u.sin.sin_addr.s_addr = htonl(mask);
829 break;
830
831 case AF_INET6:
832 for (unsigned int n = 0; n < 16; n++) {
833 unsigned int bits = (n + 1) * 8;
834 if (pattern.prefixlen > bits)
835 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = 0xff;
836 else {
837 unsigned int lastbits = 0xff;
838 lastbits <<= bits - pattern.prefixlen;
839 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = lastbits & 0xff;
840 break;
841 }
842 }
843 break;
844 case AF_UNSPEC:
845 // No mask to compute
846 break;
847 default:
848 ; /* not reached */
849 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000850
851 switch(p[0]) {
852 case '+': pattern.action = TcpFilter::Accept; break;
853 case '-': pattern.action = TcpFilter::Reject; break;
854 case '?': pattern.action = TcpFilter::Query; break;
855 };
856
857 return pattern;
858}
859
860char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000861 rfb::CharArray addr;
Tim Waughc24a64d2015-03-13 16:07:29 +0000862 char buffer[INET6_ADDRSTRLEN + 2];
863
864 if (p.address.u.sa.sa_family == AF_INET) {
865 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin),
866 buffer, sizeof (buffer), NULL, 0, NI_NUMERICHOST);
867 addr.buf = rfb::strDup(buffer);
868 } else if (p.address.u.sa.sa_family == AF_INET6) {
869 buffer[0] = '[';
870 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin6),
871 buffer + 1, sizeof (buffer) - 2, NULL, 0, NI_NUMERICHOST);
872 strcat(buffer, "]");
873 addr.buf = rfb::strDup(buffer);
874 } else if (p.address.u.sa.sa_family == AF_UNSPEC)
875 addr.buf = rfb::strDup("");
Tim Waughc24a64d2015-03-13 16:07:29 +0000876
877 char action;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000878 switch (p.action) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000879 case Accept: action = '+'; break;
880 case Reject: action = '-'; break;
881 default:
882 case Query: action = '?'; break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000883 };
Tim Waughc24a64d2015-03-13 16:07:29 +0000884 size_t resultlen = (1 // action
885 + strlen (addr.buf) // address
886 + 1 // slash
887 + 3 // prefix length, max 128
888 + 1); // terminating nul
889 char* result = new char[resultlen];
890 if (addr.buf[0] == '\0')
891 snprintf(result, resultlen, "%c", action);
892 else
893 snprintf(result, resultlen, "%c%s/%u", action, addr.buf, p.prefixlen);
894
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000895 return result;
896}