blob: 5158e805dd333694c07079542eac83dddcfa9108 [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>
34#include <netinet/in.h>
35#include <netinet/tcp.h>
36#include <netdb.h>
37#include <unistd.h>
38#include <errno.h>
39#include <string.h>
40#include <signal.h>
41#include <fcntl.h>
42#endif
43
Adam Tkac04b7fd22008-03-19 16:14:48 +000044#include <stdlib.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000045#include <network/TcpSocket.h>
46#include <rfb/util.h>
47#include <rfb/LogWriter.h>
Pierre Ossman39b3b8f2015-01-29 17:35:45 +010048#include <rfb/Configuration.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000049
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000050#ifndef INADDR_NONE
51#define INADDR_NONE ((unsigned long)-1)
52#endif
53#ifndef INADDR_LOOPBACK
54#define INADDR_LOOPBACK ((unsigned long)0x7F000001)
55#endif
56
Pierre Ossman8b6aa202012-12-13 13:55:22 +000057#if defined(HAVE_GETADDRINFO) && !defined(IN6_ARE_ADDR_EQUAL)
58#define IN6_ARE_ADDR_EQUAL(a,b) \
59 (memcmp ((const void*)(a), (const void*)(b), sizeof (struct in6_addr)) == 0)
60#endif
61
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000062using namespace network;
63using namespace rdr;
64
Adam Tkac9cb6a422008-11-14 15:56:15 +000065typedef struct vnc_sockaddr {
66 union {
67 sockaddr sa;
68 sockaddr_in sin;
69#ifdef HAVE_GETADDRINFO
70 sockaddr_in6 sin6;
71#endif
72 } u;
73} vnc_sockaddr_t;
74
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000075static rfb::LogWriter vlog("TcpSocket");
76
Pierre Ossman39b3b8f2015-01-29 17:35:45 +010077static rfb::BoolParameter UseIPv4("UseIPv4", "Use IPv4 for incoming and outgoing connections.", true);
78#ifdef HAVE_GETADDRINFO
79static rfb::BoolParameter UseIPv6("UseIPv6", "Use IPv6 for incoming and outgoing connections.", true);
80#endif
81
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000082/* Tunnelling support. */
83int network::findFreeTcpPort (void)
84{
DRCb2618e52011-02-21 13:43:44 +000085 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000086 struct sockaddr_in addr;
87 memset(&addr, 0, sizeof(addr));
88 addr.sin_family = AF_INET;
89 addr.sin_addr.s_addr = INADDR_ANY;
90
91 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
92 throw SocketException ("unable to create socket", errorNumber);
93
DRCb2618e52011-02-21 13:43:44 +000094 addr.sin_port = 0;
95 if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) < 0)
96 throw SocketException ("unable to find free port", errorNumber);
97
98 socklen_t n = sizeof(addr);
99 if (getsockname (sock, (struct sockaddr *)&addr, &n) < 0)
100 throw SocketException ("unable to get port number", errorNumber);
101
102 closesocket (sock);
103 return ntohs(addr.sin_port);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000104}
105
106
107// -=- Socket initialisation
108static bool socketsInitialised = false;
109static void initSockets() {
110 if (socketsInitialised)
111 return;
112#ifdef WIN32
113 WORD requiredVersion = MAKEWORD(2,0);
114 WSADATA initResult;
115
116 if (WSAStartup(requiredVersion, &initResult) != 0)
117 throw SocketException("unable to initialise Winsock2", errorNumber);
118#else
119 signal(SIGPIPE, SIG_IGN);
120#endif
121 socketsInitialised = true;
122}
123
124
125// -=- TcpSocket
126
127TcpSocket::TcpSocket(int sock, bool close)
128 : Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close)
129{
130}
131
132TcpSocket::TcpSocket(const char *host, int port)
133 : closeFd(true)
134{
Adam Tkac9cb6a422008-11-14 15:56:15 +0000135 int sock, err, result, family;
136 vnc_sockaddr_t sa;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000137 socklen_t salen;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000138#ifdef HAVE_GETADDRINFO
139 struct addrinfo *ai, *current, hints;
140#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000141
142 // - Create a socket
143 initSockets();
Adam Tkac9cb6a422008-11-14 15:56:15 +0000144
145#ifdef HAVE_GETADDRINFO
146 memset(&hints, 0, sizeof(struct addrinfo));
147 hints.ai_family = AF_UNSPEC;
148 hints.ai_socktype = SOCK_STREAM;
149 hints.ai_canonname = NULL;
150 hints.ai_addr = NULL;
151 hints.ai_next = NULL;
152
153 if ((result = getaddrinfo(host, NULL, &hints, &ai)) != 0) {
154 throw Exception("unable to resolve host by name: %s",
155 gai_strerror(result));
156 }
157
Pierre Ossmanf1a35012015-03-03 16:02:42 +0100158 // This logic is too complex for the compiler to determine if
159 // sock is properly assigned or not.
160 sock = -1;
161
Adam Tkac9cb6a422008-11-14 15:56:15 +0000162 for (current = ai; current != NULL; current = current->ai_next) {
163 family = current->ai_family;
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100164
165 switch (family) {
166 case AF_INET:
167 if (!UseIPv4)
168 continue;
169 break;
170 case AF_INET6:
171 if (!UseIPv6)
172 continue;
173 break;
174 default:
Adam Tkac9cb6a422008-11-14 15:56:15 +0000175 continue;
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100176 }
Adam Tkac9cb6a422008-11-14 15:56:15 +0000177
178 salen = current->ai_addrlen;
179 memcpy(&sa, current->ai_addr, salen);
180
181 if (family == AF_INET)
182 sa.u.sin.sin_port = htons(port);
183 else
184 sa.u.sin6.sin6_port = htons(port);
185
186#else /* HAVE_GETADDRINFO */
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100187 if (!UseIPv4)
188 throw Exception("Only IPv4 available but it is disabled");
189
Adam Tkac9cb6a422008-11-14 15:56:15 +0000190 family = AF_INET;
191 salen = sizeof(struct sockaddr_in);
192
193 /* Try processing the host as an IP address */
194 memset(&sa, 0, sizeof(sa));
195 sa.u.sin.sin_family = AF_INET;
196 sa.u.sin.sin_addr.s_addr = inet_addr((char *)host);
197 sa.u.sin.sin_port = htons(port);
198 if ((int)sa.u.sin.sin_addr.s_addr == -1) {
199 /* Host was not an IP address - try resolving as DNS name */
200 struct hostent *hostinfo;
201 hostinfo = gethostbyname((char *)host);
202 if (hostinfo && hostinfo->h_addr) {
203 sa.u.sin.sin_addr.s_addr = ((struct in_addr *)hostinfo->h_addr)->s_addr;
204 } else {
205 err = errorNumber;
206 throw SocketException("unable to resolve host by name", err);
207 }
208 }
209#endif /* HAVE_GETADDRINFO */
210
211 sock = socket (family, SOCK_STREAM, 0);
212 if (sock == -1) {
213 err = errorNumber;
214#ifdef HAVE_GETADDRINFO
215 freeaddrinfo(ai);
216#endif /* HAVE_GETADDRINFO */
217 throw SocketException("unable to create socket", err);
218 }
219
220 /* Attempt to connect to the remote host */
Adam Tkacc9cda3b2009-10-30 11:13:34 +0000221 while ((result = connect(sock, &sa.u.sa, salen)) == -1) {
Adam Tkac9cb6a422008-11-14 15:56:15 +0000222 err = errorNumber;
223#ifndef WIN32
224 if (err == EINTR)
225 continue;
226#endif
227 closesocket(sock);
228 break;
229 }
230
231#ifdef HAVE_GETADDRINFO
232 if (result == 0)
233 break;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000234 }
235
236 freeaddrinfo(ai);
237#endif /* HAVE_GETADDRINFO */
238
239 if (result == -1)
240 throw SocketException("unable connect to socket", err);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000241
242#ifndef WIN32
243 // - By default, close the socket on exec()
244 fcntl(sock, F_SETFD, FD_CLOEXEC);
245#endif
246
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000247 // Disable Nagle's algorithm, to reduce latency
248 enableNagles(sock, false);
249
250 // Create the input and output streams
251 instream = new FdInStream(sock);
252 outstream = new FdOutStream(sock);
253 ownStreams = true;
254}
255
256TcpSocket::~TcpSocket() {
257 if (closeFd)
258 closesocket(getFd());
259}
260
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000261int TcpSocket::getMyPort() {
262 return getSockPort(getFd());
263}
264
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000265char* TcpSocket::getPeerAddress() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000266 vnc_sockaddr_t sa;
267 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000268
Tim Waugh6ae42df2014-11-17 17:07:07 +0000269 if (getpeername(getFd(), &sa.u.sa, &sa_size) != 0) {
270 vlog.error("unable to get peer name for socket");
271 return rfb::strDup("");
272 }
273
Pierre Ossman14263e12014-11-19 11:14:49 +0100274#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_PTON)
275 if (sa.u.sa.sa_family == AF_INET6) {
Pierre Ossman07cd2292014-11-19 11:16:04 +0100276 char buffer[INET6_ADDRSTRLEN + 2];
Pierre Ossman14263e12014-11-19 11:14:49 +0100277 const char *name;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000278
Pierre Ossman07cd2292014-11-19 11:16:04 +0100279 buffer[0] = '[';
280
Pierre Ossman14263e12014-11-19 11:14:49 +0100281 name = inet_ntop(sa.u.sa.sa_family, &sa.u.sin6.sin6_addr,
Pierre Ossman07cd2292014-11-19 11:16:04 +0100282 buffer + 1, sizeof(buffer) - 2);
Pierre Ossman14263e12014-11-19 11:14:49 +0100283 if (name == NULL) {
284 vlog.error("unable to convert peer name to a string");
285 return rfb::strDup("");
286 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000287
Pierre Ossman07cd2292014-11-19 11:16:04 +0100288 strcat(buffer, "]");
289
290 return rfb::strDup(buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000291 }
Pierre Ossman14263e12014-11-19 11:14:49 +0100292#endif
293
294 if (sa.u.sa.sa_family == AF_INET) {
295 char *name;
296
297 name = inet_ntoa(sa.u.sin.sin_addr);
298 if (name == NULL) {
299 vlog.error("unable to convert peer name to a string");
300 return rfb::strDup("");
301 }
302
303 return rfb::strDup(name);
304 }
305
306 vlog.error("unknown address family for socket");
307 return rfb::strDup("");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000308}
309
310int TcpSocket::getPeerPort() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000311 vnc_sockaddr_t sa;
312 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000313
Tim Waugh6ae42df2014-11-17 17:07:07 +0000314 getpeername(getFd(), &sa.u.sa, &sa_size);
315
316 switch (sa.u.sa.sa_family) {
317#ifdef HAVE_GETADDRINFO
318 case AF_INET6:
319 return ntohs(sa.u.sin6.sin6_port);
320#endif /* HAVE_GETADDRINFO */
Pierre Ossman14263e12014-11-19 11:14:49 +0100321 case AF_INET:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000322 return ntohs(sa.u.sin.sin_port);
Pierre Ossman14263e12014-11-19 11:14:49 +0100323 default:
324 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000325 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000326}
327
328char* TcpSocket::getPeerEndpoint() {
329 rfb::CharArray address; address.buf = getPeerAddress();
330 int port = getPeerPort();
331
332 int buflen = strlen(address.buf) + 32;
333 char* buffer = new char[buflen];
334 sprintf(buffer, "%s::%d", address.buf, port);
335 return buffer;
336}
337
338bool TcpSocket::sameMachine() {
Adam Tkac897814f2009-11-12 10:32:43 +0000339 vnc_sockaddr_t peeraddr, myaddr;
340 socklen_t addrlen;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000341
Adam Tkac897814f2009-11-12 10:32:43 +0000342 addrlen = sizeof(peeraddr);
343 if (getpeername(getFd(), &peeraddr.u.sa, &addrlen) < 0)
344 throw SocketException ("unable to get peer address", errorNumber);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000345
Adam Tkac897814f2009-11-12 10:32:43 +0000346 addrlen = sizeof(myaddr); /* need to reset, since getpeername overwrote */
347 if (getsockname(getFd(), &myaddr.u.sa, &addrlen) < 0)
348 throw SocketException ("unable to get my address", errorNumber);
349
350 if (peeraddr.u.sa.sa_family != myaddr.u.sa.sa_family)
351 return false;
352
353#ifdef HAVE_GETADDRINFO
354 if (peeraddr.u.sa.sa_family == AF_INET6)
355 return IN6_ARE_ADDR_EQUAL(&peeraddr.u.sin6.sin6_addr,
356 &myaddr.u.sin6.sin6_addr);
357#endif
358
Pierre Ossman14263e12014-11-19 11:14:49 +0100359 if (peeraddr.u.sa.sa_family == AF_INET)
360 return (peeraddr.u.sin.sin_addr.s_addr == myaddr.u.sin.sin_addr.s_addr);
361
362 // No idea what this is. Assume we're on different machines.
363 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000364}
365
366void TcpSocket::shutdown()
367{
368 Socket::shutdown();
369 ::shutdown(getFd(), 2);
370}
371
372bool TcpSocket::enableNagles(int sock, bool enable) {
373 int one = enable ? 0 : 1;
374 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
375 (char *)&one, sizeof(one)) < 0) {
376 int e = errorNumber;
377 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
378 return false;
379 }
380 return true;
381}
382
Pierre Ossman64069a92011-11-08 12:10:55 +0000383bool TcpSocket::cork(int sock, bool enable) {
384#ifndef TCP_CORK
385 return false;
386#else
387 int one = enable ? 1 : 0;
388 if (setsockopt(sock, IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0)
389 return false;
390 return true;
391#endif
392}
393
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000394bool TcpSocket::isSocket(int sock)
395{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000396 vnc_sockaddr_t sa;
397 socklen_t sa_size = sizeof(sa);
398 return getsockname(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000399}
400
401bool TcpSocket::isConnected(int sock)
402{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000403 vnc_sockaddr_t sa;
404 socklen_t sa_size = sizeof(sa);
405 return getpeername(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000406}
407
408int TcpSocket::getSockPort(int sock)
409{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000410 vnc_sockaddr_t sa;
411 socklen_t sa_size = sizeof(sa);
412 if (getsockname(sock, &sa.u.sa, &sa_size) < 0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000413 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000414
415 switch (sa.u.sa.sa_family) {
416#ifdef HAVE_GETADDRINFO
417 case AF_INET6:
418 return ntohs(sa.u.sin6.sin6_port);
419#endif /* HAVE_GETADDRINFO */
420
421 default:
422 return ntohs(sa.u.sin.sin_port);
423 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000424}
425
Tim Waughe4d97262014-11-21 16:07:34 +0000426static int bindIPv6 (const char *listenaddr,
427 int port,
428 bool localhostOnly)
429{
430#ifdef HAVE_GETADDRINFO
431 struct sockaddr_in6 addr6;
432 socklen_t sa_len;
433 int fd;
434
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100435 if (!UseIPv6)
436 return -1;
437
Tim Waughe4d97262014-11-21 16:07:34 +0000438 if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
439 return -1;
440
441#ifdef IPV6_V6ONLY
442 // - We made an IPv6-capable socket, and we need it to do IPv4 too
443 int opt = 0;
444 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, sizeof(opt));
445#else
446 vlog.error("IPV6_V6ONLY support is missing. "
447 "IPv4 clients may not be able to connect.");
448#endif
449
450 memset(&addr6, 0, (sa_len = sizeof(addr6)));
451 addr6.sin6_family = AF_INET6;
452 addr6.sin6_port = htons(port);
453
454 if (localhostOnly)
455 addr6.sin6_addr = in6addr_loopback;
456 else if (listenaddr != NULL) {
457#ifdef HAVE_INET_PTON
458 if (inet_pton(AF_INET6, listenaddr, &addr6.sin6_addr) != 1) {
459 closesocket(fd);
460 return -1;
461 }
462#else
463 // Unable to parse without inet_pton
464 closesocket(fd);
465 return -1;
466#endif
467 }
468
469 if (bind(fd, (struct sockaddr *) &addr6, sa_len) == -1) {
470 closesocket(fd);
471 return -1;
472 }
473
474 return fd;
475#else
476 return -1;
477#endif /* HAVE_GETADDRINFO */
478}
479
480static int bindIPv4 (const char *listenaddr,
481 int port,
482 bool localhostOnly)
483{
484 struct sockaddr_in addr;
485 socklen_t sa_len;
486 int fd;
487
Pierre Ossman39b3b8f2015-01-29 17:35:45 +0100488 if (!UseIPv4)
489 return -1;
490
Tim Waughe4d97262014-11-21 16:07:34 +0000491 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
492 return -1;
493
494 memset(&addr, 0, (sa_len = sizeof(addr)));
495 addr.sin_family = AF_INET;
496 addr.sin_port = htons(port);
497
498 if (localhostOnly)
499 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
500 else if (listenaddr != NULL) {
501#ifdef HAVE_INET_ATON
502 if (inet_aton(listenaddr, &addr.sin_addr) == 0)
503#else
504 /* Some systems (e.g. Windows) do not have inet_aton, sigh */
505 if ((addr.sin_addr.s_addr = inet_addr(listenaddr)) == INADDR_NONE)
506#endif
507 {
508 closesocket(fd);
509 throw Exception("invalid network interface address: %s", listenaddr);
510 }
511 } else
512 /* Bind to 0.0.0.0 by default. */
513 addr.sin_addr.s_addr = htonl(INADDR_ANY);
514
515 if (bind(fd, (struct sockaddr *) &addr, sa_len) == -1) {
516 closesocket(fd);
517 return -1;
518 }
519
520 return fd;
521}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000522
Adam Tkac93ff5db2010-02-05 15:54:10 +0000523TcpListener::TcpListener(const char *listenaddr, int port, bool localhostOnly,
524 int sock, bool close_) : closeFd(close_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000525{
526 if (sock != -1) {
527 fd = sock;
528 return;
529 }
530
531 initSockets();
Tim Waughe4d97262014-11-21 16:07:34 +0000532 if ((fd = bindIPv6 (listenaddr, port, localhostOnly)) < 0)
533 if ((fd = bindIPv4 (listenaddr, port, localhostOnly)) < 0)
Tim Waugh4561f7e2014-11-12 10:24:02 +0000534 throw SocketException("unable to create listening socket", errorNumber);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000535
536#ifndef WIN32
537 // - By default, close the socket on exec()
538 fcntl(fd, F_SETFD, FD_CLOEXEC);
539
540 int one = 1;
541 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
542 (char *)&one, sizeof(one)) < 0) {
543 int e = errorNumber;
544 closesocket(fd);
545 throw SocketException("unable to create listening socket", e);
546 }
547#endif
548
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000549 // - Set it to be a listening socket
550 if (listen(fd, 5) < 0) {
551 int e = errorNumber;
552 closesocket(fd);
553 throw SocketException("unable to set socket to listening mode", e);
554 }
555}
556
557TcpListener::~TcpListener() {
558 if (closeFd) closesocket(fd);
559}
560
561void TcpListener::shutdown()
562{
563#ifdef WIN32
564 closesocket(getFd());
565#else
566 ::shutdown(getFd(), 2);
567#endif
568}
569
570
571Socket*
572TcpListener::accept() {
573 int new_sock = -1;
574
575 // Accept an incoming connection
576 if ((new_sock = ::accept(fd, 0, 0)) < 0)
577 throw SocketException("unable to accept new connection", errorNumber);
578
579#ifndef WIN32
580 // - By default, close the socket on exec()
581 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
582#endif
583
584 // Disable Nagle's algorithm, to reduce latency
585 TcpSocket::enableNagles(new_sock, false);
586
587 // Create the socket object & check connection is allowed
588 TcpSocket* s = new TcpSocket(new_sock);
589 if (filter && !filter->verifyConnection(s)) {
590 delete s;
591 return 0;
592 }
593 return s;
594}
595
596void TcpListener::getMyAddresses(std::list<char*>* result) {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000597#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_PTON)
598 vnc_sockaddr_t sa;
599 struct addrinfo *ai, *current, hints;
600
601 memset(&hints, 0, sizeof(struct addrinfo));
602 hints.ai_family = AF_UNSPEC;
603 hints.ai_socktype = SOCK_STREAM;
604 hints.ai_canonname = NULL;
605 hints.ai_addr = NULL;
606 hints.ai_next = NULL;
607
608 if ((getaddrinfo(NULL, NULL, &hints, &ai)) != 0)
609 return;
610
611 for (current= ai; current != NULL; current = current->ai_next) {
612 if (current->ai_family != AF_INET && current->ai_family != AF_INET6)
613 continue;
614
615 char *addr = new char[INET6_ADDRSTRLEN];
616 inet_ntop(current->ai_family, current->ai_addr, addr, INET6_ADDRSTRLEN);
617 result->push_back(addr);
618 }
619 freeaddrinfo(ai);
620#else
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000621 const hostent* addrs = gethostbyname(0);
622 if (addrs == 0)
623 throw rdr::SystemException("gethostbyname", errorNumber);
624 if (addrs->h_addrtype != AF_INET)
625 throw rdr::Exception("getMyAddresses: bad family");
626 for (int i=0; addrs->h_addr_list[i] != 0; i++) {
627 const char* addrC = inet_ntoa(*((struct in_addr*)addrs->h_addr_list[i]));
628 char* addr = new char[strlen(addrC)+1];
629 strcpy(addr, addrC);
630 result->push_back(addr);
631 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000632#endif /* defined(HAVE_GETADDRINFO) && defined(HAVE_INET_PTON) */
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000633}
634
635int TcpListener::getMyPort() {
636 return TcpSocket::getSockPort(getFd());
637}
638
639
640TcpFilter::TcpFilter(const char* spec) {
641 rfb::CharArray tmp;
Adam Tkacd36b6262009-09-04 10:57:20 +0000642 tmp.buf = rfb::strDup(spec);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000643 while (tmp.buf) {
644 rfb::CharArray first;
645 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
646 if (strlen(first.buf))
647 filter.push_back(parsePattern(first.buf));
648 }
649}
650
651TcpFilter::~TcpFilter() {
652}
653
654
655static bool
656patternMatchIP(const TcpFilter::Pattern& pattern, const char* value) {
657 unsigned long address = inet_addr((char *)value);
658 if (address == INADDR_NONE) return false;
659 return ((pattern.address & pattern.mask) == (address & pattern.mask));
660}
661
662bool
663TcpFilter::verifyConnection(Socket* s) {
664 rfb::CharArray name;
665
Tim Waugh6ae42df2014-11-17 17:07:07 +0000666#ifdef HAVE_GETADDRINFO
667 vnc_sockaddr_t sa;
668 socklen_t sa_size = sizeof(sa);
669 if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0 ||
670 sa.u.sa.sa_family != AF_INET)
671 /* Matching only works for IPv4 */
672 return false;
673#endif /* HAVE_GETADDRINFO */
674
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000675 name.buf = s->getPeerAddress();
676 std::list<TcpFilter::Pattern>::iterator i;
677 for (i=filter.begin(); i!=filter.end(); i++) {
678 if (patternMatchIP(*i, name.buf)) {
679 switch ((*i).action) {
680 case Accept:
681 vlog.debug("ACCEPT %s", name.buf);
682 return true;
683 case Query:
684 vlog.debug("QUERY %s", name.buf);
685 s->setRequiresQuery();
686 return true;
687 case Reject:
688 vlog.debug("REJECT %s", name.buf);
689 return false;
690 }
691 }
692 }
693
694 vlog.debug("[REJECT] %s", name.buf);
695 return false;
696}
697
698
699TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
700 TcpFilter::Pattern pattern;
701
702 bool expandMask = false;
703 rfb::CharArray addr, mask;
704
705 if (rfb::strSplit(&p[1], '/', &addr.buf, &mask.buf)) {
706 if (rfb::strContains(mask.buf, '.')) {
707 pattern.mask = inet_addr(mask.buf);
708 } else {
709 pattern.mask = atoi(mask.buf);
710 expandMask = true;
711 }
712 } else {
713 pattern.mask = 32;
714 expandMask = true;
715 }
716 if (expandMask) {
717 unsigned long expanded = 0;
718 // *** check endianness!
719 for (int i=0; i<(int)pattern.mask; i++)
720 expanded |= 1<<(31-i);
721 pattern.mask = htonl(expanded);
722 }
723
724 pattern.address = inet_addr(addr.buf) & pattern.mask;
725 if ((pattern.address == INADDR_NONE) ||
726 (pattern.address == 0)) pattern.mask = 0;
727
728 switch(p[0]) {
729 case '+': pattern.action = TcpFilter::Accept; break;
730 case '-': pattern.action = TcpFilter::Reject; break;
731 case '?': pattern.action = TcpFilter::Query; break;
732 };
733
734 return pattern;
735}
736
737char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
738 in_addr tmp;
739 rfb::CharArray addr, mask;
740 tmp.s_addr = p.address;
Adam Tkacd36b6262009-09-04 10:57:20 +0000741 addr.buf = rfb::strDup(inet_ntoa(tmp));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000742 tmp.s_addr = p.mask;
Adam Tkacd36b6262009-09-04 10:57:20 +0000743 mask.buf = rfb::strDup(inet_ntoa(tmp));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000744 char* result = new char[strlen(addr.buf)+1+strlen(mask.buf)+1+1];
745 switch (p.action) {
746 case Accept: result[0] = '+'; break;
747 case Reject: result[0] = '-'; break;
748 case Query: result[0] = '?'; break;
749 };
750 result[1] = 0;
751 strcat(result, addr.buf);
752 strcat(result, "/");
753 strcat(result, mask.buf);
754 return result;
755}