aboutsummaryrefslogtreecommitdiffstats
path: root/network/TcpSocket.cxx
diff options
context:
space:
mode:
authorConstantin Kaplinsky <const@tightvnc.com>2004-10-08 09:43:57 +0000
committerConstantin Kaplinsky <const@tightvnc.com>2004-10-08 09:43:57 +0000
commit47ed8d321c32c6b741cff1f4ff686165c4f269f4 (patch)
treeda413648adbff4ff10c8ee26124673f8e7cf238a /network/TcpSocket.cxx
parent266bb36cd47555280fffd3aab1ed86683e26d748 (diff)
downloadtigervnc-47ed8d321c32c6b741cff1f4ff686165c4f269f4.tar.gz
tigervnc-47ed8d321c32c6b741cff1f4ff686165c4f269f4.zip
Initial revision
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@2 3789f03b-4d11-0410-bbf8-ca57d06f2519
Diffstat (limited to 'network/TcpSocket.cxx')
-rw-r--r--network/TcpSocket.cxx458
1 files changed, 458 insertions, 0 deletions
diff --git a/network/TcpSocket.cxx b/network/TcpSocket.cxx
new file mode 100644
index 00000000..b536e673
--- /dev/null
+++ b/network/TcpSocket.cxx
@@ -0,0 +1,458 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifdef WIN32
+//#include <io.h>
+#include <winsock2.h>
+#define errorNumber WSAGetLastError()
+#define snprintf _snprintf
+#else
+#define errorNumber errno
+#define closesocket close
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#endif
+
+#include <network/TcpSocket.h>
+#include <rfb/util.h>
+#include <rfb/LogWriter.h>
+
+#ifndef VNC_SOCKLEN_T
+#define VNC_SOCKLEN_T int
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned long)-1)
+#endif
+
+using namespace network;
+using namespace rdr;
+
+static rfb::LogWriter vlog("TcpSocket");
+
+
+void
+TcpSocket::initTcpSockets() {
+#ifdef WIN32
+ WORD requiredVersion = MAKEWORD(2,0);
+ WSADATA initResult;
+
+ if (WSAStartup(requiredVersion, &initResult) != 0)
+ throw SocketException("unable to initialise Winsock2", errorNumber);
+#else
+ signal(SIGPIPE, SIG_IGN);
+#endif
+}
+
+// -=- TcpSocket
+
+TcpSocket::TcpSocket(int sock, bool close)
+ : Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close)
+{
+}
+
+TcpSocket::TcpSocket(const char *host, int port)
+ : closeFd(true)
+{
+ int sock;
+
+ // - Create a socket
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ throw SocketException("unable to create socket", errorNumber);
+
+#ifndef WIN32
+ // - By default, close the socket on exec()
+ fcntl(sock, F_SETFD, FD_CLOEXEC);
+#endif
+
+ // - Connect it to something
+
+ // Try processing the host as an IP address
+ struct sockaddr_in addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr(host);
+ addr.sin_port = htons(port);
+ if ((int)addr.sin_addr.s_addr == -1) {
+ // Host was not an IP address - try resolving as DNS name
+ struct hostent *hostinfo;
+ hostinfo = gethostbyname(host);
+ if (hostinfo && hostinfo->h_addr) {
+ addr.sin_addr.s_addr = ((struct in_addr *)hostinfo->h_addr)->s_addr;
+ } else {
+ int e = errorNumber;
+ closesocket(sock);
+ throw SocketException("unable to resolve host by name", e);
+ }
+ }
+
+ // Attempt to connect to the remote host
+ if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
+ int e = errorNumber;
+ closesocket(sock);
+ throw SocketException("unable to connect to host", e);
+ }
+
+ int one = 1;
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&one, sizeof(one)) < 0) {
+ int e = errorNumber;
+ closesocket(sock);
+ throw SocketException("unable to setsockopt TCP_NODELAY", e);
+ }
+
+ // Create the input and output streams
+ instream = new FdInStream(sock);
+ outstream = new FdOutStream(sock);
+ own_streams = true;
+}
+
+TcpSocket::~TcpSocket() {
+ if (closeFd)
+ closesocket(getFd());
+}
+
+char* TcpSocket::getMyAddress() {
+ struct sockaddr_in info;
+ struct in_addr addr;
+ VNC_SOCKLEN_T info_size = sizeof(info);
+
+ getsockname(getFd(), (struct sockaddr *)&info, &info_size);
+ memcpy(&addr, &info.sin_addr, sizeof(addr));
+
+ char* name = inet_ntoa(addr);
+ if (name) {
+ return rfb::strDup(name);
+ } else {
+ return rfb::strDup("");
+ }
+}
+
+int TcpSocket::getMyPort() {
+ return getSockPort(getFd());
+}
+
+char* TcpSocket::getMyEndpoint() {
+ rfb::CharArray address; address.buf = getMyAddress();
+ int port = getMyPort();
+
+ int buflen = strlen(address.buf) + 32;
+ char* buffer = new char[buflen];
+ sprintf(buffer, "%s::%d", address.buf, port);
+ return buffer;
+}
+
+char* TcpSocket::getPeerAddress() {
+ struct sockaddr_in info;
+ struct in_addr addr;
+ VNC_SOCKLEN_T info_size = sizeof(info);
+
+ getpeername(getFd(), (struct sockaddr *)&info, &info_size);
+ memcpy(&addr, &info.sin_addr, sizeof(addr));
+
+ char* name = inet_ntoa(addr);
+ if (name) {
+ return rfb::strDup(name);
+ } else {
+ return rfb::strDup("");
+ }
+}
+
+int TcpSocket::getPeerPort() {
+ struct sockaddr_in info;
+ VNC_SOCKLEN_T info_size = sizeof(info);
+
+ getpeername(getFd(), (struct sockaddr *)&info, &info_size);
+ return ntohs(info.sin_port);
+}
+
+char* TcpSocket::getPeerEndpoint() {
+ rfb::CharArray address; address.buf = getPeerAddress();
+ int port = getPeerPort();
+
+ int buflen = strlen(address.buf) + 32;
+ char* buffer = new char[buflen];
+ sprintf(buffer, "%s::%d", address.buf, port);
+ return buffer;
+}
+
+bool TcpSocket::sameMachine() {
+ struct sockaddr_in peeraddr, myaddr;
+ VNC_SOCKLEN_T addrlen = sizeof(struct sockaddr_in);
+
+ getpeername(getFd(), (struct sockaddr *)&peeraddr, &addrlen);
+ getsockname(getFd(), (struct sockaddr *)&myaddr, &addrlen);
+
+ return (peeraddr.sin_addr.s_addr == myaddr.sin_addr.s_addr);
+}
+
+void TcpSocket::shutdown()
+{
+ ::shutdown(getFd(), 2);
+}
+
+bool TcpSocket::isSocket(int sock)
+{
+ struct sockaddr_in info;
+ VNC_SOCKLEN_T info_size = sizeof(info);
+ return getsockname(sock, (struct sockaddr *)&info, &info_size) >= 0;
+}
+
+bool TcpSocket::isConnected(int sock)
+{
+ struct sockaddr_in info;
+ VNC_SOCKLEN_T info_size = sizeof(info);
+ return getpeername(sock, (struct sockaddr *)&info, &info_size) >= 0;
+}
+
+int TcpSocket::getSockPort(int sock)
+{
+ struct sockaddr_in info;
+ VNC_SOCKLEN_T info_size = sizeof(info);
+ if (getsockname(sock, (struct sockaddr *)&info, &info_size) < 0)
+ return 0;
+ return ntohs(info.sin_port);
+}
+
+
+TcpListener::TcpListener(int port, bool localhostOnly, int sock, bool close_)
+ : closeFd(close_)
+{
+ if (sock != -1) {
+ fd = sock;
+ return;
+ }
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ throw SocketException("unable to create listening socket", errorNumber);
+
+#ifndef WIN32
+ // - By default, close the socket on exec()
+ fcntl(sock, F_SETFD, FD_CLOEXEC);
+
+ int one = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&one, sizeof(one)) < 0) {
+ int e = errorNumber;
+ closesocket(fd);
+ throw SocketException("unable to create listening socket", e);
+ }
+#endif
+
+ // - Bind it to the desired port
+ struct sockaddr_in addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ if (localhostOnly)
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ else
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ int e = errorNumber;
+ closesocket(fd);
+ throw SocketException("unable to bind listening socket", e);
+ }
+
+ // - Set it to be a listening socket
+ if (listen(fd, 5) < 0) {
+ int e = errorNumber;
+ closesocket(fd);
+ throw SocketException("unable to set socket to listening mode", e);
+ }
+}
+
+TcpListener::~TcpListener() {
+ if (closeFd) closesocket(fd);
+}
+
+void TcpListener::shutdown()
+{
+#ifdef WIN32
+ closesocket(getFd());
+#else
+ ::shutdown(getFd(), 2);
+#endif
+}
+
+
+Socket*
+TcpListener::accept() {
+ int new_sock = -1;
+
+ // Accept an incoming connection
+ if ((new_sock = ::accept(fd, 0, 0)) < 0)
+ throw SocketException("unable to accept new connection", errorNumber);
+
+#ifndef WIN32
+ // - By default, close the socket on exec()
+ fcntl(new_sock, F_SETFD, FD_CLOEXEC);
+#endif
+
+ // Disable Nagle's algorithm
+ int one = 1;
+ if (setsockopt(new_sock, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&one, sizeof(one)) < 0) {
+ int e = errorNumber;
+ closesocket(new_sock);
+ throw SocketException("unable to setsockopt TCP_NODELAY", e);
+ }
+
+ // Create the socket object & check connection is allowed
+ TcpSocket* s = new TcpSocket(new_sock);
+ if (filter && !filter->verifyConnection(s)) {
+ delete s;
+ return 0;
+ }
+ return s;
+}
+
+void TcpListener::getMyAddresses(std::list<char*>* result) {
+ const hostent* addrs = gethostbyname(0);
+ if (addrs == 0)
+ throw rdr::SystemException("gethostbyname", errorNumber);
+ if (addrs->h_addrtype != AF_INET)
+ throw rdr::Exception("getMyAddresses: bad family");
+ for (int i=0; addrs->h_addr_list[i] != 0; i++) {
+ const char* addrC = inet_ntoa(*((struct in_addr*)addrs->h_addr_list[i]));
+ char* addr = new char[strlen(addrC)+1];
+ strcpy(addr, addrC);
+ result->push_back(addr);
+ }
+}
+
+int TcpListener::getMyPort() {
+ return TcpSocket::getSockPort(getFd());
+}
+
+
+TcpFilter::TcpFilter(const char* spec) {
+ rfb::CharArray tmp;
+ tmp.buf = rfb::strDup(spec);
+ while (tmp.buf) {
+ rfb::CharArray first;
+ rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf);
+ if (strlen(first.buf))
+ filter.push_back(parsePattern(first.buf));
+ }
+}
+
+TcpFilter::~TcpFilter() {
+}
+
+
+static bool
+patternMatchIP(const TcpFilter::Pattern& pattern, const char* value) {
+ unsigned long address = inet_addr(value);
+ if (address == INADDR_NONE) return false;
+ return ((pattern.address & pattern.mask) == (address & pattern.mask));
+}
+
+bool
+TcpFilter::verifyConnection(Socket* s) {
+ rfb::CharArray name;
+
+ name.buf = s->getPeerAddress();
+ std::list<TcpFilter::Pattern>::iterator i;
+ for (i=filter.begin(); i!=filter.end(); i++) {
+ if (patternMatchIP(*i, name.buf)) {
+ switch ((*i).action) {
+ case Accept:
+ vlog.debug("ACCEPT %s", name.buf);
+ return true;
+ case Query:
+ vlog.debug("QUERY %s", name.buf);
+ return queryUserAcceptConnection(s);
+ case Reject:
+ vlog.debug("REJECT %s", name.buf);
+ return false;
+ }
+ }
+ }
+
+ vlog.debug("[REJECT] %s", name.buf);
+ return false;
+}
+
+
+TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
+ TcpFilter::Pattern pattern;
+
+ bool expandMask = false;
+ rfb::CharArray addr, mask;
+
+ if (rfb::strSplit(&p[1], '/', &addr.buf, &mask.buf)) {
+ if (rfb::strContains(mask.buf, '.')) {
+ pattern.mask = inet_addr(mask.buf);
+ } else {
+ pattern.mask = atoi(mask.buf);
+ expandMask = true;
+ }
+ } else {
+ pattern.mask = 32;
+ expandMask = true;
+ }
+ if (expandMask) {
+ unsigned long expanded = 0;
+ // *** check endianness!
+ for (int i=0; i<(int)pattern.mask; i++)
+ expanded |= 1<<(31-i);
+ pattern.mask = htonl(expanded);
+ }
+
+ pattern.address = inet_addr(addr.buf) & pattern.mask;
+ if ((pattern.address == INADDR_NONE) ||
+ (pattern.address == 0)) pattern.mask = 0;
+
+ switch(p[0]) {
+ case '+': pattern.action = TcpFilter::Accept; break;
+ case '-': pattern.action = TcpFilter::Reject; break;
+ case '?': pattern.action = TcpFilter::Query; break;
+ };
+
+ return pattern;
+}
+
+char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) {
+ in_addr tmp;
+ rfb::CharArray addr, mask;
+ tmp.s_addr = p.address;
+ addr.buf = rfb::strDup(inet_ntoa(tmp));
+ tmp.s_addr = p.mask;
+ mask.buf = rfb::strDup(inet_ntoa(tmp));
+ char* result = new char[strlen(addr.buf)+1+strlen(mask.buf)+1+1];
+ switch (p.action) {
+ case Accept: result[0] = '+'; break;
+ case Reject: result[0] = '-'; break;
+ case Query: result[0] = '?'; break;
+ };
+ result[1] = 0;
+ strcat(result, addr.buf);
+ strcat(result, "/");
+ strcat(result, mask.buf);
+ return result;
+}