On Mon, Aug 25, 2003 at 12:19:27PM -0400, Jeff Trawick wrote:
> >IPv6 with Apache on tru64 has been broken for a while now, and I finally
> >got round to figuring out what it was. It appears that Tru64's
> >getaddrinfo returns ai_addrlen of 32 for AF_INET6 address, but that
> >sizeof(struct sockaddr_in6) is 28.
> >
> >This meant that apr_sockaddr_vars_set() was blindly setting salen
> >to 28, and then later when Apache called bind( , , 28) Tru64 bombed
> >out in unpleasant error land. Patch attached.
>
> I'm not sure I would use the word "blindly", as we're using
> sizeof(sockaddr_in6).
True, but the return of getaddrinfo is being ignored is what I mean.
> If getaddrinfo() returns IPv6 addresses larger
> than that, we're hosed at that point and shouldn't be copying into the
> apr_sockaddr_info_t anyway.
We get away with it, just about. Allthough the full length is
copied;
429 new_sa->pool = p;
430 memcpy(&new_sa->sa, ai->ai_addr, ai->ai_addrlen);
431 apr_sockaddr_vars_set(new_sa, ai->ai_family, port);
it's salen itself that gets overwritten. This then gets reset back
to a sensible value with the assignment in vars_set and the patch.
It's moderately bold, but it works out fine.
Now, back to the reason why it does any of this; bind() is expecting
a struct sockaddr_in6.sin6_addr to be passed, which would make the
extra 4 bytes it's looking for correspond to sockaddr_in6.sin6_scope_id.
This makes sense in a freaky-dec-developer kind of way, though it really
is a bug.
I'm guessing it means that the devlopers originally thought it might
be useful to allow people to bind() to scoped addresses , and to do
this you'd need an interface index (for the unitiated; IPv6 scoped
addresses are interface-specific).
Now, because in Apache the address we pass is always global unicast
and IN6_IS_ADDR_LINKLOCAL/IN6_IS_ADDR_SITELOCAL/IN6_IS_ADDR_MULTICAST
are never going to evaluate to true, bind() never bothers looking
for the scope id. Which is a good thing since binding to the 32nd
system interface would probably not be the desired behaviour.
> Isn't the first step to see why getaddrinfo() is returning IPv6
> addresses larger than sizeof(sockaddr_in6) and take it from there?
I really should have included that. It's an OS bug, but it's
a consistent one. bind() simply fails if passed 28, and works
fine with 32, so we might aswell pass it what it wants. I
have it running right now at http://listserv.heanet.ie/ .
Actually now that I think about it, maybe the patch below is a
better idea.
> (Alternatively, why am I confused?)
I am too, but I've been playing with it all day now and it really
does behave like this. Nothing on DU suprises me anymore.
--
Colm MacC�rthaigh Public Key: [EMAIL PROTECTED]
[EMAIL PROTECTED] http://www.stdlib.net/
Index: srclib/apr/network_io/unix/sockaddr.c
===================================================================
RCS file: /home/cvspublic/apr/network_io/unix/sockaddr.c,v
retrieving revision 1.42
diff -u -u -r1.42 sockaddr.c
--- srclib/apr/network_io/unix/sockaddr.c 15 Aug 2003 02:21:55 -0000
1.42
+++ srclib/apr/network_io/unix/sockaddr.c 25 Aug 2003 16:55:21 -0000
@@ -429,6 +429,19 @@
new_sa->pool = p;
memcpy(&new_sa->sa, ai->ai_addr, ai->ai_addrlen);
apr_sockaddr_vars_set(new_sa, ai->ai_family, port);
+
+#if defined(OSF1)
+ /* Tru64 has a getaddrinfo/bind bug in that that it
+ * returns and expects ai_addrlen = 32 for AF_INET6,
+ * even though sizeof(struct sockaddr_in6) == 28.
+ * Allthough the memcpy above has over-written salen,
+ * which then got reset by apr_sockaddr_vars we get away
+ * with it. Tru64 bind(), in it's infinite wisdom,
+ * doesn't bother checking the extra 4 bytes for global
+ * unicast IPv6 addresses.
+ */
+ new_sa->salen = ai->ai_addrlen;
+#endif
if (!prev_sa) { /* first element in new list */
if (hostname) {