blob: 04e0895029dad9271777387a3f7f9d2409b79858 [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>
26#define errorNumber WSAGetLastError()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000027#else
28#define errorNumber errno
29#define closesocket close
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <arpa/inet.h>
33#include <netinet/in.h>
34#include <netinet/tcp.h>
35#include <netdb.h>
36#include <unistd.h>
37#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>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000044#include <network/TcpSocket.h>
Adam Tkacbe4c3ac2008-12-10 16:42:33 +000045#include <os/net.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000046#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
56using namespace network;
57using namespace rdr;
58
Adam Tkac9cb6a422008-11-14 15:56:15 +000059typedef struct vnc_sockaddr {
60 union {
61 sockaddr sa;
62 sockaddr_in sin;
63#ifdef HAVE_GETADDRINFO
64 sockaddr_in6 sin6;
65#endif
66 } u;
67} vnc_sockaddr_t;
68
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000069static rfb::LogWriter vlog("TcpSocket");
70
71/* Tunnelling support. */
72int network::findFreeTcpPort (void)
73{
DRCb2618e52011-02-21 13:43:44 +000074 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000075 struct sockaddr_in addr;
76 memset(&addr, 0, sizeof(addr));
77 addr.sin_family = AF_INET;
78 addr.sin_addr.s_addr = INADDR_ANY;
79
80 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
81 throw SocketException ("unable to create socket", errorNumber);
82
DRCb2618e52011-02-21 13:43:44 +000083 addr.sin_port = 0;
84 if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) < 0)
85 throw SocketException ("unable to find free port", errorNumber);
86
87 socklen_t n = sizeof(addr);
88 if (getsockname (sock, (struct sockaddr *)&addr, &n) < 0)
89 throw SocketException ("unable to get port number", errorNumber);
90
91 closesocket (sock);
92 return ntohs(addr.sin_port);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000093}
94
95
96// -=- Socket initialisation
97static bool socketsInitialised = false;
98static void initSockets() {
99 if (socketsInitialised)
100 return;
101#ifdef WIN32
102 WORD requiredVersion = MAKEWORD(2,0);
103 WSADATA initResult;
104
105 if (WSAStartup(requiredVersion, &initResult) != 0)
106 throw SocketException("unable to initialise Winsock2", errorNumber);
107#else
108 signal(SIGPIPE, SIG_IGN);
109#endif
110 socketsInitialised = true;
111}
112
113
114// -=- TcpSocket
115
116TcpSocket::TcpSocket(int sock, bool close)
117 : Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close)
118{
119}
120
121TcpSocket::TcpSocket(const char *host, int port)
122 : closeFd(true)
123{
Adam Tkac9cb6a422008-11-14 15:56:15 +0000124 int sock, err, result, family;
125 vnc_sockaddr_t sa;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000126 socklen_t salen;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000127#ifdef HAVE_GETADDRINFO
128 struct addrinfo *ai, *current, hints;
129#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000130
131 // - Create a socket
132 initSockets();
Adam Tkac9cb6a422008-11-14 15:56:15 +0000133
134#ifdef HAVE_GETADDRINFO
135 memset(&hints, 0, sizeof(struct addrinfo));
136 hints.ai_family = AF_UNSPEC;
137 hints.ai_socktype = SOCK_STREAM;
138 hints.ai_canonname = NULL;
139 hints.ai_addr = NULL;
140 hints.ai_next = NULL;
141
142 if ((result = getaddrinfo(host, NULL, &hints, &ai)) != 0) {
143 throw Exception("unable to resolve host by name: %s",
144 gai_strerror(result));
145 }
146
147 for (current = ai; current != NULL; current = current->ai_next) {
148 family = current->ai_family;
149 if (family != AF_INET && family != AF_INET6)
150 continue;
151
152 salen = current->ai_addrlen;
153 memcpy(&sa, current->ai_addr, salen);
154
155 if (family == AF_INET)
156 sa.u.sin.sin_port = htons(port);
157 else
158 sa.u.sin6.sin6_port = htons(port);
159
160#else /* HAVE_GETADDRINFO */
161 family = AF_INET;
162 salen = sizeof(struct sockaddr_in);
163
164 /* Try processing the host as an IP address */
165 memset(&sa, 0, sizeof(sa));
166 sa.u.sin.sin_family = AF_INET;
167 sa.u.sin.sin_addr.s_addr = inet_addr((char *)host);
168 sa.u.sin.sin_port = htons(port);
169 if ((int)sa.u.sin.sin_addr.s_addr == -1) {
170 /* Host was not an IP address - try resolving as DNS name */
171 struct hostent *hostinfo;
172 hostinfo = gethostbyname((char *)host);
173 if (hostinfo && hostinfo->h_addr) {
174 sa.u.sin.sin_addr.s_addr = ((struct in_addr *)hostinfo->h_addr)->s_addr;
175 } else {
176 err = errorNumber;
177 throw SocketException("unable to resolve host by name", err);
178 }
179 }
180#endif /* HAVE_GETADDRINFO */
181
182 sock = socket (family, SOCK_STREAM, 0);
183 if (sock == -1) {
184 err = errorNumber;
185#ifdef HAVE_GETADDRINFO
186 freeaddrinfo(ai);
187#endif /* HAVE_GETADDRINFO */
188 throw SocketException("unable to create socket", err);
189 }
190
191 /* Attempt to connect to the remote host */
Adam Tkacc9cda3b2009-10-30 11:13:34 +0000192 while ((result = connect(sock, &sa.u.sa, salen)) == -1) {
Adam Tkac9cb6a422008-11-14 15:56:15 +0000193 err = errorNumber;
194#ifndef WIN32
195 if (err == EINTR)
196 continue;
197#endif
198 closesocket(sock);
199 break;
200 }
201
202#ifdef HAVE_GETADDRINFO
203 if (result == 0)
204 break;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000205 }
206
207 freeaddrinfo(ai);
208#endif /* HAVE_GETADDRINFO */
209
210 if (result == -1)
211 throw SocketException("unable connect to socket", err);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000212
213#ifndef WIN32
214 // - By default, close the socket on exec()
215 fcntl(sock, F_SETFD, FD_CLOEXEC);
216#endif
217
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000218 // Disable Nagle's algorithm, to reduce latency
219 enableNagles(sock, false);
220
221 // Create the input and output streams
222 instream = new FdInStream(sock);
223 outstream = new FdOutStream(sock);
224 ownStreams = true;
225}
226
227TcpSocket::~TcpSocket() {
228 if (closeFd)
229 closesocket(getFd());
230}
231
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000232int TcpSocket::getMyPort() {
233 return getSockPort(getFd());
234}
235
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000236char* TcpSocket::getPeerAddress() {
237 struct sockaddr_in info;
238 struct in_addr addr;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000239 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000240
241 getpeername(getFd(), (struct sockaddr *)&info, &info_size);
242 memcpy(&addr, &info.sin_addr, sizeof(addr));
243
244 char* name = inet_ntoa(addr);
245 if (name) {
Adam Tkacd36b6262009-09-04 10:57:20 +0000246 return rfb::strDup(name);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000247 } else {
Adam Tkacd36b6262009-09-04 10:57:20 +0000248 return rfb::strDup("");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000249 }
250}
251
252int TcpSocket::getPeerPort() {
253 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000254 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000255
256 getpeername(getFd(), (struct sockaddr *)&info, &info_size);
257 return ntohs(info.sin_port);
258}
259
260char* TcpSocket::getPeerEndpoint() {
261 rfb::CharArray address; address.buf = getPeerAddress();
262 int port = getPeerPort();
263
264 int buflen = strlen(address.buf) + 32;
265 char* buffer = new char[buflen];
266 sprintf(buffer, "%s::%d", address.buf, port);
267 return buffer;
268}
269
270bool TcpSocket::sameMachine() {
Adam Tkac897814f2009-11-12 10:32:43 +0000271 vnc_sockaddr_t peeraddr, myaddr;
272 socklen_t addrlen;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000273
Adam Tkac897814f2009-11-12 10:32:43 +0000274 addrlen = sizeof(peeraddr);
275 if (getpeername(getFd(), &peeraddr.u.sa, &addrlen) < 0)
276 throw SocketException ("unable to get peer address", errorNumber);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000277
Adam Tkac897814f2009-11-12 10:32:43 +0000278 addrlen = sizeof(myaddr); /* need to reset, since getpeername overwrote */
279 if (getsockname(getFd(), &myaddr.u.sa, &addrlen) < 0)
280 throw SocketException ("unable to get my address", errorNumber);
281
282 if (peeraddr.u.sa.sa_family != myaddr.u.sa.sa_family)
283 return false;
284
285#ifdef HAVE_GETADDRINFO
286 if (peeraddr.u.sa.sa_family == AF_INET6)
287 return IN6_ARE_ADDR_EQUAL(&peeraddr.u.sin6.sin6_addr,
288 &myaddr.u.sin6.sin6_addr);
289#endif
290
291 return (peeraddr.u.sin.sin_addr.s_addr == myaddr.u.sin.sin_addr.s_addr);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000292}
293
294void TcpSocket::shutdown()
295{
296 Socket::shutdown();
297 ::shutdown(getFd(), 2);
298}
299
300bool TcpSocket::enableNagles(int sock, bool enable) {
301 int one = enable ? 0 : 1;
302 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
303 (char *)&one, sizeof(one)) < 0) {
304 int e = errorNumber;
305 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
306 return false;
307 }
308 return true;
309}
310
311bool TcpSocket::isSocket(int sock)
312{
313 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000314 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000315 return getsockname(sock, (struct sockaddr *)&info, &info_size) >= 0;
316}
317
318bool TcpSocket::isConnected(int sock)
319{
320 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000321 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000322 return getpeername(sock, (struct sockaddr *)&info, &info_size) >= 0;
323}
324
325int TcpSocket::getSockPort(int sock)
326{
327 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000328 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000329 if (getsockname(sock, (struct sockaddr *)&info, &info_size) < 0)
330 return 0;
331 return ntohs(info.sin_port);
332}
333
334
Adam Tkac93ff5db2010-02-05 15:54:10 +0000335TcpListener::TcpListener(const char *listenaddr, int port, bool localhostOnly,
336 int sock, bool close_) : closeFd(close_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000337{
338 if (sock != -1) {
339 fd = sock;
340 return;
341 }
342
343 initSockets();
344 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
345 throw SocketException("unable to create listening socket", errorNumber);
346
347#ifndef WIN32
348 // - By default, close the socket on exec()
349 fcntl(fd, F_SETFD, FD_CLOEXEC);
350
351 int one = 1;
352 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
353 (char *)&one, sizeof(one)) < 0) {
354 int e = errorNumber;
355 closesocket(fd);
356 throw SocketException("unable to create listening socket", e);
357 }
358#endif
359
360 // - Bind it to the desired port
361 struct sockaddr_in addr;
362 memset(&addr, 0, sizeof(addr));
363 addr.sin_family = AF_INET;
Adam Tkac93ff5db2010-02-05 15:54:10 +0000364
365 if (localhostOnly) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000366 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
Adam Tkac93ff5db2010-02-05 15:54:10 +0000367 } else if (listenaddr != NULL) {
Pierre Ossman40659972010-02-12 09:19:32 +0000368#ifdef HAVE_INET_ATON
Adam Tkac93ff5db2010-02-05 15:54:10 +0000369 if (inet_aton(listenaddr, &addr.sin_addr) == 0)
370#else
Pierre Ossman40659972010-02-12 09:19:32 +0000371 /* Some systems (e.g. Windows) do not have inet_aton, sigh */
Adam Tkac93ff5db2010-02-05 15:54:10 +0000372 if ((addr.sin_addr.s_addr = inet_addr(listenaddr)) == INADDR_NONE)
373#endif
374 {
375 closesocket(fd);
376 throw Exception("invalid network interface address: %s", listenaddr);
377 }
378 } else
379 addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Bind to 0.0.0.0 by default. */
380
381 addr.sin_port = htons(port);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000382 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
383 int e = errorNumber;
384 closesocket(fd);
385 throw SocketException("unable to bind listening socket", e);
386 }
387
388 // - Set it to be a listening socket
389 if (listen(fd, 5) < 0) {
390 int e = errorNumber;
391 closesocket(fd);
392 throw SocketException("unable to set socket to listening mode", e);
393 }
394}
395
396TcpListener::~TcpListener() {
397 if (closeFd) closesocket(fd);
398}
399
400void TcpListener::shutdown()
401{
402#ifdef WIN32
403 closesocket(getFd());
404#else
405 ::shutdown(getFd(), 2);
406#endif
407}
408
409
410Socket*
411TcpListener::accept() {
412 int new_sock = -1;
413
414 // Accept an incoming connection
415 if ((new_sock = ::accept(fd, 0, 0)) < 0)
416 throw SocketException("unable to accept new connection", errorNumber);
417
418#ifndef WIN32
419 // - By default, close the socket on exec()
420 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
421#endif
422
423 // Disable Nagle's algorithm, to reduce latency
424 TcpSocket::enableNagles(new_sock, false);
425
426 // Create the socket object & check connection is allowed
427 TcpSocket* s = new TcpSocket(new_sock);
428 if (filter && !filter->verifyConnection(s)) {
429 delete s;
430 return 0;
431 }
432 return s;
433}
434
435void TcpListener::getMyAddresses(std::list<char*>* result) {
436 const hostent* addrs = gethostbyname(0);
437 if (addrs == 0)
438 throw rdr::SystemException("gethostbyname", errorNumber);
439 if (addrs->h_addrtype != AF_INET)
440 throw rdr::Exception("getMyAddresses: bad family");
441 for (int i=0; addrs->h_addr_list[i] != 0; i++) {
442 const char* addrC = inet_ntoa(*((struct in_addr*)addrs->h_addr_list[i]));
443 char* addr = new char[strlen(addrC)+1];
444 strcpy(addr, addrC);
445 result->push_back(addr);
446 }
447}
448
449int TcpListener::getMyPort() {
450 return TcpSocket::getSockPort(getFd());
451}
452
453
454TcpFilter::TcpFilter(const char* spec) {
455 rfb::CharArray tmp;
Adam Tkacd36b6262009-09-04 10:57:20 +0000456 tmp.buf = rfb::strDup(spec);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000457 while (tmp.buf) {
458 rfb::CharArray first;
459 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
460 if (strlen(first.buf))
461 filter.push_back(parsePattern(first.buf));
462 }
463}
464
465TcpFilter::~TcpFilter() {
466}
467
468
469static bool
470patternMatchIP(const TcpFilter::Pattern& pattern, const char* value) {
471 unsigned long address = inet_addr((char *)value);
472 if (address == INADDR_NONE) return false;
473 return ((pattern.address & pattern.mask) == (address & pattern.mask));
474}
475
476bool
477TcpFilter::verifyConnection(Socket* s) {
478 rfb::CharArray name;
479
480 name.buf = s->getPeerAddress();
481 std::list<TcpFilter::Pattern>::iterator i;
482 for (i=filter.begin(); i!=filter.end(); i++) {
483 if (patternMatchIP(*i, name.buf)) {
484 switch ((*i).action) {
485 case Accept:
486 vlog.debug("ACCEPT %s", name.buf);
487 return true;
488 case Query:
489 vlog.debug("QUERY %s", name.buf);
490 s->setRequiresQuery();
491 return true;
492 case Reject:
493 vlog.debug("REJECT %s", name.buf);
494 return false;
495 }
496 }
497 }
498
499 vlog.debug("[REJECT] %s", name.buf);
500 return false;
501}
502
503
504TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
505 TcpFilter::Pattern pattern;
506
507 bool expandMask = false;
508 rfb::CharArray addr, mask;
509
510 if (rfb::strSplit(&p[1], '/', &addr.buf, &mask.buf)) {
511 if (rfb::strContains(mask.buf, '.')) {
512 pattern.mask = inet_addr(mask.buf);
513 } else {
514 pattern.mask = atoi(mask.buf);
515 expandMask = true;
516 }
517 } else {
518 pattern.mask = 32;
519 expandMask = true;
520 }
521 if (expandMask) {
522 unsigned long expanded = 0;
523 // *** check endianness!
524 for (int i=0; i<(int)pattern.mask; i++)
525 expanded |= 1<<(31-i);
526 pattern.mask = htonl(expanded);
527 }
528
529 pattern.address = inet_addr(addr.buf) & pattern.mask;
530 if ((pattern.address == INADDR_NONE) ||
531 (pattern.address == 0)) pattern.mask = 0;
532
533 switch(p[0]) {
534 case '+': pattern.action = TcpFilter::Accept; break;
535 case '-': pattern.action = TcpFilter::Reject; break;
536 case '?': pattern.action = TcpFilter::Query; break;
537 };
538
539 return pattern;
540}
541
542char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
543 in_addr tmp;
544 rfb::CharArray addr, mask;
545 tmp.s_addr = p.address;
Adam Tkacd36b6262009-09-04 10:57:20 +0000546 addr.buf = rfb::strDup(inet_ntoa(tmp));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000547 tmp.s_addr = p.mask;
Adam Tkacd36b6262009-09-04 10:57:20 +0000548 mask.buf = rfb::strDup(inet_ntoa(tmp));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000549 char* result = new char[strlen(addr.buf)+1+strlen(mask.buf)+1+1];
550 switch (p.action) {
551 case Accept: result[0] = '+'; break;
552 case Reject: result[0] = '-'; break;
553 case Query: result[0] = '?'; break;
554 };
555 result[1] = 0;
556 strcat(result, addr.buf);
557 strcat(result, "/");
558 strcat(result, mask.buf);
559 return result;
560}