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