libmicrohttpd

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

commit f34781c87e48df6081eaf5defd1430f44013e8b6
parent 55f715e15e3ce66babc939b5a670bee02d4d9571
Author: Christian Grothoff <christian@grothoff.org>
Date:   Thu, 26 Dec 2019 14:44:18 +0100

add post processor logic fix

Diffstat:
MChangeLog | 4++++
Msrc/include/microhttpd.h | 2+-
Msrc/microhttpd/postprocessor.c | 63+++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/microhttpd/test_postprocessor.c | 56++++++++++++++++++++++++++++++++++++++++----------------
4 files changed, 90 insertions(+), 35 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,7 @@ +Thu Dec 26 14:43:27 CET 2019 + Adding fix for urlencoding of keys without values in + post-processor logic. -CG + Tue 24 Dec 2019 03:32:18 PM CET Adding patch from Ethan Tuttle with test case for urlencoding in post-processor for keys without values. -CG/ET 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 0x00096900 +#define MHD_VERSION 0x00096901 /** * MHD-internal return code for "YES". diff --git a/src/microhttpd/postprocessor.c b/src/microhttpd/postprocessor.c @@ -215,9 +215,15 @@ struct MHD_PostProcessor * Used to ensure that we do always call 'ikvi' even if the * payload is empty (but not more than once). */ - int must_ikvi; + bool must_ikvi; /** + * Set if we still need to run the unescape logic + * on the key allocated at the end of this struct. + */ + bool must_unescape_key; + + /** * State of the parser. */ enum PP_State state; @@ -395,6 +401,7 @@ process_value (struct MHD_PostProcessor *pp, } } while ( (value_start != value_end) || + (pp->must_ikvi) || (xoff > 0) ) { size_t delta = value_end - value_start; @@ -425,6 +432,7 @@ process_value (struct MHD_PostProcessor *pp, MHD_unescape_plus (xbuf); xoff = MHD_http_unescape (xbuf); /* finally: call application! */ + pp->must_ikvi = false; if (MHD_NO == pp->ikvi (pp->cls, MHD_POSTDATA_KIND, (const char *) &pp[1], /* key */ @@ -466,7 +474,8 @@ post_process_urlencoded (struct MHD_PostProcessor *pp, const char *last_escape = NULL; poff = 0; - while ( (poff < post_data_len) && + while ( ( (poff < post_data_len) || + (pp->state == PP_Callback) ) && (pp->state != PP_Error) ) { switch (pp->state) { @@ -478,6 +487,7 @@ post_process_urlencoded (struct MHD_PostProcessor *pp, /* key phase */ if (NULL == start_key) start_key = &post_data[poff]; + pp->must_ikvi = true; switch (post_data[poff]) { case '=': @@ -519,13 +529,27 @@ post_process_urlencoded (struct MHD_PostProcessor *pp, /* case 'value&' */ end_value = &post_data[poff]; poff++; - pp->state = PP_Callback; + if ( pp->must_ikvi || + (start_value != end_value) ) + { + pp->state = PP_Callback; + } + else + { + pp->buffer_pos = 0; + pp->value_offset = 0; + pp->state = PP_Init; + } + continue; case '\n': case '\r': /* Case: 'value\n' or 'value\r' */ end_value = &post_data[poff]; poff++; - pp->state = PP_Done; + if (pp->must_ikvi) + pp->state = PP_Callback; + else + pp->state = PP_Done; break; case '%': last_escape = &post_data[poff]; @@ -564,7 +588,7 @@ post_process_urlencoded (struct MHD_PostProcessor *pp, case PP_Callback: if ( (pp->buffer_pos + (end_key - start_key) > pp->buffer_size) || - (pp->buffer_pos + (end_key - start_key) > + (pp->buffer_pos + (end_key - start_key) < pp->buffer_pos) ) { /* key too long, cannot parse! */ @@ -580,9 +604,14 @@ post_process_urlencoded (struct MHD_PostProcessor *pp, pp->buffer_pos += end_key - start_key; start_key = NULL; end_key = NULL; + pp->must_unescape_key = true; + } + if (pp->must_unescape_key) + { kbuf[pp->buffer_pos] = '\0'; /* 0-terminate key */ MHD_unescape_plus (kbuf); MHD_http_unescape (kbuf); + pp->must_unescape_key = false; } process_value (pp, start_value, @@ -591,6 +620,7 @@ post_process_urlencoded (struct MHD_PostProcessor *pp, pp->value_offset = 0; start_value = NULL; end_value = NULL; + pp->buffer_pos = 0; pp->state = PP_Init; break; default: @@ -602,8 +632,7 @@ post_process_urlencoded (struct MHD_PostProcessor *pp, } /* save remaining data for next iteration */ - if ( (NULL != start_key) && - (PP_Init == pp->state) ) + if (NULL != start_key) { if (NULL == end_key) end_key = &post_data[poff]; @@ -611,22 +640,20 @@ post_process_urlencoded (struct MHD_PostProcessor *pp, start_key, end_key - start_key); pp->buffer_pos += end_key - start_key; + pp->must_unescape_key = true; + start_key = NULL; + end_key = NULL; } if ( (NULL != start_value) && (PP_ProcessValue == pp->state) ) { /* compute key, if we have not already */ - if (NULL != start_key) + if (pp->must_unescape_key) { - memcpy (&kbuf[pp->buffer_pos], - start_key, - end_key - start_key); - pp->buffer_pos += end_key - start_key; - start_key = NULL; - end_key = NULL; kbuf[pp->buffer_pos] = '\0'; /* 0-terminate key */ MHD_unescape_plus (kbuf); MHD_http_unescape (kbuf); + pp->must_unescape_key = false; } if (NULL == end_value) end_value = &post_data[poff]; @@ -634,8 +661,8 @@ post_process_urlencoded (struct MHD_PostProcessor *pp, start_value, end_value, last_escape); + pp->must_ikvi = false; } - return MHD_YES; } @@ -953,7 +980,7 @@ process_value_to_boundary (struct MHD_PostProcessor *pp, /* newline is either at beginning of boundary or at least at the last character that we are sure is not part of the boundary */ - if ( ( (MHD_YES == pp->must_ikvi) || + if ( ( (pp->must_ikvi) || (0 != newline) ) && (MHD_NO == pp->ikvi (pp->cls, MHD_POSTDATA_KIND, @@ -968,7 +995,7 @@ process_value_to_boundary (struct MHD_PostProcessor *pp, pp->state = PP_Error; return MHD_NO; } - pp->must_ikvi = MHD_NO; + pp->must_ikvi = false; pp->value_offset += newline; (*ioffptr) += newline; return MHD_YES; @@ -1155,7 +1182,7 @@ post_process_multipart (struct MHD_PostProcessor *pp, } break; case PP_ProcessEntryHeaders: - pp->must_ikvi = MHD_YES; + pp->must_ikvi = true; if (MHD_NO == process_multipart_headers (pp, &ioff, diff --git a/src/microhttpd/test_postprocessor.c b/src/microhttpd/test_postprocessor.c @@ -80,6 +80,7 @@ const char *want[] = { NULL, NULL, NULL, NULL, NULL }; + static int mismatch (const char *a, const char *b) { @@ -98,7 +99,9 @@ value_checker (void *cls, const char *filename, const char *content_type, const char *transfer_encoding, - const char *data, uint64_t off, size_t size) + const char *data, + uint64_t off, + size_t size) { int *want_off = cls; int idx = *want_off; @@ -106,20 +109,27 @@ value_checker (void *cls, #if 0 fprintf (stderr, - "VC: `%s' `%s' `%s' `%s' `%.*s'\n", + "VC: `%s' `%s' `%s' `%s' `%.*s' (%d)\n", key, filename, content_type, transfer_encoding, (int) size, - data); + data, + (int) size); #endif if ( (0 != off) && (0 == size) ) + { + if (NULL == want[idx + 4]) + *want_off = idx + 5; return MHD_YES; + } if ((idx < 0) || (want[idx] == NULL) || (0 != strcmp (key, want[idx])) || (mismatch (filename, want[idx + 1])) || (mismatch (content_type, want[idx + 2])) || (mismatch (transfer_encoding, want[idx + 3])) || - (0 != memcmp (data, &want[idx + 4][off], size))) + (0 != memcmp (data, + &want[idx + 4][off], + size))) { *want_off = -1; fprintf (stderr, @@ -128,18 +138,26 @@ value_checker (void *cls, (int) size, data); fprintf (stderr, + "Wanted: `%s' `%s' `%s' `%s' `%s'\n", + want[idx], + want[idx+1], + want[idx+2], + want[idx+3], + want[idx+4]); + fprintf (stderr, "Unexpected result: %d/%d/%d/%d/%d/%d/%d\n", (idx < 0), (want[idx] == NULL), - (0 != strcmp (key, want[idx])), + (NULL != want[idx]) && (0 != strcmp (key, want[idx])), (mismatch (filename, want[idx + 1])), (mismatch (content_type, want[idx + 2])), (mismatch (transfer_encoding, want[idx + 3])), (0 != memcmp (data, &want[idx + 4][off], size))); - return MHD_NO; } - if (off + size == strlen (want[idx + 4])) + if ( ( (NULL == want[idx+4]) && + (0 == off + size) ) || + (off + size == strlen (want[idx + 4])) ) *want_off = idx + 5; return MHD_YES; } @@ -185,8 +203,10 @@ test_urlencoding_case (unsigned int want_start, if (want_off != want_end) { fprintf (stderr, - "Test failed in line %u\n", - (unsigned int) __LINE__); + "Test failed in line %u: %u != %u\n", + (unsigned int) __LINE__, + want_off, + want_end); return 1; } return 0; @@ -198,17 +218,20 @@ test_urlencoding (void) { unsigned int errorCount = 0; - errorCount += test_urlencoding_case (URL_START, URL_END, URL_DATA); + errorCount += test_urlencoding_case (URL_START, + URL_END, + URL_DATA); errorCount += test_urlencoding_case (URL_NOVALUE1_START, URL_NOVALUE1_END, URL_NOVALUE1_DATA); errorCount += test_urlencoding_case (URL_NOVALUE2_START, URL_NOVALUE2_END, URL_NOVALUE2_DATA); - fprintf (stderr, - "Test failed in line %u with %u errors\n", - (unsigned int) __LINE__, - errorCount); + if (0 != errorCount) + fprintf (stderr, + "Test failed in line %u with %u errors\n", + (unsigned int) __LINE__, + errorCount); return errorCount; } @@ -419,8 +442,9 @@ test_empty_value (void) if (want_off != URL_EMPTY_VALUE_END) { fprintf (stderr, - "Test failed in line %u\n", - (unsigned int) __LINE__); + "Test failed in line %u at offset %d\n", + (unsigned int) __LINE__, + (int) want_off); return 8; } return 0;