Blob Blame History Raw
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f2656d1..0e325e2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -71,16 +71,9 @@ if(NOT DEFINED BUILD_WINVNC)
set(BUILD_WINVNC 1)
endif()
-# Minimum version is Windows 2000 (5.0)
+# Minimum version is Windows XP SP2 (5.2)
if(WIN32)
- if(NOT CMAKE_SIZEOF_VOID_P MATCHES 8)
- add_definitions(-D_WIN32_IE=0x0500 -D_WIN32_WINNT=0x0500)
- else()
- set(WIN64 1)
- # Win64 doesn't like us requesting a Windows version that didn't have
- # 64-bit support. Request XP (5.1) instead.
- add_definitions(-D_WIN32_IE=0x0501 -D_WIN32_WINNT=0x0501)
- endif()
+ add_definitions(-D_WIN32_IE=0x0502 -D_WIN32_WINNT=0x0502)
endif()
if(CMAKE_SIZEOF_VOID_P MATCHES 8)
@@ -366,20 +359,6 @@ if(ENABLE_PAM)
endif()
set(HAVE_PAM ${ENABLE_PAM})
-# Check for socket functions
-if(WIN32)
- set(CMAKE_EXTRA_INCLUDE_FILES winsock2.h ws2tcpip.h)
- set(CMAKE_REQUIRED_LIBRARIES ws2_32)
-else()
- set(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h)
-endif()
-check_function_exists(inet_aton HAVE_INET_ATON)
-check_function_exists(inet_ntop HAVE_INET_NTOP)
-check_function_exists(getaddrinfo HAVE_GETADDRINFO)
-check_type_size(socklen_t SOCKLEN_T)
-set(CMAKE_EXTRA_INCLUDE_FILES)
-set(CMAKE_REQUIRED_LIBRARIES)
-
# Check for the newer standard string functions
check_function_exists(snprintf HAVE_SNPRINTF)
check_function_exists(strcasecmp HAVE_STRCASECMP)
diff --git a/common/network/TcpSocket.cxx b/common/network/TcpSocket.cxx
index afd66f9..83387ab 100644
--- a/common/network/TcpSocket.cxx
+++ b/common/network/TcpSocket.cxx
@@ -31,10 +31,8 @@
#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>
@@ -42,10 +40,15 @@
#endif
#include <stdlib.h>
+#include <unistd.h>
#include <network/TcpSocket.h>
-#include <os/net.h>
#include <rfb/util.h>
#include <rfb/LogWriter.h>
+#include <rfb/Configuration.h>
+
+#ifdef WIN32
+#include <os/winerrno.h>
+#endif
#ifndef INADDR_NONE
#define INADDR_NONE ((unsigned long)-1)
@@ -54,26 +57,24 @@
#define INADDR_LOOPBACK ((unsigned long)0x7F000001)
#endif
-#if defined(HAVE_GETADDRINFO) && !defined(IN6_ARE_ADDR_EQUAL)
+#ifndef IN6_ARE_ADDR_EQUAL
#define IN6_ARE_ADDR_EQUAL(a,b) \
(memcmp ((const void*)(a), (const void*)(b), sizeof (struct in6_addr)) == 0)
#endif
+// Missing on older Windows and OS X
+#ifndef AI_NUMERICSERV
+#define AI_NUMERICSERV 0
+#endif
+
using namespace network;
using namespace rdr;
-typedef struct vnc_sockaddr {
- union {
- sockaddr sa;
- sockaddr_in sin;
-#ifdef HAVE_GETADDRINFO
- sockaddr_in6 sin6;
-#endif
- } u;
-} vnc_sockaddr_t;
-
static rfb::LogWriter vlog("TcpSocket");
+static rfb::BoolParameter UseIPv4("UseIPv4", "Use IPv4 for incoming and outgoing connections.", true);
+static rfb::BoolParameter UseIPv6("UseIPv6", "Use IPv6 for incoming and outgoing connections.", true);
+
/* Tunnelling support. */
int network::findFreeTcpPort (void)
{
@@ -117,6 +118,23 @@ static void initSockets() {
}
+// -=- Socket duplication help for Windows
+static int dupsocket(int fd)
+{
+#ifdef WIN32
+ int ret;
+ WSAPROTOCOL_INFO info;
+ ret = WSADuplicateSocket(fd, GetCurrentProcessId(), &info);
+ if (ret != 0)
+ throw SocketException("unable to duplicate socket", errorNumber);
+ return WSASocket(info.iAddressFamily, info.iSocketType, info.iProtocol,
+ &info, 0, 0);
+#else
+ return dup(fd);
+#endif
+}
+
+
// -=- TcpSocket
TcpSocket::TcpSocket(int sock, bool close)
@@ -130,14 +148,11 @@ TcpSocket::TcpSocket(const char *host, int port)
int sock, err, result, family;
vnc_sockaddr_t sa;
socklen_t salen;
-#ifdef HAVE_GETADDRINFO
struct addrinfo *ai, *current, hints;
-#endif
// - Create a socket
initSockets();
-#ifdef HAVE_GETADDRINFO
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
@@ -147,13 +162,28 @@ TcpSocket::TcpSocket(const char *host, int port)
if ((result = getaddrinfo(host, NULL, &hints, &ai)) != 0) {
throw Exception("unable to resolve host by name: %s",
- gai_strerror(result));
+ gai_strerror(result));
}
+ // This logic is too complex for the compiler to determine if
+ // sock is properly assigned or not.
+ sock = -1;
+
for (current = ai; current != NULL; current = current->ai_next) {
family = current->ai_family;
- if (family != AF_INET && family != AF_INET6)
+
+ switch (family) {
+ case AF_INET:
+ if (!UseIPv4)
+ continue;
+ break;
+ case AF_INET6:
+ if (!UseIPv6)
+ continue;
+ break;
+ default:
continue;
+ }
salen = current->ai_addrlen;
memcpy(&sa, current->ai_addr, salen);
@@ -163,34 +193,10 @@ TcpSocket::TcpSocket(const char *host, int port)
else
sa.u.sin6.sin6_port = htons(port);
-#else /* HAVE_GETADDRINFO */
- family = AF_INET;
- salen = sizeof(struct sockaddr_in);
-
- /* Try processing the host as an IP address */
- memset(&sa, 0, sizeof(sa));
- sa.u.sin.sin_family = AF_INET;
- sa.u.sin.sin_addr.s_addr = inet_addr((char *)host);
- sa.u.sin.sin_port = htons(port);
- if ((int)sa.u.sin.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) {
- sa.u.sin.sin_addr.s_addr = ((struct in_addr *)hostinfo->h_addr)->s_addr;
- } else {
- err = errorNumber;
- throw SocketException("unable to resolve host by name", err);
- }
- }
-#endif /* HAVE_GETADDRINFO */
-
sock = socket (family, SOCK_STREAM, 0);
if (sock == -1) {
err = errorNumber;
-#ifdef HAVE_GETADDRINFO
freeaddrinfo(ai);
-#endif /* HAVE_GETADDRINFO */
throw SocketException("unable to create socket", err);
}
@@ -199,19 +205,20 @@ TcpSocket::TcpSocket(const char *host, int port)
err = errorNumber;
#ifndef WIN32
if (err == EINTR)
- continue;
+ continue;
#endif
closesocket(sock);
break;
}
-#ifdef HAVE_GETADDRINFO
if (result == 0)
break;
}
freeaddrinfo(ai);
-#endif /* HAVE_GETADDRINFO */
+
+ if (current == NULL)
+ throw Exception("No useful address for host");
if (result == -1)
throw SocketException("unable connect to socket", err);
@@ -240,27 +247,63 @@ int TcpSocket::getMyPort() {
}
char* TcpSocket::getPeerAddress() {
- struct sockaddr_in info;
- struct in_addr addr;
- socklen_t info_size = sizeof(info);
+ vnc_sockaddr_t sa;
+ socklen_t sa_size = sizeof(sa);
- getpeername(getFd(), (struct sockaddr *)&info, &info_size);
- memcpy(&addr, &info.sin_addr, sizeof(addr));
+ if (getpeername(getFd(), &sa.u.sa, &sa_size) != 0) {
+ vlog.error("unable to get peer name for socket");
+ return rfb::strDup("");
+ }
+
+ if (sa.u.sa.sa_family == AF_INET6) {
+ char buffer[INET6_ADDRSTRLEN + 2];
+ int ret;
+
+ buffer[0] = '[';
+
+ ret = getnameinfo(&sa.u.sa, sizeof(sa.u.sin6),
+ buffer + 1, sizeof(buffer) - 2, NULL, 0,
+ NI_NUMERICHOST);
+ if (ret != 0) {
+ vlog.error("unable to convert peer name to a string");
+ return rfb::strDup("");
+ }
+
+ strcat(buffer, "]");
+
+ return rfb::strDup(buffer);
+ }
+
+ if (sa.u.sa.sa_family == AF_INET) {
+ char *name;
+
+ name = inet_ntoa(sa.u.sin.sin_addr);
+ if (name == NULL) {
+ vlog.error("unable to convert peer name to a string");
+ return rfb::strDup("");
+ }
- char* name = inet_ntoa(addr);
- if (name) {
return rfb::strDup(name);
- } else {
- return rfb::strDup("");
}
+
+ vlog.error("unknown address family for socket");
+ return rfb::strDup("");
}
int TcpSocket::getPeerPort() {
- struct sockaddr_in info;
- socklen_t info_size = sizeof(info);
+ vnc_sockaddr_t sa;
+ socklen_t sa_size = sizeof(sa);
+
+ getpeername(getFd(), &sa.u.sa, &sa_size);
- getpeername(getFd(), (struct sockaddr *)&info, &info_size);
- return ntohs(info.sin_port);
+ 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() {
@@ -288,13 +331,14 @@ bool TcpSocket::sameMachine() {
if (peeraddr.u.sa.sa_family != myaddr.u.sa.sa_family)
return false;
-#ifdef HAVE_GETADDRINFO
if (peeraddr.u.sa.sa_family == AF_INET6)
return IN6_ARE_ADDR_EQUAL(&peeraddr.u.sin6.sin6_addr,
- &myaddr.u.sin6.sin6_addr);
-#endif
+ &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);
- 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()
@@ -306,7 +350,7 @@ void TcpSocket::shutdown()
bool TcpSocket::enableNagles(int sock, bool enable) {
int one = enable ? 0 : 1;
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
- (char *)&one, sizeof(one)) < 0) {
+ (char *)&one, sizeof(one)) < 0) {
int e = errorNumber;
vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
return false;
@@ -335,77 +379,89 @@ bool TcpSocket::isListening(int sock)
int TcpSocket::getSockPort(int sock)
{
- struct sockaddr_in info;
- socklen_t info_size = sizeof(info);
- if (getsockname(sock, (struct sockaddr *)&info, &info_size) < 0)
- return 0;
- return ntohs(info.sin_port);
+ 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 char *listenaddr, int port, bool localhostOnly,
- int sock, bool close_) : closeFd(close_)
+TcpListener::TcpListener(const TcpListener& other)
{
- if (sock != -1) {
- fd = sock;
- return;
+ fd = dupsocket (other.fd);
+ // Hope TcpListener::shutdown(other) doesn't get called...
+}
+
+TcpListener& TcpListener::operator= (const TcpListener& other)
+{
+ if (this != &other)
+ {
+ closesocket (fd);
+ fd = dupsocket (other.fd);
+ // Hope TcpListener::shutdown(other) doesn't get called...
}
+ return *this;
+}
+
+TcpListener::TcpListener(const struct sockaddr *listenaddr,
+ socklen_t listenaddrlen)
+{
+ int one = 1;
+ vnc_sockaddr_t sa;
+ int sock;
initSockets();
- if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
- throw SocketException("unable to create listening socket", errorNumber);
+ if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0)
+ throw SocketException("unable to create listening socket", errorNumber);
+
+ memcpy (&sa, listenaddr, listenaddrlen);
+#ifdef IPV6_V6ONLY
+ if (listenaddr->sa_family == AF_INET6) {
+ if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one)))
+ throw SocketException("unable to set IPV6_V6ONLY", errorNumber);
+ }
+#endif /* defined(IPV6_V6ONLY) */
+
+ if (bind(sock, &sa.u.sa, listenaddrlen) == -1) {
+ closesocket(sock);
+ throw SocketException("failed to bind socket", errorNumber);
+ }
#ifndef WIN32
// - By default, close the socket on exec()
- fcntl(fd, F_SETFD, FD_CLOEXEC);
+ fcntl(sock, F_SETFD, FD_CLOEXEC);
- int one = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
- (char *)&one, sizeof(one)) < 0) {
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&one, sizeof(one)) < 0) {
int e = errorNumber;
- closesocket(fd);
+ closesocket(sock);
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;
- if (localhostOnly) {
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- } else if (listenaddr != NULL) {
-#ifdef HAVE_INET_ATON
- if (inet_aton(listenaddr, &addr.sin_addr) == 0)
-#else
- /* Some systems (e.g. Windows) do not have inet_aton, sigh */
- if ((addr.sin_addr.s_addr = inet_addr(listenaddr)) == INADDR_NONE)
+ fd = sock;
#endif
- {
- closesocket(fd);
- throw Exception("invalid network interface address: %s", listenaddr);
- }
- } else
- addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Bind to 0.0.0.0 by default. */
-
- addr.sin_port = htons(port);
- 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) {
+ if (listen(sock, 5) < 0) {
int e = errorNumber;
- closesocket(fd);
+ closesocket(sock);
throw SocketException("unable to set socket to listening mode", e);
}
}
TcpListener::~TcpListener() {
- if (closeFd) closesocket(fd);
+ closesocket(fd);
}
void TcpListener::shutdown()
@@ -444,17 +500,45 @@ TcpListener::accept() {
}
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);
+ struct addrinfo *ai, *current, hints;
+
+ initSockets();
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_canonname = NULL;
+ hints.ai_addr = NULL;
+ hints.ai_next = NULL;
+
+ // Windows doesn't like NULL for service, so specify something
+ if ((getaddrinfo(NULL, "1", &hints, &ai)) != 0)
+ return;
+
+ for (current= ai; current != NULL; current = current->ai_next) {
+ switch (current->ai_family) {
+ case AF_INET:
+ if (!UseIPv4)
+ continue;
+ break;
+ case AF_INET6:
+ if (!UseIPv6)
+ continue;
+ break;
+ default:
+ continue;
+ }
+
+ char *addr = new char[INET6_ADDRSTRLEN];
+
+ getnameinfo(current->ai_addr, current->ai_addrlen, addr, INET6_ADDRSTRLEN,
+ NULL, 0, NI_NUMERICHOST);
+
result->push_back(addr);
}
+
+ freeaddrinfo(ai);
}
int TcpListener::getMyPort() {
@@ -462,6 +546,115 @@ int TcpListener::getMyPort() {
}
+void network::createLocalTcpListeners(std::list<TcpListener> *listeners,
+ int port)
+{
+ std::list<TcpListener> new_listeners;
+ vnc_sockaddr_t sa;
+
+ initSockets();
+
+ if (UseIPv6) {
+ sa.u.sin6.sin6_family = AF_INET6;
+ sa.u.sin6.sin6_port = htons (port);
+ sa.u.sin6.sin6_addr = in6addr_loopback;
+ try {
+ new_listeners.push_back (TcpListener (&sa.u.sa, sizeof (sa.u.sin6)));
+ } catch (SocketException& e) {
+ // Ignore this if it is due to lack of address family support on
+ // the interface or on the system
+ if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT)
+ // Otherwise, report the error
+ throw;
+ }
+ }
+ if (UseIPv4) {
+ sa.u.sin.sin_family = AF_INET;
+ sa.u.sin.sin_port = htons (port);
+ sa.u.sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ try {
+ new_listeners.push_back (TcpListener (&sa.u.sa, sizeof (sa.u.sin)));
+ } catch (SocketException& e) {
+ // Ignore this if it is due to lack of address family support on
+ // the interface or on the system
+ if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT)
+ // Otherwise, report the error
+ throw;
+ }
+ }
+
+ if (new_listeners.empty ())
+ throw SocketException("createLocalTcpListeners: no addresses available",
+ EADDRNOTAVAIL);
+
+ listeners->splice (listeners->end(), new_listeners);
+}
+
+void network::createTcpListeners(std::list<TcpListener> *listeners,
+ const char *addr,
+ int port)
+{
+ std::list<TcpListener> new_listeners;
+
+ struct addrinfo *ai, *current, hints;
+ char service[16];
+ int result;
+
+ initSockets();
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_canonname = NULL;
+ hints.ai_addr = NULL;
+ hints.ai_next = NULL;
+
+ snprintf (service, sizeof (service) - 1, "%d", port);
+ service[sizeof (service) - 1] = '\0';
+ if ((result = getaddrinfo(addr, service, &hints, &ai)) != 0)
+ throw rdr::Exception("unable to resolve listening address: %s",
+ gai_strerror(result));
+
+ for (current = ai; current != NULL; current = current->ai_next) {
+ switch (current->ai_family) {
+ case AF_INET:
+ if (!UseIPv4)
+ continue;
+ break;
+
+ case AF_INET6:
+ if (!UseIPv6)
+ continue;
+ break;
+
+ default:
+ continue;
+ }
+
+ try {
+ new_listeners.push_back(TcpListener (current->ai_addr,
+ current->ai_addrlen));
+ } catch (SocketException& e) {
+ // Ignore this if it is due to lack of address family support on
+ // the interface or on the system
+ if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) {
+ // Otherwise, report the error
+ freeaddrinfo(ai);
+ throw;
+ }
+ }
+ }
+ freeaddrinfo(ai);
+
+ if (new_listeners.empty ())
+ throw SocketException("createTcpListeners: no addresses available",
+ EADDRNOTAVAIL);
+
+ listeners->splice (listeners->end(), new_listeners);
+}
+
+
TcpFilter::TcpFilter(const char* spec) {
rfb::CharArray tmp;
tmp.buf = rfb::strDup(spec);
@@ -478,20 +671,69 @@ 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));
+patternMatchIP(const TcpFilter::Pattern& pattern, vnc_sockaddr_t *sa) {
+ switch (pattern.address.u.sa.sa_family) {
+ unsigned long address;
+
+ case AF_INET:
+ if (sa->u.sa.sa_family != AF_INET)
+ return false;
+
+ address = sa->u.sin.sin_addr.s_addr;
+ if (address == htonl (INADDR_NONE)) return false;
+ return ((pattern.address.u.sin.sin_addr.s_addr &
+ pattern.mask.u.sin.sin_addr.s_addr) ==
+ (address & pattern.mask.u.sin.sin_addr.s_addr));
+
+ case AF_INET6:
+ if (sa->u.sa.sa_family != AF_INET6)
+ return false;
+
+ for (unsigned int n = 0; n < 16; n++) {
+ unsigned int bits = (n + 1) * 8;
+ unsigned int mask;
+ if (pattern.prefixlen > bits)
+ mask = 0xff;
+ else {
+ unsigned int lastbits = 0xff;
+ lastbits <<= bits - pattern.prefixlen;
+ mask = lastbits & 0xff;
+ }
+
+ if ((pattern.address.u.sin6.sin6_addr.s6_addr[n] & mask) !=
+ (sa->u.sin6.sin6_addr.s6_addr[n] & mask))
+ return false;
+
+ if (mask < 0xff)
+ break;
+ }
+
+ return true;
+
+ case AF_UNSPEC:
+ // Any address matches
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
}
bool
TcpFilter::verifyConnection(Socket* s) {
rfb::CharArray name;
+ vnc_sockaddr_t sa;
+ socklen_t sa_size = sizeof(sa);
+
+ if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0)
+ return false;
name.buf = s->getPeerAddress();
std::list<TcpFilter::Pattern>::iterator i;
for (i=filter.begin(); i!=filter.end(); i++) {
- if (patternMatchIP(*i, name.buf)) {
+ if (patternMatchIP(*i, &sa)) {
switch ((*i).action) {
case Accept:
vlog.debug("ACCEPT %s", name.buf);
@@ -515,31 +757,102 @@ TcpFilter::verifyConnection(Socket* s) {
TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
TcpFilter::Pattern pattern;
- bool expandMask = false;
- rfb::CharArray addr, mask;
+ rfb::CharArray addr, pref;
+ bool prefix_specified;
+ int family;
+
+ prefix_specified = rfb::strSplit(&p[1], '/', &addr.buf, &pref.buf);
+ if (addr.buf[0] == '\0') {
+ // Match any address
+ memset (&pattern.address, 0, sizeof (pattern.address));
+ pattern.address.u.sa.sa_family = AF_UNSPEC;
+ pattern.prefixlen = 0;
+ } else {
+ struct addrinfo hints;
+ struct addrinfo *ai;
+ char *p = addr.buf;
+ int result;
+ memset (&hints, 0, sizeof (hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_NUMERICHOST;
+
+ // Take out brackets, if present
+ if (*p == '[') {
+ size_t len;
+ p++;
+ len = strlen (p);
+ if (len > 0 && p[len - 1] == ']')
+ p[len - 1] = '\0';
+ }
+
+ if ((result = getaddrinfo (p, NULL, &hints, &ai)) != 0) {
+ throw Exception("unable to resolve host by name: %s",
+ gai_strerror(result));
+ }
- if (rfb::strSplit(&p[1], '/', &addr.buf, &mask.buf)) {
- if (rfb::strContains(mask.buf, '.')) {
- pattern.mask = inet_addr(mask.buf);
+ memcpy (&pattern.address.u.sa, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo (ai);
+
+ family = pattern.address.u.sa.sa_family;
+
+ if (prefix_specified) {
+ if (family == AF_INET &&
+ rfb::strContains(pref.buf, '.')) {
+ throw Exception("mask no longer supported for filter, "
+ "use prefix instead");
+ }
+
+ pattern.prefixlen = (unsigned int) atoi(pref.buf);
} else {
- pattern.mask = atoi(mask.buf);
- expandMask = true;
+ switch (family) {
+ case AF_INET:
+ pattern.prefixlen = 32;
+ break;
+ case AF_INET6:
+ pattern.prefixlen = 128;
+ break;
+ default:
+ throw Exception("unknown address family");
+ }
}
- } 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;
+ family = pattern.address.u.sa.sa_family;
+
+ if (pattern.prefixlen > (family == AF_INET ? 32: 128))
+ throw Exception("invalid prefix length for filter address: %u",
+ pattern.prefixlen);
+
+ // Compute mask from address and prefix length
+ memset (&pattern.mask, 0, sizeof (pattern.mask));
+ switch (family) {
+ unsigned long mask;
+ case AF_INET:
+ mask = 0;
+ for (unsigned int i=0; i<pattern.prefixlen; i++)
+ mask |= 1<<(31-i);
+ pattern.mask.u.sin.sin_addr.s_addr = htonl(mask);
+ break;
+
+ case AF_INET6:
+ for (unsigned int n = 0; n < 16; n++) {
+ unsigned int bits = (n + 1) * 8;
+ if (pattern.prefixlen > bits)
+ pattern.mask.u.sin6.sin6_addr.s6_addr[n] = 0xff;
+ else {
+ unsigned int lastbits = 0xff;
+ lastbits <<= bits - pattern.prefixlen;
+ pattern.mask.u.sin6.sin6_addr.s6_addr[n] = lastbits & 0xff;
+ break;
+ }
+ }
+ break;
+ case AF_UNSPEC:
+ // No mask to compute
+ break;
+ default:
+ ; /* not reached */
+ }
switch(p[0]) {
case '+': pattern.action = TcpFilter::Accept; break;
@@ -551,21 +864,39 @@ TcpFilter::Pattern TcpFilter::parsePattern(const char* p) {
}
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];
+ rfb::CharArray addr;
+ char buffer[INET6_ADDRSTRLEN + 2];
+
+ if (p.address.u.sa.sa_family == AF_INET) {
+ getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin),
+ buffer, sizeof (buffer), NULL, 0, NI_NUMERICHOST);
+ addr.buf = rfb::strDup(buffer);
+ } else if (p.address.u.sa.sa_family == AF_INET6) {
+ buffer[0] = '[';
+ getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin6),
+ buffer + 1, sizeof (buffer) - 2, NULL, 0, NI_NUMERICHOST);
+ strcat(buffer, "]");
+ addr.buf = rfb::strDup(buffer);
+ } else if (p.address.u.sa.sa_family == AF_UNSPEC)
+ addr.buf = rfb::strDup("");
+
+ char action;
switch (p.action) {
- case Accept: result[0] = '+'; break;
- case Reject: result[0] = '-'; break;
- case Query: result[0] = '?'; break;
+ case Accept: action = '+'; break;
+ case Reject: action = '-'; break;
+ default:
+ case Query: action = '?'; break;
};
- result[1] = 0;
- strcat(result, addr.buf);
- strcat(result, "/");
- strcat(result, mask.buf);
+ size_t resultlen = (1 // action
+ + strlen (addr.buf) // address
+ + 1 // slash
+ + 3 // prefix length, max 128
+ + 1); // terminating nul
+ char* result = new char[resultlen];
+ if (addr.buf[0] == '\0')
+ snprintf(result, resultlen, "%c", action);
+ else
+ snprintf(result, resultlen, "%c%s/%u", action, addr.buf, p.prefixlen);
+
return result;
}
diff --git a/common/network/TcpSocket.h b/common/network/TcpSocket.h
index cd4393e..3f72c85 100644
--- a/common/network/TcpSocket.h
+++ b/common/network/TcpSocket.h
@@ -30,6 +30,14 @@
#include <network/Socket.h>
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h> /* for socklen_t */
+#include <netinet/in.h> /* for struct sockaddr_in */
+#endif
+
#include <list>
/* Tunnelling support. */
@@ -65,20 +73,33 @@ namespace network {
class TcpListener : public SocketListener {
public:
- TcpListener(const char *listenaddr, int port, bool localhostOnly=false,
- int sock=-1, bool close=true);
+ TcpListener(const struct sockaddr *listenaddr, socklen_t listenaddrlen);
+ TcpListener(int sock);
+ TcpListener(const TcpListener& other);
+ TcpListener& operator= (const TcpListener& other);
virtual ~TcpListener();
virtual void shutdown();
virtual Socket* accept();
- void getMyAddresses(std::list<char*>* addrs);
+ static void getMyAddresses(std::list<char*>* result);
int getMyPort();
-
- private:
- bool closeFd;
};
+ void createLocalTcpListeners(std::list<TcpListener> *listeners,
+ int port);
+ void createTcpListeners(std::list<TcpListener> *listeners,
+ const char *addr,
+ int port);
+
+ typedef struct vnc_sockaddr {
+ union {
+ sockaddr sa;
+ sockaddr_in sin;
+ sockaddr_in6 sin6;
+ } u;
+ } vnc_sockaddr_t;
+
class TcpFilter : public ConnectionFilter {
public:
TcpFilter(const char* filter);
@@ -89,8 +110,10 @@ namespace network {
typedef enum {Accept, Reject, Query} Action;
struct Pattern {
Action action;
- unsigned long address;
- unsigned long mask;
+ vnc_sockaddr_t address;
+ unsigned int prefixlen;
+
+ vnc_sockaddr_t mask; // computed from address and prefix
};
static Pattern parsePattern(const char* s);
static char* patternToStr(const Pattern& p);
diff --git a/common/os/CMakeLists.txt b/common/os/CMakeLists.txt
index 39d5c10..cd066f8 100644
--- a/common/os/CMakeLists.txt
+++ b/common/os/CMakeLists.txt
@@ -2,7 +2,6 @@ include_directories(${CMAKE_SOURCE_DIR}/common)
add_library(os STATIC
print.c
- net.c
w32tiger.c
os.cxx
tls.cxx)
diff --git a/common/os/net.c b/common/os/net.c
deleted file mode 100644
index 7bad36c..0000000
--- a/common/os/net.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Copyright (C) 2008 TightVNC Team. 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 <stdlib.h>
-#include <string.h>
-
-#ifdef WIN32
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#else
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#endif
-
-#include <os/net.h>
-
-
-#ifndef HAVE_INET_NTOP
-const char *tight_inet_ntop(int af, const void *src, char *dst,
- socklen_t size) {
- char *tempstr;
-
- /* Catch bugs - we should not use IPv6 if we don't have inet_ntop */
- if (af != AF_INET)
- abort();
-
- /* inet_ntoa never fails */
- tempstr = inet_ntoa(*(struct in_addr *)(src));
- memcpy(dst, tempstr, strlen(tempstr) + 1);
-
- return dst;
-}
-#endif
diff --git a/common/os/net.h b/common/os/net.h
deleted file mode 100644
index bd8b21c..0000000
--- a/common/os/net.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Copyright (C) 2008 TightVNC Team. 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.
- */
-
-#ifndef OS_NET_H
-#define OS_NET_H
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef HAVE_SOCKLEN_T
-typedef int socklen_t;
-#endif
-
-/* IPv6 support on server side - we have to have all those functions */
-#if defined(HAVE_INET_NTOP)
-#define HAVE_IPV6
-#endif
-
-/* IPv4-only stub implementation */
-#ifndef HAVE_INET_NTOP
-const char *tight_inet_ntop(int af, const void *src,
- char *dst, socklen_t size);
-#define inet_ntop tight_inet_ntop
-#endif
-
-#ifdef __cplusplus
-};
-#endif
-
-#endif /* OS_NET_H */
diff --git a/config.h.in b/config.h.in
index ec2aed9..c6d27ee 100644
--- a/config.h.in
+++ b/config.h.in
@@ -2,9 +2,6 @@
#define PACKAGE_VERSION "@VERSION@"
#cmakedefine HAVE_SOCKLEN_T
-#cmakedefine HAVE_INET_ATON
-#cmakedefine HAVE_INET_NTOP
-#cmakedefine HAVE_GETADDRINFO
#cmakedefine HAVE_SNPRINTF
#cmakedefine HAVE_STRCASECMP
#cmakedefine HAVE_STRNCASECMP
diff --git a/unix/x0vncserver/x0vncserver.cxx b/unix/x0vncserver/x0vncserver.cxx
index b5498e1..400af75 100644
--- a/unix/x0vncserver/x0vncserver.cxx
+++ b/unix/x0vncserver/x0vncserver.cxx
@@ -434,6 +434,8 @@ int main(int argc, char** argv)
signal(SIGINT, CleanupSignalHandler);
signal(SIGTERM, CleanupSignalHandler);
+ std::list<TcpListener> listeners;
+
try {
TXWindow::init(dpy,"x0vncserver");
Geometry geo(DisplayWidth(dpy, DefaultScreen(dpy)),
@@ -448,13 +450,16 @@ int main(int argc, char** argv)
QueryConnHandler qcHandler(dpy, &server);
server.setQueryConnectionHandler(&qcHandler);
- TcpListener listener(NULL, (int)rfbport);
+ 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)
- listener.setFilter(&fileTcpFilter);
+ for (std::list<TcpListener>::iterator i = listeners.begin();
+ i != listeners.end();
+ i++)
+ (*i).setFilter(&fileTcpFilter);
delete[] hostsData;
PollingScheduler sched((int)pollingCycle, (int)maxProcessorUsage);
@@ -469,7 +474,11 @@ int main(int argc, char** argv)
TXWindow::handleXEvents(dpy);
FD_ZERO(&rfds);
- FD_SET(listener.getFd(), &rfds);
+ for (std::list<TcpListener>::iterator i = listeners.begin();
+ i != listeners.end();
+ i++)
+ FD_SET((*i).getFd(), &rfds);
+
server.getSockets(&sockets);
int clients_connected = 0;
for (i = sockets.begin(); i != sockets.end(); i++) {
@@ -514,12 +523,16 @@ int main(int argc, char** argv)
}
// Accept new VNC connections
- if (FD_ISSET(listener.getFd(), &rfds)) {
- Socket* sock = listener.accept();
- if (sock) {
- server.addSocket(sock);
- } else {
- vlog.status("Client connection rejected");
+ for (std::list<TcpListener>::iterator i = listeners.begin();
+ i != listeners.end();
+ i++) {
+ if (FD_ISSET((*i).getFd(), &rfds)) {
+ Socket* sock = (*i).accept();
+ if (sock) {
+ server.addSocket(sock);
+ } else {
+ vlog.status("Client connection rejected");
+ }
}
}
diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc
index 05169b0..d0a9953 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.cc
+++ b/unix/xserver/hw/vnc/XserverDesktop.cc
@@ -135,13 +135,13 @@ public:
XserverDesktop::XserverDesktop(ScreenPtr pScreen_,
- network::TcpListener* listener_,
- network::TcpListener* httpListener_,
+ std::list<network::TcpListener> listeners_,
+ std::list<network::TcpListener> httpListeners_,
const char* name, const rfb::PixelFormat &pf,
void* fbptr, int stride)
: pScreen(pScreen_),
server(0), httpServer(0),
- listener(listener_), httpListener(httpListener_),
+ listeners(listeners_), httpListeners(httpListeners_),
cmap(0), deferredUpdateTimerSet(false),
grabbing(false), ignoreHooks_(false), directFbptr(true),
queryConnectId(0)
@@ -155,7 +155,7 @@ XserverDesktop::XserverDesktop(ScreenPtr pScreen_,
setFramebuffer(pScreen->width, pScreen->height, fbptr, stride);
server->setQueryConnectionHandler(this);
- if (httpListener)
+ if (!httpListeners.empty ())
httpServer = new FileHTTPServer(this);
}
@@ -315,7 +315,7 @@ char* XserverDesktop::substitute(const char* varName)
}
if (strcmp(varName, "$PORT") == 0) {
char* str = new char[10];
- sprintf(str, "%d", listener ? listener->getMyPort() : 0);
+ sprintf(str, "%d", listeners.empty () ? 0 : (*listeners.begin ()).getMyPort());
return str;
}
if (strcmp(varName, "$WIDTH") == 0) {
@@ -587,14 +587,18 @@ void XserverDesktop::blockHandler(fd_set* fds, OSTimePtr timeout)
// Add all sockets we want read events for, after purging
// any closed sockets.
- if (listener)
- FD_SET(listener->getFd(), fds);
- if (httpListener)
- FD_SET(httpListener->getFd(), fds);
+ for (std::list<network::TcpListener>::iterator i = listeners.begin();
+ i != listeners.end();
+ i++)
+ FD_SET((*i).getFd(), fds);
+ for (std::list<network::TcpListener>::iterator i = httpListeners.begin();
+ i != httpListeners.end();
+ i++)
+ FD_SET((*i).getFd(), fds);
std::list<Socket*> sockets;
- server->getSockets(&sockets);
std::list<Socket*>::iterator i;
+ server->getSockets(&sockets);
for (i = sockets.begin(); i != sockets.end(); i++) {
int fd = (*i)->getFd();
if ((*i)->isShutdown()) {
@@ -646,20 +650,24 @@ void XserverDesktop::wakeupHandler(fd_set* fds, int nfds)
// First check for file descriptors with something to do
if (nfds >= 1) {
- if (listener) {
- if (FD_ISSET(listener->getFd(), fds)) {
- FD_CLR(listener->getFd(), fds);
- Socket* sock = listener->accept();
+ for (std::list<network::TcpListener>::iterator i = listeners.begin();
+ i != listeners.end();
+ i++) {
+ if (FD_ISSET((*i).getFd(), fds)) {
+ FD_CLR((*i).getFd(), fds);
+ Socket* sock = (*i).accept();
sock->outStream().setBlocking(false);
server->addSocket(sock);
vlog.debug("new client, sock %d",sock->getFd());
}
}
- if (httpListener) {
- if (FD_ISSET(httpListener->getFd(), fds)) {
- FD_CLR(httpListener->getFd(), fds);
- Socket* sock = httpListener->accept();
+ for (std::list<network::TcpListener>::iterator i = httpListeners.begin();
+ i != httpListeners.end();
+ i++) {
+ if (FD_ISSET((*i).getFd(), fds)) {
+ FD_CLR((*i).getFd(), fds);
+ Socket* sock = (*i).accept();
sock->outStream().setBlocking(false);
httpServer->addSocket(sock);
vlog.debug("new http client, sock %d",sock->getFd());
diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h
index ca6e8af..23cdc83 100644
--- a/unix/xserver/hw/vnc/XserverDesktop.h
+++ b/unix/xserver/hw/vnc/XserverDesktop.h
@@ -58,8 +58,9 @@ class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer,
public rfb::VNCServerST::QueryConnectionHandler {
public:
- XserverDesktop(ScreenPtr pScreen, network::TcpListener* listener,
- network::TcpListener* httpListener_,
+ XserverDesktop(ScreenPtr pScreen,
+ std::list<network::TcpListener> listeners_,
+ std::list<network::TcpListener> httpListeners_,
const char* name, const rfb::PixelFormat &pf,
void* fbptr, int stride);
virtual ~XserverDesktop();
@@ -136,8 +137,8 @@ private:
InputDevice *inputDevice;
rfb::VNCServerST* server;
rfb::HTTPServer* httpServer;
- network::TcpListener* listener;
- network::TcpListener* httpListener;
+ std::list<network::TcpListener> listeners;
+ std::list<network::TcpListener> httpListeners;
ColormapPtr cmap;
int stride_;
bool deferredUpdateTimerSet;
diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc
index 1abf7d5..1cfaf51 100644
--- a/unix/xserver/hw/vnc/vncExtInit.cc
+++ b/unix/xserver/hw/vnc/vncExtInit.cc
@@ -127,6 +127,9 @@ rfb::StringParameter desktopName("desktop", "Name of VNC desktop","x11");
rfb::BoolParameter localhostOnly("localhost",
"Only allow connections from localhost",
false);
+rfb::StringParameter interface("interface",
+ "listen on the specified network address",
+ "all");
static PixelFormat vncGetPixelFormat(ScreenPtr pScreen)
{
@@ -222,29 +225,43 @@ void vncExtensionInit()
for (int scr = 0; scr < screenInfo.numScreens; scr++) {
if (!desktop[scr]) {
- network::TcpListener* listener = 0;
- network::TcpListener* httpListener = 0;
+ std::list<network::TcpListener> listeners;
+ std::list<network::TcpListener> httpListeners;
if (scr == 0 && vncInetdSock != -1) {
if (network::TcpSocket::isListening(vncInetdSock))
{
- listener = new network::TcpListener(NULL, 0, 0, vncInetdSock, true);
+ listeners.push_back (network::TcpListener(vncInetdSock));
vlog.info("inetd wait");
}
} else {
+ const char *addr = interface;
int port = rfbport;
if (port == 0) port = 5900 + atoi(display);
port += 1000 * scr;
- listener = new network::TcpListener(listenaddr, port, localhostOnly);
+ if (strcasecmp(addr, "all") == 0)
+ addr = 0;
+ if (localhostOnly)
+ network::createLocalTcpListeners(&listeners, port);
+ else
+ network::createTcpListeners(&listeners, addr, port);
+
vlog.info("Listening for VNC connections on %s interface(s), port %d",
- listenaddr == NULL ? "all" : listenaddr, port);
+ localhostOnly ? "local" : (const char*)interface,
+ port);
+
CharArray httpDirStr(httpDir.getData());
if (httpDirStr.buf[0]) {
port = httpPort;
if (port == 0) port = 5800 + atoi(display);
port += 1000 * scr;
- httpListener = new network::TcpListener(listenaddr, port, localhostOnly);
+ if (localhostOnly)
+ network::createLocalTcpListeners(&httpListeners, port);
+ else
+ network::createTcpListeners(&httpListeners, addr, port);
+
vlog.info("Listening for HTTP connections on %s interface(s), port %d",
- listenaddr == NULL ? "all" : listenaddr, port);
+ localhostOnly ? "local" : (const char*)interface,
+ port);
}
}
@@ -252,15 +269,15 @@ void vncExtensionInit()
PixelFormat pf = vncGetPixelFormat(screenInfo.screens[scr]);
desktop[scr] = new XserverDesktop(screenInfo.screens[scr],
- listener,
- httpListener,
+ listeners,
+ httpListeners,
desktopNameStr.buf,
pf,
vncFbptr[scr],
vncFbstride[scr]);
vlog.info("created VNC server for screen %d", scr);
- if (scr == 0 && vncInetdSock != -1 && !listener) {
+ if (scr == 0 && vncInetdSock != -1 && listeners.empty()) {
network::Socket* sock = new network::TcpSocket(vncInetdSock);
desktop[scr]->addClient(sock, false);
vlog.info("added inetd sock");
diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx
index 2d7685f..81bf849 100644
--- a/vncviewer/vncviewer.cxx
+++ b/vncviewer/vncviewer.cxx
@@ -459,15 +459,45 @@ int main(int argc, char** argv)
#endif
if (listenMode) {
+ std::list<TcpListener> listeners;
try {
int port = 5500;
if (isdigit(vncServerName[0]))
port = atoi(vncServerName);
- TcpListener listener(NULL, port);
+ createTcpListeners(&listeners, 0, port);
vlog.info("Listening on port %d\n", port);
- sock = listener.accept();
+
+ /* Wait for a connection */
+ while (sock == NULL) {
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ for (std::list<TcpListener>::iterator i = listeners.begin();
+ i != listeners.end();
+ i++)
+ FD_SET((*i).getFd(), &rfds);
+
+ int n = select(FD_SETSIZE, &rfds, 0, 0, 0);
+ if (n < 0) {
+ if (errno == EINTR) {
+ vlog.debug("Interrupted select() system call");
+ continue;
+ } else {
+ throw rdr::SystemException("select", errno);
+ }
+ }
+
+ for (std::list<TcpListener>::iterator i = listeners.begin ();
+ i != listeners.end();
+ i++)
+ if (FD_ISSET((*i).getFd(), &rfds)) {
+ sock = (*i).accept();
+ if (sock)
+ /* Got a connection */
+ break;
+ }
+ }
} catch (rdr::Exception& e) {
vlog.error("%s", e.str());
fl_alert("%s", e.str());
diff --git a/win/rfb_win32/Dialog.cxx b/win/rfb_win32/Dialog.cxx
index 70a5fb5..24e0d9f 100644
--- a/win/rfb_win32/Dialog.cxx
+++ b/win/rfb_win32/Dialog.cxx
@@ -86,21 +86,17 @@ TCHAR* Dialog::getItemString(int id) {
}
void Dialog::setItemChecked(int id, bool state) {
- dlog.debug("bool[%d]=%d", id, (int)state);
SendMessage(GetDlgItem(handle, id), BM_SETCHECK, state ? BST_CHECKED : BST_UNCHECKED, 0);
}
void Dialog::setItemInt(int id, int value) {
- dlog.debug("int[%d]=%d", id, value);
SetDlgItemInt(handle, id, value, TRUE);
}
void Dialog::setItemString(int id, const TCHAR* s) {
- dlog.debug("string[%d]=%s", id, (const char*)CStr(s));
SetDlgItemText(handle, id, s);
}
void Dialog::enableItem(int id, bool state) {
- dlog.debug("enable[%d]=%d", id, (int)state);
EnableWindow(GetDlgItem(handle, id), state);
}
@@ -361,7 +357,6 @@ bool PropSheet::showPropSheet(HWND owner, bool showApply, bool showCtxtHelp, boo
}
void PropSheet::reInitPages() {
- plog.debug("reInitPages %lx", handle);
std::list<PropSheetPage*>::iterator pspi;
for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
if ((*pspi)->handle)
@@ -370,7 +365,6 @@ void PropSheet::reInitPages() {
}
bool PropSheet::commitPages() {
- plog.debug("commitPages %lx", handle);
bool result = true;
std::list<PropSheetPage*>::iterator pspi;
for (pspi=pages.begin(); pspi!=pages.end(); pspi++) {
@@ -383,7 +377,6 @@ bool PropSheet::commitPages() {
void PropSheetPage::setChanged(bool changed) {
if (propSheet) {
- plog.debug("setChanged[%lx(%lx)]=%d", handle, propSheet->handle, (int)changed);
if (changed)
PropSheet_Changed(propSheet->handle, handle);
else
diff --git a/win/rfb_win32/SocketManager.cxx b/win/rfb_win32/SocketManager.cxx
index d4f1965..b073b8f 100644
--- a/win/rfb_win32/SocketManager.cxx
+++ b/win/rfb_win32/SocketManager.cxx
@@ -174,7 +174,7 @@ void SocketManager::processEvent(HANDLE event) {
vlog.info("deleting listening socket");
remListener(li.sock);
} else if (network_events.lNetworkEvents & FD_ADDRESS_LIST_CHANGE) {
- li.notifier->processAddressChange(li.sock);
+ li.notifier->processAddressChange();
requestAddressChangeEvents(li.sock);
} else {
vlog.error("unknown listener event: %lx", network_events.lNetworkEvents);
diff --git a/win/rfb_win32/SocketManager.h b/win/rfb_win32/SocketManager.h
index ef35974..c3c8faf 100644
--- a/win/rfb_win32/SocketManager.h
+++ b/win/rfb_win32/SocketManager.h
@@ -48,7 +48,7 @@ namespace rfb {
class AddressChangeNotifier {
public:
virtual ~AddressChangeNotifier() {}
- virtual void processAddressChange(network::SocketListener* sl) = 0;
+ virtual void processAddressChange() = 0;
};
// Add a listening socket. Incoming connections will be added to the supplied
diff --git a/win/vncconfig/vncconfig.cxx b/win/vncconfig/vncconfig.cxx
index 535febb..74d5e66 100644
--- a/win/vncconfig/vncconfig.cxx
+++ b/win/vncconfig/vncconfig.cxx
@@ -16,6 +16,7 @@
* USA.
*/
+#include <winsock2.h>
#include <windows.h>
#include <commctrl.h>
#include <string.h>
diff --git a/win/winvnc/ManagedListener.cxx b/win/winvnc/ManagedListener.cxx
index f2933bb..ac408e7 100644
--- a/win/winvnc/ManagedListener.cxx
+++ b/win/winvnc/ManagedListener.cxx
@@ -26,12 +26,16 @@ static LogWriter vlog("ManagedListener");
ManagedListener::ManagedListener(SocketManager* mgr)
-: sock(0), filter(0), manager(mgr), addrChangeNotifier(0), server(0), port(0), localOnly(false) {
+: filter(0), manager(mgr), addrChangeNotifier(0), server(0), port(0), localOnly(false) {
}
ManagedListener::~ManagedListener() {
- if (sock)
- manager->remListener(sock);
+ if (!sockets.empty()) {
+ std::list<network::TcpListener>::iterator iter;
+ for (iter = sockets.begin(); iter != sockets.end(); ++iter)
+ manager->remListener(&*iter);
+ sockets.clear();
+ }
delete filter;
}
@@ -57,8 +61,11 @@ void ManagedListener::setFilter(const char* filterStr) {
vlog.info("set filter to %s", filterStr);
delete filter;
filter = new network::TcpFilter(filterStr);
- if (sock && !localOnly)
- sock->setFilter(filter);
+ if (!sockets.empty() && !localOnly) {
+ std::list<network::TcpListener>::iterator iter;
+ for (iter = sockets.begin(); iter != sockets.end(); ++iter)
+ iter->setFilter(filter);
+ }
}
void ManagedListener::setAddressChangeNotifier(SocketManager::AddressChangeNotifier* acn) {
@@ -68,26 +75,39 @@ void ManagedListener::setAddressChangeNotifier(SocketManager::AddressChangeNotif
refresh();
}
+bool ManagedListener::isListening() {
+ return !sockets.empty();
+}
void ManagedListener::refresh() {
- if (sock)
- manager->remListener(sock);
- sock = 0;
+ std::list<network::TcpListener>::iterator iter;
+ if (!sockets.empty()) {
+ for (iter = sockets.begin(); iter != sockets.end(); ++iter)
+ manager->remListener(&*iter);
+ sockets.clear();
+ }
if (!server)
return;
try {
- if (port)
- sock = new network::TcpListener(NULL, port, localOnly);
+ if (port) {
+ if (localOnly)
+ network::createLocalTcpListeners(&sockets, port);
+ else
+ network::createTcpListeners(&sockets, NULL, port);
+ }
} catch (rdr::Exception& e) {
vlog.error(e.str());
}
- if (sock) {
- if (!localOnly)
- sock->setFilter(filter);
+ if (!sockets.empty()) {
+ if (!localOnly) {
+ for (iter = sockets.begin(); iter != sockets.end(); ++iter)
+ iter->setFilter(filter);
+ }
try {
- manager->addListener(sock, server, addrChangeNotifier);
- } catch (...) {
- sock = 0;
+ for (iter = sockets.begin(); iter != sockets.end(); ++iter)
+ manager->addListener(&*iter, server, addrChangeNotifier); } catch (...) {
+ // FIXME: Should unwind what we've added
+ sockets.clear();
throw;
}
}
diff --git a/win/winvnc/ManagedListener.h b/win/winvnc/ManagedListener.h
index e83aa0b..1c7099f 100644
--- a/win/winvnc/ManagedListener.h
+++ b/win/winvnc/ManagedListener.h
@@ -40,10 +40,12 @@ namespace winvnc {
void setPort(int port, bool localOnly=false);
void setFilter(const char* filter);
void setAddressChangeNotifier(rfb::win32::SocketManager::AddressChangeNotifier* acn);
-
- network::TcpListener* sock;
+
+ bool isListening();
+
protected:
void refresh();
+ std::list<network::TcpListener> sockets;
network::TcpFilter* filter;
rfb::win32::SocketManager* manager;
rfb::win32::SocketManager::AddressChangeNotifier* addrChangeNotifier;
diff --git a/win/winvnc/VNCServerWin32.cxx b/win/winvnc/VNCServerWin32.cxx
index 4d89a0e..467ff70 100644
--- a/win/winvnc/VNCServerWin32.cxx
+++ b/win/winvnc/VNCServerWin32.cxx
@@ -43,7 +43,7 @@ static IntParameter http_port("HTTPPortNumber",
static IntParameter port_number("PortNumber",
"TCP/IP port on which the server will accept connections", 5900);
static StringParameter hosts("Hosts",
- "Filter describing which hosts are allowed access to this server", "+0.0.0.0/0.0.0.0");
+ "Filter describing which hosts are allowed access to this server", "+");
static BoolParameter localHost("LocalHost",
"Only accept connections from via the local loop-back network interface", false);
static BoolParameter queryOnlyIfLoggedOn("QueryOnlyIfLoggedOn",
@@ -86,8 +86,8 @@ VNCServerWin32::~VNCServerWin32() {
}
-void VNCServerWin32::processAddressChange(network::SocketListener* sock_) {
- if (!trayIcon || (sock_ != rfbSock.sock))
+void VNCServerWin32::processAddressChange() {
+ if (!trayIcon)
return;
// Tool-tip prefix depends on server mode
@@ -97,8 +97,8 @@ void VNCServerWin32::processAddressChange(network::SocketListener* sock_) {
// Fetch the list of addresses
std::list<char*> addrs;
- if (rfbSock.sock)
- rfbSock.sock->getMyAddresses(&addrs);
+ if (rfbSock.isListening())
+ TcpListener::getMyAddresses(&addrs);
else
addrs.push_front(strDup("Not accepting connections"));
@@ -132,7 +132,7 @@ void VNCServerWin32::regConfigChanged() {
httpSock.setPort(http_port, localHost);
// -=- Update the Java viewer's web page port number.
- httpServer.setRFBport(rfbSock.sock ? port_number : 0);
+ httpServer.setRFBport(rfbSock.isListening() ? port_number : 0);
// -=- Update the TCP address filter for both ports, if open.
CharArray pattern(hosts.getData());
@@ -140,7 +140,7 @@ void VNCServerWin32::regConfigChanged() {
httpSock.setFilter(pattern.buf);
// -=- Update the tray icon tooltip text with IP addresses
- processAddressChange(rfbSock.sock);
+ processAddressChange();
}
diff --git a/win/winvnc/VNCServerWin32.h b/win/winvnc/VNCServerWin32.h
index 5b40a5a..27305d0 100644
--- a/win/winvnc/VNCServerWin32.h
+++ b/win/winvnc/VNCServerWin32.h
@@ -82,7 +82,7 @@ namespace winvnc {
// SocketManager::AddressChangeNotifier interface
// Used to keep tray icon up to date
- virtual void processAddressChange(network::SocketListener* sl);
+ virtual void processAddressChange();
// RegConfig::Callback interface
// Called via the EventManager whenver RegConfig sees the registry change