blob: 23cc49bc5746e56b2cafecb7367252df5ce30e63 [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
57using namespace network;
58using namespace rdr;
59
Adam Tkac9cb6a422008-11-14 15:56:15 +000060typedef struct vnc_sockaddr {
61 union {
62 sockaddr sa;
63 sockaddr_in sin;
64#ifdef HAVE_GETADDRINFO
65 sockaddr_in6 sin6;
66#endif
67 } u;
68} vnc_sockaddr_t;
69
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000070static rfb::LogWriter vlog("TcpSocket");
71
72/* Tunnelling support. */
73int network::findFreeTcpPort (void)
74{
DRCb2618e52011-02-21 13:43:44 +000075 int sock;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000076 struct sockaddr_in addr;
77 memset(&addr, 0, sizeof(addr));
78 addr.sin_family = AF_INET;
79 addr.sin_addr.s_addr = INADDR_ANY;
80
81 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
82 throw SocketException ("unable to create socket", errorNumber);
83
DRCb2618e52011-02-21 13:43:44 +000084 addr.sin_port = 0;
85 if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) < 0)
86 throw SocketException ("unable to find free port", errorNumber);
87
88 socklen_t n = sizeof(addr);
89 if (getsockname (sock, (struct sockaddr *)&addr, &n) < 0)
90 throw SocketException ("unable to get port number", errorNumber);
91
92 closesocket (sock);
93 return ntohs(addr.sin_port);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000094}
95
96
97// -=- Socket initialisation
98static bool socketsInitialised = false;
99static void initSockets() {
100 if (socketsInitialised)
101 return;
102#ifdef WIN32
103 WORD requiredVersion = MAKEWORD(2,0);
104 WSADATA initResult;
105
106 if (WSAStartup(requiredVersion, &initResult) != 0)
107 throw SocketException("unable to initialise Winsock2", errorNumber);
108#else
109 signal(SIGPIPE, SIG_IGN);
110#endif
111 socketsInitialised = true;
112}
113
114
115// -=- TcpSocket
116
117TcpSocket::TcpSocket(int sock, bool close)
118 : Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close)
119{
120}
121
122TcpSocket::TcpSocket(const char *host, int port)
123 : closeFd(true)
124{
Adam Tkac9cb6a422008-11-14 15:56:15 +0000125 int sock, err, result, family;
126 vnc_sockaddr_t sa;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000127 socklen_t salen;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000128#ifdef HAVE_GETADDRINFO
129 struct addrinfo *ai, *current, hints;
130#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000131
132 // - Create a socket
133 initSockets();
Adam Tkac9cb6a422008-11-14 15:56:15 +0000134
135#ifdef HAVE_GETADDRINFO
136 memset(&hints, 0, sizeof(struct addrinfo));
137 hints.ai_family = AF_UNSPEC;
138 hints.ai_socktype = SOCK_STREAM;
139 hints.ai_canonname = NULL;
140 hints.ai_addr = NULL;
141 hints.ai_next = NULL;
142
143 if ((result = getaddrinfo(host, NULL, &hints, &ai)) != 0) {
144 throw Exception("unable to resolve host by name: %s",
145 gai_strerror(result));
146 }
147
148 for (current = ai; current != NULL; current = current->ai_next) {
149 family = current->ai_family;
150 if (family != AF_INET && family != AF_INET6)
151 continue;
152
153 salen = current->ai_addrlen;
154 memcpy(&sa, current->ai_addr, salen);
155
156 if (family == AF_INET)
157 sa.u.sin.sin_port = htons(port);
158 else
159 sa.u.sin6.sin6_port = htons(port);
160
161#else /* HAVE_GETADDRINFO */
162 family = AF_INET;
163 salen = sizeof(struct sockaddr_in);
164
165 /* Try processing the host as an IP address */
166 memset(&sa, 0, sizeof(sa));
167 sa.u.sin.sin_family = AF_INET;
168 sa.u.sin.sin_addr.s_addr = inet_addr((char *)host);
169 sa.u.sin.sin_port = htons(port);
170 if ((int)sa.u.sin.sin_addr.s_addr == -1) {
171 /* Host was not an IP address - try resolving as DNS name */
172 struct hostent *hostinfo;
173 hostinfo = gethostbyname((char *)host);
174 if (hostinfo && hostinfo->h_addr) {
175 sa.u.sin.sin_addr.s_addr = ((struct in_addr *)hostinfo->h_addr)->s_addr;
176 } else {
177 err = errorNumber;
178 throw SocketException("unable to resolve host by name", err);
179 }
180 }
181#endif /* HAVE_GETADDRINFO */
182
183 sock = socket (family, SOCK_STREAM, 0);
184 if (sock == -1) {
185 err = errorNumber;
186#ifdef HAVE_GETADDRINFO
187 freeaddrinfo(ai);
188#endif /* HAVE_GETADDRINFO */
189 throw SocketException("unable to create socket", err);
190 }
191
192 /* Attempt to connect to the remote host */
Adam Tkacc9cda3b2009-10-30 11:13:34 +0000193 while ((result = connect(sock, &sa.u.sa, salen)) == -1) {
Adam Tkac9cb6a422008-11-14 15:56:15 +0000194 err = errorNumber;
195#ifndef WIN32
196 if (err == EINTR)
197 continue;
198#endif
199 closesocket(sock);
200 break;
201 }
202
203#ifdef HAVE_GETADDRINFO
204 if (result == 0)
205 break;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000206 }
207
208 freeaddrinfo(ai);
209#endif /* HAVE_GETADDRINFO */
210
211 if (result == -1)
212 throw SocketException("unable connect to socket", err);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000213
214#ifndef WIN32
215 // - By default, close the socket on exec()
216 fcntl(sock, F_SETFD, FD_CLOEXEC);
217#endif
218
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000219 // Disable Nagle's algorithm, to reduce latency
220 enableNagles(sock, false);
221
222 // Create the input and output streams
223 instream = new FdInStream(sock);
224 outstream = new FdOutStream(sock);
225 ownStreams = true;
226}
227
228TcpSocket::~TcpSocket() {
229 if (closeFd)
230 closesocket(getFd());
231}
232
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000233int TcpSocket::getMyPort() {
234 return getSockPort(getFd());
235}
236
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000237char* TcpSocket::getPeerAddress() {
238 struct sockaddr_in info;
239 struct in_addr addr;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000240 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000241
242 getpeername(getFd(), (struct sockaddr *)&info, &info_size);
243 memcpy(&addr, &info.sin_addr, sizeof(addr));
244
245 char* name = inet_ntoa(addr);
246 if (name) {
Adam Tkacd36b6262009-09-04 10:57:20 +0000247 return rfb::strDup(name);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000248 } else {
Adam Tkacd36b6262009-09-04 10:57:20 +0000249 return rfb::strDup("");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000250 }
251}
252
253int TcpSocket::getPeerPort() {
254 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000255 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000256
257 getpeername(getFd(), (struct sockaddr *)&info, &info_size);
258 return ntohs(info.sin_port);
259}
260
261char* TcpSocket::getPeerEndpoint() {
262 rfb::CharArray address; address.buf = getPeerAddress();
263 int port = getPeerPort();
264
265 int buflen = strlen(address.buf) + 32;
266 char* buffer = new char[buflen];
267 sprintf(buffer, "%s::%d", address.buf, port);
268 return buffer;
269}
270
271bool TcpSocket::sameMachine() {
Adam Tkac897814f2009-11-12 10:32:43 +0000272 vnc_sockaddr_t peeraddr, myaddr;
273 socklen_t addrlen;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000274
Adam Tkac897814f2009-11-12 10:32:43 +0000275 addrlen = sizeof(peeraddr);
276 if (getpeername(getFd(), &peeraddr.u.sa, &addrlen) < 0)
277 throw SocketException ("unable to get peer address", errorNumber);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000278
Adam Tkac897814f2009-11-12 10:32:43 +0000279 addrlen = sizeof(myaddr); /* need to reset, since getpeername overwrote */
280 if (getsockname(getFd(), &myaddr.u.sa, &addrlen) < 0)
281 throw SocketException ("unable to get my address", errorNumber);
282
283 if (peeraddr.u.sa.sa_family != myaddr.u.sa.sa_family)
284 return false;
285
286#ifdef HAVE_GETADDRINFO
287 if (peeraddr.u.sa.sa_family == AF_INET6)
288 return IN6_ARE_ADDR_EQUAL(&peeraddr.u.sin6.sin6_addr,
289 &myaddr.u.sin6.sin6_addr);
290#endif
291
292 return (peeraddr.u.sin.sin_addr.s_addr == myaddr.u.sin.sin_addr.s_addr);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000293}
294
295void TcpSocket::shutdown()
296{
297 Socket::shutdown();
298 ::shutdown(getFd(), 2);
299}
300
301bool TcpSocket::enableNagles(int sock, bool enable) {
302 int one = enable ? 0 : 1;
303 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
304 (char *)&one, sizeof(one)) < 0) {
305 int e = errorNumber;
306 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
307 return false;
308 }
309 return true;
310}
311
Pierre Ossman64069a92011-11-08 12:10:55 +0000312bool TcpSocket::cork(int sock, bool enable) {
313#ifndef TCP_CORK
314 return false;
315#else
316 int one = enable ? 1 : 0;
317 if (setsockopt(sock, IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0)
318 return false;
319 return true;
320#endif
321}
322
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000323bool TcpSocket::isSocket(int sock)
324{
325 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000326 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000327 return getsockname(sock, (struct sockaddr *)&info, &info_size) >= 0;
328}
329
330bool TcpSocket::isConnected(int sock)
331{
332 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000333 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000334 return getpeername(sock, (struct sockaddr *)&info, &info_size) >= 0;
335}
336
337int TcpSocket::getSockPort(int sock)
338{
339 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000340 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000341 if (getsockname(sock, (struct sockaddr *)&info, &info_size) < 0)
342 return 0;
343 return ntohs(info.sin_port);
344}
345
346
Adam Tkac93ff5db2010-02-05 15:54:10 +0000347TcpListener::TcpListener(const char *listenaddr, int port, bool localhostOnly,
348 int sock, bool close_) : closeFd(close_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000349{
350 if (sock != -1) {
351 fd = sock;
352 return;
353 }
354
355 initSockets();
356 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
357 throw SocketException("unable to create listening socket", errorNumber);
358
359#ifndef WIN32
360 // - By default, close the socket on exec()
361 fcntl(fd, F_SETFD, FD_CLOEXEC);
362
363 int one = 1;
364 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
365 (char *)&one, sizeof(one)) < 0) {
366 int e = errorNumber;
367 closesocket(fd);
368 throw SocketException("unable to create listening socket", e);
369 }
370#endif
371
372 // - Bind it to the desired port
373 struct sockaddr_in addr;
374 memset(&addr, 0, sizeof(addr));
375 addr.sin_family = AF_INET;
Adam Tkac93ff5db2010-02-05 15:54:10 +0000376
377 if (localhostOnly) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000378 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
Adam Tkac93ff5db2010-02-05 15:54:10 +0000379 } else if (listenaddr != NULL) {
Pierre Ossman40659972010-02-12 09:19:32 +0000380#ifdef HAVE_INET_ATON
Adam Tkac93ff5db2010-02-05 15:54:10 +0000381 if (inet_aton(listenaddr, &addr.sin_addr) == 0)
382#else
Pierre Ossman40659972010-02-12 09:19:32 +0000383 /* Some systems (e.g. Windows) do not have inet_aton, sigh */
Adam Tkac93ff5db2010-02-05 15:54:10 +0000384 if ((addr.sin_addr.s_addr = inet_addr(listenaddr)) == INADDR_NONE)
385#endif
386 {
387 closesocket(fd);
388 throw Exception("invalid network interface address: %s", listenaddr);
389 }
390 } else
391 addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Bind to 0.0.0.0 by default. */
392
393 addr.sin_port = htons(port);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000394 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
395 int e = errorNumber;
396 closesocket(fd);
397 throw SocketException("unable to bind listening socket", e);
398 }
399
400 // - Set it to be a listening socket
401 if (listen(fd, 5) < 0) {
402 int e = errorNumber;
403 closesocket(fd);
404 throw SocketException("unable to set socket to listening mode", e);
405 }
406}
407
408TcpListener::~TcpListener() {
409 if (closeFd) closesocket(fd);
410}
411
412void TcpListener::shutdown()
413{
414#ifdef WIN32
415 closesocket(getFd());
416#else
417 ::shutdown(getFd(), 2);
418#endif
419}
420
421
422Socket*
423TcpListener::accept() {
424 int new_sock = -1;
425
426 // Accept an incoming connection
427 if ((new_sock = ::accept(fd, 0, 0)) < 0)
428 throw SocketException("unable to accept new connection", errorNumber);
429
430#ifndef WIN32
431 // - By default, close the socket on exec()
432 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
433#endif
434
435 // Disable Nagle's algorithm, to reduce latency
436 TcpSocket::enableNagles(new_sock, false);
437
438 // Create the socket object & check connection is allowed
439 TcpSocket* s = new TcpSocket(new_sock);
440 if (filter && !filter->verifyConnection(s)) {
441 delete s;
442 return 0;
443 }
444 return s;
445}
446
447void TcpListener::getMyAddresses(std::list<char*>* result) {
448 const hostent* addrs = gethostbyname(0);
449 if (addrs == 0)
450 throw rdr::SystemException("gethostbyname", errorNumber);
451 if (addrs->h_addrtype != AF_INET)
452 throw rdr::Exception("getMyAddresses: bad family");
453 for (int i=0; addrs->h_addr_list[i] != 0; i++) {
454 const char* addrC = inet_ntoa(*((struct in_addr*)addrs->h_addr_list[i]));
455 char* addr = new char[strlen(addrC)+1];
456 strcpy(addr, addrC);
457 result->push_back(addr);
458 }
459}
460
461int TcpListener::getMyPort() {
462 return TcpSocket::getSockPort(getFd());
463}
464
465
466TcpFilter::TcpFilter(const char* spec) {
467 rfb::CharArray tmp;
Adam Tkacd36b6262009-09-04 10:57:20 +0000468 tmp.buf = rfb::strDup(spec);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000469 while (tmp.buf) {
470 rfb::CharArray first;
471 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
472 if (strlen(first.buf))
473 filter.push_back(parsePattern(first.buf));
474 }
475}
476
477TcpFilter::~TcpFilter() {
478}
479
480
481static bool
482patternMatchIP(const TcpFilter::Pattern& pattern, const char* value) {
483 unsigned long address = inet_addr((char *)value);
484 if (address == INADDR_NONE) return false;
485 return ((pattern.address & pattern.mask) == (address & pattern.mask));
486}
487
488bool
489TcpFilter::verifyConnection(Socket* s) {
490 rfb::CharArray name;
491
492 name.buf = s->getPeerAddress();
493 std::list<TcpFilter::Pattern>::iterator i;
494 for (i=filter.begin(); i!=filter.end(); i++) {
495 if (patternMatchIP(*i, name.buf)) {
496 switch ((*i).action) {
497 case Accept:
498 vlog.debug("ACCEPT %s", name.buf);
499 return true;
500 case Query:
501 vlog.debug("QUERY %s", name.buf);
502 s->setRequiresQuery();
503 return true;
504 case Reject:
505 vlog.debug("REJECT %s", name.buf);
506 return false;
507 }
508 }
509 }
510
511 vlog.debug("[REJECT] %s", name.buf);
512 return false;
513}
514
515
516TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
517 TcpFilter::Pattern pattern;
518
519 bool expandMask = false;
520 rfb::CharArray addr, mask;
521
522 if (rfb::strSplit(&p[1], '/', &addr.buf, &mask.buf)) {
523 if (rfb::strContains(mask.buf, '.')) {
524 pattern.mask = inet_addr(mask.buf);
525 } else {
526 pattern.mask = atoi(mask.buf);
527 expandMask = true;
528 }
529 } else {
530 pattern.mask = 32;
531 expandMask = true;
532 }
533 if (expandMask) {
534 unsigned long expanded = 0;
535 // *** check endianness!
536 for (int i=0; i<(int)pattern.mask; i++)
537 expanded |= 1<<(31-i);
538 pattern.mask = htonl(expanded);
539 }
540
541 pattern.address = inet_addr(addr.buf) & pattern.mask;
542 if ((pattern.address == INADDR_NONE) ||
543 (pattern.address == 0)) pattern.mask = 0;
544
545 switch(p[0]) {
546 case '+': pattern.action = TcpFilter::Accept; break;
547 case '-': pattern.action = TcpFilter::Reject; break;
548 case '?': pattern.action = TcpFilter::Query; break;
549 };
550
551 return pattern;
552}
553
554char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
555 in_addr tmp;
556 rfb::CharArray addr, mask;
557 tmp.s_addr = p.address;
Adam Tkacd36b6262009-09-04 10:57:20 +0000558 addr.buf = rfb::strDup(inet_ntoa(tmp));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000559 tmp.s_addr = p.mask;
Adam Tkacd36b6262009-09-04 10:57:20 +0000560 mask.buf = rfb::strDup(inet_ntoa(tmp));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000561 char* result = new char[strlen(addr.buf)+1+strlen(mask.buf)+1+1];
562 switch (p.action) {
563 case Accept: result[0] = '+'; break;
564 case Reject: result[0] = '-'; break;
565 case Query: result[0] = '?'; break;
566 };
567 result[1] = 0;
568 strcat(result, addr.buf);
569 strcat(result, "/");
570 strcat(result, mask.buf);
571 return result;
572}