diff options
-rw-r--r-- | common/network/CMakeLists.txt | 9 | ||||
-rw-r--r-- | common/network/Socket.cxx | 183 | ||||
-rw-r--r-- | common/network/Socket.h | 66 | ||||
-rw-r--r-- | common/network/TcpSocket.cxx | 209 | ||||
-rw-r--r-- | common/network/TcpSocket.h | 31 | ||||
-rw-r--r-- | common/network/UnixSocket.cxx | 171 | ||||
-rw-r--r-- | common/network/UnixSocket.h | 60 | ||||
-rw-r--r-- | common/rfb/Configuration.cxx | 2 | ||||
-rw-r--r-- | unix/x0vncserver/x0vncserver.cxx | 20 | ||||
-rw-r--r-- | unix/x0vncserver/x0vncserver.man | 9 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/RFBGlue.cc | 4 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/XserverDesktop.cc | 14 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/XserverDesktop.h | 12 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/Xvnc.man | 9 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/vncExt.c | 1 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/vncExtInit.cc | 36 | ||||
-rw-r--r-- | unix/xserver/hw/vnc/vncExtInit.h | 1 | ||||
-rw-r--r-- | vncviewer/CConn.cxx | 18 | ||||
-rw-r--r-- | vncviewer/vncviewer.cxx | 18 | ||||
-rw-r--r-- | win/winvnc/ManagedListener.cxx | 8 | ||||
-rw-r--r-- | win/winvnc/ManagedListener.h | 2 |
21 files changed, 624 insertions, 259 deletions
diff --git a/common/network/CMakeLists.txt b/common/network/CMakeLists.txt index b624c8e6..d00ca452 100644 --- a/common/network/CMakeLists.txt +++ b/common/network/CMakeLists.txt @@ -1,8 +1,15 @@ include_directories(${CMAKE_SOURCE_DIR}/common) -add_library(network STATIC +set(NETWORK_SOURCES + Socket.cxx TcpSocket.cxx) +if(NOT WIN32) + set(NETWORK_SOURCES ${NETWORK_SOURCES} UnixSocket.cxx) +endif() + +add_library(network STATIC ${NETWORK_SOURCES}) + if(WIN32) target_link_libraries(network ws2_32) endif() diff --git a/common/network/Socket.cxx b/common/network/Socket.cxx new file mode 100644 index 00000000..9dd8bfe1 --- /dev/null +++ b/common/network/Socket.cxx @@ -0,0 +1,183 @@ +/* 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 HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef WIN32 +//#include <io.h> +#include <winsock2.h> +#include <ws2tcpip.h> +#define errorNumber WSAGetLastError() +#else +#define errorNumber errno +#define closesocket close +#include <sys/socket.h> +#endif + +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <errno.h> + +#include <network/Socket.h> + +using namespace network; + +// -=- Socket initialisation +static bool socketsInitialised = false; +void network::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; +} + +bool network::isSocketListening(int sock) +{ + int listening = 0; + socklen_t listening_size = sizeof(listening); + if (getsockopt(sock, SOL_SOCKET, SO_ACCEPTCONN, + (char *)&listening, &listening_size) < 0) + return false; + return listening != 0; +} + +Socket::Socket(int fd) + : instream(0), outstream(0), + isShutdown_(false), queryConnection(false) +{ + initSockets(); + setFd(fd); +} + +Socket::Socket() + : instream(0), outstream(0), + isShutdown_(false), queryConnection(false) +{ + initSockets(); +} + +Socket::~Socket() +{ + if (instream && outstream) + closesocket(getFd()); + delete instream; + delete outstream; +} + +// if shutdown() is overridden then the override MUST call on to here +void Socket::shutdown() +{ + isShutdown_ = true; + ::shutdown(getFd(), 2); +} + +bool Socket::isShutdown() const +{ + return isShutdown_; +} + +// Was there a "?" in the ConnectionFilter used to accept this Socket? +void Socket::setRequiresQuery() +{ + queryConnection = true; +} + +bool Socket::requiresQuery() const +{ + return queryConnection; +} + +void Socket::setFd(int fd) +{ +#ifndef WIN32 + // - By default, close the socket on exec() + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + + instream = new rdr::FdInStream(fd); + outstream = new rdr::FdOutStream(fd); + isShutdown_ = false; +} + +SocketListener::SocketListener(int fd) + : fd(fd), filter(0) +{ + initSockets(); +} + +SocketListener::SocketListener() + : fd(-1), filter(0) +{ + initSockets(); +} + +SocketListener::~SocketListener() +{ + if (fd != -1) + closesocket(fd); +} + +void SocketListener::shutdown() +{ +#ifdef WIN32 + closesocket(fd); + fd = -1; +#else + ::shutdown(fd, 2); +#endif +} + +Socket* SocketListener::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); + + // Create the socket object & check connection is allowed + Socket* s = createSocket(new_sock); + if (filter && !filter->verifyConnection(s)) { + delete s; + return NULL; + } + + return s; +} + +void SocketListener::listen(int sock) +{ + // - Set it to be a listening socket + if (::listen(sock, 5) < 0) { + int e = errorNumber; + closesocket(sock); + throw SocketException("unable to set socket to listening mode", e); + } + + fd = sock; +} diff --git a/common/network/Socket.h b/common/network/Socket.h index 7a30cacf..bfda8a57 100644 --- a/common/network/Socket.h +++ b/common/network/Socket.h @@ -30,52 +30,40 @@ namespace network { + void initSockets(); + + bool isSocketListening(int sock); + class Socket { public: - Socket(int fd) - : instream(new rdr::FdInStream(fd)), - outstream(new rdr::FdOutStream(fd)), - ownStreams(true), isShutdown_(false), - queryConnection(false) {} - virtual ~Socket() { - if (ownStreams) { - delete instream; - delete outstream; - } - } + Socket(int fd); + virtual ~Socket(); + rdr::FdInStream &inStream() {return *instream;} rdr::FdOutStream &outStream() {return *outstream;} int getFd() {return outstream->getFd();} - // if shutdown() is overridden then the override MUST call on to here - virtual void shutdown() {isShutdown_ = true;} - bool isShutdown() const {return isShutdown_;} - virtual bool cork(bool enable) = 0; + void shutdown(); + bool isShutdown() const; - // information about this end of the socket - virtual int getMyPort() = 0; + virtual bool cork(bool enable) = 0; // information about the remote end of the socket virtual char* getPeerAddress() = 0; // a string e.g. "192.168.0.1" - virtual int getPeerPort() = 0; virtual char* getPeerEndpoint() = 0; // <address>::<port> - // Is the remote end on the same machine? - virtual bool sameMachine() = 0; - // Was there a "?" in the ConnectionFilter used to accept this Socket? - void setRequiresQuery() {queryConnection = true;} - bool requiresQuery() const {return queryConnection;} + void setRequiresQuery(); + bool requiresQuery() const; protected: - Socket() : instream(0), outstream(0), ownStreams(false), - isShutdown_(false), queryConnection(false) {} - Socket(rdr::FdInStream* i, rdr::FdOutStream* o, bool own) - : instream(i), outstream(o), ownStreams(own), - isShutdown_(false), queryConnection(false) {} + Socket(); + + void setFd(int fd); + + private: rdr::FdInStream* instream; rdr::FdOutStream* outstream; - bool ownStreams; bool isShutdown_; bool queryConnection; }; @@ -88,20 +76,32 @@ namespace network { class SocketListener { public: - SocketListener() : fd(0), filter(0) {} - virtual ~SocketListener() {} + SocketListener(int fd); + virtual ~SocketListener(); // shutdown() stops the socket from accepting further connections - virtual void shutdown() = 0; + void shutdown(); // accept() returns a new Socket object if there is a connection // attempt in progress AND if the connection passes the filter // if one is installed. Otherwise, returns 0. - virtual Socket* accept() = 0; + Socket* accept(); + + virtual int getMyPort() = 0; // setFilter() applies the specified filter to all new connections void setFilter(ConnectionFilter* f) {filter = f;} int getFd() {return fd;} + + protected: + SocketListener(); + + void listen(int fd); + + // createSocket() should create a new socket of the correct class + // for the given file descriptor + virtual Socket* createSocket(int fd) = 0; + protected: int fd; ConnectionFilter* filter; diff --git a/common/network/TcpSocket.cxx b/common/network/TcpSocket.cxx index 9603c385..51d77c76 100644 --- a/common/network/TcpSocket.cxx +++ b/common/network/TcpSocket.cxx @@ -28,21 +28,17 @@ #else #define errorNumber errno #define closesocket close -#include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/tcp.h> #include <netdb.h> #include <errno.h> -#include <string.h> -#include <signal.h> -#include <fcntl.h> #endif #include <stdlib.h> #include <unistd.h> + #include <network/TcpSocket.h> -#include <rfb/util.h> #include <rfb/LogWriter.h> #include <rfb/Configuration.h> @@ -99,40 +95,35 @@ int network::findFreeTcpPort (void) return ntohs(addr.sin_port); } +int network::getSockPort(int sock) +{ + vnc_sockaddr_t sa; + socklen_t sa_size = sizeof(sa); + if (getsockname(sock, &sa.u.sa, &sa_size) < 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; + switch (sa.u.sa.sa_family) { + case AF_INET6: + return ntohs(sa.u.sin6.sin6_port); + default: + return ntohs(sa.u.sin.sin_port); + } } - // -=- TcpSocket -TcpSocket::TcpSocket(int sock, bool close) - : Socket(new FdInStream(sock), new FdOutStream(sock), true), closeFd(close) +TcpSocket::TcpSocket(int sock) : Socket(sock) { + // Disable Nagle's algorithm, to reduce latency + enableNagles(false); } TcpSocket::TcpSocket(const char *host, int port) - : closeFd(true) { int sock, err, result; struct addrinfo *ai, *current, hints; // - Create a socket - initSockets(); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; @@ -214,27 +205,11 @@ TcpSocket::TcpSocket(const char *host, int port) throw SocketException("unable connect to socket", err); } -#ifndef WIN32 - // - By default, close the socket on exec() - fcntl(sock, F_SETFD, FD_CLOEXEC); -#endif + // Take proper ownership of the socket + setFd(sock); // 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()); -} - -int TcpSocket::getMyPort() { - return getSockPort(getFd()); + enableNagles(false); } char* TcpSocket::getPeerAddress() { @@ -281,25 +256,20 @@ char* TcpSocket::getPeerAddress() { return rfb::strDup(""); } -int TcpSocket::getPeerPort() { +char* TcpSocket::getPeerEndpoint() { + rfb::CharArray address; address.buf = getPeerAddress(); vnc_sockaddr_t sa; socklen_t sa_size = sizeof(sa); + int port; getpeername(getFd(), &sa.u.sa, &sa_size); - switch (sa.u.sa.sa_family) { - case AF_INET6: - return ntohs(sa.u.sin6.sin6_port); - case AF_INET: - return ntohs(sa.u.sin.sin_port); - default: - return 0; - } -} - -char* TcpSocket::getPeerEndpoint() { - rfb::CharArray address; address.buf = getPeerAddress(); - int port = getPeerPort(); + if (sa.u.sa.sa_family == AF_INET6) + port = ntohs(sa.u.sin6.sin6_port); + else if (sa.u.sa.sa_family == AF_INET) + port = ntohs(sa.u.sin.sin_port); + else + port = 0; int buflen = strlen(address.buf) + 32; char* buffer = new char[buflen]; @@ -307,40 +277,9 @@ char* TcpSocket::getPeerEndpoint() { return buffer; } -bool TcpSocket::sameMachine() { - vnc_sockaddr_t peeraddr, myaddr; - socklen_t addrlen; - - addrlen = sizeof(peeraddr); - if (getpeername(getFd(), &peeraddr.u.sa, &addrlen) < 0) - throw SocketException ("unable to get peer address", errorNumber); - - addrlen = sizeof(myaddr); /* need to reset, since getpeername overwrote */ - if (getsockname(getFd(), &myaddr.u.sa, &addrlen) < 0) - throw SocketException ("unable to get my address", errorNumber); - - if (peeraddr.u.sa.sa_family != myaddr.u.sa.sa_family) - return false; - - if (peeraddr.u.sa.sa_family == AF_INET6) - return IN6_ARE_ADDR_EQUAL(&peeraddr.u.sin6.sin6_addr, - &myaddr.u.sin6.sin6_addr); - if (peeraddr.u.sa.sa_family == AF_INET) - return (peeraddr.u.sin.sin_addr.s_addr == myaddr.u.sin.sin_addr.s_addr); - - // No idea what this is. Assume we're on different machines. - return false; -} - -void TcpSocket::shutdown() -{ - Socket::shutdown(); - ::shutdown(getFd(), 2); -} - -bool TcpSocket::enableNagles(int sock, bool enable) { +bool TcpSocket::enableNagles(bool enable) { int one = enable ? 0 : 1; - if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + if (setsockopt(getFd(), IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { int e = errorNumber; vlog.error("unable to setsockopt TCP_NODELAY: %d", e); @@ -360,34 +299,8 @@ bool TcpSocket::cork(bool enable) { #endif } -bool TcpSocket::isListening(int sock) +TcpListener::TcpListener(int sock) : SocketListener(sock) { - int listening = 0; - socklen_t listening_size = sizeof(listening); - if (getsockopt(sock, SOL_SOCKET, SO_ACCEPTCONN, - (char *)&listening, &listening_size) < 0) - return false; - return listening != 0; -} - -int TcpSocket::getSockPort(int sock) -{ - vnc_sockaddr_t sa; - socklen_t sa_size = sizeof(sa); - if (getsockname(sock, &sa.u.sa, &sa_size) < 0) - return 0; - - switch (sa.u.sa.sa_family) { - case AF_INET6: - return ntohs(sa.u.sin6.sin6_port); - default: - return ntohs(sa.u.sin.sin_port); - } -} - -TcpListener::TcpListener(int sock) -{ - fd = sock; } TcpListener::TcpListener(const struct sockaddr *listenaddr, @@ -397,8 +310,6 @@ TcpListener::TcpListener(const struct sockaddr *listenaddr, vnc_sockaddr_t sa; int sock; - initSockets(); - if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0) throw SocketException("unable to create listening socket", errorNumber); @@ -436,53 +347,11 @@ TcpListener::TcpListener(const struct sockaddr *listenaddr, throw SocketException("failed to bind socket", e); } - // - Set it to be a listening socket - if (listen(sock, 5) < 0) { - int e = errorNumber; - closesocket(sock); - throw SocketException("unable to set socket to listening mode", e); - } - - fd = sock; + listen(sock); } -TcpListener::~TcpListener() { - 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; +Socket* TcpListener::createSocket(int fd) { + return new TcpSocket(fd); } void TcpListener::getMyAddresses(std::list<char*>* result) { @@ -528,11 +397,11 @@ void TcpListener::getMyAddresses(std::list<char*>* result) { } int TcpListener::getMyPort() { - return TcpSocket::getSockPort(getFd()); + return getSockPort(getFd()); } -void network::createLocalTcpListeners(std::list<TcpListener*> *listeners, +void network::createLocalTcpListeners(std::list<SocketListener*> *listeners, int port) { struct addrinfo ai[2]; @@ -562,7 +431,7 @@ void network::createLocalTcpListeners(std::list<TcpListener*> *listeners, createTcpListeners(listeners, ai); } -void network::createTcpListeners(std::list<TcpListener*> *listeners, +void network::createTcpListeners(std::list<SocketListener*> *listeners, const char *addr, int port) { @@ -594,11 +463,11 @@ void network::createTcpListeners(std::list<TcpListener*> *listeners, } } -void network::createTcpListeners(std::list<TcpListener*> *listeners, +void network::createTcpListeners(std::list<SocketListener*> *listeners, const struct addrinfo *ai) { const struct addrinfo *current; - std::list<TcpListener*> new_listeners; + std::list<SocketListener*> new_listeners; initSockets(); diff --git a/common/network/TcpSocket.h b/common/network/TcpSocket.h index c1b142ff..eb6c0958 100644 --- a/common/network/TcpSocket.h +++ b/common/network/TcpSocket.h @@ -48,48 +48,41 @@ namespace network { /* Tunnelling support. */ int findFreeTcpPort (void); + int getSockPort(int sock); + class TcpSocket : public Socket { public: - TcpSocket(int sock, bool close=true); + TcpSocket(int sock); TcpSocket(const char *name, int port); - virtual ~TcpSocket(); - - virtual int getMyPort(); virtual char* getPeerAddress(); - virtual int getPeerPort(); virtual char* getPeerEndpoint(); - virtual bool sameMachine(); - virtual void shutdown(); virtual bool cork(bool enable); - static bool enableNagles(int sock, bool enable); - static bool isListening(int sock); - static int getSockPort(int sock); - private: - bool closeFd; + protected: + bool enableNagles(bool enable); }; class TcpListener : public SocketListener { public: TcpListener(const struct sockaddr *listenaddr, socklen_t listenaddrlen); TcpListener(int sock); - virtual ~TcpListener(); - virtual void shutdown(); - virtual Socket* accept(); + virtual int getMyPort(); static void getMyAddresses(std::list<char*>* result); - int getMyPort(); + + protected: + virtual Socket* createSocket(int fd); }; - void createLocalTcpListeners(std::list<TcpListener*> *listeners, + void createLocalTcpListeners(std::list<SocketListener*> *listeners, int port); - void createTcpListeners(std::list<TcpListener*> *listeners, + void createTcpListeners(std::list<SocketListener*> *listeners, const char *addr, int port); - void createTcpListeners(std::list<TcpListener*> *listeners, + void createTcpListeners(std::list<SocketListener*> *listeners, const struct addrinfo *ai); typedef struct vnc_sockaddr { diff --git a/common/network/UnixSocket.cxx b/common/network/UnixSocket.cxx new file mode 100644 index 00000000..bfabc141 --- /dev/null +++ b/common/network/UnixSocket.cxx @@ -0,0 +1,171 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (c) 2012 University of Oslo. 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 HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <stddef.h> + +#include <network/UnixSocket.h> +#include <rfb/LogWriter.h> + +using namespace network; +using namespace rdr; + +static rfb::LogWriter vlog("UnixSocket"); + +// -=- UnixSocket + +UnixSocket::UnixSocket(int sock) : Socket(sock) +{ +} + +UnixSocket::UnixSocket(const char *path) +{ + int sock, err, result; + sockaddr_un addr; + socklen_t salen; + + if (strlen(path) >= sizeof(addr.sun_path)) + throw SocketException("socket path is too long", ENAMETOOLONG); + + // - Create a socket + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock == -1) + throw SocketException("unable to create socket", errno); + + // - Attempt to connect + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, path); + salen = sizeof(addr); + while ((result = connect(sock, (sockaddr *)&addr, salen)) == -1) { + err = errno; + close(sock); + break; + } + + if (result == -1) + throw SocketException("unable connect to socket", err); + + setFd(sock); +} + +char* UnixSocket::getPeerAddress() { + struct sockaddr_un addr; + socklen_t salen; + + // AF_UNIX only has a single address (the server side). + // Unfortunately we don't know which end we are, so we'll have to + // test a bit. + + salen = sizeof(addr); + if (getpeername(getFd(), (struct sockaddr *)&addr, &salen) != 0) { + vlog.error("unable to get peer name for socket"); + return rfb::strDup(""); + } + + if (salen > offsetof(struct sockaddr_un, sun_path)) + return rfb::strDup(addr.sun_path); + + salen = sizeof(addr); + if (getsockname(getFd(), (struct sockaddr *)&addr, &salen) != 0) { + vlog.error("unable to get local name for socket"); + return rfb::strDup(""); + } + + if (salen > offsetof(struct sockaddr_un, sun_path)) + return rfb::strDup(addr.sun_path); + + // socketpair() will create unnamed sockets + + return rfb::strDup("(unnamed UNIX socket)"); +} + +char* UnixSocket::getPeerEndpoint() { + return getPeerAddress(); +} + +bool UnixSocket::cork(bool enable) +{ + return true; +} + +UnixListener::UnixListener(const char *path, int mode) +{ + struct sockaddr_un addr; + mode_t saved_umask; + int err, result; + + if (strlen(path) >= sizeof(addr.sun_path)) + throw SocketException("socket path is too long", ENAMETOOLONG); + + // - Create a socket + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + throw SocketException("unable to create listening socket", errno); + + // - Delete existing socket (ignore result) + unlink(path); + + // - Attempt to bind to the requested path + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, path); + saved_umask = umask(0777); + result = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + err = errno; + umask(saved_umask); + if (result < 0) { + close(fd); + throw SocketException("unable to bind listening socket", err); + } + + // - Set socket mode + if (chmod(path, mode) < 0) { + err = errno; + close(fd); + throw SocketException("unable to set socket mode", err); + } + + listen(fd); +} + +UnixListener::~UnixListener() +{ + struct sockaddr_un addr; + socklen_t salen = sizeof(addr); + + if (getsockname(getFd(), (struct sockaddr *)&addr, &salen) == 0) + unlink(addr.sun_path); +} + +Socket* UnixListener::createSocket(int fd) { + return new UnixSocket(fd); +} + +int UnixListener::getMyPort() { + return 0; +} diff --git a/common/network/UnixSocket.h b/common/network/UnixSocket.h new file mode 100644 index 00000000..1ffca456 --- /dev/null +++ b/common/network/UnixSocket.h @@ -0,0 +1,60 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (c) 2012 University of Oslo. 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. + */ + +// -=- UnixSocket.h - base-class for UNIX stream sockets. +// This header also defines the UnixListener class, used +// to listen for incoming socket connections over UNIX +// +// NB: Any file descriptors created by the UnixSocket or +// UnixListener classes are close-on-exec if the OS supports +// it. UnixSockets initialised with a caller-supplied fd +// are NOT set to close-on-exec. + +#ifndef __NETWORK_UNIX_SOCKET_H__ +#define __NETWORK_UNIX_SOCKET_H__ + +#include <network/Socket.h> + +namespace network { + + class UnixSocket : public Socket { + public: + UnixSocket(int sock); + UnixSocket(const char *name); + + virtual char* getPeerAddress(); + virtual char* getPeerEndpoint(); + + virtual bool cork(bool enable); + }; + + class UnixListener : public SocketListener { + public: + UnixListener(const char *listenaddr, int mode); + virtual ~UnixListener(); + + int getMyPort(); + + protected: + virtual Socket* createSocket(int fd); + }; + +} + +#endif // __NETWORK_UNIX_SOCKET_H__ diff --git a/common/rfb/Configuration.cxx b/common/rfb/Configuration.cxx index 418a0c93..619c4d5a 100644 --- a/common/rfb/Configuration.cxx +++ b/common/rfb/Configuration.cxx @@ -338,7 +338,7 @@ bool IntParameter::setParam(const char* v) { if (immutable) return true; vlog.debug("set %s(Int) to %s", getName(), v); - int i = atoi(v); + int i = strtol(v, NULL, 0); if (i < minValue || i > maxValue) return false; value = i; diff --git a/unix/x0vncserver/x0vncserver.cxx b/unix/x0vncserver/x0vncserver.cxx index c8098f70..c08572be 100644 --- a/unix/x0vncserver/x0vncserver.cxx +++ b/unix/x0vncserver/x0vncserver.cxx @@ -31,6 +31,7 @@ #include <rfb/Configuration.h> #include <rfb/Timer.h> #include <network/TcpSocket.h> +#include <network/UnixSocket.h> #include <vncconfig/QueryConnectDialog.h> @@ -58,6 +59,8 @@ IntParameter maxProcessorUsage("MaxProcessorUsage", "Maximum percentage of " "CPU time to be consumed", 35); StringParameter displayname("display", "The X display", ""); IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900); +StringParameter rfbunixpath("rfbunixpath", "Unix socket to listen for RFB protocol", ""); +IntParameter rfbunixmode("rfbunixmode", "Unix socket access mode", 0600); IntParameter queryConnectTimeout("QueryConnectTimeout", "Number of seconds to show the Accept Connection dialog before " "rejecting the connection", @@ -291,7 +294,7 @@ int main(int argc, char** argv) signal(SIGINT, CleanupSignalHandler); signal(SIGTERM, CleanupSignalHandler); - std::list<TcpListener*> listeners; + std::list<SocketListener*> listeners; try { TXWindow::init(dpy,"x0vncserver"); @@ -307,13 +310,18 @@ int main(int argc, char** argv) QueryConnHandler qcHandler(dpy, &server); server.setQueryConnectionHandler(&qcHandler); - createTcpListeners(&listeners, 0, (int)rfbport); - vlog.info("Listening on port %d", (int)rfbport); + if (rfbunixpath.getValueStr()[0] != '\0') { + listeners.push_back(new network::UnixListener(rfbunixpath, rfbunixmode)); + vlog.info("Listening on %s (mode %04o)", (const char*)rfbunixpath, (int)rfbunixmode); + } else { + createTcpListeners(&listeners, 0, (int)rfbport); + vlog.info("Listening on port %d", (int)rfbport); + } const char *hostsData = hostsFile.getData(); FileTcpFilter fileTcpFilter(hostsData); if (strlen(hostsData) != 0) - for (std::list<TcpListener*>::iterator i = listeners.begin(); + for (std::list<SocketListener*>::iterator i = listeners.begin(); i != listeners.end(); i++) (*i)->setFilter(&fileTcpFilter); @@ -335,7 +343,7 @@ int main(int argc, char** argv) FD_ZERO(&wfds); FD_SET(ConnectionNumber(dpy), &rfds); - for (std::list<TcpListener*>::iterator i = listeners.begin(); + for (std::list<SocketListener*>::iterator i = listeners.begin(); i != listeners.end(); i++) FD_SET((*i)->getFd(), &rfds); @@ -387,7 +395,7 @@ int main(int argc, char** argv) } // Accept new VNC connections - for (std::list<TcpListener*>::iterator i = listeners.begin(); + for (std::list<SocketListener*>::iterator i = listeners.begin(); i != listeners.end(); i++) { if (FD_ISSET((*i)->getFd(), &rfds)) { diff --git a/unix/x0vncserver/x0vncserver.man b/unix/x0vncserver/x0vncserver.man index 5c8729e2..5dc0b052 100644 --- a/unix/x0vncserver/x0vncserver.man +++ b/unix/x0vncserver/x0vncserver.man @@ -66,6 +66,15 @@ Use IPv4 for incoming and outgoing connections. Default is on. Use IPv6 for incoming and outgoing connections. Default is on. . .TP +.B \-rfbunixpath \fIpath\fP +Specifies the path of a Unix domain socket on which x0vncserver listens for +connections from viewers, instead of listening on a TCP port. +. +.TP +.B \-rfbunixmode \fImode\fP +Specifies the mode of the Unix domain socket. The default is 0600. +. +.TP .B \-Log \fIlogname\fP:\fIdest\fP:\fIlevel\fP Configures the debug log settings. \fIdest\fP can currently be \fBstderr\fP, \fBstdout\fP or \fBsyslog\fP, and \fIlevel\fP is between 0 and 100, 100 meaning diff --git a/unix/xserver/hw/vnc/RFBGlue.cc b/unix/xserver/hw/vnc/RFBGlue.cc index c9153364..160177bd 100644 --- a/unix/xserver/hw/vnc/RFBGlue.cc +++ b/unix/xserver/hw/vnc/RFBGlue.cc @@ -192,14 +192,14 @@ void vncListParams(int width, int nameWidth) int vncGetSocketPort(int fd) { - return network::TcpSocket::getSockPort(fd); + return network::getSockPort(fd); } int vncIsTCPPortUsed(int port) { try { // Attempt to create TCPListeners on that port. - std::list<network::TcpListener*> dummy; + std::list<network::SocketListener*> dummy; network::createTcpListeners (&dummy, 0, port); while (!dummy.empty()) { delete dummy.back(); diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index 080943d0..4aac7655 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -32,7 +32,7 @@ #include <fcntl.h> #include <sys/utsname.h> -#include <network/TcpSocket.h> +#include <network/Socket.h> #include <rfb/Exception.h> #include <rfb/VNCServerST.h> #include <rfb/HTTPServer.h> @@ -107,8 +107,8 @@ public: XserverDesktop::XserverDesktop(int screenIndex_, - std::list<network::TcpListener*> listeners_, - std::list<network::TcpListener*> httpListeners_, + std::list<network::SocketListener*> listeners_, + std::list<network::SocketListener*> httpListeners_, const char* name, const rfb::PixelFormat &pf, int width, int height, void* fbptr, int stride) @@ -127,13 +127,13 @@ XserverDesktop::XserverDesktop(int screenIndex_, if (!httpListeners.empty ()) httpServer = new FileHTTPServer(this); - for (std::list<TcpListener*>::iterator i = listeners.begin(); + for (std::list<SocketListener*>::iterator i = listeners.begin(); i != listeners.end(); i++) { vncSetNotifyFd((*i)->getFd(), screenIndex, true, false); } - for (std::list<TcpListener*>::iterator i = httpListeners.begin(); + for (std::list<SocketListener*>::iterator i = httpListeners.begin(); i != httpListeners.end(); i++) { vncSetNotifyFd((*i)->getFd(), screenIndex, true, false); @@ -386,10 +386,10 @@ void XserverDesktop::handleSocketEvent(int fd, bool read, bool write) } bool XserverDesktop::handleListenerEvent(int fd, - std::list<TcpListener*>* sockets, + std::list<SocketListener*>* sockets, SocketServer* sockserv) { - std::list<TcpListener*>::iterator i; + std::list<SocketListener*>::iterator i; for (i = sockets->begin(); i != sockets->end(); i++) { if ((*i)->getFd() == fd) diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index f1c3e3e7..f866a4cc 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -44,7 +44,7 @@ namespace rfb { class VNCServerST; } -namespace network { class TcpListener; class Socket; class SocketServer; } +namespace network { class SocketListener; class Socket; class SocketServer; } class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer, public rdr::Substitutor, @@ -53,8 +53,8 @@ class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer, public: XserverDesktop(int screenIndex, - std::list<network::TcpListener*> listeners_, - std::list<network::TcpListener*> httpListeners_, + std::list<network::SocketListener*> listeners_, + std::list<network::SocketListener*> httpListeners_, const char* name, const rfb::PixelFormat &pf, int width, int height, void* fbptr, int stride); virtual ~XserverDesktop(); @@ -109,7 +109,7 @@ public: protected: bool handleListenerEvent(int fd, - std::list<network::TcpListener*>* sockets, + std::list<network::SocketListener*>* sockets, network::SocketServer* sockserv); bool handleSocketEvent(int fd, network::SocketServer* sockserv, @@ -122,8 +122,8 @@ private: int screenIndex; rfb::VNCServerST* server; rfb::HTTPServer* httpServer; - std::list<network::TcpListener*> listeners; - std::list<network::TcpListener*> httpListeners; + std::list<network::SocketListener*> listeners; + std::list<network::SocketListener*> httpListeners; bool directFbptr; uint32_t queryConnectId; diff --git a/unix/xserver/hw/vnc/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man index 80b78781..269be9af 100644 --- a/unix/xserver/hw/vnc/Xvnc.man +++ b/unix/xserver/hw/vnc/Xvnc.man @@ -91,6 +91,15 @@ Use IPv4 for incoming and outgoing connections. Default is on. Use IPv6 for incoming and outgoing connections. Default is on. . .TP +.B \-rfbunixpath \fIpath\fP +Specifies the path of a Unix domain socket on which Xvnc listens for +connections from viewers, instead of listening on a TCP port. +. +.TP +.B \-rfbunixmode \fImode\fP +Specifies the mode of the Unix domain socket. The default is 0600. +. +.TP .B \-rfbwait \fItime\fP, \-ClientWaitTimeMillis \fItime\fP Time in milliseconds to wait for a viewer which is blocking the server. This is necessary because the server is single-threaded and sometimes blocks until the diff --git a/unix/xserver/hw/vnc/vncExt.c b/unix/xserver/hw/vnc/vncExt.c index f7ba9fa8..fd65eab6 100644 --- a/unix/xserver/hw/vnc/vncExt.c +++ b/unix/xserver/hw/vnc/vncExt.c @@ -585,6 +585,7 @@ static int SProcVncExtDispatch(ClientPtr client) static void vncResetProc(ExtensionEntry* extEntry) { + vncExtensionClose(); } static void vncClientStateChange(CallbackListPtr * l, void * d, void * p) diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index 54cca953..d6f6b742 100644 --- a/unix/xserver/hw/vnc/vncExtInit.cc +++ b/unix/xserver/hw/vnc/vncExtInit.cc @@ -34,6 +34,7 @@ #include <rfb/Region.h> #include <rfb/ledStates.h> #include <network/TcpSocket.h> +#include <network/UnixSocket.h> #include "XserverDesktop.h" #include "vncExtInit.h" @@ -79,6 +80,8 @@ rfb::IntParameter httpPort("httpPort", "TCP port to listen for HTTP",0); rfb::AliasParameter rfbwait("rfbwait", "Alias for ClientWaitTimeMillis", &rfb::Server::clientWaitTimeMillis); rfb::IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",0); +rfb::StringParameter rfbunixpath("rfbunixpath", "Unix socket to listen for RFB protocol", ""); +rfb::IntParameter rfbunixmode("rfbunixmode", "Unix socket access mode", 0600); rfb::StringParameter desktopName("desktop", "Name of VNC desktop","x11"); rfb::BoolParameter localhostOnly("localhost", "Only allow connections from localhost", @@ -173,14 +176,29 @@ void vncExtensionInit(void) for (int scr = 0; scr < vncGetScreenCount(); scr++) { if (!desktop[scr]) { - std::list<network::TcpListener*> listeners; - std::list<network::TcpListener*> httpListeners; + std::list<network::SocketListener*> listeners; + std::list<network::SocketListener*> httpListeners; if (scr == 0 && vncInetdSock != -1) { - if (network::TcpSocket::isListening(vncInetdSock)) + if (network::isSocketListening(vncInetdSock)) { listeners.push_back(new network::TcpListener(vncInetdSock)); vlog.info("inetd wait"); } + } else if (rfbunixpath.getValueStr()[0] != '\0') { + char path[PATH_MAX]; + int mode = (int)rfbunixmode; + + if (scr == 0) + strncpy(path, rfbunixpath, sizeof(path)); + else + snprintf(path, sizeof(path), "%s.%d", + rfbunixpath.getValueStr(), scr); + path[sizeof(path)-1] = '\0'; + + listeners.push_back(new network::UnixListener(path, mode)); + + vlog.info("Listening for VNC connections on %s (mode %04o)", + path, mode); } else { const char *addr = interface; int port = rfbport; @@ -244,6 +262,18 @@ void vncExtensionInit(void) vncRegisterBlockHandlers(); } +void vncExtensionClose(void) +{ + try { + for (int scr = 0; scr < vncGetScreenCount(); scr++) { + delete desktop[scr]; + desktop[scr] = NULL; + } + } catch (rdr::Exception& e) { + vncFatalError("vncExtInit: %s",e.str()); + } +} + void vncHandleSocketEvent(int fd, int scrIdx, int read, int write) { desktop[scrIdx]->handleSocketEvent(fd, read, write); diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h index e8294340..9414723f 100644 --- a/unix/xserver/hw/vnc/vncExtInit.h +++ b/unix/xserver/hw/vnc/vncExtInit.h @@ -48,6 +48,7 @@ extern int vncFbstride[]; extern int vncInetdSock; void vncExtensionInit(void); +void vncExtensionClose(void); void vncHandleSocketEvent(int fd, int scrIdx, int read, int write); void vncCallBlockHandlers(int* timeout); diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx index 35d6e231..166597e5 100644 --- a/vncviewer/CConn.cxx +++ b/vncviewer/CConn.cxx @@ -39,6 +39,9 @@ #include <rdr/MemInStream.h> #include <rdr/MemOutStream.h> #include <network/TcpSocket.h> +#ifndef WIN32 +#include <network/UnixSocket.h> +#endif #include <FL/Fl.H> #include <FL/fl_ask.H> @@ -106,10 +109,19 @@ CConn::CConn(const char* vncServerName, network::Socket* socket=NULL) if(sock == NULL) { try { - getHostAndPort(vncServerName, &serverHost, &serverPort); +#ifndef WIN32 + if (strchr(vncServerName, '/') != NULL) { + sock = new network::UnixSocket(vncServerName); + serverHost = sock->getPeerAddress(); + vlog.info(_("connected to socket %s"), serverHost); + } else +#endif + { + getHostAndPort(vncServerName, &serverHost, &serverPort); - sock = new network::TcpSocket(serverHost, serverPort); - vlog.info(_("connected to host %s port %d"), serverHost, serverPort); + sock = new network::TcpSocket(serverHost, serverPort); + vlog.info(_("connected to host %s port %d"), serverHost, serverPort); + } } catch (rdr::Exception& e) { vlog.error("%s", e.str()); if (alertOnFatalError) diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx index e305d798..6c0c7384 100644 --- a/vncviewer/vncviewer.cxx +++ b/vncviewer/vncviewer.cxx @@ -376,6 +376,18 @@ potentiallyLoadConfigurationFile(char *vncServerName) (strchr(vncServerName, '\\')) != NULL); if (hasPathSeparator) { +#ifndef WIN32 + struct stat sb; + + // This might be a UNIX socket, we need to check + if (stat(vncServerName, &sb) == -1) { + // Some access problem; let loadViewerParameters() deal with it... + } else { + if ((sb.st_mode & S_IFMT) == S_IFSOCK) + return; + } +#endif + try { const char* newServerName; newServerName = loadViewerParameters(vncServerName); @@ -573,7 +585,7 @@ int main(int argc, char** argv) #endif if (listenMode) { - std::list<TcpListener*> listeners; + std::list<SocketListener*> listeners; try { int port = 5500; if (isdigit(vncServerName[0])) @@ -587,7 +599,7 @@ int main(int argc, char** argv) while (sock == NULL) { fd_set rfds; FD_ZERO(&rfds); - for (std::list<TcpListener*>::iterator i = listeners.begin(); + for (std::list<SocketListener*>::iterator i = listeners.begin(); i != listeners.end(); i++) FD_SET((*i)->getFd(), &rfds); @@ -602,7 +614,7 @@ int main(int argc, char** argv) } } - for (std::list<TcpListener*>::iterator i = listeners.begin (); + for (std::list<SocketListener*>::iterator i = listeners.begin (); i != listeners.end(); i++) if (FD_ISSET((*i)->getFd(), &rfds)) { diff --git a/win/winvnc/ManagedListener.cxx b/win/winvnc/ManagedListener.cxx index 9137238a..a8c6f25e 100644 --- a/win/winvnc/ManagedListener.cxx +++ b/win/winvnc/ManagedListener.cxx @@ -31,7 +31,7 @@ ManagedListener::ManagedListener(SocketManager* mgr) ManagedListener::~ManagedListener() { if (!sockets.empty()) { - std::list<network::TcpListener*>::iterator iter; + std::list<network::SocketListener*>::iterator iter; for (iter = sockets.begin(); iter != sockets.end(); ++iter) manager->remListener(*iter); sockets.clear(); @@ -62,7 +62,7 @@ void ManagedListener::setFilter(const char* filterStr) { delete filter; filter = new network::TcpFilter(filterStr); if (!sockets.empty() && !localOnly) { - std::list<network::TcpListener*>::iterator iter; + std::list<network::SocketListener*>::iterator iter; for (iter = sockets.begin(); iter != sockets.end(); ++iter) (*iter)->setFilter(filter); } @@ -80,7 +80,7 @@ bool ManagedListener::isListening() { } void ManagedListener::refresh() { - std::list<network::TcpListener*>::iterator iter; + std::list<network::SocketListener*>::iterator iter; if (!sockets.empty()) { for (iter = sockets.begin(); iter != sockets.end(); ++iter) manager->remListener(*iter); @@ -107,7 +107,7 @@ void ManagedListener::refresh() { for (iter = sockets.begin(); iter != sockets.end(); ++iter) manager->addListener(*iter, server, addrChangeNotifier); } catch (...) { - std::list<network::TcpListener*>::iterator iter2; + std::list<network::SocketListener*>::iterator iter2; for (iter2 = sockets.begin(); iter2 != iter; ++iter2) manager->remListener(*iter2); for (; iter2 != sockets.end(); ++iter2) diff --git a/win/winvnc/ManagedListener.h b/win/winvnc/ManagedListener.h index e8d3c892..39223c79 100644 --- a/win/winvnc/ManagedListener.h +++ b/win/winvnc/ManagedListener.h @@ -45,7 +45,7 @@ namespace winvnc { protected: void refresh(); - std::list<network::TcpListener*> sockets; + std::list<network::SocketListener*> sockets; network::TcpFilter* filter; rfb::win32::SocketManager* manager; rfb::win32::SocketManager::AddressChangeNotifier* addrChangeNotifier; |