blob: a25ee244d0fa2ec1390c1e5a9a4e9309e999be72 [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
Pierre Ossman3ab5db42015-03-17 17:06:22 +0100121// -=- Socket duplication help for Windows
122static int dupsocket(int fd)
123{
124#ifdef WIN32
125 int ret;
126 WSAPROTOCOL_INFO info;
127 ret = WSADuplicateSocket(fd, GetCurrentProcessId(), &info);
128 if (ret != 0)
129 throw SocketException("unable to duplicate socket", errorNumber);
130 return WSASocket(info.iAddressFamily, info.iSocketType, info.iProtocol,
131 &info, 0, 0);
132#else
133 return dup(fd);
134#endif
135}
136
137
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000138// -=- TcpSocket
139
140TcpSocket::TcpSocket(int sock, bool close)
141 : Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close)
142{
143}
144
145TcpSocket::TcpSocket(const char *host, int port)
146 : closeFd(true)
147{
Pierre Ossman5a126662015-07-30 12:24:36 +0200148 int sock, err, result;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000149 struct addrinfo *ai, *current, hints;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000150
151 // - Create a socket
152 initSockets();
Adam Tkac9cb6a422008-11-14 15:56:15 +0000153
Adam Tkac9cb6a422008-11-14 15:56:15 +0000154 memset(&hints, 0, sizeof(struct addrinfo));
155 hints.ai_family = AF_UNSPEC;
156 hints.ai_socktype = SOCK_STREAM;
157 hints.ai_canonname = NULL;
158 hints.ai_addr = NULL;
159 hints.ai_next = NULL;
160
161 if ((result = getaddrinfo(host, NULL, &hints, &ai)) != 0) {
162 throw Exception("unable to resolve host by name: %s",
Tim Waugha85363d2015-03-11 13:07:48 +0000163 gai_strerror(result));
Adam Tkac9cb6a422008-11-14 15:56:15 +0000164 }
165
Pierre Ossmanf1a35012015-03-03 16:02:42 +0100166 sock = -1;
Pierre Ossman5a126662015-07-30 12:24:36 +0200167 err = 0;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000168 for (current = ai; current != NULL; current = current->ai_next) {
Pierre Ossman5a126662015-07-30 12:24:36 +0200169 int family;
170 vnc_sockaddr_t sa;
171 socklen_t salen;
Pierre Ossmanb7e55742015-07-30 12:25:52 +0200172 char ntop[NI_MAXHOST];
Pierre Ossman5a126662015-07-30 12:24:36 +0200173
Adam Tkac9cb6a422008-11-14 15:56:15 +0000174 family = current->ai_family;
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100175
176 switch (family) {
177 case AF_INET:
178 if (!UseIPv4)
179 continue;
180 break;
181 case AF_INET6:
182 if (!UseIPv6)
183 continue;
184 break;
185 default:
Adam Tkac9cb6a422008-11-14 15:56:15 +0000186 continue;
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100187 }
Adam Tkac9cb6a422008-11-14 15:56:15 +0000188
189 salen = current->ai_addrlen;
190 memcpy(&sa, current->ai_addr, salen);
191
192 if (family == AF_INET)
193 sa.u.sin.sin_port = htons(port);
194 else
195 sa.u.sin6.sin6_port = htons(port);
196
Pierre Ossmanb7e55742015-07-30 12:25:52 +0200197 getnameinfo(&sa.u.sa, salen, ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST);
198 vlog.debug("Connecting to %s [%s] port %d", host, ntop, port);
199
Adam Tkac9cb6a422008-11-14 15:56:15 +0000200 sock = socket (family, SOCK_STREAM, 0);
201 if (sock == -1) {
202 err = errorNumber;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000203 freeaddrinfo(ai);
Adam Tkac9cb6a422008-11-14 15:56:15 +0000204 throw SocketException("unable to create socket", err);
205 }
206
207 /* Attempt to connect to the remote host */
Adam Tkacc9cda3b2009-10-30 11:13:34 +0000208 while ((result = connect(sock, &sa.u.sa, salen)) == -1) {
Adam Tkac9cb6a422008-11-14 15:56:15 +0000209 err = errorNumber;
210#ifndef WIN32
211 if (err == EINTR)
Tim Waugha85363d2015-03-11 13:07:48 +0000212 continue;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000213#endif
Pierre Ossmanb7e55742015-07-30 12:25:52 +0200214 vlog.debug("Failed to connect to address %s port %d: %d",
215 ntop, port, err);
Adam Tkac9cb6a422008-11-14 15:56:15 +0000216 closesocket(sock);
Pierre Ossman5a126662015-07-30 12:24:36 +0200217 sock = -1;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000218 break;
219 }
220
Adam Tkac9cb6a422008-11-14 15:56:15 +0000221 if (result == 0)
222 break;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000223 }
224
225 freeaddrinfo(ai);
Pierre Ossmanda9a38d2015-03-03 16:03:32 +0100226
Pierre Ossman5a126662015-07-30 12:24:36 +0200227 if (sock == -1) {
228 if (err == 0)
229 throw Exception("No useful address for host");
230 else
231 throw SocketException("unable connect to socket", err);
232 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000233
234#ifndef WIN32
235 // - By default, close the socket on exec()
236 fcntl(sock, F_SETFD, FD_CLOEXEC);
237#endif
238
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000239 // Disable Nagle's algorithm, to reduce latency
240 enableNagles(sock, false);
241
242 // Create the input and output streams
243 instream = new FdInStream(sock);
244 outstream = new FdOutStream(sock);
245 ownStreams = true;
246}
247
248TcpSocket::~TcpSocket() {
249 if (closeFd)
250 closesocket(getFd());
251}
252
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000253int TcpSocket::getMyPort() {
254 return getSockPort(getFd());
255}
256
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000257char* TcpSocket::getPeerAddress() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000258 vnc_sockaddr_t sa;
259 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000260
Tim Waugh6ae42df2014-11-17 17:07:07 +0000261 if (getpeername(getFd(), &sa.u.sa, &sa_size) != 0) {
262 vlog.error("unable to get peer name for socket");
263 return rfb::strDup("");
264 }
265
Pierre Ossman14263e12014-11-19 11:14:49 +0100266 if (sa.u.sa.sa_family == AF_INET6) {
Pierre Ossman07cd2292014-11-19 11:16:04 +0100267 char buffer[INET6_ADDRSTRLEN + 2];
Tim Waugh892d10a2015-03-11 13:12:07 +0000268 int ret;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000269
Pierre Ossman07cd2292014-11-19 11:16:04 +0100270 buffer[0] = '[';
271
Tim Waugh892d10a2015-03-11 13:12:07 +0000272 ret = getnameinfo(&sa.u.sa, sizeof(sa.u.sin6),
273 buffer + 1, sizeof(buffer) - 2, NULL, 0,
274 NI_NUMERICHOST);
275 if (ret != 0) {
Pierre Ossman14263e12014-11-19 11:14:49 +0100276 vlog.error("unable to convert peer name to a string");
277 return rfb::strDup("");
278 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000279
Pierre Ossman07cd2292014-11-19 11:16:04 +0100280 strcat(buffer, "]");
281
282 return rfb::strDup(buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000283 }
Pierre Ossman14263e12014-11-19 11:14:49 +0100284
285 if (sa.u.sa.sa_family == AF_INET) {
286 char *name;
287
288 name = inet_ntoa(sa.u.sin.sin_addr);
289 if (name == NULL) {
290 vlog.error("unable to convert peer name to a string");
291 return rfb::strDup("");
292 }
293
294 return rfb::strDup(name);
295 }
296
297 vlog.error("unknown address family for socket");
298 return rfb::strDup("");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000299}
300
301int TcpSocket::getPeerPort() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000302 vnc_sockaddr_t sa;
303 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000304
Tim Waugh6ae42df2014-11-17 17:07:07 +0000305 getpeername(getFd(), &sa.u.sa, &sa_size);
306
307 switch (sa.u.sa.sa_family) {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000308 case AF_INET6:
309 return ntohs(sa.u.sin6.sin6_port);
Pierre Ossman14263e12014-11-19 11:14:49 +0100310 case AF_INET:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000311 return ntohs(sa.u.sin.sin_port);
Pierre Ossman14263e12014-11-19 11:14:49 +0100312 default:
313 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000314 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000315}
316
317char* TcpSocket::getPeerEndpoint() {
318 rfb::CharArray address; address.buf = getPeerAddress();
319 int port = getPeerPort();
320
321 int buflen = strlen(address.buf) + 32;
322 char* buffer = new char[buflen];
323 sprintf(buffer, "%s::%d", address.buf, port);
324 return buffer;
325}
326
327bool TcpSocket::sameMachine() {
Adam Tkac897814f2009-11-12 10:32:43 +0000328 vnc_sockaddr_t peeraddr, myaddr;
329 socklen_t addrlen;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000330
Adam Tkac897814f2009-11-12 10:32:43 +0000331 addrlen = sizeof(peeraddr);
332 if (getpeername(getFd(), &peeraddr.u.sa, &addrlen) < 0)
333 throw SocketException ("unable to get peer address", errorNumber);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000334
Adam Tkac897814f2009-11-12 10:32:43 +0000335 addrlen = sizeof(myaddr); /* need to reset, since getpeername overwrote */
336 if (getsockname(getFd(), &myaddr.u.sa, &addrlen) < 0)
337 throw SocketException ("unable to get my address", errorNumber);
338
339 if (peeraddr.u.sa.sa_family != myaddr.u.sa.sa_family)
340 return false;
341
Adam Tkac897814f2009-11-12 10:32:43 +0000342 if (peeraddr.u.sa.sa_family == AF_INET6)
343 return IN6_ARE_ADDR_EQUAL(&peeraddr.u.sin6.sin6_addr,
Tim Waugha85363d2015-03-11 13:07:48 +0000344 &myaddr.u.sin6.sin6_addr);
Pierre Ossman14263e12014-11-19 11:14:49 +0100345 if (peeraddr.u.sa.sa_family == AF_INET)
346 return (peeraddr.u.sin.sin_addr.s_addr == myaddr.u.sin.sin_addr.s_addr);
347
348 // No idea what this is. Assume we're on different machines.
349 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000350}
351
352void TcpSocket::shutdown()
353{
354 Socket::shutdown();
355 ::shutdown(getFd(), 2);
356}
357
358bool TcpSocket::enableNagles(int sock, bool enable) {
359 int one = enable ? 0 : 1;
360 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
Tim Waugha85363d2015-03-11 13:07:48 +0000361 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000362 int e = errorNumber;
363 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
364 return false;
365 }
366 return true;
367}
368
Pierre Ossman64069a92011-11-08 12:10:55 +0000369bool TcpSocket::cork(int sock, bool enable) {
370#ifndef TCP_CORK
371 return false;
372#else
373 int one = enable ? 1 : 0;
374 if (setsockopt(sock, IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0)
375 return false;
376 return true;
377#endif
378}
379
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000380bool TcpSocket::isSocket(int sock)
381{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000382 vnc_sockaddr_t sa;
383 socklen_t sa_size = sizeof(sa);
384 return getsockname(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000385}
386
387bool TcpSocket::isConnected(int sock)
388{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000389 vnc_sockaddr_t sa;
390 socklen_t sa_size = sizeof(sa);
391 return getpeername(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000392}
393
394int TcpSocket::getSockPort(int sock)
395{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000396 vnc_sockaddr_t sa;
397 socklen_t sa_size = sizeof(sa);
398 if (getsockname(sock, &sa.u.sa, &sa_size) < 0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000399 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000400
401 switch (sa.u.sa.sa_family) {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000402 case AF_INET6:
403 return ntohs(sa.u.sin6.sin6_port);
Tim Waugh6ae42df2014-11-17 17:07:07 +0000404 default:
405 return ntohs(sa.u.sin.sin_port);
406 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000407}
408
Tim Waugh892d10a2015-03-11 13:12:07 +0000409TcpListener::TcpListener(int sock)
Tim Waughe4d97262014-11-21 16:07:34 +0000410{
Tim Waugh892d10a2015-03-11 13:12:07 +0000411 fd = sock;
Tim Waughe4d97262014-11-21 16:07:34 +0000412}
413
Tim Waugh892d10a2015-03-11 13:12:07 +0000414TcpListener::TcpListener(const TcpListener& other)
Tim Waughe4d97262014-11-21 16:07:34 +0000415{
Pierre Ossman3ab5db42015-03-17 17:06:22 +0100416 fd = dupsocket (other.fd);
Tim Waugh892d10a2015-03-11 13:12:07 +0000417 // Hope TcpListener::shutdown(other) doesn't get called...
Tim Waughe4d97262014-11-21 16:07:34 +0000418}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000419
Tim Waugh892d10a2015-03-11 13:12:07 +0000420TcpListener& TcpListener::operator= (const TcpListener& other)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000421{
Tim Waugh892d10a2015-03-11 13:12:07 +0000422 if (this != &other)
423 {
424 closesocket (fd);
Pierre Ossman3ab5db42015-03-17 17:06:22 +0100425 fd = dupsocket (other.fd);
Tim Waugh892d10a2015-03-11 13:12:07 +0000426 // Hope TcpListener::shutdown(other) doesn't get called...
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000427 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000428 return *this;
429}
430
431TcpListener::TcpListener(const struct sockaddr *listenaddr,
432 socklen_t listenaddrlen)
433{
434 int one = 1;
435 vnc_sockaddr_t sa;
436 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000437
438 initSockets();
Tim Waugh892d10a2015-03-11 13:12:07 +0000439
440 if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0)
441 throw SocketException("unable to create listening socket", errorNumber);
442
443 memcpy (&sa, listenaddr, listenaddrlen);
444#ifdef IPV6_V6ONLY
445 if (listenaddr->sa_family == AF_INET6) {
Pierre Ossman467df2a2015-09-29 09:42:03 +0200446 if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one))) {
447 int e = errorNumber;
448 closesocket(sock);
449 throw SocketException("unable to set IPV6_V6ONLY", e);
450 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000451 }
452#endif /* defined(IPV6_V6ONLY) */
453
Pierre Ossman056c1532015-04-23 11:30:59 +0200454#ifdef FD_CLOEXEC
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000455 // - By default, close the socket on exec()
Tim Waugh892d10a2015-03-11 13:12:07 +0000456 fcntl(sock, F_SETFD, FD_CLOEXEC);
Pierre Ossman056c1532015-04-23 11:30:59 +0200457#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000458
Pierre Ossman056c1532015-04-23 11:30:59 +0200459 // SO_REUSEADDR is broken on Windows. It allows binding to a port
460 // that already has a listening socket on it. SO_EXCLUSIVEADDRUSE
461 // might do what we want, but requires investigation.
462#ifndef WIN32
Tim Waugh892d10a2015-03-11 13:12:07 +0000463 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
464 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000465 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000466 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000467 throw SocketException("unable to create listening socket", e);
468 }
469#endif
470
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200471 if (bind(sock, &sa.u.sa, listenaddrlen) == -1) {
Pierre Ossman7ebce752015-09-29 09:42:36 +0200472 int e = errorNumber;
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200473 closesocket(sock);
Pierre Ossman7ebce752015-09-29 09:42:36 +0200474 throw SocketException("failed to bind socket", e);
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200475 }
476
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000477 // - Set it to be a listening socket
Tim Waugh892d10a2015-03-11 13:12:07 +0000478 if (listen(sock, 5) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000479 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000480 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000481 throw SocketException("unable to set socket to listening mode", e);
482 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000483
484 fd = sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000485}
486
487TcpListener::~TcpListener() {
Tim Waugh892d10a2015-03-11 13:12:07 +0000488 closesocket(fd);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000489}
490
491void TcpListener::shutdown()
492{
493#ifdef WIN32
494 closesocket(getFd());
495#else
496 ::shutdown(getFd(), 2);
497#endif
498}
499
500
501Socket*
502TcpListener::accept() {
503 int new_sock = -1;
504
505 // Accept an incoming connection
506 if ((new_sock = ::accept(fd, 0, 0)) < 0)
507 throw SocketException("unable to accept new connection", errorNumber);
508
509#ifndef WIN32
510 // - By default, close the socket on exec()
511 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
512#endif
513
514 // Disable Nagle's algorithm, to reduce latency
515 TcpSocket::enableNagles(new_sock, false);
516
517 // Create the socket object & check connection is allowed
518 TcpSocket* s = new TcpSocket(new_sock);
519 if (filter && !filter->verifyConnection(s)) {
520 delete s;
521 return 0;
522 }
523 return s;
524}
525
Pierre Ossman57cab512015-03-17 13:39:39 +0100526void TcpListener::getMyAddresses(std::list<char*>* result) {
Pierre Ossman57cab512015-03-17 13:39:39 +0100527 struct addrinfo *ai, *current, hints;
528
529 initSockets();
530
531 memset(&hints, 0, sizeof(struct addrinfo));
532 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
533 hints.ai_family = AF_UNSPEC;
534 hints.ai_socktype = SOCK_STREAM;
535 hints.ai_canonname = NULL;
536 hints.ai_addr = NULL;
537 hints.ai_next = NULL;
538
539 // Windows doesn't like NULL for service, so specify something
540 if ((getaddrinfo(NULL, "1", &hints, &ai)) != 0)
541 return;
542
543 for (current= ai; current != NULL; current = current->ai_next) {
544 switch (current->ai_family) {
545 case AF_INET:
546 if (!UseIPv4)
547 continue;
548 break;
549 case AF_INET6:
550 if (!UseIPv6)
551 continue;
552 break;
553 default:
554 continue;
555 }
556
557 char *addr = new char[INET6_ADDRSTRLEN];
558
559 getnameinfo(current->ai_addr, current->ai_addrlen, addr, INET6_ADDRSTRLEN,
560 NULL, 0, NI_NUMERICHOST);
561
562 result->push_back(addr);
563 }
564
565 freeaddrinfo(ai);
Pierre Ossman57cab512015-03-17 13:39:39 +0100566}
567
Tim Waugh892d10a2015-03-11 13:12:07 +0000568int TcpListener::getMyPort() {
569 return TcpSocket::getSockPort(getFd());
570}
571
572
573void network::createLocalTcpListeners(std::list<TcpListener> *listeners,
574 int port)
575{
576 std::list<TcpListener> new_listeners;
577 vnc_sockaddr_t sa;
Pierre Ossman9d784402015-03-17 17:10:10 +0100578
579 initSockets();
580
Tim Waugh892d10a2015-03-11 13:12:07 +0000581 if (UseIPv6) {
582 sa.u.sin6.sin6_family = AF_INET6;
583 sa.u.sin6.sin6_port = htons (port);
584 sa.u.sin6.sin6_addr = in6addr_loopback;
585 try {
586 new_listeners.push_back (TcpListener (&sa.u.sa, sizeof (sa.u.sin6)));
587 } catch (SocketException& e) {
588 // Ignore this if it is due to lack of address family support on
589 // the interface or on the system
590 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT)
591 // Otherwise, report the error
592 throw;
593 }
594 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000595 if (UseIPv4) {
596 sa.u.sin.sin_family = AF_INET;
597 sa.u.sin.sin_port = htons (port);
598 sa.u.sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
599 try {
600 new_listeners.push_back (TcpListener (&sa.u.sa, sizeof (sa.u.sin)));
601 } catch (SocketException& e) {
602 // Ignore this if it is due to lack of address family support on
603 // the interface or on the system
604 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT)
605 // Otherwise, report the error
606 throw;
607 }
608 }
609
610 if (new_listeners.empty ())
611 throw SocketException("createLocalTcpListeners: no addresses available",
612 EADDRNOTAVAIL);
613
614 listeners->splice (listeners->end(), new_listeners);
615}
616
617void network::createTcpListeners(std::list<TcpListener> *listeners,
618 const char *addr,
619 int port)
620{
621 std::list<TcpListener> new_listeners;
622
Tim Waugh6ae42df2014-11-17 17:07:07 +0000623 struct addrinfo *ai, *current, hints;
Tim Waugh892d10a2015-03-11 13:12:07 +0000624 char service[16];
Pierre Ossmanf7d15002015-03-17 17:10:56 +0100625 int result;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000626
Pierre Ossman9d784402015-03-17 17:10:10 +0100627 initSockets();
628
Tim Waugh6ae42df2014-11-17 17:07:07 +0000629 memset(&hints, 0, sizeof(struct addrinfo));
Tim Waugh892d10a2015-03-11 13:12:07 +0000630 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000631 hints.ai_family = AF_UNSPEC;
632 hints.ai_socktype = SOCK_STREAM;
633 hints.ai_canonname = NULL;
634 hints.ai_addr = NULL;
635 hints.ai_next = NULL;
636
Tim Waugh892d10a2015-03-11 13:12:07 +0000637 snprintf (service, sizeof (service) - 1, "%d", port);
638 service[sizeof (service) - 1] = '\0';
Pierre Ossmanf7d15002015-03-17 17:10:56 +0100639 if ((result = getaddrinfo(addr, service, &hints, &ai)) != 0)
640 throw rdr::Exception("unable to resolve listening address: %s",
641 gai_strerror(result));
Tim Waugh6ae42df2014-11-17 17:07:07 +0000642
Tim Waugh892d10a2015-03-11 13:12:07 +0000643 for (current = ai; current != NULL; current = current->ai_next) {
644 switch (current->ai_family) {
645 case AF_INET:
646 if (!UseIPv4)
647 continue;
648 break;
649
650 case AF_INET6:
651 if (!UseIPv6)
652 continue;
653 break;
654
655 default:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000656 continue;
Tim Waugh892d10a2015-03-11 13:12:07 +0000657 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000658
Tim Waugh892d10a2015-03-11 13:12:07 +0000659 try {
660 new_listeners.push_back(TcpListener (current->ai_addr,
661 current->ai_addrlen));
662 } catch (SocketException& e) {
663 // Ignore this if it is due to lack of address family support on
664 // the interface or on the system
665 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) {
666 // Otherwise, report the error
667 freeaddrinfo(ai);
668 throw;
669 }
670 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000671 }
672 freeaddrinfo(ai);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000673
Tim Waugh892d10a2015-03-11 13:12:07 +0000674 if (new_listeners.empty ())
675 throw SocketException("createTcpListeners: no addresses available",
676 EADDRNOTAVAIL);
677
678 listeners->splice (listeners->end(), new_listeners);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000679}
680
681
682TcpFilter::TcpFilter(const char* spec) {
683 rfb::CharArray tmp;
Adam Tkacd36b6262009-09-04 10:57:20 +0000684 tmp.buf = rfb::strDup(spec);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000685 while (tmp.buf) {
686 rfb::CharArray first;
687 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
688 if (strlen(first.buf))
689 filter.push_back(parsePattern(first.buf));
690 }
691}
692
693TcpFilter::~TcpFilter() {
694}
695
696
697static bool
Tim Waughc24a64d2015-03-13 16:07:29 +0000698patternMatchIP(const TcpFilter::Pattern& pattern, vnc_sockaddr_t *sa) {
699 switch (pattern.address.u.sa.sa_family) {
700 unsigned long address;
701
702 case AF_INET:
703 if (sa->u.sa.sa_family != AF_INET)
704 return false;
705
706 address = sa->u.sin.sin_addr.s_addr;
707 if (address == htonl (INADDR_NONE)) return false;
708 return ((pattern.address.u.sin.sin_addr.s_addr &
709 pattern.mask.u.sin.sin_addr.s_addr) ==
710 (address & pattern.mask.u.sin.sin_addr.s_addr));
711
712 case AF_INET6:
713 if (sa->u.sa.sa_family != AF_INET6)
714 return false;
715
716 for (unsigned int n = 0; n < 16; n++) {
717 unsigned int bits = (n + 1) * 8;
718 unsigned int mask;
719 if (pattern.prefixlen > bits)
720 mask = 0xff;
721 else {
722 unsigned int lastbits = 0xff;
723 lastbits <<= bits - pattern.prefixlen;
724 mask = lastbits & 0xff;
725 }
726
727 if ((pattern.address.u.sin6.sin6_addr.s6_addr[n] & mask) !=
728 (sa->u.sin6.sin6_addr.s6_addr[n] & mask))
729 return false;
730
731 if (mask < 0xff)
732 break;
733 }
734
735 return true;
736
737 case AF_UNSPEC:
738 // Any address matches
739 return true;
740
741 default:
742 break;
743 }
744
745 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000746}
747
748bool
749TcpFilter::verifyConnection(Socket* s) {
750 rfb::CharArray name;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000751 vnc_sockaddr_t sa;
752 socklen_t sa_size = sizeof(sa);
Tim Waughc24a64d2015-03-13 16:07:29 +0000753
754 if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0)
Tim Waugh6ae42df2014-11-17 17:07:07 +0000755 return false;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000756
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000757 name.buf = s->getPeerAddress();
758 std::list<TcpFilter::Pattern>::iterator i;
759 for (i=filter.begin(); i!=filter.end(); i++) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000760 if (patternMatchIP(*i, &sa)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000761 switch ((*i).action) {
762 case Accept:
763 vlog.debug("ACCEPT %s", name.buf);
764 return true;
765 case Query:
766 vlog.debug("QUERY %s", name.buf);
767 s->setRequiresQuery();
768 return true;
769 case Reject:
770 vlog.debug("REJECT %s", name.buf);
771 return false;
772 }
773 }
774 }
775
776 vlog.debug("[REJECT] %s", name.buf);
777 return false;
778}
779
780
781TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
782 TcpFilter::Pattern pattern;
783
Tim Waughc24a64d2015-03-13 16:07:29 +0000784 rfb::CharArray addr, pref;
785 bool prefix_specified;
786 int family;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000787
Tim Waughc24a64d2015-03-13 16:07:29 +0000788 prefix_specified = rfb::strSplit(&p[1], '/', &addr.buf, &pref.buf);
789 if (addr.buf[0] == '\0') {
790 // Match any address
791 memset (&pattern.address, 0, sizeof (pattern.address));
792 pattern.address.u.sa.sa_family = AF_UNSPEC;
793 pattern.prefixlen = 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000794 } else {
Tim Waughc24a64d2015-03-13 16:07:29 +0000795 struct addrinfo hints;
796 struct addrinfo *ai;
797 char *p = addr.buf;
798 int result;
799 memset (&hints, 0, sizeof (hints));
800 hints.ai_family = AF_UNSPEC;
801 hints.ai_flags = AI_NUMERICHOST;
802
803 // Take out brackets, if present
804 if (*p == '[') {
805 size_t len;
806 p++;
807 len = strlen (p);
808 if (len > 0 && p[len - 1] == ']')
809 p[len - 1] = '\0';
810 }
811
812 if ((result = getaddrinfo (p, NULL, &hints, &ai)) != 0) {
813 throw Exception("unable to resolve host by name: %s",
814 gai_strerror(result));
815 }
816
817 memcpy (&pattern.address.u.sa, ai->ai_addr, ai->ai_addrlen);
818 freeaddrinfo (ai);
Tim Waughc24a64d2015-03-13 16:07:29 +0000819
820 family = pattern.address.u.sa.sa_family;
821
822 if (prefix_specified) {
823 if (family == AF_INET &&
824 rfb::strContains(pref.buf, '.')) {
825 throw Exception("mask no longer supported for filter, "
826 "use prefix instead");
827 }
828
829 pattern.prefixlen = (unsigned int) atoi(pref.buf);
830 } else {
831 switch (family) {
832 case AF_INET:
833 pattern.prefixlen = 32;
834 break;
835 case AF_INET6:
836 pattern.prefixlen = 128;
837 break;
838 default:
839 throw Exception("unknown address family");
840 }
841 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000842 }
843
Pierre Ossmanfdc55e52015-03-17 12:56:31 +0100844 family = pattern.address.u.sa.sa_family;
845
Tim Waughc24a64d2015-03-13 16:07:29 +0000846 if (pattern.prefixlen > (family == AF_INET ? 32: 128))
847 throw Exception("invalid prefix length for filter address: %u",
848 pattern.prefixlen);
849
850 // Compute mask from address and prefix length
851 memset (&pattern.mask, 0, sizeof (pattern.mask));
852 switch (family) {
853 unsigned long mask;
854 case AF_INET:
855 mask = 0;
856 for (unsigned int i=0; i<pattern.prefixlen; i++)
857 mask |= 1<<(31-i);
858 pattern.mask.u.sin.sin_addr.s_addr = htonl(mask);
859 break;
860
861 case AF_INET6:
862 for (unsigned int n = 0; n < 16; n++) {
863 unsigned int bits = (n + 1) * 8;
864 if (pattern.prefixlen > bits)
865 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = 0xff;
866 else {
867 unsigned int lastbits = 0xff;
868 lastbits <<= bits - pattern.prefixlen;
869 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = lastbits & 0xff;
870 break;
871 }
872 }
873 break;
874 case AF_UNSPEC:
875 // No mask to compute
876 break;
877 default:
878 ; /* not reached */
879 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000880
881 switch(p[0]) {
882 case '+': pattern.action = TcpFilter::Accept; break;
883 case '-': pattern.action = TcpFilter::Reject; break;
884 case '?': pattern.action = TcpFilter::Query; break;
885 };
886
887 return pattern;
888}
889
890char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000891 rfb::CharArray addr;
Tim Waughc24a64d2015-03-13 16:07:29 +0000892 char buffer[INET6_ADDRSTRLEN + 2];
893
894 if (p.address.u.sa.sa_family == AF_INET) {
895 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin),
896 buffer, sizeof (buffer), NULL, 0, NI_NUMERICHOST);
897 addr.buf = rfb::strDup(buffer);
898 } else if (p.address.u.sa.sa_family == AF_INET6) {
899 buffer[0] = '[';
900 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin6),
901 buffer + 1, sizeof (buffer) - 2, NULL, 0, NI_NUMERICHOST);
902 strcat(buffer, "]");
903 addr.buf = rfb::strDup(buffer);
904 } else if (p.address.u.sa.sa_family == AF_UNSPEC)
905 addr.buf = rfb::strDup("");
Tim Waughc24a64d2015-03-13 16:07:29 +0000906
907 char action;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000908 switch (p.action) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000909 case Accept: action = '+'; break;
910 case Reject: action = '-'; break;
911 default:
912 case Query: action = '?'; break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000913 };
Tim Waughc24a64d2015-03-13 16:07:29 +0000914 size_t resultlen = (1 // action
915 + strlen (addr.buf) // address
916 + 1 // slash
917 + 3 // prefix length, max 128
918 + 1); // terminating nul
919 char* result = new char[resultlen];
920 if (addr.buf[0] == '\0')
921 snprintf(result, resultlen, "%c", action);
922 else
923 snprintf(result, resultlen, "%c%s/%u", action, addr.buf, p.prefixlen);
924
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000925 return result;
926}