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.
diff --git a/common/network/TcpSocket.cxx b/common/network/TcpSocket.cxx
index 80b26b1..188651e 100644
--- a/common/network/TcpSocket.cxx
+++ b/common/network/TcpSocket.cxx
@@ -34,7 +34,6 @@
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <netdb.h>
-#include <unistd.h>
 #include <errno.h>
 #include <string.h>
 #include <signal.h>
@@ -42,6 +41,7 @@
 #endif
 
 #include <stdlib.h>
+#include <unistd.h>
 #include <network/TcpSocket.h>
 #include <rfb/util.h>
 #include <rfb/LogWriter.h>
@@ -274,16 +274,17 @@
     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 @@
   }
 }
 
-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
-
-  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
-  }
-
-  if (bind(fd, (struct sockaddr *) &addr6, sa_len) == -1) {
-    closesocket(fd);
-    return -1;
-  }
-
-  return fd;
-#else
-  return -1;
-#endif /* HAVE_GETADDRINFO */
+  fd = sock;
 }
 
-static int bindIPv4 (const char *listenaddr,
-		     int port,
-		     bool localhostOnly)
+TcpListener::TcpListener(const TcpListener& other)
 {
-  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;
-
-  memset(&addr, 0, (sa_len = sizeof(addr)));
-  addr.sin_family = AF_INET;
-  addr.sin_port = htons(port);
-
-  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 (bind(fd, (struct sockaddr *) &addr, sa_len) == -1) {
-    closesocket(fd);
-    return -1;
-  }
-
-  return fd;
+  fd = dup (other.fd);
+  // Hope TcpListener::shutdown(other) doesn't get called...
 }
 
-TcpListener::TcpListener(const char *listenaddr, int port, bool localhostOnly,
-			 int sock, bool close_) : closeFd(close_)
+TcpListener& TcpListener::operator= (const TcpListener& other)
 {
-  if (sock != -1) {
-    fd = sock;
-    return;
+  if (this != &other)
+  {
+    closesocket (fd);
+    fd = dup (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 = bindIPv6 (listenaddr, port, localhostOnly)) < 0)
-    if ((fd = bindIPv4 (listenaddr, port, localhostOnly)) < 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
 
   // - 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 @@
   return s;
 }
 
-void TcpListener::getMyAddresses(std::list<char*>* result) {
-#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_PTON)
+int TcpListener::getMyPort() {
+  return TcpSocket::getSockPort(getFd());
+}
+
+
+void network::createLocalTcpListeners(std::list<TcpListener> *listeners,
+                                      int port)
+{
+  std::list<TcpListener> 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<TcpListener> *listeners,
+                                 const char *addr,
+                                 int port)
+{
+  std::list<TcpListener> 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);
 }