From 892d10a705077083489f0ed4861af123433ff811 Mon Sep 17 00:00:00 2001 From: Tim Waugh Date: Wed, 11 Mar 2015 13:12:07 +0000 Subject: [PATCH] Fixed IPv6 support. The TcpListener constructor now takes a 'struct sockaddr*' instead of a string, and the createTcpListeners function creates TcpListener instances for an address based on the results from getaddrinfo(). The XserverDesktop class now takes a list of TcpListener instances for each of the RFB and HTTP sockets. The TcpListener::closeFd member variable is not used and has been removed. --- common/network/TcpSocket.cxx | 319 +++++++++++++++----------- common/network/TcpSocket.h | 17 +- unix/x0vncserver/x0vncserver.cxx | 31 ++- unix/xserver/hw/vnc/RFBGlue.cc | 5 +- unix/xserver/hw/vnc/XserverDesktop.cc | 44 ++-- unix/xserver/hw/vnc/XserverDesktop.h | 9 +- unix/xserver/hw/vnc/vncExtInit.cc | 35 ++- vncviewer/vncviewer.cxx | 34 ++- 8 files changed, 309 insertions(+), 185 deletions(-) diff --git a/common/network/TcpSocket.cxx b/common/network/TcpSocket.cxx index 80b26b1c..188651ed 100644 --- a/common/network/TcpSocket.cxx +++ b/common/network/TcpSocket.cxx @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -42,6 +41,7 @@ #endif #include +#include #include #include #include @@ -274,16 +274,17 @@ char* TcpSocket::getPeerAddress() { return rfb::strDup(""); } -#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_PTON) +#if defined(HAVE_GETADDRINFO) if (sa.u.sa.sa_family == AF_INET6) { char buffer[INET6_ADDRSTRLEN + 2]; - const char *name; + int ret; buffer[0] = '['; - name = inet_ntop(sa.u.sa.sa_family, &sa.u.sin6.sin6_addr, - buffer + 1, sizeof(buffer) - 2); - if (name == NULL) { + 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(""); } @@ -426,139 +427,77 @@ int TcpSocket::getSockPort(int sock) } } -static int bindIPv6 (const char *listenaddr, - int port, - bool localhostOnly) +TcpListener::TcpListener(int sock) { -#ifdef HAVE_GETADDRINFO - struct sockaddr_in6 addr6; - socklen_t sa_len; - int fd; - - if (!UseIPv6) - return -1; - - if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) - return -1; - -#ifdef IPV6_V6ONLY - // - We made an IPv6-capable socket, and we need it to do IPv4 too - int opt = 0; - setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, sizeof(opt)); -#else - vlog.error("IPV6_V6ONLY support is missing. " - "IPv4 clients may not be able to connect."); -#endif + fd = sock; +} - memset(&addr6, 0, (sa_len = sizeof(addr6))); - addr6.sin6_family = AF_INET6; - addr6.sin6_port = htons(port); - - if (localhostOnly) - addr6.sin6_addr = in6addr_loopback; - else if (listenaddr != NULL) { -#ifdef HAVE_INET_PTON - if (inet_pton(AF_INET6, listenaddr, &addr6.sin6_addr) != 1) { - closesocket(fd); - return -1; - } -#else - // Unable to parse without inet_pton - closesocket(fd); - return -1; -#endif - } +TcpListener::TcpListener(const TcpListener& other) +{ + fd = dup (other.fd); + // Hope TcpListener::shutdown(other) doesn't get called... +} - if (bind(fd, (struct sockaddr *) &addr6, sa_len) == -1) { - closesocket(fd); - return -1; +TcpListener& TcpListener::operator= (const TcpListener& other) +{ + if (this != &other) + { + closesocket (fd); + fd = dup (other.fd); + // Hope TcpListener::shutdown(other) doesn't get called... } - - return fd; -#else - return -1; -#endif /* HAVE_GETADDRINFO */ + return *this; } -static int bindIPv4 (const char *listenaddr, - int port, - bool localhostOnly) +TcpListener::TcpListener(const struct sockaddr *listenaddr, + socklen_t listenaddrlen) { - struct sockaddr_in addr; - socklen_t sa_len; - int fd; - - if (!UseIPv4) - return -1; - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) - return -1; + int one = 1; + vnc_sockaddr_t sa; + int sock; - memset(&addr, 0, (sa_len = sizeof(addr))); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); + initSockets(); - 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) -#endif - { - closesocket(fd); - throw Exception("invalid network interface address: %s", listenaddr); - } - } else - /* Bind to 0.0.0.0 by default. */ - addr.sin_addr.s_addr = htonl(INADDR_ANY); + if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0) + throw SocketException("unable to create listening socket", errorNumber); - if (bind(fd, (struct sockaddr *) &addr, sa_len) == -1) { - closesocket(fd); - return -1; + 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) */ - return fd; -} - -TcpListener::TcpListener(const char *listenaddr, int port, bool localhostOnly, - int sock, bool close_) : closeFd(close_) -{ - if (sock != -1) { - fd = sock; - return; + if (bind(sock, &sa.u.sa, listenaddrlen) == -1) { + closesocket(sock); + throw SocketException("failed to bind socket", errorNumber); } - initSockets(); - if ((fd = bindIPv6 (listenaddr, port, localhostOnly)) < 0) - if ((fd = bindIPv4 (listenaddr, port, localhostOnly)) < 0) - throw SocketException("unable to create listening 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 // - 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); } + + fd = sock; } TcpListener::~TcpListener() { - if (closeFd) closesocket(fd); + closesocket(fd); } void TcpListener::shutdown() @@ -596,46 +535,160 @@ TcpListener::accept() { return s; } -void TcpListener::getMyAddresses(std::list* result) { -#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_PTON) +int TcpListener::getMyPort() { + return TcpSocket::getSockPort(getFd()); +} + + +void network::createLocalTcpListeners(std::list *listeners, + int port) +{ + std::list new_listeners; + vnc_sockaddr_t sa; +#ifdef HAVE_GETADDRINFO + 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; + } + } +#endif /* HAVE_GETADDRINFO */ + 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; + +#ifdef HAVE_GETADDRINFO struct addrinfo *ai, *current, hints; + char service[16]; 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; - if ((getaddrinfo(NULL, NULL, &hints, &ai)) != 0) - return; + snprintf (service, sizeof (service) - 1, "%d", port); + service[sizeof (service) - 1] = '\0'; + if ((getaddrinfo(addr, service, &hints, &ai)) != 0) + throw rdr::SystemException("getaddrinfo", errorNumber); - for (current= ai; current != NULL; current = current->ai_next) { - if (current->ai_family != AF_INET && current->ai_family != AF_INET6) + 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]; - inet_ntop(current->ai_family, current->ai_addr, addr, INET6_ADDRSTRLEN); - result->push_back(addr); + 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); #else - const hostent* addrs = gethostbyname(0); - if (addrs == 0) - throw rdr::SystemException("gethostbyname", errorNumber); - if (addrs->h_addrtype != AF_INET) - throw rdr::Exception("getMyAddresses: bad family"); - for (int i=0; addrs->h_addr_list[i] != 0; i++) { - const char* addrC = inet_ntoa(*((struct in_addr*)addrs->h_addr_list[i])); - char* addr = new char[strlen(addrC)+1]; - strcpy(addr, addrC); - result->push_back(addr); + const hostent* addrs; + if (addr) { + /* Bind to specific address */ + addrs = gethostbyname(addr); + if (addrs == 0) + throw rdr::SystemException("gethostbyname", errorNumber); + if (addrs->h_addrtype != AF_INET) + throw rdr::Exception("createTcpListeners: bad family"); + for (int i=0; addrs->h_addr_list[i] != 0; i++) { + struct sockaddr_in addr; + addr.sin_family = AF_INET; + memcpy (&addr.sin_addr, addrs->h_addr_list[i], addrs->h_length); + addr.sin_port = htons(port); + try { + new_listeners.push_back(TcpListener ((struct sockaddr*)&addr, + addrs->h_length)); + } 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; + } + } + } + } else { + /* Bind to any address */ + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(port); + try { + new_listeners.push_back(TcpListener ((struct sockaddr*)&addr, + sizeof (struct sockaddr_in))); + } 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; + } + } } -#endif /* defined(HAVE_GETADDRINFO) && defined(HAVE_INET_PTON) */ -} +#endif /* defined(HAVE_GETADDRINFO) */ -int TcpListener::getMyPort() { - return TcpSocket::getSockPort(getFd()); + if (new_listeners.empty ()) + throw SocketException("createTcpListeners: no addresses available", + EADDRNOTAVAIL); + + listeners->splice (listeners->end(), new_listeners); } diff --git a/common/network/TcpSocket.h b/common/network/TcpSocket.h index b0bba53b..e6273890 100644 --- a/common/network/TcpSocket.h +++ b/common/network/TcpSocket.h @@ -29,6 +29,7 @@ #define __NETWORK_TCP_SOCKET_H__ #include +#include #include @@ -66,20 +67,24 @@ 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); int getMyPort(); - - private: - bool closeFd; }; + void createLocalTcpListeners(std::list *listeners, + int port); + void createTcpListeners(std::list *listeners, + const char *addr, + int port); + class TcpFilter : public ConnectionFilter { public: TcpFilter(const char* filter); diff --git a/unix/x0vncserver/x0vncserver.cxx b/unix/x0vncserver/x0vncserver.cxx index a9b328fe..2d9a0665 100644 --- a/unix/x0vncserver/x0vncserver.cxx +++ b/unix/x0vncserver/x0vncserver.cxx @@ -477,6 +477,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)), @@ -491,13 +493,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); @@ -513,7 +518,11 @@ int main(int argc, char** argv) FD_ZERO(&rfds); FD_SET(ConnectionNumber(dpy), &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++) { @@ -558,12 +567,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/RFBGlue.cc b/unix/xserver/hw/vnc/RFBGlue.cc index 53d5bdb0..19338fc7 100644 --- a/unix/xserver/hw/vnc/RFBGlue.cc +++ b/unix/xserver/hw/vnc/RFBGlue.cc @@ -187,7 +187,10 @@ int vncGetSocketPort(int fd) int vncIsTCPPortUsed(int port) { try { - network::TcpListener l(NULL, port); + // Attempt to create TCPListeners on that port. + // They go out of scope immediately and are destroyed. + std::list dummy; + network::createTcpListeners (&dummy, 0, port); } catch (rdr::Exception& e) { return 0; } diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index 54e09cb5..165afbb6 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -91,14 +91,14 @@ public: XserverDesktop::XserverDesktop(int screenIndex_, - network::TcpListener* listener_, - network::TcpListener* httpListener_, + std::list listeners_, + std::list httpListeners_, const char* name, const rfb::PixelFormat &pf, int width, int height, void* fbptr, int stride) : screenIndex(screenIndex_), server(0), httpServer(0), - listener(listener_), httpListener(httpListener_), + listeners(listeners_), httpListeners(httpListeners_), deferredUpdateTimerSet(false), directFbptr(true), queryConnectId(0) { @@ -108,7 +108,7 @@ XserverDesktop::XserverDesktop(int screenIndex_, setFramebuffer(width, height, fbptr, stride); server->setQueryConnectionHandler(this); - if (httpListener) + if (!httpListeners.empty ()) httpServer = new FileHTTPServer(this); } @@ -222,7 +222,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) { @@ -393,14 +393,18 @@ void XserverDesktop::readBlockHandler(fd_set* fds, struct timeval ** 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()) { @@ -452,20 +456,24 @@ void XserverDesktop::readWakeupHandler(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 7dcaa29e..6909a766 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -50,8 +50,9 @@ class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer, public rfb::VNCServerST::QueryConnectionHandler { public: - XserverDesktop(int screenIndex, network::TcpListener* listener, - network::TcpListener* httpListener_, + XserverDesktop(int screenIndex, + std::list listeners_, + std::list httpListeners_, const char* name, const rfb::PixelFormat &pf, int width, int height, void* fbptr, int stride); virtual ~XserverDesktop(); @@ -112,8 +113,8 @@ private: int screenIndex; rfb::VNCServerST* server; rfb::HTTPServer* httpServer; - network::TcpListener* listener; - network::TcpListener* httpListener; + std::list listeners; + std::list httpListeners; bool deferredUpdateTimerSet; bool directFbptr; struct timeval dixTimeout; diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index 22777839..e307e319 100644 --- a/unix/xserver/hw/vnc/vncExtInit.cc +++ b/unix/xserver/hw/vnc/vncExtInit.cc @@ -134,33 +134,44 @@ void vncExtensionInit(void) for (int scr = 0; scr < vncGetScreenCount(); 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::isSocket(vncInetdSock) && !network::TcpSocket::isConnected(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(vncGetDisplay()); port += 1000 * scr; - if (strcasecmp(interface, "all") == 0) - listener = new network::TcpListener(NULL, port, localhostOnly); + if (strcasecmp(addr, "all") == 0) + addr = 0; + if (localhostOnly) + network::createLocalTcpListeners(&listeners, port); else - listener = new network::TcpListener(interface, port, localhostOnly); + network::createTcpListeners(&listeners, addr, port); + vlog.info("Listening for VNC connections on %s interface(s), port %d", - (const char*)interface, port); + localhostOnly ? "local" : (const char*)interface, + port); + CharArray httpDirStr(httpDir.getData()); if (httpDirStr.buf[0]) { port = httpPort; if (port == 0) port = 5800 + atoi(vncGetDisplay()); port += 1000 * scr; - httpListener = new network::TcpListener(interface, 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", - (const char*)interface, port); + localhostOnly ? "local" : (const char*)interface, + port); } } @@ -168,8 +179,8 @@ void vncExtensionInit(void) PixelFormat pf = vncGetPixelFormat(scr); desktop[scr] = new XserverDesktop(scr, - listener, - httpListener, + listeners, + httpListeners, desktopNameStr.buf, pf, vncGetScreenWidth(scr), @@ -178,7 +189,7 @@ void vncExtensionInit(void) 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 3f51dd3c..876e9142 100644 --- a/vncviewer/vncviewer.cxx +++ b/vncviewer/vncviewer.cxx @@ -534,15 +534,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()); -- 2.39.5