blob: 555ce21553ee7712826d0ce2545dbd818e60af8d [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#include <signal.h>
37#include <fcntl.h>
38#endif
39
Adam Tkac04b7fd22008-03-19 16:14:48 +000040#include <stdlib.h>
Tim Waugh892d10a2015-03-11 13:12:07 +000041#include <unistd.h>
Pierre Ossmand7bbbbf2018-05-21 12:06:47 +020042
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000043#include <network/TcpSocket.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000044#include <rfb/LogWriter.h>
Pierre Ossman39b3b8f2015-01-29 17:35:45 +010045#include <rfb/Configuration.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000046
Pierre Ossman2f744172015-03-17 13:38:21 +010047#ifdef WIN32
48#include <os/winerrno.h>
49#endif
50
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000051#ifndef INADDR_NONE
52#define INADDR_NONE ((unsigned long)-1)
53#endif
54#ifndef INADDR_LOOPBACK
55#define INADDR_LOOPBACK ((unsigned long)0x7F000001)
56#endif
57
Pierre Ossmancfb21162015-03-17 14:02:11 +010058#ifndef IN6_ARE_ADDR_EQUAL
Pierre Ossman8b6aa202012-12-13 13:55:22 +000059#define IN6_ARE_ADDR_EQUAL(a,b) \
60 (memcmp ((const void*)(a), (const void*)(b), sizeof (struct in6_addr)) == 0)
61#endif
62
Pierre Ossmana6570c52015-03-17 13:38:59 +010063// Missing on older Windows and OS X
64#ifndef AI_NUMERICSERV
65#define AI_NUMERICSERV 0
66#endif
67
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000068using namespace network;
69using namespace rdr;
70
71static rfb::LogWriter vlog("TcpSocket");
72
Pierre Ossman39b3b8f2015-01-29 17:35:45 +010073static rfb::BoolParameter UseIPv4("UseIPv4", "Use IPv4 for incoming and outgoing connections.", true);
Pierre Ossman39b3b8f2015-01-29 17:35:45 +010074static rfb::BoolParameter UseIPv6("UseIPv6", "Use IPv6 for incoming and outgoing connections.", true);
Pierre Ossman39b3b8f2015-01-29 17:35:45 +010075
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000076/* Tunnelling support. */
77int network::findFreeTcpPort (void)
78{
DRCb2618e52011-02-21 13:43:44 +000079 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000080 struct sockaddr_in addr;
81 memset(&addr, 0, sizeof(addr));
82 addr.sin_family = AF_INET;
83 addr.sin_addr.s_addr = INADDR_ANY;
84
85 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
86 throw SocketException ("unable to create socket", errorNumber);
87
DRCb2618e52011-02-21 13:43:44 +000088 addr.sin_port = 0;
89 if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) < 0)
90 throw SocketException ("unable to find free port", errorNumber);
91
92 socklen_t n = sizeof(addr);
93 if (getsockname (sock, (struct sockaddr *)&addr, &n) < 0)
94 throw SocketException ("unable to get port number", errorNumber);
95
96 closesocket (sock);
97 return ntohs(addr.sin_port);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000098}
99
100
101// -=- Socket initialisation
102static bool socketsInitialised = false;
103static void initSockets() {
104 if (socketsInitialised)
105 return;
106#ifdef WIN32
107 WORD requiredVersion = MAKEWORD(2,0);
108 WSADATA initResult;
109
110 if (WSAStartup(requiredVersion, &initResult) != 0)
111 throw SocketException("unable to initialise Winsock2", errorNumber);
112#else
113 signal(SIGPIPE, SIG_IGN);
114#endif
115 socketsInitialised = true;
116}
117
118
119// -=- TcpSocket
120
Pierre Ossmand7bbbbf2018-05-21 12:06:47 +0200121TcpSocket::TcpSocket(int sock)
122 : Socket(new FdInStream(sock), new FdOutStream(sock))
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000123{
124}
125
126TcpSocket::TcpSocket(const char *host, int port)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000127{
Pierre Ossman5a126662015-07-30 12:24:36 +0200128 int sock, err, result;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000129 struct addrinfo *ai, *current, hints;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000130
131 // - Create a socket
132 initSockets();
Adam Tkac9cb6a422008-11-14 15:56:15 +0000133
Adam Tkac9cb6a422008-11-14 15:56:15 +0000134 memset(&hints, 0, sizeof(struct addrinfo));
135 hints.ai_family = AF_UNSPEC;
136 hints.ai_socktype = SOCK_STREAM;
137 hints.ai_canonname = NULL;
138 hints.ai_addr = NULL;
139 hints.ai_next = NULL;
140
141 if ((result = getaddrinfo(host, NULL, &hints, &ai)) != 0) {
142 throw Exception("unable to resolve host by name: %s",
Tim Waugha85363d2015-03-11 13:07:48 +0000143 gai_strerror(result));
Adam Tkac9cb6a422008-11-14 15:56:15 +0000144 }
145
Pierre Ossmanf1a35012015-03-03 16:02:42 +0100146 sock = -1;
Pierre Ossman5a126662015-07-30 12:24:36 +0200147 err = 0;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000148 for (current = ai; current != NULL; current = current->ai_next) {
Pierre Ossman5a126662015-07-30 12:24:36 +0200149 int family;
150 vnc_sockaddr_t sa;
151 socklen_t salen;
Pierre Ossmanb7e55742015-07-30 12:25:52 +0200152 char ntop[NI_MAXHOST];
Pierre Ossman5a126662015-07-30 12:24:36 +0200153
Adam Tkac9cb6a422008-11-14 15:56:15 +0000154 family = current->ai_family;
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100155
156 switch (family) {
157 case AF_INET:
158 if (!UseIPv4)
159 continue;
160 break;
161 case AF_INET6:
162 if (!UseIPv6)
163 continue;
164 break;
165 default:
Adam Tkac9cb6a422008-11-14 15:56:15 +0000166 continue;
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100167 }
Adam Tkac9cb6a422008-11-14 15:56:15 +0000168
169 salen = current->ai_addrlen;
170 memcpy(&sa, current->ai_addr, salen);
171
172 if (family == AF_INET)
173 sa.u.sin.sin_port = htons(port);
174 else
175 sa.u.sin6.sin6_port = htons(port);
176
Pierre Ossmanb7e55742015-07-30 12:25:52 +0200177 getnameinfo(&sa.u.sa, salen, ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST);
178 vlog.debug("Connecting to %s [%s] port %d", host, ntop, port);
179
Adam Tkac9cb6a422008-11-14 15:56:15 +0000180 sock = socket (family, SOCK_STREAM, 0);
181 if (sock == -1) {
182 err = errorNumber;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000183 freeaddrinfo(ai);
Adam Tkac9cb6a422008-11-14 15:56:15 +0000184 throw SocketException("unable to create socket", err);
185 }
186
187 /* Attempt to connect to the remote host */
Adam Tkacc9cda3b2009-10-30 11:13:34 +0000188 while ((result = connect(sock, &sa.u.sa, salen)) == -1) {
Adam Tkac9cb6a422008-11-14 15:56:15 +0000189 err = errorNumber;
190#ifndef WIN32
191 if (err == EINTR)
Tim Waugha85363d2015-03-11 13:07:48 +0000192 continue;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000193#endif
Pierre Ossmanb7e55742015-07-30 12:25:52 +0200194 vlog.debug("Failed to connect to address %s port %d: %d",
195 ntop, port, err);
Adam Tkac9cb6a422008-11-14 15:56:15 +0000196 closesocket(sock);
Pierre Ossman5a126662015-07-30 12:24:36 +0200197 sock = -1;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000198 break;
199 }
200
Adam Tkac9cb6a422008-11-14 15:56:15 +0000201 if (result == 0)
202 break;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000203 }
204
205 freeaddrinfo(ai);
Pierre Ossmanda9a38d2015-03-03 16:03:32 +0100206
Pierre Ossman5a126662015-07-30 12:24:36 +0200207 if (sock == -1) {
208 if (err == 0)
209 throw Exception("No useful address for host");
210 else
211 throw SocketException("unable connect to socket", err);
212 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000213
214#ifndef WIN32
215 // - By default, close the socket on exec()
216 fcntl(sock, F_SETFD, FD_CLOEXEC);
217#endif
218
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000219 // Disable Nagle's algorithm, to reduce latency
220 enableNagles(sock, false);
221
222 // Create the input and output streams
223 instream = new FdInStream(sock);
224 outstream = new FdOutStream(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000225}
226
227TcpSocket::~TcpSocket() {
Pierre Ossmand7bbbbf2018-05-21 12:06:47 +0200228 closesocket(getFd());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000229}
230
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000231char* TcpSocket::getPeerAddress() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000232 vnc_sockaddr_t sa;
233 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000234
Tim Waugh6ae42df2014-11-17 17:07:07 +0000235 if (getpeername(getFd(), &sa.u.sa, &sa_size) != 0) {
236 vlog.error("unable to get peer name for socket");
237 return rfb::strDup("");
238 }
239
Pierre Ossman14263e12014-11-19 11:14:49 +0100240 if (sa.u.sa.sa_family == AF_INET6) {
Pierre Ossman07cd2292014-11-19 11:16:04 +0100241 char buffer[INET6_ADDRSTRLEN + 2];
Tim Waugh892d10a2015-03-11 13:12:07 +0000242 int ret;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000243
Pierre Ossman07cd2292014-11-19 11:16:04 +0100244 buffer[0] = '[';
245
Tim Waugh892d10a2015-03-11 13:12:07 +0000246 ret = getnameinfo(&sa.u.sa, sizeof(sa.u.sin6),
247 buffer + 1, sizeof(buffer) - 2, NULL, 0,
248 NI_NUMERICHOST);
249 if (ret != 0) {
Pierre Ossman14263e12014-11-19 11:14:49 +0100250 vlog.error("unable to convert peer name to a string");
251 return rfb::strDup("");
252 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000253
Pierre Ossman07cd2292014-11-19 11:16:04 +0100254 strcat(buffer, "]");
255
256 return rfb::strDup(buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000257 }
Pierre Ossman14263e12014-11-19 11:14:49 +0100258
259 if (sa.u.sa.sa_family == AF_INET) {
260 char *name;
261
262 name = inet_ntoa(sa.u.sin.sin_addr);
263 if (name == NULL) {
264 vlog.error("unable to convert peer name to a string");
265 return rfb::strDup("");
266 }
267
268 return rfb::strDup(name);
269 }
270
271 vlog.error("unknown address family for socket");
272 return rfb::strDup("");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000273}
274
Pierre Ossmand7bbbbf2018-05-21 12:06:47 +0200275char* TcpSocket::getPeerEndpoint() {
276 rfb::CharArray address; address.buf = getPeerAddress();
Tim Waugh6ae42df2014-11-17 17:07:07 +0000277 vnc_sockaddr_t sa;
278 socklen_t sa_size = sizeof(sa);
Pierre Ossmand7bbbbf2018-05-21 12:06:47 +0200279 int port;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000280
Tim Waugh6ae42df2014-11-17 17:07:07 +0000281 getpeername(getFd(), &sa.u.sa, &sa_size);
282
Pierre Ossmand7bbbbf2018-05-21 12:06:47 +0200283 if (sa.u.sa.sa_family == AF_INET6)
284 port = ntohs(sa.u.sin6.sin6_port);
285 else if (sa.u.sa.sa_family == AF_INET)
286 port = ntohs(sa.u.sin.sin_port);
287 else
288 port = 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000289
290 int buflen = strlen(address.buf) + 32;
291 char* buffer = new char[buflen];
292 sprintf(buffer, "%s::%d", address.buf, port);
293 return buffer;
294}
295
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000296void TcpSocket::shutdown()
297{
298 Socket::shutdown();
299 ::shutdown(getFd(), 2);
300}
301
302bool TcpSocket::enableNagles(int sock, bool enable) {
303 int one = enable ? 0 : 1;
304 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
Tim Waugha85363d2015-03-11 13:07:48 +0000305 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000306 int e = errorNumber;
307 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
308 return false;
309 }
310 return true;
311}
312
Peter Ã…strand (astrand)01dc1a62017-10-10 12:56:04 +0200313bool TcpSocket::cork(bool enable) {
Pierre Ossman64069a92011-11-08 12:10:55 +0000314#ifndef TCP_CORK
315 return false;
316#else
317 int one = enable ? 1 : 0;
Peter Ã…strand (astrand)01dc1a62017-10-10 12:56:04 +0200318 if (setsockopt(getFd(), IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0)
Pierre Ossman64069a92011-11-08 12:10:55 +0000319 return false;
320 return true;
321#endif
322}
323
Tristan Schmelchere5afb922011-09-02 15:29:25 -0700324bool TcpSocket::isListening(int sock)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000325{
Tristan Schmelchere5afb922011-09-02 15:29:25 -0700326 int listening = 0;
327 socklen_t listening_size = sizeof(listening);
328 if (getsockopt(sock, SOL_SOCKET, SO_ACCEPTCONN,
329 (char *)&listening, &listening_size) < 0)
330 return false;
331 return listening != 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000332}
333
334int TcpSocket::getSockPort(int sock)
335{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000336 vnc_sockaddr_t sa;
337 socklen_t sa_size = sizeof(sa);
338 if (getsockname(sock, &sa.u.sa, &sa_size) < 0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000339 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000340
341 switch (sa.u.sa.sa_family) {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000342 case AF_INET6:
343 return ntohs(sa.u.sin6.sin6_port);
Tim Waugh6ae42df2014-11-17 17:07:07 +0000344 default:
345 return ntohs(sa.u.sin.sin_port);
346 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000347}
348
Tim Waugh892d10a2015-03-11 13:12:07 +0000349TcpListener::TcpListener(int sock)
Tim Waughe4d97262014-11-21 16:07:34 +0000350{
Tim Waugh892d10a2015-03-11 13:12:07 +0000351 fd = sock;
Tim Waughe4d97262014-11-21 16:07:34 +0000352}
353
Tim Waugh892d10a2015-03-11 13:12:07 +0000354TcpListener::TcpListener(const struct sockaddr *listenaddr,
355 socklen_t listenaddrlen)
356{
357 int one = 1;
358 vnc_sockaddr_t sa;
359 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000360
361 initSockets();
Tim Waugh892d10a2015-03-11 13:12:07 +0000362
363 if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0)
364 throw SocketException("unable to create listening socket", errorNumber);
365
366 memcpy (&sa, listenaddr, listenaddrlen);
367#ifdef IPV6_V6ONLY
368 if (listenaddr->sa_family == AF_INET6) {
Pierre Ossman467df2a2015-09-29 09:42:03 +0200369 if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one))) {
370 int e = errorNumber;
371 closesocket(sock);
372 throw SocketException("unable to set IPV6_V6ONLY", e);
373 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000374 }
375#endif /* defined(IPV6_V6ONLY) */
376
Pierre Ossman056c1532015-04-23 11:30:59 +0200377#ifdef FD_CLOEXEC
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000378 // - By default, close the socket on exec()
Tim Waugh892d10a2015-03-11 13:12:07 +0000379 fcntl(sock, F_SETFD, FD_CLOEXEC);
Pierre Ossman056c1532015-04-23 11:30:59 +0200380#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000381
Pierre Ossman056c1532015-04-23 11:30:59 +0200382 // SO_REUSEADDR is broken on Windows. It allows binding to a port
383 // that already has a listening socket on it. SO_EXCLUSIVEADDRUSE
384 // might do what we want, but requires investigation.
385#ifndef WIN32
Tim Waugh892d10a2015-03-11 13:12:07 +0000386 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
387 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000388 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000389 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000390 throw SocketException("unable to create listening socket", e);
391 }
392#endif
393
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200394 if (bind(sock, &sa.u.sa, listenaddrlen) == -1) {
Pierre Ossman7ebce752015-09-29 09:42:36 +0200395 int e = errorNumber;
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200396 closesocket(sock);
Pierre Ossman7ebce752015-09-29 09:42:36 +0200397 throw SocketException("failed to bind socket", e);
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200398 }
399
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000400 // - Set it to be a listening socket
Tim Waugh892d10a2015-03-11 13:12:07 +0000401 if (listen(sock, 5) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000402 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000403 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000404 throw SocketException("unable to set socket to listening mode", e);
405 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000406
407 fd = sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000408}
409
410TcpListener::~TcpListener() {
Tim Waugh892d10a2015-03-11 13:12:07 +0000411 closesocket(fd);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000412}
413
414void TcpListener::shutdown()
415{
416#ifdef WIN32
417 closesocket(getFd());
418#else
419 ::shutdown(getFd(), 2);
420#endif
421}
422
423
424Socket*
425TcpListener::accept() {
426 int new_sock = -1;
427
428 // Accept an incoming connection
429 if ((new_sock = ::accept(fd, 0, 0)) < 0)
430 throw SocketException("unable to accept new connection", errorNumber);
431
432#ifndef WIN32
433 // - By default, close the socket on exec()
434 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
435#endif
436
437 // Disable Nagle's algorithm, to reduce latency
438 TcpSocket::enableNagles(new_sock, false);
439
440 // Create the socket object & check connection is allowed
441 TcpSocket* s = new TcpSocket(new_sock);
442 if (filter && !filter->verifyConnection(s)) {
443 delete s;
444 return 0;
445 }
446 return s;
447}
448
Pierre Ossman57cab512015-03-17 13:39:39 +0100449void TcpListener::getMyAddresses(std::list<char*>* result) {
Pierre Ossman57cab512015-03-17 13:39:39 +0100450 struct addrinfo *ai, *current, hints;
451
452 initSockets();
453
454 memset(&hints, 0, sizeof(struct addrinfo));
455 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
456 hints.ai_family = AF_UNSPEC;
457 hints.ai_socktype = SOCK_STREAM;
458 hints.ai_canonname = NULL;
459 hints.ai_addr = NULL;
460 hints.ai_next = NULL;
461
462 // Windows doesn't like NULL for service, so specify something
463 if ((getaddrinfo(NULL, "1", &hints, &ai)) != 0)
464 return;
465
466 for (current= ai; current != NULL; current = current->ai_next) {
467 switch (current->ai_family) {
468 case AF_INET:
469 if (!UseIPv4)
470 continue;
471 break;
472 case AF_INET6:
473 if (!UseIPv6)
474 continue;
475 break;
476 default:
477 continue;
478 }
479
480 char *addr = new char[INET6_ADDRSTRLEN];
481
482 getnameinfo(current->ai_addr, current->ai_addrlen, addr, INET6_ADDRSTRLEN,
483 NULL, 0, NI_NUMERICHOST);
484
485 result->push_back(addr);
486 }
487
488 freeaddrinfo(ai);
Pierre Ossman57cab512015-03-17 13:39:39 +0100489}
490
Tim Waugh892d10a2015-03-11 13:12:07 +0000491int TcpListener::getMyPort() {
492 return TcpSocket::getSockPort(getFd());
493}
494
495
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200496void network::createLocalTcpListeners(std::list<SocketListener*> *listeners,
Tim Waugh892d10a2015-03-11 13:12:07 +0000497 int port)
498{
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200499 struct addrinfo ai[2];
500 vnc_sockaddr_t sa[2];
Pierre Ossman9d784402015-03-17 17:10:10 +0100501
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200502 memset(ai, 0, sizeof(ai));
503 memset(sa, 0, sizeof(sa));
Pierre Ossman9d784402015-03-17 17:10:10 +0100504
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200505 sa[0].u.sin.sin_family = AF_INET;
506 sa[0].u.sin.sin_port = htons (port);
507 sa[0].u.sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
Tim Waugh892d10a2015-03-11 13:12:07 +0000508
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200509 ai[0].ai_family = sa[0].u.sin.sin_family;
510 ai[0].ai_addr = &sa[0].u.sa;
511 ai[0].ai_addrlen = sizeof(sa[0].u.sin);
512 ai[0].ai_next = &ai[1];
Tim Waugh892d10a2015-03-11 13:12:07 +0000513
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200514 sa[1].u.sin6.sin6_family = AF_INET6;
515 sa[1].u.sin6.sin6_port = htons (port);
516 sa[1].u.sin6.sin6_addr = in6addr_loopback;
517
518 ai[1].ai_family = sa[1].u.sin6.sin6_family;
519 ai[1].ai_addr = &sa[1].u.sa;
520 ai[1].ai_addrlen = sizeof(sa[1].u.sin6);
521 ai[1].ai_next = NULL;
522
523 createTcpListeners(listeners, ai);
Tim Waugh892d10a2015-03-11 13:12:07 +0000524}
525
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200526void network::createTcpListeners(std::list<SocketListener*> *listeners,
Tim Waugh892d10a2015-03-11 13:12:07 +0000527 const char *addr,
528 int port)
529{
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200530 struct addrinfo *ai, hints;
Tim Waugh892d10a2015-03-11 13:12:07 +0000531 char service[16];
Pierre Ossmanf7d15002015-03-17 17:10:56 +0100532 int result;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000533
Pierre Ossman9d784402015-03-17 17:10:10 +0100534 initSockets();
535
Tim Waugh6ae42df2014-11-17 17:07:07 +0000536 memset(&hints, 0, sizeof(struct addrinfo));
Tim Waugh892d10a2015-03-11 13:12:07 +0000537 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000538 hints.ai_family = AF_UNSPEC;
539 hints.ai_socktype = SOCK_STREAM;
540 hints.ai_canonname = NULL;
541 hints.ai_addr = NULL;
542 hints.ai_next = NULL;
543
Tim Waugh892d10a2015-03-11 13:12:07 +0000544 snprintf (service, sizeof (service) - 1, "%d", port);
545 service[sizeof (service) - 1] = '\0';
Pierre Ossmanf7d15002015-03-17 17:10:56 +0100546 if ((result = getaddrinfo(addr, service, &hints, &ai)) != 0)
547 throw rdr::Exception("unable to resolve listening address: %s",
548 gai_strerror(result));
Tim Waugh6ae42df2014-11-17 17:07:07 +0000549
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200550 try {
551 createTcpListeners(listeners, ai);
552 } catch(...) {
553 freeaddrinfo(ai);
554 throw;
555 }
556}
557
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200558void network::createTcpListeners(std::list<SocketListener*> *listeners,
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200559 const struct addrinfo *ai)
560{
561 const struct addrinfo *current;
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200562 std::list<SocketListener*> new_listeners;
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200563
564 initSockets();
565
Tim Waugh892d10a2015-03-11 13:12:07 +0000566 for (current = ai; current != NULL; current = current->ai_next) {
567 switch (current->ai_family) {
568 case AF_INET:
569 if (!UseIPv4)
570 continue;
571 break;
572
573 case AF_INET6:
574 if (!UseIPv6)
575 continue;
576 break;
577
578 default:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000579 continue;
Tim Waugh892d10a2015-03-11 13:12:07 +0000580 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000581
Tim Waugh892d10a2015-03-11 13:12:07 +0000582 try {
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200583 new_listeners.push_back(new TcpListener(current->ai_addr,
584 current->ai_addrlen));
Tim Waugh892d10a2015-03-11 13:12:07 +0000585 } catch (SocketException& e) {
586 // Ignore this if it is due to lack of address family support on
587 // the interface or on the system
588 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) {
589 // Otherwise, report the error
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200590 while (!new_listeners.empty()) {
591 delete new_listeners.back();
592 new_listeners.pop_back();
593 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000594 throw;
595 }
596 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000597 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000598
Tim Waugh892d10a2015-03-11 13:12:07 +0000599 if (new_listeners.empty ())
600 throw SocketException("createTcpListeners: no addresses available",
601 EADDRNOTAVAIL);
602
603 listeners->splice (listeners->end(), new_listeners);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000604}
605
606
607TcpFilter::TcpFilter(const char* spec) {
608 rfb::CharArray tmp;
Adam Tkacd36b6262009-09-04 10:57:20 +0000609 tmp.buf = rfb::strDup(spec);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000610 while (tmp.buf) {
611 rfb::CharArray first;
612 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
613 if (strlen(first.buf))
614 filter.push_back(parsePattern(first.buf));
615 }
616}
617
618TcpFilter::~TcpFilter() {
619}
620
621
622static bool
Tim Waughc24a64d2015-03-13 16:07:29 +0000623patternMatchIP(const TcpFilter::Pattern& pattern, vnc_sockaddr_t *sa) {
624 switch (pattern.address.u.sa.sa_family) {
625 unsigned long address;
626
627 case AF_INET:
628 if (sa->u.sa.sa_family != AF_INET)
629 return false;
630
631 address = sa->u.sin.sin_addr.s_addr;
632 if (address == htonl (INADDR_NONE)) return false;
633 return ((pattern.address.u.sin.sin_addr.s_addr &
634 pattern.mask.u.sin.sin_addr.s_addr) ==
635 (address & pattern.mask.u.sin.sin_addr.s_addr));
636
637 case AF_INET6:
638 if (sa->u.sa.sa_family != AF_INET6)
639 return false;
640
641 for (unsigned int n = 0; n < 16; n++) {
642 unsigned int bits = (n + 1) * 8;
643 unsigned int mask;
644 if (pattern.prefixlen > bits)
645 mask = 0xff;
646 else {
647 unsigned int lastbits = 0xff;
648 lastbits <<= bits - pattern.prefixlen;
649 mask = lastbits & 0xff;
650 }
651
652 if ((pattern.address.u.sin6.sin6_addr.s6_addr[n] & mask) !=
653 (sa->u.sin6.sin6_addr.s6_addr[n] & mask))
654 return false;
655
656 if (mask < 0xff)
657 break;
658 }
659
660 return true;
661
662 case AF_UNSPEC:
663 // Any address matches
664 return true;
665
666 default:
667 break;
668 }
669
670 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000671}
672
673bool
674TcpFilter::verifyConnection(Socket* s) {
675 rfb::CharArray name;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000676 vnc_sockaddr_t sa;
677 socklen_t sa_size = sizeof(sa);
Tim Waughc24a64d2015-03-13 16:07:29 +0000678
679 if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0)
Tim Waugh6ae42df2014-11-17 17:07:07 +0000680 return false;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000681
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000682 name.buf = s->getPeerAddress();
683 std::list<TcpFilter::Pattern>::iterator i;
684 for (i=filter.begin(); i!=filter.end(); i++) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000685 if (patternMatchIP(*i, &sa)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000686 switch ((*i).action) {
687 case Accept:
688 vlog.debug("ACCEPT %s", name.buf);
689 return true;
690 case Query:
691 vlog.debug("QUERY %s", name.buf);
692 s->setRequiresQuery();
693 return true;
694 case Reject:
695 vlog.debug("REJECT %s", name.buf);
696 return false;
697 }
698 }
699 }
700
701 vlog.debug("[REJECT] %s", name.buf);
702 return false;
703}
704
705
706TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
707 TcpFilter::Pattern pattern;
708
Tim Waughc24a64d2015-03-13 16:07:29 +0000709 rfb::CharArray addr, pref;
710 bool prefix_specified;
711 int family;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000712
Pierre Ossman398a6f42015-12-07 12:13:20 +0100713 initSockets();
714
Tim Waughc24a64d2015-03-13 16:07:29 +0000715 prefix_specified = rfb::strSplit(&p[1], '/', &addr.buf, &pref.buf);
716 if (addr.buf[0] == '\0') {
717 // Match any address
718 memset (&pattern.address, 0, sizeof (pattern.address));
719 pattern.address.u.sa.sa_family = AF_UNSPEC;
720 pattern.prefixlen = 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000721 } else {
Tim Waughc24a64d2015-03-13 16:07:29 +0000722 struct addrinfo hints;
723 struct addrinfo *ai;
724 char *p = addr.buf;
725 int result;
726 memset (&hints, 0, sizeof (hints));
727 hints.ai_family = AF_UNSPEC;
728 hints.ai_flags = AI_NUMERICHOST;
729
730 // Take out brackets, if present
731 if (*p == '[') {
732 size_t len;
733 p++;
734 len = strlen (p);
735 if (len > 0 && p[len - 1] == ']')
736 p[len - 1] = '\0';
737 }
738
739 if ((result = getaddrinfo (p, NULL, &hints, &ai)) != 0) {
740 throw Exception("unable to resolve host by name: %s",
741 gai_strerror(result));
742 }
743
744 memcpy (&pattern.address.u.sa, ai->ai_addr, ai->ai_addrlen);
745 freeaddrinfo (ai);
Tim Waughc24a64d2015-03-13 16:07:29 +0000746
747 family = pattern.address.u.sa.sa_family;
748
749 if (prefix_specified) {
750 if (family == AF_INET &&
751 rfb::strContains(pref.buf, '.')) {
752 throw Exception("mask no longer supported for filter, "
753 "use prefix instead");
754 }
755
756 pattern.prefixlen = (unsigned int) atoi(pref.buf);
757 } else {
758 switch (family) {
759 case AF_INET:
760 pattern.prefixlen = 32;
761 break;
762 case AF_INET6:
763 pattern.prefixlen = 128;
764 break;
765 default:
766 throw Exception("unknown address family");
767 }
768 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000769 }
770
Pierre Ossmanfdc55e52015-03-17 12:56:31 +0100771 family = pattern.address.u.sa.sa_family;
772
Tim Waughc24a64d2015-03-13 16:07:29 +0000773 if (pattern.prefixlen > (family == AF_INET ? 32: 128))
774 throw Exception("invalid prefix length for filter address: %u",
775 pattern.prefixlen);
776
777 // Compute mask from address and prefix length
778 memset (&pattern.mask, 0, sizeof (pattern.mask));
779 switch (family) {
780 unsigned long mask;
781 case AF_INET:
782 mask = 0;
783 for (unsigned int i=0; i<pattern.prefixlen; i++)
784 mask |= 1<<(31-i);
785 pattern.mask.u.sin.sin_addr.s_addr = htonl(mask);
786 break;
787
788 case AF_INET6:
789 for (unsigned int n = 0; n < 16; n++) {
790 unsigned int bits = (n + 1) * 8;
791 if (pattern.prefixlen > bits)
792 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = 0xff;
793 else {
794 unsigned int lastbits = 0xff;
795 lastbits <<= bits - pattern.prefixlen;
796 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = lastbits & 0xff;
797 break;
798 }
799 }
800 break;
801 case AF_UNSPEC:
802 // No mask to compute
803 break;
804 default:
805 ; /* not reached */
806 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000807
808 switch(p[0]) {
809 case '+': pattern.action = TcpFilter::Accept; break;
810 case '-': pattern.action = TcpFilter::Reject; break;
811 case '?': pattern.action = TcpFilter::Query; break;
812 };
813
814 return pattern;
815}
816
817char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000818 rfb::CharArray addr;
Tim Waughc24a64d2015-03-13 16:07:29 +0000819 char buffer[INET6_ADDRSTRLEN + 2];
820
821 if (p.address.u.sa.sa_family == AF_INET) {
822 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin),
823 buffer, sizeof (buffer), NULL, 0, NI_NUMERICHOST);
824 addr.buf = rfb::strDup(buffer);
825 } else if (p.address.u.sa.sa_family == AF_INET6) {
826 buffer[0] = '[';
827 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin6),
828 buffer + 1, sizeof (buffer) - 2, NULL, 0, NI_NUMERICHOST);
829 strcat(buffer, "]");
830 addr.buf = rfb::strDup(buffer);
831 } else if (p.address.u.sa.sa_family == AF_UNSPEC)
832 addr.buf = rfb::strDup("");
Tim Waughc24a64d2015-03-13 16:07:29 +0000833
834 char action;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000835 switch (p.action) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000836 case Accept: action = '+'; break;
837 case Reject: action = '-'; break;
838 default:
839 case Query: action = '?'; break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000840 };
Tim Waughc24a64d2015-03-13 16:07:29 +0000841 size_t resultlen = (1 // action
842 + strlen (addr.buf) // address
843 + 1 // slash
844 + 3 // prefix length, max 128
845 + 1); // terminating nul
846 char* result = new char[resultlen];
847 if (addr.buf[0] == '\0')
848 snprintf(result, resultlen, "%c", action);
849 else
850 snprintf(result, resultlen, "%c%s/%u", action, addr.buf, p.prefixlen);
851
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000852 return result;
853}