blob: e494a1128a75c6e478f1f52c046df0d7e641c1d3 [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>
Adam Tkacbe4c3ac2008-12-10 16:42:33 +000046#include <os/net.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000047#include <rfb/util.h>
48#include <rfb/LogWriter.h>
49
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
77/* Tunnelling support. */
78int network::findFreeTcpPort (void)
79{
DRCb2618e52011-02-21 13:43:44 +000080 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000081 struct sockaddr_in addr;
82 memset(&addr, 0, sizeof(addr));
83 addr.sin_family = AF_INET;
84 addr.sin_addr.s_addr = INADDR_ANY;
85
86 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
87 throw SocketException ("unable to create socket", errorNumber);
88
DRCb2618e52011-02-21 13:43:44 +000089 addr.sin_port = 0;
90 if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) < 0)
91 throw SocketException ("unable to find free port", errorNumber);
92
93 socklen_t n = sizeof(addr);
94 if (getsockname (sock, (struct sockaddr *)&addr, &n) < 0)
95 throw SocketException ("unable to get port number", errorNumber);
96
97 closesocket (sock);
98 return ntohs(addr.sin_port);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000099}
100
101
102// -=- Socket initialisation
103static bool socketsInitialised = false;
104static void initSockets() {
105 if (socketsInitialised)
106 return;
107#ifdef WIN32
108 WORD requiredVersion = MAKEWORD(2,0);
109 WSADATA initResult;
110
111 if (WSAStartup(requiredVersion, &initResult) != 0)
112 throw SocketException("unable to initialise Winsock2", errorNumber);
113#else
114 signal(SIGPIPE, SIG_IGN);
115#endif
116 socketsInitialised = true;
117}
118
119
120// -=- TcpSocket
121
122TcpSocket::TcpSocket(int sock, bool close)
123 : Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close)
124{
125}
126
127TcpSocket::TcpSocket(const char *host, int port)
128 : closeFd(true)
129{
Adam Tkac9cb6a422008-11-14 15:56:15 +0000130 int sock, err, result, family;
131 vnc_sockaddr_t sa;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000132 socklen_t salen;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000133#ifdef HAVE_GETADDRINFO
134 struct addrinfo *ai, *current, hints;
135#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000136
137 // - Create a socket
138 initSockets();
Adam Tkac9cb6a422008-11-14 15:56:15 +0000139
140#ifdef HAVE_GETADDRINFO
141 memset(&hints, 0, sizeof(struct addrinfo));
142 hints.ai_family = AF_UNSPEC;
143 hints.ai_socktype = SOCK_STREAM;
144 hints.ai_canonname = NULL;
145 hints.ai_addr = NULL;
146 hints.ai_next = NULL;
147
148 if ((result = getaddrinfo(host, NULL, &hints, &ai)) != 0) {
149 throw Exception("unable to resolve host by name: %s",
150 gai_strerror(result));
151 }
152
153 for (current = ai; current != NULL; current = current->ai_next) {
154 family = current->ai_family;
155 if (family != AF_INET && family != AF_INET6)
156 continue;
157
158 salen = current->ai_addrlen;
159 memcpy(&sa, current->ai_addr, salen);
160
161 if (family == AF_INET)
162 sa.u.sin.sin_port = htons(port);
163 else
164 sa.u.sin6.sin6_port = htons(port);
165
166#else /* HAVE_GETADDRINFO */
167 family = AF_INET;
168 salen = sizeof(struct sockaddr_in);
169
170 /* Try processing the host as an IP address */
171 memset(&sa, 0, sizeof(sa));
172 sa.u.sin.sin_family = AF_INET;
173 sa.u.sin.sin_addr.s_addr = inet_addr((char *)host);
174 sa.u.sin.sin_port = htons(port);
175 if ((int)sa.u.sin.sin_addr.s_addr == -1) {
176 /* Host was not an IP address - try resolving as DNS name */
177 struct hostent *hostinfo;
178 hostinfo = gethostbyname((char *)host);
179 if (hostinfo && hostinfo->h_addr) {
180 sa.u.sin.sin_addr.s_addr = ((struct in_addr *)hostinfo->h_addr)->s_addr;
181 } else {
182 err = errorNumber;
183 throw SocketException("unable to resolve host by name", err);
184 }
185 }
186#endif /* HAVE_GETADDRINFO */
187
188 sock = socket (family, SOCK_STREAM, 0);
189 if (sock == -1) {
190 err = errorNumber;
191#ifdef HAVE_GETADDRINFO
192 freeaddrinfo(ai);
193#endif /* HAVE_GETADDRINFO */
194 throw SocketException("unable to create socket", err);
195 }
196
197 /* Attempt to connect to the remote host */
Adam Tkacc9cda3b2009-10-30 11:13:34 +0000198 while ((result = connect(sock, &sa.u.sa, salen)) == -1) {
Adam Tkac9cb6a422008-11-14 15:56:15 +0000199 err = errorNumber;
200#ifndef WIN32
201 if (err == EINTR)
202 continue;
203#endif
204 closesocket(sock);
205 break;
206 }
207
208#ifdef HAVE_GETADDRINFO
209 if (result == 0)
210 break;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000211 }
212
213 freeaddrinfo(ai);
214#endif /* HAVE_GETADDRINFO */
215
216 if (result == -1)
217 throw SocketException("unable connect to socket", err);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000218
219#ifndef WIN32
220 // - By default, close the socket on exec()
221 fcntl(sock, F_SETFD, FD_CLOEXEC);
222#endif
223
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000224 // Disable Nagle's algorithm, to reduce latency
225 enableNagles(sock, false);
226
227 // Create the input and output streams
228 instream = new FdInStream(sock);
229 outstream = new FdOutStream(sock);
230 ownStreams = true;
231}
232
233TcpSocket::~TcpSocket() {
234 if (closeFd)
235 closesocket(getFd());
236}
237
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000238int TcpSocket::getMyPort() {
239 return getSockPort(getFd());
240}
241
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000242char* TcpSocket::getPeerAddress() {
243 struct sockaddr_in info;
244 struct in_addr addr;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000245 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000246
247 getpeername(getFd(), (struct sockaddr *)&info, &info_size);
248 memcpy(&addr, &info.sin_addr, sizeof(addr));
249
250 char* name = inet_ntoa(addr);
251 if (name) {
Adam Tkacd36b6262009-09-04 10:57:20 +0000252 return rfb::strDup(name);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000253 } else {
Adam Tkacd36b6262009-09-04 10:57:20 +0000254 return rfb::strDup("");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000255 }
256}
257
258int TcpSocket::getPeerPort() {
259 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000260 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000261
262 getpeername(getFd(), (struct sockaddr *)&info, &info_size);
263 return ntohs(info.sin_port);
264}
265
266char* TcpSocket::getPeerEndpoint() {
267 rfb::CharArray address; address.buf = getPeerAddress();
268 int port = getPeerPort();
269
270 int buflen = strlen(address.buf) + 32;
271 char* buffer = new char[buflen];
272 sprintf(buffer, "%s::%d", address.buf, port);
273 return buffer;
274}
275
276bool TcpSocket::sameMachine() {
Adam Tkac897814f2009-11-12 10:32:43 +0000277 vnc_sockaddr_t peeraddr, myaddr;
278 socklen_t addrlen;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000279
Adam Tkac897814f2009-11-12 10:32:43 +0000280 addrlen = sizeof(peeraddr);
281 if (getpeername(getFd(), &peeraddr.u.sa, &addrlen) < 0)
282 throw SocketException ("unable to get peer address", errorNumber);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000283
Adam Tkac897814f2009-11-12 10:32:43 +0000284 addrlen = sizeof(myaddr); /* need to reset, since getpeername overwrote */
285 if (getsockname(getFd(), &myaddr.u.sa, &addrlen) < 0)
286 throw SocketException ("unable to get my address", errorNumber);
287
288 if (peeraddr.u.sa.sa_family != myaddr.u.sa.sa_family)
289 return false;
290
291#ifdef HAVE_GETADDRINFO
292 if (peeraddr.u.sa.sa_family == AF_INET6)
293 return IN6_ARE_ADDR_EQUAL(&peeraddr.u.sin6.sin6_addr,
294 &myaddr.u.sin6.sin6_addr);
295#endif
296
297 return (peeraddr.u.sin.sin_addr.s_addr == myaddr.u.sin.sin_addr.s_addr);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000298}
299
300void TcpSocket::shutdown()
301{
302 Socket::shutdown();
303 ::shutdown(getFd(), 2);
304}
305
306bool TcpSocket::enableNagles(int sock, bool enable) {
307 int one = enable ? 0 : 1;
308 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
309 (char *)&one, sizeof(one)) < 0) {
310 int e = errorNumber;
311 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
312 return false;
313 }
314 return true;
315}
316
Pierre Ossman64069a92011-11-08 12:10:55 +0000317bool TcpSocket::cork(int sock, bool enable) {
318#ifndef TCP_CORK
319 return false;
320#else
321 int one = enable ? 1 : 0;
322 if (setsockopt(sock, IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0)
323 return false;
324 return true;
325#endif
326}
327
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000328bool TcpSocket::isSocket(int sock)
329{
330 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000331 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000332 return getsockname(sock, (struct sockaddr *)&info, &info_size) >= 0;
333}
334
335bool TcpSocket::isConnected(int sock)
336{
337 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000338 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000339 return getpeername(sock, (struct sockaddr *)&info, &info_size) >= 0;
340}
341
342int TcpSocket::getSockPort(int sock)
343{
344 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000345 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000346 if (getsockname(sock, (struct sockaddr *)&info, &info_size) < 0)
347 return 0;
348 return ntohs(info.sin_port);
349}
350
351
Adam Tkac93ff5db2010-02-05 15:54:10 +0000352TcpListener::TcpListener(const char *listenaddr, int port, bool localhostOnly,
353 int sock, bool close_) : closeFd(close_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000354{
355 if (sock != -1) {
356 fd = sock;
357 return;
358 }
359
360 initSockets();
361 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
362 throw SocketException("unable to create listening socket", errorNumber);
363
364#ifndef WIN32
365 // - By default, close the socket on exec()
366 fcntl(fd, F_SETFD, FD_CLOEXEC);
367
368 int one = 1;
369 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
370 (char *)&one, sizeof(one)) < 0) {
371 int e = errorNumber;
372 closesocket(fd);
373 throw SocketException("unable to create listening socket", e);
374 }
375#endif
376
377 // - Bind it to the desired port
378 struct sockaddr_in addr;
379 memset(&addr, 0, sizeof(addr));
380 addr.sin_family = AF_INET;
Adam Tkac93ff5db2010-02-05 15:54:10 +0000381
382 if (localhostOnly) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000383 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
Adam Tkac93ff5db2010-02-05 15:54:10 +0000384 } else if (listenaddr != NULL) {
Pierre Ossman40659972010-02-12 09:19:32 +0000385#ifdef HAVE_INET_ATON
Adam Tkac93ff5db2010-02-05 15:54:10 +0000386 if (inet_aton(listenaddr, &addr.sin_addr) == 0)
387#else
Pierre Ossman40659972010-02-12 09:19:32 +0000388 /* Some systems (e.g. Windows) do not have inet_aton, sigh */
Adam Tkac93ff5db2010-02-05 15:54:10 +0000389 if ((addr.sin_addr.s_addr = inet_addr(listenaddr)) == INADDR_NONE)
390#endif
391 {
392 closesocket(fd);
393 throw Exception("invalid network interface address: %s", listenaddr);
394 }
395 } else
396 addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Bind to 0.0.0.0 by default. */
397
398 addr.sin_port = htons(port);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000399 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
400 int e = errorNumber;
401 closesocket(fd);
402 throw SocketException("unable to bind listening socket", e);
403 }
404
405 // - Set it to be a listening socket
406 if (listen(fd, 5) < 0) {
407 int e = errorNumber;
408 closesocket(fd);
409 throw SocketException("unable to set socket to listening mode", e);
410 }
411}
412
413TcpListener::~TcpListener() {
414 if (closeFd) closesocket(fd);
415}
416
417void TcpListener::shutdown()
418{
419#ifdef WIN32
420 closesocket(getFd());
421#else
422 ::shutdown(getFd(), 2);
423#endif
424}
425
426
427Socket*
428TcpListener::accept() {
429 int new_sock = -1;
430
431 // Accept an incoming connection
432 if ((new_sock = ::accept(fd, 0, 0)) < 0)
433 throw SocketException("unable to accept new connection", errorNumber);
434
435#ifndef WIN32
436 // - By default, close the socket on exec()
437 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
438#endif
439
440 // Disable Nagle's algorithm, to reduce latency
441 TcpSocket::enableNagles(new_sock, false);
442
443 // Create the socket object & check connection is allowed
444 TcpSocket* s = new TcpSocket(new_sock);
445 if (filter && !filter->verifyConnection(s)) {
446 delete s;
447 return 0;
448 }
449 return s;
450}
451
452void TcpListener::getMyAddresses(std::list<char*>* result) {
453 const hostent* addrs = gethostbyname(0);
454 if (addrs == 0)
455 throw rdr::SystemException("gethostbyname", errorNumber);
456 if (addrs->h_addrtype != AF_INET)
457 throw rdr::Exception("getMyAddresses: bad family");
458 for (int i=0; addrs->h_addr_list[i] != 0; i++) {
459 const char* addrC = inet_ntoa(*((struct in_addr*)addrs->h_addr_list[i]));
460 char* addr = new char[strlen(addrC)+1];
461 strcpy(addr, addrC);
462 result->push_back(addr);
463 }
464}
465
466int TcpListener::getMyPort() {
467 return TcpSocket::getSockPort(getFd());
468}
469
470
471TcpFilter::TcpFilter(const char* spec) {
472 rfb::CharArray tmp;
Adam Tkacd36b6262009-09-04 10:57:20 +0000473 tmp.buf = rfb::strDup(spec);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000474 while (tmp.buf) {
475 rfb::CharArray first;
476 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
477 if (strlen(first.buf))
478 filter.push_back(parsePattern(first.buf));
479 }
480}
481
482TcpFilter::~TcpFilter() {
483}
484
485
486static bool
487patternMatchIP(const TcpFilter::Pattern& pattern, const char* value) {
488 unsigned long address = inet_addr((char *)value);
489 if (address == INADDR_NONE) return false;
490 return ((pattern.address & pattern.mask) == (address & pattern.mask));
491}
492
493bool
494TcpFilter::verifyConnection(Socket* s) {
495 rfb::CharArray name;
496
497 name.buf = s->getPeerAddress();
498 std::list<TcpFilter::Pattern>::iterator i;
499 for (i=filter.begin(); i!=filter.end(); i++) {
500 if (patternMatchIP(*i, name.buf)) {
501 switch ((*i).action) {
502 case Accept:
503 vlog.debug("ACCEPT %s", name.buf);
504 return true;
505 case Query:
506 vlog.debug("QUERY %s", name.buf);
507 s->setRequiresQuery();
508 return true;
509 case Reject:
510 vlog.debug("REJECT %s", name.buf);
511 return false;
512 }
513 }
514 }
515
516 vlog.debug("[REJECT] %s", name.buf);
517 return false;
518}
519
520
521TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
522 TcpFilter::Pattern pattern;
523
524 bool expandMask = false;
525 rfb::CharArray addr, mask;
526
527 if (rfb::strSplit(&p[1], '/', &addr.buf, &mask.buf)) {
528 if (rfb::strContains(mask.buf, '.')) {
529 pattern.mask = inet_addr(mask.buf);
530 } else {
531 pattern.mask = atoi(mask.buf);
532 expandMask = true;
533 }
534 } else {
535 pattern.mask = 32;
536 expandMask = true;
537 }
538 if (expandMask) {
539 unsigned long expanded = 0;
540 // *** check endianness!
541 for (int i=0; i<(int)pattern.mask; i++)
542 expanded |= 1<<(31-i);
543 pattern.mask = htonl(expanded);
544 }
545
546 pattern.address = inet_addr(addr.buf) & pattern.mask;
547 if ((pattern.address == INADDR_NONE) ||
548 (pattern.address == 0)) pattern.mask = 0;
549
550 switch(p[0]) {
551 case '+': pattern.action = TcpFilter::Accept; break;
552 case '-': pattern.action = TcpFilter::Reject; break;
553 case '?': pattern.action = TcpFilter::Query; break;
554 };
555
556 return pattern;
557}
558
559char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
560 in_addr tmp;
561 rfb::CharArray addr, mask;
562 tmp.s_addr = p.address;
Adam Tkacd36b6262009-09-04 10:57:20 +0000563 addr.buf = rfb::strDup(inet_ntoa(tmp));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000564 tmp.s_addr = p.mask;
Adam Tkacd36b6262009-09-04 10:57:20 +0000565 mask.buf = rfb::strDup(inet_ntoa(tmp));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000566 char* result = new char[strlen(addr.buf)+1+strlen(mask.buf)+1+1];
567 switch (p.action) {
568 case Accept: result[0] = '+'; break;
569 case Reject: result[0] = '-'; break;
570 case Query: result[0] = '?'; break;
571 };
572 result[1] = 0;
573 strcat(result, addr.buf);
574 strcat(result, "/");
575 strcat(result, mask.buf);
576 return result;
577}