From 57440b86e2c62c78f3904abb17f11500ba0a1a6f Mon Sep 17 00:00:00 2001
From: Steven McCoy <steven.mccoy@miru.hk>
Date: Mon, 15 Aug 2011 08:56:00 +0200
Subject: Add IPv6 support to tcp_listener

Signed-off-by: Steven McCoy <steven.mccoy@miru.hk>
Signed-off-by: Martin Sustrik <sustrik@250bpm.com>
---
 src/ip.cpp            | 17 +++++++++++++++++
 src/ip.hpp            |  3 +++
 src/tcp_connecter.cpp | 21 ++++-----------------
 src/tcp_listener.cpp  | 19 +++++++++++++++++++
 4 files changed, 43 insertions(+), 17 deletions(-)

diff --git a/src/ip.cpp b/src/ip.cpp
index f47e9bf..4bacff3 100644
--- a/src/ip.cpp
+++ b/src/ip.cpp
@@ -457,4 +457,21 @@ void zmq::unblock_socket (fd_t s_)
 #endif
 }
 
+void zmq::enable_ipv4_mapping (fd_t s_)
+{
+#ifdef IPV6_V6ONLY
+#ifdef ZMQ_HAVE_WINDOWS
+    DWORD flag = 0;
+#else
+    int flag = 0;
+#endif
+    int rc = setsockopt (s_, IPPROTO_IPV6, IPV6_V6ONLY, (const char*) &flag,
+        sizeof (flag));
+#ifdef ZMQ_HAVE_WINDOWS
+    wsa_assert (rc != SOCKET_ERROR);
+#else
+    errno_assert (rc == 0);
+#endif
+#endif
+}
 
diff --git a/src/ip.hpp b/src/ip.hpp
index 1066d4b..a8b6eb1 100644
--- a/src/ip.hpp
+++ b/src/ip.hpp
@@ -68,6 +68,9 @@ namespace zmq
     //  Sets the socket into non-blocking mode.
     void unblock_socket (fd_t s_);
 
+    //  Enable IPv4-mapping of addresses in case it is disabled by default.
+    void enable_ipv4_mapping (fd_t s_);
+
 }
 
 #endif 
diff --git a/src/tcp_connecter.cpp b/src/tcp_connecter.cpp
index b3cd563..7531445 100644
--- a/src/tcp_connecter.cpp
+++ b/src/tcp_connecter.cpp
@@ -199,23 +199,10 @@ int zmq::tcp_connecter_t::open ()
         return -1;
 #endif
 
-    //  Enable IPv4-mapping of addresses in case it is disabled by default.
-#ifdef IPV6_V6ONLY
-    if (addr.ss_family == AF_INET6) {
-#ifdef ZMQ_HAVE_WINDOWS
-        DWORD flag = 0;
-#else
-        int flag = 0;
-#endif
-        int rc = setsockopt (s, IPPROTO_IPV6, IPV6_V6ONLY,
-            (const char*) &flag, sizeof (flag));
-#ifdef ZMQ_HAVE_WINDOWS
-        wsa_assert (rc != SOCKET_ERROR);
-#else
-        errno_assert (rc == 0);
-#endif
-    }
-#endif
+    //  On some systems, IPv4 mapping in IPv6 sockets is disabled by default.
+    //  Switch it on in such cases.
+    if (addr.ss_family == AF_INET6)
+        enable_ipv4_mapping (s);
 
     // Set the socket to non-blocking mode so that we get async connect().
     unblock_socket (s);
diff --git a/src/tcp_listener.cpp b/src/tcp_listener.cpp
index ef97862..406b4d0 100644
--- a/src/tcp_listener.cpp
+++ b/src/tcp_listener.cpp
@@ -130,6 +130,20 @@ int zmq::tcp_listener_t::set_address (const char *addr_)
 
     //  Create a listening socket.
     s = ::socket (addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
+#ifdef ZMQ_HAVE_WINDOWS
+    if (s == INVALID_SOCKET)
+        wsa_error_to_errno ();
+#endif
+
+    //  IPv6 address family not supported, try automatic downgrade to IPv4.
+    if (addr.ss_family == AF_INET6 && errno == EAFNOSUPPORT &&
+          !options.ipv4only) {
+        rc = resolve_ip_interface (&addr, &addr_len, addr_, true);
+        if (rc != 0)
+            return rc;
+        s = ::socket (addr.ss_family, SOCK_STREAM, IPPROTO_TCP);
+    }
+
 #ifdef ZMQ_HAVE_WINDOWS
     if (s == INVALID_SOCKET) {
         wsa_error_to_errno ();
@@ -140,6 +154,11 @@ int zmq::tcp_listener_t::set_address (const char *addr_)
         return -1;
 #endif
 
+    //  On some systems, IPv4 mapping in IPv6 sockets is disabled by default.
+    //  Switch it on in such cases.
+    if (addr.ss_family == AF_INET6)
+        enable_ipv4_mapping (s);
+
     //  Allow reusing of the address.
     int flag = 1;
 #ifdef ZMQ_HAVE_WINDOWS
-- 
cgit v1.2.3