blob: 26ea1f5ea22fe9bf193addfea3d7ab6cd160b25d [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 Ossman8b6aa202012-12-13 13:55:22 +000060#if defined(HAVE_GETADDRINFO) && !defined(IN6_ARE_ADDR_EQUAL)
61#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);
76#ifdef HAVE_GETADDRINFO
77static rfb::BoolParameter UseIPv6("UseIPv6", "Use IPv6 for incoming and outgoing connections.", true);
78#endif
79
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000080/* Tunnelling support. */
81int network::findFreeTcpPort (void)
82{
DRCb2618e52011-02-21 13:43:44 +000083 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000084 struct sockaddr_in addr;
85 memset(&addr, 0, sizeof(addr));
86 addr.sin_family = AF_INET;
87 addr.sin_addr.s_addr = INADDR_ANY;
88
89 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
90 throw SocketException ("unable to create socket", errorNumber);
91
DRCb2618e52011-02-21 13:43:44 +000092 addr.sin_port = 0;
93 if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) < 0)
94 throw SocketException ("unable to find free port", errorNumber);
95
96 socklen_t n = sizeof(addr);
97 if (getsockname (sock, (struct sockaddr *)&addr, &n) < 0)
98 throw SocketException ("unable to get port number", errorNumber);
99
100 closesocket (sock);
101 return ntohs(addr.sin_port);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000102}
103
104
105// -=- Socket initialisation
106static bool socketsInitialised = false;
107static void initSockets() {
108 if (socketsInitialised)
109 return;
110#ifdef WIN32
111 WORD requiredVersion = MAKEWORD(2,0);
112 WSADATA initResult;
113
114 if (WSAStartup(requiredVersion, &initResult) != 0)
115 throw SocketException("unable to initialise Winsock2", errorNumber);
116#else
117 signal(SIGPIPE, SIG_IGN);
118#endif
119 socketsInitialised = true;
120}
121
122
123// -=- TcpSocket
124
125TcpSocket::TcpSocket(int sock, bool close)
126 : Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close)
127{
128}
129
130TcpSocket::TcpSocket(const char *host, int port)
131 : closeFd(true)
132{
Adam Tkac9cb6a422008-11-14 15:56:15 +0000133 int sock, err, result, family;
134 vnc_sockaddr_t sa;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000135 socklen_t salen;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000136#ifdef HAVE_GETADDRINFO
137 struct addrinfo *ai, *current, hints;
138#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000139
140 // - Create a socket
141 initSockets();
Adam Tkac9cb6a422008-11-14 15:56:15 +0000142
143#ifdef HAVE_GETADDRINFO
144 memset(&hints, 0, sizeof(struct addrinfo));
145 hints.ai_family = AF_UNSPEC;
146 hints.ai_socktype = SOCK_STREAM;
147 hints.ai_canonname = NULL;
148 hints.ai_addr = NULL;
149 hints.ai_next = NULL;
150
151 if ((result = getaddrinfo(host, NULL, &hints, &ai)) != 0) {
152 throw Exception("unable to resolve host by name: %s",
Tim Waugha85363d2015-03-11 13:07:48 +0000153 gai_strerror(result));
Adam Tkac9cb6a422008-11-14 15:56:15 +0000154 }
155
Pierre Ossmanf1a35012015-03-03 16:02:42 +0100156 // This logic is too complex for the compiler to determine if
157 // sock is properly assigned or not.
158 sock = -1;
159
Adam Tkac9cb6a422008-11-14 15:56:15 +0000160 for (current = ai; current != NULL; current = current->ai_next) {
161 family = current->ai_family;
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100162
163 switch (family) {
164 case AF_INET:
165 if (!UseIPv4)
166 continue;
167 break;
168 case AF_INET6:
169 if (!UseIPv6)
170 continue;
171 break;
172 default:
Adam Tkac9cb6a422008-11-14 15:56:15 +0000173 continue;
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100174 }
Adam Tkac9cb6a422008-11-14 15:56:15 +0000175
176 salen = current->ai_addrlen;
177 memcpy(&sa, current->ai_addr, salen);
178
179 if (family == AF_INET)
180 sa.u.sin.sin_port = htons(port);
181 else
182 sa.u.sin6.sin6_port = htons(port);
183
184#else /* HAVE_GETADDRINFO */
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100185 if (!UseIPv4)
186 throw Exception("Only IPv4 available but it is disabled");
187
Adam Tkac9cb6a422008-11-14 15:56:15 +0000188 family = AF_INET;
189 salen = sizeof(struct sockaddr_in);
190
191 /* Try processing the host as an IP address */
192 memset(&sa, 0, sizeof(sa));
193 sa.u.sin.sin_family = AF_INET;
194 sa.u.sin.sin_addr.s_addr = inet_addr((char *)host);
195 sa.u.sin.sin_port = htons(port);
196 if ((int)sa.u.sin.sin_addr.s_addr == -1) {
197 /* Host was not an IP address - try resolving as DNS name */
198 struct hostent *hostinfo;
199 hostinfo = gethostbyname((char *)host);
200 if (hostinfo && hostinfo->h_addr) {
201 sa.u.sin.sin_addr.s_addr = ((struct in_addr *)hostinfo->h_addr)->s_addr;
202 } else {
203 err = errorNumber;
204 throw SocketException("unable to resolve host by name", err);
205 }
206 }
207#endif /* HAVE_GETADDRINFO */
208
209 sock = socket (family, SOCK_STREAM, 0);
210 if (sock == -1) {
211 err = errorNumber;
212#ifdef HAVE_GETADDRINFO
213 freeaddrinfo(ai);
214#endif /* HAVE_GETADDRINFO */
215 throw SocketException("unable to create socket", err);
216 }
217
218 /* Attempt to connect to the remote host */
Adam Tkacc9cda3b2009-10-30 11:13:34 +0000219 while ((result = connect(sock, &sa.u.sa, salen)) == -1) {
Adam Tkac9cb6a422008-11-14 15:56:15 +0000220 err = errorNumber;
221#ifndef WIN32
222 if (err == EINTR)
Tim Waugha85363d2015-03-11 13:07:48 +0000223 continue;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000224#endif
225 closesocket(sock);
226 break;
227 }
228
229#ifdef HAVE_GETADDRINFO
230 if (result == 0)
231 break;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000232 }
233
234 freeaddrinfo(ai);
Pierre Ossmanda9a38d2015-03-03 16:03:32 +0100235
236 if (current == NULL)
237 throw Exception("No useful address for host");
Adam Tkac9cb6a422008-11-14 15:56:15 +0000238#endif /* HAVE_GETADDRINFO */
239
240 if (result == -1)
241 throw SocketException("unable connect to socket", err);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000242
243#ifndef WIN32
244 // - By default, close the socket on exec()
245 fcntl(sock, F_SETFD, FD_CLOEXEC);
246#endif
247
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000248 // Disable Nagle's algorithm, to reduce latency
249 enableNagles(sock, false);
250
251 // Create the input and output streams
252 instream = new FdInStream(sock);
253 outstream = new FdOutStream(sock);
254 ownStreams = true;
255}
256
257TcpSocket::~TcpSocket() {
258 if (closeFd)
259 closesocket(getFd());
260}
261
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000262int TcpSocket::getMyPort() {
263 return getSockPort(getFd());
264}
265
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000266char* TcpSocket::getPeerAddress() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000267 vnc_sockaddr_t sa;
268 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000269
Tim Waugh6ae42df2014-11-17 17:07:07 +0000270 if (getpeername(getFd(), &sa.u.sa, &sa_size) != 0) {
271 vlog.error("unable to get peer name for socket");
272 return rfb::strDup("");
273 }
274
Tim Waugh892d10a2015-03-11 13:12:07 +0000275#if defined(HAVE_GETADDRINFO)
Pierre Ossman14263e12014-11-19 11:14:49 +0100276 if (sa.u.sa.sa_family == AF_INET6) {
Pierre Ossman07cd2292014-11-19 11:16:04 +0100277 char buffer[INET6_ADDRSTRLEN + 2];
Tim Waugh892d10a2015-03-11 13:12:07 +0000278 int ret;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000279
Pierre Ossman07cd2292014-11-19 11:16:04 +0100280 buffer[0] = '[';
281
Tim Waugh892d10a2015-03-11 13:12:07 +0000282 ret = getnameinfo(&sa.u.sa, sizeof(sa.u.sin6),
283 buffer + 1, sizeof(buffer) - 2, NULL, 0,
284 NI_NUMERICHOST);
285 if (ret != 0) {
Pierre Ossman14263e12014-11-19 11:14:49 +0100286 vlog.error("unable to convert peer name to a string");
287 return rfb::strDup("");
288 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000289
Pierre Ossman07cd2292014-11-19 11:16:04 +0100290 strcat(buffer, "]");
291
292 return rfb::strDup(buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000293 }
Pierre Ossman14263e12014-11-19 11:14:49 +0100294#endif
295
296 if (sa.u.sa.sa_family == AF_INET) {
297 char *name;
298
299 name = inet_ntoa(sa.u.sin.sin_addr);
300 if (name == NULL) {
301 vlog.error("unable to convert peer name to a string");
302 return rfb::strDup("");
303 }
304
305 return rfb::strDup(name);
306 }
307
308 vlog.error("unknown address family for socket");
309 return rfb::strDup("");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000310}
311
312int TcpSocket::getPeerPort() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000313 vnc_sockaddr_t sa;
314 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000315
Tim Waugh6ae42df2014-11-17 17:07:07 +0000316 getpeername(getFd(), &sa.u.sa, &sa_size);
317
318 switch (sa.u.sa.sa_family) {
319#ifdef HAVE_GETADDRINFO
320 case AF_INET6:
321 return ntohs(sa.u.sin6.sin6_port);
322#endif /* HAVE_GETADDRINFO */
Pierre Ossman14263e12014-11-19 11:14:49 +0100323 case AF_INET:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000324 return ntohs(sa.u.sin.sin_port);
Pierre Ossman14263e12014-11-19 11:14:49 +0100325 default:
326 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000327 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000328}
329
330char* TcpSocket::getPeerEndpoint() {
331 rfb::CharArray address; address.buf = getPeerAddress();
332 int port = getPeerPort();
333
334 int buflen = strlen(address.buf) + 32;
335 char* buffer = new char[buflen];
336 sprintf(buffer, "%s::%d", address.buf, port);
337 return buffer;
338}
339
340bool TcpSocket::sameMachine() {
Adam Tkac897814f2009-11-12 10:32:43 +0000341 vnc_sockaddr_t peeraddr, myaddr;
342 socklen_t addrlen;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000343
Adam Tkac897814f2009-11-12 10:32:43 +0000344 addrlen = sizeof(peeraddr);
345 if (getpeername(getFd(), &peeraddr.u.sa, &addrlen) < 0)
346 throw SocketException ("unable to get peer address", errorNumber);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000347
Adam Tkac897814f2009-11-12 10:32:43 +0000348 addrlen = sizeof(myaddr); /* need to reset, since getpeername overwrote */
349 if (getsockname(getFd(), &myaddr.u.sa, &addrlen) < 0)
350 throw SocketException ("unable to get my address", errorNumber);
351
352 if (peeraddr.u.sa.sa_family != myaddr.u.sa.sa_family)
353 return false;
354
355#ifdef HAVE_GETADDRINFO
356 if (peeraddr.u.sa.sa_family == AF_INET6)
357 return IN6_ARE_ADDR_EQUAL(&peeraddr.u.sin6.sin6_addr,
Tim Waugha85363d2015-03-11 13:07:48 +0000358 &myaddr.u.sin6.sin6_addr);
Adam Tkac897814f2009-11-12 10:32:43 +0000359#endif
360
Pierre Ossman14263e12014-11-19 11:14:49 +0100361 if (peeraddr.u.sa.sa_family == AF_INET)
362 return (peeraddr.u.sin.sin_addr.s_addr == myaddr.u.sin.sin_addr.s_addr);
363
364 // No idea what this is. Assume we're on different machines.
365 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000366}
367
368void TcpSocket::shutdown()
369{
370 Socket::shutdown();
371 ::shutdown(getFd(), 2);
372}
373
374bool TcpSocket::enableNagles(int sock, bool enable) {
375 int one = enable ? 0 : 1;
376 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
Tim Waugha85363d2015-03-11 13:07:48 +0000377 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000378 int e = errorNumber;
379 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
380 return false;
381 }
382 return true;
383}
384
Pierre Ossman64069a92011-11-08 12:10:55 +0000385bool TcpSocket::cork(int sock, bool enable) {
386#ifndef TCP_CORK
387 return false;
388#else
389 int one = enable ? 1 : 0;
390 if (setsockopt(sock, IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0)
391 return false;
392 return true;
393#endif
394}
395
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000396bool TcpSocket::isSocket(int sock)
397{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000398 vnc_sockaddr_t sa;
399 socklen_t sa_size = sizeof(sa);
400 return getsockname(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000401}
402
403bool TcpSocket::isConnected(int sock)
404{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000405 vnc_sockaddr_t sa;
406 socklen_t sa_size = sizeof(sa);
407 return getpeername(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000408}
409
410int TcpSocket::getSockPort(int sock)
411{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000412 vnc_sockaddr_t sa;
413 socklen_t sa_size = sizeof(sa);
414 if (getsockname(sock, &sa.u.sa, &sa_size) < 0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000415 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000416
417 switch (sa.u.sa.sa_family) {
418#ifdef HAVE_GETADDRINFO
419 case AF_INET6:
420 return ntohs(sa.u.sin6.sin6_port);
421#endif /* HAVE_GETADDRINFO */
422
423 default:
424 return ntohs(sa.u.sin.sin_port);
425 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000426}
427
Tim Waugh892d10a2015-03-11 13:12:07 +0000428TcpListener::TcpListener(int sock)
Tim Waughe4d97262014-11-21 16:07:34 +0000429{
Tim Waugh892d10a2015-03-11 13:12:07 +0000430 fd = sock;
Tim Waughe4d97262014-11-21 16:07:34 +0000431}
432
Tim Waugh892d10a2015-03-11 13:12:07 +0000433TcpListener::TcpListener(const TcpListener& other)
Tim Waughe4d97262014-11-21 16:07:34 +0000434{
Tim Waugh892d10a2015-03-11 13:12:07 +0000435 fd = dup (other.fd);
436 // Hope TcpListener::shutdown(other) doesn't get called...
Tim Waughe4d97262014-11-21 16:07:34 +0000437}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000438
Tim Waugh892d10a2015-03-11 13:12:07 +0000439TcpListener& TcpListener::operator= (const TcpListener& other)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000440{
Tim Waugh892d10a2015-03-11 13:12:07 +0000441 if (this != &other)
442 {
443 closesocket (fd);
444 fd = dup (other.fd);
445 // Hope TcpListener::shutdown(other) doesn't get called...
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000446 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000447 return *this;
448}
449
450TcpListener::TcpListener(const struct sockaddr *listenaddr,
451 socklen_t listenaddrlen)
452{
453 int one = 1;
454 vnc_sockaddr_t sa;
455 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000456
457 initSockets();
Tim Waugh892d10a2015-03-11 13:12:07 +0000458
459 if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0)
460 throw SocketException("unable to create listening socket", errorNumber);
461
462 memcpy (&sa, listenaddr, listenaddrlen);
463#ifdef IPV6_V6ONLY
464 if (listenaddr->sa_family == AF_INET6) {
465 if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one)))
466 throw SocketException("unable to set IPV6_V6ONLY", errorNumber);
467 }
468#endif /* defined(IPV6_V6ONLY) */
469
470 if (bind(sock, &sa.u.sa, listenaddrlen) == -1) {
471 closesocket(sock);
472 throw SocketException("failed to bind socket", errorNumber);
473 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000474
475#ifndef WIN32
476 // - By default, close the socket on exec()
Tim Waugh892d10a2015-03-11 13:12:07 +0000477 fcntl(sock, F_SETFD, FD_CLOEXEC);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000478
Tim Waugh892d10a2015-03-11 13:12:07 +0000479 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
480 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000481 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000482 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000483 throw SocketException("unable to create listening socket", e);
484 }
485#endif
486
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000487 // - Set it to be a listening socket
Tim Waugh892d10a2015-03-11 13:12:07 +0000488 if (listen(sock, 5) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000489 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000490 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000491 throw SocketException("unable to set socket to listening mode", e);
492 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000493
494 fd = sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000495}
496
497TcpListener::~TcpListener() {
Tim Waugh892d10a2015-03-11 13:12:07 +0000498 closesocket(fd);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000499}
500
501void TcpListener::shutdown()
502{
503#ifdef WIN32
504 closesocket(getFd());
505#else
506 ::shutdown(getFd(), 2);
507#endif
508}
509
510
511Socket*
512TcpListener::accept() {
513 int new_sock = -1;
514
515 // Accept an incoming connection
516 if ((new_sock = ::accept(fd, 0, 0)) < 0)
517 throw SocketException("unable to accept new connection", errorNumber);
518
519#ifndef WIN32
520 // - By default, close the socket on exec()
521 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
522#endif
523
524 // Disable Nagle's algorithm, to reduce latency
525 TcpSocket::enableNagles(new_sock, false);
526
527 // Create the socket object & check connection is allowed
528 TcpSocket* s = new TcpSocket(new_sock);
529 if (filter && !filter->verifyConnection(s)) {
530 delete s;
531 return 0;
532 }
533 return s;
534}
535
Tim Waugh892d10a2015-03-11 13:12:07 +0000536int TcpListener::getMyPort() {
537 return TcpSocket::getSockPort(getFd());
538}
539
540
541void network::createLocalTcpListeners(std::list<TcpListener> *listeners,
542 int port)
543{
544 std::list<TcpListener> new_listeners;
545 vnc_sockaddr_t sa;
Pierre Ossman9d784402015-03-17 17:10:10 +0100546
547 initSockets();
548
Tim Waugh892d10a2015-03-11 13:12:07 +0000549#ifdef HAVE_GETADDRINFO
550 if (UseIPv6) {
551 sa.u.sin6.sin6_family = AF_INET6;
552 sa.u.sin6.sin6_port = htons (port);
553 sa.u.sin6.sin6_addr = in6addr_loopback;
554 try {
555 new_listeners.push_back (TcpListener (&sa.u.sa, sizeof (sa.u.sin6)));
556 } catch (SocketException& e) {
557 // Ignore this if it is due to lack of address family support on
558 // the interface or on the system
559 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT)
560 // Otherwise, report the error
561 throw;
562 }
563 }
564#endif /* HAVE_GETADDRINFO */
565 if (UseIPv4) {
566 sa.u.sin.sin_family = AF_INET;
567 sa.u.sin.sin_port = htons (port);
568 sa.u.sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
569 try {
570 new_listeners.push_back (TcpListener (&sa.u.sa, sizeof (sa.u.sin)));
571 } catch (SocketException& e) {
572 // Ignore this if it is due to lack of address family support on
573 // the interface or on the system
574 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT)
575 // Otherwise, report the error
576 throw;
577 }
578 }
579
580 if (new_listeners.empty ())
581 throw SocketException("createLocalTcpListeners: no addresses available",
582 EADDRNOTAVAIL);
583
584 listeners->splice (listeners->end(), new_listeners);
585}
586
587void network::createTcpListeners(std::list<TcpListener> *listeners,
588 const char *addr,
589 int port)
590{
591 std::list<TcpListener> new_listeners;
592
593#ifdef HAVE_GETADDRINFO
Tim Waugh6ae42df2014-11-17 17:07:07 +0000594 struct addrinfo *ai, *current, hints;
Tim Waugh892d10a2015-03-11 13:12:07 +0000595 char service[16];
Tim Waugh6ae42df2014-11-17 17:07:07 +0000596
Pierre Ossman9d784402015-03-17 17:10:10 +0100597 initSockets();
598
Tim Waugh6ae42df2014-11-17 17:07:07 +0000599 memset(&hints, 0, sizeof(struct addrinfo));
Tim Waugh892d10a2015-03-11 13:12:07 +0000600 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000601 hints.ai_family = AF_UNSPEC;
602 hints.ai_socktype = SOCK_STREAM;
603 hints.ai_canonname = NULL;
604 hints.ai_addr = NULL;
605 hints.ai_next = NULL;
606
Tim Waugh892d10a2015-03-11 13:12:07 +0000607 snprintf (service, sizeof (service) - 1, "%d", port);
608 service[sizeof (service) - 1] = '\0';
609 if ((getaddrinfo(addr, service, &hints, &ai)) != 0)
610 throw rdr::SystemException("getaddrinfo", errorNumber);
Tim Waugh6ae42df2014-11-17 17:07:07 +0000611
Tim Waugh892d10a2015-03-11 13:12:07 +0000612 for (current = ai; current != NULL; current = current->ai_next) {
613 switch (current->ai_family) {
614 case AF_INET:
615 if (!UseIPv4)
616 continue;
617 break;
618
619 case AF_INET6:
620 if (!UseIPv6)
621 continue;
622 break;
623
624 default:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000625 continue;
Tim Waugh892d10a2015-03-11 13:12:07 +0000626 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000627
Tim Waugh892d10a2015-03-11 13:12:07 +0000628 try {
629 new_listeners.push_back(TcpListener (current->ai_addr,
630 current->ai_addrlen));
631 } catch (SocketException& e) {
632 // Ignore this if it is due to lack of address family support on
633 // the interface or on the system
634 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) {
635 // Otherwise, report the error
636 freeaddrinfo(ai);
637 throw;
638 }
639 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000640 }
641 freeaddrinfo(ai);
642#else
Tim Waugh892d10a2015-03-11 13:12:07 +0000643 const hostent* addrs;
644 if (addr) {
645 /* Bind to specific address */
646 addrs = gethostbyname(addr);
647 if (addrs == 0)
648 throw rdr::SystemException("gethostbyname", errorNumber);
649 if (addrs->h_addrtype != AF_INET)
650 throw rdr::Exception("createTcpListeners: bad family");
651 for (int i=0; addrs->h_addr_list[i] != 0; i++) {
652 struct sockaddr_in addr;
653 addr.sin_family = AF_INET;
654 memcpy (&addr.sin_addr, addrs->h_addr_list[i], addrs->h_length);
655 addr.sin_port = htons(port);
656 try {
657 new_listeners.push_back(TcpListener ((struct sockaddr*)&addr,
658 addrs->h_length));
659 } catch (SocketException& e) {
660 // Ignore this if it is due to lack of address family support
661 // on the interface or on the system
662 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) {
663 // Otherwise, report the error
664 freeaddrinfo(ai);
665 throw;
666 }
667 }
668 }
669 } else {
670 /* Bind to any address */
671 struct sockaddr_in addr;
672 addr.sin_family = AF_INET;
673 addr.sin_addr.s_addr = htonl(INADDR_ANY);
674 addr.sin_port = htons(port);
675 try {
676 new_listeners.push_back(TcpListener ((struct sockaddr*)&addr,
677 sizeof (struct sockaddr_in)));
678 } catch (SocketException& e) {
679 // Ignore this if it is due to lack of address family support on
680 // the interface or on the system
681 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) {
682 // Otherwise, report the error
683 freeaddrinfo(ai);
684 throw;
685 }
686 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000687 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000688#endif /* defined(HAVE_GETADDRINFO) */
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000689
Tim Waugh892d10a2015-03-11 13:12:07 +0000690 if (new_listeners.empty ())
691 throw SocketException("createTcpListeners: no addresses available",
692 EADDRNOTAVAIL);
693
694 listeners->splice (listeners->end(), new_listeners);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000695}
696
697
698TcpFilter::TcpFilter(const char* spec) {
699 rfb::CharArray tmp;
Adam Tkacd36b6262009-09-04 10:57:20 +0000700 tmp.buf = rfb::strDup(spec);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000701 while (tmp.buf) {
702 rfb::CharArray first;
703 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
704 if (strlen(first.buf))
705 filter.push_back(parsePattern(first.buf));
706 }
707}
708
709TcpFilter::~TcpFilter() {
710}
711
712
713static bool
Tim Waughc24a64d2015-03-13 16:07:29 +0000714patternMatchIP(const TcpFilter::Pattern& pattern, vnc_sockaddr_t *sa) {
715 switch (pattern.address.u.sa.sa_family) {
716 unsigned long address;
717
718 case AF_INET:
719 if (sa->u.sa.sa_family != AF_INET)
720 return false;
721
722 address = sa->u.sin.sin_addr.s_addr;
723 if (address == htonl (INADDR_NONE)) return false;
724 return ((pattern.address.u.sin.sin_addr.s_addr &
725 pattern.mask.u.sin.sin_addr.s_addr) ==
726 (address & pattern.mask.u.sin.sin_addr.s_addr));
727
728 case AF_INET6:
729 if (sa->u.sa.sa_family != AF_INET6)
730 return false;
731
732 for (unsigned int n = 0; n < 16; n++) {
733 unsigned int bits = (n + 1) * 8;
734 unsigned int mask;
735 if (pattern.prefixlen > bits)
736 mask = 0xff;
737 else {
738 unsigned int lastbits = 0xff;
739 lastbits <<= bits - pattern.prefixlen;
740 mask = lastbits & 0xff;
741 }
742
743 if ((pattern.address.u.sin6.sin6_addr.s6_addr[n] & mask) !=
744 (sa->u.sin6.sin6_addr.s6_addr[n] & mask))
745 return false;
746
747 if (mask < 0xff)
748 break;
749 }
750
751 return true;
752
753 case AF_UNSPEC:
754 // Any address matches
755 return true;
756
757 default:
758 break;
759 }
760
761 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000762}
763
764bool
765TcpFilter::verifyConnection(Socket* s) {
766 rfb::CharArray name;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000767 vnc_sockaddr_t sa;
768 socklen_t sa_size = sizeof(sa);
Tim Waughc24a64d2015-03-13 16:07:29 +0000769
770 if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0)
Tim Waugh6ae42df2014-11-17 17:07:07 +0000771 return false;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000772
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000773 name.buf = s->getPeerAddress();
774 std::list<TcpFilter::Pattern>::iterator i;
775 for (i=filter.begin(); i!=filter.end(); i++) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000776 if (patternMatchIP(*i, &sa)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000777 switch ((*i).action) {
778 case Accept:
779 vlog.debug("ACCEPT %s", name.buf);
780 return true;
781 case Query:
782 vlog.debug("QUERY %s", name.buf);
783 s->setRequiresQuery();
784 return true;
785 case Reject:
786 vlog.debug("REJECT %s", name.buf);
787 return false;
788 }
789 }
790 }
791
792 vlog.debug("[REJECT] %s", name.buf);
793 return false;
794}
795
796
797TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
798 TcpFilter::Pattern pattern;
799
Tim Waughc24a64d2015-03-13 16:07:29 +0000800 rfb::CharArray addr, pref;
801 bool prefix_specified;
802 int family;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000803
Tim Waughc24a64d2015-03-13 16:07:29 +0000804 prefix_specified = rfb::strSplit(&p[1], '/', &addr.buf, &pref.buf);
805 if (addr.buf[0] == '\0') {
806 // Match any address
807 memset (&pattern.address, 0, sizeof (pattern.address));
808 pattern.address.u.sa.sa_family = AF_UNSPEC;
809 pattern.prefixlen = 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000810 } else {
Tim Waughc24a64d2015-03-13 16:07:29 +0000811#ifdef HAVE_GETADDRINFO
812 struct addrinfo hints;
813 struct addrinfo *ai;
814 char *p = addr.buf;
815 int result;
816 memset (&hints, 0, sizeof (hints));
817 hints.ai_family = AF_UNSPEC;
818 hints.ai_flags = AI_NUMERICHOST;
819
820 // Take out brackets, if present
821 if (*p == '[') {
822 size_t len;
823 p++;
824 len = strlen (p);
825 if (len > 0 && p[len - 1] == ']')
826 p[len - 1] = '\0';
827 }
828
829 if ((result = getaddrinfo (p, NULL, &hints, &ai)) != 0) {
830 throw Exception("unable to resolve host by name: %s",
831 gai_strerror(result));
832 }
833
834 memcpy (&pattern.address.u.sa, ai->ai_addr, ai->ai_addrlen);
835 freeaddrinfo (ai);
836#else
837 pattern.address.u.sa.sa_family = AF_INET;
838 pattern.address.u.sin.sin_addr.s_addr = inet_addr(addr.buf);
839#endif /* HAVE_GETADDRINFO */
840
841 family = pattern.address.u.sa.sa_family;
842
843 if (prefix_specified) {
844 if (family == AF_INET &&
845 rfb::strContains(pref.buf, '.')) {
846 throw Exception("mask no longer supported for filter, "
847 "use prefix instead");
848 }
849
850 pattern.prefixlen = (unsigned int) atoi(pref.buf);
851 } else {
852 switch (family) {
853 case AF_INET:
854 pattern.prefixlen = 32;
855 break;
856 case AF_INET6:
857 pattern.prefixlen = 128;
858 break;
859 default:
860 throw Exception("unknown address family");
861 }
862 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000863 }
864
Pierre Ossmanfdc55e52015-03-17 12:56:31 +0100865 family = pattern.address.u.sa.sa_family;
866
Tim Waughc24a64d2015-03-13 16:07:29 +0000867 if (pattern.prefixlen > (family == AF_INET ? 32: 128))
868 throw Exception("invalid prefix length for filter address: %u",
869 pattern.prefixlen);
870
871 // Compute mask from address and prefix length
872 memset (&pattern.mask, 0, sizeof (pattern.mask));
873 switch (family) {
874 unsigned long mask;
875 case AF_INET:
876 mask = 0;
877 for (unsigned int i=0; i<pattern.prefixlen; i++)
878 mask |= 1<<(31-i);
879 pattern.mask.u.sin.sin_addr.s_addr = htonl(mask);
880 break;
881
882 case AF_INET6:
883 for (unsigned int n = 0; n < 16; n++) {
884 unsigned int bits = (n + 1) * 8;
885 if (pattern.prefixlen > bits)
886 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = 0xff;
887 else {
888 unsigned int lastbits = 0xff;
889 lastbits <<= bits - pattern.prefixlen;
890 pattern.mask.u.sin6.sin6_addr.s6_addr[n] = lastbits & 0xff;
891 break;
892 }
893 }
894 break;
895 case AF_UNSPEC:
896 // No mask to compute
897 break;
898 default:
899 ; /* not reached */
900 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000901
902 switch(p[0]) {
903 case '+': pattern.action = TcpFilter::Accept; break;
904 case '-': pattern.action = TcpFilter::Reject; break;
905 case '?': pattern.action = TcpFilter::Query; break;
906 };
907
908 return pattern;
909}
910
911char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000912 rfb::CharArray addr;
913#ifdef HAVE_GETADDRINFO
914 char buffer[INET6_ADDRSTRLEN + 2];
915
916 if (p.address.u.sa.sa_family == AF_INET) {
917 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin),
918 buffer, sizeof (buffer), NULL, 0, NI_NUMERICHOST);
919 addr.buf = rfb::strDup(buffer);
920 } else if (p.address.u.sa.sa_family == AF_INET6) {
921 buffer[0] = '[';
922 getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin6),
923 buffer + 1, sizeof (buffer) - 2, NULL, 0, NI_NUMERICHOST);
924 strcat(buffer, "]");
925 addr.buf = rfb::strDup(buffer);
926 } else if (p.address.u.sa.sa_family == AF_UNSPEC)
927 addr.buf = rfb::strDup("");
928#else
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000929 in_addr tmp;
Tim Waughc24a64d2015-03-13 16:07:29 +0000930 tmp.s_addr = p.address.u.sin.sin_addr.s_addr;
Adam Tkacd36b6262009-09-04 10:57:20 +0000931 addr.buf = rfb::strDup(inet_ntoa(tmp));
Tim Waughc24a64d2015-03-13 16:07:29 +0000932#endif /* HAVE_GETADDRINFO */
933
934 char action;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000935 switch (p.action) {
Tim Waughc24a64d2015-03-13 16:07:29 +0000936 case Accept: action = '+'; break;
937 case Reject: action = '-'; break;
938 default:
939 case Query: action = '?'; break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000940 };
Tim Waughc24a64d2015-03-13 16:07:29 +0000941 size_t resultlen = (1 // action
942 + strlen (addr.buf) // address
943 + 1 // slash
944 + 3 // prefix length, max 128
945 + 1); // terminating nul
946 char* result = new char[resultlen];
947 if (addr.buf[0] == '\0')
948 snprintf(result, resultlen, "%c", action);
949 else
950 snprintf(result, resultlen, "%c%s/%u", action, addr.buf, p.prefixlen);
951
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000952 return result;
953}