blob: dd0286006d9c597de6538395cd4622787f9422ec [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>
48
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000049#ifndef INADDR_NONE
50#define INADDR_NONE ((unsigned long)-1)
51#endif
52#ifndef INADDR_LOOPBACK
53#define INADDR_LOOPBACK ((unsigned long)0x7F000001)
54#endif
55
Pierre Ossman8b6aa202012-12-13 13:55:22 +000056#if defined(HAVE_GETADDRINFO) && !defined(IN6_ARE_ADDR_EQUAL)
57#define IN6_ARE_ADDR_EQUAL(a,b) \
58 (memcmp ((const void*)(a), (const void*)(b), sizeof (struct in6_addr)) == 0)
59#endif
60
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000061using namespace network;
62using namespace rdr;
63
Adam Tkac9cb6a422008-11-14 15:56:15 +000064typedef struct vnc_sockaddr {
65 union {
66 sockaddr sa;
67 sockaddr_in sin;
68#ifdef HAVE_GETADDRINFO
69 sockaddr_in6 sin6;
70#endif
71 } u;
72} vnc_sockaddr_t;
73
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000074static rfb::LogWriter vlog("TcpSocket");
75
76/* Tunnelling support. */
77int network::findFreeTcpPort (void)
78{
DRCb2618e52011-02-21 13:43:44 +000079 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000080 struct sockaddr_in addr;
81 memset(&addr, 0, sizeof(addr));
82 addr.sin_family = AF_INET;
83 addr.sin_addr.s_addr = INADDR_ANY;
84
85 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
86 throw SocketException ("unable to create socket", errorNumber);
87
DRCb2618e52011-02-21 13:43:44 +000088 addr.sin_port = 0;
89 if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) < 0)
90 throw SocketException ("unable to find free port", errorNumber);
91
92 socklen_t n = sizeof(addr);
93 if (getsockname (sock, (struct sockaddr *)&addr, &n) < 0)
94 throw SocketException ("unable to get port number", errorNumber);
95
96 closesocket (sock);
97 return ntohs(addr.sin_port);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000098}
99
100
101// -=- Socket initialisation
102static bool socketsInitialised = false;
103static void initSockets() {
104 if (socketsInitialised)
105 return;
106#ifdef WIN32
107 WORD requiredVersion = MAKEWORD(2,0);
108 WSADATA initResult;
109
110 if (WSAStartup(requiredVersion, &initResult) != 0)
111 throw SocketException("unable to initialise Winsock2", errorNumber);
112#else
113 signal(SIGPIPE, SIG_IGN);
114#endif
115 socketsInitialised = true;
116}
117
118
119// -=- TcpSocket
120
121TcpSocket::TcpSocket(int sock, bool close)
122 : Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close)
123{
124}
125
126TcpSocket::TcpSocket(const char *host, int port)
127 : closeFd(true)
128{
Adam Tkac9cb6a422008-11-14 15:56:15 +0000129 int sock, err, result, family;
130 vnc_sockaddr_t sa;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000131 socklen_t salen;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000132#ifdef HAVE_GETADDRINFO
133 struct addrinfo *ai, *current, hints;
134#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000135
136 // - Create a socket
137 initSockets();
Adam Tkac9cb6a422008-11-14 15:56:15 +0000138
139#ifdef HAVE_GETADDRINFO
140 memset(&hints, 0, sizeof(struct addrinfo));
141 hints.ai_family = AF_UNSPEC;
142 hints.ai_socktype = SOCK_STREAM;
143 hints.ai_canonname = NULL;
144 hints.ai_addr = NULL;
145 hints.ai_next = NULL;
146
147 if ((result = getaddrinfo(host, NULL, &hints, &ai)) != 0) {
148 throw Exception("unable to resolve host by name: %s",
149 gai_strerror(result));
150 }
151
152 for (current = ai; current != NULL; current = current->ai_next) {
153 family = current->ai_family;
154 if (family != AF_INET && family != AF_INET6)
155 continue;
156
157 salen = current->ai_addrlen;
158 memcpy(&sa, current->ai_addr, salen);
159
160 if (family == AF_INET)
161 sa.u.sin.sin_port = htons(port);
162 else
163 sa.u.sin6.sin6_port = htons(port);
164
165#else /* HAVE_GETADDRINFO */
166 family = AF_INET;
167 salen = sizeof(struct sockaddr_in);
168
169 /* Try processing the host as an IP address */
170 memset(&sa, 0, sizeof(sa));
171 sa.u.sin.sin_family = AF_INET;
172 sa.u.sin.sin_addr.s_addr = inet_addr((char *)host);
173 sa.u.sin.sin_port = htons(port);
174 if ((int)sa.u.sin.sin_addr.s_addr == -1) {
175 /* Host was not an IP address - try resolving as DNS name */
176 struct hostent *hostinfo;
177 hostinfo = gethostbyname((char *)host);
178 if (hostinfo && hostinfo->h_addr) {
179 sa.u.sin.sin_addr.s_addr = ((struct in_addr *)hostinfo->h_addr)->s_addr;
180 } else {
181 err = errorNumber;
182 throw SocketException("unable to resolve host by name", err);
183 }
184 }
185#endif /* HAVE_GETADDRINFO */
186
187 sock = socket (family, SOCK_STREAM, 0);
188 if (sock == -1) {
189 err = errorNumber;
190#ifdef HAVE_GETADDRINFO
191 freeaddrinfo(ai);
192#endif /* HAVE_GETADDRINFO */
193 throw SocketException("unable to create socket", err);
194 }
195
196 /* Attempt to connect to the remote host */
Adam Tkacc9cda3b2009-10-30 11:13:34 +0000197 while ((result = connect(sock, &sa.u.sa, salen)) == -1) {
Adam Tkac9cb6a422008-11-14 15:56:15 +0000198 err = errorNumber;
199#ifndef WIN32
200 if (err == EINTR)
201 continue;
202#endif
203 closesocket(sock);
204 break;
205 }
206
207#ifdef HAVE_GETADDRINFO
208 if (result == 0)
209 break;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000210 }
211
212 freeaddrinfo(ai);
213#endif /* HAVE_GETADDRINFO */
214
215 if (result == -1)
216 throw SocketException("unable connect to socket", err);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000217
218#ifndef WIN32
219 // - By default, close the socket on exec()
220 fcntl(sock, F_SETFD, FD_CLOEXEC);
221#endif
222
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000223 // Disable Nagle's algorithm, to reduce latency
224 enableNagles(sock, false);
225
226 // Create the input and output streams
227 instream = new FdInStream(sock);
228 outstream = new FdOutStream(sock);
229 ownStreams = true;
230}
231
232TcpSocket::~TcpSocket() {
233 if (closeFd)
234 closesocket(getFd());
235}
236
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000237int TcpSocket::getMyPort() {
238 return getSockPort(getFd());
239}
240
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000241char* TcpSocket::getPeerAddress() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000242 vnc_sockaddr_t sa;
243 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000244
Tim Waugh6ae42df2014-11-17 17:07:07 +0000245 if (getpeername(getFd(), &sa.u.sa, &sa_size) != 0) {
246 vlog.error("unable to get peer name for socket");
247 return rfb::strDup("");
248 }
249
Pierre Ossman14263e12014-11-19 11:14:49 +0100250#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_PTON)
251 if (sa.u.sa.sa_family == AF_INET6) {
Pierre Ossman07cd2292014-11-19 11:16:04 +0100252 char buffer[INET6_ADDRSTRLEN + 2];
Pierre Ossman14263e12014-11-19 11:14:49 +0100253 const char *name;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000254
Pierre Ossman07cd2292014-11-19 11:16:04 +0100255 buffer[0] = '[';
256
Pierre Ossman14263e12014-11-19 11:14:49 +0100257 name = inet_ntop(sa.u.sa.sa_family, &sa.u.sin6.sin6_addr,
Pierre Ossman07cd2292014-11-19 11:16:04 +0100258 buffer + 1, sizeof(buffer) - 2);
Pierre Ossman14263e12014-11-19 11:14:49 +0100259 if (name == NULL) {
260 vlog.error("unable to convert peer name to a string");
261 return rfb::strDup("");
262 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000263
Pierre Ossman07cd2292014-11-19 11:16:04 +0100264 strcat(buffer, "]");
265
266 return rfb::strDup(buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000267 }
Pierre Ossman14263e12014-11-19 11:14:49 +0100268#endif
269
270 if (sa.u.sa.sa_family == AF_INET) {
271 char *name;
272
273 name = inet_ntoa(sa.u.sin.sin_addr);
274 if (name == NULL) {
275 vlog.error("unable to convert peer name to a string");
276 return rfb::strDup("");
277 }
278
279 return rfb::strDup(name);
280 }
281
282 vlog.error("unknown address family for socket");
283 return rfb::strDup("");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000284}
285
286int TcpSocket::getPeerPort() {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000287 vnc_sockaddr_t sa;
288 socklen_t sa_size = sizeof(sa);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000289
Tim Waugh6ae42df2014-11-17 17:07:07 +0000290 getpeername(getFd(), &sa.u.sa, &sa_size);
291
292 switch (sa.u.sa.sa_family) {
293#ifdef HAVE_GETADDRINFO
294 case AF_INET6:
295 return ntohs(sa.u.sin6.sin6_port);
296#endif /* HAVE_GETADDRINFO */
Pierre Ossman14263e12014-11-19 11:14:49 +0100297 case AF_INET:
Tim Waugh6ae42df2014-11-17 17:07:07 +0000298 return ntohs(sa.u.sin.sin_port);
Pierre Ossman14263e12014-11-19 11:14:49 +0100299 default:
300 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000301 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000302}
303
304char* TcpSocket::getPeerEndpoint() {
305 rfb::CharArray address; address.buf = getPeerAddress();
306 int port = getPeerPort();
307
308 int buflen = strlen(address.buf) + 32;
309 char* buffer = new char[buflen];
310 sprintf(buffer, "%s::%d", address.buf, port);
311 return buffer;
312}
313
314bool TcpSocket::sameMachine() {
Adam Tkac897814f2009-11-12 10:32:43 +0000315 vnc_sockaddr_t peeraddr, myaddr;
316 socklen_t addrlen;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000317
Adam Tkac897814f2009-11-12 10:32:43 +0000318 addrlen = sizeof(peeraddr);
319 if (getpeername(getFd(), &peeraddr.u.sa, &addrlen) < 0)
320 throw SocketException ("unable to get peer address", errorNumber);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000321
Adam Tkac897814f2009-11-12 10:32:43 +0000322 addrlen = sizeof(myaddr); /* need to reset, since getpeername overwrote */
323 if (getsockname(getFd(), &myaddr.u.sa, &addrlen) < 0)
324 throw SocketException ("unable to get my address", errorNumber);
325
326 if (peeraddr.u.sa.sa_family != myaddr.u.sa.sa_family)
327 return false;
328
329#ifdef HAVE_GETADDRINFO
330 if (peeraddr.u.sa.sa_family == AF_INET6)
331 return IN6_ARE_ADDR_EQUAL(&peeraddr.u.sin6.sin6_addr,
332 &myaddr.u.sin6.sin6_addr);
333#endif
334
Pierre Ossman14263e12014-11-19 11:14:49 +0100335 if (peeraddr.u.sa.sa_family == AF_INET)
336 return (peeraddr.u.sin.sin_addr.s_addr == myaddr.u.sin.sin_addr.s_addr);
337
338 // No idea what this is. Assume we're on different machines.
339 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000340}
341
342void TcpSocket::shutdown()
343{
344 Socket::shutdown();
345 ::shutdown(getFd(), 2);
346}
347
348bool TcpSocket::enableNagles(int sock, bool enable) {
349 int one = enable ? 0 : 1;
350 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
351 (char *)&one, sizeof(one)) < 0) {
352 int e = errorNumber;
353 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
354 return false;
355 }
356 return true;
357}
358
Pierre Ossman64069a92011-11-08 12:10:55 +0000359bool TcpSocket::cork(int sock, bool enable) {
360#ifndef TCP_CORK
361 return false;
362#else
363 int one = enable ? 1 : 0;
364 if (setsockopt(sock, IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0)
365 return false;
366 return true;
367#endif
368}
369
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000370bool TcpSocket::isSocket(int sock)
371{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000372 vnc_sockaddr_t sa;
373 socklen_t sa_size = sizeof(sa);
374 return getsockname(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000375}
376
377bool TcpSocket::isConnected(int sock)
378{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000379 vnc_sockaddr_t sa;
380 socklen_t sa_size = sizeof(sa);
381 return getpeername(sock, &sa.u.sa, &sa_size) >= 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000382}
383
384int TcpSocket::getSockPort(int sock)
385{
Tim Waugh6ae42df2014-11-17 17:07:07 +0000386 vnc_sockaddr_t sa;
387 socklen_t sa_size = sizeof(sa);
388 if (getsockname(sock, &sa.u.sa, &sa_size) < 0)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000389 return 0;
Tim Waugh6ae42df2014-11-17 17:07:07 +0000390
391 switch (sa.u.sa.sa_family) {
392#ifdef HAVE_GETADDRINFO
393 case AF_INET6:
394 return ntohs(sa.u.sin6.sin6_port);
395#endif /* HAVE_GETADDRINFO */
396
397 default:
398 return ntohs(sa.u.sin.sin_port);
399 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000400}
401
402
Adam Tkac93ff5db2010-02-05 15:54:10 +0000403TcpListener::TcpListener(const char *listenaddr, int port, bool localhostOnly,
404 int sock, bool close_) : closeFd(close_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000405{
406 if (sock != -1) {
407 fd = sock;
408 return;
409 }
410
Tim Waugh6abf3f42014-11-12 12:31:51 +0000411 bool use_ipv6;
Tim Waugh4561f7e2014-11-12 10:24:02 +0000412 int af;
Tim Waugh698371a2014-11-17 14:04:07 +0000413#ifdef HAVE_GETADDRINFO
Tim Waugh6abf3f42014-11-12 12:31:51 +0000414 use_ipv6 = true;
415 af = AF_INET6;
Tim Waugh4561f7e2014-11-12 10:24:02 +0000416#else
417 use_ipv6 = false;
418 af = AF_INET;
419#endif
420
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000421 initSockets();
Tim Waugh4561f7e2014-11-12 10:24:02 +0000422 if ((fd = socket(af, SOCK_STREAM, 0)) < 0) {
423 // - Socket creation failed
424 if (use_ipv6) {
425 // - Trying to make an IPv6-capable socket failed - try again, IPv4-only
426 use_ipv6 = false;
427 af = AF_INET;
428 fd = socket(af, SOCK_STREAM, 0);
429 }
430 if (fd < 0)
431 throw SocketException("unable to create listening socket", errorNumber);
432 } else {
433 // - Socket creation succeeded
434 if (use_ipv6) {
Tim Waugh698371a2014-11-17 14:04:07 +0000435#ifdef IPV6_V6ONLY
Tim Waugh4561f7e2014-11-12 10:24:02 +0000436 // - We made an IPv6-capable socket, and we need it to do IPv4 too
437 int opt = 0;
438 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
Tim Waugh698371a2014-11-17 14:04:07 +0000439#else
440 vlog.error("IPV6_V6ONLY support is missing. "
441 "IPv4 clients may not be able to connect.");
442#endif
Tim Waugh4561f7e2014-11-12 10:24:02 +0000443 }
444 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000445
446#ifndef WIN32
447 // - By default, close the socket on exec()
448 fcntl(fd, F_SETFD, FD_CLOEXEC);
449
450 int one = 1;
451 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
452 (char *)&one, sizeof(one)) < 0) {
453 int e = errorNumber;
454 closesocket(fd);
455 throw SocketException("unable to create listening socket", e);
456 }
457#endif
458
459 // - Bind it to the desired port
460 struct sockaddr_in addr;
Tim Waugh698371a2014-11-17 14:04:07 +0000461#ifdef HAVE_GETADDRINFO
Tim Waugh4561f7e2014-11-12 10:24:02 +0000462 struct sockaddr_in6 addr6;
Tim Waugh698371a2014-11-17 14:04:07 +0000463#endif
Tim Waugh4561f7e2014-11-12 10:24:02 +0000464 struct sockaddr *sa;
465 int sa_len;
Tim Waugh698371a2014-11-17 14:04:07 +0000466
467#ifdef HAVE_GETADDRINFO
Tim Waugh4561f7e2014-11-12 10:24:02 +0000468 if (use_ipv6) {
469 memset(&addr6, 0, (sa_len = sizeof(addr6)));
470 addr6.sin6_family = af;
471 addr6.sin6_port = htons(port);
Tim Waugh6abf3f42014-11-12 12:31:51 +0000472
473 if (localhostOnly)
474 addr6.sin6_addr = in6addr_loopback;
475 else if (listenaddr != NULL) {
476#ifdef HAVE_INET_PTON
477 if (inet_pton(AF_INET6, listenaddr, &addr6.sin6_addr) != 1)
478 use_ipv6 = false;
479#else
480 // Unable to parse without inet_pton
481 use_ipv6 = false;
482#endif
483 }
484
485 if (use_ipv6)
486 sa = (struct sockaddr *)&addr6;
487 }
Tim Waugh698371a2014-11-17 14:04:07 +0000488#endif
Tim Waugh6abf3f42014-11-12 12:31:51 +0000489
490 if (!use_ipv6) {
Tim Waugh4561f7e2014-11-12 10:24:02 +0000491 memset(&addr, 0, (sa_len = sizeof(addr)));
492 addr.sin_family = af;
493 addr.sin_port = htons(port);
Adam Tkac93ff5db2010-02-05 15:54:10 +0000494
Tim Waugh4561f7e2014-11-12 10:24:02 +0000495 if (localhostOnly) {
496 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
497 } else if (listenaddr != NULL) {
Pierre Ossman40659972010-02-12 09:19:32 +0000498#ifdef HAVE_INET_ATON
Tim Waugh4561f7e2014-11-12 10:24:02 +0000499 if (inet_aton(listenaddr, &addr.sin_addr) == 0)
Adam Tkac93ff5db2010-02-05 15:54:10 +0000500#else
Tim Waugh4561f7e2014-11-12 10:24:02 +0000501 /* Some systems (e.g. Windows) do not have inet_aton, sigh */
502 if ((addr.sin_addr.s_addr = inet_addr(listenaddr)) == INADDR_NONE)
Adam Tkac93ff5db2010-02-05 15:54:10 +0000503#endif
Tim Waugh4561f7e2014-11-12 10:24:02 +0000504 {
505 closesocket(fd);
506 throw Exception("invalid network interface address: %s", listenaddr);
507 }
508 } else
509 /* Bind to 0.0.0.0 by default. */
510 addr.sin_addr.s_addr = htonl(INADDR_ANY);
511
512 sa = (struct sockaddr *)&addr;
513 }
Adam Tkac93ff5db2010-02-05 15:54:10 +0000514
515 addr.sin_port = htons(port);
Tim Waugh4561f7e2014-11-12 10:24:02 +0000516 if (bind(fd, sa, sa_len) < 0) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000517 int e = errorNumber;
518 closesocket(fd);
519 throw SocketException("unable to bind listening socket", e);
520 }
521
522 // - Set it to be a listening socket
523 if (listen(fd, 5) < 0) {
524 int e = errorNumber;
525 closesocket(fd);
526 throw SocketException("unable to set socket to listening mode", e);
527 }
528}
529
530TcpListener::~TcpListener() {
531 if (closeFd) closesocket(fd);
532}
533
534void TcpListener::shutdown()
535{
536#ifdef WIN32
537 closesocket(getFd());
538#else
539 ::shutdown(getFd(), 2);
540#endif
541}
542
543
544Socket*
545TcpListener::accept() {
546 int new_sock = -1;
547
548 // Accept an incoming connection
549 if ((new_sock = ::accept(fd, 0, 0)) < 0)
550 throw SocketException("unable to accept new connection", errorNumber);
551
552#ifndef WIN32
553 // - By default, close the socket on exec()
554 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
555#endif
556
557 // Disable Nagle's algorithm, to reduce latency
558 TcpSocket::enableNagles(new_sock, false);
559
560 // Create the socket object & check connection is allowed
561 TcpSocket* s = new TcpSocket(new_sock);
562 if (filter && !filter->verifyConnection(s)) {
563 delete s;
564 return 0;
565 }
566 return s;
567}
568
569void TcpListener::getMyAddresses(std::list<char*>* result) {
Tim Waugh6ae42df2014-11-17 17:07:07 +0000570#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_PTON)
571 vnc_sockaddr_t sa;
572 struct addrinfo *ai, *current, hints;
573
574 memset(&hints, 0, sizeof(struct addrinfo));
575 hints.ai_family = AF_UNSPEC;
576 hints.ai_socktype = SOCK_STREAM;
577 hints.ai_canonname = NULL;
578 hints.ai_addr = NULL;
579 hints.ai_next = NULL;
580
581 if ((getaddrinfo(NULL, NULL, &hints, &ai)) != 0)
582 return;
583
584 for (current= ai; current != NULL; current = current->ai_next) {
585 if (current->ai_family != AF_INET && current->ai_family != AF_INET6)
586 continue;
587
588 char *addr = new char[INET6_ADDRSTRLEN];
589 inet_ntop(current->ai_family, current->ai_addr, addr, INET6_ADDRSTRLEN);
590 result->push_back(addr);
591 }
592 freeaddrinfo(ai);
593#else
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000594 const hostent* addrs = gethostbyname(0);
595 if (addrs == 0)
596 throw rdr::SystemException("gethostbyname", errorNumber);
597 if (addrs->h_addrtype != AF_INET)
598 throw rdr::Exception("getMyAddresses: bad family");
599 for (int i=0; addrs->h_addr_list[i] != 0; i++) {
600 const char* addrC = inet_ntoa(*((struct in_addr*)addrs->h_addr_list[i]));
601 char* addr = new char[strlen(addrC)+1];
602 strcpy(addr, addrC);
603 result->push_back(addr);
604 }
Tim Waugh6ae42df2014-11-17 17:07:07 +0000605#endif /* defined(HAVE_GETADDRINFO) && defined(HAVE_INET_PTON) */
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000606}
607
608int TcpListener::getMyPort() {
609 return TcpSocket::getSockPort(getFd());
610}
611
612
613TcpFilter::TcpFilter(const char* spec) {
614 rfb::CharArray tmp;
Adam Tkacd36b6262009-09-04 10:57:20 +0000615 tmp.buf = rfb::strDup(spec);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000616 while (tmp.buf) {
617 rfb::CharArray first;
618 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
619 if (strlen(first.buf))
620 filter.push_back(parsePattern(first.buf));
621 }
622}
623
624TcpFilter::~TcpFilter() {
625}
626
627
628static bool
629patternMatchIP(const TcpFilter::Pattern& pattern, const char* value) {
630 unsigned long address = inet_addr((char *)value);
631 if (address == INADDR_NONE) return false;
632 return ((pattern.address & pattern.mask) == (address & pattern.mask));
633}
634
635bool
636TcpFilter::verifyConnection(Socket* s) {
637 rfb::CharArray name;
638
Tim Waugh6ae42df2014-11-17 17:07:07 +0000639#ifdef HAVE_GETADDRINFO
640 vnc_sockaddr_t sa;
641 socklen_t sa_size = sizeof(sa);
642 if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0 ||
643 sa.u.sa.sa_family != AF_INET)
644 /* Matching only works for IPv4 */
645 return false;
646#endif /* HAVE_GETADDRINFO */
647
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000648 name.buf = s->getPeerAddress();
649 std::list<TcpFilter::Pattern>::iterator i;
650 for (i=filter.begin(); i!=filter.end(); i++) {
651 if (patternMatchIP(*i, name.buf)) {
652 switch ((*i).action) {
653 case Accept:
654 vlog.debug("ACCEPT %s", name.buf);
655 return true;
656 case Query:
657 vlog.debug("QUERY %s", name.buf);
658 s->setRequiresQuery();
659 return true;
660 case Reject:
661 vlog.debug("REJECT %s", name.buf);
662 return false;
663 }
664 }
665 }
666
667 vlog.debug("[REJECT] %s", name.buf);
668 return false;
669}
670
671
672TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
673 TcpFilter::Pattern pattern;
674
675 bool expandMask = false;
676 rfb::CharArray addr, mask;
677
678 if (rfb::strSplit(&p[1], '/', &addr.buf, &mask.buf)) {
679 if (rfb::strContains(mask.buf, '.')) {
680 pattern.mask = inet_addr(mask.buf);
681 } else {
682 pattern.mask = atoi(mask.buf);
683 expandMask = true;
684 }
685 } else {
686 pattern.mask = 32;
687 expandMask = true;
688 }
689 if (expandMask) {
690 unsigned long expanded = 0;
691 // *** check endianness!
692 for (int i=0; i<(int)pattern.mask; i++)
693 expanded |= 1<<(31-i);
694 pattern.mask = htonl(expanded);
695 }
696
697 pattern.address = inet_addr(addr.buf) & pattern.mask;
698 if ((pattern.address == INADDR_NONE) ||
699 (pattern.address == 0)) pattern.mask = 0;
700
701 switch(p[0]) {
702 case '+': pattern.action = TcpFilter::Accept; break;
703 case '-': pattern.action = TcpFilter::Reject; break;
704 case '?': pattern.action = TcpFilter::Query; break;
705 };
706
707 return pattern;
708}
709
710char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
711 in_addr tmp;
712 rfb::CharArray addr, mask;
713 tmp.s_addr = p.address;
Adam Tkacd36b6262009-09-04 10:57:20 +0000714 addr.buf = rfb::strDup(inet_ntoa(tmp));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000715 tmp.s_addr = p.mask;
Adam Tkacd36b6262009-09-04 10:57:20 +0000716 mask.buf = rfb::strDup(inet_ntoa(tmp));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000717 char* result = new char[strlen(addr.buf)+1+strlen(mask.buf)+1+1];
718 switch (p.action) {
719 case Accept: result[0] = '+'; break;
720 case Reject: result[0] = '-'; break;
721 case Query: result[0] = '?'; break;
722 };
723 result[1] = 0;
724 strcat(result, addr.buf);
725 strcat(result, "/");
726 strcat(result, mask.buf);
727 return result;
728}