123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- /* Copyright (C) 2002-2005 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 <stdlib.h>
- #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
- #ifndef INADDR_LOOPBACK
- #define INADDR_LOOPBACK ((unsigned long)0x7F000001)
- #endif
-
- using namespace network;
- using namespace rdr;
-
- static rfb::LogWriter vlog("TcpSocket");
-
- /* Tunnelling support. */
- int network::findFreeTcpPort (void)
- {
- int sock, port;
- struct sockaddr_in addr;
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = INADDR_ANY;
-
- if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0)
- throw SocketException ("unable to create socket", errorNumber);
-
- for (port = TUNNEL_PORT_OFFSET + 99; port > TUNNEL_PORT_OFFSET; port--) {
- addr.sin_port = htons ((unsigned short) port);
- if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) == 0) {
- closesocket (sock);
- return port;
- }
- }
- throw SocketException ("no free port in range", 0);
- return 0;
- }
-
-
- // -=- Socket initialisation
- static bool socketsInitialised = false;
- static void initSockets() {
- if (socketsInitialised)
- return;
- #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
- socketsInitialised = true;
- }
-
-
- // -=- 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
- initSockets();
- 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((char *)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((char *)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
- for (;;) {
- if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
- int e = errorNumber;
- #ifndef WIN32
- if (e == EINTR)
- continue;
- #endif
- closesocket(sock);
- throw SocketException("unable to connect to host", e);
- } else break;
- }
-
- // Disable Nagle's algorithm, to reduce latency
- enableNagles(sock, false);
-
- // Create the input and output streams
- instream = new FdInStream(sock);
- outstream = new FdOutStream(sock);
- ownStreams = 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()
- {
- Socket::shutdown();
- ::shutdown(getFd(), 2);
- }
-
- bool TcpSocket::enableNagles(int sock, bool enable) {
- int one = enable ? 0 : 1;
- if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
- (char *)&one, sizeof(one)) < 0) {
- int e = errorNumber;
- vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
- return false;
- }
- return true;
- }
-
- 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;
- }
-
- initSockets();
- 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(fd, F_SETFD, FD_CLOEXEC);
-
- int one = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
- (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, to reduce latency
- TcpSocket::enableNagles(new_sock, false);
-
- // 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((char *)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);
- s->setRequiresQuery();
- return true;
- 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;
- }
|