blob: 777627b4ffcaf7c616d8fa7c0f68e896c4f050b0 [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()
27#define snprintf _snprintf
28#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
56using namespace network;
57using namespace rdr;
58
59static rfb::LogWriter vlog("TcpSocket");
60
61/* Tunnelling support. */
62int network::findFreeTcpPort (void)
63{
64 int sock, port;
65 struct sockaddr_in addr;
66 memset(&addr, 0, sizeof(addr));
67 addr.sin_family = AF_INET;
68 addr.sin_addr.s_addr = INADDR_ANY;
69
70 if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
71 throw SocketException ("unable to create socket", errorNumber);
72
73 for (port = TUNNEL_PORT_OFFSET + 99; port > TUNNEL_PORT_OFFSET; port--) {
74 addr.sin_port = htons ((unsigned short) port);
75 if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) == 0) {
76 closesocket (sock);
77 return port;
78 }
79 }
80 throw SocketException ("no free port in range", 0);
81 return 0;
82}
83
84
85// -=- Socket initialisation
86static bool socketsInitialised = false;
87static void initSockets() {
88 if (socketsInitialised)
89 return;
90#ifdef WIN32
91 WORD requiredVersion = MAKEWORD(2,0);
92 WSADATA initResult;
93
94 if (WSAStartup(requiredVersion, &initResult) != 0)
95 throw SocketException("unable to initialise Winsock2", errorNumber);
96#else
97 signal(SIGPIPE, SIG_IGN);
98#endif
99 socketsInitialised = true;
100}
101
102
103// -=- TcpSocket
104
105TcpSocket::TcpSocket(int sock, bool close)
106 : Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close)
107{
108}
109
110TcpSocket::TcpSocket(const char *host, int port)
111 : closeFd(true)
112{
113 int sock;
114
115 // - Create a socket
116 initSockets();
117 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
118 throw SocketException("unable to create socket", errorNumber);
119
120#ifndef WIN32
121 // - By default, close the socket on exec()
122 fcntl(sock, F_SETFD, FD_CLOEXEC);
123#endif
124
125 // - Connect it to something
126
127 // Try processing the host as an IP address
128 struct sockaddr_in addr;
129 memset(&addr, 0, sizeof(addr));
130 addr.sin_family = AF_INET;
131 addr.sin_addr.s_addr = inet_addr((char *)host);
132 addr.sin_port = htons(port);
133 if ((int)addr.sin_addr.s_addr == -1) {
134 // Host was not an IP address - try resolving as DNS name
135 struct hostent *hostinfo;
136 hostinfo = gethostbyname((char *)host);
137 if (hostinfo && hostinfo->h_addr) {
138 addr.sin_addr.s_addr = ((struct in_addr *)hostinfo->h_addr)->s_addr;
139 } else {
140 int e = errorNumber;
141 closesocket(sock);
142 throw SocketException("unable to resolve host by name", e);
143 }
144 }
145
146 // Attempt to connect to the remote host
147 for (;;) {
148 if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
149 int e = errorNumber;
150#ifndef WIN32
151 if (e == EINTR)
152 continue;
153#endif
154 closesocket(sock);
155 throw SocketException("unable to connect to host", e);
156 } else break;
157 }
158
159 // Disable Nagle's algorithm, to reduce latency
160 enableNagles(sock, false);
161
162 // Create the input and output streams
163 instream = new FdInStream(sock);
164 outstream = new FdOutStream(sock);
165 ownStreams = true;
166}
167
168TcpSocket::~TcpSocket() {
169 if (closeFd)
170 closesocket(getFd());
171}
172
173char* TcpSocket::getMyAddress() {
174 struct sockaddr_in info;
175 struct in_addr addr;
176 VNC_SOCKLEN_T info_size = sizeof(info);
177
178 getsockname(getFd(), (struct sockaddr *)&info, &info_size);
179 memcpy(&addr, &info.sin_addr, sizeof(addr));
180
181 char* name = inet_ntoa(addr);
182 if (name) {
183 return rfb::strDup(name);
184 } else {
185 return rfb::strDup("");
186 }
187}
188
189int TcpSocket::getMyPort() {
190 return getSockPort(getFd());
191}
192
193char* TcpSocket::getMyEndpoint() {
194 rfb::CharArray address; address.buf = getMyAddress();
195 int port = getMyPort();
196
197 int buflen = strlen(address.buf) + 32;
198 char* buffer = new char[buflen];
199 sprintf(buffer, "%s::%d", address.buf, port);
200 return buffer;
201}
202
203char* TcpSocket::getPeerAddress() {
204 struct sockaddr_in info;
205 struct in_addr addr;
206 VNC_SOCKLEN_T info_size = sizeof(info);
207
208 getpeername(getFd(), (struct sockaddr *)&info, &info_size);
209 memcpy(&addr, &info.sin_addr, sizeof(addr));
210
211 char* name = inet_ntoa(addr);
212 if (name) {
213 return rfb::strDup(name);
214 } else {
215 return rfb::strDup("");
216 }
217}
218
219int TcpSocket::getPeerPort() {
220 struct sockaddr_in info;
221 VNC_SOCKLEN_T info_size = sizeof(info);
222
223 getpeername(getFd(), (struct sockaddr *)&info, &info_size);
224 return ntohs(info.sin_port);
225}
226
227char* TcpSocket::getPeerEndpoint() {
228 rfb::CharArray address; address.buf = getPeerAddress();
229 int port = getPeerPort();
230
231 int buflen = strlen(address.buf) + 32;
232 char* buffer = new char[buflen];
233 sprintf(buffer, "%s::%d", address.buf, port);
234 return buffer;
235}
236
237bool TcpSocket::sameMachine() {
238 struct sockaddr_in peeraddr, myaddr;
239 VNC_SOCKLEN_T addrlen = sizeof(struct sockaddr_in);
240
241 getpeername(getFd(), (struct sockaddr *)&peeraddr, &addrlen);
242 getsockname(getFd(), (struct sockaddr *)&myaddr, &addrlen);
243
244 return (peeraddr.sin_addr.s_addr == myaddr.sin_addr.s_addr);
245}
246
247void TcpSocket::shutdown()
248{
249 Socket::shutdown();
250 ::shutdown(getFd(), 2);
251}
252
253bool TcpSocket::enableNagles(int sock, bool enable) {
254 int one = enable ? 0 : 1;
255 if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
256 (char *)&one, sizeof(one)) < 0) {
257 int e = errorNumber;
258 vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
259 return false;
260 }
261 return true;
262}
263
264bool TcpSocket::isSocket(int sock)
265{
266 struct sockaddr_in info;
267 VNC_SOCKLEN_T info_size = sizeof(info);
268 return getsockname(sock, (struct sockaddr *)&info, &info_size) >= 0;
269}
270
271bool TcpSocket::isConnected(int sock)
272{
273 struct sockaddr_in info;
274 VNC_SOCKLEN_T info_size = sizeof(info);
275 return getpeername(sock, (struct sockaddr *)&info, &info_size) >= 0;
276}
277
278int TcpSocket::getSockPort(int sock)
279{
280 struct sockaddr_in info;
281 VNC_SOCKLEN_T info_size = sizeof(info);
282 if (getsockname(sock, (struct sockaddr *)&info, &info_size) < 0)
283 return 0;
284 return ntohs(info.sin_port);
285}
286
287
288TcpListener::TcpListener(int port, bool localhostOnly, int sock, bool close_)
289 : closeFd(close_)
290{
291 if (sock != -1) {
292 fd = sock;
293 return;
294 }
295
296 initSockets();
297 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
298 throw SocketException("unable to create listening socket", errorNumber);
299
300#ifndef WIN32
301 // - By default, close the socket on exec()
302 fcntl(fd, F_SETFD, FD_CLOEXEC);
303
304 int one = 1;
305 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
306 (char *)&one, sizeof(one)) < 0) {
307 int e = errorNumber;
308 closesocket(fd);
309 throw SocketException("unable to create listening socket", e);
310 }
311#endif
312
313 // - Bind it to the desired port
314 struct sockaddr_in addr;
315 memset(&addr, 0, sizeof(addr));
316 addr.sin_family = AF_INET;
317 addr.sin_port = htons(port);
318 if (localhostOnly)
319 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
320 else
321 addr.sin_addr.s_addr = htonl(INADDR_ANY);
322 if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
323 int e = errorNumber;
324 closesocket(fd);
325 throw SocketException("unable to bind listening socket", e);
326 }
327
328 // - Set it to be a listening socket
329 if (listen(fd, 5) < 0) {
330 int e = errorNumber;
331 closesocket(fd);
332 throw SocketException("unable to set socket to listening mode", e);
333 }
334}
335
336TcpListener::~TcpListener() {
337 if (closeFd) closesocket(fd);
338}
339
340void TcpListener::shutdown()
341{
342#ifdef WIN32
343 closesocket(getFd());
344#else
345 ::shutdown(getFd(), 2);
346#endif
347}
348
349
350Socket*
351TcpListener::accept() {
352 int new_sock = -1;
353
354 // Accept an incoming connection
355 if ((new_sock = ::accept(fd, 0, 0)) < 0)
356 throw SocketException("unable to accept new connection", errorNumber);
357
358#ifndef WIN32
359 // - By default, close the socket on exec()
360 fcntl(new_sock, F_SETFD, FD_CLOEXEC);
361#endif
362
363 // Disable Nagle's algorithm, to reduce latency
364 TcpSocket::enableNagles(new_sock, false);
365
366 // Create the socket object & check connection is allowed
367 TcpSocket* s = new TcpSocket(new_sock);
368 if (filter && !filter->verifyConnection(s)) {
369 delete s;
370 return 0;
371 }
372 return s;
373}
374
375void TcpListener::getMyAddresses(std::list<char*>* result) {
376 const hostent* addrs = gethostbyname(0);
377 if (addrs == 0)
378 throw rdr::SystemException("gethostbyname", errorNumber);
379 if (addrs->h_addrtype != AF_INET)
380 throw rdr::Exception("getMyAddresses: bad family");
381 for (int i=0; addrs->h_addr_list[i] != 0; i++) {
382 const char* addrC = inet_ntoa(*((struct in_addr*)addrs->h_addr_list[i]));
383 char* addr = new char[strlen(addrC)+1];
384 strcpy(addr, addrC);
385 result->push_back(addr);
386 }
387}
388
389int TcpListener::getMyPort() {
390 return TcpSocket::getSockPort(getFd());
391}
392
393
394TcpFilter::TcpFilter(const char* spec) {
395 rfb::CharArray tmp;
396 tmp.buf = rfb::strDup(spec);
397 while (tmp.buf) {
398 rfb::CharArray first;
399 rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
400 if (strlen(first.buf))
401 filter.push_back(parsePattern(first.buf));
402 }
403}
404
405TcpFilter::~TcpFilter() {
406}
407
408
409static bool
410patternMatchIP(const TcpFilter::Pattern& pattern, const char* value) {
411 unsigned long address = inet_addr((char *)value);
412 if (address == INADDR_NONE) return false;
413 return ((pattern.address & pattern.mask) == (address & pattern.mask));
414}
415
416bool
417TcpFilter::verifyConnection(Socket* s) {
418 rfb::CharArray name;
419
420 name.buf = s->getPeerAddress();
421 std::list<TcpFilter::Pattern>::iterator i;
422 for (i=filter.begin(); i!=filter.end(); i++) {
423 if (patternMatchIP(*i, name.buf)) {
424 switch ((*i).action) {
425 case Accept:
426 vlog.debug("ACCEPT %s", name.buf);
427 return true;
428 case Query:
429 vlog.debug("QUERY %s", name.buf);
430 s->setRequiresQuery();
431 return true;
432 case Reject:
433 vlog.debug("REJECT %s", name.buf);
434 return false;
435 }
436 }
437 }
438
439 vlog.debug("[REJECT] %s", name.buf);
440 return false;
441}
442
443
444TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
445 TcpFilter::Pattern pattern;
446
447 bool expandMask = false;
448 rfb::CharArray addr, mask;
449
450 if (rfb::strSplit(&p[1], '/', &addr.buf, &mask.buf)) {
451 if (rfb::strContains(mask.buf, '.')) {
452 pattern.mask = inet_addr(mask.buf);
453 } else {
454 pattern.mask = atoi(mask.buf);
455 expandMask = true;
456 }
457 } else {
458 pattern.mask = 32;
459 expandMask = true;
460 }
461 if (expandMask) {
462 unsigned long expanded = 0;
463 // *** check endianness!
464 for (int i=0; i<(int)pattern.mask; i++)
465 expanded |= 1<<(31-i);
466 pattern.mask = htonl(expanded);
467 }
468
469 pattern.address = inet_addr(addr.buf) & pattern.mask;
470 if ((pattern.address == INADDR_NONE) ||
471 (pattern.address == 0)) pattern.mask = 0;
472
473 switch(p[0]) {
474 case '+': pattern.action = TcpFilter::Accept; break;
475 case '-': pattern.action = TcpFilter::Reject; break;
476 case '?': pattern.action = TcpFilter::Query; break;
477 };
478
479 return pattern;
480}
481
482char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
483 in_addr tmp;
484 rfb::CharArray addr, mask;
485 tmp.s_addr = p.address;
486 addr.buf = rfb::strDup(inet_ntoa(tmp));
487 tmp.s_addr = p.mask;
488 mask.buf = rfb::strDup(inet_ntoa(tmp));
489 char* result = new char[strlen(addr.buf)+1+strlen(mask.buf)+1+1];
490 switch (p.action) {
491 case Accept: result[0] = '+'; break;
492 case Reject: result[0] = '-'; break;
493 case Query: result[0] = '?'; break;
494 };
495 result[1] = 0;
496 strcat(result, addr.buf);
497 strcat(result, "/");
498 strcat(result, mask.buf);
499 return result;
500}