blob: 684ff8b345165313b89fbd68d398ae33aab15d9a [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;
172
Adam Tkac9cb6a422008-11-14 15:56:15 +0000173 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);
Pierre Ossman5a126662015-07-30 12:24:36 +0200211 sock = -1;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000212 break;
213 }
214
Adam Tkac9cb6a422008-11-14 15:56:15 +0000215 if (result == 0)
216 break;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000217 }
218
219 freeaddrinfo(ai);
Pierre Ossmanda9a38d2015-03-03 16:03:32 +0100220
Pierre Ossman5a126662015-07-30 12:24:36 +0200221 if (sock == -1) {
222 if (err == 0)
223 throw Exception("No useful address for host");
224 else
225 throw SocketException("unable connect to socket", err);
226 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000227
228#ifndef WIN32
229 // - By default, close the socket on exec()
230 fcntl(sock, F_SETFD, FD_CLOEXEC);
231#endif
232
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000233 // Disable Nagle's algorithm, to reduce latency
234 enableNagles(sock, false);
235
236 // Create the input and output streams
237 instream = new FdInStream(sock);
238 outstream = new FdOutStream(sock);
239 ownStreams = true;
240}
241
242TcpSocket::~TcpSocket() {
243 if (closeFd)
244 closesocket(getFd());
245}
246
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000247int TcpSocket::getMyPort() {
248 return getSockPort(getFd());
249}
250
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000251char* TcpSocket::getPeerAddress() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000252 vnc_sockaddr_t sa;
253 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000254
Tim Waugh6ae42df2014-11-17 17:07:07 +0000255 if (getpeername(getFd(), &sa.u.sa, &sa_size) != 0) {
256 vlog.error("unable to get peer name for socket");
257 return rfb::strDup("");
258 }
259
Pierre Ossman14263e12014-11-19 11:14:49 +0100260 if (sa.u.sa.sa_family == AF_INET6) {
Pierre Ossman07cd2292014-11-19 11:16:04 +0100261 char buffer[INET6_ADDRSTRLEN + 2];
Tim Waugh892d10a2015-03-11 13:12:07 +0000262 int ret;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000263
Pierre Ossman07cd2292014-11-19 11:16:04 +0100264 buffer[0] = '[';
265
Tim Waugh892d10a2015-03-11 13:12:07 +0000266 ret = getnameinfo(&sa.u.sa, sizeof(sa.u.sin6),
267 buffer + 1, sizeof(buffer) - 2, NULL, 0,
268 NI_NUMERICHOST);
269 if (ret != 0) {
Pierre Ossman14263e12014-11-19 11:14:49 +0100270 vlog.error("unable to convert peer name to a string");
271 return rfb::strDup("");
272 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000273
Pierre Ossman07cd2292014-11-19 11:16:04 +0100274 strcat(buffer, "]");
275
276 return rfb::strDup(buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000277 }
Pierre Ossman14263e12014-11-19 11:14:49 +0100278
279 if (sa.u.sa.sa_family == AF_INET) {
280 char *name;
281
282 name = inet_ntoa(sa.u.sin.sin_addr);
283 if (name == NULL) {
284 vlog.error("unable to convert peer name to a string");
285 return rfb::strDup("");
286 }
287
288 return rfb::strDup(name);
289 }
290
291 vlog.error("unknown address family for socket");
292 return rfb::strDup("");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000293}
294
295int TcpSocket::getPeerPort() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000296 vnc_sockaddr_t sa;
297 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000298
Tim Waugh6ae42df2014-11-17 17:07:07 +0000299 getpeername(getFd(), &sa.u.sa, &sa_size);
300
301 switch (sa.u.sa.sa_family) {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000302 case AF_INET6:
303 return ntohs(sa.u.sin6.sin6_port);
Pierre Ossman14263e12014-11-19 11:14:49 +0100304 case AF_INET:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000305 return ntohs(sa.u.sin.sin_port);
Pierre Ossman14263e12014-11-19 11:14:49 +0100306 default:
307 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000308 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000309}
310
311char* TcpSocket::getPeerEndpoint() {
312 rfb::CharArray address; address.buf = getPeerAddress();
313 int port = getPeerPort();
314
315 int buflen = strlen(address.buf) + 32;
316 char* buffer = new char[buflen];
317 sprintf(buffer, "%s::%d", address.buf, port);
318 return buffer;
319}
320
321bool TcpSocket::sameMachine() {
Adam Tkac897814f2009-11-12 10:32:43 +0000322 vnc_sockaddr_t peeraddr, myaddr;
323 socklen_t addrlen;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000324
Adam Tkac897814f2009-11-12 10:32:43 +0000325 addrlen = sizeof(peeraddr);
326 if (getpeername(getFd(), &peeraddr.u.sa, &addrlen) < 0)
327 throw SocketException ("unable to get peer address", errorNumber);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000328
Adam Tkac897814f2009-11-12 10:32:43 +0000329 addrlen = sizeof(myaddr); /* need to reset, since getpeername overwrote */
330 if (getsockname(getFd(), &myaddr.u.sa, &addrlen) < 0)
331 throw SocketException ("unable to get my address", errorNumber);
332
333 if (peeraddr.u.sa.sa_family != myaddr.u.sa.sa_family)
334 return false;
335
Adam Tkac897814f2009-11-12 10:32:43 +0000336 if (peeraddr.u.sa.sa_family == AF_INET6)
337 return IN6_ARE_ADDR_EQUAL(&peeraddr.u.sin6.sin6_addr,
Tim Waugha85363d2015-03-11 13:07:48 +0000338 &myaddr.u.sin6.sin6_addr);
Pierre Ossman14263e12014-11-19 11:14:49 +0100339 if (peeraddr.u.sa.sa_family == AF_INET)
340 return (peeraddr.u.sin.sin_addr.s_addr == myaddr.u.sin.sin_addr.s_addr);
341
342 // No idea what this is. Assume we're on different machines.
343 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000344}
345
346void TcpSocket::shutdown()
347{
348 Socket::shutdown();
349 ::shutdown(getFd(), 2);
350}
351
352bool TcpSocket::enableNagles(int sock, bool enable) {
353 int one = enable ? 0 : 1;
354 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
Tim Waugha85363d2015-03-11 13:07:48 +0000355 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000356 int e = errorNumber;
357 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
358 return false;
359 }
360 return true;
361}
362
Pierre Ossman64069a92011-11-08 12:10:55 +0000363bool TcpSocket::cork(int sock, bool enable) {
364#ifndef TCP_CORK
365 return false;
366#else
367 int one = enable ? 1 : 0;
368 if (setsockopt(sock, IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0)
369 return false;
370 return true;
371#endif
372}
373
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000374bool TcpSocket::isSocket(int sock)
375{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000376 vnc_sockaddr_t sa;
377 socklen_t sa_size = sizeof(sa);
378 return getsockname(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000379}
380
381bool TcpSocket::isConnected(int sock)
382{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000383 vnc_sockaddr_t sa;
384 socklen_t sa_size = sizeof(sa);
385 return getpeername(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000386}
387
388int TcpSocket::getSockPort(int sock)
389{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000390 vnc_sockaddr_t sa;
391 socklen_t sa_size = sizeof(sa);
392 if (getsockname(sock, &sa.u.sa, &sa_size) < 0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000393 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000394
395 switch (sa.u.sa.sa_family) {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000396 case AF_INET6:
397 return ntohs(sa.u.sin6.sin6_port);
Tim Waugh6ae42df2014-11-17 17:07:07 +0000398 default:
399 return ntohs(sa.u.sin.sin_port);
400 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000401}
402
Tim Waugh892d10a2015-03-11 13:12:07 +0000403TcpListener::TcpListener(int sock)
Tim Waughe4d97262014-11-21 16:07:34 +0000404{
Tim Waugh892d10a2015-03-11 13:12:07 +0000405 fd = sock;
Tim Waughe4d97262014-11-21 16:07:34 +0000406}
407
Tim Waugh892d10a2015-03-11 13:12:07 +0000408TcpListener::TcpListener(const TcpListener& other)
Tim Waughe4d97262014-11-21 16:07:34 +0000409{
Pierre Ossman3ab5db42015-03-17 17:06:22 +0100410 fd = dupsocket (other.fd);
Tim Waugh892d10a2015-03-11 13:12:07 +0000411 // Hope TcpListener::shutdown(other) doesn't get called...
Tim Waughe4d97262014-11-21 16:07:34 +0000412}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000413
Tim Waugh892d10a2015-03-11 13:12:07 +0000414TcpListener& TcpListener::operator= (const TcpListener& other)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000415{
Tim Waugh892d10a2015-03-11 13:12:07 +0000416 if (this != &other)
417 {
418 closesocket (fd);
Pierre Ossman3ab5db42015-03-17 17:06:22 +0100419 fd = dupsocket (other.fd);
Tim Waugh892d10a2015-03-11 13:12:07 +0000420 // Hope TcpListener::shutdown(other) doesn't get called...
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000421 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000422 return *this;
423}
424
425TcpListener::TcpListener(const struct sockaddr *listenaddr,
426 socklen_t listenaddrlen)
427{
428 int one = 1;
429 vnc_sockaddr_t sa;
430 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000431
432 initSockets();
Tim Waugh892d10a2015-03-11 13:12:07 +0000433
434 if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0)
435 throw SocketException("unable to create listening socket", errorNumber);
436
437 memcpy (&sa, listenaddr, listenaddrlen);
438#ifdef IPV6_V6ONLY
439 if (listenaddr->sa_family == AF_INET6) {
440 if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one)))
441 throw SocketException("unable to set IPV6_V6ONLY", errorNumber);
442 }
443#endif /* defined(IPV6_V6ONLY) */
444
Pierre Ossman056c1532015-04-23 11:30:59 +0200445#ifdef FD_CLOEXEC
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000446 // - By default, close the socket on exec()
Tim Waugh892d10a2015-03-11 13:12:07 +0000447 fcntl(sock, F_SETFD, FD_CLOEXEC);
Pierre Ossman056c1532015-04-23 11:30:59 +0200448#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000449
Pierre Ossman056c1532015-04-23 11:30:59 +0200450 // SO_REUSEADDR is broken on Windows. It allows binding to a port
451 // that already has a listening socket on it. SO_EXCLUSIVEADDRUSE
452 // might do what we want, but requires investigation.
453#ifndef WIN32
Tim Waugh892d10a2015-03-11 13:12:07 +0000454 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
455 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000456 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000457 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000458 throw SocketException("unable to create listening socket", e);
459 }
460#endif
461
Pierre Ossmanb6536e22015-04-23 11:23:16 +0200462 if (bind(sock, &sa.u.sa, listenaddrlen) == -1) {
463 closesocket(sock);
464 throw SocketException("failed to bind socket", errorNumber);
465 }
466
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000467 // - Set it to be a listening socket
Tim Waugh892d10a2015-03-11 13:12:07 +0000468 if (listen(sock, 5) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000469 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000470 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000471 throw SocketException("unable to set socket to listening mode", e);
472 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000473
474 fd = sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000475}
476
477TcpListener::~TcpListener() {
Tim Waugh892d10a2015-03-11 13:12:07 +0000478 closesocket(fd);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000479}
480
481void TcpListener::shutdown()
482{
483#ifdef WIN32
484 closesocket(getFd());
485#else
486 ::shutdown(getFd(), 2);
487#endif
488}
489
490
491Socket*
492TcpListener::accept() {
493 int new_sock = -1;
494
495 // Accept an incoming connection
496 if ((new_sock = ::accept(fd, 0, 0)) < 0)
497 throw SocketException("unable to accept new connection", errorNumber);
498
499#ifndef WIN32
500 // - By default, close the socket on exec()
501 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
502#endif
503
504 // Disable Nagle's algorithm, to reduce latency
505 TcpSocket::enableNagles(new_sock, false);
506
507 // Create the socket object & check connection is allowed
508 TcpSocket* s = new TcpSocket(new_sock);
509 if (filter && !filter->verifyConnection(s)) {
510 delete s;
511 return 0;
512 }
513 return s;
514}
515
Pierre Ossman57cab512015-03-17 13:39:39 +0100516void TcpListener::getMyAddresses(std::list<char*>* result) {
Pierre Ossman57cab512015-03-17 13:39:39 +0100517 struct addrinfo *ai, *current, hints;
518
519 initSockets();
520
521 memset(&hints, 0, sizeof(struct addrinfo));
522 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
523 hints.ai_family = AF_UNSPEC;
524 hints.ai_socktype = SOCK_STREAM;
525 hints.ai_canonname = NULL;
526 hints.ai_addr = NULL;
527 hints.ai_next = NULL;
528
529 // Windows doesn't like NULL for service, so specify something
530 if ((getaddrinfo(NULL, "1", &hints, &ai)) != 0)
531 return;
532
533 for (current= ai; current != NULL; current = current->ai_next) {
534 switch (current->ai_family) {
535 case AF_INET:
536 if (!UseIPv4)
537 continue;
538 break;
539 case AF_INET6:
540 if (!UseIPv6)
541 continue;
542 break;
543 default:
544 continue;
545 }
546
547 char *addr = new char[INET6_ADDRSTRLEN];
548
549 getnameinfo(current->ai_addr, current->ai_addrlen, addr, INET6_ADDRSTRLEN,
550 NULL, 0, NI_NUMERICHOST);
551
552 result->push_back(addr);
553 }
554
555 freeaddrinfo(ai);
Pierre Ossman57cab512015-03-17 13:39:39 +0100556}
557
Tim Waugh892d10a2015-03-11 13:12:07 +0000558int TcpListener::getMyPort() {
559 return TcpSocket::getSockPort(getFd());
560}
561
562
563void network::createLocalTcpListeners(std::list<TcpListener> *listeners,
564 int port)
565{
566 std::list<TcpListener> new_listeners;
567 vnc_sockaddr_t sa;
Pierre Ossman9d784402015-03-17 17:10:10 +0100568
569 initSockets();
570
Tim Waugh892d10a2015-03-11 13:12:07 +0000571 if (UseIPv6) {
572 sa.u.sin6.sin6_family = AF_INET6;
573 sa.u.sin6.sin6_port = htons (port);
574 sa.u.sin6.sin6_addr = in6addr_loopback;
575 try {
576 new_listeners.push_back (TcpListener (&sa.u.sa, sizeof (sa.u.sin6)));
577 } catch (SocketException& e) {
578 // Ignore this if it is due to lack of address family support on
579 // the interface or on the system
580 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT)
581 // Otherwise, report the error
582 throw;
583 }
584 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000585 if (UseIPv4) {
586 sa.u.sin.sin_family = AF_INET;
587 sa.u.sin.sin_port = htons (port);
588 sa.u.sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
589 try {
590 new_listeners.push_back (TcpListener (&sa.u.sa, sizeof (sa.u.sin)));
591 } catch (SocketException& e) {
592 // Ignore this if it is due to lack of address family support on
593 // the interface or on the system
594 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT)
595 // Otherwise, report the error
596 throw;
597 }
598 }
599
600 if (new_listeners.empty ())
601 throw SocketException("createLocalTcpListeners: no addresses available",
602 EADDRNOTAVAIL);
603
604 listeners->splice (listeners->end(), new_listeners);
605}
606
607void network::createTcpListeners(std::list<TcpListener> *listeners,
608 const char *addr,
609 int port)
610{
611 std::list<TcpListener> new_listeners;
612
Tim Waugh6ae42df2014-11-17 17:07:07 +0000613 struct addrinfo *ai, *current, hints;
Tim Waugh892d10a2015-03-11 13:12:07 +0000614 char service[16];
Pierre Ossmanf7d15002015-03-17 17:10:56 +0100615 int result;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000616
Pierre Ossman9d784402015-03-17 17:10:10 +0100617 initSockets();
618
Tim Waugh6ae42df2014-11-17 17:07:07 +0000619 memset(&hints, 0, sizeof(struct addrinfo));
Tim Waugh892d10a2015-03-11 13:12:07 +0000620 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000621 hints.ai_family = AF_UNSPEC;
622 hints.ai_socktype = SOCK_STREAM;
623 hints.ai_canonname = NULL;
624 hints.ai_addr = NULL;
625 hints.ai_next = NULL;
626
Tim Waugh892d10a2015-03-11 13:12:07 +0000627 snprintf (service, sizeof (service) - 1, "%d", port);
628 service[sizeof (service) - 1] = '\0';
Pierre Ossmanf7d15002015-03-17 17:10:56 +0100629 if ((result = getaddrinfo(addr, service, &hints, &ai)) != 0)
630 throw rdr::Exception("unable to resolve listening address: %s",
631 gai_strerror(result));
Tim Waugh6ae42df2014-11-17 17:07:07 +0000632
Tim Waugh892d10a2015-03-11 13:12:07 +0000633 for (current = ai; current != NULL; current = current->ai_next) {
634 switch (current->ai_family) {
635 case AF_INET:
636 if (!UseIPv4)
637 continue;
638 break;
639
640 case AF_INET6:
641 if (!UseIPv6)
642 continue;
643 break;
644
645 default:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000646 continue;
Tim Waugh892d10a2015-03-11 13:12:07 +0000647 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000648
Tim Waugh892d10a2015-03-11 13:12:07 +0000649 try {
650 new_listeners.push_back(TcpListener (current->ai_addr,
651 current->ai_addrlen));
652 } catch (SocketException& e) {
653 // Ignore this if it is due to lack of address family support on
654 // the interface or on the system
655 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) {
656 // Otherwise, report the error
657 freeaddrinfo(ai);
658 throw;
659 }
660 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000661 }
662 freeaddrinfo(ai);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000663
Tim Waugh892d10a2015-03-11 13:12:07 +0000664 if (new_listeners.empty ())
665 throw SocketException("createTcpListeners: no addresses available",
666 EADDRNOTAVAIL);
667
668 listeners->splice (listeners->end(), new_listeners);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000669}
670
671
672TcpFilter::TcpFilter(const char* spec) {
673 rfb::CharArray tmp;
Adam Tkacd36b6262009-09-04 10:57:20 +0000674 tmp.buf = rfb::strDup(spec);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000675 while (tmp.buf) {
676 rfb::CharArray first;
677 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
678 if (strlen(first.buf))
679 filter.push_back(parsePattern(first.buf));
680 }
681}
682
683TcpFilter::~TcpFilter() {
684}
685
686
687static bool
Tim Waughc24a64d2015-03-13 16:07:29 +0000688patternMatchIP(const TcpFilter::Pattern& pattern, vnc_sockaddr_t *sa) {
689 switch (pattern.address.u.sa.sa_family) {
690 unsigned long address;
691
692 case AF_INET:
693 if (sa->u.sa.sa_family != AF_INET)
694 return false;
695
696 address = sa->u.sin.sin_addr.s_addr;
697 if (address == htonl (INADDR_NONE)) return false;
698 return ((pattern.address.u.sin.sin_addr.s_addr &
699 pattern.mask.u.sin.sin_addr.s_addr) ==
700 (address & pattern.mask.u.sin.sin_addr.s_addr));
701
702 case AF_INET6:
703 if (sa->u.sa.sa_family != AF_INET6)
704 return false;
705
706 for (unsigned int n = 0; n < 16; n++) {
707 unsigned int bits = (n + 1) * 8;
708 unsigned int mask;
709 if (pattern.prefixlen > bits)
710 mask = 0xff;
711 else {
712 unsigned int lastbits = 0xff;
713 lastbits <<= bits - pattern.prefixlen;
714 mask = lastbits & 0xff;
715 }
716
717 if ((pattern.address.u.sin6.sin6_addr.s6_addr[n] & mask) !=
718 (sa->u.sin6.sin6_addr.s6_addr[n] & mask))
719 return false;
720
721 if (mask < 0xff)
722 break;
723 }
724
725 return true;
726
727 case AF_UNSPEC:
728 // Any address matches
729 return true;
730
731 default:
732 break;
733 }
734
735 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000736}
737
738bool
739TcpFilter::verifyConnection(Socket* s) {
740 rfb::CharArray name;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000741 vnc_sockaddr_t sa;
742 socklen_t sa_size = sizeof(sa);
Tim Waughc24a64d2015-03-13 16:07:29 +0000743
744 if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0)
Tim Waugh6ae42df2014-11-17 17:07:07 +0000745 return false;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000746
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000747 name.buf = s->getPeerAddress();
748 std::list<TcpFilter::Pattern>::iterator i;
749 for (i=filter.begin(); i!=filter.end(); i++) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000750 if (patternMatchIP(*i, &sa)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000751 switch ((*i).action) {
752 case Accept:
753 vlog.debug("ACCEPT %s", name.buf);
754 return true;
755 case Query:
756 vlog.debug("QUERY %s", name.buf);
757 s->setRequiresQuery();
758 return true;
759 case Reject:
760 vlog.debug("REJECT %s", name.buf);
761 return false;
762 }
763 }
764 }
765
766 vlog.debug("[REJECT] %s", name.buf);
767 return false;
768}
769
770
771TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
772 TcpFilter::Pattern pattern;
773
Tim Waughc24a64d2015-03-13 16:07:29 +0000774 rfb::CharArray addr, pref;
775 bool prefix_specified;
776 int family;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000777
Tim Waughc24a64d2015-03-13 16:07:29 +0000778 prefix_specified = rfb::strSplit(&p[1], '/', &addr.buf, &pref.buf);
779 if (addr.buf[0] == '\0') {
780 // Match any address
781 memset (&pattern.address, 0, sizeof (pattern.address));
782 pattern.address.u.sa.sa_family = AF_UNSPEC;
783 pattern.prefixlen = 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000784 } else {
Tim Waughc24a64d2015-03-13 16:07:29 +0000785 struct addrinfo hints;
786 struct addrinfo *ai;
787 char *p = addr.buf;
788 int result;
789 memset (&hints, 0, sizeof (hints));
790 hints.ai_family = AF_UNSPEC;
791 hints.ai_flags = AI_NUMERICHOST;
792
793 // Take out brackets, if present
794 if (*p == '[') {
795 size_t len;
796 p++;
797 len = strlen (p);
798 if (len > 0 && p[len - 1] == ']')
799 p[len - 1] = '\0';
800 }
801
802 if ((result = getaddrinfo (p, NULL, &hints, &ai)) != 0) {
803 throw Exception("unable to resolve host by name: %s",
804 gai_strerror(result));
805 }
806
807 memcpy (&pattern.address.u.sa, ai->ai_addr, ai->ai_addrlen);
808 freeaddrinfo (ai);
Tim Waughc24a64d2015-03-13 16:07:29 +0000809
810 family = pattern.address.u.sa.sa_family;
811
812 if (prefix_specified) {
813 if (family == AF_INET &&
814 rfb::strContains(pref.buf, '.')) {
815 throw Exception("mask no longer supported for filter, "
816 "use prefix instead");
817 }
818
819 pattern.prefixlen = (unsigned int) atoi(pref.buf);
820 } else {
821 switch (family) {
822 case AF_INET:
823 pattern.prefixlen = 32;
824 break;
825 case AF_INET6:
826 pattern.prefixlen = 128;
827 break;
828 default:
829 throw Exception("unknown address family");
830 }
831 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000832 }
833
Pierre Ossmanfdc55e52015-03-17 12:56:31 +0100834 family = pattern.address.u.sa.sa_family;
835
Tim Waughc24a64d2015-03-13 16:07:29 +0000836 if (pattern.prefixlen > (family == AF_INET ? 32: 128))
837 throw Exception("invalid prefix length for filter address: %u",
838 pattern.prefixlen);
839
840 // Compute mask from address and prefix length
841 memset (&pattern.mask, 0, sizeof (pattern.mask));
842 switch (family) {
843 unsigned long mask;
844 case AF_INET:
845 mask = 0;
846 for (unsigned int i=0; i<pattern.prefixlen; i++)
847 mask |= 1<<(31-i);
848 pattern.mask.u.sin.sin_addr.s_addr = htonl(mask);
849 break;
850
851 case AF_INET6:
852 for (unsigned int n = 0; n < 16; n++) {
853 unsigned int bits = (n + 1) * 8;
854 if (pattern.prefixlen > bits)
855 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = 0xff;
856 else {
857 unsigned int lastbits = 0xff;
858 lastbits <<= bits - pattern.prefixlen;
859 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = lastbits & 0xff;
860 break;
861 }
862 }
863 break;
864 case AF_UNSPEC:
865 // No mask to compute
866 break;
867 default:
868 ; /* not reached */
869 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000870
871 switch(p[0]) {
872 case '+': pattern.action = TcpFilter::Accept; break;
873 case '-': pattern.action = TcpFilter::Reject; break;
874 case '?': pattern.action = TcpFilter::Query; break;
875 };
876
877 return pattern;
878}
879
880char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000881 rfb::CharArray addr;
Tim Waughc24a64d2015-03-13 16:07:29 +0000882 char buffer[INET6_ADDRSTRLEN + 2];
883
884 if (p.address.u.sa.sa_family == AF_INET) {
885 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin),
886 buffer, sizeof (buffer), NULL, 0, NI_NUMERICHOST);
887 addr.buf = rfb::strDup(buffer);
888 } else if (p.address.u.sa.sa_family == AF_INET6) {
889 buffer[0] = '[';
890 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin6),
891 buffer + 1, sizeof (buffer) - 2, NULL, 0, NI_NUMERICHOST);
892 strcat(buffer, "]");
893 addr.buf = rfb::strDup(buffer);
894 } else if (p.address.u.sa.sa_family == AF_UNSPEC)
895 addr.buf = rfb::strDup("");
Tim Waughc24a64d2015-03-13 16:07:29 +0000896
897 char action;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000898 switch (p.action) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000899 case Accept: action = '+'; break;
900 case Reject: action = '-'; break;
901 default:
902 case Query: action = '?'; break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000903 };
Tim Waughc24a64d2015-03-13 16:07:29 +0000904 size_t resultlen = (1 // action
905 + strlen (addr.buf) // address
906 + 1 // slash
907 + 3 // prefix length, max 128
908 + 1); // terminating nul
909 char* result = new char[resultlen];
910 if (addr.buf[0] == '\0')
911 snprintf(result, resultlen, "%c", action);
912 else
913 snprintf(result, resultlen, "%c%s/%u", action, addr.buf, p.prefixlen);
914
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000915 return result;
916}