| diff --git a/CMakeLists.txt b/CMakeLists.txt |
| index f2656d1..0e325e2 100644 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |