stream_process_request.c (147943B)
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) 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 <= c->rq.hdrs.rq_line.rq_tgt_qmark)); 1389 mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \ 1390 (c->rq.req_target_len > \ 1391 (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \ 1392 - c->rq.hdrs.rq_line.rq_tgt))); 1393 1394 /* Log callback before the request-target is modified/decoded */ 1395 if (NULL != c->daemon->req_cfg.uri_cb.cb) 1396 { 1397 struct MHD_EarlyUriCbData req_data; 1398 req_data.request = &(c->rq); 1399 req_data.full_uri.cstr = c->rq.hdrs.rq_line.rq_tgt; 1400 req_data.full_uri.len = c->rq.req_target_len; 1401 c->rq.app_aware = true; 1402 c->daemon->req_cfg.uri_cb.cb (c->daemon->req_cfg.uri_cb.cls, 1403 &req_data, 1404 &(c->rq.app_context)); 1405 } 1406 1407 if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark) 1408 { 1409 params_len = 1410 c->rq.req_target_len 1411 - (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark - c->rq.hdrs.rq_line.rq_tgt); 1412 1413 mhd_assert (1 <= params_len); 1414 1415 c->rq.hdrs.rq_line.rq_tgt_qmark[0] = 0; /* Replace '?' with zero termination */ 1416 1417 // TODO: support detection of decoding errors 1418 if (! mhd_parse_uri_args (params_len - 1, 1419 c->rq.hdrs.rq_line.rq_tgt_qmark + 1, 1420 &request_add_get_arg, 1421 &(c->h1_stream))) 1422 { 1423 mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_GET_PARAM, 1424 "Not enough memory in the pool to store GET parameter"); 1425 1426 mhd_RESPOND_WITH_ERROR_STATIC ( 1427 c, 1428 mhd_stream_get_no_space_err_status_code (c, 1429 MHD_PROC_RECV_URI, 1430 0, 1431 NULL), 1432 ERR_RSP_MSG_REQUEST_TOO_BIG); 1433 mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING != c->stage); 1434 return false; 1435 1436 } 1437 } 1438 else 1439 params_len = 0; 1440 1441 mhd_assert (strlen (c->rq.hdrs.rq_line.rq_tgt) == \ 1442 c->rq.req_target_len - params_len); 1443 1444 /* Finally unescape URI itself */ 1445 // TODO: support detection of decoding errors 1446 c->rq.url_len = 1447 mhd_str_pct_decode_lenient_n (c->rq.hdrs.rq_line.rq_tgt, 1448 c->rq.req_target_len - params_len, 1449 c->rq.hdrs.rq_line.rq_tgt, 1450 c->rq.req_target_len - params_len, 1451 NULL); 1452 c->rq.url = c->rq.hdrs.rq_line.rq_tgt; 1453 1454 return true; 1455 } 1456 1457 1458 #ifndef MHD_MAX_FIXED_URI_LEN 1459 /** 1460 * The maximum size of the fixed URI for automatic redirection 1461 */ 1462 #define MHD_MAX_FIXED_URI_LEN (64 * 1024) 1463 #endif /* ! MHD_MAX_FIXED_URI_LEN */ 1464 1465 /** 1466 * Send the automatic redirection to fixed URI when received URI with 1467 * whitespaces. 1468 * If URI is too large, close connection with error. 1469 * 1470 * @param c the connection to process 1471 */ 1472 static void 1473 send_redirect_fixed_rq_target (struct MHD_Connection *restrict c) 1474 { 1475 static const char hdr_prefix[] = MHD_HTTP_HEADER_LOCATION ": "; 1476 static const size_t hdr_prefix_len = 1477 mhd_SSTR_LEN (MHD_HTTP_HEADER_LOCATION ": "); 1478 char *hdr_line; 1479 char *b; 1480 size_t fixed_uri_len; 1481 size_t i; 1482 size_t o; 1483 1484 mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); 1485 mhd_assert (0 != c->rq.hdrs.rq_line.num_ws_in_uri); 1486 mhd_assert (c->rq.hdrs.rq_line.num_ws_in_uri <= \ 1487 c->rq.req_target_len); 1488 fixed_uri_len = c->rq.req_target_len 1489 + 2 * c->rq.hdrs.rq_line.num_ws_in_uri; 1490 if ( (fixed_uri_len + 200 > c->daemon->conns.cfg.mem_pool_size) || 1491 (fixed_uri_len > MHD_MAX_FIXED_URI_LEN) || 1492 (NULL == 1493 (hdr_line = (char *) malloc (fixed_uri_len + 1 + hdr_prefix_len))) ) 1494 { 1495 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN, \ 1496 "The request has whitespace character is " \ 1497 "in the URI and the URI is too large to " \ 1498 "send automatic redirect to fixed URI."); 1499 return; 1500 } 1501 memcpy (hdr_line, hdr_prefix, hdr_prefix_len); 1502 b = hdr_line + hdr_prefix_len; 1503 i = 0; 1504 o = 0; 1505 1506 do 1507 { 1508 const char chr = c->rq.hdrs.rq_line.rq_tgt[i++]; 1509 1510 mhd_assert ('\r' != chr); /* Replaced during request line parsing */ 1511 mhd_assert ('\n' != chr); /* Rejected during request line parsing */ 1512 mhd_assert (0 != chr); /* Rejected during request line parsing */ 1513 switch (chr) 1514 { 1515 case ' ': 1516 b[o++] = '%'; 1517 b[o++] = '2'; 1518 b[o++] = '0'; 1519 break; 1520 case '\t': 1521 b[o++] = '%'; 1522 b[o++] = '0'; 1523 b[o++] = '9'; 1524 break; 1525 case 0x0B: /* VT (vertical tab) */ 1526 b[o++] = '%'; 1527 b[o++] = '0'; 1528 b[o++] = 'B'; 1529 break; 1530 case 0x0C: /* FF (form feed) */ 1531 b[o++] = '%'; 1532 b[o++] = '0'; 1533 b[o++] = 'C'; 1534 break; 1535 default: 1536 b[o++] = chr; 1537 break; 1538 } 1539 } while (i < c->rq.req_target_len); 1540 mhd_assert (fixed_uri_len == o); 1541 b[o] = 0; /* Zero-terminate the result */ 1542 1543 mhd_RESPOND_WITH_ERROR_HEADER (c, 1544 MHD_HTTP_STATUS_MOVED_PERMANENTLY, 1545 ERR_RSP_RQ_TARGET_INVALID_CHAR, 1546 o + hdr_prefix_len, 1547 hdr_line); 1548 1549 return; 1550 } 1551 1552 1553 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 1554 mhd_stream_get_request_line (struct MHD_Connection *restrict c) 1555 { 1556 const int discp_lvl = c->daemon->req_cfg.strictness; 1557 /* Parse whitespace in URI, special parsing of the request line */ 1558 const bool wsp_in_uri = (0 >= discp_lvl); 1559 /* Keep whitespace in URI, give app URI with whitespace instead of 1560 automatic redirect to fixed URI */ 1561 const bool wsp_in_uri_keep = (-2 >= discp_lvl); 1562 1563 if (! get_request_line_inner (c)) 1564 { 1565 /* End of the request line has not been found yet */ 1566 mhd_assert ((! wsp_in_uri) || NULL == c->rq.version); 1567 if ((NULL != c->rq.version) && 1568 (HTTP_VER_LEN < 1569 (c->rq.hdrs.rq_line.proc_pos 1570 - (size_t) (c->rq.version - c->read_buffer)))) 1571 { 1572 c->rq.http_ver = MHD_HTTP_VERSION_INVALID; 1573 mhd_RESPOND_WITH_ERROR_STATIC (c, 1574 MHD_HTTP_STATUS_BAD_REQUEST, 1575 ERR_RSP_REQUEST_MALFORMED); 1576 return true; /* Error in the request */ 1577 } 1578 return false; 1579 } 1580 if (mhd_HTTP_STAGE_REQ_LINE_RECEIVING < c->stage) 1581 return true; /* Error in the request */ 1582 1583 mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage); 1584 mhd_assert (NULL == c->rq.url); 1585 mhd_assert (0 == c->rq.url_len); 1586 mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt); 1587 if (0 != c->rq.hdrs.rq_line.num_ws_in_uri) 1588 { 1589 if (! wsp_in_uri) 1590 { 1591 mhd_RESPOND_WITH_ERROR_STATIC (c, 1592 MHD_HTTP_STATUS_BAD_REQUEST, 1593 ERR_RSP_RQ_TARGET_INVALID_CHAR); 1594 return true; /* Error in the request */ 1595 } 1596 if (! wsp_in_uri_keep) 1597 { 1598 send_redirect_fixed_rq_target (c); 1599 return true; /* Error in the request */ 1600 } 1601 } 1602 if (! process_request_target (c)) 1603 return true; /* Error in processing */ 1604 1605 c->stage = mhd_HTTP_STAGE_REQ_LINE_RECEIVED; 1606 return true; 1607 } 1608 1609 1610 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 1611 mhd_stream_switch_to_rq_headers_proc (struct MHD_Connection *restrict c) 1612 { 1613 c->rq.field_lines.start = c->read_buffer; 1614 mhd_stream_reset_rq_hdr_proc_state (c); 1615 c->stage = mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING; 1616 } 1617 1618 1619 /** 1620 * Send error reply when receive buffer space exhausted while receiving or 1621 * storing the request headers 1622 * @param c the connection to handle 1623 * @param add_header the optional pointer to the current header string being 1624 * processed or the header failed to be added. 1625 * Could be not zero-terminated and can contain binary zeros. 1626 * Can be NULL. 1627 * @param add_header_size the size of the @a add_header 1628 */ 1629 mhd_static_inline 1630 MHD_FN_PAR_NONNULL_ (1) void 1631 handle_req_headers_no_space (struct MHD_Connection *restrict c, 1632 const char *restrict add_header, 1633 size_t add_header_size) 1634 { 1635 unsigned int err_code; 1636 1637 err_code = mhd_stream_get_no_space_err_status_code (c, 1638 MHD_PROC_RECV_HEADERS, 1639 add_header_size, 1640 add_header); 1641 mhd_RESPOND_WITH_ERROR_STATIC (c, 1642 err_code, 1643 ERR_RSP_REQUEST_HEADER_TOO_BIG); 1644 } 1645 1646 1647 /** 1648 * Send error reply when receive buffer space exhausted while receiving or 1649 * storing the request footers (for chunked requests). 1650 * @param c the connection to handle 1651 * @param add_footer the optional pointer to the current footer string being 1652 * processed or the footer failed to be added. 1653 * Could be not zero-terminated and can contain binary zeros. 1654 * Can be NULL. 1655 * @param add_footer_size the size of the @a add_footer 1656 */ 1657 mhd_static_inline 1658 MHD_FN_PAR_NONNULL_ (1) void 1659 handle_req_footers_no_space (struct MHD_Connection *restrict c, 1660 const char *restrict add_footer, 1661 size_t add_footer_size) 1662 { 1663 (void) add_footer; (void) add_footer_size; /* Unused */ 1664 mhd_assert (c->rq.have_chunked_upload); 1665 1666 /* Footers should be optional */ 1667 mhd_RESPOND_WITH_ERROR_STATIC ( 1668 c, 1669 MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE, 1670 ERR_RSP_REQUEST_FOOTER_TOO_BIG); 1671 } 1672 1673 1674 /** 1675 * Results of header line reading 1676 */ 1677 enum MHD_FIXED_ENUM_ mhd_HdrLineReadRes 1678 { 1679 /** 1680 * Not enough data yet 1681 */ 1682 MHD_HDR_LINE_READING_NEED_MORE_DATA = 0, 1683 /** 1684 * New header line has been read 1685 */ 1686 MHD_HDR_LINE_READING_GOT_HEADER, 1687 /** 1688 * Error in header data, error response has been queued 1689 */ 1690 MHD_HDR_LINE_READING_DATA_ERROR, 1691 /** 1692 * Found the end of the request header (end of field lines) 1693 */ 1694 MHD_HDR_LINE_READING_GOT_END_OF_HEADER 1695 }; 1696 1697 1698 /** 1699 * Find the end of the request header line and make basic header parsing. 1700 * Handle errors and header folding. 1701 * @param c the connection to process 1702 * @param process_footers if true then footers are processed, 1703 * if false then headers are processed 1704 * @param[out] hdr_name the name of the parsed header (field) 1705 * @param[out] hdr_value the value of the parsed header (field) 1706 * @return mhd_HdrLineReadRes value 1707 */ 1708 static enum mhd_HdrLineReadRes 1709 get_req_header (struct MHD_Connection *restrict c, 1710 bool process_footers, 1711 struct MHD_String *restrict hdr_name, 1712 struct MHD_String *restrict hdr_value) 1713 { 1714 const int discp_lvl = c->daemon->req_cfg.strictness; 1715 /* Treat bare LF as the end of the line. 1716 RFC 9112, section 2.2-3 1717 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5). 1718 Bare LF is processed as end of the line or rejected as broken request. */ 1719 const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl); 1720 /* Keep bare CR character as is. 1721 Violates RFC 9112, section 2.2-4 */ 1722 const bool bare_cr_keep = (-3 >= discp_lvl); 1723 /* Treat bare CR as space; replace it with space before processing. 1724 RFC 9112, section 2.2-4 */ 1725 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl)); 1726 /* Treat NUL as space; replace it with space before processing. 1727 RFC 9110, section 5.5-5 */ 1728 const bool nul_as_sp = (-1 >= discp_lvl); 1729 /* Allow folded header lines. 1730 RFC 9112, section 5.2-4 */ 1731 const bool allow_folded = (0 >= discp_lvl); 1732 /* Do not reject headers with the whitespace at the start of the first line. 1733 When allowed, the first line with whitespace character at the first 1734 position is ignored (as well as all possible line foldings of the first 1735 line). 1736 RFC 9112, section 2.2-8 */ 1737 const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl); 1738 /* Allow whitespace in header (field) name. 1739 Violates RFC 9110, section 5.1-2 */ 1740 const bool allow_wsp_in_name = (-2 >= discp_lvl); 1741 /* Allow zero-length header (field) name. 1742 Violates RFC 9110, section 5.1-2 */ 1743 const bool allow_empty_name = (-2 >= discp_lvl); 1744 /* Allow whitespace before colon. 1745 Violates RFC 9112, section 5.1-2 */ 1746 const bool allow_wsp_before_colon = (-3 >= discp_lvl); 1747 /* Do not abort the request when header line has no colon, just skip such 1748 bad lines. 1749 RFC 9112, section 5-1 */ 1750 const bool allow_line_without_colon = (-2 >= discp_lvl); 1751 1752 size_t p; /**< The position of the currently processed character */ 1753 1754 (void) process_footers; /* Unused parameter in non-debug and no messages */ 1755 1756 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 1757 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 1758 c->stage); 1759 1760 p = c->rq.hdrs.hdr.proc_pos; 1761 1762 mhd_assert (p <= c->read_buffer_offset); 1763 while (p < c->read_buffer_offset) 1764 { 1765 char *const restrict read_buffer = c->read_buffer; 1766 const char chr = read_buffer[p]; 1767 bool end_of_line; 1768 1769 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \ 1770 (c->rq.hdrs.hdr.name_len < p)); 1771 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p)); 1772 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \ 1773 (c->rq.hdrs.hdr.name_end_found)); 1774 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \ 1775 (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start)); 1776 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \ 1777 (0 != c->rq.hdrs.hdr.name_len)); 1778 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \ 1779 (0 == c->rq.hdrs.hdr.name_len) || \ 1780 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len)); 1781 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \ 1782 (0 == c->rq.hdrs.hdr.value_start) || \ 1783 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start)); 1784 1785 /* Check for the end of the line */ 1786 if ('\r' == chr) 1787 { 1788 if (0 != p) 1789 { 1790 /* Line is not empty, need to check for possible line folding */ 1791 if (p + 2 >= c->read_buffer_offset) 1792 break; /* Not enough data yet to check for folded line */ 1793 } 1794 else 1795 { 1796 /* Line is empty, no need to check for possible line folding */ 1797 if (p + 2 > c->read_buffer_offset) 1798 break; /* Not enough data yet to check for the end of the line */ 1799 } 1800 if ('\n' == read_buffer[p + 1]) 1801 end_of_line = true; 1802 else 1803 { 1804 /* Bare CR alone */ 1805 /* Must be rejected or replaced with space char. 1806 See RFC 9112, section 2.2-4 */ 1807 if (bare_cr_as_sp) 1808 { 1809 read_buffer[p] = ' '; 1810 c->rq.num_cr_sp_replaced++; 1811 continue; /* Re-start processing of the current character */ 1812 } 1813 else if (! bare_cr_keep) 1814 { 1815 if (! process_footers) 1816 mhd_RESPOND_WITH_ERROR_STATIC (c, 1817 MHD_HTTP_STATUS_BAD_REQUEST, 1818 ERR_RSP_BARE_CR_IN_HEADER); 1819 else 1820 mhd_RESPOND_WITH_ERROR_STATIC (c, 1821 MHD_HTTP_STATUS_BAD_REQUEST, 1822 ERR_RSP_BARE_CR_IN_FOOTER); 1823 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1824 } 1825 end_of_line = false; 1826 } 1827 } 1828 else if ('\n' == chr) 1829 { 1830 /* Bare LF may be recognised as a line delimiter. 1831 See RFC 9112, section 2.2-3 */ 1832 if (bare_lf_as_crlf) 1833 { 1834 if (0 != p) 1835 { 1836 /* Line is not empty, need to check for possible line folding */ 1837 if (p + 1 >= c->read_buffer_offset) 1838 break; /* Not enough data yet to check for folded line */ 1839 } 1840 end_of_line = true; 1841 } 1842 else 1843 { 1844 if (! process_footers) 1845 mhd_RESPOND_WITH_ERROR_STATIC (c, 1846 MHD_HTTP_STATUS_BAD_REQUEST, 1847 ERR_RSP_BARE_LF_IN_HEADER); 1848 else 1849 mhd_RESPOND_WITH_ERROR_STATIC (c, 1850 MHD_HTTP_STATUS_BAD_REQUEST, 1851 ERR_RSP_BARE_LF_IN_FOOTER); 1852 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1853 } 1854 } 1855 else 1856 end_of_line = false; 1857 1858 if (end_of_line) 1859 { 1860 /* Handle the end of the line */ 1861 /** 1862 * The full length of the line, including CRLF (or bare LF). 1863 */ 1864 const size_t line_len = p + (('\r' == chr) ? 2 : 1); 1865 char next_line_char; 1866 mhd_assert (line_len <= c->read_buffer_offset); 1867 1868 if (0 == p) 1869 { 1870 /* Zero-length header line. This is the end of the request header 1871 section. 1872 RFC 9112, Section 2.1-1 */ 1873 mhd_assert (! c->rq.hdrs.hdr.starts_with_ws); 1874 mhd_assert (! c->rq.hdrs.hdr.name_end_found); 1875 mhd_assert (0 == c->rq.hdrs.hdr.name_len); 1876 mhd_assert (0 == c->rq.hdrs.hdr.ws_start); 1877 mhd_assert (0 == c->rq.hdrs.hdr.value_start); 1878 /* Consume the line with CRLF (or bare LF) */ 1879 c->read_buffer += line_len; 1880 c->read_buffer_offset -= line_len; 1881 c->read_buffer_size -= line_len; 1882 return MHD_HDR_LINE_READING_GOT_END_OF_HEADER; 1883 } 1884 1885 mhd_assert (line_len < c->read_buffer_offset); 1886 mhd_assert (0 != line_len); 1887 mhd_assert ('\n' == read_buffer[line_len - 1]); 1888 next_line_char = read_buffer[line_len]; 1889 if ((' ' == next_line_char) || 1890 ('\t' == next_line_char)) 1891 { 1892 /* Folded line */ 1893 if (! allow_folded) 1894 { 1895 if (! process_footers) 1896 mhd_RESPOND_WITH_ERROR_STATIC (c, 1897 MHD_HTTP_STATUS_BAD_REQUEST, 1898 ERR_RSP_OBS_FOLD); 1899 else 1900 mhd_RESPOND_WITH_ERROR_STATIC (c, 1901 MHD_HTTP_STATUS_BAD_REQUEST, 1902 ERR_RSP_OBS_FOLD_FOOTER); 1903 1904 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1905 } 1906 /* Replace CRLF (or bare LF) character(s) with space characters. 1907 See RFC 9112, Section 5.2-4 */ 1908 read_buffer[p] = ' '; 1909 if ('\r' == chr) 1910 read_buffer[p + 1] = ' '; 1911 continue; /* Re-start processing of the current character */ 1912 } 1913 else 1914 { 1915 /* It is not a folded line, it's the real end of the non-empty line */ 1916 bool skip_line = false; 1917 mhd_assert (0 != p); 1918 if (c->rq.hdrs.hdr.starts_with_ws) 1919 { 1920 /* This is the first line and it starts with whitespace. This line 1921 must be discarded completely. 1922 See RFC 9112, Section 2.2-8 */ 1923 mhd_assert (allow_wsp_at_start); 1924 1925 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FIRST_HEADER_LINE_SPACE_PREFIXED, 1926 "Whitespace-prefixed first header line " \ 1927 "has been skipped."); 1928 skip_line = true; 1929 } 1930 else if (! c->rq.hdrs.hdr.name_end_found) 1931 { 1932 if (! allow_line_without_colon) 1933 { 1934 if (! process_footers) 1935 mhd_RESPOND_WITH_ERROR_STATIC (c, 1936 MHD_HTTP_STATUS_BAD_REQUEST, 1937 ERR_RSP_HEADER_WITHOUT_COLON); 1938 else 1939 mhd_RESPOND_WITH_ERROR_STATIC (c, 1940 MHD_HTTP_STATUS_BAD_REQUEST, 1941 ERR_RSP_FOOTER_WITHOUT_COLON); 1942 1943 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 1944 } 1945 /* Skip broken line completely */ 1946 c->rq.skipped_broken_lines++; 1947 skip_line = true; 1948 } 1949 if (skip_line) 1950 { 1951 /* Skip the entire line */ 1952 c->read_buffer += line_len; 1953 c->read_buffer_offset -= line_len; 1954 c->read_buffer_size -= line_len; 1955 p = 0; 1956 /* Reset processing state */ 1957 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr)); 1958 /* Start processing of the next line */ 1959 continue; 1960 } 1961 else 1962 { 1963 /* This line should be valid header line */ 1964 size_t value_len; 1965 mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name); 1966 1967 hdr_name->cstr = read_buffer + 0; /* The name always starts at the first character */ 1968 hdr_name->len = c->rq.hdrs.hdr.name_len; 1969 mhd_assert (0 == hdr_name->cstr[hdr_name->len]); 1970 1971 if (0 == c->rq.hdrs.hdr.value_start) 1972 { 1973 c->rq.hdrs.hdr.value_start = p; 1974 read_buffer[p] = 0; 1975 value_len = 0; 1976 } 1977 else if (0 != c->rq.hdrs.hdr.ws_start) 1978 { 1979 mhd_assert (p > c->rq.hdrs.hdr.ws_start); 1980 mhd_assert (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start); 1981 read_buffer[c->rq.hdrs.hdr.ws_start] = 0; 1982 value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start; 1983 } 1984 else 1985 { 1986 mhd_assert (p > c->rq.hdrs.hdr.ws_start); 1987 read_buffer[p] = 0; 1988 value_len = p - c->rq.hdrs.hdr.value_start; 1989 } 1990 hdr_value->cstr = read_buffer + c->rq.hdrs.hdr.value_start; 1991 hdr_value->len = value_len; 1992 mhd_assert (0 == hdr_value->cstr[hdr_value->len]); 1993 /* Consume the entire line */ 1994 c->read_buffer += line_len; 1995 c->read_buffer_offset -= line_len; 1996 c->read_buffer_size -= line_len; 1997 return MHD_HDR_LINE_READING_GOT_HEADER; 1998 } 1999 } 2000 } 2001 else if ((' ' == chr) || ('\t' == chr)) 2002 { 2003 if (0 == p) 2004 { 2005 if (! allow_wsp_at_start) 2006 { 2007 if (! process_footers) 2008 mhd_RESPOND_WITH_ERROR_STATIC (c, 2009 MHD_HTTP_STATUS_BAD_REQUEST, 2010 ERR_RSP_WSP_BEFORE_HEADER); 2011 else 2012 mhd_RESPOND_WITH_ERROR_STATIC (c, 2013 MHD_HTTP_STATUS_BAD_REQUEST, 2014 ERR_RSP_WSP_BEFORE_FOOTER); 2015 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2016 } 2017 c->rq.hdrs.hdr.starts_with_ws = true; 2018 } 2019 else if ((! c->rq.hdrs.hdr.name_end_found) && 2020 (! c->rq.hdrs.hdr.starts_with_ws)) 2021 { 2022 /* Whitespace in header name / between header name and colon */ 2023 if (allow_wsp_in_name || allow_wsp_before_colon) 2024 { 2025 if (0 == c->rq.hdrs.hdr.ws_start) 2026 c->rq.hdrs.hdr.ws_start = p; 2027 } 2028 else 2029 { 2030 if (! process_footers) 2031 mhd_RESPOND_WITH_ERROR_STATIC (c, 2032 MHD_HTTP_STATUS_BAD_REQUEST, 2033 ERR_RSP_WSP_IN_HEADER_NAME); 2034 else 2035 mhd_RESPOND_WITH_ERROR_STATIC (c, 2036 MHD_HTTP_STATUS_BAD_REQUEST, 2037 ERR_RSP_WSP_IN_FOOTER_NAME); 2038 2039 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2040 } 2041 } 2042 else 2043 { 2044 /* Whitespace before/inside/after header (field) value */ 2045 if (0 == c->rq.hdrs.hdr.ws_start) 2046 c->rq.hdrs.hdr.ws_start = p; 2047 } 2048 } 2049 else if (0 == chr) 2050 { 2051 if (! nul_as_sp) 2052 { 2053 if (! process_footers) 2054 mhd_RESPOND_WITH_ERROR_STATIC (c, 2055 MHD_HTTP_STATUS_BAD_REQUEST, 2056 ERR_RSP_INVALID_CHR_IN_HEADER); 2057 else 2058 mhd_RESPOND_WITH_ERROR_STATIC (c, 2059 MHD_HTTP_STATUS_BAD_REQUEST, 2060 ERR_RSP_INVALID_CHR_IN_FOOTER); 2061 2062 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2063 } 2064 read_buffer[p] = ' '; 2065 continue; /* Re-start processing of the current character */ 2066 } 2067 else 2068 { 2069 /* Not a whitespace, not the end of the header line */ 2070 mhd_assert ('\r' != chr); 2071 mhd_assert ('\n' != chr); 2072 mhd_assert ('\0' != chr); 2073 if ((! c->rq.hdrs.hdr.name_end_found) && 2074 (! c->rq.hdrs.hdr.starts_with_ws)) 2075 { 2076 /* Processing the header (field) name */ 2077 if (':' == chr) 2078 { 2079 if (0 == c->rq.hdrs.hdr.ws_start) 2080 c->rq.hdrs.hdr.name_len = p; 2081 else 2082 { 2083 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon); 2084 if (! allow_wsp_before_colon) 2085 { 2086 if (! process_footers) 2087 mhd_RESPOND_WITH_ERROR_STATIC (c, 2088 MHD_HTTP_STATUS_BAD_REQUEST, 2089 ERR_RSP_WSP_IN_HEADER_NAME); 2090 else 2091 mhd_RESPOND_WITH_ERROR_STATIC (c, 2092 MHD_HTTP_STATUS_BAD_REQUEST, 2093 ERR_RSP_WSP_IN_FOOTER_NAME); 2094 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2095 } 2096 c->rq.hdrs.hdr.name_len = c->rq.hdrs.hdr.ws_start; 2097 #ifndef MHD_FAVOR_SMALL_CODE 2098 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */ 2099 #endif /* ! MHD_FAVOR_SMALL_CODE */ 2100 } 2101 if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name) 2102 { 2103 if (! process_footers) 2104 mhd_RESPOND_WITH_ERROR_STATIC (c, 2105 MHD_HTTP_STATUS_BAD_REQUEST, 2106 ERR_RSP_EMPTY_HEADER_NAME); 2107 else 2108 mhd_RESPOND_WITH_ERROR_STATIC (c, 2109 MHD_HTTP_STATUS_BAD_REQUEST, 2110 ERR_RSP_EMPTY_FOOTER_NAME); 2111 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2112 } 2113 c->rq.hdrs.hdr.name_end_found = true; 2114 read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */ 2115 } 2116 else 2117 { 2118 if (0 != c->rq.hdrs.hdr.ws_start) 2119 { 2120 /* End of the whitespace in header (field) name */ 2121 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon); 2122 if (! allow_wsp_in_name) 2123 { 2124 if (! process_footers) 2125 mhd_RESPOND_WITH_ERROR_STATIC (c, 2126 MHD_HTTP_STATUS_BAD_REQUEST, 2127 ERR_RSP_WSP_IN_HEADER_NAME); 2128 else 2129 mhd_RESPOND_WITH_ERROR_STATIC (c, 2130 MHD_HTTP_STATUS_BAD_REQUEST, 2131 ERR_RSP_WSP_IN_FOOTER_NAME); 2132 2133 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */ 2134 } 2135 #ifndef MHD_FAVOR_SMALL_CODE 2136 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */ 2137 #endif /* ! MHD_FAVOR_SMALL_CODE */ 2138 } 2139 } 2140 } 2141 else 2142 { 2143 /* Processing the header (field) value */ 2144 if (0 == c->rq.hdrs.hdr.value_start) 2145 c->rq.hdrs.hdr.value_start = p; 2146 #ifndef MHD_FAVOR_SMALL_CODE 2147 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */ 2148 #endif /* ! MHD_FAVOR_SMALL_CODE */ 2149 } 2150 #ifdef MHD_FAVOR_SMALL_CODE 2151 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */ 2152 #endif /* MHD_FAVOR_SMALL_CODE */ 2153 } 2154 p++; 2155 } 2156 c->rq.hdrs.hdr.proc_pos = p; 2157 return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */ 2158 } 2159 2160 2161 /** 2162 * Reset request header processing state. 2163 * 2164 * This function resets the processing state before processing the next header 2165 * (or footer) line. 2166 * @param c the connection to process 2167 */ 2168 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 2169 mhd_stream_reset_rq_hdr_proc_state (struct MHD_Connection *c) 2170 { 2171 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr)); 2172 } 2173 2174 2175 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 2176 mhd_stream_get_request_headers (struct MHD_Connection *restrict c, 2177 bool process_footers) 2178 { 2179 do 2180 { 2181 struct MHD_String hdr_name; 2182 struct MHD_String hdr_value; 2183 enum mhd_HdrLineReadRes res; 2184 2185 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2186 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 2187 c->stage); 2188 2189 #ifndef NDEBUG 2190 hdr_name.cstr = NULL; 2191 hdr_value.cstr = NULL; 2192 #endif /* ! NDEBUG */ 2193 res = get_req_header (c, process_footers, &hdr_name, &hdr_value); 2194 if (MHD_HDR_LINE_READING_GOT_HEADER == res) 2195 { 2196 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2197 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 2198 c->stage); 2199 mhd_assert (NULL != hdr_name.cstr); 2200 mhd_assert (NULL != hdr_value.cstr); 2201 /* Values must be zero-terminated and must not have binary zeros */ 2202 mhd_assert (strlen (hdr_name.cstr) == hdr_name.len); 2203 mhd_assert (strlen (hdr_value.cstr) == hdr_value.len); 2204 /* Values must not have whitespaces at the start or at the end */ 2205 mhd_assert ((hdr_name.len == 0) || (hdr_name.cstr[0] != ' ')); 2206 mhd_assert ((hdr_name.len == 0) || (hdr_name.cstr[0] != '\t')); 2207 mhd_assert ((hdr_name.len == 0) || \ 2208 (hdr_name.cstr[hdr_name.len - 1] != ' ')); 2209 mhd_assert ((hdr_name.len == 0) || \ 2210 (hdr_name.cstr[hdr_name.len - 1] != '\t')); 2211 mhd_assert ((hdr_value.len == 0) || (hdr_value.cstr[0] != ' ')); 2212 mhd_assert ((hdr_value.len == 0) || (hdr_value.cstr[0] != '\t')); 2213 mhd_assert ((hdr_value.len == 0) || \ 2214 (hdr_value.cstr[hdr_value.len - 1] != ' ')); 2215 mhd_assert ((hdr_value.len == 0) || \ 2216 (hdr_value.cstr[hdr_value.len - 1] != '\t')); 2217 2218 if (! mhd_stream_add_field (&(c->h1_stream), 2219 process_footers ? 2220 MHD_VK_TRAILER : MHD_VK_HEADER, 2221 &hdr_name, 2222 &hdr_value)) 2223 { 2224 size_t add_element_size; 2225 2226 mhd_assert (hdr_name.cstr < hdr_value.cstr); 2227 2228 if (! process_footers) 2229 mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_REQ, \ 2230 "Failed to allocate memory in the connection memory " \ 2231 "pool to store header."); 2232 else 2233 mhd_LOG_MSG (c->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_REQ, \ 2234 "Failed to allocate memory in the connection memory " \ 2235 "pool to store footer."); 2236 2237 add_element_size = hdr_value.len 2238 + (size_t) (hdr_value.cstr - hdr_name.cstr); 2239 2240 if (! process_footers) 2241 handle_req_headers_no_space (c, hdr_name.cstr, add_element_size); 2242 else 2243 handle_req_footers_no_space (c, hdr_name.cstr, add_element_size); 2244 2245 mhd_assert (mhd_HTTP_STAGE_FULL_REQ_RECEIVED < c->stage); 2246 return true; 2247 } 2248 /* Reset processing state */ 2249 mhd_stream_reset_rq_hdr_proc_state (c); 2250 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2251 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 2252 c->stage); 2253 /* Read the next header (field) line */ 2254 continue; 2255 } 2256 else if (MHD_HDR_LINE_READING_NEED_MORE_DATA == res) 2257 { 2258 mhd_assert ((process_footers ? mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2259 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) == \ 2260 c->stage); 2261 return false; 2262 } 2263 else if (MHD_HDR_LINE_READING_DATA_ERROR == res) 2264 { 2265 mhd_assert ((process_footers ? \ 2266 mhd_HTTP_STAGE_FOOTERS_RECEIVING : \ 2267 mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING) < c->stage); 2268 mhd_assert (c->stop_with_error); 2269 mhd_assert (c->discard_request); 2270 return true; 2271 } 2272 mhd_assert (MHD_HDR_LINE_READING_GOT_END_OF_HEADER == res); 2273 break; 2274 } while (1); 2275 2276 if (1 == c->rq.num_cr_sp_replaced) 2277 { 2278 if (! process_footers) 2279 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2280 "One bare CR character has been replaced with space " \ 2281 "in the request line or in the request headers."); 2282 else 2283 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_CR_REPLACED, \ 2284 "One bare CR character has been replaced with space " \ 2285 "in the request footers."); 2286 } 2287 else if (0 != c->rq.num_cr_sp_replaced) 2288 { 2289 if (! process_footers) 2290 mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2291 mhd_LOG_FMT ("%" PRIuFAST64 " bare CR characters have " \ 2292 "been replaced with spaces in the request " \ 2293 "line and/or in the request headers."), \ 2294 (uint_fast64_t) c->rq.num_cr_sp_replaced); 2295 else 2296 mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2297 mhd_LOG_FMT ("%" PRIuFAST64 " bare CR characters have " \ 2298 "been replaced with spaces in the request " \ 2299 "footers."), \ 2300 (uint_fast64_t) c->rq.num_cr_sp_replaced); 2301 2302 2303 } 2304 if (1 == c->rq.skipped_broken_lines) 2305 { 2306 if (! process_footers) 2307 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_HEADER_LINE_NO_COLON, \ 2308 "One header line without colon has been skipped."); 2309 else 2310 mhd_LOG_MSG (c->daemon, MHD_SC_REQ_FOOTER_LINE_NO_COLON, \ 2311 "One footer line without colon has been skipped."); 2312 } 2313 else if (0 != c->rq.skipped_broken_lines) 2314 { 2315 if (! process_footers) 2316 mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2317 mhd_LOG_FMT ("%" PRIu64 " header lines without colons " 2318 "have been skipped."), 2319 (uint_fast64_t) c->rq.skipped_broken_lines); 2320 else 2321 mhd_LOG_PRINT (c->daemon, MHD_SC_REQ_HEADER_CR_REPLACED, \ 2322 mhd_LOG_FMT ("%" PRIu64 " footer lines without colons " 2323 "have been skipped."), 2324 (uint_fast64_t) c->rq.skipped_broken_lines); 2325 } 2326 2327 mhd_assert (c->rq.method.cstr < c->read_buffer); 2328 if (! process_footers) 2329 { 2330 c->rq.header_size = (size_t) (c->read_buffer - c->rq.method.cstr); 2331 mhd_assert (NULL != c->rq.field_lines.start); 2332 c->rq.field_lines.size = 2333 (size_t) ((c->read_buffer - c->rq.field_lines.start) - 1); 2334 if ('\r' == *(c->read_buffer - 2)) 2335 c->rq.field_lines.size--; 2336 c->stage = mhd_HTTP_STAGE_HEADERS_RECEIVED; 2337 2338 if (mhd_BUF_INC_SIZE > c->read_buffer_size) 2339 { 2340 /* Try to re-use some of the last bytes of the request header */ 2341 /* Do this only if space in the read buffer is limited AND 2342 amount of read ahead data is small. */ 2343 /** 2344 * The position of the terminating NUL after the last character of 2345 * the last header element. 2346 */ 2347 const char *last_elmnt_end; 2348 size_t shift_back_size; 2349 struct mhd_RequestField *header; 2350 header = mhd_DLINKEDL_GET_LAST (&(c->rq), fields); 2351 if (NULL != header) 2352 last_elmnt_end = 2353 header->field.nv.value.cstr + header->field.nv.value.len; 2354 else 2355 last_elmnt_end = c->rq.version + HTTP_VER_LEN; 2356 mhd_assert ((last_elmnt_end + 1) < c->read_buffer); 2357 shift_back_size = (size_t) (c->read_buffer - (last_elmnt_end + 1)); 2358 if (0 != c->read_buffer_offset) 2359 memmove (c->read_buffer - shift_back_size, 2360 c->read_buffer, 2361 c->read_buffer_offset); 2362 c->read_buffer -= shift_back_size; 2363 c->read_buffer_size += shift_back_size; 2364 } 2365 } 2366 else 2367 c->stage = mhd_HTTP_STAGE_FOOTERS_RECEIVED; 2368 2369 return true; 2370 } 2371 2372 2373 #ifdef MHD_SUPPORT_COOKIES 2374 2375 /** 2376 * Cookie parsing result 2377 */ 2378 enum mhd_ParseCookie 2379 { 2380 MHD_PARSE_COOKIE_OK_LAX = 2 /**< Cookies parsed, but workarounds used */ 2381 , 2382 MHD_PARSE_COOKIE_OK = 1 /**< Success or no cookies in headers */ 2383 , 2384 MHD_PARSE_COOKIE_NO_MEMORY = 0 /**< Not enough memory in the pool */ 2385 , 2386 MHD_PARSE_COOKIE_MALFORMED = -1 /**< Invalid cookie header */ 2387 }; 2388 2389 2390 /** 2391 * Parse the cookies string (see RFC 6265). 2392 * 2393 * Try to parse the cookies string even if it is not strictly formed 2394 * as specified by RFC 6265. 2395 * 2396 * @param str_len the size of the @a str, not including mandatory 2397 * zero-termination 2398 * @param str the string to parse, without leading whitespaces 2399 * @param strictness the protocol strictness 2400 * @param s the stream to process 2401 * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise 2402 */ 2403 static MHD_FN_PAR_NONNULL_ALL_ 2404 MHD_FN_PAR_CSTR_ (2) 2405 MHD_FN_PAR_INOUT_SIZE_ (2,1) enum mhd_ParseCookie 2406 parse_cookies_string (const size_t str_len, 2407 char *restrict str, 2408 enum MHD_ProtocolStrictLevel strictness, 2409 struct MHD_Stream *restrict s) 2410 { 2411 size_t i; 2412 bool non_strict; 2413 /* Skip extra whitespaces and empty cookies */ 2414 const bool allow_wsp_empty = (MHD_PSL_DEFAULT >= strictness); 2415 /* Allow whitespaces around '=' character */ 2416 const bool wsp_around_eq = (MHD_PSL_EXTRA_PERMISSIVE >= strictness); 2417 /* Allow whitespaces in quoted cookie value */ 2418 const bool wsp_in_quoted = (MHD_PSL_VERY_PERMISSIVE >= strictness); 2419 /* Allow tab as space after semicolon between cookies */ 2420 const bool tab_as_sp = (MHD_PSL_DEFAULT >= strictness); 2421 /* Allow no space after semicolon between cookies */ 2422 const bool allow_no_space = (MHD_PSL_DEFAULT >= strictness); 2423 2424 non_strict = false; 2425 i = 0; 2426 while (i < str_len) 2427 { 2428 size_t name_start; 2429 size_t name_len; 2430 size_t value_start; 2431 size_t value_len; 2432 bool val_quoted; 2433 /* Skip any whitespaces and empty cookies */ 2434 while (' ' == str[i] || '\t' == str[i] || ';' == str[i]) 2435 { 2436 if (! allow_wsp_empty) 2437 return MHD_PARSE_COOKIE_MALFORMED; 2438 non_strict = true; 2439 i++; 2440 if (i == str_len) 2441 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK; 2442 } 2443 /* 'i' must point to the first char of cookie-name */ 2444 name_start = i; 2445 /* Find the end of the cookie-name */ 2446 do 2447 { 2448 const char l = str[i]; 2449 if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) || 2450 (';' == l) || (0 == l)) 2451 break; 2452 } while (str_len > ++i); 2453 name_len = i - name_start; 2454 /* Skip any whitespaces */ 2455 while (str_len > i && (' ' == str[i] || '\t' == str[i])) 2456 { 2457 if (! wsp_around_eq) 2458 return MHD_PARSE_COOKIE_MALFORMED; 2459 non_strict = true; 2460 i++; 2461 } 2462 if ((str_len == i) || ('=' != str[i]) || (0 == name_len)) 2463 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */ 2464 /* 'i' must point to the '=' char */ 2465 mhd_assert ('=' == str[i]); 2466 i++; 2467 /* Skip any whitespaces */ 2468 while (str_len > i && (' ' == str[i] || '\t' == str[i])) 2469 { 2470 if (! wsp_around_eq) 2471 return MHD_PARSE_COOKIE_MALFORMED; 2472 non_strict = true; 2473 i++; 2474 } 2475 /* 'i' must point to the first char of cookie-value */ 2476 if (str_len == i) 2477 { 2478 value_start = 0; 2479 value_len = 0; 2480 #ifndef NDEBUG 2481 val_quoted = false; /* This assignment used in assert */ 2482 #endif 2483 } 2484 else 2485 { 2486 bool valid_cookie; 2487 val_quoted = ('"' == str[i]); 2488 if (val_quoted) 2489 i++; 2490 value_start = i; 2491 /* Find the end of the cookie-value */ 2492 while (str_len > i) 2493 { 2494 const char l = str[i]; 2495 if ((';' == l) || ('"' == l) || (',' == l) || ('\\' == l) || (0 == l)) 2496 break; 2497 if ((' ' == l) || ('\t' == l)) 2498 { 2499 if (! val_quoted) 2500 break; 2501 if (! wsp_in_quoted) 2502 return MHD_PARSE_COOKIE_MALFORMED; 2503 non_strict = true; 2504 } 2505 i++; 2506 } 2507 value_len = i - value_start; 2508 if (val_quoted) 2509 { 2510 if ((str_len == i) || ('"' != str[i])) 2511 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */ 2512 i++; 2513 } 2514 /* Skip any whitespaces */ 2515 if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i]))) 2516 { 2517 do 2518 { 2519 i++; 2520 } while (str_len > i && (' ' == str[i] || '\t' == str[i])); 2521 /* Whitespace at the end? */ 2522 if (str_len > i) 2523 { 2524 if (! allow_wsp_empty) 2525 return MHD_PARSE_COOKIE_MALFORMED; 2526 non_strict = true; 2527 } 2528 } 2529 if (str_len == i) 2530 valid_cookie = true; 2531 else if (';' == str[i]) 2532 valid_cookie = true; 2533 else 2534 valid_cookie = false; 2535 2536 if (! valid_cookie) 2537 return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */ 2538 } 2539 2540 mhd_ASSUME (0u != name_len); 2541 mhd_ASSUME (str_len > name_start + name_len); 2542 str[name_start + name_len] = '\0'; 2543 2544 if (0 != value_len) 2545 { 2546 struct MHD_String name; 2547 struct MHD_String value; 2548 mhd_ASSUME (str_len >= value_start + value_len); 2549 name.cstr = str + name_start; 2550 name.len = name_len; 2551 if (value_start + value_len < str_len) /* Do not write outside allowed area */ 2552 str[value_start + value_len] = '\0'; /* Zero-terminate the value */ 2553 value.cstr = str + value_start; 2554 value.len = value_len; 2555 if (! mhd_stream_add_field (s, 2556 MHD_VK_COOKIE, 2557 &name, 2558 &value)) 2559 return MHD_PARSE_COOKIE_NO_MEMORY; 2560 } 2561 else 2562 { 2563 struct MHD_String name; 2564 struct MHD_String value; 2565 name.cstr = str + name_start; 2566 name.len = name_len; 2567 value.cstr = ""; 2568 value.len = 0; 2569 if (! mhd_stream_add_field (s, 2570 MHD_VK_COOKIE, 2571 &name, 2572 &value)) 2573 return MHD_PARSE_COOKIE_NO_MEMORY; 2574 } 2575 if (str_len > i) 2576 { 2577 mhd_assert (0 == str[i] || ';' == str[i]); 2578 mhd_assert (! val_quoted || ';' == str[i]); 2579 mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len); 2580 i++; 2581 if (str_len == i) 2582 { /* No next cookie after semicolon */ 2583 if (! allow_wsp_empty) 2584 return MHD_PARSE_COOKIE_MALFORMED; 2585 non_strict = true; 2586 } 2587 else if (' ' != str[i]) 2588 {/* No space after semicolon */ 2589 if (('\t' == str[i]) && tab_as_sp) 2590 i++; 2591 else if (! allow_no_space) 2592 return MHD_PARSE_COOKIE_MALFORMED; 2593 non_strict = true; 2594 } 2595 else 2596 { 2597 i++; 2598 if (str_len == i) 2599 { 2600 if (! allow_wsp_empty) 2601 return MHD_PARSE_COOKIE_MALFORMED; 2602 non_strict = true; 2603 } 2604 } 2605 } 2606 } 2607 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK; 2608 } 2609 2610 2611 /** 2612 * Parse the cookie header (see RFC 6265). 2613 * 2614 * @param connection connection to parse header of 2615 * @param cookie_val the value of the "Cookie:" header 2616 * @return #MHD_PARSE_COOKIE_OK for success, error code otherwise 2617 */ 2618 static enum mhd_ParseCookie 2619 parse_cookie_header (struct MHD_Connection *restrict connection, 2620 struct MHD_StringNullable *restrict cookie_val) 2621 { 2622 char *cpy; 2623 size_t i; 2624 enum mhd_ParseCookie parse_res; 2625 struct mhd_RequestField *const saved_tail = 2626 connection->rq.fields.last; // FIXME: a better way? 2627 const bool allow_partially_correct_cookie = 2628 (1 >= connection->daemon->req_cfg.strictness); 2629 2630 if (NULL == cookie_val) 2631 return MHD_PARSE_COOKIE_OK; 2632 if (0 == cookie_val->len) 2633 return MHD_PARSE_COOKIE_OK; 2634 2635 cpy = (char *) mhd_stream_alloc_memory (connection, 2636 cookie_val->len + 1); 2637 if (NULL == cpy) 2638 parse_res = MHD_PARSE_COOKIE_NO_MEMORY; 2639 else 2640 { 2641 memcpy (cpy, 2642 cookie_val->cstr, 2643 cookie_val->len + 1); 2644 mhd_assert (0 == cpy[cookie_val->len]); 2645 2646 /* Must not have initial whitespaces */ 2647 mhd_assert (' ' != cpy[0]); 2648 mhd_assert ('\t' != cpy[0]); 2649 2650 i = 0; 2651 parse_res = parse_cookies_string (cookie_val->len - i, 2652 cpy + i, 2653 connection->daemon->req_cfg.strictness, 2654 &(connection->h1_stream)); 2655 } 2656 2657 switch (parse_res) 2658 { 2659 case MHD_PARSE_COOKIE_OK: 2660 break; 2661 case MHD_PARSE_COOKIE_OK_LAX: 2662 if (saved_tail != connection->rq.fields.last) 2663 mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_PARSED_NOT_COMPLIANT, \ 2664 "The Cookie header has been parsed, but it is not " 2665 "fully compliant with specifications."); 2666 break; 2667 case MHD_PARSE_COOKIE_MALFORMED: 2668 if (saved_tail != connection->rq.fields.last) // FIXME: a better way? 2669 { 2670 if (! allow_partially_correct_cookie) 2671 { 2672 /* Remove extracted values from partially broken cookie */ 2673 /* Memory remains allocated until the end of the request processing */ 2674 connection->rq.fields.last = saved_tail; // FIXME: a better way? 2675 saved_tail->fields.next = NULL; // FIXME: a better way? 2676 mhd_LOG_MSG ( \ 2677 connection->daemon, MHD_SC_REQ_COOKIE_IGNORED_NOT_COMPLIANT, \ 2678 "The Cookie header is ignored as it contains malformed data."); 2679 } 2680 else 2681 mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_PARSED_PARTIALLY, \ 2682 "The Cookie header has been only partially parsed " \ 2683 "as it contains malformed data."); 2684 } 2685 else 2686 mhd_LOG_MSG (connection->daemon, MHD_SC_REQ_COOKIE_INVALID, 2687 "The Cookie header has malformed data."); 2688 break; 2689 case MHD_PARSE_COOKIE_NO_MEMORY: 2690 mhd_LOG_MSG (connection->daemon, MHD_SC_CONNECTION_POOL_NO_MEM_COOKIE, 2691 "Not enough memory in the connection pool to " 2692 "parse client cookies!\n"); 2693 break; 2694 default: 2695 mhd_UNREACHABLE (); 2696 break; 2697 } 2698 2699 return parse_res; 2700 } 2701 2702 2703 /** 2704 * Send error reply when receive buffer space exhausted while receiving or 2705 * storing the request headers 2706 * @param c the connection to handle 2707 */ 2708 mhd_static_inline void 2709 handle_req_cookie_no_space (struct MHD_Connection *restrict c) 2710 { 2711 unsigned int err_code; 2712 2713 err_code = mhd_stream_get_no_space_err_status_code (c, 2714 MHD_PROC_RECV_COOKIE, 2715 0, 2716 NULL); 2717 mhd_RESPOND_WITH_ERROR_STATIC (c, 2718 err_code, 2719 ERR_RSP_REQUEST_HEADER_TOO_BIG); 2720 } 2721 2722 2723 #endif /* MHD_SUPPORT_COOKIES */ 2724 2725 2726 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 2727 mhd_stream_parse_request_headers (struct MHD_Connection *restrict c) 2728 { 2729 bool has_host; 2730 bool has_trenc; 2731 bool has_cntnlen; 2732 bool has_keepalive; 2733 struct mhd_RequestField *f; 2734 2735 /* The presence of the request body is indicated by "Content-Length:" or 2736 "Transfer-Encoding:" request headers. 2737 Unless one of these two headers is used, the request has no request body. 2738 See RFC9112, Section 6, paragraph 4. */ 2739 c->rq.have_chunked_upload = false; 2740 c->rq.cntn.cntn_size = 0; 2741 2742 has_host = false; 2743 has_trenc = false; 2744 has_cntnlen = false; 2745 has_keepalive = true; 2746 2747 for (f = mhd_DLINKEDL_GET_FIRST (&(c->rq), fields); 2748 NULL != f; 2749 f = mhd_DLINKEDL_GET_NEXT (f, fields)) 2750 { 2751 if (MHD_VK_HEADER != f->field.kind) 2752 continue; 2753 2754 /* "Host:" */ 2755 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_HOST, 2756 f->field.nv.name.cstr, 2757 f->field.nv.name.len)) 2758 { 2759 if ((has_host) 2760 && (-3 < c->daemon->req_cfg.strictness)) 2761 { 2762 mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_SEVERAL, \ 2763 "Received request with more than one 'Host' header."); 2764 mhd_RESPOND_WITH_ERROR_STATIC (c, 2765 MHD_HTTP_STATUS_BAD_REQUEST, 2766 ERR_RSP_REQUEST_HAS_SEVERAL_HOSTS); 2767 return; 2768 } 2769 if ((0u == f->field.nv.value.len) 2770 && (-3 < c->daemon->req_cfg.strictness)) 2771 { 2772 mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_MALFORMED, \ 2773 "Received request with empty 'Host' header."); 2774 mhd_RESPOND_WITH_ERROR_STATIC (c, 2775 MHD_HTTP_STATUS_BAD_REQUEST, 2776 ERR_RSP_REQUEST_HAS_MALFORMED_HOST); 2777 return; 2778 } 2779 has_host = true; 2780 continue; 2781 } 2782 2783 /* "Content-Length:" */ 2784 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONTENT_LENGTH, 2785 f->field.nv.name.cstr, 2786 f->field.nv.name.len)) 2787 { 2788 size_t num_digits; 2789 uint_fast64_t cntn_size; 2790 2791 num_digits = mhd_str_to_uint64_n (f->field.nv.value.cstr, 2792 f->field.nv.value.len, 2793 &cntn_size); 2794 if (((0 == num_digits) && 2795 (0 != f->field.nv.value.len) && 2796 ('9' >= f->field.nv.value.cstr[0]) 2797 && ('0' <= f->field.nv.value.cstr[0])) 2798 || (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size)) 2799 { 2800 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_TOO_LARGE, \ 2801 "Too large value of 'Content-Length' header. " \ 2802 "Closing connection."); 2803 mhd_RESPOND_WITH_ERROR_STATIC (c, \ 2804 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, \ 2805 ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE); 2806 return; 2807 } 2808 else if ((f->field.nv.value.len != num_digits) || 2809 (0 == num_digits)) 2810 { 2811 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_MALFORMED, \ 2812 "Failed to parse 'Content-Length' header. " \ 2813 "Closing connection."); 2814 mhd_RESPOND_WITH_ERROR_STATIC (c, \ 2815 MHD_HTTP_STATUS_BAD_REQUEST, \ 2816 ERR_RSP_REQUEST_CONTENTLENGTH_MALFORMED); 2817 return; 2818 } 2819 2820 if (has_cntnlen) 2821 { 2822 bool send_err; 2823 send_err = false; 2824 if (c->rq.cntn.cntn_size == cntn_size) 2825 { 2826 if (0 < c->daemon->req_cfg.strictness) 2827 { 2828 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_SEVERAL_SAME, \ 2829 "Received request with more than one " \ 2830 "'Content-Length' header with the same value."); 2831 send_err = true; 2832 } 2833 } 2834 else 2835 { 2836 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_SEVERAL_DIFFERENT, \ 2837 "Received request with more than one " \ 2838 "'Content-Length' header with conflicting values."); 2839 send_err = true; 2840 } 2841 2842 if (send_err) 2843 { 2844 mhd_RESPOND_WITH_ERROR_STATIC ( \ 2845 c, \ 2846 MHD_HTTP_STATUS_BAD_REQUEST, \ 2847 ERR_RSP_REQUEST_CONTENTLENGTH_SEVERAL); 2848 return; 2849 } 2850 } 2851 mhd_assert ((0 == c->rq.cntn.cntn_size) || \ 2852 (c->rq.cntn.cntn_size == cntn_size)); 2853 c->rq.cntn.cntn_size = cntn_size; 2854 has_cntnlen = true; 2855 continue; 2856 } 2857 2858 /* "Connection:" */ 2859 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONNECTION, 2860 f->field.nv.name.cstr, 2861 f->field.nv.name.len)) 2862 { 2863 if (mhd_str_has_token_caseless (f->field.nv.value.cstr, // TODO: compare as size string 2864 "close", 2865 mhd_SSTR_LEN ("close"))) 2866 { 2867 mhd_assert (mhd_CONN_MUST_UPGRADE != c->conn_reuse); 2868 c->conn_reuse = mhd_CONN_MUST_CLOSE; 2869 } 2870 else if ((MHD_HTTP_VERSION_1_0 == c->rq.http_ver) 2871 && (mhd_CONN_MUST_CLOSE != c->conn_reuse)) 2872 { 2873 if (mhd_str_has_token_caseless (f->field.nv.value.cstr, // TODO: compare as size string 2874 "keep-alive", 2875 mhd_SSTR_LEN ("keep-alive"))) 2876 has_keepalive = true; 2877 } 2878 2879 continue; 2880 } 2881 2882 /* "Transfer-Encoding:" */ 2883 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_TRANSFER_ENCODING, 2884 f->field.nv.name.cstr, 2885 f->field.nv.name.len)) 2886 { 2887 if (mhd_str_equal_caseless_n_st ("chunked", 2888 f->field.nv.value.cstr, 2889 f->field.nv.value.len)) 2890 { 2891 c->rq.have_chunked_upload = true; 2892 c->rq.cntn.cntn_size = MHD_SIZE_UNKNOWN; 2893 } 2894 else 2895 { 2896 mhd_LOG_MSG (c->daemon, MHD_SC_TRANSFER_ENCODING_UNSUPPORTED, \ 2897 "The 'Transfer-Encoding' used in request is " \ 2898 "unsupported or invalid."); 2899 mhd_RESPOND_WITH_ERROR_STATIC (c, 2900 MHD_HTTP_STATUS_BAD_REQUEST, 2901 ERR_RSP_UNSUPPORTED_TR_ENCODING); 2902 return; 2903 } 2904 has_trenc = true; 2905 continue; 2906 } 2907 2908 #ifdef MHD_SUPPORT_COOKIES 2909 /* "Cookie:" */ 2910 if ((! c->daemon->req_cfg.disable_cookies) && 2911 mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_COOKIE, 2912 f->field.nv.name.cstr, 2913 f->field.nv.name.len)) 2914 { 2915 if (MHD_PARSE_COOKIE_NO_MEMORY == 2916 parse_cookie_header (c, 2917 &(f->field.nv.value))) 2918 { 2919 handle_req_cookie_no_space (c); 2920 return; 2921 } 2922 continue; 2923 } 2924 #endif /* MHD_SUPPORT_COOKIES */ 2925 2926 /* "Expect: 100-continue" */ 2927 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_EXPECT, 2928 f->field.nv.name.cstr, 2929 f->field.nv.name.len)) 2930 { 2931 if (mhd_str_equal_caseless_n_st ("100-continue", 2932 f->field.nv.value.cstr, 2933 f->field.nv.value.len)) 2934 c->rq.have_expect_100 = true; 2935 else 2936 { 2937 if (0 < c->daemon->req_cfg.strictness) 2938 { 2939 mhd_LOG_MSG (c->daemon, MHD_SC_EXPECT_HEADER_VALUE_UNSUPPORTED, \ 2940 "The 'Expect' header value used in request is " \ 2941 "unsupported or invalid."); 2942 mhd_RESPOND_WITH_ERROR_STATIC (c, 2943 MHD_HTTP_STATUS_EXPECTATION_FAILED, 2944 ERR_RSP_UNSUPPORTED_EXPECT_HDR_VALUE); 2945 return; 2946 } 2947 } 2948 continue; 2949 } 2950 } 2951 2952 c->rq.cntn.cntn_present = (has_trenc || has_cntnlen); 2953 if (has_trenc && has_cntnlen) 2954 { 2955 if (0 < c->daemon->req_cfg.strictness) 2956 { 2957 mhd_RESPOND_WITH_ERROR_STATIC ( \ 2958 c, \ 2959 MHD_HTTP_STATUS_BAD_REQUEST, \ 2960 ERR_RSP_REQUEST_CNTNLENGTH_WITH_TR_ENCODING); 2961 return; 2962 } 2963 /* Must close connection after reply to prevent potential attack */ 2964 c->conn_reuse = mhd_CONN_MUST_CLOSE; 2965 c->rq.cntn.cntn_size = MHD_SIZE_UNKNOWN; 2966 mhd_assert (c->rq.have_chunked_upload); 2967 mhd_LOG_MSG (c->daemon, MHD_SC_CONTENT_LENGTH_AND_TR_ENC, \ 2968 "The 'Content-Length' request header is ignored " \ 2969 "as chunked 'Transfer-Encoding' is used " \ 2970 "for this request."); 2971 } 2972 2973 if (MHD_HTTP_VERSION_IS_LIKE_11 (c->rq.http_ver)) 2974 { 2975 if ((! has_host) && 2976 (-3 < c->daemon->req_cfg.strictness)) 2977 { 2978 mhd_LOG_MSG (c->daemon, MHD_SC_HOST_HEADER_MISSING, \ 2979 "Received HTTP/1.1 request without 'Host' header."); 2980 mhd_RESPOND_WITH_ERROR_STATIC (c, 2981 MHD_HTTP_STATUS_BAD_REQUEST, 2982 ERR_RSP_REQUEST_LACKS_HOST); 2983 return; 2984 } 2985 } 2986 else 2987 { 2988 if (! has_keepalive) 2989 c->conn_reuse = mhd_CONN_MUST_CLOSE; /* Do not re-use HTTP/1.0 connection by default */ 2990 if (has_trenc) 2991 c->conn_reuse = mhd_CONN_MUST_CLOSE; /* Framing could be incorrect */ 2992 } 2993 2994 c->stage = mhd_HTTP_STAGE_HEADERS_PROCESSED; 2995 return; 2996 } 2997 2998 2999 /** 3000 * Is "100 Continue" needed to be sent for current request? 3001 * 3002 * @param c the connection to check 3003 * @return false 100 CONTINUE is not needed, 3004 * true otherwise 3005 */ 3006 static MHD_FN_PAR_NONNULL_ALL_ bool 3007 need_100_continue (struct MHD_Connection *restrict c) 3008 { 3009 mhd_assert (MHD_HTTP_VERSION_IS_1X (c->rq.http_ver)); 3010 mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= c->stage); 3011 mhd_assert (mhd_HTTP_STAGE_BODY_RECEIVING > c->stage); 3012 3013 if (! c->rq.have_expect_100) 3014 return false; /* "100 Continue" has not been requested by the client */ 3015 3016 if (0 != c->read_buffer_offset) 3017 return false; /* Part of the content has been received already */ 3018 3019 if (0 == c->rq.cntn.cntn_size) 3020 return false; /* There is no content or zero-sized content for this request */ 3021 3022 if (MHD_HTTP_VERSION_1_0 == c->rq.http_ver) 3023 return false; /* '100 Continue' is not allowed for HTTP/1.0 */ 3024 3025 return true; 3026 } 3027 3028 3029 /** 3030 * Check whether special buffer is required to handle the upload content and 3031 * try to allocate if necessary. 3032 * Respond with error to the client if buffer cannot be allocated 3033 * @param c the connection to 3034 * @return true if succeed, 3035 * false if error response is set 3036 */ 3037 static MHD_FN_PAR_NONNULL_ALL_ bool 3038 check_and_alloc_buf_for_upload_processing (struct MHD_Connection *restrict c) 3039 { 3040 mhd_assert ((mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act) || \ 3041 (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act)); 3042 3043 if (c->rq.have_chunked_upload) 3044 return true; /* The size is unknown, buffers will be dynamically allocated 3045 and re-allocated */ 3046 mhd_assert (c->read_buffer_size > c->read_buffer_offset); 3047 #if 0 // TODO: support processing full response in the connection buffer 3048 if ((c->read_buffer_size - c->read_buffer_offset) >= 3049 c->rq.cntn.cntn_size) 3050 return true; /* No additional buffer needed */ 3051 #endif 3052 3053 if ((mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act) && 3054 (NULL == c->rq.app_act.head_act.data.upload.full.cb)) 3055 return true; /* data will be processed only incrementally */ 3056 3057 if (mhd_ACTION_UPLOAD != c->rq.app_act.head_act.act) 3058 { 3059 // TODO: add check for intermental-only POST processing */ 3060 mhd_assert (0 && "Not implemented yet"); 3061 return false; 3062 } 3063 3064 if ((c->rq.cntn.cntn_size > 3065 c->rq.app_act.head_act.data.upload.large_buffer_size) || 3066 ! mhd_daemon_get_lbuf (c->daemon, 3067 (size_t) c->rq.cntn.cntn_size, 3068 &(c->rq.cntn.lbuf))) 3069 { 3070 if (NULL != c->rq.app_act.head_act.data.upload.inc.cb) 3071 { 3072 c->rq.app_act.head_act.data.upload.full.cb = NULL; 3073 return true; /* Data can be processed incrementally */ 3074 } 3075 3076 mhd_RESPOND_WITH_ERROR_STATIC (c, 3077 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, 3078 ERR_RSP_REQUEST_CONTENTLENGTH_TOOLARGE); 3079 return false; 3080 } 3081 3082 return true; 3083 } 3084 3085 3086 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 3087 mhd_stream_call_app_request_cb (struct MHD_Connection *restrict c) 3088 { 3089 struct MHD_Daemon *restrict d = c->daemon; 3090 struct MHD_String path; 3091 const struct MHD_Action *a; 3092 3093 mhd_assert (mhd_HTTP_METHOD_NO_METHOD != c->rq.http_mthd); 3094 mhd_assert (NULL == c->rp.response); 3095 3096 if (mhd_ACTION_NO_ACTION != c->rq.app_act.head_act.act) 3097 MHD_PANIC ("MHD_Action has been set already"); 3098 3099 path.cstr = c->rq.url; 3100 path.len = c->rq.url_len; 3101 3102 c->rq.app_aware = true; 3103 a = d->req_cfg.cb (d->req_cfg.cb_cls, 3104 &(c->rq), 3105 &path, 3106 (enum MHD_HTTP_Method) c->rq.http_mthd, 3107 c->rq.cntn.cntn_size); 3108 3109 if ((NULL != a) 3110 && (((&(c->rq.app_act.head_act) != a)) 3111 || ! mhd_ACTION_IS_VALID (c->rq.app_act.head_act.act))) 3112 { 3113 mhd_LOG_MSG (d, MHD_SC_ACTION_INVALID, \ 3114 "Provided action is not a correct action generated " \ 3115 "for the current request."); 3116 /* Perform cleanup of the created but now unused action */ 3117 switch (c->rq.app_act.head_act.act) 3118 { 3119 case mhd_ACTION_RESPONSE: 3120 mhd_assert (NULL != c->rq.app_act.head_act.data.response); 3121 mhd_response_dec_use_count (c->rq.app_act.head_act.data.response); 3122 break; 3123 case mhd_ACTION_UPLOAD: 3124 case mhd_ACTION_SUSPEND: 3125 /* No cleanup needed */ 3126 break; 3127 #ifdef MHD_SUPPORT_POST_PARSER 3128 case mhd_ACTION_POST_PARSE: 3129 /* No cleanup needed */ 3130 break; 3131 #endif /* MHD_SUPPORT_POST_PARSER */ 3132 #ifdef MHD_SUPPORT_UPGRADE 3133 case mhd_ACTION_UPGRADE: 3134 /* No cleanup needed */ 3135 break; 3136 #endif /* MHD_SUPPORT_UPGRADE */ 3137 case mhd_ACTION_ABORT: 3138 mhd_UNREACHABLE (); 3139 break; 3140 case mhd_ACTION_NO_ACTION: 3141 default: 3142 break; 3143 } 3144 a = NULL; 3145 } 3146 if (NULL == a) 3147 c->rq.app_act.head_act.act = mhd_ACTION_ABORT; 3148 3149 switch (c->rq.app_act.head_act.act) 3150 { 3151 case mhd_ACTION_RESPONSE: 3152 c->rp.response = c->rq.app_act.head_act.data.response; 3153 c->stage = mhd_HTTP_STAGE_REQ_RECV_FINISHED; 3154 return true; 3155 case mhd_ACTION_UPLOAD: 3156 if (0 != c->rq.cntn.cntn_size) 3157 { 3158 if (! check_and_alloc_buf_for_upload_processing (c)) 3159 return true; 3160 if (need_100_continue (c)) 3161 { 3162 c->stage = mhd_HTTP_STAGE_CONTINUE_SENDING; 3163 return true; 3164 } 3165 c->stage = mhd_HTTP_STAGE_BODY_RECEIVING; 3166 return (0 != c->read_buffer_offset); 3167 } 3168 c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; 3169 return true; 3170 #ifdef MHD_SUPPORT_POST_PARSER 3171 case mhd_ACTION_POST_PARSE: 3172 if (0 == c->rq.cntn.cntn_size) 3173 { 3174 c->rq.u_proc.post.parse_result = MHD_POST_PARSE_RES_REQUEST_EMPTY; 3175 c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; 3176 return true; 3177 } 3178 if (! mhd_stream_prepare_for_post_parse (c)) 3179 { 3180 mhd_assert (mhd_HTTP_STAGE_FOOTERS_RECEIVED < c->stage); 3181 return true; 3182 } 3183 if (need_100_continue (c)) 3184 { 3185 c->stage = mhd_HTTP_STAGE_CONTINUE_SENDING; 3186 return true; 3187 } 3188 c->stage = mhd_HTTP_STAGE_BODY_RECEIVING; 3189 return true; 3190 #endif /* MHD_SUPPORT_POST_PARSER */ 3191 case mhd_ACTION_SUSPEND: 3192 c->suspended = true; 3193 #ifdef MHD_USE_TRACE_SUSPEND_RESUME 3194 fprintf (stderr, 3195 "%%%%%% Suspending connection, FD: %2llu\n", 3196 (unsigned long long) c->sk.fd); 3197 #endif /* MHD_USE_TRACE_SUSPEND_RESUME */ 3198 c->rq.app_act.head_act.act = mhd_ACTION_NO_ACTION; 3199 return false; 3200 #ifdef MHD_SUPPORT_UPGRADE 3201 case mhd_ACTION_UPGRADE: 3202 mhd_assert (0 == c->rq.cntn.cntn_size); 3203 c->stage = mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING; 3204 return false; 3205 #endif /* MHD_SUPPORT_UPGRADE */ 3206 case mhd_ACTION_ABORT: 3207 mhd_conn_start_closing_app_abort (c); 3208 return true; 3209 case mhd_ACTION_NO_ACTION: 3210 default: 3211 mhd_assert (0 && "Impossible value"); 3212 break; 3213 } 3214 mhd_UNREACHABLE (); 3215 return false; 3216 } 3217 3218 3219 /** 3220 * React on provided action for upload 3221 * @param c the stream to use 3222 * @param act the action provided by application 3223 * @param final set to 'true' if this is final upload callback 3224 * @return true if connection state has been changed, 3225 * false otherwise 3226 */ 3227 MHD_INTERNAL 3228 MHD_FN_PAR_NONNULL_ (1) bool 3229 mhd_stream_process_upload_action (struct MHD_Connection *restrict c, 3230 const struct MHD_UploadAction *act, 3231 bool final) 3232 { 3233 if (NULL != act) 3234 { 3235 if ((&(c->rq.app_act.upl_act) != act) || 3236 ! mhd_UPLOAD_ACTION_IS_VALID (c->rq.app_act.upl_act.act) || 3237 (final && 3238 (mhd_UPLOAD_ACTION_CONTINUE == c->rq.app_act.upl_act.act))) 3239 { 3240 /* Perform cleanup of the created but now unused action */ 3241 switch (c->rq.app_act.upl_act.act) 3242 { 3243 case mhd_UPLOAD_ACTION_RESPONSE: 3244 mhd_assert (NULL != c->rq.app_act.upl_act.data.response); 3245 mhd_response_dec_use_count (c->rq.app_act.upl_act.data.response); 3246 break; 3247 case mhd_UPLOAD_ACTION_CONTINUE: 3248 case mhd_UPLOAD_ACTION_SUSPEND: 3249 /* No cleanup needed */ 3250 break; 3251 #ifdef MHD_SUPPORT_UPGRADE 3252 case mhd_UPLOAD_ACTION_UPGRADE: 3253 /* No cleanup needed */ 3254 break; 3255 #endif /* MHD_SUPPORT_UPGRADE */ 3256 case mhd_UPLOAD_ACTION_ABORT: 3257 mhd_UNREACHABLE (); 3258 break; 3259 case mhd_UPLOAD_ACTION_NO_ACTION: 3260 default: 3261 break; 3262 } 3263 mhd_LOG_MSG (c->daemon, MHD_SC_UPLOAD_ACTION_INVALID, \ 3264 "Provided action is not a correct action generated " \ 3265 "for the current request."); 3266 act = NULL; 3267 } 3268 } 3269 if (NULL == act) 3270 c->rq.app_act.upl_act.act = mhd_UPLOAD_ACTION_ABORT; 3271 3272 switch (c->rq.app_act.upl_act.act) 3273 { 3274 case mhd_UPLOAD_ACTION_RESPONSE: 3275 c->rp.response = c->rq.app_act.upl_act.data.response; 3276 c->stage = mhd_HTTP_STAGE_REQ_RECV_FINISHED; 3277 return true; 3278 case mhd_UPLOAD_ACTION_CONTINUE: 3279 memset (&(c->rq.app_act.upl_act), 0, sizeof(c->rq.app_act.upl_act)); 3280 return false; 3281 case mhd_UPLOAD_ACTION_SUSPEND: 3282 c->suspended = true; 3283 #ifdef MHD_USE_TRACE_SUSPEND_RESUME 3284 fprintf (stderr, 3285 "%%%%%% Suspending connection, FD: %2llu\n", 3286 (unsigned long long) c->sk.fd); 3287 #endif /* MHD_USE_TRACE_SUSPEND_RESUME */ 3288 memset (&(c->rq.app_act.upl_act), 0, sizeof(c->rq.app_act.upl_act)); 3289 return false; 3290 #ifdef MHD_SUPPORT_UPGRADE 3291 case mhd_UPLOAD_ACTION_UPGRADE: 3292 mhd_assert (c->rq.cntn.recv_size == c->rq.cntn.cntn_size); 3293 mhd_assert (! c->rq.have_chunked_upload || \ 3294 mhd_HTTP_STAGE_FULL_REQ_RECEIVED == c->stage); 3295 c->stage = mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING; 3296 return false; 3297 #endif /* MHD_SUPPORT_UPGRADE */ 3298 case mhd_UPLOAD_ACTION_ABORT: 3299 mhd_conn_start_closing_app_abort (c); 3300 return true; 3301 case mhd_UPLOAD_ACTION_NO_ACTION: 3302 default: 3303 mhd_assert (0 && "Impossible value"); 3304 break; 3305 } 3306 mhd_UNREACHABLE (); 3307 return false; 3308 } 3309 3310 3311 static MHD_FN_PAR_NONNULL_ALL_ bool 3312 process_request_chunked_body (struct MHD_Connection *restrict c) 3313 { 3314 struct MHD_Daemon *restrict d = c->daemon; 3315 size_t available; 3316 bool has_more_data; 3317 char *restrict buffer_head; 3318 const int discp_lvl = d->req_cfg.strictness; 3319 /* Treat bare LF as the end of the line. 3320 RFC 9112, section 2.2-3 3321 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5). 3322 Bare LF is processed as end of the line or rejected as broken request. */ 3323 const bool bare_lf_as_crlf = mhd_ALLOW_BARE_LF_AS_CRLF (discp_lvl); 3324 /* Allow "Bad WhiteSpace" in chunk extension. 3325 RFC 9112, Section 7.1.1, Paragraph 2 */ 3326 const bool allow_bws = (MHD_PSL_VERY_STRICT > discp_lvl); 3327 bool state_updated; 3328 3329 mhd_assert (NULL == c->rp.response); 3330 mhd_assert (c->rq.have_chunked_upload); 3331 mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size); 3332 3333 buffer_head = c->read_buffer; 3334 available = c->read_buffer_offset; 3335 state_updated = false; 3336 do 3337 { 3338 size_t cntn_data_ready; 3339 bool need_inc_proc; 3340 3341 has_more_data = false; 3342 3343 if ( (c->rq.current_chunk_offset == 3344 c->rq.current_chunk_size) && 3345 (0 != c->rq.current_chunk_size) ) 3346 { 3347 size_t i; 3348 mhd_assert (0 != available); 3349 /* skip new line at the *end* of a chunk */ 3350 i = 0; 3351 if ( (2 <= available) && 3352 ('\r' == buffer_head[0]) && 3353 ('\n' == buffer_head[1]) ) 3354 i += 2; /* skip CRLF */ 3355 else if (bare_lf_as_crlf && ('\n' == buffer_head[0])) 3356 i++; /* skip bare LF */ 3357 else if (2 > available) 3358 break; /* need more upload data */ 3359 if (0 == i) 3360 { 3361 /* malformed encoding */ 3362 mhd_RESPOND_WITH_ERROR_STATIC (c, 3363 MHD_HTTP_STATUS_BAD_REQUEST, 3364 ERR_RSP_REQUEST_CHUNKED_MALFORMED); 3365 return true; 3366 } 3367 available -= i; 3368 buffer_head += i; 3369 c->rq.current_chunk_offset = 0; 3370 c->rq.current_chunk_size = 0; 3371 if (0 == available) 3372 break; 3373 } 3374 if (0 != c->rq.current_chunk_size) 3375 { 3376 uint_fast64_t cur_chunk_left; 3377 mhd_assert (c->rq.current_chunk_offset < \ 3378 c->rq.current_chunk_size); 3379 /* we are in the middle of a chunk, give 3380 as much as possible to the client (without 3381 crossing chunk boundaries) */ 3382 cur_chunk_left 3383 = c->rq.current_chunk_size 3384 - c->rq.current_chunk_offset; 3385 if (cur_chunk_left > available) 3386 cntn_data_ready = available; 3387 else 3388 { /* cur_chunk_left <= (size_t)available */ 3389 cntn_data_ready = (size_t) cur_chunk_left; 3390 if (available > cntn_data_ready) 3391 has_more_data = true; 3392 } 3393 } 3394 else 3395 { /* Need the parse the chunk size line */ 3396 /** The number of found digits in the chunk size number */ 3397 size_t num_dig; 3398 uint_fast64_t chunk_size; 3399 bool broken; 3400 bool overflow; 3401 3402 mhd_assert (0 != available); 3403 3404 overflow = false; 3405 chunk_size = 0; /* Mute possible compiler warning. 3406 The real value will be set later. */ 3407 3408 num_dig = mhd_strx_to_uint64_n (buffer_head, 3409 available, 3410 &chunk_size); 3411 mhd_assert (num_dig <= available); 3412 if (num_dig == available) 3413 continue; /* Need line delimiter */ 3414 3415 broken = (0 == num_dig); 3416 if (broken) 3417 /* Check whether result is invalid due to uint64_t overflow */ 3418 overflow = ((('0' <= buffer_head[0]) && ('9' >= buffer_head[0])) || 3419 (('A' <= buffer_head[0]) && ('F' >= buffer_head[0])) || 3420 (('a' <= buffer_head[0]) && ('f' >= buffer_head[0]))); 3421 else 3422 { 3423 /** 3424 * The length of the string with the number of the chunk size, 3425 * including chunk extension 3426 */ 3427 size_t chunk_size_line_len; 3428 3429 chunk_size_line_len = 0; 3430 if ((';' == buffer_head[num_dig]) || 3431 (allow_bws && 3432 ((' ' == buffer_head[num_dig]) || 3433 ('\t' == buffer_head[num_dig])))) 3434 { /* Chunk extension */ 3435 size_t i; 3436 3437 /* Skip bad whitespaces (if any) */ 3438 for (i = num_dig; i < available; ++i) 3439 { 3440 if ((' ' != buffer_head[i]) && ('\t' != buffer_head[i])) 3441 break; 3442 } 3443 if (i == available) 3444 break; /* need more data */ 3445 if (';' == buffer_head[i]) 3446 { 3447 for (++i; i < available; ++i) 3448 { 3449 if ('\n' == buffer_head[i]) 3450 break; 3451 } 3452 if (i == available) 3453 break; /* need more data */ 3454 mhd_assert (i > num_dig); 3455 mhd_assert (1 <= i); 3456 /* Found LF position at 'i' (buffer_head[i] = '\n'), chunk ends at i+1 */ 3457 if (bare_lf_as_crlf) 3458 chunk_size_line_len = i + 1; /* Don't care about CR before LF */ 3459 else if ('\r' == buffer_head[i - 1]) 3460 chunk_size_line_len = i + 1; /* Have CR LF, all good */ 3461 /* else: invalid termination, leave chunk_size_line_len at 0 */ 3462 } 3463 else 3464 { /* No ';' after "bad whitespace" */ 3465 mhd_assert (allow_bws); 3466 mhd_assert (0 == chunk_size_line_len); 3467 } 3468 } 3469 else 3470 { 3471 mhd_assert (available >= num_dig); 3472 if ((2 <= (available - num_dig)) && 3473 ('\r' == buffer_head[num_dig]) && 3474 ('\n' == buffer_head[num_dig + 1])) 3475 chunk_size_line_len = num_dig + 2; 3476 else if (bare_lf_as_crlf && 3477 ('\n' == buffer_head[num_dig])) 3478 chunk_size_line_len = num_dig + 1; 3479 else if (2 > (available - num_dig)) 3480 break; /* need more data */ 3481 } 3482 3483 if (0 != chunk_size_line_len) 3484 { /* Valid termination of the chunk size line */ 3485 mhd_assert (chunk_size_line_len <= available); 3486 /* Start reading payload data of the chunk */ 3487 c->rq.current_chunk_offset = 0; 3488 c->rq.current_chunk_size = chunk_size; 3489 3490 available -= chunk_size_line_len; 3491 buffer_head += chunk_size_line_len; 3492 3493 if (0 == chunk_size) 3494 { /* The final (termination) chunk */ 3495 c->rq.cntn.cntn_size = c->rq.cntn.recv_size; 3496 c->stage = mhd_HTTP_STAGE_BODY_RECEIVED; 3497 state_updated = true; 3498 break; 3499 } 3500 if (available > 0) 3501 has_more_data = true; 3502 continue; 3503 } 3504 /* Invalid chunk size line */ 3505 } 3506 3507 if (! overflow) 3508 mhd_RESPOND_WITH_ERROR_STATIC (c, 3509 MHD_HTTP_STATUS_BAD_REQUEST, 3510 ERR_RSP_REQUEST_CHUNKED_MALFORMED); 3511 else 3512 mhd_RESPOND_WITH_ERROR_STATIC (c, 3513 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, 3514 ERR_RSP_REQUEST_CHUNK_TOO_LARGE); 3515 return true; 3516 } 3517 mhd_assert (c->rq.app_aware); 3518 3519 #ifdef MHD_SUPPORT_POST_PARSER 3520 if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act) 3521 { 3522 size_t size_provided; 3523 3524 c->rq.cntn.recv_size += cntn_data_ready; 3525 size_provided = cntn_data_ready; 3526 3527 state_updated = mhd_stream_post_parse (c, 3528 &size_provided, 3529 buffer_head); 3530 // TODO: support one chunk in-place processing? 3531 mhd_assert ((0 == size_provided) || \ 3532 (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result) || \ 3533 (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage)); 3534 if (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage) 3535 c->discard_request = true; 3536 } 3537 else 3538 #endif /* MHD_SUPPORT_POST_PARSER */ 3539 if (1) 3540 { 3541 mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act); 3542 if (NULL != c->rq.app_act.head_act.data.upload.full.cb) 3543 { 3544 need_inc_proc = false; 3545 3546 mhd_assert (0 == c->rq.cntn.proc_size); 3547 if ((uint_fast64_t) c->rq.cntn.lbuf.size < 3548 c->rq.cntn.recv_size + cntn_data_ready) 3549 { 3550 size_t grow_size; 3551 3552 grow_size = (size_t) (c->rq.cntn.recv_size + cntn_data_ready 3553 - c->rq.cntn.lbuf.size); 3554 if (((size_t) (c->rq.cntn.recv_size + cntn_data_ready) < 3555 cntn_data_ready) || 3556 (! mhd_daemon_grow_lbuf (d, 3557 grow_size, 3558 &(c->rq.cntn.lbuf)))) 3559 { 3560 /* Failed to grow the buffer, no space to put the new data */ 3561 const struct MHD_UploadAction *act; 3562 if (NULL != c->rq.app_act.head_act.data.upload.inc.cb) 3563 { 3564 mhd_RESPOND_WITH_ERROR_STATIC ( 3565 c, 3566 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, 3567 ERR_RSP_MSG_REQUEST_TOO_BIG); 3568 return true; 3569 } 3570 c->rq.app_act.head_act.data.upload.full.cb = NULL; /* Cannot process "full" content */ 3571 /* Process previously buffered data */ 3572 mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size); 3573 act = c->rq.app_act.head_act.data.upload.inc.cb ( 3574 c->rq.app_act.head_act.data.upload.inc.cls, 3575 &(c->rq), 3576 (size_t) c->rq.cntn.recv_size, 3577 c->rq.cntn.lbuf.data); 3578 c->rq.cntn.proc_size = c->rq.cntn.recv_size; 3579 mhd_daemon_free_lbuf (d, &(c->rq.cntn.lbuf)); 3580 if (mhd_stream_process_upload_action (c, act, false)) 3581 return true; 3582 need_inc_proc = true; 3583 } 3584 } 3585 if (! need_inc_proc) 3586 { 3587 memcpy (c->rq.cntn.lbuf.data + c->rq.cntn.recv_size, 3588 buffer_head, cntn_data_ready); 3589 c->rq.cntn.recv_size += cntn_data_ready; 3590 } 3591 } 3592 else 3593 need_inc_proc = true; 3594 3595 if (need_inc_proc) 3596 { 3597 const struct MHD_UploadAction *act; 3598 mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb); 3599 3600 c->rq.cntn.recv_size += cntn_data_ready; 3601 act = c->rq.app_act.head_act.data.upload.inc.cb ( 3602 c->rq.app_act.head_act.data.upload.inc.cls, 3603 &(c->rq), 3604 cntn_data_ready, 3605 buffer_head); 3606 c->rq.cntn.proc_size += cntn_data_ready; 3607 state_updated = mhd_stream_process_upload_action (c, act, false); 3608 } 3609 } 3610 /* dh left "processed" bytes in buffer for next time... */ 3611 mhd_ASSUME (available >= cntn_data_ready); 3612 buffer_head += cntn_data_ready; 3613 available -= cntn_data_ready; 3614 mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size); 3615 mhd_ASSUME (c->rq.current_chunk_offset + cntn_data_ready >= 3616 c->rq.current_chunk_offset); 3617 c->rq.current_chunk_offset += cntn_data_ready; 3618 } while (has_more_data && ! state_updated); 3619 /* TODO: optionally? zero out reused memory region */ 3620 if ( (available > 0) && 3621 (buffer_head != c->read_buffer) ) 3622 memmove (c->read_buffer, 3623 buffer_head, 3624 available); 3625 else 3626 mhd_assert ((0 == available) || \ 3627 (c->read_buffer_offset == available)); 3628 c->read_buffer_offset = available; 3629 3630 return state_updated; 3631 } 3632 3633 3634 static MHD_FN_PAR_NONNULL_ALL_ bool 3635 process_request_nonchunked_body (struct MHD_Connection *restrict c) 3636 { 3637 size_t cntn_data_ready; 3638 bool read_buf_reuse; 3639 bool state_updated; 3640 3641 mhd_assert (NULL == c->rp.response); 3642 mhd_assert (! c->rq.have_chunked_upload); 3643 mhd_assert (MHD_SIZE_UNKNOWN != c->rq.cntn.cntn_size); 3644 mhd_assert (c->rq.cntn.recv_size < c->rq.cntn.cntn_size); 3645 mhd_assert (c->rq.app_aware); 3646 3647 if ((c->rq.cntn.cntn_size - c->rq.cntn.recv_size) < c->read_buffer_offset) 3648 cntn_data_ready = (size_t) (c->rq.cntn.cntn_size - c->rq.cntn.recv_size); 3649 else 3650 cntn_data_ready = c->read_buffer_offset; 3651 3652 read_buf_reuse = false; 3653 state_updated = false; 3654 mhd_assert (! read_buf_reuse); /* Mute analyser warning */ 3655 3656 #ifdef MHD_SUPPORT_POST_PARSER 3657 if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act) 3658 { 3659 size_t size_provided; 3660 // TODO: rework to correctly support partial processing 3661 // TODO: rework to support receiving directly into "large buffer" 3662 c->rq.cntn.recv_size += cntn_data_ready; 3663 size_provided = cntn_data_ready; 3664 3665 state_updated = mhd_stream_post_parse (c, 3666 &size_provided, 3667 c->read_buffer); 3668 mhd_assert ((0 == size_provided) || \ 3669 (MHD_POST_PARSE_RES_OK != c->rq.u_proc.post.parse_result) || \ 3670 (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage)); 3671 if (mhd_HTTP_STAGE_BODY_RECEIVING != c->stage) 3672 c->discard_request = true; 3673 3674 read_buf_reuse = true; 3675 c->rq.cntn.proc_size += cntn_data_ready; 3676 if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size) 3677 { 3678 c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; 3679 state_updated = true; 3680 } 3681 } 3682 else 3683 #endif /* MHD_SUPPORT_POST_PARSER */ 3684 if (1) 3685 { 3686 mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act); 3687 if (NULL != c->rq.app_act.head_act.data.upload.full.cb) 3688 { 3689 // TODO: implement processing in pool memory if buffer is large enough 3690 mhd_assert ((c->rq.cntn.recv_size + cntn_data_ready) <= 3691 (uint_fast64_t) c->rq.cntn.lbuf.size); 3692 memcpy (c->rq.cntn.lbuf.data + c->rq.cntn.recv_size, 3693 c->read_buffer, cntn_data_ready); 3694 c->rq.cntn.recv_size += cntn_data_ready; 3695 read_buf_reuse = true; 3696 if (c->rq.cntn.recv_size == c->rq.cntn.cntn_size) 3697 { 3698 c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED; 3699 state_updated = true; 3700 } 3701 } 3702 else 3703 { 3704 const struct MHD_UploadAction *act; 3705 mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb); 3706 3707 c->rq.cntn.recv_size += cntn_data_ready; 3708 act = c->rq.app_act.head_act.data.upload.inc.cb ( 3709 c->rq.app_act.head_act.data.upload.inc.cls, 3710 &(c->rq), 3711 cntn_data_ready, 3712 c->read_buffer); 3713 c->rq.cntn.proc_size += cntn_data_ready; 3714 read_buf_reuse = true; 3715 state_updated = mhd_stream_process_upload_action (c, act, false); 3716 } 3717 } 3718 3719 if (read_buf_reuse) 3720 { 3721 size_t data_left_size; 3722 mhd_assert (c->read_buffer_offset >= cntn_data_ready); 3723 data_left_size = c->read_buffer_offset - cntn_data_ready; 3724 if (0 != data_left_size) 3725 memmove (c->read_buffer, 3726 c->read_buffer + cntn_data_ready, 3727 data_left_size); 3728 c->read_buffer_offset = data_left_size; 3729 } 3730 3731 return state_updated; 3732 } 3733 3734 3735 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 3736 mhd_stream_process_request_body (struct MHD_Connection *restrict c) 3737 { 3738 if (c->rq.have_chunked_upload) 3739 return process_request_chunked_body (c); 3740 3741 return process_request_nonchunked_body (c); 3742 } 3743 3744 3745 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 3746 mhd_stream_call_app_final_upload_cb (struct MHD_Connection *restrict c) 3747 { 3748 const struct MHD_UploadAction *act; 3749 bool state_changed; 3750 mhd_assert (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act || \ 3751 mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act); 3752 3753 #ifdef MHD_SUPPORT_POST_PARSER 3754 if (mhd_ACTION_POST_PARSE == c->rq.app_act.head_act.act) 3755 return mhd_stream_process_post_finish (c); 3756 #endif /* MHD_SUPPORT_POST_PARSER */ 3757 3758 mhd_assert (mhd_ACTION_UPLOAD == c->rq.app_act.head_act.act); 3759 3760 if (NULL != c->rq.app_act.head_act.data.upload.full.cb) 3761 { 3762 mhd_assert (c->rq.cntn.recv_size == c->rq.cntn.cntn_size); 3763 mhd_assert (0 == c->rq.cntn.proc_size); 3764 mhd_assert (NULL != c->rq.cntn.lbuf.data); 3765 mhd_assert (c->rq.cntn.recv_size <= c->rq.cntn.lbuf.size); 3766 // TODO: implement processing in pool memory if it is large enough 3767 act = c->rq.app_act.head_act.data.upload.full.cb ( 3768 c->rq.app_act.head_act.data.upload.full.cls, 3769 &(c->rq), 3770 (size_t) c->rq.cntn.recv_size, 3771 c->rq.cntn.lbuf.data); 3772 c->rq.cntn.proc_size = c->rq.cntn.recv_size; 3773 } 3774 else 3775 { 3776 mhd_assert (NULL != c->rq.app_act.head_act.data.upload.inc.cb); 3777 mhd_assert (c->rq.cntn.cntn_size == c->rq.cntn.proc_size); 3778 act = c->rq.app_act.head_act.data.upload.inc.cb ( 3779 c->rq.app_act.head_act.data.upload.inc.cls, 3780 &(c->rq), 3781 0, 3782 NULL); 3783 } 3784 3785 state_changed = mhd_stream_process_upload_action (c, act, true); 3786 if (! c->suspended) 3787 mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf)); 3788 3789 return state_changed; 3790 } 3791 3792 3793 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 3794 mhd_stream_process_req_recv_finished (struct MHD_Connection *restrict c) 3795 { 3796 if (NULL != c->rq.cntn.lbuf.data) 3797 mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf)); 3798 c->rq.cntn.lbuf.data = NULL; 3799 if (c->rq.cntn.cntn_size != c->rq.cntn.proc_size) 3800 c->discard_request = true; 3801 mhd_assert (NULL != c->rp.response); 3802 c->stage = mhd_HTTP_STAGE_START_REPLY; 3803 return true; 3804 } 3805 3806 3807 /** 3808 * Send error reply when receive buffer space exhausted while receiving 3809 * the chunk size line. 3810 * @param c the connection to handle 3811 * @param chunk_size_line the optional pointer to the partially received 3812 * the current chunk size line. 3813 * Could be not zero-terminated and can contain binary 3814 * zeros. 3815 * Can be NULL. 3816 * @param chunk_size_line_size the size of the @a chunk_size_line 3817 */ 3818 static void 3819 handle_req_chunk_size_line_no_space (struct MHD_Connection *c, 3820 const char *chunk_size_line, 3821 size_t chunk_size_line_size) 3822 { 3823 unsigned int err_code; 3824 3825 if (NULL != chunk_size_line) 3826 { 3827 const char *semicol; 3828 /* Check for chunk extension */ 3829 semicol = (const char *) 3830 memchr (chunk_size_line, 3831 ';', 3832 chunk_size_line_size); 3833 if (NULL != semicol) 3834 { /* Chunk extension present. It could be removed without any loss of the 3835 details of the request. */ 3836 mhd_RESPOND_WITH_ERROR_STATIC (c, 3837 MHD_HTTP_STATUS_CONTENT_TOO_LARGE, 3838 ERR_RSP_REQUEST_CHUNK_LINE_EXT_TOO_BIG); 3839 return; 3840 } 3841 } 3842 err_code = mhd_stream_get_no_space_err_status_code (c, 3843 MHD_PROC_RECV_BODY_CHUNKED, 3844 chunk_size_line_size, 3845 chunk_size_line); 3846 mhd_RESPOND_WITH_ERROR_STATIC (c, 3847 err_code, 3848 ERR_RSP_REQUEST_CHUNK_LINE_TOO_BIG); 3849 } 3850 3851 3852 /** 3853 * Handle situation with read buffer exhaustion. 3854 * Must be called when no more space left in the read buffer, no more 3855 * space left in the memory pool to grow the read buffer, but more data 3856 * need to be received from the client. 3857 * Could be called when the result of received data processing cannot be 3858 * stored in the memory pool (like some header). 3859 * @param c the connection to process 3860 * @param stage the receive stage where the exhaustion happens. 3861 * @return 'true' if connection should NOT be closed, 3862 * 'false' if connection is closing 3863 */ 3864 static MHD_FN_PAR_NONNULL_ALL_ bool 3865 handle_recv_no_space (struct MHD_Connection *c, 3866 enum MHD_ProcRecvDataStage stage) 3867 { 3868 mhd_assert (MHD_PROC_RECV_INIT <= stage); 3869 mhd_assert (MHD_PROC_RECV_FOOTERS >= stage); 3870 mhd_assert (mhd_HTTP_STAGE_FULL_REQ_RECEIVED > c->stage); 3871 mhd_assert ((MHD_PROC_RECV_INIT != stage) || \ 3872 (mhd_HTTP_STAGE_INIT == c->stage)); 3873 mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \ 3874 (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage)); 3875 mhd_assert ((MHD_PROC_RECV_URI != stage) || \ 3876 (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage)); 3877 mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \ 3878 (mhd_HTTP_STAGE_REQ_LINE_RECEIVING == c->stage)); 3879 mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \ 3880 (mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING == c->stage)); 3881 mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */ 3882 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \ 3883 (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage)); 3884 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \ 3885 (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage)); 3886 mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \ 3887 (mhd_HTTP_STAGE_FOOTERS_RECEIVING == c->stage)); 3888 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \ 3889 (! c->rq.have_chunked_upload)); 3890 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \ 3891 (c->rq.have_chunked_upload)); 3892 switch (stage) 3893 { 3894 case MHD_PROC_RECV_INIT: 3895 case MHD_PROC_RECV_METHOD: 3896 /* Some data has been received, but it is not clear yet whether 3897 * the received data is an valid HTTP request */ 3898 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST, \ 3899 "No space left in the read buffer when " \ 3900 "receiving the initial part of " \ 3901 "the request line."); 3902 return false; 3903 case MHD_PROC_RECV_URI: 3904 case MHD_PROC_RECV_HTTPVER: 3905 /* Some data has been received, but the request line is incomplete */ 3906 mhd_assert (mhd_HTTP_METHOD_NO_METHOD != c->rq.http_mthd); 3907 mhd_assert (MHD_HTTP_VERSION_INVALID == c->rq.http_ver); 3908 /* A quick simple check whether the incomplete line looks 3909 * like an HTTP request */ 3910 if ((mhd_HTTP_METHOD_GET <= c->rq.http_mthd) && 3911 (mhd_HTTP_METHOD_DELETE >= c->rq.http_mthd)) 3912 { 3913 mhd_RESPOND_WITH_ERROR_STATIC (c, 3914 MHD_HTTP_STATUS_URI_TOO_LONG, 3915 ERR_RSP_MSG_REQUEST_TOO_BIG); 3916 return true; 3917 } 3918 mhd_STREAM_ABORT (c, mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST, \ 3919 "No space left in the read buffer when " \ 3920 "receiving the URI in " \ 3921 "the request line. " \ 3922 "The request uses non-standard HTTP request " \ 3923 "method token."); 3924 return false; 3925 case MHD_PROC_RECV_HEADERS: 3926 handle_req_headers_no_space (c, c->read_buffer, c->read_buffer_offset); 3927 return true; 3928 case MHD_PROC_RECV_BODY_NORMAL: 3929 /* A header probably has been added to a suspended connection and 3930 it took precisely all the space in the buffer. 3931 Very low probability. */ 3932 mhd_assert (! c->rq.have_chunked_upload); 3933 handle_req_headers_no_space (c, NULL, 0); // FIXME: check 3934 return true; 3935 case MHD_PROC_RECV_BODY_CHUNKED: 3936 mhd_assert (c->rq.have_chunked_upload); 3937 if (c->rq.current_chunk_offset != c->rq.current_chunk_size) 3938 { /* Receiving content of the chunk */ 3939 /* A header probably has been added to a suspended connection and 3940 it took precisely all the space in the buffer. 3941 Very low probability. */ 3942 handle_req_headers_no_space (c, NULL, 0); // FIXME: check 3943 } 3944 else 3945 { 3946 if (0 != c->rq.current_chunk_size) 3947 { /* Waiting for chunk-closing CRLF */ 3948 /* Not really possible as some payload should be 3949 processed and the space used by payload should be available. */ 3950 handle_req_headers_no_space (c, NULL, 0); // FIXME: check 3951 } 3952 else 3953 { /* Reading the line with the chunk size */ 3954 handle_req_chunk_size_line_no_space (c, 3955 c->read_buffer, 3956 c->read_buffer_offset); 3957 } 3958 } 3959 return true; 3960 case MHD_PROC_RECV_FOOTERS: 3961 handle_req_footers_no_space (c, c->read_buffer, c->read_buffer_offset); 3962 return true; 3963 /* The next cases should not be possible */ 3964 case MHD_PROC_RECV_COOKIE: 3965 default: 3966 break; 3967 } 3968 mhd_UNREACHABLE (); 3969 return false; 3970 } 3971 3972 3973 /** 3974 * Try growing the read buffer. We initially claim half the available 3975 * buffer space for the read buffer (the other half being left for 3976 * management data structures; the write buffer can in the end take 3977 * virtually everything as the read buffer can be reduced to the 3978 * minimum necessary at that point. 3979 * 3980 * @param connection the connection 3981 * @param required set to 'true' if grow is required, i.e. connection 3982 * will fail if no additional space is granted 3983 * @return 'true' on success, 'false' on failure 3984 */ 3985 static MHD_FN_PAR_NONNULL_ALL_ bool 3986 try_grow_read_buffer (struct MHD_Connection *restrict connection, 3987 bool required) 3988 { 3989 size_t new_size; 3990 size_t avail_size; 3991 const size_t def_grow_size = 1536; // TODO: remove hardcoded increment 3992 char *rb; 3993 3994 avail_size = mhd_pool_get_free (connection->pool); 3995 if (0 == avail_size) 3996 return false; /* No more space available */ 3997 if (0 == connection->read_buffer_size) 3998 new_size = avail_size / 2; /* Use half of available buffer for reading */ 3999 else 4000 { 4001 size_t grow_size; 4002 4003 grow_size = avail_size / 8; 4004 if (def_grow_size > grow_size) 4005 { /* Shortage of space */ 4006 const size_t left_free = 4007 connection->read_buffer_size - connection->read_buffer_offset; 4008 mhd_assert (connection->read_buffer_size >= \ 4009 connection->read_buffer_offset); 4010 if ((def_grow_size <= grow_size + left_free) 4011 && (left_free < def_grow_size)) 4012 grow_size = def_grow_size - left_free; /* Use precise 'def_grow_size' for new free space */ 4013 else if (! required) 4014 return false; /* Grow is not mandatory, leave some space in pool */ 4015 else 4016 { 4017 /* Shortage of space, but grow is mandatory */ 4018 const size_t small_inc = 4019 ((mhd_BUF_INC_SIZE > def_grow_size) ? 4020 def_grow_size : mhd_BUF_INC_SIZE) / 8; 4021 if (small_inc < avail_size) 4022 grow_size = small_inc; 4023 else 4024 grow_size = avail_size; 4025 } 4026 } 4027 new_size = connection->read_buffer_size + grow_size; 4028 } 4029 /* Make sure that read buffer will not be moved */ 4030 if ((NULL != connection->read_buffer) && 4031 ! mhd_pool_is_resizable_inplace (connection->pool, 4032 connection->read_buffer, 4033 connection->read_buffer_size)) 4034 { 4035 mhd_assert (0); 4036 return false; 4037 } 4038 /* we can actually grow the buffer, do it! */ 4039 rb = (char *) mhd_pool_reallocate (connection->pool, 4040 connection->read_buffer, 4041 connection->read_buffer_size, 4042 new_size); 4043 if (NULL == rb) 4044 { 4045 /* This should NOT be possible: we just computed 'new_size' so that 4046 it should fit. If it happens, somehow our read buffer is not in 4047 the right position in the pool, say because someone called 4048 mhd_pool_allocate() without 'from_end' set to 'true'? Anyway, 4049 should be investigated! (Ideally provide all data from 4050 *pool and connection->read_buffer and new_size for debugging). */ 4051 mhd_assert (0); 4052 return false; 4053 } 4054 mhd_assert (connection->read_buffer == rb); 4055 connection->read_buffer = rb; 4056 mhd_assert (NULL != connection->read_buffer); 4057 connection->read_buffer_size = new_size; 4058 return true; 4059 } 4060 4061 4062 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ enum mhd_ConnectionBufferGrowResult 4063 mhd_stream_check_and_grow_read_buffer_space (struct MHD_Connection *restrict c) 4064 { 4065 enum MHD_ProcRecvDataStage stage; 4066 bool res; 4067 /** 4068 * The increase of read buffer size is desirable. 4069 */ 4070 bool rbuff_grow_desired; 4071 /** 4072 * The increase of read buffer size is a hard requirement. 4073 */ 4074 bool rbuff_grow_required; 4075 4076 mhd_assert (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info)); 4077 mhd_assert (! c->discard_request); 4078 4079 rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size); 4080 if (rbuff_grow_required) 4081 rbuff_grow_desired = true; 4082 else 4083 { 4084 rbuff_grow_desired = (c->read_buffer_offset + 1536 > // TODO: remove handcoded buffer grow size 4085 c->read_buffer_size); 4086 4087 if ((rbuff_grow_desired) && 4088 (mhd_HTTP_STAGE_BODY_RECEIVING == c->stage)) 4089 { 4090 if (! c->rq.have_chunked_upload) 4091 { 4092 mhd_assert (MHD_SIZE_UNKNOWN != c->rq.cntn.cntn_size); 4093 /* Do not grow read buffer more than necessary to process the current 4094 request. */ 4095 rbuff_grow_desired = 4096 (c->rq.cntn.cntn_size - c->rq.cntn.recv_size > c->read_buffer_size); // FIXME 4097 } 4098 else 4099 { 4100 mhd_assert (MHD_SIZE_UNKNOWN == c->rq.cntn.cntn_size); 4101 if (0 == c->rq.current_chunk_size) 4102 rbuff_grow_desired = /* Reading value of the next chunk size */ 4103 (MHD_CHUNK_HEADER_REASONABLE_LEN > 4104 c->read_buffer_size); 4105 else 4106 { 4107 const uint_fast64_t cur_chunk_left = 4108 c->rq.current_chunk_size - c->rq.current_chunk_offset; 4109 /* Do not grow read buffer more than necessary to process the current 4110 chunk with terminating CRLF. */ 4111 mhd_assert (c->rq.current_chunk_offset <= c->rq.current_chunk_size); 4112 rbuff_grow_desired = 4113 ((cur_chunk_left + 2) > (uint_fast64_t) (c->read_buffer_size)); 4114 } 4115 } 4116 } 4117 } 4118 4119 if (! rbuff_grow_desired) 4120 return mhd_CONN_BUFF_GROW_OK; /* No need to increase the buffer */ 4121 4122 if (try_grow_read_buffer (c, rbuff_grow_required)) 4123 return mhd_CONN_BUFF_GROW_OK; /* Buffer increase succeed */ 4124 4125 if (! rbuff_grow_required) 4126 return mhd_CONN_BUFF_GROW_OK; /* Can continue without buffer increase */ 4127 4128 /* Failed to increase the read buffer size, but need to read the data 4129 from the network. 4130 No more space left in the buffer, no more space to increase the buffer. */ 4131 4132 switch (c->stage) 4133 { 4134 case mhd_HTTP_STAGE_INIT: 4135 stage = MHD_PROC_RECV_INIT; 4136 break; 4137 case mhd_HTTP_STAGE_REQ_LINE_RECEIVING: 4138 if (mhd_HTTP_METHOD_NO_METHOD == c->rq.http_mthd) 4139 stage = MHD_PROC_RECV_METHOD; 4140 else if (0 == c->rq.req_target_len) 4141 stage = MHD_PROC_RECV_URI; 4142 else 4143 stage = MHD_PROC_RECV_HTTPVER; 4144 break; 4145 case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING: 4146 stage = MHD_PROC_RECV_HEADERS; 4147 break; 4148 case mhd_HTTP_STAGE_BODY_RECEIVING: 4149 stage = c->rq.have_chunked_upload ? 4150 MHD_PROC_RECV_BODY_CHUNKED : MHD_PROC_RECV_BODY_NORMAL; 4151 break; 4152 case mhd_HTTP_STAGE_FOOTERS_RECEIVING: 4153 stage = MHD_PROC_RECV_FOOTERS; 4154 break; 4155 case mhd_HTTP_STAGE_REQ_LINE_RECEIVED: 4156 case mhd_HTTP_STAGE_HEADERS_RECEIVED: 4157 case mhd_HTTP_STAGE_HEADERS_PROCESSED: 4158 case mhd_HTTP_STAGE_CONTINUE_SENDING: 4159 case mhd_HTTP_STAGE_BODY_RECEIVED: 4160 case mhd_HTTP_STAGE_FOOTERS_RECEIVED: 4161 case mhd_HTTP_STAGE_FULL_REQ_RECEIVED: 4162 case mhd_HTTP_STAGE_REQ_RECV_FINISHED: 4163 case mhd_HTTP_STAGE_START_REPLY: 4164 case mhd_HTTP_STAGE_HEADERS_SENDING: 4165 case mhd_HTTP_STAGE_HEADERS_SENT: 4166 case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY: 4167 case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY: 4168 case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY: 4169 case mhd_HTTP_STAGE_CHUNKED_BODY_READY: 4170 case mhd_HTTP_STAGE_CHUNKED_BODY_SENT: 4171 case mhd_HTTP_STAGE_FOOTERS_SENDING: 4172 case mhd_HTTP_STAGE_FULL_REPLY_SENT: 4173 case mhd_HTTP_STAGE_PRE_CLOSING: 4174 case mhd_HTTP_STAGE_CLOSED: 4175 #ifdef MHD_SUPPORT_UPGRADE 4176 case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING: 4177 case mhd_HTTP_STAGE_UPGRADING: 4178 case mhd_HTTP_STAGE_UPGRADED: 4179 case mhd_HTTP_STAGE_UPGRADED_CLEANING: 4180 #endif /* MHD_SUPPORT_UPGRADE */ 4181 default: 4182 mhd_UNREACHABLE (); 4183 stage = MHD_PROC_RECV_BODY_NORMAL; 4184 break; 4185 } 4186 4187 res = handle_recv_no_space (c, stage); 4188 4189 mhd_assert (! res || ! c->dbg.closing_started); 4190 mhd_assert (res || c->dbg.closing_started); 4191 4192 return 4193 res ? mhd_CONN_BUFF_GROW_ERR_REPLY : mhd_CONN_BUFF_GROW_ERR_CONN_CLOSE; 4194 }