untrusted comment: signature from openbsd 5.8 base secret key
RWQNNZXtC/MqP0sYKM+F6r6VxjPUui4Z713kStPtVNEIR5IPFxl880x1zJliwCtkPtJAWnkaG1UWWwwLNC7PWfcy462xZzY9fQQ=

OpenBSD 5.8 errata 28, Aug 06, 2016:

Improve parsing of the Host-header by following RFC 7230 Section 5.4 more
strictly.

Apply by doing:
    signify -Vep /etc/signify/openbsd-58-base.pub -x 028_relayd.patch.sig \
        -m - | (cd /usr/src && patch -p0)

And then rebuild and install relayd:
        cd /usr/src/usr.sbin/relayd
        make obj
        make depend
        make
        make install

Index: usr.sbin/relayd/relay_http.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v
retrieving revision 1.52.4.1
diff -u -p -r1.52.4.1 relay_http.c
--- usr.sbin/relayd/relay_http.c	23 Jul 2016 20:56:02 -0000	1.52.4.1
+++ usr.sbin/relayd/relay_http.c	2 Aug 2016 19:24:52 -0000
@@ -162,7 +162,8 @@ relay_read_http(struct bufferevent *bev,
 	struct protocol		*proto = rlay->rl_proto;
 	struct evbuffer		*src = EVBUFFER_INPUT(bev);
 	char			*line = NULL, *key, *value;
-	int			 action;
+	char			*urlproto, *host, *path;
+	int			 action, unique, ret;
 	const char		*errstr;
 	size_t			 size, linelen;
 	struct kv		*hdr = NULL;
@@ -343,11 +344,35 @@ relay_read_http(struct bufferevent *bev,
 		    strcasecmp("chunked", value) == 0)
 			desc->http_chunked = 1;
 
+		/* The following header should only occur once */
+		if (strcasecmp("Host", key) == 0) {
+			unique = 1;
+
+			/*
+			 * The path may contain a URL.  The host in the
+			 * URL has to match the Host: value.
+			 */
+			if (parse_url(desc->http_path,
+			    &urlproto, &host, &path) == 0) {
+				ret = strcasecmp(host, value);
+				free(urlproto);
+				free(host);
+				free(path);
+				if (ret != 0) {
+					relay_abort_http(con, 400,
+					    "malformed host", 0);
+					goto abort;
+				}
+			}
+		} else
+			unique = 0;
+
 		if (cre->line != 1) {
 			if ((hdr = kv_add(&desc->http_headers, key,
-			    value)) == NULL) {
-				free(line);
-				goto fail;
+			    value, unique)) == NULL) {
+				relay_abort_http(con, 400,
+				    "malformed header", 0);
+				goto abort;
 			}
 			desc->http_lastheader = hdr;
 		}
@@ -1550,7 +1575,7 @@ relay_apply_actions(struct ctl_relay_eve
 		if (addkv && kv->kv_matchtree != NULL) {
 			/* Add new entry to the list (eg. new HTTP header) */
 			if ((match = kv_add(kv->kv_matchtree, kp->kv_key,
-			    kp->kv_value)) == NULL)
+			    kp->kv_value, 0)) == NULL)
 				goto fail;
 			match->kv_option = kp->kv_option;
 			match->kv_type = kp->kv_type;
Index: usr.sbin/relayd/relayd.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.c,v
retrieving revision 1.143
diff -u -p -r1.143 relayd.c
--- usr.sbin/relayd/relayd.c	29 Jul 2015 20:55:43 -0000	1.143
+++ usr.sbin/relayd/relayd.c	2 Aug 2016 19:24:52 -0000
@@ -650,9 +650,8 @@ purge_relay(struct relayd *env, struct r
 	free(rlay);
 }
 
-
 struct kv *
-kv_add(struct kvtree *keys, char *key, char *value)
+kv_add(struct kvtree *keys, char *key, char *value, int unique)
 {
 	struct kv	*kv, *oldkv;
 
@@ -660,24 +659,30 @@ kv_add(struct kvtree *keys, char *key, c
 		return (NULL);
 	if ((kv = calloc(1, sizeof(*kv))) == NULL)
 		return (NULL);
-	if ((kv->kv_key = strdup(key)) == NULL) {
-		free(kv);
-		return (NULL);
-	}
+	if ((kv->kv_key = strdup(key)) == NULL)
+		goto fail;
 	if (value != NULL &&
-	    (kv->kv_value = strdup(value)) == NULL) {
-		free(kv->kv_key);
-		free(kv);
-		return (NULL);
-	}
+	    (kv->kv_value = strdup(value)) == NULL)
+		goto fail;
 	TAILQ_INIT(&kv->kv_children);
 
 	if ((oldkv = RB_INSERT(kvtree, keys, kv)) != NULL) {
+		/*
+		 * return error if the key should occur only once,
+		 * or add it to a list attached to the key's node.
+		 */
+		if (unique)
+			goto fail;
 		TAILQ_INSERT_TAIL(&oldkv->kv_children, kv, kv_entry);
 		kv->kv_parent = oldkv;
 	}
 
 	return (kv);
+ fail:
+	free(kv->kv_key);
+	free(kv->kv_value);
+	free(kv);
+	return (NULL);
 }
 
 int
@@ -1387,6 +1392,52 @@ canonicalize_host(const char *host, char
  fail:
 	errno = EINVAL;
 	return (NULL);
+}
+
+int
+parse_url(const char *url, char **protoptr, char **hostptr, char **pathptr)
+{
+	char	*p, *proto = NULL, *host = NULL, *path = NULL;
+
+	/* return error if it is not a URL */
+	if ((p = strstr(url, ":/")) == NULL ||
+	    (strcspn(url, ":/") != (size_t)(p - url)))
+		return (-1);
+
+	/* get protocol */
+	if ((proto = strdup(url)) == NULL)
+		goto fail;
+	p = proto + (p - url);
+
+	/* get host */
+	p += strspn(p, ":/");
+	if (*p == '\0' || (host = strdup(p)) == NULL)
+		goto fail;
+	*p = '\0';
+
+	/* find and copy path or default to "/" */
+	if ((p = strchr(host, '/')) == NULL)
+		p = "/";
+	if ((path = strdup(p)) == NULL)
+		goto fail;
+
+	/* strip path after host */
+	host[strcspn(host, "/")] = '\0';
+
+	DPRINTF("%s: %s proto %s, host %s, path %s", __func__,
+	    url, proto, host, path);
+
+	*protoptr = proto;
+	*hostptr = host;
+	*pathptr = path;
+
+	return (0);
+
+ fail:
+	free(proto);
+	free(host);
+	free(path);
+	return (-1);
 }
 
 int
Index: usr.sbin/relayd/relayd.h
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v
retrieving revision 1.213.4.1
diff -u -p -r1.213.4.1 relayd.h
--- usr.sbin/relayd/relayd.h	23 Jul 2016 20:56:02 -0000	1.213.4.1
+++ usr.sbin/relayd/relayd.h	2 Aug 2016 19:24:52 -0000
@@ -1270,6 +1270,7 @@ void		 purge_table(struct relayd *, stru
 void		 purge_relay(struct relayd *, struct relay *);
 char		*digeststr(enum digest_type, const u_int8_t *, size_t, char *);
 const char	*canonicalize_host(const char *, char *, size_t);
+int		 parse_url(const char *, char **, char **, char **);
 int		 map6to4(struct sockaddr_storage *);
 int		 map4to6(struct sockaddr_storage *, struct sockaddr_storage *);
 void		 imsg_event_add(struct imsgev *);
@@ -1283,7 +1284,7 @@ struct in6_addr *prefixlen2mask6(u_int8_
 u_int32_t	 prefixlen2mask(u_int8_t);
 int		 accept_reserve(int, struct sockaddr *, socklen_t *, int,
 		     volatile int *);
-struct kv	*kv_add(struct kvtree *, char *, char *);
+struct kv	*kv_add(struct kvtree *, char *, char *, int);
 int		 kv_set(struct kv *, char *, ...);
 int		 kv_setkey(struct kv *, char *, ...);
 void		 kv_delete(struct kvtree *, struct kv *);