]> source.dussan.org Git - tigervnc.git/commitdiff
IPv6: create new socket if IPv6 bind fails (#77).
authorTim Waugh <twaugh@redhat.com>
Fri, 21 Nov 2014 16:07:34 +0000 (16:07 +0000)
committerPierre Ossman <ossman@cendio.se>
Wed, 10 Dec 2014 12:31:29 +0000 (13:31 +0100)
common/network/TcpSocket.cxx

index 0db6ff6b06b9671a620eeae2ac08d4abaa6e61bc..f2d76bfae724b649ee4074b26a745071aa2d6909 100644 (file)
@@ -399,125 +399,122 @@ int TcpSocket::getSockPort(int sock)
   }
 }
 
-
-TcpListener::TcpListener(const char *listenaddr, int port, bool localhostOnly,
-                        int sock, bool close_) : closeFd(close_)
+static int bindIPv6 (const char *listenaddr,
+                    int port,
+                    bool localhostOnly)
 {
-  if (sock != -1) {
-    fd = sock;
-    return;
-  }
-
-  bool use_ipv6;
-  int af;
 #ifdef HAVE_GETADDRINFO
-  use_ipv6 = true;
-  af = AF_INET6;
+  struct sockaddr_in6 addr6;
+  socklen_t sa_len;
+  int fd;
+
+  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
-  use_ipv6 = false;
-  af = AF_INET;
+  vlog.error("IPV6_V6ONLY support is missing. "
+            "IPv4 clients may not be able to connect.");
 #endif
 
-  initSockets();
-  if ((fd = socket(af, SOCK_STREAM, 0)) < 0) {
-    // - Socket creation failed
-    if (use_ipv6) {
-      // - Trying to make an IPv6-capable socket failed - try again, IPv4-only
-      use_ipv6 = false;
-      af = AF_INET;
-      fd = socket(af, SOCK_STREAM, 0);
+  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;
     }
-    if (fd < 0)
-      throw SocketException("unable to create listening socket", errorNumber);
-  } else {
-    // - Socket creation succeeded
-    if (use_ipv6) {
-#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.");
+    // Unable to parse without inet_pton
+    closesocket(fd);
+    return -1;
 #endif
-    }
   }
 
-#ifndef WIN32
-  // - By default, close the socket on exec()
-  fcntl(fd, F_SETFD, FD_CLOEXEC);
-
-  int one = 1;
-  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
-                (char *)&one, sizeof(one)) < 0) {
-    int e = errorNumber;
+  if (bind(fd, (struct sockaddr *) &addr6, sa_len) == -1) {
     closesocket(fd);
-    throw SocketException("unable to create listening socket", e);
+    return -1;
   }
-#endif
 
-  // - Bind it to the desired port
+  return fd;
+#else
+  return -1;
+#endif /* HAVE_GETADDRINFO */
+}
+
+static int bindIPv4 (const char *listenaddr,
+                    int port,
+                    bool localhostOnly)
+{
   struct sockaddr_in addr;
-#ifdef HAVE_GETADDRINFO
-  struct sockaddr_in6 addr6;
-#endif
-  struct sockaddr *sa;
-  int sa_len;
+  socklen_t sa_len;
+  int fd;
 
-#ifdef HAVE_GETADDRINFO
-  if (use_ipv6) {
-    memset(&addr6, 0, (sa_len = sizeof(addr6)));
-    addr6.sin6_family = af;
-    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)
-       use_ipv6 = false;
+  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
-      // Unable to parse without inet_pton
-      use_ipv6 = false;
+      /* 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 (use_ipv6)
-      sa = (struct sockaddr *)&addr6;
+  if (bind(fd, (struct sockaddr *) &addr, sa_len) == -1) {
+    closesocket(fd);
+    return -1;
   }
-#endif
 
-  if (!use_ipv6) {
-    memset(&addr, 0, (sa_len = sizeof(addr)));
-    addr.sin_family = af;
-    addr.sin_port = htons(port);
+  return fd;
+}
 
-    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);
-
-    sa = (struct sockaddr *)&addr;
+TcpListener::TcpListener(const char *listenaddr, int port, bool localhostOnly,
+                        int sock, bool close_) : closeFd(close_)
+{
+  if (sock != -1) {
+    fd = sock;
+    return;
   }
 
-  addr.sin_port = htons(port);
-  if (bind(fd, sa, sa_len) < 0) {
+  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);
+
+  int one = 1;
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+                (char *)&one, sizeof(one)) < 0) {
     int e = errorNumber;
     closesocket(fd);
-    throw SocketException("unable to bind listening socket", e);
+    throw SocketException("unable to create listening socket", e);
   }
+#endif
 
   // - Set it to be a listening socket
   if (listen(fd, 5) < 0) {