blob: 7ceff0b002514da6618050ead4fd697f81ba015b [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 Tkacad1cbd92008-10-06 14:08:00 +000019#ifdef HAVE_COMMON_CONFIG_H
20#include <common-config.h>
21#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{
74 int sock, port;
75 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
83 for (port = TUNNEL_PORT_OFFSET + 99; port > TUNNEL_PORT_OFFSET; port--) {
84 addr.sin_port = htons ((unsigned short) port);
85 if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) == 0) {
86 closesocket (sock);
87 return port;
88 }
89 }
90 throw SocketException ("no free port in range", 0);
91 return 0;
92}
93
94
95// -=- Socket initialisation
96static bool socketsInitialised = false;
97static void initSockets() {
98 if (socketsInitialised)
99 return;
100#ifdef WIN32
101 WORD requiredVersion = MAKEWORD(2,0);
102 WSADATA initResult;
103
104 if (WSAStartup(requiredVersion, &initResult) != 0)
105 throw SocketException("unable to initialise Winsock2", errorNumber);
106#else
107 signal(SIGPIPE, SIG_IGN);
108#endif
109 socketsInitialised = true;
110}
111
112
113// -=- TcpSocket
114
115TcpSocket::TcpSocket(int sock, bool close)
116 : Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close)
117{
118}
119
120TcpSocket::TcpSocket(const char *host, int port)
121 : closeFd(true)
122{
Adam Tkac9cb6a422008-11-14 15:56:15 +0000123 int sock, err, result, family;
124 vnc_sockaddr_t sa;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000125 socklen_t salen;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000126#ifdef HAVE_GETADDRINFO
127 struct addrinfo *ai, *current, hints;
128#endif
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000129
130 // - Create a socket
131 initSockets();
Adam Tkac9cb6a422008-11-14 15:56:15 +0000132
133#ifdef HAVE_GETADDRINFO
134 memset(&hints, 0, sizeof(struct addrinfo));
135 hints.ai_family = AF_UNSPEC;
136 hints.ai_socktype = SOCK_STREAM;
137 hints.ai_canonname = NULL;
138 hints.ai_addr = NULL;
139 hints.ai_next = NULL;
140
141 if ((result = getaddrinfo(host, NULL, &hints, &ai)) != 0) {
142 throw Exception("unable to resolve host by name: %s",
143 gai_strerror(result));
144 }
145
146 for (current = ai; current != NULL; current = current->ai_next) {
147 family = current->ai_family;
148 if (family != AF_INET && family != AF_INET6)
149 continue;
150
151 salen = current->ai_addrlen;
152 memcpy(&sa, current->ai_addr, salen);
153
154 if (family == AF_INET)
155 sa.u.sin.sin_port = htons(port);
156 else
157 sa.u.sin6.sin6_port = htons(port);
158
159#else /* HAVE_GETADDRINFO */
160 family = AF_INET;
161 salen = sizeof(struct sockaddr_in);
162
163 /* Try processing the host as an IP address */
164 memset(&sa, 0, sizeof(sa));
165 sa.u.sin.sin_family = AF_INET;
166 sa.u.sin.sin_addr.s_addr = inet_addr((char *)host);
167 sa.u.sin.sin_port = htons(port);
168 if ((int)sa.u.sin.sin_addr.s_addr == -1) {
169 /* Host was not an IP address - try resolving as DNS name */
170 struct hostent *hostinfo;
171 hostinfo = gethostbyname((char *)host);
172 if (hostinfo && hostinfo->h_addr) {
173 sa.u.sin.sin_addr.s_addr = ((struct in_addr *)hostinfo->h_addr)->s_addr;
174 } else {
175 err = errorNumber;
176 throw SocketException("unable to resolve host by name", err);
177 }
178 }
179#endif /* HAVE_GETADDRINFO */
180
181 sock = socket (family, SOCK_STREAM, 0);
182 if (sock == -1) {
183 err = errorNumber;
184#ifdef HAVE_GETADDRINFO
185 freeaddrinfo(ai);
186#endif /* HAVE_GETADDRINFO */
187 throw SocketException("unable to create socket", err);
188 }
189
190 /* Attempt to connect to the remote host */
191 while ((result = connect(sock, &sa.u.sa, sizeof(sa))) == -1) {
192 err = errorNumber;
193#ifndef WIN32
194 if (err == EINTR)
195 continue;
196#endif
197 closesocket(sock);
198 break;
199 }
200
201#ifdef HAVE_GETADDRINFO
202 if (result == 0)
203 break;
Adam Tkac9cb6a422008-11-14 15:56:15 +0000204 }
205
206 freeaddrinfo(ai);
207#endif /* HAVE_GETADDRINFO */
208
209 if (result == -1)
210 throw SocketException("unable connect to socket", err);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000211
212#ifndef WIN32
213 // - By default, close the socket on exec()
214 fcntl(sock, F_SETFD, FD_CLOEXEC);
215#endif
216
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000217 // Disable Nagle's algorithm, to reduce latency
218 enableNagles(sock, false);
219
220 // Create the input and output streams
221 instream = new FdInStream(sock);
222 outstream = new FdOutStream(sock);
223 ownStreams = true;
224}
225
226TcpSocket::~TcpSocket() {
227 if (closeFd)
228 closesocket(getFd());
229}
230
231char* TcpSocket::getMyAddress() {
232 struct sockaddr_in info;
233 struct in_addr addr;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000234 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000235
236 getsockname(getFd(), (struct sockaddr *)&info, &info_size);
237 memcpy(&addr, &info.sin_addr, sizeof(addr));
238
239 char* name = inet_ntoa(addr);
240 if (name) {
241 return rfb::strDup(name);
242 } else {
243 return rfb::strDup("");
244 }
245}
246
247int TcpSocket::getMyPort() {
248 return getSockPort(getFd());
249}
250
251char* TcpSocket::getMyEndpoint() {
252 rfb::CharArray address; address.buf = getMyAddress();
253 int port = getMyPort();
254
255 int buflen = strlen(address.buf) + 32;
256 char* buffer = new char[buflen];
257 sprintf(buffer, "%s::%d", address.buf, port);
258 return buffer;
259}
260
261char* TcpSocket::getPeerAddress() {
262 struct sockaddr_in info;
263 struct in_addr addr;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000264 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000265
266 getpeername(getFd(), (struct sockaddr *)&info, &info_size);
267 memcpy(&addr, &info.sin_addr, sizeof(addr));
268
269 char* name = inet_ntoa(addr);
270 if (name) {
271 return rfb::strDup(name);
272 } else {
273 return rfb::strDup("");
274 }
275}
276
277int TcpSocket::getPeerPort() {
278 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000279 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000280
281 getpeername(getFd(), (struct sockaddr *)&info, &info_size);
282 return ntohs(info.sin_port);
283}
284
285char* TcpSocket::getPeerEndpoint() {
286 rfb::CharArray address; address.buf = getPeerAddress();
287 int port = getPeerPort();
288
289 int buflen = strlen(address.buf) + 32;
290 char* buffer = new char[buflen];
291 sprintf(buffer, "%s::%d", address.buf, port);
292 return buffer;
293}
294
295bool TcpSocket::sameMachine() {
296 struct sockaddr_in peeraddr, myaddr;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000297 socklen_t addrlen = sizeof(struct sockaddr_in);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000298
299 getpeername(getFd(), (struct sockaddr *)&peeraddr, &addrlen);
300 getsockname(getFd(), (struct sockaddr *)&myaddr, &addrlen);
301
302 return (peeraddr.sin_addr.s_addr == myaddr.sin_addr.s_addr);
303}
304
305void TcpSocket::shutdown()
306{
307 Socket::shutdown();
308 ::shutdown(getFd(), 2);
309}
310
311bool TcpSocket::enableNagles(int sock, bool enable) {
312 int one = enable ? 0 : 1;
313 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
314 (char *)&one, sizeof(one)) < 0) {
315 int e = errorNumber;
316 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
317 return false;
318 }
319 return true;
320}
321
322bool TcpSocket::isSocket(int sock)
323{
324 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000325 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000326 return getsockname(sock, (struct sockaddr *)&info, &info_size) >= 0;
327}
328
329bool TcpSocket::isConnected(int sock)
330{
331 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000332 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000333 return getpeername(sock, (struct sockaddr *)&info, &info_size) >= 0;
334}
335
336int TcpSocket::getSockPort(int sock)
337{
338 struct sockaddr_in info;
Adam Tkacbe4c3ac2008-12-10 16:42:33 +0000339 socklen_t info_size = sizeof(info);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000340 if (getsockname(sock, (struct sockaddr *)&info, &info_size) < 0)
341 return 0;
342 return ntohs(info.sin_port);
343}
344
345
346TcpListener::TcpListener(int port, bool localhostOnly, int sock, bool close_)
347 : closeFd(close_)
348{
349 if (sock != -1) {
350 fd = sock;
351 return;
352 }
353
354 initSockets();
355 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
356 throw SocketException("unable to create listening socket", errorNumber);
357
358#ifndef WIN32
359 // - By default, close the socket on exec()
360 fcntl(fd, F_SETFD, FD_CLOEXEC);
361
362 int one = 1;
363 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
364 (char *)&one, sizeof(one)) < 0) {
365 int e = errorNumber;
366 closesocket(fd);
367 throw SocketException("unable to create listening socket", e);
368 }
369#endif
370
371 // - Bind it to the desired port
372 struct sockaddr_in addr;
373 memset(&addr, 0, sizeof(addr));
374 addr.sin_family = AF_INET;
375 addr.sin_port = htons(port);
376 if (localhostOnly)
377 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
378 else
379 addr.sin_addr.s_addr = htonl(INADDR_ANY);
380 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
381 int e = errorNumber;
382 closesocket(fd);
383 throw SocketException("unable to bind listening socket", e);
384 }
385
386 // - Set it to be a listening socket
387 if (listen(fd, 5) < 0) {
388 int e = errorNumber;
389 closesocket(fd);
390 throw SocketException("unable to set socket to listening mode", e);
391 }
392}
393
394TcpListener::~TcpListener() {
395 if (closeFd) closesocket(fd);
396}
397
398void TcpListener::shutdown()
399{
400#ifdef WIN32
401 closesocket(getFd());
402#else
403 ::shutdown(getFd(), 2);
404#endif
405}
406
407
408Socket*
409TcpListener::accept() {
410 int new_sock = -1;
411
412 // Accept an incoming connection
413 if ((new_sock = ::accept(fd, 0, 0)) < 0)
414 throw SocketException("unable to accept new connection", errorNumber);
415
416#ifndef WIN32
417 // - By default, close the socket on exec()
418 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
419#endif
420
421 // Disable Nagle's algorithm, to reduce latency
422 TcpSocket::enableNagles(new_sock, false);
423
424 // Create the socket object & check connection is allowed
425 TcpSocket* s = new TcpSocket(new_sock);
426 if (filter && !filter->verifyConnection(s)) {
427 delete s;
428 return 0;
429 }
430 return s;
431}
432
433void TcpListener::getMyAddresses(std::list<char*>* result) {
434 const hostent* addrs = gethostbyname(0);
435 if (addrs == 0)
436 throw rdr::SystemException("gethostbyname", errorNumber);
437 if (addrs->h_addrtype != AF_INET)
438 throw rdr::Exception("getMyAddresses: bad family");
439 for (int i=0; addrs->h_addr_list[i] != 0; i++) {
440 const char* addrC = inet_ntoa(*((struct in_addr*)addrs->h_addr_list[i]));
441 char* addr = new char[strlen(addrC)+1];
442 strcpy(addr, addrC);
443 result->push_back(addr);
444 }
445}
446
447int TcpListener::getMyPort() {
448 return TcpSocket::getSockPort(getFd());
449}
450
451
452TcpFilter::TcpFilter(const char* spec) {
453 rfb::CharArray tmp;
454 tmp.buf = rfb::strDup(spec);
455 while (tmp.buf) {
456 rfb::CharArray first;
457 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
458 if (strlen(first.buf))
459 filter.push_back(parsePattern(first.buf));
460 }
461}
462
463TcpFilter::~TcpFilter() {
464}
465
466
467static bool
468patternMatchIP(const TcpFilter::Pattern& pattern, const char* value) {
469 unsigned long address = inet_addr((char *)value);
470 if (address == INADDR_NONE) return false;
471 return ((pattern.address & pattern.mask) == (address & pattern.mask));
472}
473
474bool
475TcpFilter::verifyConnection(Socket* s) {
476 rfb::CharArray name;
477
478 name.buf = s->getPeerAddress();
479 std::list<TcpFilter::Pattern>::iterator i;
480 for (i=filter.begin(); i!=filter.end(); i++) {
481 if (patternMatchIP(*i, name.buf)) {
482 switch ((*i).action) {
483 case Accept:
484 vlog.debug("ACCEPT %s", name.buf);
485 return true;
486 case Query:
487 vlog.debug("QUERY %s", name.buf);
488 s->setRequiresQuery();
489 return true;
490 case Reject:
491 vlog.debug("REJECT %s", name.buf);
492 return false;
493 }
494 }
495 }
496
497 vlog.debug("[REJECT] %s", name.buf);
498 return false;
499}
500
501
502TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
503 TcpFilter::Pattern pattern;
504
505 bool expandMask = false;
506 rfb::CharArray addr, mask;
507
508 if (rfb::strSplit(&p[1], '/', &addr.buf, &mask.buf)) {
509 if (rfb::strContains(mask.buf, '.')) {
510 pattern.mask = inet_addr(mask.buf);
511 } else {
512 pattern.mask = atoi(mask.buf);
513 expandMask = true;
514 }
515 } else {
516 pattern.mask = 32;
517 expandMask = true;
518 }
519 if (expandMask) {
520 unsigned long expanded = 0;
521 // *** check endianness!
522 for (int i=0; i<(int)pattern.mask; i++)
523 expanded |= 1<<(31-i);
524 pattern.mask = htonl(expanded);
525 }
526
527 pattern.address = inet_addr(addr.buf) & pattern.mask;
528 if ((pattern.address == INADDR_NONE) ||
529 (pattern.address == 0)) pattern.mask = 0;
530
531 switch(p[0]) {
532 case '+': pattern.action = TcpFilter::Accept; break;
533 case '-': pattern.action = TcpFilter::Reject; break;
534 case '?': pattern.action = TcpFilter::Query; break;
535 };
536
537 return pattern;
538}
539
540char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
541 in_addr tmp;
542 rfb::CharArray addr, mask;
543 tmp.s_addr = p.address;
544 addr.buf = rfb::strDup(inet_ntoa(tmp));
545 tmp.s_addr = p.mask;
546 mask.buf = rfb::strDup(inet_ntoa(tmp));
547 char* result = new char[strlen(addr.buf)+1+strlen(mask.buf)+1+1];
548 switch (p.action) {
549 case Accept: result[0] = '+'; break;
550 case Reject: result[0] = '-'; break;
551 case Query: result[0] = '?'; break;
552 };
553 result[1] = 0;
554 strcat(result, addr.buf);
555 strcat(result, "/");
556 strcat(result, mask.buf);
557 return result;
558}