blob: 188651edc69155e0f5bd5e2ae07fb3bab9c56f89 [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>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000037#include <errno.h>
38#include <string.h>
39#include <signal.h>
40#include <fcntl.h>
41#endif
42
Adam Tkac04b7fd22008-03-19 16:14:48 +000043#include <stdlib.h>
Tim Waugh892d10a2015-03-11 13:12:07 +000044#include <unistd.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",
Tim Waugha85363d2015-03-11 13:07:48 +0000155 gai_strerror(result));
Adam Tkac9cb6a422008-11-14 15:56:15 +0000156 }
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)
Tim Waugha85363d2015-03-11 13:07:48 +0000225 continue;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000226#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);
Pierre Ossmanda9a38d2015-03-03 16:03:32 +0100237
238 if (current == NULL)
239 throw Exception("No useful address for host");
Adam Tkac9cb6a422008-11-14 15:56:15 +0000240#endif /* HAVE_GETADDRINFO */
241
242 if (result == -1)
243 throw SocketException("unable connect to socket", err);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000244
245#ifndef WIN32
246 // - By default, close the socket on exec()
247 fcntl(sock, F_SETFD, FD_CLOEXEC);
248#endif
249
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000250 // Disable Nagle's algorithm, to reduce latency
251 enableNagles(sock, false);
252
253 // Create the input and output streams
254 instream = new FdInStream(sock);
255 outstream = new FdOutStream(sock);
256 ownStreams = true;
257}
258
259TcpSocket::~TcpSocket() {
260 if (closeFd)
261 closesocket(getFd());
262}
263
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000264int TcpSocket::getMyPort() {
265 return getSockPort(getFd());
266}
267
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000268char* TcpSocket::getPeerAddress() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000269 vnc_sockaddr_t sa;
270 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000271
Tim Waugh6ae42df2014-11-17 17:07:07 +0000272 if (getpeername(getFd(), &sa.u.sa, &sa_size) != 0) {
273 vlog.error("unable to get peer name for socket");
274 return rfb::strDup("");
275 }
276
Tim Waugh892d10a2015-03-11 13:12:07 +0000277#if defined(HAVE_GETADDRINFO)
Pierre Ossman14263e12014-11-19 11:14:49 +0100278 if (sa.u.sa.sa_family == AF_INET6) {
Pierre Ossman07cd2292014-11-19 11:16:04 +0100279 char buffer[INET6_ADDRSTRLEN + 2];
Tim Waugh892d10a2015-03-11 13:12:07 +0000280 int ret;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000281
Pierre Ossman07cd2292014-11-19 11:16:04 +0100282 buffer[0] = '[';
283
Tim Waugh892d10a2015-03-11 13:12:07 +0000284 ret = getnameinfo(&sa.u.sa, sizeof(sa.u.sin6),
285 buffer + 1, sizeof(buffer) - 2, NULL, 0,
286 NI_NUMERICHOST);
287 if (ret != 0) {
Pierre Ossman14263e12014-11-19 11:14:49 +0100288 vlog.error("unable to convert peer name to a string");
289 return rfb::strDup("");
290 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000291
Pierre Ossman07cd2292014-11-19 11:16:04 +0100292 strcat(buffer, "]");
293
294 return rfb::strDup(buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000295 }
Pierre Ossman14263e12014-11-19 11:14:49 +0100296#endif
297
298 if (sa.u.sa.sa_family == AF_INET) {
299 char *name;
300
301 name = inet_ntoa(sa.u.sin.sin_addr);
302 if (name == NULL) {
303 vlog.error("unable to convert peer name to a string");
304 return rfb::strDup("");
305 }
306
307 return rfb::strDup(name);
308 }
309
310 vlog.error("unknown address family for socket");
311 return rfb::strDup("");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000312}
313
314int TcpSocket::getPeerPort() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000315 vnc_sockaddr_t sa;
316 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000317
Tim Waugh6ae42df2014-11-17 17:07:07 +0000318 getpeername(getFd(), &sa.u.sa, &sa_size);
319
320 switch (sa.u.sa.sa_family) {
321#ifdef HAVE_GETADDRINFO
322 case AF_INET6:
323 return ntohs(sa.u.sin6.sin6_port);
324#endif /* HAVE_GETADDRINFO */
Pierre Ossman14263e12014-11-19 11:14:49 +0100325 case AF_INET:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000326 return ntohs(sa.u.sin.sin_port);
Pierre Ossman14263e12014-11-19 11:14:49 +0100327 default:
328 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000329 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000330}
331
332char* TcpSocket::getPeerEndpoint() {
333 rfb::CharArray address; address.buf = getPeerAddress();
334 int port = getPeerPort();
335
336 int buflen = strlen(address.buf) + 32;
337 char* buffer = new char[buflen];
338 sprintf(buffer, "%s::%d", address.buf, port);
339 return buffer;
340}
341
342bool TcpSocket::sameMachine() {
Adam Tkac897814f2009-11-12 10:32:43 +0000343 vnc_sockaddr_t peeraddr, myaddr;
344 socklen_t addrlen;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000345
Adam Tkac897814f2009-11-12 10:32:43 +0000346 addrlen = sizeof(peeraddr);
347 if (getpeername(getFd(), &peeraddr.u.sa, &addrlen) < 0)
348 throw SocketException ("unable to get peer address", errorNumber);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000349
Adam Tkac897814f2009-11-12 10:32:43 +0000350 addrlen = sizeof(myaddr); /* need to reset, since getpeername overwrote */
351 if (getsockname(getFd(), &myaddr.u.sa, &addrlen) < 0)
352 throw SocketException ("unable to get my address", errorNumber);
353
354 if (peeraddr.u.sa.sa_family != myaddr.u.sa.sa_family)
355 return false;
356
357#ifdef HAVE_GETADDRINFO
358 if (peeraddr.u.sa.sa_family == AF_INET6)
359 return IN6_ARE_ADDR_EQUAL(&peeraddr.u.sin6.sin6_addr,
Tim Waugha85363d2015-03-11 13:07:48 +0000360 &myaddr.u.sin6.sin6_addr);
Adam Tkac897814f2009-11-12 10:32:43 +0000361#endif
362
Pierre Ossman14263e12014-11-19 11:14:49 +0100363 if (peeraddr.u.sa.sa_family == AF_INET)
364 return (peeraddr.u.sin.sin_addr.s_addr == myaddr.u.sin.sin_addr.s_addr);
365
366 // No idea what this is. Assume we're on different machines.
367 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000368}
369
370void TcpSocket::shutdown()
371{
372 Socket::shutdown();
373 ::shutdown(getFd(), 2);
374}
375
376bool TcpSocket::enableNagles(int sock, bool enable) {
377 int one = enable ? 0 : 1;
378 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
Tim Waugha85363d2015-03-11 13:07:48 +0000379 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000380 int e = errorNumber;
381 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
382 return false;
383 }
384 return true;
385}
386
Pierre Ossman64069a92011-11-08 12:10:55 +0000387bool TcpSocket::cork(int sock, bool enable) {
388#ifndef TCP_CORK
389 return false;
390#else
391 int one = enable ? 1 : 0;
392 if (setsockopt(sock, IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0)
393 return false;
394 return true;
395#endif
396}
397
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000398bool TcpSocket::isSocket(int sock)
399{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000400 vnc_sockaddr_t sa;
401 socklen_t sa_size = sizeof(sa);
402 return getsockname(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000403}
404
405bool TcpSocket::isConnected(int sock)
406{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000407 vnc_sockaddr_t sa;
408 socklen_t sa_size = sizeof(sa);
409 return getpeername(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000410}
411
412int TcpSocket::getSockPort(int sock)
413{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000414 vnc_sockaddr_t sa;
415 socklen_t sa_size = sizeof(sa);
416 if (getsockname(sock, &sa.u.sa, &sa_size) < 0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000417 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000418
419 switch (sa.u.sa.sa_family) {
420#ifdef HAVE_GETADDRINFO
421 case AF_INET6:
422 return ntohs(sa.u.sin6.sin6_port);
423#endif /* HAVE_GETADDRINFO */
424
425 default:
426 return ntohs(sa.u.sin.sin_port);
427 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000428}
429
Tim Waugh892d10a2015-03-11 13:12:07 +0000430TcpListener::TcpListener(int sock)
Tim Waughe4d97262014-11-21 16:07:34 +0000431{
Tim Waugh892d10a2015-03-11 13:12:07 +0000432 fd = sock;
Tim Waughe4d97262014-11-21 16:07:34 +0000433}
434
Tim Waugh892d10a2015-03-11 13:12:07 +0000435TcpListener::TcpListener(const TcpListener& other)
Tim Waughe4d97262014-11-21 16:07:34 +0000436{
Tim Waugh892d10a2015-03-11 13:12:07 +0000437 fd = dup (other.fd);
438 // Hope TcpListener::shutdown(other) doesn't get called...
Tim Waughe4d97262014-11-21 16:07:34 +0000439}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000440
Tim Waugh892d10a2015-03-11 13:12:07 +0000441TcpListener& TcpListener::operator= (const TcpListener& other)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000442{
Tim Waugh892d10a2015-03-11 13:12:07 +0000443 if (this != &other)
444 {
445 closesocket (fd);
446 fd = dup (other.fd);
447 // Hope TcpListener::shutdown(other) doesn't get called...
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000448 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000449 return *this;
450}
451
452TcpListener::TcpListener(const struct sockaddr *listenaddr,
453 socklen_t listenaddrlen)
454{
455 int one = 1;
456 vnc_sockaddr_t sa;
457 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000458
459 initSockets();
Tim Waugh892d10a2015-03-11 13:12:07 +0000460
461 if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0)
462 throw SocketException("unable to create listening socket", errorNumber);
463
464 memcpy (&sa, listenaddr, listenaddrlen);
465#ifdef IPV6_V6ONLY
466 if (listenaddr->sa_family == AF_INET6) {
467 if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one)))
468 throw SocketException("unable to set IPV6_V6ONLY", errorNumber);
469 }
470#endif /* defined(IPV6_V6ONLY) */
471
472 if (bind(sock, &sa.u.sa, listenaddrlen) == -1) {
473 closesocket(sock);
474 throw SocketException("failed to bind socket", errorNumber);
475 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000476
477#ifndef WIN32
478 // - By default, close the socket on exec()
Tim Waugh892d10a2015-03-11 13:12:07 +0000479 fcntl(sock, F_SETFD, FD_CLOEXEC);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000480
Tim Waugh892d10a2015-03-11 13:12:07 +0000481 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
482 (char *)&one, sizeof(one)) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000483 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000484 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000485 throw SocketException("unable to create listening socket", e);
486 }
487#endif
488
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000489 // - Set it to be a listening socket
Tim Waugh892d10a2015-03-11 13:12:07 +0000490 if (listen(sock, 5) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000491 int e = errorNumber;
Tim Waugh892d10a2015-03-11 13:12:07 +0000492 closesocket(sock);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000493 throw SocketException("unable to set socket to listening mode", e);
494 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000495
496 fd = sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000497}
498
499TcpListener::~TcpListener() {
Tim Waugh892d10a2015-03-11 13:12:07 +0000500 closesocket(fd);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000501}
502
503void TcpListener::shutdown()
504{
505#ifdef WIN32
506 closesocket(getFd());
507#else
508 ::shutdown(getFd(), 2);
509#endif
510}
511
512
513Socket*
514TcpListener::accept() {
515 int new_sock = -1;
516
517 // Accept an incoming connection
518 if ((new_sock = ::accept(fd, 0, 0)) < 0)
519 throw SocketException("unable to accept new connection", errorNumber);
520
521#ifndef WIN32
522 // - By default, close the socket on exec()
523 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
524#endif
525
526 // Disable Nagle's algorithm, to reduce latency
527 TcpSocket::enableNagles(new_sock, false);
528
529 // Create the socket object & check connection is allowed
530 TcpSocket* s = new TcpSocket(new_sock);
531 if (filter && !filter->verifyConnection(s)) {
532 delete s;
533 return 0;
534 }
535 return s;
536}
537
Tim Waugh892d10a2015-03-11 13:12:07 +0000538int TcpListener::getMyPort() {
539 return TcpSocket::getSockPort(getFd());
540}
541
542
543void network::createLocalTcpListeners(std::list<TcpListener> *listeners,
544 int port)
545{
546 std::list<TcpListener> new_listeners;
547 vnc_sockaddr_t sa;
548#ifdef HAVE_GETADDRINFO
549 if (UseIPv6) {
550 sa.u.sin6.sin6_family = AF_INET6;
551 sa.u.sin6.sin6_port = htons (port);
552 sa.u.sin6.sin6_addr = in6addr_loopback;
553 try {
554 new_listeners.push_back (TcpListener (&sa.u.sa, sizeof (sa.u.sin6)));
555 } catch (SocketException& e) {
556 // Ignore this if it is due to lack of address family support on
557 // the interface or on the system
558 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT)
559 // Otherwise, report the error
560 throw;
561 }
562 }
563#endif /* HAVE_GETADDRINFO */
564 if (UseIPv4) {
565 sa.u.sin.sin_family = AF_INET;
566 sa.u.sin.sin_port = htons (port);
567 sa.u.sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
568 try {
569 new_listeners.push_back (TcpListener (&sa.u.sa, sizeof (sa.u.sin)));
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 }
578
579 if (new_listeners.empty ())
580 throw SocketException("createLocalTcpListeners: no addresses available",
581 EADDRNOTAVAIL);
582
583 listeners->splice (listeners->end(), new_listeners);
584}
585
586void network::createTcpListeners(std::list<TcpListener> *listeners,
587 const char *addr,
588 int port)
589{
590 std::list<TcpListener> new_listeners;
591
592#ifdef HAVE_GETADDRINFO
Tim Waugh6ae42df2014-11-17 17:07:07 +0000593 struct addrinfo *ai, *current, hints;
Tim Waugh892d10a2015-03-11 13:12:07 +0000594 char service[16];
Tim Waugh6ae42df2014-11-17 17:07:07 +0000595
596 memset(&hints, 0, sizeof(struct addrinfo));
Tim Waugh892d10a2015-03-11 13:12:07 +0000597 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000598 hints.ai_family = AF_UNSPEC;
599 hints.ai_socktype = SOCK_STREAM;
600 hints.ai_canonname = NULL;
601 hints.ai_addr = NULL;
602 hints.ai_next = NULL;
603
Tim Waugh892d10a2015-03-11 13:12:07 +0000604 snprintf (service, sizeof (service) - 1, "%d", port);
605 service[sizeof (service) - 1] = '\0';
606 if ((getaddrinfo(addr, service, &hints, &ai)) != 0)
607 throw rdr::SystemException("getaddrinfo", errorNumber);
Tim Waugh6ae42df2014-11-17 17:07:07 +0000608
Tim Waugh892d10a2015-03-11 13:12:07 +0000609 for (current = ai; current != NULL; current = current->ai_next) {
610 switch (current->ai_family) {
611 case AF_INET:
612 if (!UseIPv4)
613 continue;
614 break;
615
616 case AF_INET6:
617 if (!UseIPv6)
618 continue;
619 break;
620
621 default:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000622 continue;
Tim Waugh892d10a2015-03-11 13:12:07 +0000623 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000624
Tim Waugh892d10a2015-03-11 13:12:07 +0000625 try {
626 new_listeners.push_back(TcpListener (current->ai_addr,
627 current->ai_addrlen));
628 } catch (SocketException& e) {
629 // Ignore this if it is due to lack of address family support on
630 // the interface or on the system
631 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) {
632 // Otherwise, report the error
633 freeaddrinfo(ai);
634 throw;
635 }
636 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000637 }
638 freeaddrinfo(ai);
639#else
Tim Waugh892d10a2015-03-11 13:12:07 +0000640 const hostent* addrs;
641 if (addr) {
642 /* Bind to specific address */
643 addrs = gethostbyname(addr);
644 if (addrs == 0)
645 throw rdr::SystemException("gethostbyname", errorNumber);
646 if (addrs->h_addrtype != AF_INET)
647 throw rdr::Exception("createTcpListeners: bad family");
648 for (int i=0; addrs->h_addr_list[i] != 0; i++) {
649 struct sockaddr_in addr;
650 addr.sin_family = AF_INET;
651 memcpy (&addr.sin_addr, addrs->h_addr_list[i], addrs->h_length);
652 addr.sin_port = htons(port);
653 try {
654 new_listeners.push_back(TcpListener ((struct sockaddr*)&addr,
655 addrs->h_length));
656 } catch (SocketException& e) {
657 // Ignore this if it is due to lack of address family support
658 // on the interface or on the system
659 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) {
660 // Otherwise, report the error
661 freeaddrinfo(ai);
662 throw;
663 }
664 }
665 }
666 } else {
667 /* Bind to any address */
668 struct sockaddr_in addr;
669 addr.sin_family = AF_INET;
670 addr.sin_addr.s_addr = htonl(INADDR_ANY);
671 addr.sin_port = htons(port);
672 try {
673 new_listeners.push_back(TcpListener ((struct sockaddr*)&addr,
674 sizeof (struct sockaddr_in)));
675 } catch (SocketException& e) {
676 // Ignore this if it is due to lack of address family support on
677 // the interface or on the system
678 if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) {
679 // Otherwise, report the error
680 freeaddrinfo(ai);
681 throw;
682 }
683 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000684 }
Tim Waugh892d10a2015-03-11 13:12:07 +0000685#endif /* defined(HAVE_GETADDRINFO) */
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000686
Tim Waugh892d10a2015-03-11 13:12:07 +0000687 if (new_listeners.empty ())
688 throw SocketException("createTcpListeners: no addresses available",
689 EADDRNOTAVAIL);
690
691 listeners->splice (listeners->end(), new_listeners);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000692}
693
694
695TcpFilter::TcpFilter(const char* spec) {
696 rfb::CharArray tmp;
Adam Tkacd36b6262009-09-04 10:57:20 +0000697 tmp.buf = rfb::strDup(spec);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000698 while (tmp.buf) {
699 rfb::CharArray first;
700 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
701 if (strlen(first.buf))
702 filter.push_back(parsePattern(first.buf));
703 }
704}
705
706TcpFilter::~TcpFilter() {
707}
708
709
710static bool
711patternMatchIP(const TcpFilter::Pattern& pattern, const char* value) {
712 unsigned long address = inet_addr((char *)value);
713 if (address == INADDR_NONE) return false;
714 return ((pattern.address & pattern.mask) == (address & pattern.mask));
715}
716
717bool
718TcpFilter::verifyConnection(Socket* s) {
719 rfb::CharArray name;
720
Tim Waugh6ae42df2014-11-17 17:07:07 +0000721#ifdef HAVE_GETADDRINFO
722 vnc_sockaddr_t sa;
723 socklen_t sa_size = sizeof(sa);
724 if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0 ||
725 sa.u.sa.sa_family != AF_INET)
726 /* Matching only works for IPv4 */
727 return false;
728#endif /* HAVE_GETADDRINFO */
729
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000730 name.buf = s->getPeerAddress();
731 std::list<TcpFilter::Pattern>::iterator i;
732 for (i=filter.begin(); i!=filter.end(); i++) {
733 if (patternMatchIP(*i, name.buf)) {
734 switch ((*i).action) {
735 case Accept:
736 vlog.debug("ACCEPT %s", name.buf);
737 return true;
738 case Query:
739 vlog.debug("QUERY %s", name.buf);
740 s->setRequiresQuery();
741 return true;
742 case Reject:
743 vlog.debug("REJECT %s", name.buf);
744 return false;
745 }
746 }
747 }
748
749 vlog.debug("[REJECT] %s", name.buf);
750 return false;
751}
752
753
754TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
755 TcpFilter::Pattern pattern;
756
757 bool expandMask = false;
758 rfb::CharArray addr, mask;
759
760 if (rfb::strSplit(&p[1], '/', &addr.buf, &mask.buf)) {
761 if (rfb::strContains(mask.buf, '.')) {
762 pattern.mask = inet_addr(mask.buf);
763 } else {
764 pattern.mask = atoi(mask.buf);
765 expandMask = true;
766 }
767 } else {
768 pattern.mask = 32;
769 expandMask = true;
770 }
771 if (expandMask) {
772 unsigned long expanded = 0;
773 // *** check endianness!
774 for (int i=0; i<(int)pattern.mask; i++)
775 expanded |= 1<<(31-i);
776 pattern.mask = htonl(expanded);
777 }
778
779 pattern.address = inet_addr(addr.buf) & pattern.mask;
780 if ((pattern.address == INADDR_NONE) ||
781 (pattern.address == 0)) pattern.mask = 0;
782
783 switch(p[0]) {
784 case '+': pattern.action = TcpFilter::Accept; break;
785 case '-': pattern.action = TcpFilter::Reject; break;
786 case '?': pattern.action = TcpFilter::Query; break;
787 };
788
789 return pattern;
790}
791
792char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
793 in_addr tmp;
794 rfb::CharArray addr, mask;
795 tmp.s_addr = p.address;
Adam Tkacd36b6262009-09-04 10:57:20 +0000796 addr.buf = rfb::strDup(inet_ntoa(tmp));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000797 tmp.s_addr = p.mask;
Adam Tkacd36b6262009-09-04 10:57:20 +0000798 mask.buf = rfb::strDup(inet_ntoa(tmp));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000799 char* result = new char[strlen(addr.buf)+1+strlen(mask.buf)+1+1];
800 switch (p.action) {
801 case Accept: result[0] = '+'; break;
802 case Reject: result[0] = '-'; break;
803 case Query: result[0] = '?'; break;
804 };
805 result[1] = 0;
806 strcat(result, addr.buf);
807 strcat(result, "/");
808 strcat(result, mask.buf);
809 return result;
810}