libmicrohttpd

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

commit 185f740e0684a8758cbf381fe5c44b3ed72d4a40
parent 3e2ba2b1ced0b4c9d13961e89e43c3932399f6d6
Author: Christian Grothoff <christian@grothoff.org>
Date:   Fri, 25 Oct 2019 14:40:02 +0200

allow clients to override sanity check for content-length header

Diffstat:
MChangeLog | 3+++
Mdoc/libmicrohttpd.texi | 14++++++++++++++
Msrc/include/microhttpd.h | 40++++++++++++++++++++++++++++++++++++----
Msrc/microhttpd/connection.c | 5+++--
Msrc/microhttpd/daemon.c | 14++++++++++----
Msrc/microhttpd/internal.h | 6++++++
Msrc/microhttpd/response.c | 32++++++++++++++++++++------------
7 files changed, 92 insertions(+), 22 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,6 @@ +Fri 25 Oct 2019 02:31:59 PM CEST + Introduce MHD_RF_INSANITY_HEADER_CONTENT_LENGTH. -CG + Thu 17 Oct 2019 04:50:52 PM CEST Integrate 0-byte send() method for uncorking for old FreeBSD/OS X systems into new mhd_send.c logic for uncorking. diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi @@ -776,6 +776,15 @@ in header field names. This is disallowed by the standard. It is not recommended to set it to -1 on publicly available servers as it may potentially lower level of protection. +@item MHD_OPTION_SERVER_INSANITY +@cindex testing +Allows the application to disable certain sanity precautions in MHD. With +these, the client can break the HTTP protocol, so this should never be used in +production. The options are, however, useful for testing HTTP clients against +"broken" server implementations. This argument must be followed by an +@code{unsigned int}, corresponding to an @code{enum MHD_DisableSanityCheck}. + +Right now, no sanity checks can be disabled. @item MHD_OPTION_SOCK_ADDR @cindex bind, restricting bind @@ -1207,6 +1216,11 @@ GET / HTTP/1.1 as the ``Host'' header is missing and is mandatory in HTTP 1.1, but it should succeed when interpreted with HTTP 1.0. + +@item MHD_RF_INSANITY_HEADER_CONTENT_LENGTH +Disable sanity check preventing clients from manually +setting the HTTP content length option. + @end table @end deftp diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -132,7 +132,7 @@ typedef intptr_t ssize_t; * Current version of the library. * 0x01093001 = 1.9.30-1. */ -#define MHD_VERSION 0x00096701 +#define MHD_VERSION 0x00096702 /** * MHD-internal return code for "YES". @@ -1691,10 +1691,35 @@ enum MHD_OPTION * followed by a argument of type `gnutls_certificate_retrieve_function3 *`. * This option provides an * alternative/extension to #MHD_OPTION_HTTPS_CERT_CALLBACK. - * You must use this version if you want to use OCSP stapling. + * You must use this version if you want to use OCSP stapling. * Using this option requires GnuTLS 3.6.3 or higher. */ - MHD_OPTION_HTTPS_CERT_CALLBACK2 = 31 + MHD_OPTION_HTTPS_CERT_CALLBACK2 = 31, + + /** + * Allows the application to disable certain sanity precautions + * in MHD. With these, the client can break the HTTP protocol, + * so this should never be used in production. The options are, + * however, useful for testing HTTP clients against "broken" + * server implementations. + * This argument must be followed by an "unsigned int", corresponding + * to an `enum MHD_DisableSanityCheck`. + */ + MHD_OPTION_SERVER_INSANITY = 32 +}; + + +/** + * Bitfield for the #MHD_OPTION_SERVER_INSANITY specifying + * which santiy checks should be disabled. + */ +enum MHD_DisableSanityCheck +{ + /** + * All sanity checks are enabled. + */ + MHD_DSC_SANE = 0 + }; @@ -2924,7 +2949,14 @@ enum MHD_ResponseFlags * #MHD_RF_HTTP_VERSION_1_0_ONLY flag, the response's HTTP version will * always be set to 1.0 and "Connection" headers are still supported. */ - MHD_RF_HTTP_VERSION_1_0_RESPONSE = 2 + MHD_RF_HTTP_VERSION_1_0_RESPONSE = 2, + + /** + * Disable sanity check preventing clients from manually + * setting the HTTP content length option. + */ + MHD_RF_INSANITY_HEADER_CONTENT_LENGTH = 4 + }; diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -1430,8 +1430,9 @@ build_header_response (struct MHD_Connection *connection) (MHD_HTTP_NO_CONTENT != rc) && (MHD_HTTP_NOT_MODIFIED != rc) && (MHD_HTTP_OK <= rc) && - (NULL == /* this should always succeed due to check in - MHD_add_response_header() */ + (NULL == /* this COULD fail if the check in + MHD_add_response_header() was bypassed + via #MHD_RF_INSANITY_HEADER_CONTENT_LENGTH */ MHD_get_response_header (response, MHD_HTTP_HEADER_CONTENT_LENGTH)) && (may_add_content_length) && diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c @@ -2182,7 +2182,7 @@ thread_main_handle_connection (void *data) MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN : MHD_REQUEST_TERMINATED_WITH_ERROR); MHD_connection_handle_idle (con); -exit: + exit: if (NULL != con->response) { MHD_destroy_response (con->response); @@ -2742,7 +2742,7 @@ internal_add_connection (struct MHD_Daemon *daemon, #endif } return MHD_YES; -cleanup: + cleanup: if (NULL != daemon->notify_connection) daemon->notify_connection (daemon->notify_connection_cls, connection, @@ -5062,6 +5062,11 @@ parse_options_va (struct MHD_Daemon *daemon, daemon->uri_log_callback_cls = va_arg (ap, void *); break; + case MHD_OPTION_SERVER_INSANITY: + daemon->insanity_level = (enum MHD_DisableSanityCheck) + va_arg (ap, + unsigned int); + break; #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) case MHD_OPTION_THREAD_POOL_SIZE: daemon->worker_pool_size = va_arg (ap, @@ -5404,6 +5409,7 @@ parse_options_va (struct MHD_Daemon *daemon, case MHD_OPTION_TCP_FASTOPEN_QUEUE_SIZE: case MHD_OPTION_LISTENING_ADDRESS_REUSE: case MHD_OPTION_LISTEN_BACKLOG_SIZE: + case MHD_OPTION_SERVER_INSANITY: if (MHD_YES != parse_options (daemon, servaddr, opt, @@ -6547,7 +6553,7 @@ MHD_start_daemon_va (unsigned int flags, return daemon; #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS) -thread_failed: + thread_failed: /* If no worker threads created, then shut down normally. Calling MHD_stop_daemon (as we do below) doesn't work here since it assumes a 0-sized thread pool means we had been in the default @@ -6571,7 +6577,7 @@ thread_failed: return NULL; #endif -free_and_fail: + free_and_fail: /* clean up basic memory state in 'daemon' and return NULL to indicate failure */ #ifdef EPOLL_SUPPORT diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h @@ -1461,6 +1461,12 @@ struct MHD_Daemon size_t thread_stack_size; /** + * Our #MHD_OPTION_SERVER_INSANITY level, bits indicating + * which sanity checks are off. + */ + enum MHD_DisableSanityCheck insanity_level; + + /** * Number of worker daemons */ unsigned int worker_pool_size; diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c @@ -147,8 +147,10 @@ MHD_add_response_header (struct MHD_Response *response, /* NOTE: for compressed bodies, use the "Content-encoding" header */ return MHD_NO; } - if (MHD_str_equal_caseless_ (header, - MHD_HTTP_HEADER_CONTENT_LENGTH)) + if ( (0 == (MHD_RF_INSANITY_HEADER_CONTENT_LENGTH + & response->flags)) && + (MHD_str_equal_caseless_ (header, + MHD_HTTP_HEADER_CONTENT_LENGTH)) ) { /* MHD will set Content-length if allowed and possible, reject attempt by application */ @@ -838,17 +840,20 @@ MHD_upgrade_action (struct MHD_UpgradeResponseHandle *urh, return MHD_YES; } else -#else +#endif { if (0 == MHD_socket_cork_ (connection->socket_fd, true)) + { connection->sk_cork_on = true; - } -#endif - case MHD_UPGRADE_ACTION_CORK_OFF: - if (! connection->sk_cork_on) return MHD_YES; + } + return MHD_NO; + } + case MHD_UPGRADE_ACTION_CORK_OFF: + if (! connection->sk_cork_on) + return MHD_YES; #ifdef HTTPS_SUPPORT if (0 != (daemon->options & MHD_USE_TLS) ) { @@ -857,17 +862,20 @@ MHD_upgrade_action (struct MHD_UpgradeResponseHandle *urh, return MHD_YES; } else -#else +#endif { if (0 == MHD_socket_cork_ (connection->socket_fd, false)) + { connection->sk_cork_on = false; - } -#endif - default: - /* we don't understand this one */ + return MHD_YES; + } return MHD_NO; + } + default: + /* we don't understand this one */ + return MHD_NO; } }