On 01/15/2012 04:41 PM, Steinar H. Gunderson wrote:
Hi,

Apologies for not using the bug tracker, but I really couldn't get it to
work; on one of my email addresses, it never sent a notification, and in
another one, I got a notification but as soon as I had created an account and
set a password, I was logged right out and denied access back in :-)

Very strange. I saw some account creation e-mails (I get notifications), and it works for others.

Anyway, included is a patch to improve the IPv6 support in libmicrohttpd;
it supports a “dual-stack” mode that allows for both IPv4 and IPv6
connections on the same socket. The MHD_USE_IPv6 mode already does this
more-or-less by accident on some operating systems, but on Linux it depends
on a sysctl, and on *BSD you won't get it at all. Also, MHD_USE_IPv6 does not
fall back to IPv4 if IPv6 is unavailable, making it a less attractive option
for application developers -- MHD_USE_DUALSTACK should be safe for general
use unless you specifically do not want IPv6.

The patch also includes a fix so that if you _do_ receive an IPv4 connection
on the dual-stack socket, you get back the proper IPv4 address (e.g.
127.0.0.1) and not the mapped-v4 address (::ffff:127.0.0.1).

I've tested this on Debian, with XBMC as the user (that's what I wanted it
for in the first place :-) ). Thus, the patch is against 0.4.6. Let me know
if there are any questions.

0.4.6? You got to be kidding me. We're at 0.9.17, 0.4.6 is about 2 years old. More importantly, the patch is overall not taking the code in the right direction. The modern, recommended way to do dual-stack IPv6 is what MHD already supports: use two listen sockets. Your approach may work on some systems (see: need for sysctl) but is again not how it should be done. There've been discussions about this on the MHD mailinglist in the past. Maybe I'll need to put something more explicit into the documentation...

Anyway, you *can* do your style of dual-stack with MHD 0.9.x using the MHD_OPTION_LISTEN_SOCKET (you'll need to move some of the code from your patch, specifically the code doing SOCKET + SETSOCKOPT) before MHD_start_daemon and pass the resulting listen socket to MHD_start_daemon. However, I won't accept a patch to put explicit support for this style into the code as it is not the recommended way to dual-stack modern applications and thus we should not encourage the use of a socket bound to both IPv4 and IPv6.

Happy hacking!

Christian
Index: libmicrohttpd-0.4.6/src/daemon/daemon.c
===================================================================
--- libmicrohttpd-0.4.6.orig/src/daemon/daemon.c	2010-02-20 10:00:01.000000000 +0100
+++ libmicrohttpd-0.4.6/src/daemon/daemon.c	2012-01-15 16:30:54.000000000 +0100
@@ -695,6 +695,25 @@
     }
 #endif
 
+#if HAVE_INET6
+    /* If receiving IPv4 connections on an IPv6 socket (dual-stack sockets),
+     * the operating system will typically report back a so called mapped-v4
+     * address, of the form ::ffff:127.0.0.1. Rewrite these to IPv4 addresses,
+     * since that is what all the other code expects.
+     */
+    if (addrstorage.sin6_family == AF_INET6) {
+      const uint32_t *addr32 = (const uint32_t *) addrstorage.sin6_addr.s6_addr;
+      if (addr32[0] == 0 && addr32[1] == 0 && addr32[2] == htonl(0xffff)) {
+        uint32_t addr4 = addr32[3];
+        uint16_t port = addrstorage.sin6_port;
+
+        struct sockaddr_in *addr_in = (struct sockaddr_in *) &addrstorage;
+        addr_in->sin_family = AF_INET;
+        addr_in->sin_addr.s_addr = addr4;
+        addr_in->sin_port = port;
+      }
+    } 
+#endif
 
 #if HAVE_MESSAGES
 #if DEBUG_CONNECT
@@ -1411,6 +1430,7 @@
                      void *apc_cls,
                      MHD_AccessHandlerCallback dh, void *dh_cls, va_list ap)
 {
+  const int off = 0;
   const int on = 1;
   struct MHD_Daemon *retVal;
   int socket_fd;
@@ -1507,20 +1527,57 @@
       return NULL;
     }
 #endif
-  if ((options & MHD_USE_IPv6) != 0)
-#if HAVE_INET6
-    socket_fd = SOCKET (PF_INET6, SOCK_STREAM, 0);
-#else
+
+  /* MHD_USE_DUALSTACK overrides MHD_USE_IPv6. */
+  if ((options & MHD_USE_DUALSTACK) != 0)
+    options &= ~MHD_USE_IPv6;
+
+#if HAVE_INET6 && defined(IPV6_V6ONLY)
+  /* If we have IPv6 support, and support the IPV6_V6ONLY flag from
+   * RFC3493, we can support IPv4 and IPv6 on the same socket.
+   * If not, we ignore the flag and open it as IPv4-only.
+   */
+  if ((options & MHD_USE_DUALSTACK) != 0)
     {
+      socket_fd = SOCKET (PF_INET6, SOCK_STREAM, 0);
+      if (socket_fd == -1)
+        {
+          options &= ~MHD_USE_DUALSTACK;
+        }
+      else if (SETSOCKOPT (socket_fd,
+                           IPPROTO_IPV6,
+                           IPV6_V6ONLY,
+                           &off, sizeof (off)) < 0)
+        {
 #if HAVE_MESSAGES
-      fprintf (stderr, "AF_INET6 not supported\n");
+          FPRINTF (stderr, "setsockopt(IPV6_V6ONLY) failed: %s\n", STRERROR (errno));
 #endif
-      free (retVal);
-      return NULL;
+          CLOSE (socket_fd);
+          options &= ~MHD_USE_DUALSTACK;
+        }
     }
 #endif
-  else
-    socket_fd = SOCKET (PF_INET, SOCK_STREAM, 0);
+
+  /* If MHD_USE_DUALSTACK is set at this point, we have a working socket
+   * and should not try to create a new one. Otherwise, open one here.
+   */
+  if ((options & MHD_USE_DUALSTACK) == 0)
+    {
+      if ((options & MHD_USE_IPv6) != 0)
+#if HAVE_INET6
+        socket_fd = SOCKET (PF_INET6, SOCK_STREAM, 0);
+#else
+        {
+#if HAVE_MESSAGES
+          fprintf (stderr, "AF_INET6 not supported\n");
+#endif
+          free (retVal);
+          return NULL;
+        }
+#endif
+      else
+        socket_fd = SOCKET (PF_INET, SOCK_STREAM, 0);
+  }
   if (socket_fd == -1)
     {
 #if HAVE_MESSAGES
@@ -1566,7 +1623,7 @@
   if (NULL == servaddr)
     {
 #if HAVE_INET6
-      if ((options & MHD_USE_IPv6) != 0)
+      if ((options & MHD_USE_IPv6) != 0 || (options & MHD_USE_DUALSTACK) != 0)
         {
           memset (&servaddr6, 0, sizeof (struct sockaddr_in6));
           servaddr6.sin6_family = AF_INET6;
Index: libmicrohttpd-0.4.6/src/include/microhttpd.h
===================================================================
--- libmicrohttpd-0.4.6.orig/src/include/microhttpd.h	2010-03-13 09:41:30.000000000 +0100
+++ libmicrohttpd-0.4.6/src/include/microhttpd.h	2012-01-15 14:54:00.000000000 +0100
@@ -299,7 +299,14 @@
    * This option only works in conjunction with MHD_USE_THREAD_PER_CONNECTION
    * (at this point).
    */
-  MHD_USE_POLL = 64
+  MHD_USE_POLL = 64,
+
+  /**
+   * Accept both IPv6 and IPv4 connections if possible; otherwise,
+   * fall back to IPv4. Will override MHD_USE_IPv6, so if you give this flag,
+   * you will never get a pure IPv6 socket.
+   */
+  MHD_USE_DUALSTACK = 128,
 };
 
 /**

Reply via email to