blob: 9e277cbb8e32e8909a9b90e7bd31d0d7129bd0c2 [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
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000031#include <sys/socket.h>
32#include <arpa/inet.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000033#include <netinet/tcp.h>
34#include <netdb.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000035#include <errno.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000036#endif
37
Adam Tkac04b7fd22008-03-19 16:14:48 +000038#include <stdlib.h>
Tim Waugh892d10a2015-03-11 13:12:07 +000039#include <unistd.h>
Pierre Ossmand7bbbbf2018-05-21 12:06:47 +020040
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000041#include <network/TcpSocket.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000042#include <rfb/LogWriter.h>
Pierre Ossman39b3b8f2015-01-29 17:35:45 +010043#include <rfb/Configuration.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000044
Pierre Ossman2f744172015-03-17 13:38:21 +010045#ifdef WIN32
46#include <os/winerrno.h>
47#endif
48
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000049#ifndef INADDR_NONE
50#define INADDR_NONE ((unsigned long)-1)
51#endif
52#ifndef INADDR_LOOPBACK
53#define INADDR_LOOPBACK ((unsigned long)0x7F000001)
54#endif
55
Pierre Ossmancfb21162015-03-17 14:02:11 +010056#ifndef IN6_ARE_ADDR_EQUAL
Pierre Ossman8b6aa202012-12-13 13:55:22 +000057#define IN6_ARE_ADDR_EQUAL(a,b) \
58 (memcmp ((const void*)(a), (const void*)(b), sizeof (struct in6_addr)) == 0)
59#endif
60
Pierre Ossmana6570c52015-03-17 13:38:59 +010061// Missing on older Windows and OS X
62#ifndef AI_NUMERICSERV
63#define AI_NUMERICSERV 0
64#endif
65
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000066using namespace network;
67using namespace rdr;
68
69static rfb::LogWriter vlog("TcpSocket");
70
Pierre Ossman39b3b8f2015-01-29 17:35:45 +010071static rfb::BoolParameter UseIPv4("UseIPv4", "Use IPv4 for incoming and outgoing connections.", true);
Pierre Ossman39b3b8f2015-01-29 17:35:45 +010072static rfb::BoolParameter UseIPv6("UseIPv6", "Use IPv6 for incoming and outgoing connections.", true);
Pierre Ossman39b3b8f2015-01-29 17:35:45 +010073
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000074/* Tunnelling support. */
75int network::findFreeTcpPort (void)
76{
DRCb2618e52011-02-21 13:43:44 +000077 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000078 struct sockaddr_in addr;
79 memset(&addr, 0, sizeof(addr));
80 addr.sin_family = AF_INET;
81 addr.sin_addr.s_addr = INADDR_ANY;
82
83 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
84 throw SocketException ("unable to create socket", errorNumber);
85
DRCb2618e52011-02-21 13:43:44 +000086 addr.sin_port = 0;
87 if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) < 0)
88 throw SocketException ("unable to find free port", errorNumber);
89
90 socklen_t n = sizeof(addr);
91 if (getsockname (sock, (struct sockaddr *)&addr, &n) < 0)
92 throw SocketException ("unable to get port number", errorNumber);
93
94 closesocket (sock);
95 return ntohs(addr.sin_port);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000096}
97
Pierre Ossman559e8b82018-05-04 12:44:31 +020098int network::getSockPort(int sock)
99{
100 vnc_sockaddr_t sa;
101 socklen_t sa_size = sizeof(sa);
102 if (getsockname(sock, &sa.u.sa, &sa_size) < 0)
103 return 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000104
Pierre Ossman559e8b82018-05-04 12:44:31 +0200105 switch (sa.u.sa.sa_family) {
106 case AF_INET6:
107 return ntohs(sa.u.sin6.sin6_port);
108 default:
109 return ntohs(sa.u.sin.sin_port);
110 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000111}
112
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000113// -=- TcpSocket
114
Pierre Ossman559e8b82018-05-04 12:44:31 +0200115TcpSocket::TcpSocket(int sock) : Socket(sock)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000116{
Pierre Ossman559e8b82018-05-04 12:44:31 +0200117 // Disable Nagle's algorithm, to reduce latency
118 enableNagles(false);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000119}
120
121TcpSocket::TcpSocket(const char *host, int port)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000122{
Pierre Ossman5a126662015-07-30 12:24:36 +0200123 int sock, err, result;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000124 struct addrinfo *ai, *current, hints;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000125
126 // - Create a socket
Adam Tkac9cb6a422008-11-14 15:56:15 +0000127
Adam Tkac9cb6a422008-11-14 15:56:15 +0000128 memset(&hints, 0, sizeof(struct addrinfo));
129 hints.ai_family = AF_UNSPEC;
130 hints.ai_socktype = SOCK_STREAM;
131 hints.ai_canonname = NULL;
132 hints.ai_addr = NULL;
133 hints.ai_next = NULL;
134
135 if ((result = getaddrinfo(host, NULL, &hints, &ai)) != 0) {
136 throw Exception("unable to resolve host by name: %s",
Tim Waugha85363d2015-03-11 13:07:48 +0000137 gai_strerror(result));
Adam Tkac9cb6a422008-11-14 15:56:15 +0000138 }
139
Pierre Ossmanf1a35012015-03-03 16:02:42 +0100140 sock = -1;
Pierre Ossman5a126662015-07-30 12:24:36 +0200141 err = 0;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000142 for (current = ai; current != NULL; current = current->ai_next) {
Pierre Ossman5a126662015-07-30 12:24:36 +0200143 int family;
144 vnc_sockaddr_t sa;
145 socklen_t salen;
Pierre Ossmanb7e55742015-07-30 12:25:52 +0200146 char ntop[NI_MAXHOST];
Pierre Ossman5a126662015-07-30 12:24:36 +0200147
Adam Tkac9cb6a422008-11-14 15:56:15 +0000148 family = current->ai_family;
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100149
150 switch (family) {
151 case AF_INET:
152 if (!UseIPv4)
153 continue;
154 break;
155 case AF_INET6:
156 if (!UseIPv6)
157 continue;
158 break;
159 default:
Adam Tkac9cb6a422008-11-14 15:56:15 +0000160 continue;
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100161 }
Adam Tkac9cb6a422008-11-14 15:56:15 +0000162
163 salen = current->ai_addrlen;
164 memcpy(&sa, current->ai_addr, salen);
165
166 if (family == AF_INET)
167 sa.u.sin.sin_port = htons(port);
168 else
169 sa.u.sin6.sin6_port = htons(port);
170
Pierre Ossmanb7e55742015-07-30 12:25:52 +0200171 getnameinfo(&sa.u.sa, salen, ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST);
172 vlog.debug("Connecting to %s [%s] port %d", host, ntop, port);
173
Adam Tkac9cb6a422008-11-14 15:56:15 +0000174 sock = socket (family, SOCK_STREAM, 0);
175 if (sock == -1) {
176 err = errorNumber;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000177 freeaddrinfo(ai);
Adam Tkac9cb6a422008-11-14 15:56:15 +0000178 throw SocketException("unable to create socket", err);
179 }
180
181 /* Attempt to connect to the remote host */
Adam Tkacc9cda3b2009-10-30 11:13:34 +0000182 while ((result = connect(sock, &sa.u.sa, salen)) == -1) {
Adam Tkac9cb6a422008-11-14 15:56:15 +0000183 err = errorNumber;
184#ifndef WIN32
185 if (err == EINTR)
Tim Waugha85363d2015-03-11 13:07:48 +0000186 continue;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000187#endif
Pierre Ossmanb7e55742015-07-30 12:25:52 +0200188 vlog.debug("Failed to connect to address %s port %d: %d",
189 ntop, port, err);
Adam Tkac9cb6a422008-11-14 15:56:15 +0000190 closesocket(sock);
Pierre Ossman5a126662015-07-30 12:24:36 +0200191 sock = -1;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000192 break;
193 }
194
Adam Tkac9cb6a422008-11-14 15:56:15 +0000195 if (result == 0)
196 break;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000197 }
198
199 freeaddrinfo(ai);
Pierre Ossmanda9a38d2015-03-03 16:03:32 +0100200
Pierre Ossman5a126662015-07-30 12:24:36 +0200201 if (sock == -1) {
202 if (err == 0)
203 throw Exception("No useful address for host");
204 else
205 throw SocketException("unable connect to socket", err);
206 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000207
Pierre Ossman559e8b82018-05-04 12:44:31 +0200208 // Take proper ownership of the socket
209 setFd(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000210
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000211 // Disable Nagle's algorithm, to reduce latency
Pierre Ossman559e8b82018-05-04 12:44:31 +0200212 enableNagles(false);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000213}
214
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000215char* TcpSocket::getPeerAddress() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000216 vnc_sockaddr_t sa;
217 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000218
Tim Waugh6ae42df2014-11-17 17:07:07 +0000219 if (getpeername(getFd(), &sa.u.sa, &sa_size) != 0) {
220 vlog.error("unable to get peer name for socket");
221 return rfb::strDup("");
222 }
223
Pierre Ossman14263e12014-11-19 11:14:49 +0100224 if (sa.u.sa.sa_family == AF_INET6) {
Pierre Ossman07cd2292014-11-19 11:16:04 +0100225 char buffer[INET6_ADDRSTRLEN + 2];
Tim Waugh892d10a2015-03-11 13:12:07 +0000226 int ret;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000227
Pierre Ossman07cd2292014-11-19 11:16:04 +0100228 buffer[0] = '[';
229
Tim Waugh892d10a2015-03-11 13:12:07 +0000230 ret = getnameinfo(&sa.u.sa, sizeof(sa.u.sin6),
231 buffer + 1, sizeof(buffer) - 2, NULL, 0,
232 NI_NUMERICHOST);
233 if (ret != 0) {
Pierre Ossman14263e12014-11-19 11:14:49 +0100234 vlog.error("unable to convert peer name to a string");
235 return rfb::strDup("");
236 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000237
Pierre Ossman07cd2292014-11-19 11:16:04 +0100238 strcat(buffer, "]");
239
240 return rfb::strDup(buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000241 }
Pierre Ossman14263e12014-11-19 11:14:49 +0100242
243 if (sa.u.sa.sa_family == AF_INET) {
244 char *name;
245
246 name = inet_ntoa(sa.u.sin.sin_addr);
247 if (name == NULL) {
248 vlog.error("unable to convert peer name to a string");
249 return rfb::strDup("");
250 }
251
252 return rfb::strDup(name);
253 }
254
255 vlog.error("unknown address family for socket");
256 return rfb::strDup("");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000257}
258
Pierre Ossmand7bbbbf2018-05-21 12:06:47 +0200259char* TcpSocket::getPeerEndpoint() {
260 rfb::CharArray address; address.buf = getPeerAddress();
Tim Waugh6ae42df2014-11-17 17:07:07 +0000261 vnc_sockaddr_t sa;
262 socklen_t sa_size = sizeof(sa);
Pierre Ossmand7bbbbf2018-05-21 12:06:47 +0200263 int port;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000264
Tim Waugh6ae42df2014-11-17 17:07:07 +0000265 getpeername(getFd(), &sa.u.sa, &sa_size);
266
Pierre Ossmand7bbbbf2018-05-21 12:06:47 +0200267 if (sa.u.sa.sa_family == AF_INET6)
268 port = ntohs(sa.u.sin6.sin6_port);
269 else if (sa.u.sa.sa_family == AF_INET)
270 port = ntohs(sa.u.sin.sin_port);
271 else
272 port = 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000273
274 int buflen = strlen(address.buf) + 32;
275 char* buffer = new char[buflen];
276 sprintf(buffer, "%s::%d", address.buf, port);
277 return buffer;
278}
279
Pierre Ossman559e8b82018-05-04 12:44:31 +0200280bool TcpSocket::enableNagles(bool enable) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000281 int one = enable ? 0 : 1;
Pierre Ossman559e8b82018-05-04 12:44:31 +0200282 if (setsockopt(getFd(), IPPROTO_TCP, TCP_NODELAY,
Tim Waugha85363d2015-03-11 13:07:48 +0000283 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000284 int e = errorNumber;
285 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
286 return false;
287 }
288 return true;
289}
290
Peter Ã…strand (astrand)01dc1a62017-10-10 12:56:04 +0200291bool TcpSocket::cork(bool enable) {
Pierre Ossman64069a92011-11-08 12:10:55 +0000292#ifndef TCP_CORK
293 return false;
294#else
295 int one = enable ? 1 : 0;
Peter Ã…strand (astrand)01dc1a62017-10-10 12:56:04 +0200296 if (setsockopt(getFd(), IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0)
Pierre Ossman64069a92011-11-08 12:10:55 +0000297 return false;
298 return true;
299#endif
300}
301
Pierre Ossman559e8b82018-05-04 12:44:31 +0200302TcpListener::TcpListener(int sock) : SocketListener(sock)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000303{
Tim Waughe4d97262014-11-21 16:07:34 +0000304}
305
Tim Waugh892d10a2015-03-11 13:12:07 +0000306TcpListener::TcpListener(const struct sockaddr *listenaddr,
307 socklen_t listenaddrlen)
308{
309 int one = 1;
310 vnc_sockaddr_t sa;
311 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000312
Tim Waugh892d10a2015-03-11 13:12:07 +0000313 if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0)
314 throw SocketException("unable to create listening socket", errorNumber);
315
316 memcpy (&sa, listenaddr, listenaddrlen);
317#ifdef IPV6_V6ONLY
318 if (listenaddr->sa_family == AF_INET6) {
Pierre Ossman467df2a2015-09-29 09:42:03 +0200319 if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one))) {
320 int e = errorNumber;
321 closesocket(sock);
322 throw SocketException("unable to set IPV6_V6ONLY", e);
323 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000324 }
325#endif /* defined(IPV6_V6ONLY) */
326
Pierre Ossman056c1532015-04-23 11:30:59 +0200327#ifdef FD_CLOEXEC
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000328 // - By default, close the socket on exec()
Tim Waugh892d10a2015-03-11 13:12:07 +0000329 fcntl(sock, F_SETFD, FD_CLOEXEC);
Pierre Ossman056c1532015-04-23 11:30:59 +0200330#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000331
Pierre Ossman056c1532015-04-23 11:30:59 +0200332 // SO_REUSEADDR is broken on Windows. It allows binding to a port
333 // that already has a listening socket on it. SO_EXCLUSIVEADDRUSE
334 // might do what we want, but requires investigation.
335#ifndef WIN32
Tim Waugh892d10a2015-03-11 13:12:07 +0000336 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
337 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000338 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000339 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000340 throw SocketException("unable to create listening socket", e);
341 }
342#endif
343
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200344 if (bind(sock, &sa.u.sa, listenaddrlen) == -1) {
Pierre Ossman7ebce752015-09-29 09:42:36 +0200345 int e = errorNumber;
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200346 closesocket(sock);
Pierre Ossman7ebce752015-09-29 09:42:36 +0200347 throw SocketException("failed to bind socket", e);
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200348 }
349
Pierre Ossman559e8b82018-05-04 12:44:31 +0200350 listen(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000351}
352
Pierre Ossman559e8b82018-05-04 12:44:31 +0200353Socket* TcpListener::createSocket(int fd) {
354 return new TcpSocket(fd);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000355}
356
Pierre Ossman57cab512015-03-17 13:39:39 +0100357void TcpListener::getMyAddresses(std::list<char*>* result) {
Pierre Ossman57cab512015-03-17 13:39:39 +0100358 struct addrinfo *ai, *current, hints;
359
360 initSockets();
361
362 memset(&hints, 0, sizeof(struct addrinfo));
363 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
364 hints.ai_family = AF_UNSPEC;
365 hints.ai_socktype = SOCK_STREAM;
366 hints.ai_canonname = NULL;
367 hints.ai_addr = NULL;
368 hints.ai_next = NULL;
369
370 // Windows doesn't like NULL for service, so specify something
371 if ((getaddrinfo(NULL, "1", &hints, &ai)) != 0)
372 return;
373
374 for (current= ai; current != NULL; current = current->ai_next) {
375 switch (current->ai_family) {
376 case AF_INET:
377 if (!UseIPv4)
378 continue;
379 break;
380 case AF_INET6:
381 if (!UseIPv6)
382 continue;
383 break;
384 default:
385 continue;
386 }
387
388 char *addr = new char[INET6_ADDRSTRLEN];
389
390 getnameinfo(current->ai_addr, current->ai_addrlen, addr, INET6_ADDRSTRLEN,
391 NULL, 0, NI_NUMERICHOST);
392
393 result->push_back(addr);
394 }
395
396 freeaddrinfo(ai);
Pierre Ossman57cab512015-03-17 13:39:39 +0100397}
398
Tim Waugh892d10a2015-03-11 13:12:07 +0000399int TcpListener::getMyPort() {
Pierre Ossman559e8b82018-05-04 12:44:31 +0200400 return getSockPort(getFd());
Tim Waugh892d10a2015-03-11 13:12:07 +0000401}
402
403
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200404void network::createLocalTcpListeners(std::list<SocketListener*> *listeners,
Tim Waugh892d10a2015-03-11 13:12:07 +0000405 int port)
406{
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200407 struct addrinfo ai[2];
408 vnc_sockaddr_t sa[2];
Pierre Ossman9d784402015-03-17 17:10:10 +0100409
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200410 memset(ai, 0, sizeof(ai));
411 memset(sa, 0, sizeof(sa));
Pierre Ossman9d784402015-03-17 17:10:10 +0100412
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200413 sa[0].u.sin.sin_family = AF_INET;
414 sa[0].u.sin.sin_port = htons (port);
415 sa[0].u.sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
Tim Waugh892d10a2015-03-11 13:12:07 +0000416
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200417 ai[0].ai_family = sa[0].u.sin.sin_family;
418 ai[0].ai_addr = &sa[0].u.sa;
419 ai[0].ai_addrlen = sizeof(sa[0].u.sin);
420 ai[0].ai_next = &ai[1];
Tim Waugh892d10a2015-03-11 13:12:07 +0000421
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200422 sa[1].u.sin6.sin6_family = AF_INET6;
423 sa[1].u.sin6.sin6_port = htons (port);
424 sa[1].u.sin6.sin6_addr = in6addr_loopback;
425
426 ai[1].ai_family = sa[1].u.sin6.sin6_family;
427 ai[1].ai_addr = &sa[1].u.sa;
428 ai[1].ai_addrlen = sizeof(sa[1].u.sin6);
429 ai[1].ai_next = NULL;
430
431 createTcpListeners(listeners, ai);
Tim Waugh892d10a2015-03-11 13:12:07 +0000432}
433
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200434void network::createTcpListeners(std::list<SocketListener*> *listeners,
Tim Waugh892d10a2015-03-11 13:12:07 +0000435 const char *addr,
436 int port)
437{
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200438 struct addrinfo *ai, hints;
Tim Waugh892d10a2015-03-11 13:12:07 +0000439 char service[16];
Pierre Ossmanf7d15002015-03-17 17:10:56 +0100440 int result;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000441
Pierre Ossman9d784402015-03-17 17:10:10 +0100442 initSockets();
443
Tim Waugh6ae42df2014-11-17 17:07:07 +0000444 memset(&hints, 0, sizeof(struct addrinfo));
Tim Waugh892d10a2015-03-11 13:12:07 +0000445 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000446 hints.ai_family = AF_UNSPEC;
447 hints.ai_socktype = SOCK_STREAM;
448 hints.ai_canonname = NULL;
449 hints.ai_addr = NULL;
450 hints.ai_next = NULL;
451
Tim Waugh892d10a2015-03-11 13:12:07 +0000452 snprintf (service, sizeof (service) - 1, "%d", port);
453 service[sizeof (service) - 1] = '\0';
Pierre Ossmanf7d15002015-03-17 17:10:56 +0100454 if ((result = getaddrinfo(addr, service, &hints, &ai)) != 0)
455 throw rdr::Exception("unable to resolve listening address: %s",
456 gai_strerror(result));
Tim Waugh6ae42df2014-11-17 17:07:07 +0000457
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200458 try {
459 createTcpListeners(listeners, ai);
460 } catch(...) {
461 freeaddrinfo(ai);
462 throw;
463 }
464}
465
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200466void network::createTcpListeners(std::list<SocketListener*> *listeners,
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200467 const struct addrinfo *ai)
468{
469 const struct addrinfo *current;
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200470 std::list<SocketListener*> new_listeners;
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200471
472 initSockets();
473
Tim Waugh892d10a2015-03-11 13:12:07 +0000474 for (current = ai; current != NULL; current = current->ai_next) {
475 switch (current->ai_family) {
476 case AF_INET:
477 if (!UseIPv4)
478 continue;
479 break;
480
481 case AF_INET6:
482 if (!UseIPv6)
483 continue;
484 break;
485
486 default:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000487 continue;
Tim Waugh892d10a2015-03-11 13:12:07 +0000488 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000489
Tim Waugh892d10a2015-03-11 13:12:07 +0000490 try {
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200491 new_listeners.push_back(new TcpListener(current->ai_addr,
492 current->ai_addrlen));
Tim Waugh892d10a2015-03-11 13:12:07 +0000493 } catch (SocketException& e) {
494 // Ignore this if it is due to lack of address family support on
495 // the interface or on the system
496 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) {
497 // Otherwise, report the error
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200498 while (!new_listeners.empty()) {
499 delete new_listeners.back();
500 new_listeners.pop_back();
501 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000502 throw;
503 }
504 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000505 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000506
Tim Waugh892d10a2015-03-11 13:12:07 +0000507 if (new_listeners.empty ())
508 throw SocketException("createTcpListeners: no addresses available",
509 EADDRNOTAVAIL);
510
511 listeners->splice (listeners->end(), new_listeners);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000512}
513
514
515TcpFilter::TcpFilter(const char* spec) {
516 rfb::CharArray tmp;
Adam Tkacd36b6262009-09-04 10:57:20 +0000517 tmp.buf = rfb::strDup(spec);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000518 while (tmp.buf) {
519 rfb::CharArray first;
520 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
521 if (strlen(first.buf))
522 filter.push_back(parsePattern(first.buf));
523 }
524}
525
526TcpFilter::~TcpFilter() {
527}
528
529
530static bool
Tim Waughc24a64d2015-03-13 16:07:29 +0000531patternMatchIP(const TcpFilter::Pattern& pattern, vnc_sockaddr_t *sa) {
532 switch (pattern.address.u.sa.sa_family) {
533 unsigned long address;
534
535 case AF_INET:
536 if (sa->u.sa.sa_family != AF_INET)
537 return false;
538
539 address = sa->u.sin.sin_addr.s_addr;
540 if (address == htonl (INADDR_NONE)) return false;
541 return ((pattern.address.u.sin.sin_addr.s_addr &
542 pattern.mask.u.sin.sin_addr.s_addr) ==
543 (address & pattern.mask.u.sin.sin_addr.s_addr));
544
545 case AF_INET6:
546 if (sa->u.sa.sa_family != AF_INET6)
547 return false;
548
549 for (unsigned int n = 0; n < 16; n++) {
550 unsigned int bits = (n + 1) * 8;
551 unsigned int mask;
552 if (pattern.prefixlen > bits)
553 mask = 0xff;
554 else {
555 unsigned int lastbits = 0xff;
556 lastbits <<= bits - pattern.prefixlen;
557 mask = lastbits & 0xff;
558 }
559
560 if ((pattern.address.u.sin6.sin6_addr.s6_addr[n] & mask) !=
561 (sa->u.sin6.sin6_addr.s6_addr[n] & mask))
562 return false;
563
564 if (mask < 0xff)
565 break;
566 }
567
568 return true;
569
570 case AF_UNSPEC:
571 // Any address matches
572 return true;
573
574 default:
575 break;
576 }
577
578 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000579}
580
581bool
582TcpFilter::verifyConnection(Socket* s) {
583 rfb::CharArray name;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000584 vnc_sockaddr_t sa;
585 socklen_t sa_size = sizeof(sa);
Tim Waughc24a64d2015-03-13 16:07:29 +0000586
587 if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0)
Tim Waugh6ae42df2014-11-17 17:07:07 +0000588 return false;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000589
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000590 name.buf = s->getPeerAddress();
591 std::list<TcpFilter::Pattern>::iterator i;
592 for (i=filter.begin(); i!=filter.end(); i++) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000593 if (patternMatchIP(*i, &sa)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000594 switch ((*i).action) {
595 case Accept:
596 vlog.debug("ACCEPT %s", name.buf);
597 return true;
598 case Query:
599 vlog.debug("QUERY %s", name.buf);
600 s->setRequiresQuery();
601 return true;
602 case Reject:
603 vlog.debug("REJECT %s", name.buf);
604 return false;
605 }
606 }
607 }
608
609 vlog.debug("[REJECT] %s", name.buf);
610 return false;
611}
612
613
614TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
615 TcpFilter::Pattern pattern;
616
Tim Waughc24a64d2015-03-13 16:07:29 +0000617 rfb::CharArray addr, pref;
618 bool prefix_specified;
619 int family;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000620
Pierre Ossman398a6f42015-12-07 12:13:20 +0100621 initSockets();
622
Tim Waughc24a64d2015-03-13 16:07:29 +0000623 prefix_specified = rfb::strSplit(&p[1], '/', &addr.buf, &pref.buf);
624 if (addr.buf[0] == '\0') {
625 // Match any address
626 memset (&pattern.address, 0, sizeof (pattern.address));
627 pattern.address.u.sa.sa_family = AF_UNSPEC;
628 pattern.prefixlen = 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000629 } else {
Tim Waughc24a64d2015-03-13 16:07:29 +0000630 struct addrinfo hints;
631 struct addrinfo *ai;
632 char *p = addr.buf;
633 int result;
634 memset (&hints, 0, sizeof (hints));
635 hints.ai_family = AF_UNSPEC;
636 hints.ai_flags = AI_NUMERICHOST;
637
638 // Take out brackets, if present
639 if (*p == '[') {
640 size_t len;
641 p++;
642 len = strlen (p);
643 if (len > 0 && p[len - 1] == ']')
644 p[len - 1] = '\0';
645 }
646
647 if ((result = getaddrinfo (p, NULL, &hints, &ai)) != 0) {
648 throw Exception("unable to resolve host by name: %s",
649 gai_strerror(result));
650 }
651
652 memcpy (&pattern.address.u.sa, ai->ai_addr, ai->ai_addrlen);
653 freeaddrinfo (ai);
Tim Waughc24a64d2015-03-13 16:07:29 +0000654
655 family = pattern.address.u.sa.sa_family;
656
657 if (prefix_specified) {
658 if (family == AF_INET &&
659 rfb::strContains(pref.buf, '.')) {
660 throw Exception("mask no longer supported for filter, "
661 "use prefix instead");
662 }
663
664 pattern.prefixlen = (unsigned int) atoi(pref.buf);
665 } else {
666 switch (family) {
667 case AF_INET:
668 pattern.prefixlen = 32;
669 break;
670 case AF_INET6:
671 pattern.prefixlen = 128;
672 break;
673 default:
674 throw Exception("unknown address family");
675 }
676 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000677 }
678
Pierre Ossmanfdc55e52015-03-17 12:56:31 +0100679 family = pattern.address.u.sa.sa_family;
680
Tim Waughc24a64d2015-03-13 16:07:29 +0000681 if (pattern.prefixlen > (family == AF_INET ? 32: 128))
682 throw Exception("invalid prefix length for filter address: %u",
683 pattern.prefixlen);
684
685 // Compute mask from address and prefix length
686 memset (&pattern.mask, 0, sizeof (pattern.mask));
687 switch (family) {
688 unsigned long mask;
689 case AF_INET:
690 mask = 0;
691 for (unsigned int i=0; i<pattern.prefixlen; i++)
692 mask |= 1<<(31-i);
693 pattern.mask.u.sin.sin_addr.s_addr = htonl(mask);
694 break;
695
696 case AF_INET6:
697 for (unsigned int n = 0; n < 16; n++) {
698 unsigned int bits = (n + 1) * 8;
699 if (pattern.prefixlen > bits)
700 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = 0xff;
701 else {
702 unsigned int lastbits = 0xff;
703 lastbits <<= bits - pattern.prefixlen;
704 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = lastbits & 0xff;
705 break;
706 }
707 }
708 break;
709 case AF_UNSPEC:
710 // No mask to compute
711 break;
712 default:
713 ; /* not reached */
714 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000715
716 switch(p[0]) {
717 case '+': pattern.action = TcpFilter::Accept; break;
718 case '-': pattern.action = TcpFilter::Reject; break;
719 case '?': pattern.action = TcpFilter::Query; break;
720 };
721
722 return pattern;
723}
724
725char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000726 rfb::CharArray addr;
Tim Waughc24a64d2015-03-13 16:07:29 +0000727 char buffer[INET6_ADDRSTRLEN + 2];
728
729 if (p.address.u.sa.sa_family == AF_INET) {
730 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin),
731 buffer, sizeof (buffer), NULL, 0, NI_NUMERICHOST);
732 addr.buf = rfb::strDup(buffer);
733 } else if (p.address.u.sa.sa_family == AF_INET6) {
734 buffer[0] = '[';
735 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin6),
736 buffer + 1, sizeof (buffer) - 2, NULL, 0, NI_NUMERICHOST);
737 strcat(buffer, "]");
738 addr.buf = rfb::strDup(buffer);
Jan Grulichfeca1d32018-09-26 10:48:15 +0200739 } else
Tim Waughc24a64d2015-03-13 16:07:29 +0000740 addr.buf = rfb::strDup("");
Tim Waughc24a64d2015-03-13 16:07:29 +0000741
742 char action;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000743 switch (p.action) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000744 case Accept: action = '+'; break;
745 case Reject: action = '-'; break;
746 default:
747 case Query: action = '?'; break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000748 };
Tim Waughc24a64d2015-03-13 16:07:29 +0000749 size_t resultlen = (1 // action
750 + strlen (addr.buf) // address
751 + 1 // slash
752 + 3 // prefix length, max 128
753 + 1); // terminating nul
754 char* result = new char[resultlen];
755 if (addr.buf[0] == '\0')
756 snprintf(result, resultlen, "%c", action);
757 else
758 snprintf(result, resultlen, "%c%s/%u", action, addr.buf, p.prefixlen);
759
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000760 return result;
761}