blob: 99895ad822baf1d678602b85d00a55fec812cf8f [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{
Adam Tkac9cb6a422008-11-14 15:56:15 +0000148 int sock, err, result, family;
149 vnc_sockaddr_t sa;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000150 socklen_t salen;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000151 struct addrinfo *ai, *current, hints;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000152
153 // - Create a socket
154 initSockets();
Adam Tkac9cb6a422008-11-14 15:56:15 +0000155
Adam Tkac9cb6a422008-11-14 15:56:15 +0000156 memset(&hints, 0, sizeof(struct addrinfo));
157 hints.ai_family = AF_UNSPEC;
158 hints.ai_socktype = SOCK_STREAM;
159 hints.ai_canonname = NULL;
160 hints.ai_addr = NULL;
161 hints.ai_next = NULL;
162
163 if ((result = getaddrinfo(host, NULL, &hints, &ai)) != 0) {
164 throw Exception("unable to resolve host by name: %s",
Tim Waugha85363d2015-03-11 13:07:48 +0000165 gai_strerror(result));
Adam Tkac9cb6a422008-11-14 15:56:15 +0000166 }
167
Pierre Ossmanf1a35012015-03-03 16:02:42 +0100168 // This logic is too complex for the compiler to determine if
169 // sock is properly assigned or not.
170 sock = -1;
171
Adam Tkac9cb6a422008-11-14 15:56:15 +0000172 for (current = ai; current != NULL; current = current->ai_next) {
173 family = current->ai_family;
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100174
175 switch (family) {
176 case AF_INET:
177 if (!UseIPv4)
178 continue;
179 break;
180 case AF_INET6:
181 if (!UseIPv6)
182 continue;
183 break;
184 default:
Adam Tkac9cb6a422008-11-14 15:56:15 +0000185 continue;
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100186 }
Adam Tkac9cb6a422008-11-14 15:56:15 +0000187
188 salen = current->ai_addrlen;
189 memcpy(&sa, current->ai_addr, salen);
190
191 if (family == AF_INET)
192 sa.u.sin.sin_port = htons(port);
193 else
194 sa.u.sin6.sin6_port = htons(port);
195
Adam Tkac9cb6a422008-11-14 15:56:15 +0000196 sock = socket (family, SOCK_STREAM, 0);
197 if (sock == -1) {
198 err = errorNumber;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000199 freeaddrinfo(ai);
Adam Tkac9cb6a422008-11-14 15:56:15 +0000200 throw SocketException("unable to create socket", err);
201 }
202
203 /* Attempt to connect to the remote host */
Adam Tkacc9cda3b2009-10-30 11:13:34 +0000204 while ((result = connect(sock, &sa.u.sa, salen)) == -1) {
Adam Tkac9cb6a422008-11-14 15:56:15 +0000205 err = errorNumber;
206#ifndef WIN32
207 if (err == EINTR)
Tim Waugha85363d2015-03-11 13:07:48 +0000208 continue;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000209#endif
210 closesocket(sock);
211 break;
212 }
213
Adam Tkac9cb6a422008-11-14 15:56:15 +0000214 if (result == 0)
215 break;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000216 }
217
218 freeaddrinfo(ai);
Pierre Ossmanda9a38d2015-03-03 16:03:32 +0100219
220 if (current == NULL)
221 throw Exception("No useful address for host");
Adam Tkac9cb6a422008-11-14 15:56:15 +0000222
223 if (result == -1)
224 throw SocketException("unable connect to socket", err);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000225
226#ifndef WIN32
227 // - By default, close the socket on exec()
228 fcntl(sock, F_SETFD, FD_CLOEXEC);
229#endif
230
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000231 // Disable Nagle's algorithm, to reduce latency
232 enableNagles(sock, false);
233
234 // Create the input and output streams
235 instream = new FdInStream(sock);
236 outstream = new FdOutStream(sock);
237 ownStreams = true;
238}
239
240TcpSocket::~TcpSocket() {
241 if (closeFd)
242 closesocket(getFd());
243}
244
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000245int TcpSocket::getMyPort() {
246 return getSockPort(getFd());
247}
248
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000249char* TcpSocket::getPeerAddress() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000250 vnc_sockaddr_t sa;
251 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000252
Tim Waugh6ae42df2014-11-17 17:07:07 +0000253 if (getpeername(getFd(), &sa.u.sa, &sa_size) != 0) {
254 vlog.error("unable to get peer name for socket");
255 return rfb::strDup("");
256 }
257
Pierre Ossman14263e12014-11-19 11:14:49 +0100258 if (sa.u.sa.sa_family == AF_INET6) {
Pierre Ossman07cd2292014-11-19 11:16:04 +0100259 char buffer[INET6_ADDRSTRLEN + 2];
Tim Waugh892d10a2015-03-11 13:12:07 +0000260 int ret;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000261
Pierre Ossman07cd2292014-11-19 11:16:04 +0100262 buffer[0] = '[';
263
Tim Waugh892d10a2015-03-11 13:12:07 +0000264 ret = getnameinfo(&sa.u.sa, sizeof(sa.u.sin6),
265 buffer + 1, sizeof(buffer) - 2, NULL, 0,
266 NI_NUMERICHOST);
267 if (ret != 0) {
Pierre Ossman14263e12014-11-19 11:14:49 +0100268 vlog.error("unable to convert peer name to a string");
269 return rfb::strDup("");
270 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000271
Pierre Ossman07cd2292014-11-19 11:16:04 +0100272 strcat(buffer, "]");
273
274 return rfb::strDup(buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000275 }
Pierre Ossman14263e12014-11-19 11:14:49 +0100276
277 if (sa.u.sa.sa_family == AF_INET) {
278 char *name;
279
280 name = inet_ntoa(sa.u.sin.sin_addr);
281 if (name == NULL) {
282 vlog.error("unable to convert peer name to a string");
283 return rfb::strDup("");
284 }
285
286 return rfb::strDup(name);
287 }
288
289 vlog.error("unknown address family for socket");
290 return rfb::strDup("");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000291}
292
293int TcpSocket::getPeerPort() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000294 vnc_sockaddr_t sa;
295 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000296
Tim Waugh6ae42df2014-11-17 17:07:07 +0000297 getpeername(getFd(), &sa.u.sa, &sa_size);
298
299 switch (sa.u.sa.sa_family) {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000300 case AF_INET6:
301 return ntohs(sa.u.sin6.sin6_port);
Pierre Ossman14263e12014-11-19 11:14:49 +0100302 case AF_INET:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000303 return ntohs(sa.u.sin.sin_port);
Pierre Ossman14263e12014-11-19 11:14:49 +0100304 default:
305 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000306 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000307}
308
309char* TcpSocket::getPeerEndpoint() {
310 rfb::CharArray address; address.buf = getPeerAddress();
311 int port = getPeerPort();
312
313 int buflen = strlen(address.buf) + 32;
314 char* buffer = new char[buflen];
315 sprintf(buffer, "%s::%d", address.buf, port);
316 return buffer;
317}
318
319bool TcpSocket::sameMachine() {
Adam Tkac897814f2009-11-12 10:32:43 +0000320 vnc_sockaddr_t peeraddr, myaddr;
321 socklen_t addrlen;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000322
Adam Tkac897814f2009-11-12 10:32:43 +0000323 addrlen = sizeof(peeraddr);
324 if (getpeername(getFd(), &peeraddr.u.sa, &addrlen) < 0)
325 throw SocketException ("unable to get peer address", errorNumber);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000326
Adam Tkac897814f2009-11-12 10:32:43 +0000327 addrlen = sizeof(myaddr); /* need to reset, since getpeername overwrote */
328 if (getsockname(getFd(), &myaddr.u.sa, &addrlen) < 0)
329 throw SocketException ("unable to get my address", errorNumber);
330
331 if (peeraddr.u.sa.sa_family != myaddr.u.sa.sa_family)
332 return false;
333
Adam Tkac897814f2009-11-12 10:32:43 +0000334 if (peeraddr.u.sa.sa_family == AF_INET6)
335 return IN6_ARE_ADDR_EQUAL(&peeraddr.u.sin6.sin6_addr,
Tim Waugha85363d2015-03-11 13:07:48 +0000336 &myaddr.u.sin6.sin6_addr);
Pierre Ossman14263e12014-11-19 11:14:49 +0100337 if (peeraddr.u.sa.sa_family == AF_INET)
338 return (peeraddr.u.sin.sin_addr.s_addr == myaddr.u.sin.sin_addr.s_addr);
339
340 // No idea what this is. Assume we're on different machines.
341 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000342}
343
344void TcpSocket::shutdown()
345{
346 Socket::shutdown();
347 ::shutdown(getFd(), 2);
348}
349
350bool TcpSocket::enableNagles(int sock, bool enable) {
351 int one = enable ? 0 : 1;
352 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
Tim Waugha85363d2015-03-11 13:07:48 +0000353 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000354 int e = errorNumber;
355 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
356 return false;
357 }
358 return true;
359}
360
Pierre Ossman64069a92011-11-08 12:10:55 +0000361bool TcpSocket::cork(int sock, bool enable) {
362#ifndef TCP_CORK
363 return false;
364#else
365 int one = enable ? 1 : 0;
366 if (setsockopt(sock, IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0)
367 return false;
368 return true;
369#endif
370}
371
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000372bool TcpSocket::isSocket(int sock)
373{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000374 vnc_sockaddr_t sa;
375 socklen_t sa_size = sizeof(sa);
376 return getsockname(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000377}
378
379bool TcpSocket::isConnected(int sock)
380{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000381 vnc_sockaddr_t sa;
382 socklen_t sa_size = sizeof(sa);
383 return getpeername(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000384}
385
386int TcpSocket::getSockPort(int sock)
387{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000388 vnc_sockaddr_t sa;
389 socklen_t sa_size = sizeof(sa);
390 if (getsockname(sock, &sa.u.sa, &sa_size) < 0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000391 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000392
393 switch (sa.u.sa.sa_family) {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000394 case AF_INET6:
395 return ntohs(sa.u.sin6.sin6_port);
Tim Waugh6ae42df2014-11-17 17:07:07 +0000396 default:
397 return ntohs(sa.u.sin.sin_port);
398 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000399}
400
Tim Waugh892d10a2015-03-11 13:12:07 +0000401TcpListener::TcpListener(int sock)
Tim Waughe4d97262014-11-21 16:07:34 +0000402{
Tim Waugh892d10a2015-03-11 13:12:07 +0000403 fd = sock;
Tim Waughe4d97262014-11-21 16:07:34 +0000404}
405
Tim Waugh892d10a2015-03-11 13:12:07 +0000406TcpListener::TcpListener(const TcpListener& other)
Tim Waughe4d97262014-11-21 16:07:34 +0000407{
Pierre Ossman3ab5db42015-03-17 17:06:22 +0100408 fd = dupsocket (other.fd);
Tim Waugh892d10a2015-03-11 13:12:07 +0000409 // Hope TcpListener::shutdown(other) doesn't get called...
Tim Waughe4d97262014-11-21 16:07:34 +0000410}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000411
Tim Waugh892d10a2015-03-11 13:12:07 +0000412TcpListener& TcpListener::operator= (const TcpListener& other)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000413{
Tim Waugh892d10a2015-03-11 13:12:07 +0000414 if (this != &other)
415 {
416 closesocket (fd);
Pierre Ossman3ab5db42015-03-17 17:06:22 +0100417 fd = dupsocket (other.fd);
Tim Waugh892d10a2015-03-11 13:12:07 +0000418 // Hope TcpListener::shutdown(other) doesn't get called...
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000419 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000420 return *this;
421}
422
423TcpListener::TcpListener(const struct sockaddr *listenaddr,
424 socklen_t listenaddrlen)
425{
426 int one = 1;
427 vnc_sockaddr_t sa;
428 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000429
430 initSockets();
Tim Waugh892d10a2015-03-11 13:12:07 +0000431
432 if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0)
433 throw SocketException("unable to create listening socket", errorNumber);
434
435 memcpy (&sa, listenaddr, listenaddrlen);
436#ifdef IPV6_V6ONLY
437 if (listenaddr->sa_family == AF_INET6) {
438 if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one)))
439 throw SocketException("unable to set IPV6_V6ONLY", errorNumber);
440 }
441#endif /* defined(IPV6_V6ONLY) */
442
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000443#ifndef WIN32
444 // - By default, close the socket on exec()
Tim Waugh892d10a2015-03-11 13:12:07 +0000445 fcntl(sock, F_SETFD, FD_CLOEXEC);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000446
Tim Waugh892d10a2015-03-11 13:12:07 +0000447 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
448 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000449 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000450 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000451 throw SocketException("unable to create listening socket", e);
452 }
453#endif
454
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200455 if (bind(sock, &sa.u.sa, listenaddrlen) == -1) {
456 closesocket(sock);
457 throw SocketException("failed to bind socket", errorNumber);
458 }
459
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000460 // - Set it to be a listening socket
Tim Waugh892d10a2015-03-11 13:12:07 +0000461 if (listen(sock, 5) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000462 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000463 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000464 throw SocketException("unable to set socket to listening mode", e);
465 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000466
467 fd = sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000468}
469
470TcpListener::~TcpListener() {
Tim Waugh892d10a2015-03-11 13:12:07 +0000471 closesocket(fd);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000472}
473
474void TcpListener::shutdown()
475{
476#ifdef WIN32
477 closesocket(getFd());
478#else
479 ::shutdown(getFd(), 2);
480#endif
481}
482
483
484Socket*
485TcpListener::accept() {
486 int new_sock = -1;
487
488 // Accept an incoming connection
489 if ((new_sock = ::accept(fd, 0, 0)) < 0)
490 throw SocketException("unable to accept new connection", errorNumber);
491
492#ifndef WIN32
493 // - By default, close the socket on exec()
494 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
495#endif
496
497 // Disable Nagle's algorithm, to reduce latency
498 TcpSocket::enableNagles(new_sock, false);
499
500 // Create the socket object & check connection is allowed
501 TcpSocket* s = new TcpSocket(new_sock);
502 if (filter && !filter->verifyConnection(s)) {
503 delete s;
504 return 0;
505 }
506 return s;
507}
508
Pierre Ossman57cab512015-03-17 13:39:39 +0100509void TcpListener::getMyAddresses(std::list<char*>* result) {
Pierre Ossman57cab512015-03-17 13:39:39 +0100510 struct addrinfo *ai, *current, hints;
511
512 initSockets();
513
514 memset(&hints, 0, sizeof(struct addrinfo));
515 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
516 hints.ai_family = AF_UNSPEC;
517 hints.ai_socktype = SOCK_STREAM;
518 hints.ai_canonname = NULL;
519 hints.ai_addr = NULL;
520 hints.ai_next = NULL;
521
522 // Windows doesn't like NULL for service, so specify something
523 if ((getaddrinfo(NULL, "1", &hints, &ai)) != 0)
524 return;
525
526 for (current= ai; current != NULL; current = current->ai_next) {
527 switch (current->ai_family) {
528 case AF_INET:
529 if (!UseIPv4)
530 continue;
531 break;
532 case AF_INET6:
533 if (!UseIPv6)
534 continue;
535 break;
536 default:
537 continue;
538 }
539
540 char *addr = new char[INET6_ADDRSTRLEN];
541
542 getnameinfo(current->ai_addr, current->ai_addrlen, addr, INET6_ADDRSTRLEN,
543 NULL, 0, NI_NUMERICHOST);
544
545 result->push_back(addr);
546 }
547
548 freeaddrinfo(ai);
Pierre Ossman57cab512015-03-17 13:39:39 +0100549}
550
Tim Waugh892d10a2015-03-11 13:12:07 +0000551int TcpListener::getMyPort() {
552 return TcpSocket::getSockPort(getFd());
553}
554
555
556void network::createLocalTcpListeners(std::list<TcpListener> *listeners,
557 int port)
558{
559 std::list<TcpListener> new_listeners;
560 vnc_sockaddr_t sa;
Pierre Ossman9d784402015-03-17 17:10:10 +0100561
562 initSockets();
563
Tim Waugh892d10a2015-03-11 13:12:07 +0000564 if (UseIPv6) {
565 sa.u.sin6.sin6_family = AF_INET6;
566 sa.u.sin6.sin6_port = htons (port);
567 sa.u.sin6.sin6_addr = in6addr_loopback;
568 try {
569 new_listeners.push_back (TcpListener (&sa.u.sa, sizeof (sa.u.sin6)));
570 } catch (SocketException& e) {
571 // Ignore this if it is due to lack of address family support on
572 // the interface or on the system
573 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT)
574 // Otherwise, report the error
575 throw;
576 }
577 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000578 if (UseIPv4) {
579 sa.u.sin.sin_family = AF_INET;
580 sa.u.sin.sin_port = htons (port);
581 sa.u.sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
582 try {
583 new_listeners.push_back (TcpListener (&sa.u.sa, sizeof (sa.u.sin)));
584 } catch (SocketException& e) {
585 // Ignore this if it is due to lack of address family support on
586 // the interface or on the system
587 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT)
588 // Otherwise, report the error
589 throw;
590 }
591 }
592
593 if (new_listeners.empty ())
594 throw SocketException("createLocalTcpListeners: no addresses available",
595 EADDRNOTAVAIL);
596
597 listeners->splice (listeners->end(), new_listeners);
598}
599
600void network::createTcpListeners(std::list<TcpListener> *listeners,
601 const char *addr,
602 int port)
603{
604 std::list<TcpListener> new_listeners;
605
Tim Waugh6ae42df2014-11-17 17:07:07 +0000606 struct addrinfo *ai, *current, hints;
Tim Waugh892d10a2015-03-11 13:12:07 +0000607 char service[16];
Pierre Ossmanf7d15002015-03-17 17:10:56 +0100608 int result;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000609
Pierre Ossman9d784402015-03-17 17:10:10 +0100610 initSockets();
611
Tim Waugh6ae42df2014-11-17 17:07:07 +0000612 memset(&hints, 0, sizeof(struct addrinfo));
Tim Waugh892d10a2015-03-11 13:12:07 +0000613 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000614 hints.ai_family = AF_UNSPEC;
615 hints.ai_socktype = SOCK_STREAM;
616 hints.ai_canonname = NULL;
617 hints.ai_addr = NULL;
618 hints.ai_next = NULL;
619
Tim Waugh892d10a2015-03-11 13:12:07 +0000620 snprintf (service, sizeof (service) - 1, "%d", port);
621 service[sizeof (service) - 1] = '\0';
Pierre Ossmanf7d15002015-03-17 17:10:56 +0100622 if ((result = getaddrinfo(addr, service, &hints, &ai)) != 0)
623 throw rdr::Exception("unable to resolve listening address: %s",
624 gai_strerror(result));
Tim Waugh6ae42df2014-11-17 17:07:07 +0000625
Tim Waugh892d10a2015-03-11 13:12:07 +0000626 for (current = ai; current != NULL; current = current->ai_next) {
627 switch (current->ai_family) {
628 case AF_INET:
629 if (!UseIPv4)
630 continue;
631 break;
632
633 case AF_INET6:
634 if (!UseIPv6)
635 continue;
636 break;
637
638 default:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000639 continue;
Tim Waugh892d10a2015-03-11 13:12:07 +0000640 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000641
Tim Waugh892d10a2015-03-11 13:12:07 +0000642 try {
643 new_listeners.push_back(TcpListener (current->ai_addr,
644 current->ai_addrlen));
645 } catch (SocketException& e) {
646 // Ignore this if it is due to lack of address family support on
647 // the interface or on the system
648 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) {
649 // Otherwise, report the error
650 freeaddrinfo(ai);
651 throw;
652 }
653 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000654 }
655 freeaddrinfo(ai);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000656
Tim Waugh892d10a2015-03-11 13:12:07 +0000657 if (new_listeners.empty ())
658 throw SocketException("createTcpListeners: no addresses available",
659 EADDRNOTAVAIL);
660
661 listeners->splice (listeners->end(), new_listeners);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000662}
663
664
665TcpFilter::TcpFilter(const char* spec) {
666 rfb::CharArray tmp;
Adam Tkacd36b6262009-09-04 10:57:20 +0000667 tmp.buf = rfb::strDup(spec);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000668 while (tmp.buf) {
669 rfb::CharArray first;
670 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
671 if (strlen(first.buf))
672 filter.push_back(parsePattern(first.buf));
673 }
674}
675
676TcpFilter::~TcpFilter() {
677}
678
679
680static bool
Tim Waughc24a64d2015-03-13 16:07:29 +0000681patternMatchIP(const TcpFilter::Pattern& pattern, vnc_sockaddr_t *sa) {
682 switch (pattern.address.u.sa.sa_family) {
683 unsigned long address;
684
685 case AF_INET:
686 if (sa->u.sa.sa_family != AF_INET)
687 return false;
688
689 address = sa->u.sin.sin_addr.s_addr;
690 if (address == htonl (INADDR_NONE)) return false;
691 return ((pattern.address.u.sin.sin_addr.s_addr &
692 pattern.mask.u.sin.sin_addr.s_addr) ==
693 (address & pattern.mask.u.sin.sin_addr.s_addr));
694
695 case AF_INET6:
696 if (sa->u.sa.sa_family != AF_INET6)
697 return false;
698
699 for (unsigned int n = 0; n < 16; n++) {
700 unsigned int bits = (n + 1) * 8;
701 unsigned int mask;
702 if (pattern.prefixlen > bits)
703 mask = 0xff;
704 else {
705 unsigned int lastbits = 0xff;
706 lastbits <<= bits - pattern.prefixlen;
707 mask = lastbits & 0xff;
708 }
709
710 if ((pattern.address.u.sin6.sin6_addr.s6_addr[n] & mask) !=
711 (sa->u.sin6.sin6_addr.s6_addr[n] & mask))
712 return false;
713
714 if (mask < 0xff)
715 break;
716 }
717
718 return true;
719
720 case AF_UNSPEC:
721 // Any address matches
722 return true;
723
724 default:
725 break;
726 }
727
728 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000729}
730
731bool
732TcpFilter::verifyConnection(Socket* s) {
733 rfb::CharArray name;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000734 vnc_sockaddr_t sa;
735 socklen_t sa_size = sizeof(sa);
Tim Waughc24a64d2015-03-13 16:07:29 +0000736
737 if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0)
Tim Waugh6ae42df2014-11-17 17:07:07 +0000738 return false;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000739
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000740 name.buf = s->getPeerAddress();
741 std::list<TcpFilter::Pattern>::iterator i;
742 for (i=filter.begin(); i!=filter.end(); i++) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000743 if (patternMatchIP(*i, &sa)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000744 switch ((*i).action) {
745 case Accept:
746 vlog.debug("ACCEPT %s", name.buf);
747 return true;
748 case Query:
749 vlog.debug("QUERY %s", name.buf);
750 s->setRequiresQuery();
751 return true;
752 case Reject:
753 vlog.debug("REJECT %s", name.buf);
754 return false;
755 }
756 }
757 }
758
759 vlog.debug("[REJECT] %s", name.buf);
760 return false;
761}
762
763
764TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
765 TcpFilter::Pattern pattern;
766
Tim Waughc24a64d2015-03-13 16:07:29 +0000767 rfb::CharArray addr, pref;
768 bool prefix_specified;
769 int family;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000770
Tim Waughc24a64d2015-03-13 16:07:29 +0000771 prefix_specified = rfb::strSplit(&p[1], '/', &addr.buf, &pref.buf);
772 if (addr.buf[0] == '\0') {
773 // Match any address
774 memset (&pattern.address, 0, sizeof (pattern.address));
775 pattern.address.u.sa.sa_family = AF_UNSPEC;
776 pattern.prefixlen = 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000777 } else {
Tim Waughc24a64d2015-03-13 16:07:29 +0000778 struct addrinfo hints;
779 struct addrinfo *ai;
780 char *p = addr.buf;
781 int result;
782 memset (&hints, 0, sizeof (hints));
783 hints.ai_family = AF_UNSPEC;
784 hints.ai_flags = AI_NUMERICHOST;
785
786 // Take out brackets, if present
787 if (*p == '[') {
788 size_t len;
789 p++;
790 len = strlen (p);
791 if (len > 0 && p[len - 1] == ']')
792 p[len - 1] = '\0';
793 }
794
795 if ((result = getaddrinfo (p, NULL, &hints, &ai)) != 0) {
796 throw Exception("unable to resolve host by name: %s",
797 gai_strerror(result));
798 }
799
800 memcpy (&pattern.address.u.sa, ai->ai_addr, ai->ai_addrlen);
801 freeaddrinfo (ai);
Tim Waughc24a64d2015-03-13 16:07:29 +0000802
803 family = pattern.address.u.sa.sa_family;
804
805 if (prefix_specified) {
806 if (family == AF_INET &&
807 rfb::strContains(pref.buf, '.')) {
808 throw Exception("mask no longer supported for filter, "
809 "use prefix instead");
810 }
811
812 pattern.prefixlen = (unsigned int) atoi(pref.buf);
813 } else {
814 switch (family) {
815 case AF_INET:
816 pattern.prefixlen = 32;
817 break;
818 case AF_INET6:
819 pattern.prefixlen = 128;
820 break;
821 default:
822 throw Exception("unknown address family");
823 }
824 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000825 }
826
Pierre Ossmanfdc55e52015-03-17 12:56:31 +0100827 family = pattern.address.u.sa.sa_family;
828
Tim Waughc24a64d2015-03-13 16:07:29 +0000829 if (pattern.prefixlen > (family == AF_INET ? 32: 128))
830 throw Exception("invalid prefix length for filter address: %u",
831 pattern.prefixlen);
832
833 // Compute mask from address and prefix length
834 memset (&pattern.mask, 0, sizeof (pattern.mask));
835 switch (family) {
836 unsigned long mask;
837 case AF_INET:
838 mask = 0;
839 for (unsigned int i=0; i<pattern.prefixlen; i++)
840 mask |= 1<<(31-i);
841 pattern.mask.u.sin.sin_addr.s_addr = htonl(mask);
842 break;
843
844 case AF_INET6:
845 for (unsigned int n = 0; n < 16; n++) {
846 unsigned int bits = (n + 1) * 8;
847 if (pattern.prefixlen > bits)
848 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = 0xff;
849 else {
850 unsigned int lastbits = 0xff;
851 lastbits <<= bits - pattern.prefixlen;
852 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = lastbits & 0xff;
853 break;
854 }
855 }
856 break;
857 case AF_UNSPEC:
858 // No mask to compute
859 break;
860 default:
861 ; /* not reached */
862 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000863
864 switch(p[0]) {
865 case '+': pattern.action = TcpFilter::Accept; break;
866 case '-': pattern.action = TcpFilter::Reject; break;
867 case '?': pattern.action = TcpFilter::Query; break;
868 };
869
870 return pattern;
871}
872
873char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000874 rfb::CharArray addr;
Tim Waughc24a64d2015-03-13 16:07:29 +0000875 char buffer[INET6_ADDRSTRLEN + 2];
876
877 if (p.address.u.sa.sa_family == AF_INET) {
878 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin),
879 buffer, sizeof (buffer), NULL, 0, NI_NUMERICHOST);
880 addr.buf = rfb::strDup(buffer);
881 } else if (p.address.u.sa.sa_family == AF_INET6) {
882 buffer[0] = '[';
883 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin6),
884 buffer + 1, sizeof (buffer) - 2, NULL, 0, NI_NUMERICHOST);
885 strcat(buffer, "]");
886 addr.buf = rfb::strDup(buffer);
887 } else if (p.address.u.sa.sa_family == AF_UNSPEC)
888 addr.buf = rfb::strDup("");
Tim Waughc24a64d2015-03-13 16:07:29 +0000889
890 char action;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000891 switch (p.action) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000892 case Accept: action = '+'; break;
893 case Reject: action = '-'; break;
894 default:
895 case Query: action = '?'; break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000896 };
Tim Waughc24a64d2015-03-13 16:07:29 +0000897 size_t resultlen = (1 // action
898 + strlen (addr.buf) // address
899 + 1 // slash
900 + 3 // prefix length, max 128
901 + 1); // terminating nul
902 char* result = new char[resultlen];
903 if (addr.buf[0] == '\0')
904 snprintf(result, resultlen, "%c", action);
905 else
906 snprintf(result, resultlen, "%c%s/%u", action, addr.buf, p.prefixlen);
907
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000908 return result;
909}