[PATCH] remove zone identifier for IPv6 link-local addresses

Fabian Hugelshofer hugelshofer2006 at gmx.ch
Sun Jun 28 17:28:01 UTC 2009


On Fri, 2009-06-26 at 15:43 -0500, Rob Landley wrote:
> On Friday 26 June 2009 04:31:57 Fabian Hugelshofer wrote:
> > IPv6 knows scoped address types i.e. link and site local addresses. Link
> > local addresses can have a scope identifier to specify the
> > interface/link an address is valid on (e.g. fe80::1%eth0). This scope
> > identifier is only valid on a single node.
> >
> > RFC 4007 says that the scope identifier must not be sent across the
> > wire, unless every node agrees on the semantics (which is not the case
> > for link-local zones). Apache e.g. rejects HTTP requests with a scope
> > identifier in the host header (see
> > https://issues.apache.org/bugzilla/show_bug.cgi?id=35122).
> >
> > This patch removes the scope identifier from the HTTP request, whereas
> > it remains in the structure used to connect to the server.
> >
> > Signed-off-by: <hugelshofer2006 at gmx.ch>
> >
> > Regards,
> >
> > Fabian
> > diff --git a/networking/wget.c b/networking/wget.c
> > index ca3acd0..852e628 100644
> > --- a/networking/wget.c
> > +++ b/networking/wget.c
> > @@ -340,6 +340,57 @@ static void parse_url(char *src_url, struct host_info
> > *h) sp = h->host;
> >  }
> >
> > +/* RFC 4007 sais that the scope identifier MUST NOT be sent accross the
> > wire, + * unless all nodes agree on the semantic. Apache e.g. regards zone
> > identifiers + * in the Host header as invalid requests, see
> > + * https://issues.apache.org/bugzilla/show_bug.cgi?id=35122
> > + */
> > +static void strip_ipv6_scope(struct host_info *h)
> > +{
> > +	char *scope, *cp;
> > +
> > +	/* Remove the IPv6 zone identifier from the host address,
> > +	 * ugly parsing like in str2sockaddr() */
> > +
> > +	if (!ENABLE_FEATURE_IPV6) {
> > +		/* only for ipv6 */
> > +		return;
> > +	}
> 
> Um, that's not how ENABLE stuff works.  We're not just trying to disable 
> functionality, we're trying to save storage and memory by making the binary 
> smaller.
> 
> >        parse_url(argv[optind], &target);
> >-       server.host = target.host;
> >+       server.host = xstrdup(target.host);
> >        server.port = target.port;
> >+       strip_ipv6_scope(&target);
> 
> Here, try this instead:
> 
> parse_url(argv[optind], &target);
> server.port = target.port;
> if (ENABLE_FEATURE_IPV6) {
>   server.host = xstrdup(target.host);
>   strip_ipv6_scope(&target);
> } else server.host = target.host;
> 
> Now you can remove the if (ENABLE) from the actual strip_ipv6_scope function, 
> and it should only get called when the feature is enabled.
> 
> The point of doing that is that the compiler can tell at compile time whether 
> the ENABLE macro is set to 0 or 1, meaning it can do dead code elimination on 
> code that can never be called, meaning the entire strip_ipv6_scope() function 
> can be discarded when it's not configured in (since it's static, and therefore 
> couldn't be called from outside this file), and won't take up space for 
> everybody else.

Sure, this makes sense. I thought of putting a precompiler condition
around the code block, but of course the dead code elimination is much
nicer.

Below you find the patch including Rob's suggestions.

Fabian

diff --git a/networking/wget.c b/networking/wget.c
index ca3acd0..3608b90 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -340,6 +340,52 @@ static void parse_url(char *src_url, struct host_info *h)
 	sp = h->host;
 }
 
+/* RFC 4007 says that the scope identifier MUST NOT be sent across the wire,
+ * unless all nodes agree on the semantic. Apache e.g. regards zone identifiers
+ * in the Host header as invalid requests, see
+ * https://issues.apache.org/bugzilla/show_bug.cgi?id=35122
+ */
+static void strip_ipv6_scope(struct host_info *h)
+{
+	char *scope, *cp;
+
+	/* Remove the IPv6 zone identifier from the host address,
+	 * ugly parsing like in str2sockaddr() */
+
+	/* test if it's an IPv6 address */
+	if (h->host[0] == '[') {
+		cp = strchr(h->host, ']');
+		if (!cp || (cp[1] != ':' && cp[1] != '\0')) {
+			/* malformed address, must be [xx]:nn or [xx], dnt touch */
+			return;
+		}
+	} else {
+		cp = strrchr(h->host, ':');
+		if (cp && strchr(h->host, ':') != cp) {
+			/* more than one ':' but no port specifier, eg. "::1" */
+			cp = NULL;
+		} else {
+			/* no or only one ':' => no IPv6 address, dnt touch */
+			return;
+		}
+	}
+
+	/* as we have an IPv6 address, let's see if there is a zone identifier */
+	scope = strchr(h->host, '%');
+	if (!scope || (cp && scope > cp)) {
+		/* no scope identifier, dnt touch */
+		return;
+	}
+
+	/* remove the zone identifier */
+	if (cp) {
+		/* cp points to "]", overwrite the zone identifier with the rest */
+		overlapping_strcpy(scope, cp);
+	} else {
+		/* no port specifier, terminate at zone identifier */
+		*scope = '\0';
+	}
+}
 
 static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
 {
@@ -527,8 +573,13 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
 #endif
 
 	parse_url(argv[optind], &target);
-	server.host = target.host;
 	server.port = target.port;
+	if (ENABLE_FEATURE_IPV6) {
+		server.host = xstrdup(target.host);
+		strip_ipv6_scope(&target);
+	} else {
+		server.host = target.host;
+	}
 
 	/* Use the proxy if necessary */
 	if (use_proxy) {





More information about the busybox mailing list