diff options
Diffstat (limited to 'src/microhttpd/connection.c')
-rw-r--r-- | src/microhttpd/connection.c | 151 |
1 files changed, 101 insertions, 50 deletions
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c index 5928c5ea..816bd5a8 100644 --- a/src/microhttpd/connection.c +++ b/src/microhttpd/connection.c | |||
@@ -115,6 +115,16 @@ | |||
115 | #endif | 115 | #endif |
116 | 116 | ||
117 | /** | 117 | /** |
118 | * Response text used when the request HTTP chunk is too large. | ||
119 | */ | ||
120 | #ifdef HAVE_MESSAGES | ||
121 | #define REQUEST_CHUNK_TOO_LARGE \ | ||
122 | "<html><head><title>Request content too large</title></head><body>The chunk size used in your HTTP chunked encoded request is too large.</body></html>" | ||
123 | #else | ||
124 | #define REQUEST_CHUNK_TOO_LARGE "" | ||
125 | #endif | ||
126 | |||
127 | /** | ||
118 | * Response text used when the request HTTP chunked encoding is | 128 | * Response text used when the request HTTP chunked encoding is |
119 | * malformed. | 129 | * malformed. |
120 | */ | 130 | */ |
@@ -3164,73 +3174,114 @@ process_request_body (struct MHD_Connection *connection) | |||
3164 | else | 3174 | else |
3165 | { | 3175 | { |
3166 | size_t i; | 3176 | size_t i; |
3167 | size_t end_size; | 3177 | /** The length of the string with the number of the chunk size */ |
3178 | size_t chunk_size_len; | ||
3179 | bool found_chunk_size_str; | ||
3168 | bool malformed; | 3180 | bool malformed; |
3169 | 3181 | ||
3170 | /* we need to read chunk boundaries */ | 3182 | /* we need to read chunk boundaries */ |
3171 | i = 0; | 3183 | i = 0; |
3172 | while (i < available) | 3184 | found_chunk_size_str = false; |
3173 | { | 3185 | chunk_size_len = 0; |
3174 | if ( ('\r' == buffer_head[i]) || | 3186 | mhd_assert (0 != available); |
3175 | ('\n' == buffer_head[i]) || | 3187 | do |
3176 | (';' == buffer_head[i]) ) | ||
3177 | break; | ||
3178 | i++; | ||
3179 | } | ||
3180 | if (i >= available) | ||
3181 | break; | ||
3182 | end_size = i; | ||
3183 | /* find beginning of CRLF (skip over chunk extensions) */ | ||
3184 | if (';' == buffer_head[i]) | ||
3185 | { | 3188 | { |
3186 | while (i < available) | 3189 | if ('\n' == buffer_head[i]) |
3187 | { | 3190 | { |
3188 | if ( ('\r' == buffer_head[i]) || | 3191 | if ((0 < i) && ('\r' == buffer_head[i - 1])) |
3189 | ('\n' == buffer_head[i]) ) | 3192 | { /* CRLF */ |
3190 | break; | 3193 | if (! found_chunk_size_str) |
3191 | i++; | 3194 | chunk_size_len = i - 1; |
3195 | } | ||
3196 | else | ||
3197 | { /* bare LF */ | ||
3198 | /* TODO: Add an option to disallow bare LF */ | ||
3199 | if (! found_chunk_size_str) | ||
3200 | chunk_size_len = i; | ||
3201 | } | ||
3202 | found_chunk_size_str = true; | ||
3203 | break; /* Found the end of the string */ | ||
3192 | } | 3204 | } |
3193 | } | 3205 | else if (! found_chunk_size_str && (';' == buffer_head[i])) |
3194 | /* take '\n' into account; if '\n' is the unavailable | 3206 | { /* Found chunk extension */ |
3195 | character, we will need to wait until we have it | 3207 | chunk_size_len = i; |
3196 | before going further */ | 3208 | found_chunk_size_str = true; |
3197 | if (i + 1 >= available) | 3209 | } |
3198 | break; /* need more data... */ | 3210 | } while (available > ++i); |
3199 | i++; | 3211 | mhd_assert ((i == available) || found_chunk_size_str); |
3212 | mhd_assert ((0 == chunk_size_len) || found_chunk_size_str); | ||
3213 | malformed = ((0 == chunk_size_len) && found_chunk_size_str); | ||
3200 | if (! malformed) | 3214 | if (! malformed) |
3201 | { | 3215 | { |
3202 | size_t num_dig = MHD_strx_to_uint64_n_ (buffer_head, | 3216 | /* Check whether size is valid hexadecimal number |
3203 | end_size, | 3217 | * even if end of the string is not found yet. */ |
3204 | &connection-> | 3218 | size_t num_dig; |
3205 | current_chunk_size); | 3219 | uint64_t chunk_size; |
3206 | malformed = (end_size != num_dig); | 3220 | mhd_assert (0 < i); |
3221 | if (! found_chunk_size_str) | ||
3222 | { | ||
3223 | mhd_assert (i == available); | ||
3224 | /* Check already available part of the size string for valid | ||
3225 | * hexadecimal digits. */ | ||
3226 | chunk_size_len = i; | ||
3227 | if ('\r' == buffer_head[i - 1]) | ||
3228 | { | ||
3229 | chunk_size_len--; | ||
3230 | malformed = (0 == chunk_size_len); | ||
3231 | } | ||
3232 | } | ||
3233 | num_dig = MHD_strx_to_uint64_n_ (buffer_head, | ||
3234 | chunk_size_len, | ||
3235 | &chunk_size); | ||
3236 | malformed = malformed || (chunk_size_len != num_dig); | ||
3237 | |||
3238 | if ((available != i) && ! malformed) | ||
3239 | { | ||
3240 | /* Found end of the string and the size of the chunk is valid */ | ||
3241 | |||
3242 | mhd_assert (found_chunk_size_str); | ||
3243 | /* Start reading payload data of the chunk */ | ||
3244 | connection->current_chunk_offset = 0; | ||
3245 | connection->current_chunk_size = chunk_size; | ||
3246 | i++; /* Consume the last checked char */ | ||
3247 | available -= i; | ||
3248 | buffer_head += i; | ||
3249 | |||
3250 | if (0 == connection->current_chunk_size) | ||
3251 | { /* The final (termination) chunk */ | ||
3252 | connection->remaining_upload_size = 0; | ||
3253 | break; | ||
3254 | } | ||
3255 | if (available > 0) | ||
3256 | instant_retry = true; | ||
3257 | continue; | ||
3258 | } | ||
3259 | |||
3260 | if ((0 == num_dig) && (0 != chunk_size_len)) | ||
3261 | { /* Check whether result is invalid due to uint64_t overflow */ | ||
3262 | /* At least one byte is always available | ||
3263 | * in the input buffer here. */ | ||
3264 | const char d = buffer_head[0]; /**< first digit */ | ||
3265 | if ((('0' <= d) && ('9' >= d)) || | ||
3266 | (('A' <= d) && ('F' >= d)) || | ||
3267 | (('a' <= d) && ('f' >= d))) | ||
3268 | { /* The first char is a valid hexadecimal digit */ | ||
3269 | transmit_error_response_static (connection, | ||
3270 | MHD_HTTP_CONTENT_TOO_LARGE, | ||
3271 | REQUEST_CHUNK_TOO_LARGE); | ||
3272 | return; | ||
3273 | } | ||
3274 | } | ||
3207 | } | 3275 | } |
3208 | if (malformed) | 3276 | if (malformed) |
3209 | { | 3277 | { |
3210 | /* malformed encoding */ | ||
3211 | transmit_error_response_static (connection, | 3278 | transmit_error_response_static (connection, |
3212 | MHD_HTTP_BAD_REQUEST, | 3279 | MHD_HTTP_BAD_REQUEST, |
3213 | REQUEST_CHUNKED_MALFORMED); | 3280 | REQUEST_CHUNKED_MALFORMED); |
3214 | return; | 3281 | return; |
3215 | } | 3282 | } |
3216 | /* skip 2nd part of line feed */ | 3283 | mhd_assert (available == i); |
3217 | if ( (i < available) && | 3284 | break; /* The end of the string not found, need more upload data */ |
3218 | ( ('\r' == buffer_head[i]) || | ||
3219 | ('\n' == buffer_head[i]) ) ) | ||
3220 | i++; | ||
3221 | |||
3222 | buffer_head += i; | ||
3223 | available -= i; | ||
3224 | connection->current_chunk_offset = 0; | ||
3225 | |||
3226 | if (available > 0) | ||
3227 | instant_retry = true; | ||
3228 | if (0LLU == connection->current_chunk_size) | ||
3229 | { | ||
3230 | connection->remaining_upload_size = 0; | ||
3231 | break; | ||
3232 | } | ||
3233 | continue; | ||
3234 | } | 3285 | } |
3235 | } | 3286 | } |
3236 | else | 3287 | else |