Forum

No virtual hosts when IPv6 is enabled?

Guido
26 April 2013, 20:12


Hiawatha version: 9.1
Operating System: Ubuntu 10.04.4 LTS


I've just started trying to configure Hiawatha because of it being a lightweight server and also having loads of attention for security.

I've set up three virtual hosts, with separate logging, which does work. This is with a binding configuring only a port.

Noticing that I had no IPv6 connectivity, I added "Interface = ::", which does give me both IPv4 & IPv6 connectivity as expected.

Unexepectedly tho, doing so, I am losing my virtual hosts and can only connect to the default website.
It does not matter whether I connect over IPv4 or IPv6, the virtual hosts are gone.

Removing the "Interface = ::" makes me lose IPv6 connectivy, yet re-enables the virtual hosts again....

I am either missing something in the needed configuration settings or I am hitting a bug...

Regards,

Guido
Hugo Leisink
26 April 2013, 20:19
Can you show me your configuration file?
Guido
26 April 2013, 20:50
Sent you a link over the Contact form.
Hugo Leisink
27 April 2013, 17:27
Try specifying two bindings, one for IPv4 and one for IPv6:
Binding {
Port = 80
Interface = <your IPv4 address>
}

Binding {
Port = 80
Interface = <your IPv6 address>
}
Guido
27 April 2013, 19:22
On the IPv4 the virtual hosts work. The IPv6 will only show the default website.
Hugo Leisink
27 April 2013, 19:25
What URLs do you use to test IPv4 and IPv6 connectivity?
Guido
27 April 2013, 21:16
The URLs are both the same, however, as I am testing I am using an alternate port.
For forcing IPv4 or IPv6, I am making use of the hosts lookup file to circumvent the regular resolver.

So in the webbrowser I type: http://www.mydomain.com:88/

Come to think of it, you might be on to something here...
That alternate port could be confusing on IPv6 because of the colon.
Guido
27 April 2013, 21:37
Just tested it with Hiawatha 9.0 on another machine: it definetely has to do with the alternate port. On the regular port 80 the virtualhost works over IPv6, on an alternate port the virtualhost on IPv6 is broken.
Guido
27 April 2013, 23:57
Looks like I found it... Not sure if you like the coding, but it gives the idea.
And yes, I guess it would have been easier to simply put it on port 80 where it'll be going soon, but where is the fun in that...
--- session.c.orig      2013-04-27 22:41:09.100374864 +0200
+++ session.c 2013-04-27 23:51:51.746869519 +0200
@@ -463,9 +463,6 @@
*/
int remove_port_from_hostname(char *hostname, t_binding *binding) {
char *c;
-#ifdef ENABLE_IPV6
- char old, ip[IPv6_LEN];
-#endif

if (hostname == NULL) {
return -1;
@@ -481,21 +478,29 @@
}
#ifdef ENABLE_IPV6
} else if (binding->interface.family == AF_INET6) {
- if ((c = strrchr(hostname, '.')) != NULL) {
+
+ if (hostname[0] == '[' )
+ {
+ int i = 0;
+ for (i = 0; i < (int)strlen(hostname); i++)
+ {
+ if ( hostname[i-1] == ']' )
+ {
+ i--;
+ break;
+ }
+ hostname[i-1] = hostname[i];
+ }
+ hostname[i] = '\0';
+
+ }
+ else if ((c = strrchr(hostname, ':')) != NULL)
+ {
if (c == hostname) {
return -1;
}

- old = *c;
*c = '\0';
-
- if ((*hostname == '[') && (*(c - 1) == ']')) {
- return 0;
- }
-
- if (inet_pton(AF_INET6, hostname, ip) <= 0) {
- *c = old;
- }
}
#endif
}
Hugo Leisink
28 April 2013, 12:06
The patch you supplied contains bugs that could cause stability issues. But I understand the issue, I'll take a look at it.
Hugo Leisink
28 April 2013, 15:12
Try this new function please:
int remove_port_from_hostname(char *hostname, t_binding *binding) {
char *c;

if (hostname == NULL) {
return -1;
}

#ifdef ENABLE_IPV6
if (binding->interface.family == AF_INET6) {
if ((*hostname == '[') && ((c = strchr(hostname, ']')) != NULL)) {
if (*(c + 1) == ':') {
*(c + 1) = '\0';
}

return 0;
}
}
#endif

if ((c = strrchr(hostname, ':')) != NULL) {
if (c == hostname) {
return -1;
}

*c = '\0';
}

return 0;
}
Guido
29 April 2013, 19:51

Hugo, thanks a lot for the great software and great support!! So far, that patch seems to works as expected!

On a sidenote, what are your thoughts about IPv4-over-IPv6 Mapped Addresses? While debugging this issue I noted that because of the dual-stack the IPv4 addreses are both logged and forwarded in the proxy headers in their hybrid notation. My personal preference would be to record the IPv4-over-IPv6 mapped addresses in their IPv4 representation instead. I haven't been able to figure out with the RFC-guidelines on this are.

While this might be categorized as a feature request, it also seems to result in a bug or at least into unexpected behavior: among others, a quick test seems to indicate that using the IPv6-mapped representation will result in circumvention of the access control.

For example, using IPv6-mapped representation will allow to bypass

AccessList = deny 10.11.12.13

To block it, one needs the IPv6-mapped representation instead:

AccessList = deny ::ffff:10.11.12.13

Haven't fully debugged this, so I guess this will only happen when one uses a single binding for all interfaces, i.e. Interface = :: .

With some searching on the web, I coded the following patch... Not sure if it is all fully correct, but on first glance it seems to do what I want it to do...

--- hiawatha.c.orig     2013-04-29 19:40:47.512425668 +0200
+++ hiawatha.c 2013-04-29 19:40:47.512425668 +0200
@@ -114,6 +114,18 @@
#endif
;

+#ifdef ENABLE_IPV6
+#ifndef IN6_IS_ADDR_V4MAPPED
+#define IN6_IS_ADDR_V4MAPPED(a) \
+ ((((a)->s6_words[0]) == 0) && \
+ (((a)->s6_words[1]) == 0) && \
+ (((a)->s6_word[2]) == 0) && \
+ (((a)->s6_word[3]) == 0) && \
+ (((a)->s6_word[4]) == 0) && \
+ (((a)->s6_word[5]) == 0xFFFF))
+#endif
+#endif
+
/* Create all logfiles with the right ownership and accessrights
*/
void touch_logfiles(t_config *config) {
@@ -432,9 +444,24 @@
return -1;
}

+ if (IN6_IS_ADDR_V4MAPPED(&caddr6.sin6_addr))
+ {
+ session->ip_address.family = AF_INET;
+ session->ip_address.size = IPv4_LEN;
+
+ const uint8_t *bytes = caddr6.sin6_addr.s6_addr;
+ bytes += 12;
+ struct in_addr addr = { *(const in_addr_t *)bytes };
+
+ memcpy(&(session->ip_address.value), (char*)(&addr), session->ip_address.size);
+ }
+ else
+ {
session->ip_address.family = AF_INET6;
session->ip_address.size = IPv6_LEN;
+
memcpy(&(session->ip_address.value), (char*)&caddr6.sin6_addr.s6_addr, session->ip_address.size);
+ }
#endif
} else {
Hugo Leisink
1 May 2013, 08:40
I'm not an IPv6 expert, so I will have to look into this IPv4 mapping thing. Thanks for reporting this issue.
This topic has been closed.