libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit de6922ca8204fe08d8105591bf094c0a359da8c3
parent 65413082c902934cacc32899fcda4ddae35e2f21
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Fri, 21 May 2021 18:59:49 +0300

Added detection of HTTP version during early parsing

Requests with unsupported HTTP versions are rejected as specified
by RFC 7230.

Diffstat:
Msrc/microhttpd/connection.c | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/microhttpd/internal.h | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 150 insertions(+), 0 deletions(-)

diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -114,6 +114,26 @@ #define INTERNAL_ERROR "" #endif +/** + * Response text used when the request HTTP version is too old. + */ +#ifdef HAVE_MESSAGES +#define REQ_HTTP_VER_IS_TOO_OLD \ + "<html><head><title>Requested HTTP version is not supported</title></head><body>Requested HTTP version is too old and not supported.</body></html>" +#else +#define REQ_HTTP_VER_IS_TOO_OLD "" +#endif + +/** + * Response text used when the request HTTP version is too old. + */ +#ifdef HAVE_MESSAGES +#define REQ_HTTP_VER_IS_NOT_SUPPORTED \ + "<html><head><title>Requested HTTP version is not supported</title></head><body>Requested HTTP version is not supported.</body></html>" +#else +#define REQ_HTTP_VER_IS_NOT_SUPPORTED "" +#endif + /** * sendfile() chuck size @@ -2088,6 +2108,69 @@ parse_cookie_header (struct MHD_Connection *connection) /** + * Detect HTTP version + * + * @param connection the connection + * @param http_string the pointer to HTTP version string + * @param len the length of @a http_string in bytes + * @return #MHD_YES if HTTP version is correct and supported, + * #MHD_NO if HTTP version is not correct or unsupported. + */ +static enum MHD_Result +parse_http_version (struct MHD_Connection *connection, + const char*http_string, + size_t len) +{ + const char *const h = http_string; /**< short alias */ + mhd_assert (NULL != http_string); + + /* String must starts with 'HTTP/d.d', case-sensetive match. + * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */ + if ((len != 8) || + (h[0] != 'H') || (h[1] != 'T') || (h[2] != 'T') || (h[3] != 'P') || + (h[4] != '/') + || (h[6] != '.') || + ((h[5] < '0') || (h[5] > '9')) || + ((h[7] < '0') || (h[7] > '9'))) + { + connection->http_ver = MHD_HTTP_VER_INVALID; + transmit_error_response (connection, + MHD_HTTP_BAD_REQUEST, + REQUEST_MALFORMED); + return MHD_NO; + } + if (1 == h[5] - '0') + { + /* HTTP/1.x */ + if (1 == h[7] - '0') + connection->http_ver = MHD_HTTP_VER_1_1; + else if (0 == h[7] - '0') + connection->http_ver = MHD_HTTP_VER_1_0; + else + connection->http_ver = MHD_HTTP_VER_1_2__1_9; + + return MHD_YES; + } + + if (0 == h[5] - '0') + { + /* Too old major version */ + connection->http_ver = MHD_HTTP_VER_TOO_OLD; + transmit_error_response (connection, + MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED, + REQ_HTTP_VER_IS_TOO_OLD); + return MHD_NO; + } + + connection->http_ver = MHD_HTTP_VER_FUTURE; + transmit_error_response (connection, + MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED, + REQ_HTTP_VER_IS_NOT_SUPPORTED); + return MHD_NO; +} + + +/** * Parse the first line of the HTTP HEADER. * * @param connection the connection (updated) @@ -2126,6 +2209,8 @@ parse_initial_message_line (struct MHD_Connection *connection, uri = NULL; connection->version = ""; args = NULL; + if (MHD_NO == parse_http_version (connection, connection->version, 0)) + return MHD_NO; } else { @@ -2146,11 +2231,17 @@ parse_initial_message_line (struct MHD_Connection *connection, /* http_version points to character before HTTP version string */ http_version[0] = '\0'; connection->version = http_version + 1; + if (MHD_NO == parse_http_version (connection, connection->version, + line_len + - (connection->version - line))) + return MHD_NO; uri_len = http_version - uri; } else { connection->version = ""; + if (MHD_NO == parse_http_version (connection, connection->version, 0)) + return MHD_NO; uri_len = line_len - (uri - line); } /* check for spaces in URI if we are "strict" */ @@ -3756,6 +3847,7 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) /* can try to keep-alive */ connection->version = NULL; + connection->http_ver = MHD_HTTP_VER_UNKNOWN; connection->state = MHD_CONNECTION_INIT; connection->last = NULL; connection->colon = NULL; diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h @@ -732,6 +732,59 @@ enum MHD_ConnKeepAlive MHD_CONN_USE_KEEPALIVE = 1 }; +enum MHD_HTTP_version +{ + /** + * Not a HTTP protocol or HTTP version is invalid. + */ + MHD_HTTP_VER_INVALID = -1, + + /** + * HTTP version is not yet received from the client. + */ + MHD_HTTP_VER_UNKNOWN = 0, + + /** + * HTTP version before 1.0, unsupported. + */ + MHD_HTTP_VER_TOO_OLD = 1, + + /** + * HTTP version 1.0 + */ + MHD_HTTP_VER_1_0 = 2, + + /** + * HTTP version 1.1 + */ + MHD_HTTP_VER_1_1 = 3, + + /** + * HTTP version 1.2-1.9, must be used as 1.1 + */ + MHD_HTTP_VER_1_2__1_9 = 4, + + /** + * HTTP future version. Unsupported. + * Reserved, not really detected. + */ + MHD_HTTP_VER_FUTURE = 100 +}; + +/** + * Returns boolean 'true' if HTTP version is supported by MHD + */ +#define MHD_IS_HTTP_VER_SUPPORTED(ver) (MHD_HTTP_VER_1_0 <= (ver) && \ + MHD_HTTP_VER_1_2__1_9 >= (ver)) + +/** + * Protocol should be used as HTTP/1.1 protocol. + * + * See the last paragraph of + * https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 + */ +#define MHD_IS_HTTP_VER_1_1_COMPAT(ver) (MHD_HTTP_VER_1_1 == (ver) || \ + MHD_HTTP_VER_1_2__1_9 == (ver)) /** * State kept for each HTTP request. @@ -840,6 +893,11 @@ struct MHD_Connection char *version; /** + * HTTP protocol version as enum. + */ + enum MHD_HTTP_version http_ver; + + /** * Close connection after sending response? * Functions may change value from "Unknown" or "KeepAlive" to "Must close", * but no functions reset value "Must Close" to any other value.