Hello,

while experimenting with httpd and IPv6 on WinXP, I found out that it's not working well. In short, the code expects that IPv4 mapped addresses and IPV6_V6ONLY socket option are supported. This is not true for NTs < 6.0. They have IPv4 and IPv6 as two separate stacks and therefore all IPv6 sockets always behave as V6ONLY and no IPv4 mapped addresses are possible. Unfortunately the IPV6_V6ONLY option is not understood, so if the code tries to set it, if fails with OS error. APR seems to be the best place where it can be fixed.

The attached patch modifies apr_socket_opt_set(). When the requested option is APR_IPV6_V6ONLY, it checks Windows version and if it's less than Vista, it does not call setsockopt(), but instead it calls directly apr_set_option() and pretends that the option was set. Then it returns APR_SUCCESS or APR_ENOTIMPL depending on if it was requested to set or unset the option. I'm not exactly sure about returning APR_ENOTIMPL, but httpd would fail with anything else. At first I just set "on = 1;" and let the original apr_set_option() use it, but that wasn't right, because it always returned APR_SUCCESS and some application other than httpd can depend on the result and in case it requested to unset the option, returning APR_SUCCESS would be wrong. Httpd doesn't really care about the result and checks the state later using apr_socket_opt_get(). That's why always calling apr_set_option(sock, APR_IPV6_V6ONLY, 1) is important. If omited, it would be possible (if compiled with IPv4 mapped addresses support) for apr_socket_opt_get() to return 0 (= socket can accept both IPv4 and 6), but that's not possible on these older Windows and if some code is depending on it, it may fail (like current httpd).

Sob
diff -Naur httpd-2.2.9/srclib/apr/include/arch/win32/apr_arch_networkio.h 
httpd-2.2.9-xpipv6/srclib/apr/include/arch/win32/apr_arch_networkio.h
--- httpd-2.2.9/srclib/apr/include/arch/win32/apr_arch_networkio.h      
2006-08-03 05:55:32.000000000 +0200
+++ httpd-2.2.9-xpipv6/srclib/apr/include/arch/win32/apr_arch_networkio.h       
2008-06-19 02:05:09.531250000 +0200
@@ -86,5 +86,14 @@
             (skt)->options &= ~(option);        \
     } while (0)
 
+/* Ugly solution - only the Windows 2008 SDK or later have this symbol defined.
+ * The symbol doesn't guarantee that the socket option is supported on
+ * the runtime version of Windows, so we define it here (for build systems) 
+ * and always check at runtime if it is supported.
+ */
+#ifndef IPV6_V6ONLY
+#define IPV6_V6ONLY 27
+#endif
+
 #endif  /* ! NETWORK_IO_H */
 
diff -Naur httpd-2.2.9/srclib/apr/network_io/win32/sockopt.c 
httpd-2.2.9-xpipv6/srclib/apr/network_io/win32/sockopt.c
--- httpd-2.2.9/srclib/apr/network_io/win32/sockopt.c   2006-08-03 
05:55:32.000000000 +0200
+++ httpd-2.2.9-xpipv6/srclib/apr/network_io/win32/sockopt.c    2008-06-20 
03:35:18.265625000 +0200
@@ -19,6 +19,7 @@
 #include "apr_general.h"
 #include "apr_strings.h"
 #include <string.h>
+#include "apr_arch_misc.h"
 
 apr_status_t soblock(SOCKET sd)
 {
@@ -195,12 +196,19 @@
         }
         break;
     case APR_IPV6_V6ONLY:
-#if APR_HAVE_IPV6 && defined(IPV6_V6ONLY)
+#if APR_HAVE_IPV6
         /* we don't know the initial setting of this option,
          * so don't check sock->options since that optimization
          * won't work
          */
-        if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_V6ONLY,
+        if (apr_os_level < APR_WIN_VISTA) {
+          /* Windows before Vista don't understand IPV6_V6ONLY, but since they
+           * have separate IPv4 and IPv6 stacks, they always behave like the
+           * IPV6_V6ONLY is set.
+           */
+          apr_set_option(sock, APR_IPV6_V6ONLY, 1);
+          if(on == 1) return APR_SUCCESS; else return APR_ENOTIMPL;
+        } else if (setsockopt(sock->socketdes, IPPROTO_IPV6, IPV6_V6ONLY,
                        (void *)&on, sizeof(int)) == -1) {
             return apr_get_netos_error();
         }

Reply via email to