From: benno Date: Wed, 24 Mar 2021 20:59:53 +0000 (+0000) Subject: Responses to HEAD requests must not have a message body (even though they have X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=53e8df0d329f0e7890bb7e3976fd59a63d68e5b4;p=openbsd Responses to HEAD requests must not have a message body (even though they have a Content-Length header). HTTP RFC 7231 section 4.3.2. found by niklas@, claudio@ agrees. --- diff --git a/usr.sbin/relayd/http.h b/usr.sbin/relayd/http.h index e7688165275..b9784ad55ce 100644 --- a/usr.sbin/relayd/http.h +++ b/usr.sbin/relayd/http.h @@ -1,4 +1,4 @@ -/* $OpenBSD: http.h,v 1.11 2019/05/08 23:22:19 reyk Exp $ */ +/* $OpenBSD: http.h,v 1.12 2021/03/24 20:59:53 benno Exp $ */ /* * Copyright (c) 2012 - 2015 Reyk Floeter @@ -19,6 +19,8 @@ #ifndef HTTP_H #define HTTP_H +#include + #define HTTP_PORT 80 #define HTTPS_PORT 443 @@ -251,4 +253,13 @@ struct http_descriptor { struct kvtree http_headers; }; +struct http_method_node { + enum httpmethod hmn_method; + SIMPLEQ_ENTRY(http_method_node) hmn_entry; +}; + +struct http_session { + SIMPLEQ_HEAD(, http_method_node) hs_methods; +}; + #endif /* HTTP_H */ diff --git a/usr.sbin/relayd/relay.c b/usr.sbin/relayd/relay.c index 02324f516c2..da4a1aa0cc1 100644 --- a/usr.sbin/relayd/relay.c +++ b/usr.sbin/relayd/relay.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay.c,v 1.253 2021/01/27 20:33:05 eric Exp $ */ +/* $OpenBSD: relay.c,v 1.254 2021/03/24 20:59:53 benno Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter @@ -800,7 +800,7 @@ relay_input(struct rsession *con) switch (rlay->rl_proto->type) { case RELAY_PROTO_HTTP: - if (relay_httpdesc_init(&con->se_in) == -1) { + if (relay_http_priv_init(con) == -1) { relay_close(con, "failed to allocate http descriptor", 1); return; diff --git a/usr.sbin/relayd/relay_http.c b/usr.sbin/relayd/relay_http.c index 69b48697427..29e401e0935 100644 --- a/usr.sbin/relayd/relay_http.c +++ b/usr.sbin/relayd/relay_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay_http.c,v 1.80 2021/01/09 08:53:58 denis Exp $ */ +/* $OpenBSD: relay_http.c,v 1.81 2021/03/24 20:59:54 benno Exp $ */ /* * Copyright (c) 2006 - 2016 Reyk Floeter @@ -112,6 +112,21 @@ relay_http_init(struct relay *rlay) relay_calc_skip_steps(&rlay->rl_proto->rules); } +int +relay_http_priv_init(struct rsession *con) +{ + + struct http_session *hs; + + if ((hs = calloc(1, sizeof(*hs))) == NULL) + return (-1); + SIMPLEQ_INIT(&hs->hs_methods); + DPRINTF("%s: session %d http_session %p", __func__, + con->se_id, hs); + con->se_priv = hs; + return (relay_httpdesc_init(&con->se_in)); +} + int relay_httpdesc_init(struct ctl_relay_event *cre) { @@ -162,6 +177,9 @@ relay_read_http(struct bufferevent *bev, void *arg) size_t size, linelen; struct kv *hdr = NULL; struct kv *upgrade = NULL, *upgrade_ws = NULL; + struct http_method_node *hmn; + struct http_session *hs; + enum httpmethod request_method; getmonotime(&con->se_tv_last); cre->timedout = 0; @@ -239,11 +257,34 @@ relay_read_http(struct bufferevent *bev, void *arg) DPRINTF("%s: session %d: header '%s: %s'", __func__, con->se_id, key, value); + hs = con->se_priv; + DPRINTF("%s: session %d http_session %p", __func__, + con->se_id, hs); + /* * Identify and handle specific HTTP request methods */ if (cre->line == 1 && cre->dir == RELAY_DIR_RESPONSE) { desc->http_method = HTTP_METHOD_RESPONSE; + hmn = SIMPLEQ_FIRST(&hs->hs_methods); + + /* + * There is nothing preventing the relay to send + * an unbalanced response. Be prepared. + */ + if (hmn == NULL) { + request_method = HTTP_METHOD_NONE; + DPRINTF("%s: session %d unbalanced response", + __func__, con->se_id); + } else { + SIMPLEQ_REMOVE_HEAD(&hs->hs_methods, hmn_entry); + request_method = hmn->hmn_method; + DPRINTF("%s: session %d dequeing %s", __func__, + con->se_id, + relay_httpmethod_byid(request_method)); + free(hmn); + } + /* * Decode response path and query */ @@ -287,6 +328,15 @@ relay_read_http(struct bufferevent *bev, void *arg) free(line); goto fail; } + if ((hmn = calloc(1, sizeof *hmn)) == NULL) { + free(line); + goto fail; + } + hmn->hmn_method = desc->http_method; + DPRINTF("%s: session %d enqueing %s", __func__, + con->se_id, + relay_httpmethod_byid(hmn->hmn_method)); + SIMPLEQ_INSERT_TAIL(&hs->hs_methods, hmn, hmn_entry); /* * Decode request path and query */ @@ -332,16 +382,26 @@ relay_read_http(struct bufferevent *bev, void *arg) goto abort; } /* - * Need to read data from the client after the - * HTTP header. - * XXX What about non-standard clients not using - * the carriage return? And some browsers seem to - * include the line length in the content-length. + * HEAD responses may provide a Content-Length header, + * but if so it should just be ignored, since there is + * no actual payload in the response. */ - cre->toread = strtonum(value, 0, LLONG_MAX, &errstr); - if (errstr) { - relay_abort_http(con, 500, errstr, 0); - goto abort; + if (desc->http_method != HTTP_METHOD_RESPONSE + || request_method != HTTP_METHOD_HEAD) { + /* + * Need to read data from the client after the + * HTTP header. + * XXX What about non-standard clients not + * using the carriage return? And some browsers + * seem to include the line length in the + * content-length. + */ + cre->toread = strtonum(value, 0, LLONG_MAX, + &errstr); + if (errstr) { + relay_abort_http(con, 500, errstr, 0); + goto abort; + } } /* * response with a status code of 1xx @@ -502,8 +562,9 @@ relay_read_http(struct bufferevent *bev, void *arg) case HTTP_METHOD_SEARCH: case HTTP_METHOD_PATCH: /* HTTP request payload */ - if (cre->toread > 0) + if (cre->toread > 0) { bev->readcb = relay_read_httpcontent; + } /* Single-pass HTTP body */ if (cre->toread < 0) { @@ -1117,6 +1178,19 @@ relay_abort_http(struct rsession *con, u_int code, const char *msg, void relay_close_http(struct rsession *con) { + struct http_session *hs = con->se_priv; + struct http_method_node *hmn; + + DPRINTF("%s: session %d http_session %p", __func__, + con->se_id, hs); + if (hs != NULL) + while (!SIMPLEQ_EMPTY(&hs->hs_methods)) { + hmn = SIMPLEQ_FIRST(&hs->hs_methods); + SIMPLEQ_REMOVE_HEAD(&hs->hs_methods, hmn_entry); + DPRINTF("%s: session %d freeing %s", __func__, + con->se_id, relay_httpmethod_byid(hmn->hmn_method)); + free(hmn); + } relay_httpdesc_free(con->se_in.desc); free(con->se_in.desc); relay_httpdesc_free(con->se_out.desc);