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 #include #include -#include #include #include -#include #include #include #include @@ -42,10 +40,15 @@ #endif #include +#include #include -#include #include #include +#include + +#ifdef WIN32 +#include +#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* 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 *listeners, + int port) +{ + std::list 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 *listeners, + const char *addr, + int port) +{ + std::list 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::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 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 +#ifdef WIN32 +#include +#include +#else +#include /* for socklen_t */ +#include /* for struct sockaddr_in */ +#endif + #include /* 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* addrs); + static void getMyAddresses(std::list* result); int getMyPort(); - - private: - bool closeFd; }; + void createLocalTcpListeners(std::list *listeners, + int port); + void createTcpListeners(std::list *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 -#endif - -#include -#include - -#ifdef WIN32 -#include -#include -#else -#include -#include -#include -#endif - -#include - - -#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 -#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 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::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::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::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 listeners_, + std::list 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::iterator i = listeners.begin(); + i != listeners.end(); + i++) + FD_SET((*i).getFd(), fds); + for (std::list::iterator i = httpListeners.begin(); + i != httpListeners.end(); + i++) + FD_SET((*i).getFd(), fds); std::list sockets; - server->getSockets(&sockets); std::list::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::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::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 listeners_, + std::list 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 listeners; + std::list 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 listeners; + std::list 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 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::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::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::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::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 #include #include #include 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::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::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::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 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 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