commit 3ff82c514becd08922fcf9bc9f4870941650932a
parent c3ddb2dd14bd7a39dedbbf3520c9a2052dd3e1ff
Author: Laslo Hunhold <dev@frign.de>
Date:   Tue,  3 Apr 2018 00:55:52 +0200
Clean up request host properly
We all agree that the IPv6 address format is a big clusterfuck and only
an insane person would've come up with it given the double colons
interfere with the way one actually appends a port to a normal IPv4 address.
To counteract in this issue, the RFC specifies that one should enclose
IPv6-addresses in square brackets to make the disctinction possible,
i.e.
	host: ::1
	port: 80
	--> [::1]:80
The host field can contain both a port suffix and, of course by the RFC,
have the address enclosed in square brackets. Given I personally see
this as a "transport enclosure" I'd rather like to see it gone as soon
as possible and thus implement this cleanup in the http-header-parser so
the output is nice and clean and we don't have to deal with this garbage
later on.
Thanks to Josuah Demangeon <mail@josuah.net> for his wonderful input and
his dedication to read the RFCs 3986 and 2732 in such great detail.
Diffstat:
| M | http.c | | | 38 | ++++++++++++++++++++++++++++++++++++++ | 
1 file changed, 38 insertions(+), 0 deletions(-)
diff --git a/http.c b/http.c
@@ -95,6 +95,7 @@ decode(char src[PATH_MAX], char dest[PATH_MAX])
 int
 http_get_request(int fd, struct request *r)
 {
+	struct in6_addr res;
 	size_t hlen, i, mlen;
 	ssize_t off;
 	char h[HEADER_MAX], *p, *q;
@@ -232,6 +233,43 @@ http_get_request(int fd, struct request *r)
 		p = q + (sizeof("\r\n") - 1);
 	}
 
+	/*
+	 * clean up host
+	 */
+
+	p = strrchr(r->field[REQ_HOST], ':');
+	q = strrchr(r->field[REQ_HOST], ']');
+
+	/* strip port suffix but don't interfere with IPv6 bracket notation
+	 * as per RFC 2732 */
+	if (p && (!q || p > q)) {
+		/* port suffix must not be empty */
+		if (*(p + 1) == '\0') {
+			return http_send_status(fd, S_BAD_REQUEST);
+		}
+		*p = '\0';
+	}
+
+	/* strip the brackets from the IPv6 notation and validate the address */
+	if (q) {
+		/* brackets must be on the outside */
+		if (r->field[REQ_HOST][0] != '[' || *(q + 1) != '\0') {
+			return http_send_status(fd, S_BAD_REQUEST);
+		}
+
+		/* remove the right bracket */
+		*q = '\0';
+		p = r->field[REQ_HOST] + 1;
+
+		/* validate the contained IPv6 address */
+		if (inet_pton(AF_INET6, p, &res) != 1) {
+			return http_send_status(fd, S_BAD_REQUEST);
+		}
+
+		/* copy it into the host field */
+		memmove(r->field[REQ_HOST], p, q - p + 1);
+	}
+
 	return 0;
 }