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();
}