libmicrohttpd

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

commit 1b610e5b13b7b96e7b3f372c8da1ec9d840f896a
parent e2c268e3796557560fb2ffe3c2e9c18f74fc9471
Author: Christian Grothoff <christian@grothoff.org>
Date:   Wed, 20 Mar 2019 10:46:21 +0100

allow binary zero in unescaped GET arguments (see mailinglist)

Diffstat:
MChangeLog | 6++++++
Mdoc/examples/logging.c | 17++++++++++++-----
Mdoc/libmicrohttpd.texi | 13++++++++++++-
Msrc/include/microhttpd.h | 56++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/microhttpd/connection.c | 91++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Msrc/microhttpd/digestauth.c | 9+++++++--
Msrc/microhttpd/internal.c | 19++++++++++++-------
Msrc/microhttpd/internal.h | 9++++++++-
Msrc/microhttpd/response.c | 3++-
Msrc/testcurl/test_process_headers.c | 8+++++++-
Msrc/testcurl/test_urlparse.c | 9+++++++--
11 files changed, 195 insertions(+), 45 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,9 @@ +Wed 20 Mar 2019 10:20:24 AM CET + Adding additional "value_length" argument to MHD_KeyValueIterator + callback to support binary zeros in values. This is done in a + backwards-compatible way, but may require adding a cast to existing + code to avoid a compiler warning. -CG + Sun Feb 10 21:00:37 BRT 2019 Added example for how to compress a chunked HTTP response. -SC diff --git a/doc/examples/logging.c b/doc/examples/logging.c @@ -15,11 +15,16 @@ static int -print_out_key (void *cls, enum MHD_ValueKind kind, const char *key, - const char *value) +print_out_key (void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *value, + size_t value_size) { - (void)cls; /* Unused. Silent compiler warning. */ - (void)kind; /* Unused. Silent compiler warning. */ + (void) cls; /* Unused. Silent compiler warning. */ + (void) kind; /* Unused. Silent compiler warning. */ + (void) value_size; + printf ("%s: %s\n", key, value); return MHD_YES; } @@ -38,7 +43,9 @@ answer_to_connection (void *cls, struct MHD_Connection *connection, (void)con_cls; /* Unused. Silent compiler warning. */ printf ("New %s request for %s using version %s\n", method, url, version); - MHD_get_connection_values (connection, MHD_HEADER_KIND, print_out_key, + MHD_get_connection_values (connection, + MHD_HEADER_KIND, + &print_out_key, NULL); return MHD_NO; diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi @@ -1356,7 +1356,7 @@ reason for request termination see @code{MHD_OPTION_NOTIFY_COMPLETED}. @end deftypefn -@deftypefn {Function Pointer} int {*MHD_KeyValueIterator} (void *cls, enum MHD_ValueKind kind, const char *key, const char *value) +@deftypefn {Function Pointer} int {*MHD_KeyValueIterator} (void *cls, enum MHD_ValueKind kind, const char *key, const char *value, size_t value_size) Iterator over key-value pairs. This iterator can be used to iterate over all of the cookies, headers, or @code{POST}-data fields of a request, and also to iterate over the headers that have been added to a @@ -1375,6 +1375,17 @@ key for the value, can be an empty string @item value value corresponding value, can be NULL +@item value_size +number of bytes in @code{value}. This argument was introduced in +@code{MHD_VERSION} 0x00096301 to allow applications to use binary +zeros in values. Applications using this argument must ensure that +they are using a sufficiently recent version of MHD, i.e. by testing +@code{MHD_get_version()} for values above or equal to 0.9.64. +Applications that do not need zeros in values and that want to compile +without warnings against newer versions of MHD should not declare this +argument and cast the function pointer argument to +@code{MHD_KeyValueIterator}. + @end table Return @code{MHD_YES} to continue iterating, @code{MHD_NO} to abort the diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - Copyright (C) 2006-2018 Christian Grothoff (and other contributing authors) + Copyright (C) 2006--2019 Christian Grothoff (and other contributing authors) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -126,7 +126,7 @@ typedef intptr_t ssize_t; * Current version of the library. * 0x01093001 = 1.9.30-1. */ -#define MHD_VERSION 0x00096300 +#define MHD_VERSION 0x00096301 /** * MHD-internal return code for "YES". @@ -1380,10 +1380,12 @@ enum MHD_OPTION * struct MHD_Connection *c, * char *s) * - * where the return value must be "strlen(s)" and "s" should be - * updated. Note that the unescape function must not lengthen "s" - * (the result must be shorter than the input and still be - * 0-terminated). "cls" will be set to the second argument + * where the return value must be the length of the value left in + * "s" (without the 0-terminator) and "s" should be updated. Note + * that the unescape function must not lengthen "s" (the result must + * be shorter than the input and must still be 0-terminated). + * However, it may also include binary zeros before the + * 0-termination. "cls" will be set to the second argument * following #MHD_OPTION_UNESCAPE_CALLBACK. */ MHD_OPTION_UNESCAPE_CALLBACK = 16, @@ -2025,6 +2027,8 @@ typedef void * @param kind kind of the header we are looking at * @param key key for the value, can be an empty string * @param value corresponding value, can be NULL + * @param value_size number of bytes in @a value, NEW since #MHD_VERSION 0x00096301; + * for C-strings, the length excludes the 0-terminator * @return #MHD_YES to continue iterating, * #MHD_NO to abort the iteration * @ingroup request @@ -2033,7 +2037,8 @@ typedef int (*MHD_KeyValueIterator) (void *cls, enum MHD_ValueKind kind, const char *key, - const char *value); + const char *value, + size_t value_size); /** @@ -2494,6 +2499,40 @@ MHD_set_connection_value (struct MHD_Connection *connection, /** + * This function can be used to add an entry to the HTTP headers of a + * connection (so that the #MHD_get_connection_values function will + * return them -- and the `struct MHD_PostProcessor` will also see + * them). This maybe required in certain situations (see Mantis + * #1399) where (broken) HTTP implementations fail to supply values + * needed by the post processor (or other parts of the application). + * + * This function MUST only be called from within the + * #MHD_AccessHandlerCallback (otherwise, access maybe improperly + * synchronized). Furthermore, the client must guarantee that the key + * and value arguments are 0-terminated strings that are NOT freed + * until the connection is closed. (The easiest way to do this is by + * passing only arguments to permanently allocated strings.). + * + * @param connection the connection for which a + * value should be set + * @param kind kind of the value + * @param key key for the value + * @param value the value itself + * @param value_size number of bytes in @a value (excluding 0-terminator for C-strings) + * @return #MHD_NO if the operation could not be + * performed due to insufficient memory; + * #MHD_YES on success + * @ingroup request + */ +int +MHD_set_connection_value2 (struct MHD_Connection *connection, + enum MHD_ValueKind kind, + const char *key, + const char *value, + size_t value_size); + + +/** * Sets the global error handler to a different implementation. @a cb * will only be called in the case of typically fatal, serious * internal consistency issues. These issues should only arise in the @@ -3075,7 +3114,8 @@ MHD_del_response_header (struct MHD_Response *response, */ _MHD_EXTERN int MHD_get_response_headers (struct MHD_Response *response, - MHD_KeyValueIterator iterator, void *iterator_cls); + MHD_KeyValueIterator iterator, + void *iterator_cls); /** diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - Copyright (C) 2007-2017 Daniel Pittman and Christian Grothoff + Copyright (C) 2007-2019 Daniel Pittman and Christian Grothoff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -706,7 +706,8 @@ MHD_get_connection_values (struct MHD_Connection *connection, (MHD_YES != iterator (iterator_cls, pos->kind, pos->header, - pos->value)) ) + pos->value, + pos->value_size)) ) return ret; } return ret; @@ -733,16 +734,18 @@ MHD_get_connection_values (struct MHD_Connection *connection, * @param kind kind of the value * @param key key for the value * @param value the value itself + * @param value_size number of bytes in @a value * @return #MHD_NO if the operation could not be * performed due to insufficient memory; * #MHD_YES on success * @ingroup request */ int -MHD_set_connection_value (struct MHD_Connection *connection, - enum MHD_ValueKind kind, - const char *key, - const char *value) +MHD_set_connection_value2 (struct MHD_Connection *connection, + enum MHD_ValueKind kind, + const char *key, + const char *value, + size_t value_size) { struct MHD_HTTP_Header *pos; @@ -753,6 +756,7 @@ MHD_set_connection_value (struct MHD_Connection *connection, return MHD_NO; pos->header = (char *) key; pos->value = (char *) value; + pos->value_size = value_size; pos->kind = kind; pos->next = NULL; /* append 'pos' to the linked list of headers */ @@ -771,6 +775,47 @@ MHD_set_connection_value (struct MHD_Connection *connection, /** + * This function can be used to add an entry to the HTTP headers of a + * connection (so that the #MHD_get_connection_values function will + * return them -- and the `struct MHD_PostProcessor` will also see + * them). This maybe required in certain situations (see Mantis + * #1399) where (broken) HTTP implementations fail to supply values + * needed by the post processor (or other parts of the application). + * + * This function MUST only be called from within the + * #MHD_AccessHandlerCallback (otherwise, access maybe improperly + * synchronized). Furthermore, the client must guarantee that the key + * and value arguments are 0-terminated strings that are NOT freed + * until the connection is closed. (The easiest way to do this is by + * passing only arguments to permanently allocated strings.). + * + * @param connection the connection for which a + * value should be set + * @param kind kind of the value + * @param key key for the value + * @param value the value itself + * @return #MHD_NO if the operation could not be + * performed due to insufficient memory; + * #MHD_YES on success + * @ingroup request + */ +int +MHD_set_connection_value (struct MHD_Connection *connection, + enum MHD_ValueKind kind, + const char *key, + const char *value) +{ + return MHD_set_connection_value2 (connection, + kind, + key, + value, + NULL != value + ? strlen (value) + : 0); +} + + +/** * Get a particular header value. If multiple * values match the kind, return any one of them. * @@ -2061,19 +2106,22 @@ get_next_header_line (struct MHD_Connection *connection, * @param kind kind of the value * @param key key for the value * @param value the value itself + * @param value_size number of bytes in @a value * @return #MHD_NO on failure (out of memory), #MHD_YES for success */ static int connection_add_header (struct MHD_Connection *connection, const char *key, const char *value, + size_t value_size, enum MHD_ValueKind kind) { if (MHD_NO == - MHD_set_connection_value (connection, - kind, - key, - value)) + MHD_set_connection_value2 (connection, + kind, + key, + value, + value_size)) { #ifdef HAVE_MESSAGES MHD_DLOG (connection->daemon, @@ -2104,6 +2152,7 @@ parse_cookie_header (struct MHD_Connection *connection) char *semicolon; char *equals; char *ekill; + char *end; char old; int quotes; @@ -2155,6 +2204,7 @@ parse_cookie_header (struct MHD_Connection *connection) connection_add_header (connection, pos, "", + 0, MHD_COOKIE_KIND)) return MHD_NO; if (old == '\0') @@ -2174,6 +2224,7 @@ parse_cookie_header (struct MHD_Connection *connection) quotes = (quotes + 1) & 1; semicolon++; } + end = semicolon; if ('\0' == semicolon[0]) semicolon = NULL; if (NULL != semicolon) @@ -2183,15 +2234,17 @@ parse_cookie_header (struct MHD_Connection *connection) } /* remove quotes */ if ( ('"' == equals[0]) && - ('"' == equals[strlen (equals) - 1]) ) + ('"' == end[-1]) ) { - equals[strlen (equals) - 1] = '\0'; equals++; + end--; + *end = '\0'; } if (MHD_NO == connection_add_header (connection, pos, equals, + end - equals, MHD_COOKIE_KIND)) return MHD_NO; pos = semicolon; @@ -2711,16 +2764,20 @@ process_broken_line (struct MHD_Connection *connection, REQUEST_TOO_BIG); return MHD_NO; } - memcpy (&last[last_len], tmp, tmp_len + 1); + memcpy (&last[last_len], + tmp, + tmp_len + 1); connection->last = last; return MHD_YES; /* possibly more than 2 lines... */ } mhd_assert ( (NULL != last) && (NULL != connection->colon) ); - if ((MHD_NO == connection_add_header (connection, - last, - connection->colon, - kind))) + if (MHD_NO == + connection_add_header (connection, + last, + connection->colon, + strlen (connection->colon), + kind)) { transmit_error_response (connection, MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE, diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c @@ -732,6 +732,7 @@ calculate_nonce (uint32_t nonce_time, * @param connection the connection * @param key the key * @param value the value, can be NULL + * @param value_size number of bytes in @a value * @param kind type of the header * @return #MHD_YES if the key-value pair is in the headers, * #MHD_NO if not @@ -740,6 +741,7 @@ static int test_header (struct MHD_Connection *connection, const char *key, const char *value, + size_t value_size, enum MHD_ValueKind kind) { struct MHD_HTTP_Header *pos; @@ -748,6 +750,8 @@ test_header (struct MHD_Connection *connection, { if (kind != pos->kind) continue; + if (value_size != pos->value_size) + continue; if (0 != strcmp (key, pos->header)) continue; @@ -756,8 +760,9 @@ test_header (struct MHD_Connection *connection, return MHD_YES; if ( (NULL == value) || (NULL == pos->value) || - (0 != strcmp (value, - pos->value)) ) + (0 != memcmp (value, + pos->value, + value_size)) ) continue; return MHD_YES; } diff --git a/src/microhttpd/internal.c b/src/microhttpd/internal.c @@ -162,7 +162,7 @@ MHD_http_unescape (char *val) } } *wpos = '\0'; /* add 0-terminator */ - return wpos - val; /* = strlen(val) */ + return wpos - val; } @@ -190,6 +190,7 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, struct MHD_Daemon *daemon = connection->daemon; char *equals; char *amper; + size_t len; *num_headers = 0; while ( (NULL != args) && @@ -210,6 +211,7 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, if (MHD_YES != cb (connection, args, NULL, + 0, kind)) return MHD_NO; (*num_headers)++; @@ -223,12 +225,13 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, connection, args); MHD_unescape_plus (equals); - daemon->unescape_callback (daemon->unescape_callback_cls, - connection, - equals); + len = daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + equals); if (MHD_YES != cb (connection, args, equals, + len, kind)) return MHD_NO; (*num_headers)++; @@ -248,6 +251,7 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, if (MHD_YES != cb (connection, args, NULL, + 0, kind)) return MHD_NO; /* continue with 'bar' */ @@ -264,12 +268,13 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, connection, args); MHD_unescape_plus (equals); - daemon->unescape_callback (daemon->unescape_callback_cls, - connection, - equals); + len = daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + equals); if (MHD_YES != cb (connection, args, equals, + len, kind)) return MHD_NO; (*num_headers)++; diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h @@ -276,6 +276,11 @@ struct MHD_HTTP_Header char *value; /** + * Number of bytes in @a value. + */ + size_t value_size; + + /** * Type of the header (where in the HTTP protocol is this header * from). */ @@ -1881,7 +1886,8 @@ MHD_unescape_plus (char *arg); * * @param connection context of the iteration * @param key 0-terminated key string, never NULL - * @param value 0-terminated value string, may be NULL + * @param value 0-terminated binary data, may include binary zeros, may be NULL + * @param value_size number of bytes in value * @param kind origin of the key-value pair * @return #MHD_YES on success (continue to iterate) * #MHD_NO to signal failure (and abort iteration) @@ -1890,6 +1896,7 @@ typedef int (*MHD_ArgumentIterator_)(struct MHD_Connection *connection, const char *key, const char *value, + size_t value_size, enum MHD_ValueKind kind); diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c @@ -240,7 +240,8 @@ MHD_get_response_headers (struct MHD_Response *response, (MHD_YES != iterator (iterator_cls, pos->kind, pos->header, - pos->value))) + pos->value, + pos->value_size))) break; } return numHeaders; diff --git a/src/testcurl/test_process_headers.c b/src/testcurl/test_process_headers.c @@ -65,8 +65,13 @@ copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) return size * nmemb; } + static int -kv_cb (void *cls, enum MHD_ValueKind kind, const char *key, const char *value) +kv_cb (void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *value, + size_t value_size) { if ((0 == strcmp (key, MHD_HTTP_HEADER_HOST)) && (0 == strncmp (value, "127.0.0.1", strlen("127.0.0.1"))) && (kind == MHD_HEADER_KIND)) @@ -77,6 +82,7 @@ kv_cb (void *cls, enum MHD_ValueKind kind, const char *key, const char *value) return MHD_YES; } + static int ahc_echo (void *cls, struct MHD_Connection *connection, diff --git a/src/testcurl/test_urlparse.c b/src/testcurl/test_urlparse.c @@ -67,13 +67,18 @@ copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) return size * nmemb; } + static int test_values (void *cls, enum MHD_ValueKind kind, const char *key, - const char *value) + const char *value, + size_t value_size) { - (void)cls;(void)kind; /* Unused. Silent compiler warning. */ + (void) cls; + (void) kind; + (void) value_size; + if ( (0 == strcmp (key, "a")) && (0 == strcmp (value, "b")) ) matches += 1;