blob: 4287132eacb65faf284e3d9ae957640712343147 [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
Peter Ã…strand (astrand)01dc1a62017-10-10 12:56:04 +0200352bool TcpSocket::cork(bool enable) {
Pierre Ossman64069a92011-11-08 12:10:55 +0000353#ifndef TCP_CORK
354 return false;
355#else
356 int one = enable ? 1 : 0;
Peter Ã…strand (astrand)01dc1a62017-10-10 12:56:04 +0200357 if (setsockopt(getFd(), IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0)
Pierre Ossman64069a92011-11-08 12:10:55 +0000358 return false;
359 return true;
360#endif
361}
362
Tristan Schmelchere5afb922011-09-02 15:29:25 -0700363bool TcpSocket::isListening(int sock)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000364{
Tristan Schmelchere5afb922011-09-02 15:29:25 -0700365 int listening = 0;
366 socklen_t listening_size = sizeof(listening);
367 if (getsockopt(sock, SOL_SOCKET, SO_ACCEPTCONN,
368 (char *)&listening, &listening_size) < 0)
369 return false;
370 return listening != 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000371}
372
373int TcpSocket::getSockPort(int sock)
374{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000375 vnc_sockaddr_t sa;
376 socklen_t sa_size = sizeof(sa);
377 if (getsockname(sock, &sa.u.sa, &sa_size) < 0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000378 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000379
380 switch (sa.u.sa.sa_family) {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000381 case AF_INET6:
382 return ntohs(sa.u.sin6.sin6_port);
Tim Waugh6ae42df2014-11-17 17:07:07 +0000383 default:
384 return ntohs(sa.u.sin.sin_port);
385 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000386}
387
Tim Waugh892d10a2015-03-11 13:12:07 +0000388TcpListener::TcpListener(int sock)
Tim Waughe4d97262014-11-21 16:07:34 +0000389{
Tim Waugh892d10a2015-03-11 13:12:07 +0000390 fd = sock;
Tim Waughe4d97262014-11-21 16:07:34 +0000391}
392
Tim Waugh892d10a2015-03-11 13:12:07 +0000393TcpListener::TcpListener(const struct sockaddr *listenaddr,
394 socklen_t listenaddrlen)
395{
396 int one = 1;
397 vnc_sockaddr_t sa;
398 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000399
400 initSockets();
Tim Waugh892d10a2015-03-11 13:12:07 +0000401
402 if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0)
403 throw SocketException("unable to create listening socket", errorNumber);
404
405 memcpy (&sa, listenaddr, listenaddrlen);
406#ifdef IPV6_V6ONLY
407 if (listenaddr->sa_family == AF_INET6) {
Pierre Ossman467df2a2015-09-29 09:42:03 +0200408 if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one))) {
409 int e = errorNumber;
410 closesocket(sock);
411 throw SocketException("unable to set IPV6_V6ONLY", e);
412 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000413 }
414#endif /* defined(IPV6_V6ONLY) */
415
Pierre Ossman056c1532015-04-23 11:30:59 +0200416#ifdef FD_CLOEXEC
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000417 // - By default, close the socket on exec()
Tim Waugh892d10a2015-03-11 13:12:07 +0000418 fcntl(sock, F_SETFD, FD_CLOEXEC);
Pierre Ossman056c1532015-04-23 11:30:59 +0200419#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000420
Pierre Ossman056c1532015-04-23 11:30:59 +0200421 // SO_REUSEADDR is broken on Windows. It allows binding to a port
422 // that already has a listening socket on it. SO_EXCLUSIVEADDRUSE
423 // might do what we want, but requires investigation.
424#ifndef WIN32
Tim Waugh892d10a2015-03-11 13:12:07 +0000425 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
426 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000427 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000428 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000429 throw SocketException("unable to create listening socket", e);
430 }
431#endif
432
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200433 if (bind(sock, &sa.u.sa, listenaddrlen) == -1) {
Pierre Ossman7ebce752015-09-29 09:42:36 +0200434 int e = errorNumber;
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200435 closesocket(sock);
Pierre Ossman7ebce752015-09-29 09:42:36 +0200436 throw SocketException("failed to bind socket", e);
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200437 }
438
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000439 // - Set it to be a listening socket
Tim Waugh892d10a2015-03-11 13:12:07 +0000440 if (listen(sock, 5) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000441 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000442 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000443 throw SocketException("unable to set socket to listening mode", e);
444 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000445
446 fd = sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000447}
448
449TcpListener::~TcpListener() {
Tim Waugh892d10a2015-03-11 13:12:07 +0000450 closesocket(fd);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000451}
452
453void TcpListener::shutdown()
454{
455#ifdef WIN32
456 closesocket(getFd());
457#else
458 ::shutdown(getFd(), 2);
459#endif
460}
461
462
463Socket*
464TcpListener::accept() {
465 int new_sock = -1;
466
467 // Accept an incoming connection
468 if ((new_sock = ::accept(fd, 0, 0)) < 0)
469 throw SocketException("unable to accept new connection", errorNumber);
470
471#ifndef WIN32
472 // - By default, close the socket on exec()
473 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
474#endif
475
476 // Disable Nagle's algorithm, to reduce latency
477 TcpSocket::enableNagles(new_sock, false);
478
479 // Create the socket object & check connection is allowed
480 TcpSocket* s = new TcpSocket(new_sock);
481 if (filter && !filter->verifyConnection(s)) {
482 delete s;
483 return 0;
484 }
485 return s;
486}
487
Pierre Ossman57cab512015-03-17 13:39:39 +0100488void TcpListener::getMyAddresses(std::list<char*>* result) {
Pierre Ossman57cab512015-03-17 13:39:39 +0100489 struct addrinfo *ai, *current, hints;
490
491 initSockets();
492
493 memset(&hints, 0, sizeof(struct addrinfo));
494 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
495 hints.ai_family = AF_UNSPEC;
496 hints.ai_socktype = SOCK_STREAM;
497 hints.ai_canonname = NULL;
498 hints.ai_addr = NULL;
499 hints.ai_next = NULL;
500
501 // Windows doesn't like NULL for service, so specify something
502 if ((getaddrinfo(NULL, "1", &hints, &ai)) != 0)
503 return;
504
505 for (current= ai; current != NULL; current = current->ai_next) {
506 switch (current->ai_family) {
507 case AF_INET:
508 if (!UseIPv4)
509 continue;
510 break;
511 case AF_INET6:
512 if (!UseIPv6)
513 continue;
514 break;
515 default:
516 continue;
517 }
518
519 char *addr = new char[INET6_ADDRSTRLEN];
520
521 getnameinfo(current->ai_addr, current->ai_addrlen, addr, INET6_ADDRSTRLEN,
522 NULL, 0, NI_NUMERICHOST);
523
524 result->push_back(addr);
525 }
526
527 freeaddrinfo(ai);
Pierre Ossman57cab512015-03-17 13:39:39 +0100528}
529
Tim Waugh892d10a2015-03-11 13:12:07 +0000530int TcpListener::getMyPort() {
531 return TcpSocket::getSockPort(getFd());
532}
533
534
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200535void network::createLocalTcpListeners(std::list<SocketListener*> *listeners,
Tim Waugh892d10a2015-03-11 13:12:07 +0000536 int port)
537{
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200538 struct addrinfo ai[2];
539 vnc_sockaddr_t sa[2];
Pierre Ossman9d784402015-03-17 17:10:10 +0100540
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200541 memset(ai, 0, sizeof(ai));
542 memset(sa, 0, sizeof(sa));
Pierre Ossman9d784402015-03-17 17:10:10 +0100543
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200544 sa[0].u.sin.sin_family = AF_INET;
545 sa[0].u.sin.sin_port = htons (port);
546 sa[0].u.sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
Tim Waugh892d10a2015-03-11 13:12:07 +0000547
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200548 ai[0].ai_family = sa[0].u.sin.sin_family;
549 ai[0].ai_addr = &sa[0].u.sa;
550 ai[0].ai_addrlen = sizeof(sa[0].u.sin);
551 ai[0].ai_next = &ai[1];
Tim Waugh892d10a2015-03-11 13:12:07 +0000552
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200553 sa[1].u.sin6.sin6_family = AF_INET6;
554 sa[1].u.sin6.sin6_port = htons (port);
555 sa[1].u.sin6.sin6_addr = in6addr_loopback;
556
557 ai[1].ai_family = sa[1].u.sin6.sin6_family;
558 ai[1].ai_addr = &sa[1].u.sa;
559 ai[1].ai_addrlen = sizeof(sa[1].u.sin6);
560 ai[1].ai_next = NULL;
561
562 createTcpListeners(listeners, ai);
Tim Waugh892d10a2015-03-11 13:12:07 +0000563}
564
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200565void network::createTcpListeners(std::list<SocketListener*> *listeners,
Tim Waugh892d10a2015-03-11 13:12:07 +0000566 const char *addr,
567 int port)
568{
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200569 struct addrinfo *ai, hints;
Tim Waugh892d10a2015-03-11 13:12:07 +0000570 char service[16];
Pierre Ossmanf7d15002015-03-17 17:10:56 +0100571 int result;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000572
Pierre Ossman9d784402015-03-17 17:10:10 +0100573 initSockets();
574
Tim Waugh6ae42df2014-11-17 17:07:07 +0000575 memset(&hints, 0, sizeof(struct addrinfo));
Tim Waugh892d10a2015-03-11 13:12:07 +0000576 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000577 hints.ai_family = AF_UNSPEC;
578 hints.ai_socktype = SOCK_STREAM;
579 hints.ai_canonname = NULL;
580 hints.ai_addr = NULL;
581 hints.ai_next = NULL;
582
Tim Waugh892d10a2015-03-11 13:12:07 +0000583 snprintf (service, sizeof (service) - 1, "%d", port);
584 service[sizeof (service) - 1] = '\0';
Pierre Ossmanf7d15002015-03-17 17:10:56 +0100585 if ((result = getaddrinfo(addr, service, &hints, &ai)) != 0)
586 throw rdr::Exception("unable to resolve listening address: %s",
587 gai_strerror(result));
Tim Waugh6ae42df2014-11-17 17:07:07 +0000588
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200589 try {
590 createTcpListeners(listeners, ai);
591 } catch(...) {
592 freeaddrinfo(ai);
593 throw;
594 }
595}
596
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200597void network::createTcpListeners(std::list<SocketListener*> *listeners,
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200598 const struct addrinfo *ai)
599{
600 const struct addrinfo *current;
Pierre Ossmane3a2be62018-05-03 14:03:55 +0200601 std::list<SocketListener*> new_listeners;
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200602
603 initSockets();
604
Tim Waugh892d10a2015-03-11 13:12:07 +0000605 for (current = ai; current != NULL; current = current->ai_next) {
606 switch (current->ai_family) {
607 case AF_INET:
608 if (!UseIPv4)
609 continue;
610 break;
611
612 case AF_INET6:
613 if (!UseIPv6)
614 continue;
615 break;
616
617 default:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000618 continue;
Tim Waugh892d10a2015-03-11 13:12:07 +0000619 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000620
Tim Waugh892d10a2015-03-11 13:12:07 +0000621 try {
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200622 new_listeners.push_back(new TcpListener(current->ai_addr,
623 current->ai_addrlen));
Tim Waugh892d10a2015-03-11 13:12:07 +0000624 } catch (SocketException& e) {
625 // Ignore this if it is due to lack of address family support on
626 // the interface or on the system
627 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) {
628 // Otherwise, report the error
Pierre Ossmanf7aa3f92015-09-29 15:40:49 +0200629 while (!new_listeners.empty()) {
630 delete new_listeners.back();
631 new_listeners.pop_back();
632 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000633 throw;
634 }
635 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000636 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000637
Tim Waugh892d10a2015-03-11 13:12:07 +0000638 if (new_listeners.empty ())
639 throw SocketException("createTcpListeners: no addresses available",
640 EADDRNOTAVAIL);
641
642 listeners->splice (listeners->end(), new_listeners);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000643}
644
645
646TcpFilter::TcpFilter(const char* spec) {
647 rfb::CharArray tmp;
Adam Tkacd36b6262009-09-04 10:57:20 +0000648 tmp.buf = rfb::strDup(spec);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000649 while (tmp.buf) {
650 rfb::CharArray first;
651 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
652 if (strlen(first.buf))
653 filter.push_back(parsePattern(first.buf));
654 }
655}
656
657TcpFilter::~TcpFilter() {
658}
659
660
661static bool
Tim Waughc24a64d2015-03-13 16:07:29 +0000662patternMatchIP(const TcpFilter::Pattern& pattern, vnc_sockaddr_t *sa) {
663 switch (pattern.address.u.sa.sa_family) {
664 unsigned long address;
665
666 case AF_INET:
667 if (sa->u.sa.sa_family != AF_INET)
668 return false;
669
670 address = sa->u.sin.sin_addr.s_addr;
671 if (address == htonl (INADDR_NONE)) return false;
672 return ((pattern.address.u.sin.sin_addr.s_addr &
673 pattern.mask.u.sin.sin_addr.s_addr) ==
674 (address & pattern.mask.u.sin.sin_addr.s_addr));
675
676 case AF_INET6:
677 if (sa->u.sa.sa_family != AF_INET6)
678 return false;
679
680 for (unsigned int n = 0; n < 16; n++) {
681 unsigned int bits = (n + 1) * 8;
682 unsigned int mask;
683 if (pattern.prefixlen > bits)
684 mask = 0xff;
685 else {
686 unsigned int lastbits = 0xff;
687 lastbits <<= bits - pattern.prefixlen;
688 mask = lastbits & 0xff;
689 }
690
691 if ((pattern.address.u.sin6.sin6_addr.s6_addr[n] & mask) !=
692 (sa->u.sin6.sin6_addr.s6_addr[n] & mask))
693 return false;
694
695 if (mask < 0xff)
696 break;
697 }
698
699 return true;
700
701 case AF_UNSPEC:
702 // Any address matches
703 return true;
704
705 default:
706 break;
707 }
708
709 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000710}
711
712bool
713TcpFilter::verifyConnection(Socket* s) {
714 rfb::CharArray name;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000715 vnc_sockaddr_t sa;
716 socklen_t sa_size = sizeof(sa);
Tim Waughc24a64d2015-03-13 16:07:29 +0000717
718 if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0)
Tim Waugh6ae42df2014-11-17 17:07:07 +0000719 return false;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000720
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000721 name.buf = s->getPeerAddress();
722 std::list<TcpFilter::Pattern>::iterator i;
723 for (i=filter.begin(); i!=filter.end(); i++) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000724 if (patternMatchIP(*i, &sa)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000725 switch ((*i).action) {
726 case Accept:
727 vlog.debug("ACCEPT %s", name.buf);
728 return true;
729 case Query:
730 vlog.debug("QUERY %s", name.buf);
731 s->setRequiresQuery();
732 return true;
733 case Reject:
734 vlog.debug("REJECT %s", name.buf);
735 return false;
736 }
737 }
738 }
739
740 vlog.debug("[REJECT] %s", name.buf);
741 return false;
742}
743
744
745TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
746 TcpFilter::Pattern pattern;
747
Tim Waughc24a64d2015-03-13 16:07:29 +0000748 rfb::CharArray addr, pref;
749 bool prefix_specified;
750 int family;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000751
Pierre Ossman398a6f42015-12-07 12:13:20 +0100752 initSockets();
753
Tim Waughc24a64d2015-03-13 16:07:29 +0000754 prefix_specified = rfb::strSplit(&p[1], '/', &addr.buf, &pref.buf);
755 if (addr.buf[0] == '\0') {
756 // Match any address
757 memset (&pattern.address, 0, sizeof (pattern.address));
758 pattern.address.u.sa.sa_family = AF_UNSPEC;
759 pattern.prefixlen = 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000760 } else {
Tim Waughc24a64d2015-03-13 16:07:29 +0000761 struct addrinfo hints;
762 struct addrinfo *ai;
763 char *p = addr.buf;
764 int result;
765 memset (&hints, 0, sizeof (hints));
766 hints.ai_family = AF_UNSPEC;
767 hints.ai_flags = AI_NUMERICHOST;
768
769 // Take out brackets, if present
770 if (*p == '[') {
771 size_t len;
772 p++;
773 len = strlen (p);
774 if (len > 0 && p[len - 1] == ']')
775 p[len - 1] = '\0';
776 }
777
778 if ((result = getaddrinfo (p, NULL, &hints, &ai)) != 0) {
779 throw Exception("unable to resolve host by name: %s",
780 gai_strerror(result));
781 }
782
783 memcpy (&pattern.address.u.sa, ai->ai_addr, ai->ai_addrlen);
784 freeaddrinfo (ai);
Tim Waughc24a64d2015-03-13 16:07:29 +0000785
786 family = pattern.address.u.sa.sa_family;
787
788 if (prefix_specified) {
789 if (family == AF_INET &&
790 rfb::strContains(pref.buf, '.')) {
791 throw Exception("mask no longer supported for filter, "
792 "use prefix instead");
793 }
794
795 pattern.prefixlen = (unsigned int) atoi(pref.buf);
796 } else {
797 switch (family) {
798 case AF_INET:
799 pattern.prefixlen = 32;
800 break;
801 case AF_INET6:
802 pattern.prefixlen = 128;
803 break;
804 default:
805 throw Exception("unknown address family");
806 }
807 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000808 }
809
Pierre Ossmanfdc55e52015-03-17 12:56:31 +0100810 family = pattern.address.u.sa.sa_family;
811
Tim Waughc24a64d2015-03-13 16:07:29 +0000812 if (pattern.prefixlen > (family == AF_INET ? 32: 128))
813 throw Exception("invalid prefix length for filter address: %u",
814 pattern.prefixlen);
815
816 // Compute mask from address and prefix length
817 memset (&pattern.mask, 0, sizeof (pattern.mask));
818 switch (family) {
819 unsigned long mask;
820 case AF_INET:
821 mask = 0;
822 for (unsigned int i=0; i<pattern.prefixlen; i++)
823 mask |= 1<<(31-i);
824 pattern.mask.u.sin.sin_addr.s_addr = htonl(mask);
825 break;
826
827 case AF_INET6:
828 for (unsigned int n = 0; n < 16; n++) {
829 unsigned int bits = (n + 1) * 8;
830 if (pattern.prefixlen > bits)
831 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = 0xff;
832 else {
833 unsigned int lastbits = 0xff;
834 lastbits <<= bits - pattern.prefixlen;
835 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = lastbits & 0xff;
836 break;
837 }
838 }
839 break;
840 case AF_UNSPEC:
841 // No mask to compute
842 break;
843 default:
844 ; /* not reached */
845 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000846
847 switch(p[0]) {
848 case '+': pattern.action = TcpFilter::Accept; break;
849 case '-': pattern.action = TcpFilter::Reject; break;
850 case '?': pattern.action = TcpFilter::Query; break;
851 };
852
853 return pattern;
854}
855
856char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000857 rfb::CharArray addr;
Tim Waughc24a64d2015-03-13 16:07:29 +0000858 char buffer[INET6_ADDRSTRLEN + 2];
859
860 if (p.address.u.sa.sa_family == AF_INET) {
861 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin),
862 buffer, sizeof (buffer), NULL, 0, NI_NUMERICHOST);
863 addr.buf = rfb::strDup(buffer);
864 } else if (p.address.u.sa.sa_family == AF_INET6) {
865 buffer[0] = '[';
866 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin6),
867 buffer + 1, sizeof (buffer) - 2, NULL, 0, NI_NUMERICHOST);
868 strcat(buffer, "]");
869 addr.buf = rfb::strDup(buffer);
870 } else if (p.address.u.sa.sa_family == AF_UNSPEC)
871 addr.buf = rfb::strDup("");
Tim Waughc24a64d2015-03-13 16:07:29 +0000872
873 char action;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000874 switch (p.action) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000875 case Accept: action = '+'; break;
876 case Reject: action = '-'; break;
877 default:
878 case Query: action = '?'; break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000879 };
Tim Waughc24a64d2015-03-13 16:07:29 +0000880 size_t resultlen = (1 // action
881 + strlen (addr.buf) // address
882 + 1 // slash
883 + 3 // prefix length, max 128
884 + 1); // terminating nul
885 char* result = new char[resultlen];
886 if (addr.buf[0] == '\0')
887 snprintf(result, resultlen, "%c", action);
888 else
889 snprintf(result, resultlen, "%c%s/%u", action, addr.buf, p.prefixlen);
890
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000891 return result;
892}