stream_process_request.c (147733B)
1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ 2 /* 3 This file is part of GNU libmicrohttpd. 4 Copyright (C) 2014-2024 Evgeny Grin (Karlson2k) 5 Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff 6 7 GNU libmicrohttpd is free software; you can redistribute it and/or 8 modify it under the terms of the GNU Lesser General Public 9 License as published by the Free Software Foundation; either 10 version 2.1 of the License, or (at your option) any later version. 11 12 GNU libmicrohttpd is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 Lesser General Public License for more details. 16 17 Alternatively, you can redistribute GNU libmicrohttpd and/or 18 modify it under the terms of the GNU General Public License as 19 published by the Free Software Foundation; either version 2 of 20 the License, or (at your option) any later version, together 21 with the eCos exception, as follows: 22 23 As a special exception, if other files instantiate templates or 24 use macros or inline functions from this file, or you compile this 25 file and link it with other works to produce a work based on this 26 file, this file does not by itself cause the resulting work to be 27 covered by the GNU General Public License. However the source code 28 for this file must still be made available in accordance with 29 section (3) of the GNU General Public License v2. 30 31 This exception does not invalidate any other reasons why a work 32 based on this file might be covered by the GNU General Public 33 License. 34 35 You should have received copies of the GNU Lesser General Public 36 License and the GNU General Public License along with this library; 37 if not, see <https://www.gnu.org/licenses/>. 38 */ 39 40 /** 41 * @file src/mhd2/stream_process_request.c 42 * @brief The implementation of internal functions for requests parsing 43 * and processing 44 * @author Karlson2k (Evgeny Grin) 45 * 46 * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff and other 47 * contributors. 48 */ 49 50 #include "mhd_sys_options.h" 51 #include "stream_process_request.h" 52 53 #include "sys_bool_type.h" 54 #include "sys_base_types.h" 55 56 #include "mhd_assert.h" 57 #include "mhd_unreachable.h" 58 #include "mhd_assume.h" 59 60 #include "sys_malloc.h" 61 62 #ifdef MHD_USE_TRACE_SUSPEND_RESUME 63 # include <stdio.h> 64 #endif /* MHD_USE_TRACE_SUSPEND_RESUME */ 65 66 #include "mhd_str_types.h" 67 #include "mhd_str_macros.h" 68 #include "mhd_str.h" 69 70 #include <string.h> 71 72 #include "mhd_daemon.h" 73 #include "mhd_connection.h" 74 75 #include "daemon_logger.h" 76 #include "mhd_panic.h" 77 78 #include "mempool_funcs.h" 79 80 #include "response_destroy.h" 81 #include "request_funcs.h" 82 #include "request_get_value.h" 83 #include "respond_with_error.h" 84 #include "stream_funcs.h" 85 #include "daemon_funcs.h" 86 87 #ifdef MHD_SUPPORT_POST_PARSER 88 # include "post_parser_funcs.h" 89 #endif /* MHD_SUPPORT_POST_PARSER */ 90 91 #include "mhd_public_api.h" 92 93 94 /** 95 * Response text used when the request (http header) is 96 * malformed. 97 */ 98 #define ERR_RSP_REQUEST_MALFORMED \ 99 "<html><head><title>Request malformed</title></head>" \ 100 "<body>HTTP request is syntactically incorrect.</body></html>" 101 102 /** 103 * Response text used when the request HTTP version is too old. 104 */ 105 #define ERR_RSP_REQ_HTTP_VER_IS_TOO_OLD \ 106 "<html>" \ 107 "<head><title>Requested HTTP version is not supported</title></head>" \ 108 "<body>Requested HTTP version is too old and not " \ 109 "supported.</body></html>" 110 /** 111 * Response text used when the request HTTP version is not supported. 112 */ 113 #define ERR_RSP_REQ_HTTP_VER_IS_NOT_SUPPORTED \ 114 "<html>" \ 115 "<head><title>Requested HTTP version is not supported</title></head>" \ 116 "<body>Requested HTTP version is not supported.</body></html>" 117 118 /** 119 * Response text used when the request HTTP header has bare CR character 120 * without LF character (and CR is not allowed to be treated as whitespace). 121 */ 122 #define ERR_RSP_BARE_CR_IN_HEADER \ 123 "<html>" \ 124 "<head><title>Request broken</title></head>" \ 125 "<body>Request HTTP header has bare CR character without " \ 126 "following LF character.</body>" \ 127 "</html>" 128 129 /** 130 * Response text used when the request HTTP footer has bare CR character 131 * without LF character (and CR is not allowed to be treated as whitespace). 132 */ 133 #define ERR_RSP_BARE_CR_IN_FOOTER \ 134 "<html>" \ 135 "<head><title>Request broken</title></head>" \ 136 "<body>Request HTTP footer has bare CR character without " \ 137 "following LF character.</body>" \ 138 "</html>" 139 140 /** 141 * Response text used when the request HTTP header has bare LF character 142 * without CR character. 143 */ 144 #define ERR_RSP_BARE_LF_IN_HEADER \ 145 "<html>" \ 146 "<head><title>Request broken</title></head>" \ 147 "<body>Request HTTP header has bare LF character without " \ 148 "preceding CR character.</body>" \ 149 "</html>" 150 /** 151 * Response text used when the request HTTP footer has bare LF character 152 * without CR character. 153 */ 154 #define ERR_RSP_BARE_LF_IN_FOOTER \ 155 "<html>" \ 156 "<head><title>Request broken</title></head>" \ 157 "<body>Request HTTP footer has bare LF character without " \ 158 "preceding CR character.</body>" \ 159 "</html>" 160 161 /** 162 * Response text used when the request line has more then two whitespaces. 163 */ 164 #define ERR_RSP_RQ_LINE_TOO_MANY_WSP \ 165 "<html>" \ 166 "<head><title>Request broken</title></head>" \ 167 "<body>The request line has more then two whitespaces.</body>" \ 168 "</html>" 169 170 /** 171 * Response text used when the request line has invalid characters in URI. 172 */ 173 #define ERR_RSP_RQ_TARGET_INVALID_CHAR \ 174 "<html>" \ 175 "<head><title>Request broken</title></head>" \ 176 "<body>HTTP request has invalid characters in " \ 177 "the request-target.</body>" \ 178 "</html>" 179 180 /** 181 * Response text used when line folding is used in request headers. 182 */ 183 #define ERR_RSP_OBS_FOLD \ 184 "<html>" \ 185 "<head><title>Request broken</title></head>" \ 186 "<body>Obsolete line folding is used in HTTP request header.</body>" \ 187 "</html>" 188 189 /** 190 * Response text used when line folding is used in request footers. 191 */ 192 #define ERR_RSP_OBS_FOLD_FOOTER \ 193 "<html>" \ 194 "<head><title>Request broken</title></head>" \ 195 "<body>Obsolete line folding is used in HTTP request footer.</body>" \ 196 "</html>" 197 198 /** 199 * Response text used when request header has no colon character. 200 */ 201 #define ERR_RSP_HEADER_WITHOUT_COLON \ 202 "<html>" \ 203 "<head><title>Request broken</title></head>" \ 204 "<body>HTTP request header line has no colon character.</body>" \ 205 "</html>" 206 207 /** 208 * Response text used when request footer has no colon character. 209 */ 210 #define ERR_RSP_FOOTER_WITHOUT_COLON \ 211 "<html>" \ 212 "<head><title>Request broken</title></head>" \ 213 "<body>HTTP request footer line has no colon character.</body>" \ 214 "</html>" 215 /** 216 * Response text used when the request has whitespace at the start 217 * of the first header line. 218 */ 219 #define ERR_RSP_WSP_BEFORE_HEADER \ 220 "<html>" \ 221 "<head><title>Request broken</title></head>" \ 222 "<body>HTTP request has whitespace between the request line and " \ 223 "the first header.</body>" \ 224 "</html>" 225 226 /** 227 * Response text used when the request has whitespace at the start 228 * of the first footer line. 229 */ 230 #define ERR_RSP_WSP_BEFORE_FOOTER \ 231 "<html>" \ 232 "<head><title>Request broken</title></head>" \ 233 "<body>First HTTP footer line has whitespace at the first " \ 234 "position.</body>" \ 235 "</html>" 236 237 /** 238 * Response text used when the whitespace found before colon (inside header 239 * name or between header name and colon). 240 */ 241 #define ERR_RSP_WSP_IN_HEADER_NAME \ 242 "<html>" \ 243 "<head><title>Request broken</title></head>" \ 244 "<body>HTTP request has whitespace before the first colon " \ 245 "in header line.</body>" \ 246 "</html>" 247 248 /** 249 * Response text used when the whitespace found before colon (inside header 250 * name or between header name and colon). 251 */ 252 #define ERR_RSP_WSP_IN_FOOTER_NAME \ 253 "<html>" \ 254 "<head><title>Request broken</title></head>" \ 255 "<body>HTTP request has whitespace before the first colon " \ 256 "in footer line.</body>" \ 257 "</html>" 258 /** 259 * Response text used when request header has invalid character. 260 */ 261 #define ERR_RSP_INVALID_CHR_IN_HEADER \ 262 "<html>" \ 263 "<head><title>Request broken</title></head>" \ 264 "<body>HTTP request has invalid character in header.</body>" \ 265 "</html>" 266 267 /** 268 * Response text used when request header has invalid character. 269 */ 270 #define ERR_RSP_INVALID_CHR_IN_FOOTER \ 271 "<html>" \ 272 "<head><title>Request broken</title></head>" \ 273 "<body>HTTP request has invalid character in footer.</body>" \ 274 "</html>" 275 276 /** 277 * Response text used when request header has zero-length header (filed) name. 278 */ 279 #define ERR_RSP_EMPTY_HEADER_NAME \ 280 "<html>" \ 281 "<head><title>Request broken</title></head>" \ 282 "<body>HTTP request header has empty header name.</body>" \ 283 "</html>" 284 285 /** 286 * Response text used when request header has zero-length header (filed) name. 287 */ 288 #define ERR_RSP_EMPTY_FOOTER_NAME \ 289 "<html>" \ 290 "<head><title>Request broken</title></head>" \ 291 "<body>HTTP request footer has empty footer name.</body>" \ 292 "</html>" 293 294 /** 295 * Response text used when the request header is too big to be processed. 296 */ 297 #define ERR_RSP_REQUEST_HEADER_TOO_BIG \ 298 "<html>" \ 299 "<head><title>Request too big</title></head>" \ 300 "<body><p>The total size of the request headers, which includes the " \ 301 "request target and the request field lines, exceeds the memory " \ 302 "constraints of this web server.</p>" \ 303 "<p>The request could be re-tried with shorter field lines, a shorter " \ 304 "request target or a shorter request method token.</p></body>" \ 305 "</html>" 306 307 /** 308 * Response text used when the request header is too big to be processed. 309 */ 310 #define ERR_RSP_REQUEST_FOOTER_TOO_BIG \ 311 "<html>" \ 312 "<head><title>Request too big</title></head>" \ 313 "<body><p>The total size of the request headers, which includes the " \ 314 "request target, the request field lines and the chunked trailer " \ 315 "section exceeds the memory constraints of this web server.</p>" \ 316 "<p>The request could be re-tried with a shorter chunked trailer " \ 317 "section, shorter field lines, a shorter request target or " \ 318 "a shorter request method token.</p></body>" \ 319 "</html>" 320 321 /** 322 * Response text used when the request (http header) is too big to 323 * be processed. 324 */ 325 #define ERR_RSP_MSG_REQUEST_TOO_BIG \ 326 "<html>" \ 327 "<head><title>Request too big</title></head>" \ 328 "<body>Request HTTP header is too big for the memory constraints " \ 329 "of this webserver.</body>" \ 330 "</html>" 331 /** 332 * Response text used when the request chunk size line with chunk extension 333 * cannot fit the buffer. 334 */ 335 #define ERR_RSP_REQUEST_CHUNK_LINE_EXT_TOO_BIG \ 336 "<html>" \ 337 "<head><title>Request too big</title></head>" \ 338 "<body><p>The total size of the request target, the request field lines " \ 339 "and the chunk size line exceeds the memory constraints of this web " \ 340 "server.</p>" \ 341 "<p>The request could be re-tried without chunk extensions, with a smaller " \ 342 "chunk size, shorter field lines, a shorter request target or a shorter " \ 343 "request method token.</p></body>" \ 344 "</html>" 345 346 /** 347 * Response text used when the request chunk size line without chunk extension 348 * cannot fit the buffer. 349 */ 350 #define ERR_RSP_REQUEST_CHUNK_LINE_TOO_BIG \ 351 "<html>" \ 352 "<head><title>Request too big</title></head>" \ 353 "<body><p>The total size of the request target, the request field lines " \ 354 "and the chunk size line exceeds the memory constraints of this web " \ 355 "server.</p>" \ 356 "<p>The request could be re-tried with a smaller " \ 357 "chunk size, shorter field lines, a shorter request target or a shorter " \ 358 "request method token.</p></body>" \ 359 "</html>" 360 361 /** 362 * Response text used when the request (http header) does not 363 * contain a "Host:" header and still claims to be HTTP 1.1. 364 */ 365 #define ERR_RSP_REQUEST_LACKS_HOST \ 366 "<html>" \ 367 "<head><title>"Host:" header required</title></head>" \ 368 "<body>HTTP/1.1 request without <b>"Host:"</b>.</body>" \ 369 "</html>" 370 371 /** 372 * Response text used when the request has more than one "Host:" header. 373 */ 374 #define ERR_RSP_REQUEST_HAS_SEVERAL_HOSTS \ 375 "<html>" \ 376 "<head>" \ 377 "<title>Several "Host:" headers used</title></head>" \ 378 "<body>" \ 379 "Request with more than one <b>"Host:"</b> header.</body>" \ 380 "</html>" 381 382 /** 383 * Response text used when the request has more than one "Host:" header. 384 */ 385 #define ERR_RSP_REQUEST_HAS_MALFORMED_HOST \ 386 "<html>" \ 387 "<head>" \ 388 "<title>Malformed "Host:" header</title></head>" \ 389 "<body>" \ 390 "Malformed <b>"Host:"</b> header in the request.</body>" \ 391 "</html>" 392 393 /** 394 * Response text used when the request has unsupported "Transfer-Encoding:". 395 */ 396 #define ERR_RSP_UNSUPPORTED_TR_ENCODING \ 397 "<html>" \ 398 "<head><title>Unsupported Transfer-Encoding</title></head>" \ 399 "<body>The Transfer-Encoding used in request is not supported.</body>" \ 400 "</html>" 401 402 /** 403 * Response text used when the request has unsupported "Expect:" value. 404 */ 405 #define ERR_RSP_UNSUPPORTED_EXPECT_HDR_VALUE \ 406 "<html>" \ 407 "<head><title>Unsupported 'Expect:'</title></head>" \ 408 "<body>The value of 'Expect:' header used in the request is " \ 409 "not supported.</body>" \ 410 "</html>" 411 412 /** 413 * Response text used when the request has unsupported both headers: 414 * "Transfer-Encoding:" and "Content-Length:" 415 */ 416 #define ERR_RSP_REQUEST_CNTNLENGTH_WITH_TR_ENCODING \ 417 "<html>" \ 418 "<head><title>Malformed request</title></head>" \ 419 "<body>Wrong combination of the request headers: both Transfer-Encoding " \ 420 "and Content-Length headers are used at the same time.</body>" \ 421 "</html>" 422 423 /** 424 * Response text used when the request HTTP content is too large. 425 */ 426 #define ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE \ 427 "<html><head><title>Request content too large</title></head>" \ 428 "<body>HTTP request has too large value for " \ 429 "<b>Content-Length</b> header.</body></html>" 430 431 /** 432 * Response text used when the request HTTP chunked encoding is 433 * malformed. 434 */ 435 #define ERR_RSP_REQUEST_CONTENTLENGTH_MALFORMED \ 436 "<html><head><title>Request malformed</title></head>" \ 437 "<body>HTTP request has wrong value for " \ 438 "<b>Content-Length</b> header.</body></html>" 439 440 /** 441 * Response text used when the request has more than one "Content-Length:" 442 * header. 443 */ 444 #define ERR_RSP_REQUEST_CONTENTLENGTH_SEVERAL \ 445 "<html><head><title>Request malformed</title></head>" \ 446 "<body>HTTP request has several " \ 447 "<b>Content-Length</b> headers.</body></html>" 448 449 /** 450 * Response text used when the request HTTP chunked encoding is 451 * malformed. 452 */ 453 #define ERR_RSP_REQUEST_CHUNKED_MALFORMED \ 454 "<html><head><title>Request malformed</title></head>" \ 455 "<body>HTTP chunked encoding is syntactically incorrect.</body></html>" 456 457 /** 458 * Response text used when the request HTTP chunk is too large. 459 */ 460 #define ERR_RSP_REQUEST_CHUNK_TOO_LARGE \ 461 "<html><head><title>Request content too large</title></head>" \ 462 "<body>The chunk size used in HTTP chunked encoded " \ 463 "request is too large.</body></html>" 464 465 466 /** 467 * The reasonable length of the upload chunk "header" (the size specifier 468 * with optional chunk extension). 469 * MHD tries to keep the space in the read buffer large enough to read 470 * the chunk "header" in one step. 471 * The real "header" could be much larger, it will be handled correctly 472 * anyway, however it may require several rounds of buffer grow. 473 */ 474 #define MHD_CHUNK_HEADER_REASONABLE_LEN 24 475 476 /** 477 * The valid length of any HTTP version string 478 */ 479 #define HTTP_VER_LEN (mhd_SSTR_LEN (MHD_HTTP_VERSION_1_1_STR)) 480 481 482 /** 483 * Parse HTTP method string. 484 * @param len the length of the @a mtd string 485 * @param mtd the method string, does not need to be zero-terminated 486 * @return enum mhd_HTTP_Method value 487 */ 488 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ 489 MHD_FN_PAR_IN_SIZE_ (2, 1) 490 MHD_FN_PURE_ enum mhd_HTTP_Method 491 mhd_parse_http_method (size_t len, 492 const char mtd[MHD_FN_PAR_DYN_ARR_SIZE_ (len)]) 493 { 494 switch (len) 495 { 496 case 3: /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_GET) */ 497 /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_PUT) */ 498 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_GET)); 499 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_PUT)); 500 if (0 == memcmp (mtd, 501 MHD_HTTP_METHOD_STR_GET, 502 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_GET))) 503 return mhd_HTTP_METHOD_GET; 504 else if (0 == memcmp (mtd, 505 MHD_HTTP_METHOD_STR_PUT, 506 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_PUT))) 507 return mhd_HTTP_METHOD_PUT; 508 break; 509 case 4: /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_HEAD) */ 510 /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_POST) */ 511 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_HEAD)); 512 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_POST)); 513 if (0 == memcmp (mtd, 514 MHD_HTTP_METHOD_STR_HEAD, 515 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_HEAD))) 516 return mhd_HTTP_METHOD_HEAD; 517 else if (0 == memcmp (mtd, 518 MHD_HTTP_METHOD_STR_POST, 519 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_POST))) 520 return mhd_HTTP_METHOD_POST; 521 break; 522 case 6: /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_DELETE) */ 523 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_DELETE)); 524 if (0 == memcmp (mtd, 525 MHD_HTTP_METHOD_STR_DELETE, 526 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_DELETE))) 527 return mhd_HTTP_METHOD_DELETE; 528 break; 529 case 7: /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_CONNECT) */ 530 /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_OPTIONS) */ 531 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_CONNECT)); 532 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_OPTIONS)); 533 if (0 == memcmp (mtd, 534 MHD_HTTP_METHOD_STR_CONNECT, 535 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_CONNECT))) 536 return mhd_HTTP_METHOD_CONNECT; 537 else if (0 == memcmp (mtd, 538 MHD_HTTP_METHOD_STR_OPTIONS, 539 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_OPTIONS))) 540 return mhd_HTTP_METHOD_OPTIONS; 541 break; 542 case 5: /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_TRACE) */ 543 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_TRACE)); 544 if (0 == memcmp (mtd, 545 MHD_HTTP_METHOD_STR_TRACE, 546 mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_TRACE))) 547 return mhd_HTTP_METHOD_TRACE; 548 break; 549 case 1: /* mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_ASTERISK) */ 550 mhd_assert (len == mhd_SSTR_LEN (MHD_HTTP_METHOD_STR_ASTERISK)); 551 if ('*' == mtd[0]) 552 return mhd_HTTP_METHOD_ASTERISK; 553 break; 554 default: 555 break; /* Handled after the "switch()" body */ 556 } 557 return mhd_HTTP_METHOD_OTHER; 558 } 559 560 561 /** 562 * Detect standard HTTP request method 563 * 564 * @param connection the connection to process 565 */ 566 static MHD_FN_PAR_NONNULL_ALL_ void 567 parse_http_std_method (struct MHD_Connection *restrict connection) 568 { 569 const char *const restrict m = connection->rq.method.cstr; /**< short alias */ 570 const size_t len = connection->rq.method.len; /**< short alias */ 571 mhd_assert (NULL != m); 572 mhd_assert (0 != len); 573 574 connection->rq.http_mthd = mhd_parse_http_method (len, 575 m); 576 } 577 578 579 /** 580 * Internal version of #MHD_HTTP_ProtocolVersion extended with mhd_HTTP_VER_0X 581 */ 582 enum MHD_FIXED_ENUM_MHD_SET_ mhd_HTTP_ProtVerParse 583 { 584 mhd_HTTP_VER_INVALID = MHD_HTTP_VERSION_INVALID 585 , 586 mhd_HTTP_VER_0X = 9 587 , 588 mhd_HTTP_VER_1_0 = MHD_HTTP_VERSION_1_0 589 , 590 mhd_HTTP_VER_1_1 = MHD_HTTP_VERSION_1_1 591 , 592 mhd_HTTP_VER_1_2P = MHD_HTTP_VERSION_1_2P 593 , 594 mhd_HTTP_VER_2 = MHD_HTTP_VERSION_2 595 , 596 mhd_HTTP_VER_3 = MHD_HTTP_VERSION_3 597 , 598 mhd_HTTP_VER_FUTURE = MHD_HTTP_VERSION_FUTURE 599 }; 600 601 602 /** 603 * Parse HTTP version 604 * 605 * @param len the length of @a http_string in bytes 606 * @param http_string the pointer to HTTP version string 607 */ 608 static MHD_FN_PAR_IN_SIZE_ (2, 1) MHD_FN_PAR_NONNULL_ALL_ 609 enum mhd_HTTP_ProtVerParse 610 parse_http_version (size_t len, 611 const char *restrict http_string) 612 { 613 const char *const h = http_string; /**< short alias */ 614 mhd_assert (NULL != http_string); 615 616 /* String must start with 'HTTP/d.d', case-sensitive match. 617 * See https://www.rfc-editor.org/rfc/rfc9112#name-http-version */ 618 if ((HTTP_VER_LEN == len) 619 && (0 == memcmp ("HTTP/", h, 5u)) 620 && ('.' == h[6])) 621 { 622 const unsigned char mj = (unsigned char)(h[5] - '0'); /**< Major number */ 623 const unsigned char mn = (unsigned char)(h[7] - '0'); /**< Minor number */ 624 625 if (1u == mj) 626 { 627 /* HTTP/1.x */ 628 if (1u == mn) 629 return mhd_HTTP_VER_1_1; 630 if (0u == mn) 631 return mhd_HTTP_VER_1_0; 632 if (9u >= mn) 633 return mhd_HTTP_VER_1_2P; 634 635 return mhd_HTTP_VER_INVALID; 636 } 637 638 if ((0u == mj) && (9u >= mn)) 639 return mhd_HTTP_VER_0X; /* Too old major version */ 640 641 if ((2u == mj) && (0u == mn)) 642 return mhd_HTTP_VER_2; 643 } 644 645 return mhd_HTTP_VER_INVALID; 646 } 647 648 649 /** 650 * Detect HTTP version, send error response if version is not supported 651 * 652 * @param connection the connection 653 * @param http_string the pointer to HTTP version string 654 * @param len the length of @a http_string in bytes 655 * @return true if HTTP version is correct and supported, 656 * false if HTTP version is not correct or unsupported. 657 */ 658 static MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_NONNULL_ALL_ bool 659 process_http_version (struct MHD_Connection *restrict connection, 660 size_t len, 661 const char *restrict http_string) 662 { 663 enum mhd_HTTP_ProtVerParse h_ver; 664 665 h_ver = parse_http_version (len, 666 http_string); 667 if (mhd_HTTP_VER_0X == h_ver) 668 { 669 connection->rq.http_ver = MHD_HTTP_VERSION_INVALID; 670 mhd_RESPOND_WITH_ERROR_STATIC (connection, 671 MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED, 672 ERR_RSP_REQ_HTTP_VER_IS_TOO_OLD); 673 return false; 674 } 675 676 connection->rq.http_ver = (enum MHD_HTTP_ProtocolVersion)h_ver; 677 678 switch (connection->rq.http_ver) 679 { 680 case MHD_HTTP_VERSION_INVALID: 681 break; 682 case MHD_HTTP_VERSION_1_1: 683 case MHD_HTTP_VERSION_1_0: 684 return true; 685 case MHD_HTTP_VERSION_1_2P: 686 if (MHD_PSL_VERY_STRICT > connection->daemon->req_cfg.strictness) 687 return true; 688 break; 689 case MHD_HTTP_VERSION_2: 690 mhd_RESPOND_WITH_ERROR_STATIC (connection, 691 MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED, 692 ERR_RSP_REQ_HTTP_VER_IS_NOT_SUPPORTED); 693 return false; 694 case MHD_HTTP_VERSION_3: 695 case MHD_HTTP_VERSION_FUTURE: 696 default: 697 mhd_UNREACHABLE (); 698 break; 699 } 700 701 mhd_RESPOND_WITH_ERROR_STATIC (connection, 702 MHD_HTTP_STATUS_BAD_REQUEST, 703 ERR_RSP_REQUEST_MALFORMED); 704 return false; 705 } 706 707 708 #ifndef MHD_MAX_EMPTY_LINES_SKIP 709 /** 710 * The maximum number of ignored empty line before the request line 711 * at default "strictness" level. 712 */ 713 # define MHD_MAX_EMPTY_LINES_SKIP 1024 714 #endif /* ! MHD_MAX_EMPTY_LINES_SKIP */ 715 716 717 /** 718 * Find and parse the request line. 719 * @param c the connection to process 720 * @return true if request line completely processed (or unrecoverable error 721 * found) and state is changed, 722 * false if not enough data yet in the receive buffer 723 */ 724 static MHD_FN_PAR_NONNULL_ALL_ bool 725 get_request_line_inner (struct MHD_Connection *restrict c) 726 { 727 size_t p; /**< The current processing position */ 728 const int discp_lvl = c->daemon->req_cfg.strictness; 729 /* Allow to skip one or more empty lines before the request line. 730 RFC 9112, section 2.2 */ 731 const bool skip_empty_lines = (1 >= discp_lvl); 732 /* Allow to skip more then one empty line before the request line. 733 RFC 9112, section 2.2 */ 734 const bool skip_several_empty_lines = (skip_empty_lines && (0 >= discp_lvl)); 735 /* Allow to skip unlimited number of empty lines before the request line. 736 RFC 9112, section 2.2 */ 737 const bool skip_unlimited_empty_lines = 738 (skip_empty_lines && (-3 >= discp_lvl)); 739 /* Treat bare LF as the end of the line. 740 RFC 9112, section 2.2 */ 741 const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl); 742 /* Treat tab as whitespace delimiter. 743 RFC 9112, section 3 */ 744 const bool tab_as_wsp = (0 >= discp_lvl); 745 /* Treat VT (vertical tab) and FF (form feed) as whitespace delimiters. 746 RFC 9112, section 3 */ 747 const bool other_wsp_as_wsp = (-1 >= discp_lvl); 748 /* Treat continuous whitespace block as a single space. 749 RFC 9112, section 3 */ 750 const bool wsp_blocks = (-1 >= discp_lvl); 751 /* Parse whitespace in URI, special parsing of the request line. 752 RFC 9112, section 3.2 */ 753 const bool wsp_in_uri = (0 >= discp_lvl); 754 /* Keep whitespace in URI, give app URI with whitespace instead of 755 automatic redirect to fixed URI. 756 Violates RFC 9112, section 3.2 */ 757 const bool wsp_in_uri_keep = (-2 >= discp_lvl); 758 /* Keep bare CR character as is. 759 Violates RFC 9112, section 2.2 */ 760 const bool bare_cr_keep = (wsp_in_uri_keep && (-3 >= discp_lvl)); 761 /* Treat bare CR as space; replace it with space before processing. 762 RFC 9112, section 2.2 */ 763 const bool bare_cr_as_sp = ((!bare_cr_keep) && (-1 >= discp_lvl)); 764 765 mhd_assert (mhd_HTTP_STAGE_INIT == c->stage \ 766 || mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); 767 mhd_assert (NULL == c->rq.method.cstr \ 768 || mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); 769 mhd_assert (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd \ 770 || mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); 771 mhd_assert (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd \ 772 || 0 != c->rq.hdrs.rq_line.proc_pos); 773 774 if (0 == c->read_buffer_offset) 775 { 776 mhd_assert (mhd_HTTP_STAGE_INIT == c->stage); 777 return false; /* No data to process */ 778 } 779 p = c->rq.hdrs.rq_line.proc_pos; 780 mhd_assert (p <= c->read_buffer_offset); 781 782 /* Skip empty lines, if any (and if allowed) */ 783 /* See RFC 9112, section 2.2 */ 784 if ((0 == p) 785 && (skip_empty_lines)) 786 { 787 /* Skip empty lines before the request line. 788 See RFC 9112, section 2.2 */ 789 bool is_empty_line; 790 mhd_assert (mhd_HTTP_STAGE_INIT == c->stage); 791 mhd_assert (0 == c->rq.method.len); 792 mhd_assert (NULL == c->rq.method.cstr); 793 mhd_assert (NULL == c->rq.url); 794 mhd_assert (0 == c->rq.url_len); 795 mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt); 796 mhd_assert (0 == c->rq.req_target_len); 797 mhd_assert (NULL == c->rq.version); 798 do 799 { 800 is_empty_line = false; 801 if ('\r' == c->read_buffer[0]) 802 { 803 if (1 == c->read_buffer_offset) 804 return false; /* Not enough data yet */ 805 if ('\n' == c->read_buffer[1]) 806 { 807 is_empty_line = true; 808 c->read_buffer += 2; 809 c->read_buffer_size -= 2; 810 c->read_buffer_offset -= 2; 811 c->rq.hdrs.rq_line.skipped_empty_lines++; 812 } 813 } 814 else if (('\n' == c->read_buffer[0]) 815 && (bare_lf_as_crlf)) 816 { 817 is_empty_line = true; 818 c->read_buffer += 1; 819 c->read_buffer_size -= 1; 820 c->read_buffer_offset -= 1; 821 c->rq.hdrs.rq_line.skipped_empty_lines++; 822 } 823 if (is_empty_line) 824 { 825 if ((!skip_unlimited_empty_lines) 826 && (((unsigned int)((skip_several_empty_lines) ? 827 MHD_MAX_EMPTY_LINES_SKIP : 1)) < 828 c->rq.hdrs.rq_line.skipped_empty_lines)) 829 { 830 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 831 "Too many meaningless extra empty lines " \ 832 "received before the request."); 833 return true; /* Process connection closure */ 834 } 835 if (0 == c->read_buffer_offset) 836 return false; /* No more data to process */ 837 } 838 } while (is_empty_line); 839 } 840 /* All empty lines are skipped */ 841 842 c->stage = mhd_HTTP_STAGE_REQ_LINE_RECEIVING; 843 /* Read and parse the request line */ 844 mhd_assert (1 <= c->read_buffer_offset); 845 846 while (p < c->read_buffer_offset) 847 { 848 char *const restrict read_buffer = c->read_buffer; 849 const char chr = read_buffer[p]; 850 bool end_of_line; 851 /* 852 The processing logic is different depending on the configured strictness: 853 854 When whitespace BLOCKS are NOT ALLOWED, the end of the whitespace is 855 processed BEFORE processing of the current character. 856 When whitespace BLOCKS are ALLOWED, the end of the whitespace is 857 processed AFTER processing of the current character. 858 859 When space char in the URI is ALLOWED, the delimiter between the URI and 860 the HTTP version string is processed only at the END of the line. 861 When space in the URI is NOT ALLOWED, the delimiter between the URI and 862 the HTTP version string is processed as soon as the FIRST whitespace is 863 found after URI start. 864 */ 865 866 end_of_line = false; 867 868 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) \ 869 || (c->rq.hdrs.rq_line.last_ws_end > \ 870 c->rq.hdrs.rq_line.last_ws_start)); 871 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_start) \ 872 || (0 != c->rq.hdrs.rq_line.last_ws_end)); 873 874 /* Check for the end of the line */ 875 if ('\r' == chr) 876 { 877 if (p + 1 == c->read_buffer_offset) 878 { 879 c->rq.hdrs.rq_line.proc_pos = p; 880 return false; /* Not enough data yet */ 881 } 882 else if ('\n' == read_buffer[p + 1]) 883 end_of_line = true; 884 else 885 { 886 /* Bare CR alone */ 887 /* Must be rejected or replaced with space char. 888 See RFC 9112, section 2.2 */ 889 if (bare_cr_as_sp) 890 { 891 read_buffer[p] = ' '; 892 c->rq.num_cr_sp_replaced++; 893 continue; /* Re-start processing of the current character */ 894 } 895 else if (!bare_cr_keep) 896 { 897 /* A quick simple check whether this line looks like an HTTP request */ 898 if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) 899 && (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd)) 900 { 901 mhd_RESPOND_WITH_ERROR_STATIC (c, 902 MHD_HTTP_STATUS_BAD_REQUEST, 903 ERR_RSP_BARE_CR_IN_HEADER); 904 } 905 else 906 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 907 "Bare CR characters are not allowed " \ 908 "in the request line."); 909 910 return true; /* Error in the request */ 911 } 912 } 913 } 914 else if ('\n' == chr) 915 { 916 /* Bare LF may be recognised as a line delimiter. 917 See RFC 9112, section 2.2 */ 918 if (bare_lf_as_crlf) 919 end_of_line = true; 920 else 921 { 922 /* While RFC does not enforce error for bare LF character, 923 if this char is not treated as a line delimiter, it should be 924 rejected to avoid any security weakness due to request smuggling. */ 925 /* A quick simple check whether this line looks like an HTTP request */ 926 if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) 927 && (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd)) 928 { 929 mhd_RESPOND_WITH_ERROR_STATIC (c, 930 MHD_HTTP_STATUS_BAD_REQUEST, 931 ERR_RSP_BARE_LF_IN_HEADER); 932 } 933 else 934 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 935 "Bare LF characters are not allowed " \ 936 "in the request line."); 937 return true; /* Error in the request */ 938 } 939 } 940 941 if (end_of_line) 942 { 943 /* Handle the end of the request line */ 944 945 if (NULL != c->rq.method.cstr) 946 { 947 if (wsp_in_uri) 948 { 949 /* The end of the URI and the start of the HTTP version string 950 should be determined now. */ 951 mhd_assert (NULL == c->rq.version); 952 mhd_assert (0 == c->rq.req_target_len); 953 if (0 != c->rq.hdrs.rq_line.last_ws_end) 954 { 955 /* Determine the end and the length of the URI */ 956 if (NULL != c->rq.hdrs.rq_line.rq_tgt) 957 { 958 read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */ 959 c->rq.req_target_len = 960 c->rq.hdrs.rq_line.last_ws_start 961 - (size_t)(c->rq.hdrs.rq_line.rq_tgt - read_buffer); 962 } 963 else if ((c->rq.hdrs.rq_line.last_ws_start + 1 < 964 c->rq.hdrs.rq_line.last_ws_end) 965 && (HTTP_VER_LEN == (p - c->rq.hdrs.rq_line.last_ws_end))) 966 { 967 /* Found only HTTP method and HTTP version and more than one 968 whitespace between them. Assume zero-length URI. */ 969 mhd_assert (wsp_blocks); 970 c->rq.hdrs.rq_line.last_ws_start++; 971 read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */ 972 c->rq.hdrs.rq_line.rq_tgt = 973 read_buffer + c->rq.hdrs.rq_line.last_ws_start; 974 c->rq.req_target_len = 0; 975 c->rq.hdrs.rq_line.num_ws_in_uri = 0; 976 c->rq.hdrs.rq_line.rq_tgt_qmark = NULL; 977 } 978 /* Determine the start of the HTTP version string */ 979 if (NULL != c->rq.hdrs.rq_line.rq_tgt) 980 { 981 c->rq.version = read_buffer + c->rq.hdrs.rq_line.last_ws_end; 982 } 983 } 984 } 985 else 986 { 987 /* The end of the URI and the start of the HTTP version string 988 should be already known. */ 989 if ((NULL == c->rq.version) 990 && (NULL != c->rq.hdrs.rq_line.rq_tgt) 991 && (HTTP_VER_LEN == p - (size_t)(c->rq.hdrs.rq_line.rq_tgt 992 - read_buffer)) 993 && (0 != read_buffer[(size_t) 994 (c->rq.hdrs.rq_line.rq_tgt 995 - read_buffer) - 1])) 996 { 997 /* Found only HTTP method and HTTP version and more than one 998 whitespace between them. Assume zero-length URI. */ 999 size_t uri_pos; 1000 mhd_assert (wsp_blocks); 1001 mhd_assert (0 == c->rq.req_target_len); 1002 uri_pos = (size_t)(c->rq.hdrs.rq_line.rq_tgt - read_buffer) - 1; 1003 mhd_assert (uri_pos < p); 1004 c->rq.version = c->rq.hdrs.rq_line.rq_tgt; 1005 read_buffer[uri_pos] = 0; /* Zero terminate the URI */ 1006 c->rq.hdrs.rq_line.rq_tgt = read_buffer + uri_pos; 1007 c->rq.req_target_len = 0; 1008 c->rq.hdrs.rq_line.num_ws_in_uri = 0; 1009 c->rq.hdrs.rq_line.rq_tgt_qmark = NULL; 1010 } 1011 } 1012 1013 if (NULL != c->rq.version) 1014 { 1015 mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt); 1016 if (!process_http_version (c, 1017 p - (size_t)(c->rq.version 1018 - read_buffer), 1019 c->rq.version)) 1020 { 1021 mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING < c->stage); 1022 return true; /* Unsupported / broken HTTP version */ 1023 } 1024 read_buffer[p] = 0; /* Zero terminate the HTTP version strings */ 1025 if ('\r' == chr) 1026 { 1027 p++; /* Consume CR */ 1028 mhd_assert (p < c->read_buffer_offset); /* The next character has been already checked */ 1029 } 1030 p++; /* Consume LF */ 1031 c->read_buffer += p; 1032 c->read_buffer_size -= p; 1033 c->read_buffer_offset -= p; 1034 mhd_assert (c->rq.hdrs.rq_line.num_ws_in_uri <= \ 1035 c->rq.req_target_len); 1036 mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) \ 1037 || (0 != c->rq.req_target_len)); 1038 mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) \ 1039 || ((size_t)(c->rq.hdrs.rq_line.rq_tgt_qmark \ 1040 - c->rq.hdrs.rq_line.rq_tgt) < \ 1041 c->rq.req_target_len)); 1042 mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) \ 1043 || (c->rq.hdrs.rq_line.rq_tgt_qmark >= \ 1044 c->rq.hdrs.rq_line.rq_tgt)); 1045 return true; /* The request line is successfully parsed */ 1046 } 1047 } 1048 /* Error in the request line */ 1049 1050 /* A quick simple check whether this line looks like an HTTP request */ 1051 if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) 1052 && (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd)) 1053 { 1054 mhd_RESPOND_WITH_ERROR_STATIC (c, 1055 MHD_HTTP_STATUS_BAD_REQUEST, 1056 ERR_RSP_REQUEST_MALFORMED); 1057 } 1058 else 1059 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 1060 "The request line is malformed."); 1061 1062 return true; 1063 } 1064 1065 /* Process possible end of the previously found whitespace delimiter */ 1066 if ((!wsp_blocks) 1067 && (p == c->rq.hdrs.rq_line.last_ws_end) 1068 && (0 != c->rq.hdrs.rq_line.last_ws_end)) 1069 { 1070 /* Previous character was a whitespace char and whitespace blocks 1071 are not allowed. */ 1072 /* The current position is the next character after 1073 a whitespace delimiter */ 1074 if (NULL == c->rq.hdrs.rq_line.rq_tgt) 1075 { 1076 /* The current position is the start of the URI */ 1077 mhd_assert (0 == c->rq.req_target_len); 1078 mhd_assert (NULL == c->rq.version); 1079 c->rq.hdrs.rq_line.rq_tgt = read_buffer + p; 1080 /* Reset the whitespace marker */ 1081 c->rq.hdrs.rq_line.last_ws_start = 0; 1082 c->rq.hdrs.rq_line.last_ws_end = 0; 1083 } 1084 else 1085 { 1086 /* It was a whitespace after the start of the URI */ 1087 if (!wsp_in_uri) 1088 { 1089 mhd_assert ((0 != c->rq.req_target_len) \ 1090 || (c->rq.hdrs.rq_line.rq_tgt + 1 == read_buffer + p)); 1091 mhd_assert (NULL == c->rq.version); /* Too many whitespaces? This error is handled at whitespace start */ 1092 c->rq.version = read_buffer + p; 1093 /* Reset the whitespace marker */ 1094 c->rq.hdrs.rq_line.last_ws_start = 0; 1095 c->rq.hdrs.rq_line.last_ws_end = 0; 1096 } 1097 } 1098 } 1099 1100 /* Process the current character. 1101 Is it not the end of the line. */ 1102 if ((' ' == chr) 1103 || (('\t' == chr) && (tab_as_wsp)) 1104 || ((other_wsp_as_wsp) && ((0xb == chr) || (0xc == chr)))) 1105 { 1106 /* A whitespace character */ 1107 if ((0 == c->rq.hdrs.rq_line.last_ws_end) 1108 || (p != c->rq.hdrs.rq_line.last_ws_end) 1109 || (!wsp_blocks)) 1110 { 1111 /* Found first whitespace char of the new whitespace block */ 1112 if (NULL == c->rq.method.cstr) 1113 { 1114 /* Found the end of the HTTP method string */ 1115 mhd_assert (0 == c->rq.hdrs.rq_line.last_ws_start); 1116 mhd_assert (0 == c->rq.hdrs.rq_line.last_ws_end); 1117 mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt); 1118 mhd_assert (0 == c->rq.req_target_len); 1119 mhd_assert (NULL == c->rq.version); 1120 if (0 == p) 1121 { 1122 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 1123 "The request line starts with a whitespace."); 1124 return true; /* Error in the request */ 1125 } 1126 read_buffer[p] = 0; /* Zero-terminate the request method string */ 1127 c->rq.method.cstr = read_buffer; 1128 c->rq.method.len = p; 1129 parse_http_std_method (c); 1130 } 1131 else 1132 { 1133 /* A whitespace after the start of the URI */ 1134 if (!wsp_in_uri) 1135 { 1136 /* Whitespace in URI is not allowed to be parsed */ 1137 if (NULL == c->rq.version) 1138 { 1139 mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt); 1140 /* This is a delimiter between URI and HTTP version string */ 1141 read_buffer[p] = 0; /* Zero-terminate request URI string */ 1142 mhd_assert (((size_t)(c->rq.hdrs.rq_line.rq_tgt \ 1143 - read_buffer)) <= p); 1144 c->rq.req_target_len = 1145 p - (size_t)(c->rq.hdrs.rq_line.rq_tgt - read_buffer); 1146 } 1147 else 1148 { 1149 /* This is a delimiter AFTER version string */ 1150 1151 /* A quick simple check whether this line looks like an HTTP request */ 1152 if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) 1153 && (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd)) 1154 { 1155 mhd_RESPOND_WITH_ERROR_STATIC (c, 1156 MHD_HTTP_STATUS_BAD_REQUEST, 1157 ERR_RSP_RQ_LINE_TOO_MANY_WSP); 1158 } 1159 else 1160 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 1161 "The request line has more than " 1162 "two whitespaces."); 1163 return true; /* Error in the request */ 1164 } 1165 } 1166 else 1167 { 1168 /* Whitespace in URI is allowed to be parsed */ 1169 if (0 != c->rq.hdrs.rq_line.last_ws_end) 1170 { 1171 /* The whitespace after the start of the URI has been found already */ 1172 c->rq.hdrs.rq_line.num_ws_in_uri += 1173 c->rq.hdrs.rq_line.last_ws_end 1174 - c->rq.hdrs.rq_line.last_ws_start; 1175 } 1176 } 1177 } 1178 c->rq.hdrs.rq_line.last_ws_start = p; 1179 c->rq.hdrs.rq_line.last_ws_end = p + 1; /* Will be updated on the next char parsing */ 1180 } 1181 else 1182 { 1183 /* Continuation of the whitespace block */ 1184 mhd_assert (0 != c->rq.hdrs.rq_line.last_ws_end); 1185 mhd_assert (0 != p); 1186 c->rq.hdrs.rq_line.last_ws_end = p + 1; 1187 } 1188 } 1189 else 1190 { 1191 /* Non-whitespace char, not the end of the line */ 1192 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) \ 1193 || (c->rq.hdrs.rq_line.last_ws_end == p) \ 1194 || wsp_in_uri); 1195 1196 if ((p == c->rq.hdrs.rq_line.last_ws_end) 1197 && (0 != c->rq.hdrs.rq_line.last_ws_end) 1198 && (wsp_blocks)) 1199 { 1200 /* The end of the whitespace block */ 1201 if (NULL == c->rq.hdrs.rq_line.rq_tgt) 1202 { 1203 /* This is the first character of the URI */ 1204 mhd_assert (0 == c->rq.req_target_len); 1205 mhd_assert (NULL == c->rq.version); 1206 c->rq.hdrs.rq_line.rq_tgt = read_buffer + p; 1207 /* Reset the whitespace marker */ 1208 c->rq.hdrs.rq_line.last_ws_start = 0; 1209 c->rq.hdrs.rq_line.last_ws_end = 0; 1210 } 1211 else 1212 { 1213 if (!wsp_in_uri) 1214 { 1215 /* This is the first character of the HTTP version */ 1216 mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt); 1217 mhd_assert ((0 != c->rq.req_target_len) \ 1218 || (c->rq.hdrs.rq_line.rq_tgt + 1 == read_buffer + p)); 1219 mhd_assert (NULL == c->rq.version); /* Handled at whitespace start */ 1220 c->rq.version = read_buffer + p; 1221 /* Reset the whitespace marker */ 1222 c->rq.hdrs.rq_line.last_ws_start = 0; 1223 c->rq.hdrs.rq_line.last_ws_end = 0; 1224 } 1225 } 1226 } 1227 1228 /* Handle other special characters */ 1229 if ('?' == chr) 1230 { 1231 if ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) 1232 && (NULL != c->rq.hdrs.rq_line.rq_tgt)) 1233 { 1234 c->rq.hdrs.rq_line.rq_tgt_qmark = read_buffer + p; 1235 } 1236 } 1237 else if ((0xb == chr) || (0xc == chr)) 1238 { 1239 /* VT or LF characters */ 1240 mhd_assert (!other_wsp_as_wsp); 1241 if ((NULL != c->rq.hdrs.rq_line.rq_tgt) 1242 && (NULL == c->rq.version) 1243 && (wsp_in_uri)) 1244 { 1245 c->rq.hdrs.rq_line.num_ws_in_uri++; 1246 } 1247 else 1248 { 1249 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 1250 "Invalid character is in the request line."); 1251 return true; /* Error in the request */ 1252 } 1253 } 1254 else if (0 == chr) 1255 { 1256 /* NUL character */ 1257 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, 1258 "The NUL character is in the request line."); 1259 return true; /* Error in the request */ 1260 } 1261 } 1262 1263 p++; 1264 } 1265 1266 c->rq.hdrs.rq_line.proc_pos = p; 1267 return false; /* Not enough data yet */ 1268 } 1269 1270 1271 /** 1272 * Callback for iterating over GET parameters 1273 * @param cls the iterator metadata 1274 * @param name the name of the parameter 1275 * @param value the value of the parameter 1276 * @return bool to continue iterations, 1277 * false to stop the iteration 1278 */ 1279 static MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3) bool 1280 request_add_get_arg (void *restrict cls, 1281 const struct MHD_String *restrict name, 1282 const struct MHD_StringNullable *restrict value) 1283 { 1284 struct MHD_Stream *s = (struct MHD_Stream *)cls; 1285 1286 return mhd_stream_add_field_nullable (s, MHD_VK_URI_QUERY_PARAM, name, value); 1287 } 1288 1289 1290 MHD_INTERNAL 1291 MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2) 1292 MHD_FN_PAR_INOUT_SIZE_ (2, 1) bool 1293 // TODO: detect and report errors 1294 mhd_parse_uri_args (size_t args_len, 1295 char *restrict args, 1296 mhd_GetArgumentInter cb, 1297 void *restrict cls) 1298 { 1299 size_t i; 1300 1301 mhd_assert (args_len < (size_t)(args_len + 1)); /* Does not work when args_len == SIZE_MAX */ 1302 1303 for (i = 0; i < args_len; ++i) /* Looking for names of the parameters */ 1304 { 1305 size_t name_start; 1306 size_t name_len; 1307 size_t value_start; 1308 size_t value_len; 1309 struct MHD_String name; 1310 struct MHD_StringNullable value; 1311 1312 /* Found start of the name */ 1313 1314 value_start = 0; 1315 for (name_start = i; i < args_len; ++i) /* Processing parameter */ 1316 { 1317 if ('+' == args[i]) 1318 args[i] = ' '; 1319 else if ('=' == args[i]) 1320 { 1321 /* Found start of the value */ 1322 for (value_start = ++i; i < args_len; ++i) /* Processing parameter value */ 1323 { 1324 if ('+' == args[i]) 1325 args[i] = ' '; 1326 else if ('&' == args[i]) /* delimiter for the next parameter */ 1327 break; /* Next parameter */ 1328 } 1329 break; /* End of the current parameter */ 1330 } 1331 else if ('&' == args[i]) 1332 break; /* End of the name of the parameter without a value */ 1333 } 1334 1335 /* PCT-decode, zero-terminate and store the found parameter */ 1336 1337 if (0 != value_start) /* Value cannot start at zero position */ 1338 { /* Name with value */ 1339 mhd_assert (name_start + 1 <= value_start); 1340 name_len = value_start - name_start - 1; 1341 1342 value_len = 1343 mhd_str_pct_decode_lenient_n (args + value_start, i - value_start, 1344 args + value_start, i - value_start, 1345 NULL); // TODO: add support for broken encoding detection 1346 if (value_start + value_len < args_len) 1347 args[value_start + value_len] = 0; 1348 value.cstr = args + value_start; 1349 value.len = value_len; 1350 } 1351 else 1352 { /* Name without value */ 1353 name_len = i - name_start; 1354 1355 value.cstr = NULL; 1356 value.len = 0; 1357 } 1358 name_len = mhd_str_pct_decode_lenient_n (args + name_start, name_len, 1359 args + name_start, name_len, 1360 NULL); // TODO: add support for broken encoding detection 1361 if (name_start + name_len < args_len) 1362 args[name_start + name_len] = 0; 1363 name.cstr = args + name_start; 1364 name.len = name_len; 1365 if (!cb (cls, &name, &value)) 1366 return false; 1367 } 1368 return true; 1369 } 1370 1371 1372 /** 1373 * Process request-target string, form URI and URI parameters 1374 * @param c the connection to process 1375 * @return true if request-target successfully processed, 1376 * false if error encountered 1377 */ 1378 static MHD_FN_PAR_NONNULL_ALL_ bool 1379 process_request_target (struct MHD_Connection *c) 1380 { 1381 size_t params_len; 1382 1383 mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); 1384 mhd_assert (NULL == c->rq.url); 1385 mhd_assert (0 == c->rq.url_len); 1386 mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt); 1387 mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) \ 1388 || (c->rq.hdrs.rq_line.rq_tgt <= 1389 c->rq.hdrs.rq_line.rq_tgt_qmark)); 1390 mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) \ 1391 || (c->rq.req_target_len > \ 1392 (size_t)(c->rq.hdrs.rq_line.rq_tgt_qmark \ 1393 - c->rq.hdrs.rq_line.rq_tgt))); 1394 1395 /* Log callback before the request-target is modified/decoded */ 1396 if (NULL != c->daemon->req_cfg.uri_cb.cb) 1397 { 1398 struct MHD_EarlyUriCbData req_data; 1399 req_data.request = &(c->rq); 1400 req_data.full_uri.cstr = c->rq.hdrs.rq_line.rq_tgt; 1401 req_data.full_uri.len = c->rq.req_target_len; 1402 c->rq.app_aware = true; 1403 c->daemon->req_cfg.uri_cb.cb (c->daemon->req_cfg.uri_cb.cls, 1404 &req_data, 1405 &(c->rq.app_context)); 1406 } 1407 1408 if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark) 1409 { 1410 params_len = 1411 c->rq.req_target_len 1412 - (size_t)(c->rq.hdrs.rq_line.rq_tgt_qmark - c->rq.hdrs.rq_line.rq_tgt); 1413 1414 mhd_assert (1 <= params_len); 1415 1416 c->rq.hdrs.rq_line.rq_tgt_qmark[0] = 0; /* Replace '?' with zero termination */ 1417 1418 // TODO: support detection of decoding errors 1419 if (!mhd_parse_uri_args (params_len - 1, 1420 c->rq.hdrs.rq_line.rq_tgt_qmark + 1, 1421 &request_add_get_arg, 1422 &(c->h1_stream))) 1423 { 1424 mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_GET_PARAM, 1425 "Not enough memory in the pool to store GET parameter"); 1426 1427 mhd_RESPOND_WITH_ERROR_STATIC ( 1428 c, 1429 mhd_stream_get_no_space_err_status_code (c, 1430 MHD_PROC_RECV_URI, 1431 0, 1432 NULL), 1433 ERR_RSP_MSG_REQUEST_TOO_BIG); 1434 mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING != c->stage); 1435 return false; 1436 1437 } 1438 } 1439 else 1440 params_len = 0; 1441 1442 mhd_assert (strlen (c->rq.hdrs.rq_line.rq_tgt) == \ 1443 c->rq.req_target_len - params_len); 1444 1445 /* Finally unescape URI itself */ 1446 // TODO: support detection of decoding errors 1447 c->rq.url_len = 1448 mhd_str_pct_decode_lenient_n (c->rq.hdrs.rq_line.rq_tgt, 1449 c->rq.req_target_len - params_len, 1450 c->rq.hdrs.rq_line.rq_tgt, 1451 c->rq.req_target_len - params_len, 1452 NULL); 1453 c->rq.url = c->rq.hdrs.rq_line.rq_tgt; 1454 1455 return true; 1456 } 1457 1458 1459 #ifndef MHD_MAX_FIXED_URI_LEN 1460 /** 1461 * The maximum size of the fixed URI for automatic redirection 1462 */ 1463 # define MHD_MAX_FIXED_URI_LEN (64 * 1024) 1464 #endif /* ! MHD_MAX_FIXED_URI_LEN */ 1465 1466 /** 1467 * Send the automatic redirection to fixed URI when received URI with 1468 * whitespaces. 1469 * If URI is too large, close connection with error. 1470 * 1471 * @param c the connection to process 1472 */ 1473 static void 1474 send_redirect_fixed_rq_target (struct MHD_Connection *restrict c) 1475 { 1476 static const char hdr_prefix[] = MHD_HTTP_HEADER_LOCATION ": "; 1477 static const size_t hdr_prefix_len = 1478 mhd_SSTR_LEN (MHD_HTTP_HEADER_LOCATION ": "); 1479 char *hdr_line; 1480 char *b; 1481 size_t fixed_uri_len; 1482 size_t i; 1483 size_t o; 1484 1485 mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); 1486 mhd_assert (0 != c->rq.hdrs.rq_line.num_ws_in_uri); 1487 mhd_assert (c->rq.hdrs.rq_line.num_ws_in_uri <= \ 1488 c->rq.req_target_len); 1489 fixed_uri_len = c->rq.req_target_len 1490 + 2 * c->rq.hdrs.rq_line.num_ws_in_uri; 1491 if ((fixed_uri_len + 200 > c->daemon->conns.cfg.mem_pool_size) 1492 || (fixed_uri_len > MHD_MAX_FIXED_URI_LEN) 1493 || (NULL == 1494 (hdr_line = (char *)malloc (fixed_uri_len + 1 + hdr_prefix_len)))) 1495 { 1496 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, \ 1497 "The request has whitespace character is " \ 1498 "in the URI and the URI is too large to " \ 1499 "send automatic redirect to fixed URI."); 1500 return; 1501 } 1502 memcpy (hdr_line, hdr_prefix, hdr_prefix_len); 1503 b = hdr_line + hdr_prefix_len; 1504 i = 0; 1505 o = 0; 1506 1507 do 1508 { 1509 const char chr = c->rq.hdrs.rq_line.rq_tgt[i++]; 1510 1511 mhd_assert ('\r' != chr); /* Replaced during request line parsing */ 1512 mhd_assert ('\n' != chr); /* Rejected during request line parsing */ 1513 mhd_assert (0 != chr); /* Rejected during request line parsing */ 1514 switch (chr) 1515 { 1516 case ' ': 1517 b[o++] = '%'; 1518 b[o++] = '2'; 1519 b[o++] = '0'; 1520 break; 1521 case '\t': 1522 b[o++] = '%'; 1523 b[o++] = '0'; 1524 b[o++] = '9'; 1525 break; 1526 case 0x0B: /* VT (vertical tab) */ 1527 b[o++] = '%'; 1528 b[o++] = '0'; 1529 b[o++] = 'B'; 1530 break; 1531 case 0x0C: /* FF (form feed) */ 1532 b[o++] = '%'; 1533 b[o++] = '0'; 1534 b[o++] = 'C'; 1535 break; 1536 default: 1537 b[o++] = chr; 1538 break; 1539 } 1540 } while (i < c->rq.req_target_len); 1541 mhd_assert (fixed_uri_len == o); 1542 b[o] = 0; /* Zero-terminate the result */ 1543 1544 mhd_RESPOND_WITH_ERROR_HEADER (c, 1545 MHD_HTTP_STATUS_MOVED_PERMANENTLY, 1546 ERR_RSP_RQ_TARGET_INVALID_CHAR, 1547 o + hdr_prefix_len, 1548 hdr_line); 1549 1550 return; 1551 } 1552 1553 1554 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 1555 mhd_stream_get_request_line (struct MHD_Connection *restrict c) 1556 { 1557 const int discp_lvl = c->daemon->req_cfg.strictness; 1558 /* Parse whitespace in URI, special parsing of the request line */ 1559 const bool wsp_in_uri = (0 >= discp_lvl); 1560 /* Keep whitespace in URI, give app URI with whitespace instead of 1561 automatic redirect to fixed URI */ 1562 const bool wsp_in_uri_keep = (-2 >= discp_lvl); 1563 1564 if (!get_request_line_inner (c)) 1565 { 1566 /* End of the request line has not been found yet */ 1567 mhd_assert ((!wsp_in_uri) || NULL == c->rq.version); 1568 if ((NULL != c->rq.version) 1569 && (HTTP_VER_LEN < 1570 (c->rq.hdrs.rq_line.proc_pos 1571 - (size_t)(c->rq.version - c->read_buffer)))) 1572 { 1573 c->rq.http_ver = MHD_HTTP_VERSION_INVALID; 1574 mhd_RESPOND_WITH_ERROR_STATIC (c, 1575 MHD_HTTP_STATUS_BAD_REQUEST, 1576 ERR_RSP_REQUEST_MALFORMED); 1577 return true; /* Error in the request */ 1578 } 1579 return false; 1580 } 1581 if (mhd_HTTP_STAGE_REQ_LINE_RECEIVING < c->stage) 1582 return true; /* Error in the request */ 1583 1584 mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); 1585 mhd_assert (NULL == c->rq.url); 1586 mhd_assert (0 == c->rq.url_len); 1587 mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt); 1588 if (0 != c->rq.hdrs.rq_line.num_ws_in_uri) 1589 { 1590 if (!wsp_in_uri) 1591 { 1592 mhd_RESPOND_WITH_ERROR_STATIC (c, 1593 MHD_HTTP_STATUS_BAD_REQUEST, 1594 ERR_RSP_RQ_TARGET_INVALID_CHAR); 1595 return true; /* Error in the request */ 1596 } 1597 if (!wsp_in_uri_keep) 1598 { 1599 send_redirect_fixed_rq_target (c); 1600 return true; /* Error in the request */ 1601 } 1602 } 1603 if (!process_request_target (c)) 1604 return true; /* Error in processing */ 1605 1606 c->stage = mhd_HTTP_STAGE_REQ_LINE_RECEIVED; 1607 return true; 1608 } 1609 1610 1611 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 1612 mhd_stream_switch_to_rq_headers_proc (struct MHD_Connection *restrict c) 1613 { 1614 c->rq.field_lines.start = c->read_buffer; 1615 mhd_stream_reset_rq_hdr_proc_state (c); 1616 c->stage = mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING; 1617 } 1618 1619 1620 /** 1621 * Send error reply when receive buffer space exhausted while receiving or 1622 * storing the request headers 1623 * @param c the connection to handle 1624 * @param add_header the optional pointer to the current header string being 1625 * processed or the header failed to be added. 1626 * Could be not zero-terminated and can contain binary zeros. 1627 * Can be NULL. 1628 * @param add_header_size the size of the @a add_header 1629 */ 1630 mhd_static_inline 1631 MHD_FN_PAR_NONNULL_ (1) void 1632 handle_req_headers_no_space (struct MHD_Connection *restrict c, 1633 const char *restrict add_header, 1634 size_t add_header_size) 1635 { 1636 unsigned int err_code; 1637 1638 err_code = mhd_stream_get_no_space_err_status_code (c, 1639 MHD_PROC_RECV_HEADERS, 1640 add_header_size, 1641 add_header); 1642 mhd_RESPOND_WITH_ERROR_STATIC (c, 1643 err_code, 1644 ERR_RSP_REQUEST_HEADER_TOO_BIG); 1645 } 1646 1647 1648 /** 1649 * Send error reply when receive buffer space exhausted while receiving or 1650 * storing the request footers (for chunked requests). 1651 * @param c the connection to handle 1652 * @param add_footer the optional pointer to the current footer string being 1653 * processed or the footer failed to be added. 1654 * Could be not zero-terminated and can contain binary zeros. 1655 * Can be NULL. 1656 * @param add_footer_size the size of the @a add_footer 1657 */ 1658 mhd_static_inline 1659 MHD_FN_PAR_NONNULL_ (1) void 1660 handle_req_footers_no_space (struct MHD_Connection *restrict c, 1661 const char *restrict add_footer, 1662 size_t add_footer_size) 1663 { 1664 (void)add_footer; 1665 (void)add_footer_size; /* Unused */ 1666 mhd_assert (c->rq.have_chunked_upload); 1667 1668 /* Footers should be optional */ 1669 mhd_RESPOND_WITH_ERROR_STATIC ( 1670 c, 1671 MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE, 1672 ERR_RSP_REQUEST_FOOTER_TOO_BIG); 1673 } 1674 1675 1676 /** 1677 * Results of header line reading 1678 */ 1679 enum MHD_FIXED_ENUM_ mhd_HdrLineReadRes 1680 { 1681 /** 1682 * Not enough data yet 1683 */ 1684 MHD_HDR_LINE_READING_NEED_MORE_DATA = 0, 1685 /** 1686 * New header line has been read 1687 */ 1688 MHD_HDR_LINE_READING_GOT_HEADER, 1689 /** 1690 * Error in header data, error response has been queued 1691 */ 1692 MHD_HDR_LINE_READING_DATA_ERROR, 1693 /** 1694 * Found the end of the request header (end of field lines) 1695 */ 1696 MHD_HDR_LINE_READING_GOT_END_OF_HEADER 1697 }; 1698 1699 1700 /** 1701 * Find the end of the request header line and make basic header parsing. 1702 * Handle errors and header folding. 1703 * @param c the connection to process 1704 * @param process_footers if true then footers are processed, 1705 * if false then headers are processed 1706 * @param[out] hdr_name the name of the parsed header (field) 1707 * @param[out] hdr_value the value of the parsed header (field) 1708 * @return mhd_HdrLineReadRes value 1709 */ 1710 static enum mhd_HdrLineReadRes 1711 get_req_header (struct MHD_Connection *restrict c, 1712 bool process_footers, 1713 struct MHD_String *restrict hdr_name, 1714 struct MHD_String *restrict hdr_value) 1715 { 1716 const int discp_lvl = c->daemon->req_cfg.strictness; 1717 /* Treat bare LF as the end of the line. 1718 RFC 9112, section 2.2-3 1719 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5). 1720 Bare LF is processed as end of the line or rejected as broken request. */ 1721 const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl); 1722 /* Keep bare CR character as is. 1723 Violates RFC 9112, section 2.2-4 */ 1724 const bool bare_cr_keep = (-3 >= discp_lvl); 1725 /* Treat bare CR as space; replace it with space before processing. 1726 RFC 9112, section 2.2-4 */ 1727 const bool bare_cr_as_sp = ((!bare_cr_keep) && (-1 >= discp_lvl)); 1728 /* Treat NUL as space; replace it with space before processing. 1729 RFC 9110, section 5.5-5 */ 1730 const bool nul_as_sp = (-1 >= discp_lvl); 1731 /* Allow folded header lines. 1732 RFC 9112, section 5.2-4 */ 1733 const bool allow_folded = (0 >= discp_lvl); 1734 /* Do not reject headers with the whitespace at the start of the first line. 1735 When allowed, the first line with whitespace character at the first 1736 position is ignored (as well as all possible line foldings of the first 1737 line). 1738 RFC 9112, section 2.2-8 */ 1739 const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl); 1740 /* Allow whitespace in header (field) name. 1741 Violates RFC 9110, section 5.1-2 */ 1742 const bool allow_wsp_in_name = (-2 >= discp_lvl); 1743 /* Allow zero-length header (field) name. 1744 Violates RFC 9110, section 5.1-2 */ 1745 const bool allow_empty_name = (-2 >= discp_lvl); 1746 /* Allow whitespace before colon. 1747 Violates RFC 9112, section 5.1-2 */ 1748 const bool allow_wsp_before_colon = (-3 >= discp_lvl); 1749 /* Do not abort the request when header line has no colon, just skip such 1750 bad lines. 1751 RFC 9112, section 5-1 */ 1752 const bool allow_line_without_colon = (-2 >= discp_lvl); 1753 1754 size_t p; /**< The position of the currently processed character */ 1755 1756 (void)process_footers; /* Unused parameter in non-debug and no messages */ 1757 1758 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 1759 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 1760 c->stage); 1761 1762 p = c->rq.hdrs.hdr.proc_pos; 1763 1764 mhd_assert (p <= c->read_buffer_offset); 1765 while (p < c->read_buffer_offset) 1766 { 1767 char *const restrict read_buffer = c->read_buffer; 1768 const char chr = read_buffer[p]; 1769 bool end_of_line; 1770 1771 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) \ 1772 || (c->rq.hdrs.hdr.name_len < p)); 1773 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p)); 1774 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) \ 1775 || (c->rq.hdrs.hdr.name_end_found)); 1776 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) \ 1777 || (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start)); 1778 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) \ 1779 || (0 != c->rq.hdrs.hdr.name_len)); 1780 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) \ 1781 || (0 == c->rq.hdrs.hdr.name_len) \ 1782 || (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len)); 1783 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) \ 1784 || (0 == c->rq.hdrs.hdr.value_start) \ 1785 || (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start)); 1786 1787 /* Check for the end of the line */ 1788 if ('\r' == chr) 1789 { 1790 if (0 != p) 1791 { 1792 /* Line is not empty, need to check for possible line folding */ 1793 if (p + 2 >= c->read_buffer_offset) 1794 break; /* Not enough data yet to check for folded line */ 1795 } 1796 else 1797 { 1798 /* Line is empty, no need to check for possible line folding */ 1799 if (p + 2 > c->read_buffer_offset) 1800 break; /* Not enough data yet to check for the end of the line */ 1801 } 1802 if ('\n' == read_buffer[p + 1]) 1803 end_of_line = true; 1804 else 1805 { 1806 /* Bare CR alone */ 1807 /* Must be rejected or replaced with space char. 1808 See RFC 9112, section 2.2-4 */ 1809 if (bare_cr_as_sp) 1810 { 1811 read_buffer[p] = ' '; 1812 c->rq.num_cr_sp_replaced++; 1813 continue; /* Re-start processing of the current character */ 1814 } 1815 else if (!bare_cr_keep) 1816 { 1817 if (!process_footers) 1818 mhd_RESPOND_WITH_ERROR_STATIC (c, 1819 MHD_HTTP_STATUS_BAD_REQUEST, 1820 ERR_RSP_BARE_CR_IN_HEADER); 1821 else 1822 mhd_RESPOND_WITH_ERROR_STATIC (c, 1823 MHD_HTTP_STATUS_BAD_REQUEST, 1824 ERR_RSP_BARE_CR_IN_FOOTER); 1825 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1826 } 1827 end_of_line = false; 1828 } 1829 } 1830 else if ('\n' == chr) 1831 { 1832 /* Bare LF may be recognised as a line delimiter. 1833 See RFC 9112, section 2.2-3 */ 1834 if (bare_lf_as_crlf) 1835 { 1836 if (0 != p) 1837 { 1838 /* Line is not empty, need to check for possible line folding */ 1839 if (p + 1 >= c->read_buffer_offset) 1840 break; /* Not enough data yet to check for folded line */ 1841 } 1842 end_of_line = true; 1843 } 1844 else 1845 { 1846 if (!process_footers) 1847 mhd_RESPOND_WITH_ERROR_STATIC (c, 1848 MHD_HTTP_STATUS_BAD_REQUEST, 1849 ERR_RSP_BARE_LF_IN_HEADER); 1850 else 1851 mhd_RESPOND_WITH_ERROR_STATIC (c, 1852 MHD_HTTP_STATUS_BAD_REQUEST, 1853 ERR_RSP_BARE_LF_IN_FOOTER); 1854 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1855 } 1856 } 1857 else 1858 end_of_line = false; 1859 1860 if (end_of_line) 1861 { 1862 /* Handle the end of the line */ 1863 /** 1864 * The full length of the line, including CRLF (or bare LF). 1865 */ 1866 const size_t line_len = p + (('\r' == chr) ? 2 : 1); 1867 char next_line_char; 1868 mhd_assert (line_len <= c->read_buffer_offset); 1869 1870 if (0 == p) 1871 { 1872 /* Zero-length header line. This is the end of the request header 1873 section. 1874 RFC 9112, Section 2.1-1 */ 1875 mhd_assert (!c->rq.hdrs.hdr.starts_with_ws); 1876 mhd_assert (!c->rq.hdrs.hdr.name_end_found); 1877 mhd_assert (0 == c->rq.hdrs.hdr.name_len); 1878 mhd_assert (0 == c->rq.hdrs.hdr.ws_start); 1879 mhd_assert (0 == c->rq.hdrs.hdr.value_start); 1880 /* Consume the line with CRLF (or bare LF) */ 1881 c->read_buffer += line_len; 1882 c->read_buffer_offset -= line_len; 1883 c->read_buffer_size -= line_len; 1884 return MHD_HDR_LINE_READING_GOT_END_OF_HEADER; 1885 } 1886 1887 mhd_assert (line_len < c->read_buffer_offset); 1888 mhd_assert (0 != line_len); 1889 mhd_assert ('\n' == read_buffer[line_len - 1]); 1890 next_line_char = read_buffer[line_len]; 1891 if ((' ' == next_line_char) 1892 || ('\t' == next_line_char)) 1893 { 1894 /* Folded line */ 1895 if (!allow_folded) 1896 { 1897 if (!process_footers) 1898 mhd_RESPOND_WITH_ERROR_STATIC (c, 1899 MHD_HTTP_STATUS_BAD_REQUEST, 1900 ERR_RSP_OBS_FOLD); 1901 else 1902 mhd_RESPOND_WITH_ERROR_STATIC (c, 1903 MHD_HTTP_STATUS_BAD_REQUEST, 1904 ERR_RSP_OBS_FOLD_FOOTER); 1905 1906 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1907 } 1908 /* Replace CRLF (or bare LF) character(s) with space characters. 1909 See RFC 9112, Section 5.2-4 */ 1910 read_buffer[p] = ' '; 1911 if ('\r' == chr) 1912 read_buffer[p + 1] = ' '; 1913 continue; /* Re-start processing of the current character */ 1914 } 1915 else 1916 { 1917 /* It is not a folded line, it's the real end of the non-empty line */ 1918 bool skip_line = false; 1919 mhd_assert (0 != p); 1920 if (c->rq.hdrs.hdr.starts_with_ws) 1921 { 1922 /* This is the first line and it starts with whitespace. This line 1923 must be discarded completely. 1924 See RFC 9112, Section 2.2-8 */ 1925 mhd_assert (allow_wsp_at_start); 1926 1927 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FIRST_HEADER_LINE_SPACE_PREFIXED, 1928 "Whitespace-prefixed first header line " \ 1929 "has been skipped."); 1930 skip_line = true; 1931 } 1932 else if (!c->rq.hdrs.hdr.name_end_found) 1933 { 1934 if (!allow_line_without_colon) 1935 { 1936 if (!process_footers) 1937 mhd_RESPOND_WITH_ERROR_STATIC (c, 1938 MHD_HTTP_STATUS_BAD_REQUEST, 1939 ERR_RSP_HEADER_WITHOUT_COLON); 1940 else 1941 mhd_RESPOND_WITH_ERROR_STATIC (c, 1942 MHD_HTTP_STATUS_BAD_REQUEST, 1943 ERR_RSP_FOOTER_WITHOUT_COLON); 1944 1945 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1946 } 1947 /* Skip broken line completely */ 1948 c->rq.skipped_broken_lines++; 1949 skip_line = true; 1950 } 1951 if (skip_line) 1952 { 1953 /* Skip the entire line */ 1954 c->read_buffer += line_len; 1955 c->read_buffer_offset -= line_len; 1956 c->read_buffer_size -= line_len; 1957 p = 0; 1958 /* Reset processing state */ 1959 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr)); 1960 /* Start processing of the next line */ 1961 continue; 1962 } 1963 else 1964 { 1965 /* This line should be valid header line */ 1966 size_t value_len; 1967 mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name); 1968 1969 hdr_name->cstr = read_buffer + 0; /* The name always starts at the first character */ 1970 hdr_name->len = c->rq.hdrs.hdr.name_len; 1971 mhd_assert (0 == hdr_name->cstr[hdr_name->len]); 1972 1973 if (0 == c->rq.hdrs.hdr.value_start) 1974 { 1975 c->rq.hdrs.hdr.value_start = p; 1976 read_buffer[p] = 0; 1977 value_len = 0; 1978 } 1979 else if (0 != c->rq.hdrs.hdr.ws_start) 1980 { 1981 mhd_assert (p > c->rq.hdrs.hdr.ws_start); 1982 mhd_assert (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start); 1983 read_buffer[c->rq.hdrs.hdr.ws_start] = 0; 1984 value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start; 1985 } 1986 else 1987 { 1988 mhd_assert (p > c->rq.hdrs.hdr.ws_start); 1989 read_buffer[p] = 0; 1990 value_len = p - c->rq.hdrs.hdr.value_start; 1991 } 1992 hdr_value->cstr = read_buffer + c->rq.hdrs.hdr.value_start; 1993 hdr_value->len = value_len; 1994 mhd_assert (0 == hdr_value->cstr[hdr_value->len]); 1995 /* Consume the entire line */ 1996 c->read_buffer += line_len; 1997 c->read_buffer_offset -= line_len; 1998 c->read_buffer_size -= line_len; 1999 return MHD_HDR_LINE_READING_GOT_HEADER; 2000 } 2001 } 2002 } 2003 else if ((' ' == chr) || ('\t' == chr)) 2004 { 2005 if (0 == p) 2006 { 2007 if (!allow_wsp_at_start) 2008 { 2009 if (!process_footers) 2010 mhd_RESPOND_WITH_ERROR_STATIC (c, 2011 MHD_HTTP_STATUS_BAD_REQUEST, 2012 ERR_RSP_WSP_BEFORE_HEADER); 2013 else 2014 mhd_RESPOND_WITH_ERROR_STATIC (c, 2015 MHD_HTTP_STATUS_BAD_REQUEST, 2016 ERR_RSP_WSP_BEFORE_FOOTER); 2017 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2018 } 2019 c->rq.hdrs.hdr.starts_with_ws = true; 2020 } 2021 else if ((!c->rq.hdrs.hdr.name_end_found) 2022 && (!c->rq.hdrs.hdr.starts_with_ws)) 2023 { 2024 /* Whitespace in header name / between header name and colon */ 2025 if (allow_wsp_in_name || allow_wsp_before_colon) 2026 { 2027 if (0 == c->rq.hdrs.hdr.ws_start) 2028 c->rq.hdrs.hdr.ws_start = p; 2029 } 2030 else 2031 { 2032 if (!process_footers) 2033 mhd_RESPOND_WITH_ERROR_STATIC (c, 2034 MHD_HTTP_STATUS_BAD_REQUEST, 2035 ERR_RSP_WSP_IN_HEADER_NAME); 2036 else 2037 mhd_RESPOND_WITH_ERROR_STATIC (c, 2038 MHD_HTTP_STATUS_BAD_REQUEST, 2039 ERR_RSP_WSP_IN_FOOTER_NAME); 2040 2041 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2042 } 2043 } 2044 else 2045 { 2046 /* Whitespace before/inside/after header (field) value */ 2047 if (0 == c->rq.hdrs.hdr.ws_start) 2048 c->rq.hdrs.hdr.ws_start = p; 2049 } 2050 } 2051 else if (0 == chr) 2052 { 2053 if (!nul_as_sp) 2054 { 2055 if (!process_footers) 2056 mhd_RESPOND_WITH_ERROR_STATIC (c, 2057 MHD_HTTP_STATUS_BAD_REQUEST, 2058 ERR_RSP_INVALID_CHR_IN_HEADER); 2059 else 2060 mhd_RESPOND_WITH_ERROR_STATIC (c, 2061 MHD_HTTP_STATUS_BAD_REQUEST, 2062 ERR_RSP_INVALID_CHR_IN_FOOTER); 2063 2064 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2065 } 2066 read_buffer[p] = ' '; 2067 continue; /* Re-start processing of the current character */ 2068 } 2069 else 2070 { 2071 /* Not a whitespace, not the end of the header line */ 2072 mhd_assert ('\r' != chr); 2073 mhd_assert ('\n' != chr); 2074 mhd_assert ('\0' != chr); 2075 if ((!c->rq.hdrs.hdr.name_end_found) 2076 && (!c->rq.hdrs.hdr.starts_with_ws)) 2077 { 2078 /* Processing the header (field) name */ 2079 if (':' == chr) 2080 { 2081 if (0 == c->rq.hdrs.hdr.ws_start) 2082 c->rq.hdrs.hdr.name_len = p; 2083 else 2084 { 2085 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon); 2086 if (!allow_wsp_before_colon) 2087 { 2088 if (!process_footers) 2089 mhd_RESPOND_WITH_ERROR_STATIC (c, 2090 MHD_HTTP_STATUS_BAD_REQUEST, 2091 ERR_RSP_WSP_IN_HEADER_NAME); 2092 else 2093 mhd_RESPOND_WITH_ERROR_STATIC (c, 2094 MHD_HTTP_STATUS_BAD_REQUEST, 2095 ERR_RSP_WSP_IN_FOOTER_NAME); 2096 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2097 } 2098 c->rq.hdrs.hdr.name_len = c->rq.hdrs.hdr.ws_start; 2099 #ifndef MHD_FAVOR_SMALL_CODE 2100 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */ 2101 #endif /* ! MHD_FAVOR_SMALL_CODE */ 2102 } 2103 if ((0 == c->rq.hdrs.hdr.name_len) && !allow_empty_name) 2104 { 2105 if (!process_footers) 2106 mhd_RESPOND_WITH_ERROR_STATIC (c, 2107 MHD_HTTP_STATUS_BAD_REQUEST, 2108 ERR_RSP_EMPTY_HEADER_NAME); 2109 else 2110 mhd_RESPOND_WITH_ERROR_STATIC (c, 2111 MHD_HTTP_STATUS_BAD_REQUEST, 2112 ERR_RSP_EMPTY_FOOTER_NAME); 2113 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2114 } 2115 c->rq.hdrs.hdr.name_end_found = true; 2116 read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */ 2117 } 2118 else 2119 { 2120 if (0 != c->rq.hdrs.hdr.ws_start) 2121 { 2122 /* End of the whitespace in header (field) name */ 2123 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon); 2124 if (!allow_wsp_in_name) 2125 { 2126 if (!process_footers) 2127 mhd_RESPOND_WITH_ERROR_STATIC (c, 2128 MHD_HTTP_STATUS_BAD_REQUEST, 2129 ERR_RSP_WSP_IN_HEADER_NAME); 2130 else 2131 mhd_RESPOND_WITH_ERROR_STATIC (c, 2132 MHD_HTTP_STATUS_BAD_REQUEST, 2133 ERR_RSP_WSP_IN_FOOTER_NAME); 2134 2135 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2136 } 2137 #ifndef MHD_FAVOR_SMALL_CODE 2138 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */ 2139 #endif /* ! MHD_FAVOR_SMALL_CODE */ 2140 } 2141 } 2142 } 2143 else 2144 { 2145 /* Processing the header (field) value */ 2146 if (0 == c->rq.hdrs.hdr.value_start) 2147 c->rq.hdrs.hdr.value_start = p; 2148 #ifndef MHD_FAVOR_SMALL_CODE 2149 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */ 2150 #endif /* ! MHD_FAVOR_SMALL_CODE */ 2151 } 2152 #ifdef MHD_FAVOR_SMALL_CODE 2153 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */ 2154 #endif /* MHD_FAVOR_SMALL_CODE */ 2155 } 2156 p++; 2157 } 2158 c->rq.hdrs.hdr.proc_pos = p; 2159 return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */ 2160 } 2161 2162 2163 /** 2164 * Reset request header processing state. 2165 * 2166 * This function resets the processing state before processing the next header 2167 * (or footer) line. 2168 * @param c the connection to process 2169 */ 2170 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 2171 mhd_stream_reset_rq_hdr_proc_state (struct MHD_Connection *c) 2172 { 2173 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr)); 2174 } 2175 2176 2177 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 2178 mhd_stream_get_request_headers (struct MHD_Connection *restrict c, 2179 bool process_footers) 2180 { 2181 do 2182 { 2183 struct MHD_String hdr_name; 2184 struct MHD_String hdr_value; 2185 enum mhd_HdrLineReadRes res; 2186 2187 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2188 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 2189 c->stage); 2190 2191 #ifndef NDEBUG 2192 hdr_name.cstr = NULL; 2193 hdr_value.cstr = NULL; 2194 #endif /* ! NDEBUG */ 2195 res = get_req_header (c, process_footers, &hdr_name, &hdr_value); 2196 if (MHD_HDR_LINE_READING_GOT_HEADER == res) 2197 { 2198 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2199 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 2200 c->stage); 2201 mhd_assert (NULL != hdr_name.cstr); 2202 mhd_assert (NULL != hdr_value.cstr); 2203 /* Values must be zero-terminated and must not have binary zeros */ 2204 mhd_assert (strlen (hdr_name.cstr) == hdr_name.len); 2205 mhd_assert (strlen (hdr_value.cstr) == hdr_value.len); 2206 /* Values must not have whitespaces at the start or at the end */ 2207 mhd_assert ((hdr_name.len == 0) || (hdr_name.cstr[0] != ' ')); 2208 mhd_assert ((hdr_name.len == 0) || (hdr_name.cstr[0] != '\t')); 2209 mhd_assert ((hdr_name.len == 0) \ 2210 || (hdr_name.cstr[hdr_name.len - 1] != ' ')); 2211 mhd_assert ((hdr_name.len == 0) \ 2212 || (hdr_name.cstr[hdr_name.len - 1] != '\t')); 2213 mhd_assert ((hdr_value.len == 0) || (hdr_value.cstr[0] != ' ')); 2214 mhd_assert ((hdr_value.len == 0) || (hdr_value.cstr[0] != '\t')); 2215 mhd_assert ((hdr_value.len == 0) \ 2216 || (hdr_value.cstr[hdr_value.len - 1] != ' ')); 2217 mhd_assert ((hdr_value.len == 0) \ 2218 || (hdr_value.cstr[hdr_value.len - 1] != '\t')); 2219 2220 if (!mhd_stream_add_field (&(c->h1_stream), 2221 process_footers ? 2222 MHD_VK_TRAILER : MHD_VK_HEADER, 2223 &hdr_name, 2224 &hdr_value)) 2225 { 2226 size_t add_element_size; 2227 2228 mhd_assert (hdr_name.cstr < hdr_value.cstr); 2229 2230 if (!process_footers) 2231 mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_REQ, \ 2232 "Failed to allocate memory in the connection memory " \ 2233 "pool to store header."); 2234 else 2235 mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_REQ, \ 2236 "Failed to allocate memory in the connection memory " \ 2237 "pool to store footer."); 2238 2239 add_element_size = hdr_value.len 2240 + (size_t)(hdr_value.cstr - hdr_name.cstr); 2241 2242 if (!process_footers) 2243 handle_req_headers_no_space (c, hdr_name.cstr, add_element_size); 2244 else 2245 handle_req_footers_no_space (c, hdr_name.cstr, add_element_size); 2246 2247 mhd_assert (mhd_HTTP_STAGE_FULL_REQ_RECEIVED < c->stage); 2248 return true; 2249 } 2250 /* Reset processing state */ 2251 mhd_stream_reset_rq_hdr_proc_state (c); 2252 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2253 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 2254 c->stage); 2255 /* Read the next header (field) line */ 2256 continue; 2257 } 2258 else if (MHD_HDR_LINE_READING_NEED_MORE_DATA == res) 2259 { 2260 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2261 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 2262 c->stage); 2263 return false; 2264 } 2265 else if (MHD_HDR_LINE_READING_DATA_ERROR == res) 2266 { 2267 mhd_assert ((process_footers ? \ 2268 mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2269 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) < c->stage); 2270 mhd_assert (c->stop_with_error); 2271 mhd_assert (c->discard_request); 2272 return true; 2273 } 2274 mhd_assert (MHD_HDR_LINE_READING_GOT_END_OF_HEADER == res); 2275 break; 2276 } while (1); 2277 2278 if (1 == c->rq.num_cr_sp_replaced) 2279 { 2280 if (!process_footers) 2281 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2282 "One bare CR character has been replaced with space " \ 2283 "in the request line or in the request headers."); 2284 else 2285 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_CR_REPLACED, \ 2286 "One bare CR character has been replaced with space " \ 2287 "in the request footers."); 2288 } 2289 else if (0 != c->rq.num_cr_sp_replaced) 2290 { 2291 if (!process_footers) 2292 mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2293 mhd_LOG_FMT ("%" PRIuFAST64 " bare CR characters have " \ 2294 "been replaced with spaces in the request " \ 2295 "line and/or in the request headers."), \ 2296 (uint_fast64_t)c->rq.num_cr_sp_replaced); 2297 else 2298 mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2299 mhd_LOG_FMT ("%" PRIuFAST64 " bare CR characters have " \ 2300 "been replaced with spaces in the request " \ 2301 "footers."), \ 2302 (uint_fast64_t)c->rq.num_cr_sp_replaced); 2303 2304 2305 } 2306 if (1 == c->rq.skipped_broken_lines) 2307 { 2308 if (!process_footers) 2309 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_LINE_NO_COLON, \ 2310 "One header line without colon has been skipped."); 2311 else 2312 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_LINE_NO_COLON, \ 2313 "One footer line without colon has been skipped."); 2314 } 2315 else if (0 != c->rq.skipped_broken_lines) 2316 { 2317 if (!process_footers) 2318 mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2319 mhd_LOG_FMT ("%" PRIu64 " header lines without colons " 2320 "have been skipped."), 2321 (uint_fast64_t)c->rq.skipped_broken_lines); 2322 else 2323 mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2324 mhd_LOG_FMT ("%" PRIu64 " footer lines without colons " 2325 "have been skipped."), 2326 (uint_fast64_t)c->rq.skipped_broken_lines); 2327 } 2328 2329 mhd_assert (c->rq.method.cstr < c->read_buffer); 2330 if (!process_footers) 2331 { 2332 c->rq.header_size = (size_t)(c->read_buffer - c->rq.method.cstr); 2333 mhd_assert (NULL != c->rq.field_lines.start); 2334 c->rq.field_lines.size = 2335 (size_t)((c->read_buffer - c->rq.field_lines.start) - 1); 2336 if ('\r' == *(c->read_buffer - 2)) 2337 c->rq.field_lines.size--; 2338 c->stage = mhd_HTTP_STAGE_HEADERS_RECEIVED; 2339 2340 if (mhd_BUF_INC_SIZE > c->read_buffer_size) 2341 { 2342 /* Try to re-use some of the last bytes of the request header */ 2343 /* Do this only if space in the read buffer is limited AND 2344 amount of read ahead data is small. */ 2345 /** 2346 * The position of the terminating NUL after the last character of 2347 * the last header element. 2348 */ 2349 const char *last_elmnt_end; 2350 size_t shift_back_size; 2351 struct mhd_RequestField *header; 2352 header = mhd_DLINKEDL_GET_LAST (&(c->rq), fields); 2353 if (NULL != header) 2354 last_elmnt_end = 2355 header->field.nv.value.cstr + header->field.nv.value.len; 2356 else 2357 last_elmnt_end = c->rq.version + HTTP_VER_LEN; 2358 mhd_assert ((last_elmnt_end + 1) < c->read_buffer); 2359 shift_back_size = (size_t)(c->read_buffer - (last_elmnt_end + 1)); 2360 if (0 != c->read_buffer_offset) 2361 memmove (c->read_buffer - shift_back_size, 2362 c->read_buffer, 2363 c->read_buffer_offset); 2364 c->read_buffer -= shift_back_size; 2365 c->read_buffer_size += shift_back_size; 2366 } 2367 } 2368 else 2369 c->stage = mhd_HTTP_STAGE_FOOTERS_RECEIVED; 2370 2371 return true; 2372 } 2373 2374 2375 #ifdef MHD_SUPPORT_COOKIES 2376 2377 /** 2378 * Cookie parsing result 2379 */ 2380 enum mhd_ParseCookie 2381 { 2382 MHD_PARSE_COOKIE_OK_LAX = 2 /**< Cookies parsed, but workarounds used */ 2383 , 2384 MHD_PARSE_COOKIE_OK = 1 /**< Success or no cookies in headers */ 2385 , 2386 MHD_PARSE_COOKIE_NO_MEMORY = 0 /**< Not enough memory in the pool */ 2387 , 2388 MHD_PARSE_COOKIE_MALFORMED = -1 /**< Invalid cookie header */ 2389 }; 2390 2391 2392 /** 2393 * Parse the cookies string (see RFC 6265). 2394 * 2395 * Try to parse the cookies string even if it is not strictly formed 2396 * as specified by RFC 6265. 2397 * 2398 * @param str_len the size of the @a str, not including mandatory 2399 * zero-termination 2400 * @param str the string to parse, without leading whitespaces 2401 * @param strictness the protocol strictness 2402 * @param s the stream to process 2403 * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise 2404 */ 2405 static MHD_FN_PAR_NONNULL_ALL_ 2406 MHD_FN_PAR_CSTR_ (2) 2407 MHD_FN_PAR_INOUT_SIZE_ (2, 1) enum mhd_ParseCookie 2408 parse_cookies_string (const size_t str_len, 2409 char *restrict str, 2410 enum MHD_ProtocolStrictLevel strictness, 2411 struct MHD_Stream *restrict s) 2412 { 2413 size_t i; 2414 bool non_strict; 2415 /* Skip extra whitespaces and empty cookies */ 2416 const bool allow_wsp_empty = (MHD_PSL_DEFAULT >= strictness); 2417 /* Allow whitespaces around '=' character */ 2418 const bool wsp_around_eq = (MHD_PSL_EXTRA_PERMISSIVE >= strictness); 2419 /* Allow whitespaces in quoted cookie value */ 2420 const bool wsp_in_quoted = (MHD_PSL_VERY_PERMISSIVE >= strictness); 2421 /* Allow tab as space after semicolon between cookies */ 2422 const bool tab_as_sp = (MHD_PSL_DEFAULT >= strictness); 2423 /* Allow no space after semicolon between cookies */ 2424 const bool allow_no_space = (MHD_PSL_DEFAULT >= strictness); 2425 2426 non_strict = false; 2427 i = 0; 2428 while (i < str_len) 2429 { 2430 size_t name_start; 2431 size_t name_len; 2432 size_t value_start; 2433 size_t value_len; 2434 bool val_quoted; 2435 /* Skip any whitespaces and empty cookies */ 2436 while (' ' == str[i] || '\t' == str[i] || ';' == str[i]) 2437 { 2438 if (!allow_wsp_empty) 2439 return MHD_PARSE_COOKIE_MALFORMED; 2440 non_strict = true; 2441 i++; 2442 if (i == str_len) 2443 return non_strict ? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK; 2444 } 2445 /* 'i' must point to the first char of cookie-name */ 2446 name_start = i; 2447 /* Find the end of the cookie-name */ 2448 do 2449 { 2450 const char l = str[i]; 2451 if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) 2452 || (';' == l) || (0 == l)) 2453 break; 2454 } while (str_len > ++i); 2455 name_len = i - name_start; 2456 /* Skip any whitespaces */ 2457 while (str_len > i && (' ' == str[i] || '\t' == str[i])) 2458 { 2459 if (!wsp_around_eq) 2460 return MHD_PARSE_COOKIE_MALFORMED; 2461 non_strict = true; 2462 i++; 2463 } 2464 if ((str_len == i) || ('=' != str[i]) || (0 == name_len)) 2465 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */ 2466 /* 'i' must point to the '=' char */ 2467 mhd_assert ('=' == str[i]); 2468 i++; 2469 /* Skip any whitespaces */ 2470 while (str_len > i && (' ' == str[i] || '\t' == str[i])) 2471 { 2472 if (!wsp_around_eq) 2473 return MHD_PARSE_COOKIE_MALFORMED; 2474 non_strict = true; 2475 i++; 2476 } 2477 /* 'i' must point to the first char of cookie-value */ 2478 if (str_len == i) 2479 { 2480 value_start = 0; 2481 value_len = 0; 2482 # ifndef NDEBUG 2483 val_quoted = false; /* This assignment used in assert */ 2484 # endif 2485 } 2486 else 2487 { 2488 bool valid_cookie; 2489 val_quoted = ('"' == str[i]); 2490 if (val_quoted) 2491 i++; 2492 value_start = i; 2493 /* Find the end of the cookie-value */ 2494 while (str_len > i) 2495 { 2496 const char l = str[i]; 2497 if ((';' == l) || ('"' == l) || (',' == l) || ('\\' == l) || (0 == l)) 2498 break; 2499 if ((' ' == l) || ('\t' == l)) 2500 { 2501 if (!val_quoted) 2502 break; 2503 if (!wsp_in_quoted) 2504 return MHD_PARSE_COOKIE_MALFORMED; 2505 non_strict = true; 2506 } 2507 i++; 2508 } 2509 value_len = i - value_start; 2510 if (val_quoted) 2511 { 2512 if ((str_len == i) || ('"' != str[i])) 2513 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */ 2514 i++; 2515 } 2516 /* Skip any whitespaces */ 2517 if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i]))) 2518 { 2519 do 2520 { 2521 i++; 2522 } while (str_len > i && (' ' == str[i] || '\t' == str[i])); 2523 /* Whitespace at the end? */ 2524 if (str_len > i) 2525 { 2526 if (!allow_wsp_empty) 2527 return MHD_PARSE_COOKIE_MALFORMED; 2528 non_strict = true; 2529 } 2530 } 2531 if (str_len == i) 2532 valid_cookie = true; 2533 else if (';' == str[i]) 2534 valid_cookie = true; 2535 else 2536 valid_cookie = false; 2537 2538 if (!valid_cookie) 2539 return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */ 2540 } 2541 2542 mhd_ASSUME (0u != name_len); 2543 mhd_ASSUME (str_len > name_start + name_len); 2544 str[name_start + name_len] = '\0'; 2545 2546 if (0 != value_len) 2547 { 2548 struct MHD_String name; 2549 struct MHD_String value; 2550 mhd_ASSUME (str_len >= value_start + value_len); 2551 name.cstr = str + name_start; 2552 name.len = name_len; 2553 if (value_start + value_len < str_len) /* Do not write outside allowed area */ 2554 str[value_start + value_len] = '\0'; /* Zero-terminate the value */ 2555 value.cstr = str + value_start; 2556 value.len = value_len; 2557 if (!mhd_stream_add_field (s, 2558 MHD_VK_COOKIE, 2559 &name, 2560 &value)) 2561 return MHD_PARSE_COOKIE_NO_MEMORY; 2562 } 2563 else 2564 { 2565 struct MHD_String name; 2566 struct MHD_String value; 2567 name.cstr = str + name_start; 2568 name.len = name_len; 2569 value.cstr = ""; 2570 value.len = 0; 2571 if (!mhd_stream_add_field (s, 2572 MHD_VK_COOKIE, 2573 &name, 2574 &value)) 2575 return MHD_PARSE_COOKIE_NO_MEMORY; 2576 } 2577 if (str_len > i) 2578 { 2579 mhd_assert (0 == str[i] || ';' == str[i]); 2580 mhd_assert (!val_quoted || ';' == str[i]); 2581 mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len); 2582 i++; 2583 if (str_len == i) 2584 { /* No next cookie after semicolon */ 2585 if (!allow_wsp_empty) 2586 return MHD_PARSE_COOKIE_MALFORMED; 2587 non_strict = true; 2588 } 2589 else if (' ' != str[i]) 2590 {/* No space after semicolon */ 2591 if (('\t' == str[i]) && tab_as_sp) 2592 i++; 2593 else if (!allow_no_space) 2594 return MHD_PARSE_COOKIE_MALFORMED; 2595 non_strict = true; 2596 } 2597 else 2598 { 2599 i++; 2600 if (str_len == i) 2601 { 2602 if (!allow_wsp_empty) 2603 return MHD_PARSE_COOKIE_MALFORMED; 2604 non_strict = true; 2605 } 2606 } 2607 } 2608 } 2609 return non_strict ? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK; 2610 } 2611 2612 2613 /** 2614 * Parse the cookie header (see RFC 6265). 2615 * 2616 * @param connection connection to parse header of 2617 * @param cookie_val the value of the "Cookie:" header 2618 * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise 2619 */ 2620 static enum mhd_ParseCookie 2621 parse_cookie_header (struct MHD_Connection *restrict connection, 2622 struct MHD_StringNullable *restrict cookie_val) 2623 { 2624 char *cpy; 2625 size_t i; 2626 enum mhd_ParseCookie parse_res; 2627 struct mhd_RequestField *const saved_tail = 2628 connection->rq.fields.last; // FIXME: a better way? 2629 const bool allow_partially_correct_cookie = 2630 (1 >= connection->daemon->req_cfg.strictness); 2631 2632 if (NULL == cookie_val) 2633 return MHD_PARSE_COOKIE_OK; 2634 if (0 == cookie_val->len) 2635 return MHD_PARSE_COOKIE_OK; 2636 2637 cpy = (char *)mhd_stream_alloc_memory (connection, 2638 cookie_val->len + 1); 2639 if (NULL == cpy) 2640 parse_res = MHD_PARSE_COOKIE_NO_MEMORY; 2641 else 2642 { 2643 memcpy (cpy, 2644 cookie_val->cstr, 2645 cookie_val->len + 1); 2646 mhd_assert (0 == cpy[cookie_val->len]); 2647 2648 /* Must not have initial whitespaces */ 2649 mhd_assert (' ' != cpy[0]); 2650 mhd_assert ('\t' != cpy[0]); 2651 2652 i = 0; 2653 parse_res = parse_cookies_string (cookie_val->len - i, 2654 cpy + i, 2655 connection->daemon->req_cfg.strictness, 2656 &(connection->h1_stream)); 2657 } 2658 2659 switch (parse_res) 2660 { 2661 case MHD_PARSE_COOKIE_OK: 2662 break; 2663 case MHD_PARSE_COOKIE_OK_LAX: 2664 if (saved_tail != connection->rq.fields.last) 2665 mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_PARSED_NOT_COMPLIANT, \ 2666 "The Cookie header has been parsed, but it is not " 2667 "fully compliant with specifications."); 2668 break; 2669 case MHD_PARSE_COOKIE_MALFORMED: 2670 if (saved_tail != connection->rq.fields.last) // FIXME: a better way? 2671 { 2672 if (!allow_partially_correct_cookie) 2673 { 2674 /* Remove extracted values from partially broken cookie */ 2675 /* Memory remains allocated until the end of the request processing */ 2676 connection->rq.fields.last = saved_tail; // FIXME: a better way? 2677 saved_tail->fields.next = NULL; // FIXME: a better way? 2678 mhd_LOG_MSG ( \ 2679 connection->daemon, MHD_SC_REQ_COOKIE_IGNORED_NOT_COMPLIANT, \ 2680 "The Cookie header is ignored as it contains malformed data."); 2681 } 2682 else 2683 mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_PARSED_PARTIALLY, \ 2684 "The Cookie header has been only partially parsed " \ 2685 "as it contains malformed data."); 2686 } 2687 else 2688 mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_INVALID, 2689 "The Cookie header has malformed data."); 2690 break; 2691 case MHD_PARSE_COOKIE_NO_MEMORY: 2692 mhd_LOG_MSG (connection->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_COOKIE, 2693 "Not enough memory in the connection pool to " 2694 "parse client cookies!\n"); 2695 break; 2696 default: 2697 mhd_UNREACHABLE (); 2698 break; 2699 } 2700 2701 return parse_res; 2702 } 2703 2704 2705 /** 2706 * Send error reply when receive buffer space exhausted while receiving or 2707 * storing the request headers 2708 * @param c the connection to handle 2709 */ 2710 mhd_static_inline void 2711 handle_req_cookie_no_space (struct MHD_Connection *restrict c) 2712 { 2713 unsigned int err_code; 2714 2715 err_code = mhd_stream_get_no_space_err_status_code (c, 2716 MHD_PROC_RECV_COOKIE, 2717 0, 2718 NULL); 2719 mhd_RESPOND_WITH_ERROR_STATIC (c, 2720 err_code, 2721 ERR_RSP_REQUEST_HEADER_TOO_BIG); 2722 } 2723 2724 2725 #endif /* MHD_SUPPORT_COOKIES */ 2726 2727 2728 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 2729 mhd_stream_parse_request_headers (struct MHD_Connection *restrict c) 2730 { 2731 bool has_host; 2732 bool has_trenc; 2733 bool has_cntnlen; 2734 bool has_keepalive; 2735 struct mhd_RequestField *f; 2736 2737 /* The presence of the request body is indicated by "Content-Length:" or 2738 "Transfer-Encoding:" request headers. 2739 Unless one of these two headers is used, the request has no request body. 2740 See RFC9112, Section 6, paragraph 4. */ 2741 c->rq.have_chunked_upload = false; 2742 c->rq.cntn.cntn_size = 0; 2743 2744 has_host = false; 2745 has_trenc = false; 2746 has_cntnlen = false; 2747 has_keepalive = true; 2748 2749 for (f = mhd_DLINKEDL_GET_FIRST (&(c->rq), fields); 2750 NULL != f; 2751 f = mhd_DLINKEDL_GET_NEXT (f, fields)) 2752 { 2753 if (MHD_VK_HEADER != f->field.kind) 2754 continue; 2755 2756 /* "Host:" */ 2757 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_HOST, 2758 f->field.nv.name.cstr, 2759 f->field.nv.name.len)) 2760 { 2761 if ((has_host) 2762 && (-3 < c->daemon->req_cfg.strictness)) 2763 { 2764 mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_SEVERAL, \ 2765 "Received request with more than one 'Host' header."); 2766 mhd_RESPOND_WITH_ERROR_STATIC (c, 2767 MHD_HTTP_STATUS_BAD_REQUEST, 2768 ERR_RSP_REQUEST_HAS_SEVERAL_HOSTS); 2769 return; 2770 } 2771 if ((0u == f->field.nv.value.len) 2772 && (-3 < c->daemon->req_cfg.strictness)) 2773 { 2774 mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_MALFORMED, \ 2775 "Received request with empty 'Host' header."); 2776 mhd_RESPOND_WITH_ERROR_STATIC (c, 2777 MHD_HTTP_STATUS_BAD_REQUEST, 2778 ERR_RSP_REQUEST_HAS_MALFORMED_HOST); 2779 return; 2780 } 2781 has_host = true; 2782 continue; 2783 } 2784 2785 /* "Content-Length:" */ 2786 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONTENT_LENGTH, 2787 f->field.nv.name.cstr, 2788 f->field.nv.name.len)) 2789 { 2790 size_t num_digits; 2791 uint_fast64_t cntn_size; 2792 2793 num_digits = mhd_str_to_uint64_n (f->field.nv.value.cstr, 2794 f->field.nv.value.len, 2795 &cntn_size); 2796 if (((0 == num_digits) 2797 && (0 != f->field.nv.value.len) 2798 && ('9' >= f->field.nv.value.cstr[0]) 2799 && ('0' <= f->field.nv.value.cstr[0])) 2800 || (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size)) 2801 { 2802 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_TOO_LARGE, \ 2803 "Too large value of 'Content-Length' header. " \ 2804 "Closing connection."); 2805 mhd_RESPOND_WITH_ERROR_STATIC (c, \ 2806 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, \ 2807 ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE); 2808 return; 2809 } 2810 else if ((f->field.nv.value.len != num_digits) 2811 || (0 == num_digits)) 2812 { 2813 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_MALFORMED, \ 2814 "Failed to parse 'Content-Length' header. " \ 2815 "Closing connection."); 2816 mhd_RESPOND_WITH_ERROR_STATIC (c, \ 2817 MHD_HTTP_STATUS_BAD_REQUEST, \ 2818 ERR_RSP_REQUEST_CONTENTLENGTH_MALFORMED); 2819 return; 2820 } 2821 2822 if (has_cntnlen) 2823 { 2824 bool send_err; 2825 send_err = false; 2826 if (c->rq.cntn.cntn_size == cntn_size) 2827 { 2828 if (0 < c->daemon->req_cfg.strictness) 2829 { 2830 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_SEVERAL_SAME, \ 2831 "Received request with more than one " \ 2832 "'Content-Length' header with the same value."); 2833 send_err = true; 2834 } 2835 } 2836 else 2837 { 2838 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_SEVERAL_DIFFERENT, \ 2839 "Received request with more than one " \ 2840 "'Content-Length' header with conflicting values."); 2841 send_err = true; 2842 } 2843 2844 if (send_err) 2845 { 2846 mhd_RESPOND_WITH_ERROR_STATIC ( \ 2847 c, \ 2848 MHD_HTTP_STATUS_BAD_REQUEST, \ 2849 ERR_RSP_REQUEST_CONTENTLENGTH_SEVERAL); 2850 return; 2851 } 2852 } 2853 mhd_assert ((0 == c->rq.cntn.cntn_size) \ 2854 || (c->rq.cntn.cntn_size == cntn_size)); 2855 c->rq.cntn.cntn_size = cntn_size; 2856 has_cntnlen = true; 2857 continue; 2858 } 2859 2860 /* "Connection:" */ 2861 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONNECTION, 2862 f->field.nv.name.cstr, 2863 f->field.nv.name.len)) 2864 { 2865 if (mhd_str_has_token_caseless (f->field.nv.value.cstr, // TODO: compare as size string 2866 "close", 2867 mhd_SSTR_LEN ("close"))) 2868 { 2869 mhd_assert (mhd_CONN_MUST_UPGRADE != c->conn_reuse); 2870 c->conn_reuse = mhd_CONN_MUST_CLOSE; 2871 } 2872 else if ((MHD_HTTP_VERSION_1_0 == c->rq.http_ver) 2873 && (mhd_CONN_MUST_CLOSE != c->conn_reuse)) 2874 { 2875 if (mhd_str_has_token_caseless (f->field.nv.value.cstr, // TODO: compare as size string 2876 "keep-alive", 2877 mhd_SSTR_LEN ("keep-alive"))) 2878 has_keepalive = true; 2879 } 2880 2881 continue; 2882 } 2883 2884 /* "Transfer-Encoding:" */ 2885 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_TRANSFER_ENCODING, 2886 f->field.nv.name.cstr, 2887 f->field.nv.name.len)) 2888 { 2889 if (mhd_str_equal_caseless_n_st ("chunked", 2890 f->field.nv.value.cstr, 2891 f->field.nv.value.len)) 2892 { 2893 c->rq.have_chunked_upload = true; 2894 c->rq.cntn.cntn_size = MHD_SIZE_UNKNOWN; 2895 } 2896 else 2897 { 2898 mhd_LOG_MSG (c->daemon, MHD_SC_TRANSFER_ENCODING_UNSUPPORTED, \ 2899 "The 'Transfer-Encoding' used in request is " \ 2900 "unsupported or invalid."); 2901 mhd_RESPOND_WITH_ERROR_STATIC (c, 2902 MHD_HTTP_STATUS_BAD_REQUEST, 2903 ERR_RSP_UNSUPPORTED_TR_ENCODING); 2904 return; 2905 } 2906 has_trenc = true; 2907 continue; 2908 } 2909 2910 #ifdef MHD_SUPPORT_COOKIES 2911 /* "Cookie:" */ 2912 if ((!c->daemon->req_cfg.disable_cookies) 2913 && mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_COOKIE, 2914 f->field.nv.name.cstr, 2915 f->field.nv.name.len)) 2916 { 2917 if (MHD_PARSE_COOKIE_NO_MEMORY == 2918 parse_cookie_header (c, 2919 &(f->field.nv.value))) 2920 { 2921 handle_req_cookie_no_space (c); 2922 return; 2923 } 2924 continue; 2925 } 2926 #endif /* MHD_SUPPORT_COOKIES */ 2927 2928 /* "Expect: 100-continue" */ 2929 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_EXPECT, 2930 f->field.nv.name.cstr, 2931 f->field.nv.name.len)) 2932 { 2933 if (mhd_str_equal_caseless_n_st ("100-continue", 2934 f->field.nv.value.cstr, 2935 f->field.nv.value.len)) 2936 c->rq.have_expect_100 = true; 2937 else 2938 { 2939 if (0 < c->daemon->req_cfg.strictness) 2940 { 2941 mhd_LOG_MSG (c->daemon, MHD_SC_EXPECT_HEADER_VALUE_UNSUPPORTED, \ 2942 "The 'Expect' header value used in request is " \ 2943 "unsupported or invalid."); 2944 mhd_RESPOND_WITH_ERROR_STATIC (c, 2945 MHD_HTTP_STATUS_EXPECTATION_FAILED, 2946 ERR_RSP_UNSUPPORTED_EXPECT_HDR_VALUE); 2947 return; 2948 } 2949 } 2950 continue; 2951 } 2952 } 2953 2954 c->rq.cntn.cntn_present = (has_trenc || has_cntnlen); 2955 if (has_trenc && has_cntnlen) 2956 { 2957 if (0 < c->daemon->req_cfg.strictness) 2958 { 2959 mhd_RESPOND_WITH_ERROR_STATIC ( \ 2960 c, \ 2961 MHD_HTTP_STATUS_BAD_REQUEST, \ 2962 ERR_RSP_REQUEST_CNTNLENGTH_WITH_TR_ENCODING); 2963 return; 2964 } 2965 /* Must close connection after reply to prevent potential attack */ 2966 c->conn_reuse = mhd_CONN_MUST_CLOSE; 2967 c->rq.cntn.cntn_size = MHD_SIZE_UNKNOWN; 2968 mhd_assert (c->rq.have_chunked_upload); 2969 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_AND_TR_ENC, \ 2970 "The 'Content-Length' request header is ignored " \ 2971 "as chunked 'Transfer-Encoding' is used " \ 2972 "for this request."); 2973 } 2974 2975 if (MHD_HTTP_VERSION_IS_LIKE_11 (c->rq.http_ver)) 2976 { 2977 if ((!has_host) 2978 && (-3 < c->daemon->req_cfg.strictness)) 2979 { 2980 mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_MISSING, \ 2981 "Received HTTP/1.1 request without 'Host' header."); 2982 mhd_RESPOND_WITH_ERROR_STATIC (c, 2983 MHD_HTTP_STATUS_BAD_REQUEST, 2984 ERR_RSP_REQUEST_LACKS_HOST); 2985 return; 2986 } 2987 } 2988 else 2989 { 2990 if (!has_keepalive) 2991 c->conn_reuse = mhd_CONN_MUST_CLOSE; /* Do not re-use HTTP/1.0 connection by default */ 2992 if (has_trenc) 2993 c->conn_reuse = mhd_CONN_MUST_CLOSE; /* Framing could be incorrect */ 2994 } 2995 2996 c->stage = mhd_HTTP_STAGE_HEADERS_PROCESSED; 2997 return; 2998 } 2999 3000 3001 /** 3002 * Is "100 Continue" needed to be sent for current request? 3003 * 3004 * @param c the connection to check 3005 * @return false 100 CONTINUE is not needed, 3006 * true otherwise 3007 */ 3008 static MHD_FN_PAR_NONNULL_ALL_ bool 3009 need_100_continue (struct MHD_Connection *restrict c) 3010 { 3011 mhd_assert (MHD_HTTP_VERSION_IS_1X (c->rq.http_ver)); 3012 mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= c->stage); 3013 mhd_assert (mhd_HTTP_STAGE_BODY_RECEIVING > c->stage); 3014 3015 if (!c->rq.have_expect_100) 3016 return false; /* "100 Continue" has not been requested by the client */ 3017 3018 if (0 != c->read_buffer_offset) 3019 return false; /* Part of the content has been received already */ 3020 3021 if (0 == c->rq.cntn.cntn_size) 3022 return false; /* There is no content or zero-sized content for this request */ 3023 3024 if (MHD_HTTP_VERSION_1_0 == c->rq.http_ver) 3025 return false; /* '100 Continue' is not allowed for HTTP/1.0 */ 3026 3027 return true; 3028 } 3029 3030 3031 /** 3032 * Check whether special buffer is required to handle the upload content and 3033 * try to allocate if necessary. 3034 * Respond with error to the client if buffer cannot be allocated 3035 * @param c the connection to 3036 * @return true if succeed, 3037 * false if error response is set 3038 */ 3039 static MHD_FN_PAR_NONNULL_ALL_ bool 3040 check_and_alloc_buf_for_upload_processing (struct MHD_Connection *restrict c) 3041 { 3042 mhd_assert ((mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act) \ 3043 || (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act)); 3044 3045 if (c->rq.have_chunked_upload) 3046 return true; /* The size is unknown, buffers will be dynamically allocated 3047 and re-allocated */ 3048 mhd_assert (c->read_buffer_size > c->read_buffer_offset); 3049 #if 0 // TODO: support processing full response in the connection buffer 3050 if ((c->read_buffer_size - c->read_buffer_offset) >= 3051 c->rq.cntn.cntn_size) 3052 return true; /* No additional buffer needed */ 3053 #endif 3054 3055 if ((mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act) 3056 && (NULL == c->rq.app_act.head_act.data.upload.full.cb)) 3057 return true; /* data will be processed only incrementally */ 3058 3059 if (mhd_ACTION_UPLOAD != c->rq.app_act.head_act.act) 3060 { 3061 // TODO: add check for intermental-only POST processing */ 3062 mhd_assert (0 && "Not implemented yet"); 3063 return false; 3064 } 3065 3066 if ((c->rq.cntn.cntn_size > 3067 c->rq.app_act.head_act.data.upload.large_buffer_size) 3068 || !mhd_daemon_get_lbuf (c->daemon, 3069 (size_t)c->rq.cntn.cntn_size, 3070 &(c->rq.cntn.lbuf))) 3071 { 3072 if (NULL != c->rq.app_act.head_act.data.upload.inc.cb) 3073 { 3074 c->rq.app_act.head_act.data.upload.full.cb = NULL; 3075 return true; /* Data can be processed incrementally */ 3076 } 3077 3078 mhd_RESPOND_WITH_ERROR_STATIC (c, 3079 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, 3080 ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE); 3081 return false; 3082 } 3083 3084 return true; 3085 } 3086 3087 3088 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 3089 mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c) 3090 { 3091 struct MHD_Daemon *restrict d = c->daemon; 3092 struct MHD_String path; 3093 const struct MHD_Action *a; 3094 3095 mhd_assert (mhd_HTTP_METHOD_NO_METHOD != c->rq.http_mthd); 3096 mhd_assert (NULL == c->rp.response); 3097 3098 if (mhd_ACTION_NO_ACTION != c->rq.app_act.head_act.act) 3099 MHD_PANIC ("MHD_Action has been set already"); 3100 3101 path.cstr = c->rq.url; 3102 path.len = c->rq.url_len; 3103 3104 c->rq.app_aware = true; 3105 a = d->req_cfg.cb (d->req_cfg.cb_cls, 3106 &(c->rq), 3107 &path, 3108 (enum MHD_HTTP_Method)c->rq.http_mthd, 3109 c->rq.cntn.cntn_size); 3110 3111 if ((NULL != a) 3112 && (((&(c->rq.app_act.head_act) != a)) 3113 || !mhd_ACTION_IS_VALID (c->rq.app_act.head_act.act))) 3114 { 3115 mhd_LOG_MSG (d, MHD_SC_ACTION_INVALID, \ 3116 "Provided action is not a correct action generated " \ 3117 "for the current request."); 3118 /* Perform cleanup of the created but now unused action */ 3119 switch (c->rq.app_act.head_act.act) 3120 { 3121 case mhd_ACTION_RESPONSE: 3122 mhd_assert (NULL != c->rq.app_act.head_act.data.response); 3123 mhd_response_dec_use_count (c->rq.app_act.head_act.data.response); 3124 break; 3125 case mhd_ACTION_UPLOAD: 3126 case mhd_ACTION_SUSPEND: 3127 /* No cleanup needed */ 3128 break; 3129 #ifdef MHD_SUPPORT_POST_PARSER 3130 case mhd_ACTION_POST_PARSE: 3131 /* No cleanup needed */ 3132 break; 3133 #endif /* MHD_SUPPORT_POST_PARSER */ 3134 #ifdef MHD_SUPPORT_UPGRADE 3135 case mhd_ACTION_UPGRADE: 3136 /* No cleanup needed */ 3137 break; 3138 #endif /* MHD_SUPPORT_UPGRADE */ 3139 case mhd_ACTION_ABORT: 3140 mhd_UNREACHABLE (); 3141 break; 3142 case mhd_ACTION_NO_ACTION: 3143 default: 3144 break; 3145 } 3146 a = NULL; 3147 } 3148 if (NULL == a) 3149 c->rq.app_act.head_act.act = mhd_ACTION_ABORT; 3150 3151 switch (c->rq.app_act.head_act.act) 3152 { 3153 case mhd_ACTION_RESPONSE: 3154 c->rp.response = c->rq.app_act.head_act.data.response; 3155 c->stage = mhd_HTTP_STAGE_REQ_RECV_FINISHED; 3156 return true; 3157 case mhd_ACTION_UPLOAD: 3158 if (0 != c->rq.cntn.cntn_size) 3159 { 3160 if (!check_and_alloc_buf_for_upload_processing (c)) 3161 return true; 3162 if (need_100_continue (c)) 3163 { 3164 c->stage = mhd_HTTP_STAGE_CONTINUE_SENDING; 3165 return true; 3166 } 3167 c->stage = mhd_HTTP_STAGE_BODY_RECEIVING; 3168 return (0 != c->read_buffer_offset); 3169 } 3170 c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; 3171 return true; 3172 #ifdef MHD_SUPPORT_POST_PARSER 3173 case mhd_ACTION_POST_PARSE: 3174 if (0 == c->rq.cntn.cntn_size) 3175 { 3176 c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_REQUEST_EMPTY; 3177 c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; 3178 return true; 3179 } 3180 if (!mhd_stream_prepare_for_post_parse (c)) 3181 { 3182 mhd_assert (mhd_HTTP_STAGE_FOOTERS_RECEIVED < c->stage); 3183 return true; 3184 } 3185 if (need_100_continue (c)) 3186 { 3187 c->stage = mhd_HTTP_STAGE_CONTINUE_SENDING; 3188 return true; 3189 } 3190 c->stage = mhd_HTTP_STAGE_BODY_RECEIVING; 3191 return true; 3192 #endif /* MHD_SUPPORT_POST_PARSER */ 3193 case mhd_ACTION_SUSPEND: 3194 c->suspended = true; 3195 #ifdef MHD_USE_TRACE_SUSPEND_RESUME 3196 fprintf (stderr, 3197 "%%%%%% Suspending connection, FD: %2llu\n", 3198 (unsigned long long)c->sk.fd); 3199 #endif /* MHD_USE_TRACE_SUSPEND_RESUME */ 3200 c->rq.app_act.head_act.act = mhd_ACTION_NO_ACTION; 3201 return false; 3202 #ifdef MHD_SUPPORT_UPGRADE 3203 case mhd_ACTION_UPGRADE: 3204 mhd_assert (0 == c->rq.cntn.cntn_size); 3205 c->stage = mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING; 3206 return false; 3207 #endif /* MHD_SUPPORT_UPGRADE */ 3208 case mhd_ACTION_ABORT: 3209 mhd_conn_start_closing_app_abort (c); 3210 return true; 3211 case mhd_ACTION_NO_ACTION: 3212 default: 3213 mhd_assert (0 && "Impossible value"); 3214 break; 3215 } 3216 mhd_UNREACHABLE (); 3217 return false; 3218 } 3219 3220 3221 /** 3222 * React on provided action for upload 3223 * @param c the stream to use 3224 * @param act the action provided by application 3225 * @param final set to 'true' if this is final upload callback 3226 * @return true if connection state has been changed, 3227 * false otherwise 3228 */ 3229 MHD_INTERNAL 3230 MHD_FN_PAR_NONNULL_ (1) bool 3231 mhd_stream_process_upload_action (struct MHD_Connection *restrict c, 3232 const struct MHD_UploadAction *act, 3233 bool final) 3234 { 3235 if (NULL != act) 3236 { 3237 if ((&(c->rq.app_act.upl_act) != act) 3238 || !mhd_UPLOAD_ACTION_IS_VALID (c->rq.app_act.upl_act.act) 3239 || (final 3240 && (mhd_UPLOAD_ACTION_CONTINUE == c->rq.app_act.upl_act.act))) 3241 { 3242 /* Perform cleanup of the created but now unused action */ 3243 switch (c->rq.app_act.upl_act.act) 3244 { 3245 case mhd_UPLOAD_ACTION_RESPONSE: 3246 mhd_assert (NULL != c->rq.app_act.upl_act.data.response); 3247 mhd_response_dec_use_count (c->rq.app_act.upl_act.data.response); 3248 break; 3249 case mhd_UPLOAD_ACTION_CONTINUE: 3250 case mhd_UPLOAD_ACTION_SUSPEND: 3251 /* No cleanup needed */ 3252 break; 3253 #ifdef MHD_SUPPORT_UPGRADE 3254 case mhd_UPLOAD_ACTION_UPGRADE: 3255 /* No cleanup needed */ 3256 break; 3257 #endif /* MHD_SUPPORT_UPGRADE */ 3258 case mhd_UPLOAD_ACTION_ABORT: 3259 mhd_UNREACHABLE (); 3260 break; 3261 case mhd_UPLOAD_ACTION_NO_ACTION: 3262 default: 3263 break; 3264 } 3265 mhd_LOG_MSG (c->daemon, MHD_SC_UPLOAD_ACTION_INVALID, \ 3266 "Provided action is not a correct action generated " \ 3267 "for the current request."); 3268 act = NULL; 3269 } 3270 } 3271 if (NULL == act) 3272 c->rq.app_act.upl_act.act = mhd_UPLOAD_ACTION_ABORT; 3273 3274 switch (c->rq.app_act.upl_act.act) 3275 { 3276 case mhd_UPLOAD_ACTION_RESPONSE: 3277 c->rp.response = c->rq.app_act.upl_act.data.response; 3278 c->stage = mhd_HTTP_STAGE_REQ_RECV_FINISHED; 3279 return true; 3280 case mhd_UPLOAD_ACTION_CONTINUE: 3281 memset (&(c->rq.app_act.upl_act), 0, sizeof(c->rq.app_act.upl_act)); 3282 return false; 3283 case mhd_UPLOAD_ACTION_SUSPEND: 3284 c->suspended = true; 3285 #ifdef MHD_USE_TRACE_SUSPEND_RESUME 3286 fprintf (stderr, 3287 "%%%%%% Suspending connection, FD: %2llu\n", 3288 (unsigned long long)c->sk.fd); 3289 #endif /* MHD_USE_TRACE_SUSPEND_RESUME */ 3290 memset (&(c->rq.app_act.upl_act), 0, sizeof(c->rq.app_act.upl_act)); 3291 return false; 3292 #ifdef MHD_SUPPORT_UPGRADE 3293 case mhd_UPLOAD_ACTION_UPGRADE: 3294 mhd_assert (c->rq.cntn.recv_size == c->rq.cntn.cntn_size); 3295 mhd_assert (!c->rq.have_chunked_upload \ 3296 || mhd_HTTP_STAGE_FULL_REQ_RECEIVED == c->stage); 3297 c->stage = mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING; 3298 return false; 3299 #endif /* MHD_SUPPORT_UPGRADE */ 3300 case mhd_UPLOAD_ACTION_ABORT: 3301 mhd_conn_start_closing_app_abort (c); 3302 return true; 3303 case mhd_UPLOAD_ACTION_NO_ACTION: 3304 default: 3305 mhd_assert (0 && "Impossible value"); 3306 break; 3307 } 3308 mhd_UNREACHABLE (); 3309 return false; 3310 } 3311 3312 3313 static MHD_FN_PAR_NONNULL_ALL_ bool 3314 process_request_chunked_body (struct MHD_Connection *restrict c) 3315 { 3316 struct MHD_Daemon *restrict d = c->daemon; 3317 size_t available; 3318 bool has_more_data; 3319 char *restrict buffer_head; 3320 const int discp_lvl = d->req_cfg.strictness; 3321 /* Treat bare LF as the end of the line. 3322 RFC 9112, section 2.2-3 3323 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5). 3324 Bare LF is processed as end of the line or rejected as broken request. */ 3325 const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl); 3326 /* Allow "Bad WhiteSpace" in chunk extension. 3327 RFC 9112, Section 7.1.1, Paragraph 2 */ 3328 const bool allow_bws = (MHD_PSL_VERY_STRICT > discp_lvl); 3329 bool state_updated; 3330 3331 mhd_assert (NULL == c->rp.response); 3332 mhd_assert (c->rq.have_chunked_upload); 3333 mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size); 3334 3335 buffer_head = c->read_buffer; 3336 available = c->read_buffer_offset; 3337 state_updated = false; 3338 do 3339 { 3340 size_t cntn_data_ready; 3341 bool need_inc_proc; 3342 3343 has_more_data = false; 3344 3345 if ((c->rq.current_chunk_offset == 3346 c->rq.current_chunk_size) 3347 && (0 != c->rq.current_chunk_size)) 3348 { 3349 size_t i; 3350 mhd_assert (0 != available); 3351 /* skip new line at the *end* of a chunk */ 3352 i = 0; 3353 if ((2 <= available) 3354 && ('\r' == buffer_head[0]) 3355 && ('\n' == buffer_head[1])) 3356 i += 2; /* skip CRLF */ 3357 else if (bare_lf_as_crlf && ('\n' == buffer_head[0])) 3358 i++; /* skip bare LF */ 3359 else if (2 > available) 3360 break; /* need more upload data */ 3361 if (0 == i) 3362 { 3363 /* malformed encoding */ 3364 mhd_RESPOND_WITH_ERROR_STATIC (c, 3365 MHD_HTTP_STATUS_BAD_REQUEST, 3366 ERR_RSP_REQUEST_CHUNKED_MALFORMED); 3367 return true; 3368 } 3369 available -= i; 3370 buffer_head += i; 3371 c->rq.current_chunk_offset = 0; 3372 c->rq.current_chunk_size = 0; 3373 if (0 == available) 3374 break; 3375 } 3376 if (0 != c->rq.current_chunk_size) 3377 { 3378 uint_fast64_t cur_chunk_left; 3379 mhd_assert (c->rq.current_chunk_offset < \ 3380 c->rq.current_chunk_size); 3381 /* we are in the middle of a chunk, give 3382 as much as possible to the client (without 3383 crossing chunk boundaries) */ 3384 cur_chunk_left 3385 = c->rq.current_chunk_size 3386 - c->rq.current_chunk_offset; 3387 if (cur_chunk_left > available) 3388 cntn_data_ready = available; 3389 else 3390 { /* cur_chunk_left <= (size_t)available */ 3391 cntn_data_ready = (size_t)cur_chunk_left; 3392 if (available > cntn_data_ready) 3393 has_more_data = true; 3394 } 3395 } 3396 else 3397 { /* Need the parse the chunk size line */ 3398 /** The number of found digits in the chunk size number */ 3399 size_t num_dig; 3400 uint_fast64_t chunk_size; 3401 bool broken; 3402 bool overflow; 3403 3404 mhd_assert (0 != available); 3405 3406 overflow = false; 3407 chunk_size = 0; /* Mute possible compiler warning. 3408 The real value will be set later. */ 3409 3410 num_dig = mhd_strx_to_uint64_n (buffer_head, 3411 available, 3412 &chunk_size); 3413 mhd_assert (num_dig <= available); 3414 if (num_dig == available) 3415 continue; /* Need line delimiter */ 3416 3417 broken = (0 == num_dig); 3418 if (broken) 3419 /* Check whether result is invalid due to uint64_t overflow */ 3420 overflow = ((('0' <= buffer_head[0]) && ('9' >= buffer_head[0])) 3421 || (('A' <= buffer_head[0]) && ('F' >= buffer_head[0])) 3422 || (('a' <= buffer_head[0]) && ('f' >= buffer_head[0]))); 3423 else 3424 { 3425 /** 3426 * The length of the string with the number of the chunk size, 3427 * including chunk extension 3428 */ 3429 size_t chunk_size_line_len; 3430 3431 chunk_size_line_len = 0; 3432 if ((';' == buffer_head[num_dig]) 3433 || (allow_bws 3434 && ((' ' == buffer_head[num_dig]) 3435 || ('\t' == buffer_head[num_dig])))) 3436 { /* Chunk extension */ 3437 size_t i; 3438 3439 /* Skip bad whitespaces (if any) */ 3440 for (i = num_dig; i < available; ++i) 3441 { 3442 if ((' ' != buffer_head[i]) && ('\t' != buffer_head[i])) 3443 break; 3444 } 3445 if (i == available) 3446 break; /* need more data */ 3447 if (';' == buffer_head[i]) 3448 { 3449 for (++i; i < available; ++i) 3450 { 3451 if ('\n' == buffer_head[i]) 3452 break; 3453 } 3454 if (i == available) 3455 break; /* need more data */ 3456 mhd_assert (i > num_dig); 3457 mhd_assert (1 <= i); 3458 /* Found LF position at 'i' (buffer_head[i] = '\n'), chunk ends at i+1 */ 3459 if (bare_lf_as_crlf) 3460 chunk_size_line_len = i + 1; /* Don't care about CR before LF */ 3461 else if ('\r' == buffer_head[i - 1]) 3462 chunk_size_line_len = i + 1; /* Have CR LF, all good */ 3463 /* else: invalid termination, leave chunk_size_line_len at 0 */ 3464 } 3465 else 3466 { /* No ';' after "bad whitespace" */ 3467 mhd_assert (allow_bws); 3468 mhd_assert (0 == chunk_size_line_len); 3469 } 3470 } 3471 else 3472 { 3473 mhd_assert (available >= num_dig); 3474 if ((2 <= (available - num_dig)) 3475 && ('\r' == buffer_head[num_dig]) 3476 && ('\n' == buffer_head[num_dig + 1])) 3477 chunk_size_line_len = num_dig + 2; 3478 else if (bare_lf_as_crlf 3479 && ('\n' == buffer_head[num_dig])) 3480 chunk_size_line_len = num_dig + 1; 3481 else if (2 > (available - num_dig)) 3482 break; /* need more data */ 3483 } 3484 3485 if (0 != chunk_size_line_len) 3486 { /* Valid termination of the chunk size line */ 3487 mhd_assert (chunk_size_line_len <= available); 3488 /* Start reading payload data of the chunk */ 3489 c->rq.current_chunk_offset = 0; 3490 c->rq.current_chunk_size = chunk_size; 3491 3492 available -= chunk_size_line_len; 3493 buffer_head += chunk_size_line_len; 3494 3495 if (0 == chunk_size) 3496 { /* The final (termination) chunk */ 3497 c->rq.cntn.cntn_size = c->rq.cntn.recv_size; 3498 c->stage = mhd_HTTP_STAGE_BODY_RECEIVED; 3499 state_updated = true; 3500 break; 3501 } 3502 if (available > 0) 3503 has_more_data = true; 3504 continue; 3505 } 3506 /* Invalid chunk size line */ 3507 } 3508 3509 if (!overflow) 3510 mhd_RESPOND_WITH_ERROR_STATIC (c, 3511 MHD_HTTP_STATUS_BAD_REQUEST, 3512 ERR_RSP_REQUEST_CHUNKED_MALFORMED); 3513 else 3514 mhd_RESPOND_WITH_ERROR_STATIC (c, 3515 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, 3516 ERR_RSP_REQUEST_CHUNK_TOO_LARGE); 3517 return true; 3518 } 3519 mhd_assert (c->rq.app_aware); 3520 3521 #ifdef MHD_SUPPORT_POST_PARSER 3522 if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act) 3523 { 3524 size_t size_provided; 3525 3526 c->rq.cntn.recv_size += cntn_data_ready; 3527 size_provided = cntn_data_ready; 3528 3529 state_updated = mhd_stream_post_parse (c, 3530 &size_provided, 3531 buffer_head); 3532 // TODO: support one chunk in-place processing? 3533 mhd_assert ((0 == size_provided) \ 3534 || (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result) \ 3535 || (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage)); 3536 if (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage) 3537 c->discard_request = true; 3538 } 3539 else 3540 #endif /* MHD_SUPPORT_POST_PARSER */ 3541 if (1) 3542 { 3543 mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act); 3544 if (NULL != c->rq.app_act.head_act.data.upload.full.cb) 3545 { 3546 need_inc_proc = false; 3547 3548 mhd_assert (0 == c->rq.cntn.proc_size); 3549 if ((uint_fast64_t)c->rq.cntn.lbuf.size < 3550 c->rq.cntn.recv_size + cntn_data_ready) 3551 { 3552 size_t grow_size; 3553 3554 grow_size = (size_t)(c->rq.cntn.recv_size + cntn_data_ready 3555 - c->rq.cntn.lbuf.size); 3556 if (((size_t)(c->rq.cntn.recv_size + cntn_data_ready) < 3557 cntn_data_ready) 3558 || (!mhd_daemon_grow_lbuf (d, 3559 grow_size, 3560 &(c->rq.cntn.lbuf)))) 3561 { 3562 /* Failed to grow the buffer, no space to put the new data */ 3563 const struct MHD_UploadAction *act; 3564 if (NULL == c->rq.app_act.head_act.data.upload.inc.cb) 3565 { 3566 mhd_RESPOND_WITH_ERROR_STATIC ( 3567 c, 3568 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, 3569 ERR_RSP_MSG_REQUEST_TOO_BIG); 3570 return true; 3571 } 3572 c->rq.app_act.head_act.data.upload.full.cb = NULL; /* Cannot process "full" content */ 3573 /* Process previously buffered data */ 3574 mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size); 3575 act = c->rq.app_act.head_act.data.upload.inc.cb ( 3576 c->rq.app_act.head_act.data.upload.inc.cls, 3577 &(c->rq), 3578 (size_t)c->rq.cntn.recv_size, 3579 c->rq.cntn.lbuf.data); 3580 c->rq.cntn.proc_size = c->rq.cntn.recv_size; 3581 mhd_daemon_free_lbuf (d, &(c->rq.cntn.lbuf)); 3582 if (mhd_stream_process_upload_action (c, act, false)) 3583 return true; 3584 need_inc_proc = true; 3585 } 3586 } 3587 if (!need_inc_proc) 3588 { 3589 memcpy (c->rq.cntn.lbuf.data + c->rq.cntn.recv_size, 3590 buffer_head, cntn_data_ready); 3591 c->rq.cntn.recv_size += cntn_data_ready; 3592 } 3593 } 3594 else 3595 need_inc_proc = true; 3596 3597 if (need_inc_proc) 3598 { 3599 const struct MHD_UploadAction *act; 3600 mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb); 3601 3602 c->rq.cntn.recv_size += cntn_data_ready; 3603 act = c->rq.app_act.head_act.data.upload.inc.cb ( 3604 c->rq.app_act.head_act.data.upload.inc.cls, 3605 &(c->rq), 3606 cntn_data_ready, 3607 buffer_head); 3608 c->rq.cntn.proc_size += cntn_data_ready; 3609 state_updated = mhd_stream_process_upload_action (c, act, false); 3610 } 3611 } 3612 /* dh left "processed" bytes in buffer for next time... */ 3613 mhd_ASSUME (available >= cntn_data_ready); 3614 buffer_head += cntn_data_ready; 3615 available -= cntn_data_ready; 3616 mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size); 3617 mhd_ASSUME (c->rq.current_chunk_offset + cntn_data_ready >= 3618 c->rq.current_chunk_offset); 3619 c->rq.current_chunk_offset += cntn_data_ready; 3620 } while (has_more_data && !state_updated); 3621 /* TODO: optionally? zero out reused memory region */ 3622 if ((available > 0) 3623 && (buffer_head != c->read_buffer)) 3624 memmove (c->read_buffer, 3625 buffer_head, 3626 available); 3627 else 3628 mhd_assert ((0 == available) \ 3629 || (c->read_buffer_offset == available)); 3630 c->read_buffer_offset = available; 3631 3632 return state_updated; 3633 } 3634 3635 3636 static MHD_FN_PAR_NONNULL_ALL_ bool 3637 process_request_nonchunked_body (struct MHD_Connection *restrict c) 3638 { 3639 size_t cntn_data_ready; 3640 bool read_buf_reuse; 3641 bool state_updated; 3642 3643 mhd_assert (NULL == c->rp.response); 3644 mhd_assert (!c->rq.have_chunked_upload); 3645 mhd_assert (MHD_SIZE_UNKNOWN != c->rq.cntn.cntn_size); 3646 mhd_assert (c->rq.cntn.recv_size < c->rq.cntn.cntn_size); 3647 mhd_assert (c->rq.app_aware); 3648 3649 if ((c->rq.cntn.cntn_size - c->rq.cntn.recv_size) < c->read_buffer_offset) 3650 cntn_data_ready = (size_t)(c->rq.cntn.cntn_size - c->rq.cntn.recv_size); 3651 else 3652 cntn_data_ready = c->read_buffer_offset; 3653 3654 read_buf_reuse = false; 3655 state_updated = false; 3656 mhd_assert (!read_buf_reuse); /* Mute analyser warning */ 3657 3658 #ifdef MHD_SUPPORT_POST_PARSER 3659 if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act) 3660 { 3661 size_t size_provided; 3662 // TODO: rework to correctly support partial processing 3663 // TODO: rework to support receiving directly into "large buffer" 3664 c->rq.cntn.recv_size += cntn_data_ready; 3665 size_provided = cntn_data_ready; 3666 3667 state_updated = mhd_stream_post_parse (c, 3668 &size_provided, 3669 c->read_buffer); 3670 mhd_assert ((0 == size_provided) \ 3671 || (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result) \ 3672 || (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage)); 3673 if (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage) 3674 c->discard_request = true; 3675 3676 read_buf_reuse = true; 3677 c->rq.cntn.proc_size += cntn_data_ready; 3678 if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size) 3679 { 3680 c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; 3681 state_updated = true; 3682 } 3683 } 3684 else 3685 #endif /* MHD_SUPPORT_POST_PARSER */ 3686 if (1) 3687 { 3688 mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act); 3689 if (NULL != c->rq.app_act.head_act.data.upload.full.cb) 3690 { 3691 // TODO: implement processing in pool memory if buffer is large enough 3692 mhd_assert ((c->rq.cntn.recv_size + cntn_data_ready) <= 3693 (uint_fast64_t)c->rq.cntn.lbuf.size); 3694 memcpy (c->rq.cntn.lbuf.data + c->rq.cntn.recv_size, 3695 c->read_buffer, cntn_data_ready); 3696 c->rq.cntn.recv_size += cntn_data_ready; 3697 read_buf_reuse = true; 3698 if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size) 3699 { 3700 c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; 3701 state_updated = true; 3702 } 3703 } 3704 else 3705 { 3706 const struct MHD_UploadAction *act; 3707 mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb); 3708 3709 c->rq.cntn.recv_size += cntn_data_ready; 3710 act = c->rq.app_act.head_act.data.upload.inc.cb ( 3711 c->rq.app_act.head_act.data.upload.inc.cls, 3712 &(c->rq), 3713 cntn_data_ready, 3714 c->read_buffer); 3715 c->rq.cntn.proc_size += cntn_data_ready; 3716 read_buf_reuse = true; 3717 state_updated = mhd_stream_process_upload_action (c, act, false); 3718 } 3719 } 3720 3721 if (read_buf_reuse) 3722 { 3723 size_t data_left_size; 3724 mhd_assert (c->read_buffer_offset >= cntn_data_ready); 3725 data_left_size = c->read_buffer_offset - cntn_data_ready; 3726 if (0 != data_left_size) 3727 memmove (c->read_buffer, 3728 c->read_buffer + cntn_data_ready, 3729 data_left_size); 3730 c->read_buffer_offset = data_left_size; 3731 } 3732 3733 return state_updated; 3734 } 3735 3736 3737 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 3738 mhd_stream_process_request_body (struct MHD_Connection *restrict c) 3739 { 3740 if (c->rq.have_chunked_upload) 3741 return process_request_chunked_body (c); 3742 3743 return process_request_nonchunked_body (c); 3744 } 3745 3746 3747 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 3748 mhd_stream_call_app_final_upload_cb (struct MHD_Connection *restrict c) 3749 { 3750 const struct MHD_UploadAction *act; 3751 bool state_changed; 3752 mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act \ 3753 || mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act); 3754 3755 #ifdef MHD_SUPPORT_POST_PARSER 3756 if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act) 3757 return mhd_stream_process_post_finish (c); 3758 #endif /* MHD_SUPPORT_POST_PARSER */ 3759 3760 mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act); 3761 3762 if (NULL != c->rq.app_act.head_act.data.upload.full.cb) 3763 { 3764 mhd_assert (c->rq.cntn.recv_size == c->rq.cntn.cntn_size); 3765 mhd_assert (0 == c->rq.cntn.proc_size); 3766 mhd_assert (NULL != c->rq.cntn.lbuf.data); 3767 mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size); 3768 // TODO: implement processing in pool memory if it is large enough 3769 act = c->rq.app_act.head_act.data.upload.full.cb ( 3770 c->rq.app_act.head_act.data.upload.full.cls, 3771 &(c->rq), 3772 (size_t)c->rq.cntn.recv_size, 3773 c->rq.cntn.lbuf.data); 3774 c->rq.cntn.proc_size = c->rq.cntn.recv_size; 3775 } 3776 else 3777 { 3778 mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb); 3779 mhd_assert (c->rq.cntn.cntn_size == c->rq.cntn.proc_size); 3780 act = c->rq.app_act.head_act.data.upload.inc.cb ( 3781 c->rq.app_act.head_act.data.upload.inc.cls, 3782 &(c->rq), 3783 0, 3784 NULL); 3785 } 3786 3787 state_changed = mhd_stream_process_upload_action (c, act, true); 3788 if (!c->suspended) 3789 mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf)); 3790 3791 return state_changed; 3792 } 3793 3794 3795 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 3796 mhd_stream_process_req_recv_finished (struct MHD_Connection *restrict c) 3797 { 3798 if (NULL != c->rq.cntn.lbuf.data) 3799 mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf)); 3800 c->rq.cntn.lbuf.data = NULL; 3801 if (c->rq.cntn.cntn_size != c->rq.cntn.proc_size) 3802 c->discard_request = true; 3803 mhd_assert (NULL != c->rp.response); 3804 c->stage = mhd_HTTP_STAGE_START_REPLY; 3805 return true; 3806 } 3807 3808 3809 /** 3810 * Send error reply when receive buffer space exhausted while receiving 3811 * the chunk size line. 3812 * @param c the connection to handle 3813 * @param chunk_size_line the optional pointer to the partially received 3814 * the current chunk size line. 3815 * Could be not zero-terminated and can contain binary 3816 * zeros. 3817 * Can be NULL. 3818 * @param chunk_size_line_size the size of the @a chunk_size_line 3819 */ 3820 static void 3821 handle_req_chunk_size_line_no_space (struct MHD_Connection *c, 3822 const char *chunk_size_line, 3823 size_t chunk_size_line_size) 3824 { 3825 unsigned int err_code; 3826 3827 if (NULL != chunk_size_line) 3828 { 3829 const char *semicol; 3830 /* Check for chunk extension */ 3831 semicol = (const char *) 3832 memchr (chunk_size_line, 3833 ';', 3834 chunk_size_line_size); 3835 if (NULL != semicol) 3836 { /* Chunk extension present. It could be removed without any loss of the 3837 details of the request. */ 3838 mhd_RESPOND_WITH_ERROR_STATIC (c, 3839 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, 3840 ERR_RSP_REQUEST_CHUNK_LINE_EXT_TOO_BIG); 3841 return; 3842 } 3843 } 3844 err_code = mhd_stream_get_no_space_err_status_code (c, 3845 MHD_PROC_RECV_BODY_CHUNKED, 3846 chunk_size_line_size, 3847 chunk_size_line); 3848 mhd_RESPOND_WITH_ERROR_STATIC (c, 3849 err_code, 3850 ERR_RSP_REQUEST_CHUNK_LINE_TOO_BIG); 3851 } 3852 3853 3854 /** 3855 * Handle situation with read buffer exhaustion. 3856 * Must be called when no more space left in the read buffer, no more 3857 * space left in the memory pool to grow the read buffer, but more data 3858 * need to be received from the client. 3859 * Could be called when the result of received data processing cannot be 3860 * stored in the memory pool (like some header). 3861 * @param c the connection to process 3862 * @param stage the receive stage where the exhaustion happens. 3863 * @return 'true' if connection should NOT be closed, 3864 * 'false' if connection is closing 3865 */ 3866 static MHD_FN_PAR_NONNULL_ALL_ bool 3867 handle_recv_no_space (struct MHD_Connection *c, 3868 enum MHD_ProcRecvDataStage stage) 3869 { 3870 mhd_assert (MHD_PROC_RECV_INIT <= stage); 3871 mhd_assert (MHD_PROC_RECV_FOOTERS >= stage); 3872 mhd_assert (mhd_HTTP_STAGE_FULL_REQ_RECEIVED > c->stage); 3873 mhd_assert ((MHD_PROC_RECV_INIT != stage) \ 3874 || (mhd_HTTP_STAGE_INIT == c->stage)); 3875 mhd_assert ((MHD_PROC_RECV_METHOD != stage) \ 3876 || (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage)); 3877 mhd_assert ((MHD_PROC_RECV_URI != stage) \ 3878 || (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage)); 3879 mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) \ 3880 || (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage)); 3881 mhd_assert ((MHD_PROC_RECV_HEADERS != stage) \ 3882 || (mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING == c->stage)); 3883 mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */ 3884 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) \ 3885 || (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage)); 3886 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) \ 3887 || (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage)); 3888 mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) \ 3889 || (mhd_HTTP_STAGE_FOOTERS_RECEIVING == c->stage)); 3890 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) \ 3891 || (!c->rq.have_chunked_upload)); 3892 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) \ 3893 || (c->rq.have_chunked_upload)); 3894 switch (stage) 3895 { 3896 case MHD_PROC_RECV_INIT: 3897 case MHD_PROC_RECV_METHOD: 3898 /* Some data has been received, but it is not clear yet whether 3899 * the received data is an valid HTTP request */ 3900 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST, \ 3901 "No space left in the read buffer when " \ 3902 "receiving the initial part of " \ 3903 "the request line."); 3904 return false; 3905 case MHD_PROC_RECV_URI: 3906 case MHD_PROC_RECV_HTTPVER: 3907 /* Some data has been received, but the request line is incomplete */ 3908 mhd_assert (mhd_HTTP_METHOD_NO_METHOD != c->rq.http_mthd); 3909 mhd_assert (MHD_HTTP_VERSION_INVALID == c->rq.http_ver); 3910 /* A quick simple check whether the incomplete line looks 3911 * like an HTTP request */ 3912 if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) 3913 && (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd)) 3914 { 3915 mhd_RESPOND_WITH_ERROR_STATIC (c, 3916 MHD_HTTP_STATUS_URI_TOO_LONG, 3917 ERR_RSP_MSG_REQUEST_TOO_BIG); 3918 return true; 3919 } 3920 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST, \ 3921 "No space left in the read buffer when " \ 3922 "receiving the URI in " \ 3923 "the request line. " \ 3924 "The request uses non-standard HTTP request " \ 3925 "method token."); 3926 return false; 3927 case MHD_PROC_RECV_HEADERS: 3928 handle_req_headers_no_space (c, c->read_buffer, c->read_buffer_offset); 3929 return true; 3930 case MHD_PROC_RECV_BODY_NORMAL: 3931 /* A header probably has been added to a suspended connection and 3932 it took precisely all the space in the buffer. 3933 Very low probability. */ 3934 mhd_assert (!c->rq.have_chunked_upload); 3935 handle_req_headers_no_space (c, NULL, 0); // FIXME: check 3936 return true; 3937 case MHD_PROC_RECV_BODY_CHUNKED: 3938 mhd_assert (c->rq.have_chunked_upload); 3939 if (c->rq.current_chunk_offset != c->rq.current_chunk_size) 3940 { /* Receiving content of the chunk */ 3941 /* A header probably has been added to a suspended connection and 3942 it took precisely all the space in the buffer. 3943 Very low probability. */ 3944 handle_req_headers_no_space (c, NULL, 0); // FIXME: check 3945 } 3946 else 3947 { 3948 if (0 != c->rq.current_chunk_size) 3949 { /* Waiting for chunk-closing CRLF */ 3950 /* Not really possible as some payload should be 3951 processed and the space used by payload should be available. */ 3952 handle_req_headers_no_space (c, NULL, 0); // FIXME: check 3953 } 3954 else 3955 { /* Reading the line with the chunk size */ 3956 handle_req_chunk_size_line_no_space (c, 3957 c->read_buffer, 3958 c->read_buffer_offset); 3959 } 3960 } 3961 return true; 3962 case MHD_PROC_RECV_FOOTERS: 3963 handle_req_footers_no_space (c, c->read_buffer, c->read_buffer_offset); 3964 return true; 3965 /* The next cases should not be possible */ 3966 case MHD_PROC_RECV_COOKIE: 3967 default: 3968 break; 3969 } 3970 mhd_UNREACHABLE (); 3971 return false; 3972 } 3973 3974 3975 /** 3976 * Try growing the read buffer. We initially claim half the available 3977 * buffer space for the read buffer (the other half being left for 3978 * management data structures; the write buffer can in the end take 3979 * virtually everything as the read buffer can be reduced to the 3980 * minimum necessary at that point. 3981 * 3982 * @param connection the connection 3983 * @param required set to 'true' if grow is required, i.e. connection 3984 * will fail if no additional space is granted 3985 * @return 'true' on success, 'false' on failure 3986 */ 3987 static MHD_FN_PAR_NONNULL_ALL_ bool 3988 try_grow_read_buffer (struct MHD_Connection *restrict connection, 3989 bool required) 3990 { 3991 size_t new_size; 3992 size_t avail_size; 3993 const size_t def_grow_size = 1536; // TODO: remove hardcoded increment 3994 char *rb; 3995 3996 avail_size = mhd_pool_get_free (connection->pool); 3997 if (0 == avail_size) 3998 return false; /* No more space available */ 3999 if (0 == connection->read_buffer_size) 4000 new_size = avail_size / 2; /* Use half of available buffer for reading */ 4001 else 4002 { 4003 size_t grow_size; 4004 4005 grow_size = avail_size / 8; 4006 if (def_grow_size > grow_size) 4007 { /* Shortage of space */ 4008 const size_t left_free = 4009 connection->read_buffer_size - connection->read_buffer_offset; 4010 mhd_assert (connection->read_buffer_size >= \ 4011 connection->read_buffer_offset); 4012 if ((def_grow_size <= grow_size + left_free) 4013 && (left_free < def_grow_size)) 4014 grow_size = def_grow_size - left_free; /* Use precise 'def_grow_size' for new free space */ 4015 else if (!required) 4016 return false; /* Grow is not mandatory, leave some space in pool */ 4017 else 4018 { 4019 /* Shortage of space, but grow is mandatory */ 4020 const size_t small_inc = 4021 ((mhd_BUF_INC_SIZE > def_grow_size) ? 4022 def_grow_size : mhd_BUF_INC_SIZE) / 8; 4023 if (small_inc < avail_size) 4024 grow_size = small_inc; 4025 else 4026 grow_size = avail_size; 4027 } 4028 } 4029 new_size = connection->read_buffer_size + grow_size; 4030 } 4031 /* Make sure that read buffer will not be moved */ 4032 if ((NULL != connection->read_buffer) 4033 && !mhd_pool_is_resizable_inplace (connection->pool, 4034 connection->read_buffer, 4035 connection->read_buffer_size)) 4036 { 4037 mhd_assert (0); 4038 return false; 4039 } 4040 /* we can actually grow the buffer, do it! */ 4041 rb = (char *)mhd_pool_reallocate (connection->pool, 4042 connection->read_buffer, 4043 connection->read_buffer_size, 4044 new_size); 4045 if (NULL == rb) 4046 { 4047 /* This should NOT be possible: we just computed 'new_size' so that 4048 it should fit. If it happens, somehow our read buffer is not in 4049 the right position in the pool, say because someone called 4050 mhd_pool_allocate() without 'from_end' set to 'true'? Anyway, 4051 should be investigated! (Ideally provide all data from 4052 *pool and connection->read_buffer and new_size for debugging). */ 4053 mhd_assert (0); 4054 return false; 4055 } 4056 mhd_assert (connection->read_buffer == rb); 4057 connection->read_buffer = rb; 4058 mhd_assert (NULL != connection->read_buffer); 4059 connection->read_buffer_size = new_size; 4060 return true; 4061 } 4062 4063 4064 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ enum mhd_ConnectionBufferGrowResult 4065 mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c) 4066 { 4067 enum MHD_ProcRecvDataStage stage; 4068 bool res; 4069 /** 4070 * The increase of read buffer size is desirable. 4071 */ 4072 bool rbuff_grow_desired; 4073 /** 4074 * The increase of read buffer size is a hard requirement. 4075 */ 4076 bool rbuff_grow_required; 4077 4078 mhd_assert (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info)); 4079 mhd_assert (!c->discard_request); 4080 4081 rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size); 4082 if (rbuff_grow_required) 4083 rbuff_grow_desired = true; 4084 else 4085 { 4086 rbuff_grow_desired = (c->read_buffer_offset + 1536 > // TODO: remove handcoded buffer grow size 4087 c->read_buffer_size); 4088 4089 if ((rbuff_grow_desired) 4090 && (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage)) 4091 { 4092 if (!c->rq.have_chunked_upload) 4093 { 4094 mhd_assert (MHD_SIZE_UNKNOWN != c->rq.cntn.cntn_size); 4095 /* Do not grow read buffer more than necessary to process the current 4096 request. */ 4097 rbuff_grow_desired = 4098 (c->rq.cntn.cntn_size - c->rq.cntn.recv_size > c->read_buffer_size); // FIXME 4099 } 4100 else 4101 { 4102 mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size); 4103 if (0 == c->rq.current_chunk_size) 4104 rbuff_grow_desired = /* Reading value of the next chunk size */ 4105 (MHD_CHUNK_HEADER_REASONABLE_LEN > 4106 c->read_buffer_size); 4107 else 4108 { 4109 const uint_fast64_t cur_chunk_left = 4110 c->rq.current_chunk_size - c->rq.current_chunk_offset; 4111 /* Do not grow read buffer more than necessary to process the current 4112 chunk with terminating CRLF. */ 4113 mhd_assert (c->rq.current_chunk_offset <= c->rq.current_chunk_size); 4114 rbuff_grow_desired = 4115 ((cur_chunk_left + 2) > (uint_fast64_t)(c->read_buffer_size)); 4116 } 4117 } 4118 } 4119 } 4120 4121 if (!rbuff_grow_desired) 4122 return mhd_CONN_BUFF_GROW_OK; /* No need to increase the buffer */ 4123 4124 if (try_grow_read_buffer (c, rbuff_grow_required)) 4125 return mhd_CONN_BUFF_GROW_OK; /* Buffer increase succeed */ 4126 4127 if (!rbuff_grow_required) 4128 return mhd_CONN_BUFF_GROW_OK; /* Can continue without buffer increase */ 4129 4130 /* Failed to increase the read buffer size, but need to read the data 4131 from the network. 4132 No more space left in the buffer, no more space to increase the buffer. */ 4133 4134 switch (c->stage) 4135 { 4136 case mhd_HTTP_STAGE_INIT: 4137 stage = MHD_PROC_RECV_INIT; 4138 break; 4139 case mhd_HTTP_STAGE_REQ_LINE_RECEIVING: 4140 if (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd) 4141 stage = MHD_PROC_RECV_METHOD; 4142 else if (0 == c->rq.req_target_len) 4143 stage = MHD_PROC_RECV_URI; 4144 else 4145 stage = MHD_PROC_RECV_HTTPVER; 4146 break; 4147 case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING: 4148 stage = MHD_PROC_RECV_HEADERS; 4149 break; 4150 case mhd_HTTP_STAGE_BODY_RECEIVING: 4151 stage = c->rq.have_chunked_upload ? 4152 MHD_PROC_RECV_BODY_CHUNKED : MHD_PROC_RECV_BODY_NORMAL; 4153 break; 4154 case mhd_HTTP_STAGE_FOOTERS_RECEIVING: 4155 stage = MHD_PROC_RECV_FOOTERS; 4156 break; 4157 case mhd_HTTP_STAGE_REQ_LINE_RECEIVED: 4158 case mhd_HTTP_STAGE_HEADERS_RECEIVED: 4159 case mhd_HTTP_STAGE_HEADERS_PROCESSED: 4160 case mhd_HTTP_STAGE_CONTINUE_SENDING: 4161 case mhd_HTTP_STAGE_BODY_RECEIVED: 4162 case mhd_HTTP_STAGE_FOOTERS_RECEIVED: 4163 case mhd_HTTP_STAGE_FULL_REQ_RECEIVED: 4164 case mhd_HTTP_STAGE_REQ_RECV_FINISHED: 4165 case mhd_HTTP_STAGE_START_REPLY: 4166 case mhd_HTTP_STAGE_HEADERS_SENDING: 4167 case mhd_HTTP_STAGE_HEADERS_SENT: 4168 case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY: 4169 case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY: 4170 case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY: 4171 case mhd_HTTP_STAGE_CHUNKED_BODY_READY: 4172 case mhd_HTTP_STAGE_CHUNKED_BODY_SENT: 4173 case mhd_HTTP_STAGE_FOOTERS_SENDING: 4174 case mhd_HTTP_STAGE_FULL_REPLY_SENT: 4175 case mhd_HTTP_STAGE_PRE_CLOSING: 4176 case mhd_HTTP_STAGE_CLOSED: 4177 #ifdef MHD_SUPPORT_UPGRADE 4178 case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING: 4179 case mhd_HTTP_STAGE_UPGRADING: 4180 case mhd_HTTP_STAGE_UPGRADED: 4181 case mhd_HTTP_STAGE_UPGRADED_CLEANING: 4182 #endif /* MHD_SUPPORT_UPGRADE */ 4183 default: 4184 mhd_UNREACHABLE (); 4185 stage = MHD_PROC_RECV_BODY_NORMAL; 4186 break; 4187 } 4188 4189 res = handle_recv_no_space (c, stage); 4190 4191 mhd_assert (!res || !c->dbg.closing_started); 4192 mhd_assert (res || c->dbg.closing_started); 4193 4194 return 4195 res ? mhd_CONN_BUFF_GROW_ERR_REPLY : mhd_CONN_BUFF_GROW_ERR_CONN_CLOSE; 4196 }