aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd/connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microhttpd/connection.c')
-rw-r--r--src/microhttpd/connection.c151
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