paivana-httpd_reverse.c (119906B)
1 /* 2 This file is part of GNU Taler 3 Copyright (C) 2012-2014 GNUnet e.V. 4 Copyright (C) 2018, 2025, 2026 Taler Systems SA 5 6 GNU Taler is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Affero General Public License 8 as published by the Free Software Foundation; either version 9 3, or (at your option) any later version. 10 11 GNU Taler is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public 17 License along with GNU Taler; see the file COPYING. If not, 18 write to the Free Software Foundation, Inc., 51 Franklin 19 Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 */ 21 22 /** 23 * @author Martin Schanzenbach 24 * @author Christian Grothoff 25 * @author Marcello Stanisci 26 * @file src/backend/paivana-httpd_reverse.c 27 * @brief Reverse proxy logic that just forwards the request 28 */ 29 #include "platform.h" 30 #include <curl/curl.h> 31 #include <gnunet/gnunet_util_lib.h> 32 #include <gnunet/gnunet_curl_lib.h> 33 #include <taler/taler_mhd_lib.h> 34 #include "paivana-httpd.h" 35 #include "paivana-httpd_cookie.h" 36 #include "paivana-httpd_helper.h" 37 #include "paivana-httpd_reverse.h" 38 39 40 /** 41 * Log curl error. 42 * 43 * @param level log level 44 * @param fun name of curl_easy-function that gave the error 45 * @param rc return code from curl 46 */ 47 #define LOG_CURL_EASY(level,fun,rc) \ 48 GNUNET_log (level, "%s failed at %s:%d: `%s'\n", fun, __FILE__, \ 49 __LINE__, \ 50 curl_easy_strerror (rc)) 51 52 /** 53 * Set curl option @a opt on handle @a h to @a val, saying so when 54 * libcurl declines. 55 * 56 * Every option here used to be set and the result thrown away, so a 57 * CURLE_OUT_OF_MEMORY on CURLOPT_URL or a CURLOPT_UNIX_SOCKET_PATH 58 * libcurl was not built for left no trace at all: the transfer went on 59 * with the option simply not in effect, and the operator saw only the 60 * eventual 502. Has to stay a macro because `curl_easy_setopt()' is 61 * variadic -- @a val must reach libcurl with the type it was written 62 * with. 63 */ 64 #define PH_SETOPT(h,opt,val) \ 65 do { \ 66 CURLcode setopt_rc = curl_easy_setopt (h, opt, val); \ 67 \ 68 if (CURLE_OK != setopt_rc) \ 69 LOG_CURL_EASY (GNUNET_ERROR_TYPE_WARNING, \ 70 "curl_easy_setopt(" # opt ")", \ 71 setopt_rc); \ 72 } while (0) 73 74 /** 75 * Value of `upstream_content_length` when the upstream declared no 76 * usable `Content-Length` for its response. Not a length any message 77 * can have: RFC 9110 §8.6 makes the field a count of octets, and we 78 * never buffer more than #GNUNET_MAX_MALLOC_CHECKED of them. 79 */ 80 #define PH_NO_CONTENT_LENGTH UINT64_MAX 81 82 /** 83 * The methods `configure_curl_method()' is willing to forward, in the 84 * form RFC 9110 §10.2.1 wants for an `Allow' field: sent on the 405 85 * for anything else, and on the OPTIONS we answer ourselves. 86 */ 87 #define PH_ALLOWED_METHODS \ 88 "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS" 89 90 /** 91 * Value for CURLOPT_CONNECTTIMEOUT, in seconds. 92 * 93 * Must stay below #PH_upstream_timeout: a connect that hangs is a 502, 94 * and it has to be able to say so before the gateway-timeout deadline 95 * claims it and calls the same failure a 504. 96 */ 97 #define PH_CURL_CONNECT_TIMEOUT_S 30L 98 99 100 /** 101 * Size of the buffer MHD fills from our content reader, and the unit 102 * in which a chunked response is chunked. Allocated per queued 103 * response, so it is a per-in-flight-request cost on top of 104 * #PH_response_buffer_max and wants to stay modest; 16 KiB also 105 * matches CURL_MAX_WRITE_SIZE, the most libcurl hands us at once, so 106 * one MHD read tends to correspond to one libcurl write. 107 */ 108 #define PH_STREAM_BLOCK_SIZE (16 * 1024) 109 110 /** 111 * Smallest ring we will allocate, and the step the ring grows in. 112 * Also the floor under the configured caps: a ring that cannot hold 113 * one libcurl delivery would pause with an empty buffer and never be 114 * woken, so #ring_put() has to be able to take CURL_MAX_WRITE_SIZE. 115 */ 116 #define PH_RING_MIN_SIZE (16 * 1024) 117 118 119 /** 120 * How far the client's request body has got. 121 * 122 * Independent of #DownState: since the origin request now starts 123 * before the body has arrived, the two directions run at the same 124 * time and neither is a stage of the other. 125 */ 126 enum UploadState 127 { 128 /** 129 * Initial state. MHD's first access-handler call (immediately 130 * after parsing the request headers, `upload_data_size == 0`) 131 * has not yet been observed. In this state we can still queue a 132 * final response before MHD auto-generates a 100 Continue and the 133 * client invariably starts with the upload (if any). 134 */ 135 UP_HEADERS_PENDING, 136 137 /** 138 * Relaying the client's body: MHD hands us chunks, they go into 139 * @e up_ring, libcurl's read callback takes them out. 140 */ 141 UP_STREAMING, 142 143 /** 144 * The client's body is fully received. Whatever is still in 145 * @e up_ring has to reach the origin, after which the read 146 * callback reports end-of-body. 147 */ 148 UP_CLIENT_DONE, 149 150 /** 151 * The origin has consumed the whole body. 152 */ 153 UP_DONE, 154 155 /** 156 * The origin transfer is over — it answered early, or failed — 157 * while the client is still sending. Its remaining bytes are read 158 * and discarded, because MHD will not let us queue a response 159 * while the request body is still arriving and closing the 160 * connection instead would throw away the status the origin took 161 * the trouble to send. 162 */ 163 UP_DRAINING 164 }; 165 166 167 /** 168 * How far the origin's response has got. 169 */ 170 enum DownState 171 { 172 /** 173 * The final response's header section has not ended yet. Nothing 174 * can be queued to the client: the status is not known. 175 */ 176 DOWN_WAITING_HEADERS, 177 178 /** 179 * Headers are in and the response is (or is about to be) queued; 180 * body bytes are moving from libcurl through @e down_ring into 181 * MHD's content reader. 182 */ 183 DOWN_STREAMING, 184 185 /** 186 * The origin delivered its whole body. Once @e down_ring drains, 187 * the content reader reports end of stream. 188 */ 189 DOWN_COMPLETE, 190 191 /** 192 * The transfer failed. If this happened before #DOWN_STREAMING we 193 * still owe the client a status (502 or 504); if it happened after, 194 * the status is long gone and all we can do is break the framing. 195 */ 196 DOWN_FAILED 197 }; 198 199 200 /** 201 * One direction's worth of body bytes in flight, as a ring buffer. 202 * 203 * A ring rather than a list of chunks: one allocation instead of one 204 * per delivery, a hard bound with no fragmentation, and no per-chunk 205 * bookkeeping. Grown lazily from #PH_RING_MIN_SIZE so that a 3 KiB 206 * HTML page does not cost a quarter megabyte — which matters now that 207 * every response takes this path, not only the large ones. 208 */ 209 struct PH_Ring 210 { 211 /** 212 * The bytes, @e size of them allocated, or NULL before the first 213 * one arrives. 214 */ 215 char *buf; 216 217 /** 218 * Bytes allocated in @e buf. 219 */ 220 size_t size; 221 222 /** 223 * Bytes currently held, starting at @e off and wrapping. 224 */ 225 size_t len; 226 227 /** 228 * Offset of the oldest byte held. 229 */ 230 size_t off; 231 232 /** 233 * Largest @e size we will grow to on demand. 234 */ 235 size_t cap; 236 }; 237 238 239 /** 240 * A header list 241 */ 242 struct HttpResponseHeader 243 { 244 /** 245 * DLL 246 */ 247 struct HttpResponseHeader *next; 248 249 /** 250 * DLL 251 */ 252 struct HttpResponseHeader *prev; 253 254 /** 255 * Header type 256 */ 257 char *type; 258 259 /** 260 * Header value 261 */ 262 char *value; 263 }; 264 265 266 /** 267 * A structure for socks requests 268 */ 269 struct HttpRequest 270 { 271 272 /** 273 * Kept in DLL. 274 */ 275 struct HttpRequest *prev; 276 277 /** 278 * Kept in DLL. 279 */ 280 struct HttpRequest *next; 281 282 /** 283 * MHD request that triggered us. 284 */ 285 struct MHD_Connection *con; 286 287 /** 288 * MHD response object for this request. 289 */ 290 struct MHD_Response *response; 291 292 /** 293 * The URL to fetch 294 */ 295 char *url; 296 297 /** 298 * Handle to cURL 299 */ 300 CURL *curl; 301 302 /** 303 * Job handle for the CURL request in our event loop. 304 */ 305 struct GNUNET_CURL_Job *job; 306 307 /** 308 * HTTP request headers for the curl request. 309 */ 310 struct curl_slist *headers; 311 312 /** 313 * Headers from response 314 */ 315 struct HttpResponseHeader *header_head; 316 317 /** 318 * Headers from response 319 */ 320 struct HttpResponseHeader *header_tail; 321 322 /** 323 * Client's request body on its way to the origin. 324 */ 325 struct PH_Ring up_ring; 326 327 /** 328 * Origin's response body on its way to the client. 329 */ 330 struct PH_Ring down_ring; 331 332 /** 333 * Bytes of request body received from the client so far, checked 334 * against #PH_max_request_size. A client that declares no 335 * `Content-Length` can only be caught here. 336 */ 337 uint64_t up_received; 338 339 /** 340 * Bytes of response body handed to MHD so far. Only used for 341 * logging a truncated response. 342 */ 343 uint64_t down_sent; 344 345 /** 346 * HTTP response code to give to MHD for the response. Taken from 347 * the origin's status line in `curl_check_hdr()` rather than from 348 * CURLINFO_RESPONSE_CODE, since it is needed while the transfer is 349 * still running. 350 */ 351 unsigned int response_code; 352 353 /** 354 * `Content-Length` the upstream declared for its response, or 355 * #PH_NO_CONTENT_LENGTH if it declared none we could parse. 356 * `curl_check_hdr()` drops the header itself and this value becomes 357 * the size of the MHD response instead, which is what makes MHD 358 * emit it again — and what makes a HEAD or a 304 report the length 359 * of a body it does not send. 360 */ 361 uint64_t upstream_content_length; 362 363 /** 364 * How far the client's request body has got. 365 */ 366 enum UploadState up_state; 367 368 /** 369 * How far the origin's response has got. 370 */ 371 enum DownState down_state; 372 373 /** 374 * Deferred unpause of libcurl, or NULL if none is pending. 375 * 376 * Never done directly from an MHD callback: GNUNET_CURL_job_unpause() 377 * hands over whatever libcurl buffered while paused by calling 378 * straight back into our stream callback, which would then be 379 * running inside MHD's content reader and could resume a connection 380 * that is not suspended. One scheduler round trip of latency 381 * removes the whole class of re-entrancy bug. 382 */ 383 struct GNUNET_SCHEDULER_Task *unpause_task; 384 385 /** 386 * Watchdog for #PH_upstream_stall_timeout; see `stall_check()`. 387 */ 388 struct GNUNET_SCHEDULER_Task *stall_task; 389 390 /** 391 * When a byte last moved in either direction. The stall watchdog 392 * measures from here rather than being rearmed on every delivery, 393 * which would mean a scheduler cancel-and-add per 16 KiB. 394 */ 395 struct GNUNET_TIME_Absolute last_progress; 396 397 /** 398 * Did we suspend MHD processing? 399 */ 400 enum GNUNET_GenericReturnValue suspended; 401 402 /** 403 * Is @e response ours to destroy? 404 * 405 * False for the four shared error responses, which outlive any one 406 * request. This used to be inferred by comparing @e response 407 * against those four statics, which only worked while they were 408 * non-NULL: #PAIVANA_HTTPD_reverse_shutdown() clears them while MHD 409 * may still hold queued references, and from then on every 410 * comparison matched nothing and cleanup destroyed a response it 411 * never owned. 412 */ 413 bool own_response; 414 415 /** 416 * Was the client's method HEAD? A response to HEAD never carries a 417 * body, however large a `Content-Length` it declares (RFC 9110 418 * §9.3.2), so the truncation check in `curl_download_cb()` must not 419 * be applied to it. 420 */ 421 bool head_request; 422 423 /** 424 * Has the blank line ending the final response's header section 425 * arrived? Everything `curl_check_hdr()' is handed after that is a 426 * TRAILER field: libcurl delivers those through the same callback 427 * (it flags them CURLH_TRAILER, which the callback cannot see) and 428 * there is no CURLOPT_* to suppress them. 429 * 430 * RFC 9110 §6.5.1: "A recipient MUST NOT merge a trailer field into 431 * a header section unless the recipient understands the 432 * corresponding header field definition and that definition 433 * explicitly permits and defines how trailer field values can be 434 * safely merged." Practically no field definition does; Set-Cookie 435 * and Cache-Control certainly do not. We also strip `Trailer' as 436 * hop-by-hop, so the client would not even be told which fields had 437 * been trailers. Hence: drop them. 438 */ 439 bool headers_complete; 440 441 /** 442 * Are we inside the header section of a 1xx interim response? 443 * 444 * libcurl reports the headers of *every* response it receives, 445 * including interim ones, through the same callback. Merging those 446 * into the final response is how a 103 Early Hints `Link' -- which 447 * origins commonly build out of the request path -- reappears on the 448 * final response, where it is subject to entirely different 449 * processing (preload fetches, CSP, cookie jars). 450 * 451 * RFC 9110 §15.2 says a proxy MUST forward 1xx responses; our fully 452 * buffered model cannot, since MHD is handed one finished response. 453 * Dropping them is therefore a known deviation -- but dropping is 454 * what the status code degrades to gracefully, and merging is not. 455 */ 456 bool interim_response; 457 458 /** 459 * Did #PH_upstream_timeout or #PH_upstream_stall_timeout expire on 460 * this request? Decides whether a #DOWN_FAILED that still owes the 461 * client a status becomes a 504 or a 502. 462 */ 463 bool upstream_timed_out; 464 465 /** 466 * Are we holding libcurl's receive side paused because @e down_ring 467 * is full? While this is set the stall watchdog does not run: the 468 * origin is not sending because we told it not to. 469 */ 470 bool curl_recv_paused; 471 472 /** 473 * Are we holding libcurl's send side paused because @e up_ring is 474 * empty and the client has not sent the rest of its body? 475 */ 476 bool curl_send_paused; 477 478 /** 479 * Directions @e unpause_task is to resume. 480 */ 481 bool want_recv_cont; 482 483 /** 484 * Directions @e unpause_task is to resume. 485 */ 486 bool want_send_cont; 487 488 /** 489 * Has the client's body been refused (413)? Set on the declared 490 * `Content-Length` where there is one, and otherwise once the body 491 * actually exceeds #PH_max_request_size. 492 */ 493 bool reject_upload; 494 495 /** 496 * Has `PAIVANA_HTTPD_reverse_cleanup()` run? Half of the ownership 497 * handshake with @e reader_gone: MHD calls its completion notifier 498 * *before* it destroys the response, so at that moment MHD still 499 * holds a content reader whose closure is this very structure. 500 * Whoever finishes second frees it. 501 */ 502 bool mhd_gone; 503 504 /** 505 * Has the content reader's free callback run, or was one never 506 * armed? Starts true, and is cleared when the streaming response 507 * is created. See @e mhd_gone. 508 */ 509 bool reader_gone; 510 511 /** 512 * Deadline for the upstream request, armed while @e job is 513 * outstanding. See #PH_upstream_timeout. 514 */ 515 struct GNUNET_SCHEDULER_Task *timeout_task; 516 517 /** 518 * Ready-made `Max-Forwards: N' line to send upstream in place of the 519 * client's, with N one less than the value we received; NULL when 520 * the client sent none or the method is not OPTIONS. `con_val_iter' 521 * drops the raw header whenever this is set, so the origin sees 522 * exactly one, decremented (RFC 9110 §7.6.2). 523 */ 524 char *max_forwards; 525 526 /** 527 * Concatenated value of the client's Via header(s), if any. Per 528 * RFC 9110 §7.6.3 a proxy must *append* its own entry to this 529 * list, not replace it; we capture the inbound value here before 530 * iterating over the other headers and emit our appended Via when 531 * building the curl request. 532 */ 533 char *client_via; 534 535 /** 536 * Concatenated value of the client's `X-Forwarded-For` header(s), 537 * if any. Only meaningful when #PH_respect_forwarded_headers is 538 * set, in which case our own entry is *appended* to this chain 539 * rather than replacing it: the origin needs the whole path a 540 * request took, and the client address it names has to agree with 541 * the one the access cookie was keyed on. Captured here (like 542 * @e client_via) because `con_val_iter` drops the raw header and 543 * `append_forwarded_headers` re-emits a single combined one. 544 */ 545 char *client_xff; 546 547 /** 548 * Value of the client's `X-Forwarded-Proto` header, if any. Used 549 * in place of the scheme of our own listener when 550 * #PH_respect_forwarded_headers is set. Not a list header: the 551 * first value wins. 552 */ 553 char *client_xfp; 554 555 /** 556 * Value of the client's `X-Forwarded-Port` header, if any. Like 557 * @e client_xfp this names a single value, so the first one wins. 558 * `con_val_iter' drops every `X-Forwarded-*' on the way in, and 559 * `append_forwarded_headers' used to re-emit only -For, -Proto and 560 * -Host -- so a port the front-end had told us about was consumed 561 * for our own base URL and then withheld from the origin, which 562 * then generated absolute URLs on the wrong port. 563 */ 564 char *client_xfport; 565 566 /** 567 * Value of the client's `X-Forwarded-Host` header, if any. Used 568 * in place of the client's `Host:` when 569 * #PH_respect_forwarded_headers is set. Not a list header: the 570 * first value wins. 571 */ 572 char *client_xfh; 573 574 /** 575 * Concatenated value of the client's `Forwarded` header(s), if any 576 * (RFC 7239). The standardized form of @e client_xff; like it, our 577 * own element is appended when #PH_respect_forwarded_headers is 578 * set, and it is replaced outright when it is not. 579 */ 580 char *client_forwarded; 581 582 /** 583 * Concatenated value of the client's Connection header(s), if 584 * any. Per RFC 9110 §7.6.1 this lists additional header names 585 * that are connection-specific and must not be forwarded; we 586 * consult this list in `con_val_iter` to filter such headers out 587 * of the upstream request. 588 */ 589 char *client_connection; 590 591 /** 592 * Concatenated value of the upstream's Connection header(s), if 593 * any. The symmetric counterpart of @e client_connection for the 594 * response direction: the names listed here must not be relayed to 595 * the client. Collected in `curl_check_hdr()` and applied to 596 * @e header_head in `curl_download_cb()` — it cannot be applied as 597 * the headers stream in, since Connection may arrive *after* the 598 * headers it names. 599 */ 600 char *upstream_connection; 601 }; 602 603 604 /** 605 * DLL of active HTTP requests. 606 */ 607 static struct HttpRequest *hr_head; 608 609 /** 610 * DLL of active HTTP requests. 611 */ 612 static struct HttpRequest *hr_tail; 613 614 /** 615 * Response we return on cURL failures. 616 */ 617 static struct MHD_Response *curl_failure_response; 618 619 /** 620 * Response we return when the upstream did not answer in time. 621 * Distinct from #curl_failure_response so the two can carry the status 622 * codes RFC 9110 §15.6.3 and §15.6.5 actually assign them. 623 */ 624 static struct MHD_Response *timeout_failure_response; 625 626 /** 627 * Response we return if the HTTP method is not allowed. 628 */ 629 static struct MHD_Response *method_failure_response; 630 631 /** 632 * Response we return if the upload is too big. 633 */ 634 static struct MHD_Response *upload_failure_response; 635 636 /** 637 * Response we return if we encountered an internal failure. 638 */ 639 static struct MHD_Response *internal_failure_response; 640 641 /** 642 * Response we return to an OPTIONS request we may not forward, that 643 * is one whose `Max-Forwards' had reached zero. 644 */ 645 static struct MHD_Response *options_response; 646 647 648 /** 649 * Create HTML response using @a body 650 * 651 * @param body UTF-8 encoded 0-terminated body to use 652 * @return NULL on error 653 */ 654 static struct MHD_Response * 655 make_html_response (const char *body) 656 { 657 struct MHD_Response *ret; 658 659 ret = MHD_create_response_from_buffer_static (strlen (body), 660 body); 661 if (NULL == ret) 662 { 663 GNUNET_break (0); 664 return NULL; 665 } 666 GNUNET_break (MHD_YES == 667 MHD_add_response_header (ret, 668 MHD_HTTP_HEADER_CONTENT_TYPE, 669 "text/html; charset=utf-8")); 670 return ret; 671 } 672 673 674 bool 675 PAIVANA_HTTPD_reverse_init (void) 676 { 677 static const char *curl_failure_body = 678 "<!DOCTYPE html>\n" 679 "<html><head><title>Bad Gateway</title></head>" 680 "<body><h1>502 Bad Gateway</h1>" 681 "<p>The upstream server could not be reached.</p>" 682 "</body></html>\n"; 683 static const char *timeout_failure_body = 684 "<!DOCTYPE html>\n" 685 "<html><head><title>Gateway Timeout</title></head>" 686 "<body><h1>504 Gateway Timeout</h1>" 687 "<p>The upstream server did not respond in time.</p>" 688 "</body></html>\n"; 689 static const char *internal_failure_body = 690 "<!DOCTYPE html>\n" 691 "<html><head><title>Internal server failure</title></head>" 692 "<body><h1>500 Internal Server Failure</h1>" 693 "<p>The server experienced an internal failure.</p>" 694 "</body></html>\n"; 695 static const char *upload_failure_body = 696 "<!DOCTYPE html>\n" 697 "<html><head><title>Content too large</title></head>" 698 "<body><h1>413 Content too large</h1>" 699 "<p>The size of the body exceeds the limit.</p>" 700 "</body></html>\n"; 701 static const char *method_failure_body = 702 "<!DOCTYPE html>\n" 703 "<html><head><title>Method not allowed</title></head>" 704 "<body><h1>405 Method not allowed</h1>" 705 "<p>The HTTP method specified is not allowed.</p>" 706 "</body></html>\n"; 707 708 curl_failure_response 709 = make_html_response (curl_failure_body); 710 if (NULL == curl_failure_response) 711 { 712 GNUNET_break (0); 713 return false; 714 } 715 timeout_failure_response 716 = make_html_response (timeout_failure_body); 717 if (NULL == timeout_failure_response) 718 { 719 GNUNET_break (0); 720 return false; 721 } 722 upload_failure_response 723 = make_html_response (upload_failure_body); 724 if (NULL == upload_failure_response) 725 { 726 GNUNET_break (0); 727 return false; 728 } 729 method_failure_response 730 = make_html_response (method_failure_body); 731 if (NULL == method_failure_response) 732 { 733 GNUNET_break (0); 734 return false; 735 } 736 GNUNET_break (MHD_YES == 737 MHD_add_response_header (method_failure_response, 738 MHD_HTTP_HEADER_ALLOW, 739 PH_ALLOWED_METHODS)); 740 /* Answer for an OPTIONS that arrives with `Max-Forwards: 0': we are 741 then the final recipient and must describe ourselves, not ask the 742 origin (RFC 9110 §7.6.2). */ 743 options_response 744 = MHD_create_response_from_buffer_static (0, 745 ""); 746 if (NULL == options_response) 747 { 748 GNUNET_break (0); 749 return false; 750 } 751 /* MHD derives `Content-Length: 0' from the zero-size body itself, 752 and rejects an explicit one here. */ 753 GNUNET_break (MHD_YES == 754 MHD_add_response_header (options_response, 755 MHD_HTTP_HEADER_ALLOW, 756 PH_ALLOWED_METHODS)); 757 internal_failure_response 758 = make_html_response (internal_failure_body); 759 if (NULL == internal_failure_response) 760 { 761 GNUNET_break (0); 762 return false; 763 } 764 return true; 765 } 766 767 768 void 769 PAIVANA_HTTPD_reverse_shutdown (void) 770 { 771 for (struct HttpRequest *hr = hr_head; 772 NULL != hr; 773 hr = hr->next) 774 { 775 if (GNUNET_YES == hr->suspended) 776 { 777 hr->suspended = GNUNET_NO; 778 MHD_resume_connection (hr->con); 779 } 780 } 781 /* Dropping *our* reference to the shared responses here is safe even 782 though MHD is stopped only afterwards: a queued response is 783 reference-counted (MHD_queue_response() takes its own reference), 784 so what follows merely relinquishes ownership. What must not 785 happen is a second release from 786 #PAIVANA_HTTPD_reverse_cleanup() — hence the explicit 787 `own_response` rather than a pointer comparison against the 788 statics we are about to NULL out. */ 789 if (NULL != curl_failure_response) 790 { 791 MHD_destroy_response (curl_failure_response); 792 curl_failure_response = NULL; 793 } 794 if (NULL != upload_failure_response) 795 { 796 MHD_destroy_response (upload_failure_response); 797 upload_failure_response = NULL; 798 } 799 if (NULL != method_failure_response) 800 { 801 MHD_destroy_response (method_failure_response); 802 method_failure_response = NULL; 803 } 804 if (NULL != internal_failure_response) 805 { 806 MHD_destroy_response (internal_failure_response); 807 internal_failure_response = NULL; 808 } 809 if (NULL != options_response) 810 { 811 MHD_destroy_response (options_response); 812 options_response = NULL; 813 } 814 if (NULL != timeout_failure_response) 815 { 816 MHD_destroy_response (timeout_failure_response); 817 timeout_failure_response = NULL; 818 } 819 } 820 821 822 /** 823 * Is @a name a well-formed HTTP field name, i.e. a non-empty `token' 824 * (RFC 9110 section 5.6.2)? 825 * 826 * What `curl_check_hdr()' calls a field name is whatever stood before 827 * the first colon of a line the upstream sent, which is only a field 828 * name if the line was a field line. An obs-fold continuation line 829 * that happens to contain a colon yields a "name" of leading 830 * whitespace and text; MHD refuses to attach it, but only after we 831 * have kept it around for the length of the request and asked. 832 * 833 * @param name candidate field name, 0-terminated 834 * @return true if @a name is a token 835 */ 836 static bool 837 is_field_name (const char *name) 838 { 839 if ('\0' == *name) 840 return false; 841 for (const char *p = name; '\0' != *p; p++) 842 { 843 if ( ( ('a' <= *p) && ('z' >= *p) ) || 844 ( ('A' <= *p) && ('Z' >= *p) ) || 845 ( ('0' <= *p) && ('9' >= *p) ) ) 846 continue; 847 if (NULL == strchr ("!#$%&'*+-.^_`|~", 848 *p)) 849 return false; 850 } 851 return true; 852 } 853 854 855 /** 856 * Is @a name a hop-by-hop HTTP header name that a proxy must not 857 * forward (RFC 9110 section 7.6.1 and RFC 7230, section 6.1). 858 * Either peer may name *additional* hop-by-hop headers in its 859 * Connection header; those are handled separately in 860 * `connection_lists_header()`. 861 * 862 * @param name header field name 863 * @return true if @a name is a hop-by-hop header 864 */ 865 static bool 866 is_hop_by_hop_header (const char *name) 867 { 868 static const char *const hop_headers[] = { 869 MHD_HTTP_HEADER_CONNECTION, 870 MHD_HTTP_HEADER_KEEP_ALIVE, 871 /* RFC 9110 section 7.6.1 names it explicitly among the fields an 872 intermediary should remove; libmicrohttpd has no 873 MHD_HTTP_HEADER_* for it. */ 874 "Proxy-Connection", 875 MHD_HTTP_HEADER_PROXY_AUTHENTICATE, 876 MHD_HTTP_HEADER_PROXY_AUTHORIZATION, 877 MHD_HTTP_HEADER_TE, 878 MHD_HTTP_HEADER_TRAILER, 879 MHD_HTTP_HEADER_TRANSFER_ENCODING, 880 MHD_HTTP_HEADER_UPGRADE, 881 NULL 882 }; 883 884 for (unsigned int i = 0; NULL != hop_headers[i]; i++) 885 if (0 == strcasecmp (name, 886 hop_headers[i])) 887 return true; 888 return false; 889 } 890 891 892 /** 893 * Return true if @a name appears (case-insensitively) as a 894 * comma-separated element of @a list. Used to honor RFC 9110 895 * §7.6.1: a Connection header lists additional hop-by-hop header 896 * names that must not be forwarded. Applied to the client's 897 * Connection list on the request side and to the upstream's on the 898 * response side. 899 * 900 * @param list comma-separated list of header names, may be NULL 901 * @param name header name to look for 902 * @return true if @a list names @a name 903 */ 904 static bool 905 connection_lists_header (const char *list, 906 const char *name) 907 { 908 size_t namelen; 909 const char *p; 910 911 if (NULL == list) 912 return false; 913 namelen = strlen (name); 914 p = list; 915 while ('\0' != *p) 916 { 917 const char *comma; 918 size_t len; 919 920 while ( (' ' == *p) || 921 ('\t' == *p) || 922 (',' == *p) ) 923 p++; 924 if ('\0' == *p) 925 break; 926 comma = strchr (p, ','); 927 len = (NULL == comma) ? strlen (p) : (size_t) (comma - p); 928 while ( (len > 0) && 929 ( (' ' == p[len - 1]) || 930 ('\t' == p[len - 1]) ) ) 931 len--; 932 if ( (len == namelen) && 933 (0 == strncasecmp (p, name, namelen)) ) 934 return true; 935 if (NULL == comma) 936 break; 937 p = comma + 1; 938 } 939 return false; 940 } 941 942 943 /** 944 * Append @a value to the list header accumulated at @a target, 945 * joining with ", " per the list-header combining rule of RFC 9110 946 * §5.3. Takes ownership of nothing; @a target is (re)allocated. 947 * 948 * @param[in,out] target where the combined value is kept, `*target` 949 * may be NULL (in which case @a value is simply duplicated) 950 * @param value value to append 951 */ 952 static void 953 append_list_value (char **target, 954 const char *value) 955 { 956 char *combined; 957 958 if (NULL == *target) 959 { 960 *target = GNUNET_strdup (value); 961 return; 962 } 963 GNUNET_asprintf (&combined, 964 "%s, %s", 965 *target, 966 value); 967 GNUNET_free (*target); 968 *target = combined; 969 } 970 971 972 /** 973 * Pre-iteration collector: records the client's Via, Connection and 974 * X-Forwarded-* headers on @a hr so the subsequent forwarding pass 975 * can append to Via (RFC 9110 §7.6.3), honor the hop-by-hop names 976 * listed in Connection (RFC 9110 §7.6.1), and extend rather than 977 * discard an inbound forwarding chain. `con_val_iter` drops all of 978 * these from the upstream request; `append_forwarded_headers` 979 * re-emits them, which is why they have to be captured before that 980 * pass runs. 981 * 982 * Via, Connection and X-Forwarded-For are list headers: multiple 983 * values are joined with ", " per RFC 9110 §5.3. X-Forwarded-Proto 984 * and X-Forwarded-Host name a single value each, so a repeat is 985 * ignored rather than concatenated into something no longer parseable 986 * as a scheme or an authority. 987 * 988 * @param cls our `struct HttpRequest *` 989 * @param kind value kind (unused) 990 * @param key header field name 991 * @param value header field value 992 * @return #MHD_YES to continue iteration 993 */ 994 static enum MHD_Result 995 collect_proxy_state (void *cls, 996 enum MHD_ValueKind kind, 997 const char *key, 998 const char *value) 999 { 1000 struct HttpRequest *hr = cls; 1001 char **target; 1002 1003 (void) kind; 1004 if (NULL == value) 1005 return MHD_YES; 1006 if (0 == strcasecmp (PH_HEADER_X_FORWARDED_PROTO, 1007 key)) 1008 { 1009 if (NULL == hr->client_xfp) 1010 hr->client_xfp = GNUNET_strdup (value); 1011 return MHD_YES; 1012 } 1013 if (0 == strcasecmp (PH_HEADER_X_FORWARDED_HOST, 1014 key)) 1015 { 1016 if (NULL == hr->client_xfh) 1017 hr->client_xfh = GNUNET_strdup (value); 1018 return MHD_YES; 1019 } 1020 if (0 == strcasecmp (PH_HEADER_X_FORWARDED_PORT, 1021 key)) 1022 { 1023 if (NULL == hr->client_xfport) 1024 hr->client_xfport = GNUNET_strdup (value); 1025 return MHD_YES; 1026 } 1027 if (0 == strcasecmp (MHD_HTTP_HEADER_VIA, 1028 key)) 1029 target = &hr->client_via; 1030 else if (0 == strcasecmp (MHD_HTTP_HEADER_CONNECTION, 1031 key)) 1032 target = &hr->client_connection; 1033 else if (0 == strcasecmp (PH_HEADER_X_FORWARDED_FOR, 1034 key)) 1035 target = &hr->client_xff; 1036 else if (0 == strcasecmp (MHD_HTTP_HEADER_FORWARDED, 1037 key)) 1038 target = &hr->client_forwarded; 1039 else 1040 return MHD_YES; 1041 append_list_value (target, 1042 value); 1043 return MHD_YES; 1044 } 1045 1046 1047 /* *************** HTTP handling with cURL ***************** */ 1048 1049 1050 /** 1051 * Is @a txt a port number we are willing to pass on? 1052 * 1053 * @param txt candidate text 1054 * @return true if @a txt is 1..5 digits naming a port in 1..65535 1055 */ 1056 static bool 1057 valid_port_text (const char *txt) 1058 { 1059 size_t len = strlen (txt); 1060 unsigned long port; 1061 1062 if ( (0 == len) || 1063 (len > 5) ) 1064 return false; 1065 for (size_t i = 0; i < len; i++) 1066 if (! isdigit ((unsigned char) txt[i])) 1067 return false; 1068 port = strtoul (txt, 1069 NULL, 1070 10); 1071 return (port > 0) && (port <= 65535); 1072 } 1073 1074 1075 /** 1076 * Return the port of the authority @a host, if it carries one. 1077 * 1078 * Understands the bracketed form an IPv6 literal needs (RFC 3986 1079 * §3.2.2), where the colons of the address itself must not be mistaken 1080 * for the port separator. 1081 * 1082 * @param host authority, such as "example.com:8443" or "[::1]:8443" 1083 * @return pointer into @a host just past the ':', or NULL if there is 1084 * no usable port 1085 */ 1086 static const char * 1087 host_port_suffix (const char *host) 1088 { 1089 const char *colon; 1090 1091 if ('[' == host[0]) 1092 { 1093 const char *close = strchr (host, 1094 ']'); 1095 1096 if (NULL == close) 1097 return NULL; 1098 colon = (':' == close[1]) ? close + 1 : NULL; 1099 } 1100 else 1101 { 1102 colon = strchr (host, 1103 ':'); 1104 /* More than one colon and no brackets is a bare IPv6 literal, 1105 which has no port to find. */ 1106 if ( (NULL != colon) && 1107 (NULL != strchr (colon + 1, 1108 ':')) ) 1109 return NULL; 1110 } 1111 if (NULL == colon) 1112 return NULL; 1113 return valid_port_text (colon + 1) ? colon + 1 : NULL; 1114 } 1115 1116 1117 /** 1118 * Discard every response header accumulated for @a hr so far. 1119 * 1120 * @param[in,out] hr request whose collected headers to release 1121 */ 1122 static void 1123 free_response_headers (struct HttpRequest *hr) 1124 { 1125 struct HttpResponseHeader *header; 1126 1127 while (NULL != (header = hr->header_head)) 1128 { 1129 GNUNET_CONTAINER_DLL_remove (hr->header_head, 1130 hr->header_tail, 1131 header); 1132 GNUNET_free (header->type); 1133 GNUNET_free (header->value); 1134 GNUNET_free (header); 1135 } 1136 } 1137 1138 1139 /* ******************* ring buffers ******************** */ 1140 1141 1142 /** 1143 * Release the memory @a r holds. 1144 * 1145 * @param[in,out] r ring to empty 1146 */ 1147 static void 1148 ring_free (struct PH_Ring *r) 1149 { 1150 GNUNET_free (r->buf); 1151 r->size = 0; 1152 r->len = 0; 1153 r->off = 0; 1154 } 1155 1156 1157 /** 1158 * Make room for at least @a need bytes in @a r, if we may. 1159 * 1160 * Doubles from #PH_RING_MIN_SIZE and stops at @e cap, so a small 1161 * response never costs more than the one delivery it arrived in. 1162 * Linearises on the way, which is what keeps the wrap arithmetic in 1163 * `ring_put()` and `ring_get()` to a single branch each. 1164 * 1165 * @param[in,out] r ring to grow 1166 * @param need bytes the caller wants to be able to hold in total 1167 * @param beyond_cap grow past @e cap if that is what @a need takes; 1168 * for the one caller that would otherwise deadlock 1169 */ 1170 static void 1171 ring_grow (struct PH_Ring *r, 1172 size_t need, 1173 bool beyond_cap) 1174 { 1175 size_t ns; 1176 char *nb; 1177 1178 if ( (! beyond_cap) && 1179 (need > r->cap) ) 1180 need = r->cap; 1181 if (need <= r->size) 1182 return; 1183 ns = (0 == r->size) ? PH_RING_MIN_SIZE : r->size; 1184 while (ns < need) 1185 ns *= 2; 1186 if ( (! beyond_cap) && 1187 (ns > r->cap) ) 1188 ns = r->cap; 1189 /* Deliberately not GNUNET_malloc(): that aborts the whole daemon 1190 when malloc() returns NULL, dropping every other client because 1191 one body could not be relayed. Keeping the smaller ring instead 1192 merely means more suspend/resume round trips. */ 1193 nb = GNUNET_malloc_large (ns); 1194 if (NULL == nb) 1195 { 1196 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1197 "Could not grow a relay buffer to %llu bytes\n", 1198 (unsigned long long) ns); 1199 return; 1200 } 1201 if (0 != r->len) 1202 { 1203 size_t first = GNUNET_MIN (r->len, 1204 r->size - r->off); 1205 1206 GNUNET_memcpy (nb, 1207 &r->buf[r->off], 1208 first); 1209 if (first < r->len) 1210 GNUNET_memcpy (&nb[first], 1211 r->buf, 1212 r->len - first); 1213 } 1214 GNUNET_free (r->buf); 1215 r->buf = nb; 1216 r->size = ns; 1217 r->off = 0; 1218 } 1219 1220 1221 /** 1222 * Copy as much of @a data into @a r as fits, growing it if it may. 1223 * 1224 * @param[in,out] r ring to append to 1225 * @param data bytes to append 1226 * @param n number of bytes in @a data 1227 * @param beyond_cap see `ring_grow()` 1228 * @return number of bytes taken, 0 if there was no room at all 1229 */ 1230 static size_t 1231 ring_put (struct PH_Ring *r, 1232 const char *data, 1233 size_t n, 1234 bool beyond_cap) 1235 { 1236 size_t wpos; 1237 size_t first; 1238 1239 if (r->size - r->len < n) 1240 ring_grow (r, 1241 r->len + n, 1242 beyond_cap); 1243 if (n > r->size - r->len) 1244 n = r->size - r->len; 1245 if (0 == n) 1246 return 0; 1247 wpos = (r->off + r->len) % r->size; 1248 first = GNUNET_MIN (n, 1249 r->size - wpos); 1250 GNUNET_memcpy (&r->buf[wpos], 1251 data, 1252 first); 1253 if (first < n) 1254 GNUNET_memcpy (r->buf, 1255 &data[first], 1256 n - first); 1257 r->len += n; 1258 return n; 1259 } 1260 1261 1262 /** 1263 * Copy all of @a data into @a r, or nothing at all. 1264 * 1265 * The all-or-nothing part is load-bearing on the download side. 1266 * libcurl's write callback cannot take a delivery partially — any 1267 * short return fails the transfer — so the only other answer is to 1268 * pause, and after the resume libcurl offers *the same bytes again 1269 * from the start*. A partial copy followed by a pause therefore 1270 * writes its prefix into the ring twice, which shows up as a 1271 * response that is longer than the origin sent and corrupt from the 1272 * first pause onwards. 1273 * 1274 * @param[in,out] r ring to append to 1275 * @param data bytes to append 1276 * @param n number of bytes in @a data 1277 * @param beyond_cap see `ring_grow()' 1278 * @return true if all @a n bytes were taken 1279 */ 1280 static bool 1281 ring_put_all (struct PH_Ring *r, 1282 const char *data, 1283 size_t n, 1284 bool beyond_cap) 1285 { 1286 if (r->size - r->len < n) 1287 ring_grow (r, 1288 r->len + n, 1289 beyond_cap); 1290 if (r->size - r->len < n) 1291 return false; /* nothing copied */ 1292 GNUNET_assert (n == ring_put (r, 1293 data, 1294 n, 1295 beyond_cap)); 1296 return true; 1297 } 1298 1299 1300 /** 1301 * Copy up to @a n bytes out of @a r into @a out. 1302 * 1303 * @param[in,out] r ring to consume from 1304 * @param[out] out where to put them 1305 * @param n capacity of @a out 1306 * @return number of bytes copied, 0 if @a r was empty 1307 */ 1308 static size_t 1309 ring_get (struct PH_Ring *r, 1310 char *out, 1311 size_t n) 1312 { 1313 size_t first; 1314 1315 if (n > r->len) 1316 n = r->len; 1317 if (0 == n) 1318 return 0; 1319 first = GNUNET_MIN (n, 1320 r->size - r->off); 1321 GNUNET_memcpy (out, 1322 &r->buf[r->off], 1323 first); 1324 if (first < n) 1325 GNUNET_memcpy (&out[first], 1326 r->buf, 1327 n - first); 1328 r->off = (r->off + n) % r->size; 1329 r->len -= n; 1330 if (0 == r->len) 1331 r->off = 0; /* keep the next fill linear */ 1332 return n; 1333 } 1334 1335 1336 /* ************ waking the two halves up ************* */ 1337 1338 1339 /** 1340 * Let MHD get on with @a hr: resume the connection if we suspended 1341 * it, and make sure the daemon is polled again. 1342 * 1343 * Safe to call from a libcurl callback — MHD_resume_connection() is 1344 * documented as callable from any thread and TALER_MHD_daemon_trigger() 1345 * only re-arms a scheduler task. The reverse direction, unpausing 1346 * libcurl, is *not* safe that way; see @e unpause_task. 1347 * 1348 * @param[in,out] hr request whose client connection to wake 1349 */ 1350 static void 1351 wake_client (struct HttpRequest *hr) 1352 { 1353 if (GNUNET_YES != hr->suspended) 1354 return; 1355 hr->suspended = GNUNET_NO; 1356 MHD_resume_connection (hr->con); 1357 TALER_MHD_daemon_trigger (); 1358 } 1359 1360 1361 /** 1362 * Stop letting MHD call us about @a hr until something wakes it. 1363 * 1364 * @param[in,out] hr request whose client connection to park 1365 */ 1366 static void 1367 suspend_client (struct HttpRequest *hr) 1368 { 1369 if (GNUNET_YES == hr->suspended) 1370 return; 1371 MHD_suspend_connection (hr->con); 1372 hr->suspended = GNUNET_YES; 1373 } 1374 1375 1376 /** 1377 * Resume the libcurl directions that were asked for. 1378 * 1379 * @param cls the `struct HttpRequest *` 1380 */ 1381 static void 1382 do_unpause (void *cls) 1383 { 1384 struct HttpRequest *hr = cls; 1385 bool woke = false; 1386 1387 hr->unpause_task = NULL; 1388 if (hr->want_recv_cont) 1389 { 1390 hr->want_recv_cont = false; 1391 hr->curl_recv_paused = false; 1392 woke = true; 1393 } 1394 if (hr->want_send_cont) 1395 { 1396 hr->want_send_cont = false; 1397 hr->curl_send_paused = false; 1398 woke = true; 1399 } 1400 if ( (! woke) || 1401 (NULL == hr->job) ) 1402 return; 1403 /* The origin was never at fault for the interval we held it paused, 1404 so the stall clock restarts from the resume rather than from the 1405 last byte that moved. */ 1406 hr->last_progress = GNUNET_TIME_absolute_get (); 1407 /* May call straight back into our stream or read callback with what 1408 libcurl buffered while paused, which is precisely why this is a 1409 task and not a call from inside MHD -- and why the two flags are 1410 cleared before the call rather than after. */ 1411 GNUNET_CURL_job_set_paused (hr->job, 1412 hr->curl_recv_paused, 1413 hr->curl_send_paused); 1414 } 1415 1416 1417 /** 1418 * Arrange for libcurl's receive side to be resumed. 1419 * 1420 * @param[in,out] hr request to resume 1421 */ 1422 static void 1423 schedule_recv_cont (struct HttpRequest *hr) 1424 { 1425 if (! hr->curl_recv_paused) 1426 return; 1427 hr->want_recv_cont = true; 1428 if (NULL == hr->unpause_task) 1429 hr->unpause_task = GNUNET_SCHEDULER_add_now (&do_unpause, 1430 hr); 1431 } 1432 1433 1434 /** 1435 * Arrange for libcurl's send side to be resumed. 1436 * 1437 * @param[in,out] hr request to resume 1438 */ 1439 static void 1440 schedule_send_cont (struct HttpRequest *hr) 1441 { 1442 if (! hr->curl_send_paused) 1443 return; 1444 hr->want_send_cont = true; 1445 if (NULL == hr->unpause_task) 1446 hr->unpause_task = GNUNET_SCHEDULER_add_now (&do_unpause, 1447 hr); 1448 } 1449 1450 1451 /* **************** the stall watchdog **************** */ 1452 1453 1454 /** 1455 * Give up on @a hr because the origin transfer failed or stalled. 1456 * 1457 * @param[in,out] hr request to fail 1458 */ 1459 static void 1460 fail_transfer (struct HttpRequest *hr) 1461 { 1462 if (NULL != hr->job) 1463 { 1464 GNUNET_CURL_job_cancel (hr->job); 1465 hr->job = NULL; 1466 } 1467 hr->down_state = DOWN_FAILED; 1468 if (UP_DONE != hr->up_state) 1469 hr->up_state = UP_DRAINING; 1470 wake_client (hr); 1471 } 1472 1473 1474 static void 1475 stall_check (void *cls); 1476 1477 1478 /** 1479 * Arm the stall watchdog for @a hr, unless we are the reason nothing 1480 * is moving. 1481 * 1482 * @param[in,out] hr request to watch 1483 */ 1484 static void 1485 stall_arm (struct HttpRequest *hr) 1486 { 1487 if (NULL != hr->stall_task) 1488 return; 1489 if (NULL == hr->job) 1490 return; 1491 hr->stall_task = GNUNET_SCHEDULER_add_delayed (PH_upstream_stall_timeout, 1492 &stall_check, 1493 hr); 1494 } 1495 1496 1497 /** 1498 * Note that a byte moved on @a hr, in either direction. 1499 * 1500 * Records the time instead of rearming the watchdog, so that a 200 MiB 1501 * transfer does not cost a scheduler cancel-and-add per 16 KiB. 1502 * 1503 * @param[in,out] hr request that made progress 1504 */ 1505 static void 1506 note_progress (struct HttpRequest *hr) 1507 { 1508 hr->last_progress = GNUNET_TIME_absolute_get (); 1509 stall_arm (hr); 1510 } 1511 1512 1513 /** 1514 * Task checking whether the origin has made any progress within 1515 * #PH_upstream_stall_timeout. 1516 * 1517 * The clock does not run while we hold libcurl's receive side paused: 1518 * the origin has stopped sending because we told it to, and blaming it 1519 * for that would kill exactly the transfers this whole change exists 1520 * to support — a large download to a client on a slow link. The send 1521 * side is different: there, nothing moving means the *client* has gone 1522 * quiet mid-body, and since a suspended connection is off MHD's 1523 * timeout lists (see `CLIENT_CONNECTION_TIMEOUT`) this watchdog is the 1524 * only thing that will ever notice. 1525 * 1526 * libcurl's own CURLOPT_LOW_SPEED_LIMIT / _TIME would be this, already 1527 * written and already tested — and it is deliberately not used. 1528 * `Curl_speedcheck()` exempts a transfer whose *receive* side is 1529 * paused ("A paused transfer is not qualified for speed checks"), but 1530 * there is no matching exemption for the send side, while the speed it 1531 * checks is the sum over both directions of one easy handle. Measured 1532 * against libcurl 8.14.1: a receive-paused transfer survives a 15 s 1533 * pause and a 200 B/s client for 180 s, but a send-paused upload dies 1534 * at exactly LOW_SPEED_TIME. Since one handle carries both directions 1535 * and the option is one setting on it, enabling it at all would make a 1536 * client on a slow uplink kill its own upload. 1537 * 1538 * @param cls the `struct HttpRequest *` 1539 */ 1540 static void 1541 stall_check (void *cls) 1542 { 1543 struct HttpRequest *hr = cls; 1544 struct GNUNET_TIME_Relative rem; 1545 1546 hr->stall_task = NULL; 1547 if (NULL == hr->job) 1548 return; 1549 if (hr->curl_recv_paused) 1550 { 1551 /* Our doing, not the origin's. Look again later. */ 1552 hr->last_progress = GNUNET_TIME_absolute_get (); 1553 hr->stall_task = GNUNET_SCHEDULER_add_delayed (PH_upstream_stall_timeout, 1554 &stall_check, 1555 hr); 1556 return; 1557 } 1558 rem = GNUNET_TIME_absolute_get_remaining ( 1559 GNUNET_TIME_absolute_add (hr->last_progress, 1560 PH_upstream_stall_timeout)); 1561 if (0 != rem.rel_value_us) 1562 { 1563 hr->stall_task = GNUNET_SCHEDULER_add_delayed (rem, 1564 &stall_check, 1565 hr); 1566 return; 1567 } 1568 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1569 "Upstream `%s' moved no data for %s; giving up\n", 1570 hr->url, 1571 GNUNET_STRINGS_relative_time_to_string ( 1572 PH_upstream_stall_timeout, 1573 true)); 1574 hr->upstream_timed_out = true; 1575 fail_transfer (hr); 1576 } 1577 1578 1579 /** 1580 * Transform _one_ CURL header (gotten from the request) into 1581 * MHD format and put it into the response headers list; mostly 1582 * copies the headers, but makes special adjustments based on 1583 * control requests. 1584 * 1585 * @param cls our `struct HttpRequest *` 1586 * @param buffer curl buffer with a single 1587 * line of header data; not 0-terminated! 1588 * @param bytes number of bytes in @a buffer 1589 * @return #GNUNET_OK to carry on with the transfer 1590 */ 1591 static enum GNUNET_GenericReturnValue 1592 curl_check_hdr (void *cls, 1593 const char *buffer, 1594 size_t bytes) 1595 { 1596 struct HttpRequest *hr = cls; 1597 struct HttpResponseHeader *header; 1598 size_t len; 1599 char *ndup; 1600 const char *hdr_type; 1601 char *hdr_val; 1602 char *tok; 1603 1604 /* Past the end of the final header section: a trailer field. See 1605 @e headers_complete. */ 1606 if (hr->headers_complete) 1607 return GNUNET_OK; 1608 /* Raw line is not guaranteed to be null-terminated. */ 1609 ndup = GNUNET_malloc (bytes + 1); 1610 memcpy (ndup, 1611 buffer, 1612 bytes); 1613 ndup[bytes] = '\0'; 1614 /* Trim the line terminator here rather than leaving it to the value 1615 parsing below: the blank line and the status line both have to be 1616 recognised before anything is treated as a field line. */ 1617 len = strlen (ndup); 1618 while ( (len > 0) && 1619 ( ('\r' == ndup[len - 1]) || 1620 ('\n' == ndup[len - 1]) ) ) 1621 ndup[--len] = '\0'; 1622 if (0 == len) 1623 { 1624 /* Blank line: end of a header section. Which one it ended is what 1625 decides whether trailers follow (final) or another response is 1626 still to come (interim). */ 1627 if (hr->interim_response) 1628 { 1629 hr->interim_response = false; 1630 GNUNET_free (ndup); 1631 return GNUNET_OK; 1632 } 1633 hr->headers_complete = true; 1634 GNUNET_free (ndup); 1635 /* The status is known, the header list is complete, and any 1636 `Content-Length' the origin declared has been recorded: this is 1637 the earliest moment at which the client can be given a status 1638 line. Everything after it is body, relayed as it arrives. 1639 1640 The response is not queued here. We are inside a libcurl 1641 callback, and queuing is MHD's business — and in any case MHD 1642 will not accept a response while the client's own request body 1643 is still coming in. Waking the access handler lets it decide; 1644 see `queue_stream_response()'. */ 1645 hr->down_state = DOWN_STREAMING; 1646 if (NULL != hr->timeout_task) 1647 { 1648 GNUNET_SCHEDULER_cancel (hr->timeout_task); 1649 hr->timeout_task = NULL; 1650 } 1651 note_progress (hr); 1652 wake_client (hr); 1653 return GNUNET_OK; 1654 } 1655 if (0 == strncasecmp (ndup, 1656 "HTTP/", 1657 strlen ("HTTP/"))) 1658 { 1659 /* A status line, not a field line. Recognising it is what keeps 1660 `HTTP/1.1 500 Error: foo' from being parsed as a header whose 1661 name is "HTTP/1.1 500 Error" -- MHD rejects that name, so the 1662 only visible effect used to be a remotely triggerable 1663 assertion in the log. */ 1664 unsigned int code = 0; 1665 const char *sp = strchr (ndup, ' '); 1666 1667 if (NULL != sp) 1668 code = (unsigned int) strtoul (sp + 1, 1669 NULL, 1670 10); 1671 hr->interim_response = ( (code >= 100) && 1672 (code < MHD_HTTP_OK) ); 1673 if (! hr->interim_response) 1674 { 1675 /* Start of a final response's header section. Discard anything 1676 accumulated so far: if a 1xx preceded us its fields belong to 1677 it alone, and libcurl can also replay a full exchange (for 1678 instance an authentication round) through this callback. */ 1679 free_response_headers (hr); 1680 hr->upstream_content_length = PH_NO_CONTENT_LENGTH; 1681 GNUNET_free (hr->upstream_connection); 1682 hr->upstream_connection = NULL; 1683 /* Taken from the status line rather than from 1684 CURLINFO_RESPONSE_CODE, which libgnunetcurl only reads out at 1685 completion: the response is queued long before that. */ 1686 hr->response_code = code; 1687 } 1688 GNUNET_free (ndup); 1689 return GNUNET_OK; 1690 } 1691 if (hr->interim_response) 1692 { 1693 PAIVANA_LOG_DEBUG ("Not merging interim-response header line `%s'\n", 1694 ndup); 1695 GNUNET_free (ndup); 1696 return GNUNET_OK; 1697 } 1698 hdr_type = strtok (ndup, ":"); 1699 if (NULL == hdr_type) 1700 { 1701 GNUNET_free (ndup); 1702 return GNUNET_OK; 1703 } 1704 if (! is_field_name (hdr_type)) 1705 { 1706 /* Not a field line at all. Dropping it here is what keeps the 1707 name out of `header_head' and out of MHD_add_response_header(), 1708 which would reject it once per response. */ 1709 GNUNET_break_op (0); 1710 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1711 "Upstream `%s' sent `%s' where a field name was due;" 1712 " dropping the line\n", 1713 hr->url, 1714 hdr_type); 1715 GNUNET_free (ndup); 1716 return GNUNET_OK; 1717 } 1718 hdr_val = strtok (NULL, "\n"); 1719 if (NULL == hdr_val) 1720 { 1721 GNUNET_free (ndup); 1722 return GNUNET_OK; 1723 } 1724 while (' ' == *hdr_val) 1725 hdr_val++; 1726 1727 /* MHD rejects CR and LF in header values, and forwarding either 1728 would be response splitting. HTAB, on the other hand, is legal 1729 field-content (RFC 9110 section 5.5) and truncating at it corrupts 1730 values such as a Content-Disposition filename. */ 1731 if (NULL != (tok = strchr (hdr_val, '\n'))) 1732 *tok = '\0'; 1733 if (NULL != (tok = strchr (hdr_val, '\r'))) 1734 *tok = '\0'; 1735 PAIVANA_LOG_DEBUG ("Parsed line: '%s: %s'\n", 1736 hdr_type, 1737 hdr_val); 1738 /* Do not relay the "Content-Length:" line itself: MHD emits the 1739 field from the size the response was created with, and adding a 1740 second one is a framing error. The value becomes exactly that 1741 size in `queue_stream_response()', so the origin's number does 1742 reach the client — including on a HEAD or a 304, where it states 1743 the length of a body that is not sent (RFC 9110 §8.6). */ 1744 if (0 == strcasecmp (hdr_type, 1745 MHD_HTTP_HEADER_CONTENT_LENGTH)) 1746 { 1747 char *endptr; 1748 unsigned long long cl; 1749 1750 errno = 0; 1751 cl = strtoull (hdr_val, 1752 &endptr, 1753 10); 1754 if ( (0 == errno) && 1755 (endptr != hdr_val) && 1756 ('\0' == *endptr) ) 1757 hr->upstream_content_length = (uint64_t) cl; 1758 GNUNET_free (ndup); 1759 return GNUNET_OK; 1760 } 1761 /* Remember the upstream's Connection list; the headers it names 1762 are connection-specific and get stripped from `header_head' in 1763 `curl_download_cb()' once the full header block is in (RFC 9110 1764 §7.6.1). The header itself is hop-by-hop and dropped below. */ 1765 if (0 == strcasecmp (hdr_type, 1766 MHD_HTTP_HEADER_CONNECTION)) 1767 append_list_value (&hr->upstream_connection, 1768 hdr_val); 1769 /* Skip hop-by-hop headers. In particular Transfer-Encoding 1770 must not leak through: libcurl has already dechunked the 1771 body for us and MHD will decide whether to re-chunk. */ 1772 if (is_hop_by_hop_header (hdr_type)) 1773 { 1774 GNUNET_free (ndup); 1775 return GNUNET_OK; 1776 } 1777 if (0 != strlen (hdr_val)) /* Rely in MHD to set those */ 1778 { 1779 header = GNUNET_new (struct HttpResponseHeader); 1780 header->type = GNUNET_strdup (hdr_type); 1781 header->value = GNUNET_strdup (hdr_val); 1782 /* RFC 9110 section 5.3: "a proxy MUST NOT change the order of 1783 these field line values when a message is forwarded". A head 1784 insert reverses them, which matters for Set-Cookie, Link, 1785 WWW-Authenticate and Content-Security-Policy. */ 1786 GNUNET_CONTAINER_DLL_insert_tail (hr->header_head, 1787 hr->header_tail, 1788 header); 1789 } 1790 GNUNET_free (ndup); 1791 return GNUNET_OK; 1792 } 1793 1794 1795 /** 1796 * Can a response with status @a code carry a body at all? 1797 * 1798 * RFC 9110 §6.4.1: 1xx, 204 and 304 responses never do. §8.6 even 1799 * allows a 304 to state the `Content-Length` the full representation 1800 * would have, which is precisely a declared length that no body 1801 * follows — so these have to be excluded before comparing the two. 1802 * 1803 * @param code HTTP status code the upstream returned 1804 * @return true if a response body is to be expected 1805 */ 1806 static bool 1807 status_allows_body (unsigned int code) 1808 { 1809 return (code >= MHD_HTTP_OK) && 1810 (MHD_HTTP_NO_CONTENT != code) && 1811 (MHD_HTTP_NOT_MODIFIED != code); 1812 } 1813 1814 1815 /** 1816 * Task run when the origin did not produce response *headers* within 1817 * #PH_upstream_timeout. 1818 * 1819 * Exists so that a slow origin can be told apart from an unreachable 1820 * one: both reach `curl_done_cb()' as a response code of 0, and RFC 1821 * 9110 gives them different status codes (§15.6.5 504 "did not receive 1822 * a timely response from an upstream server" versus §15.6.3 502). 1823 * Monitoring and CDN retry policies act on the difference -- a 504 is 1824 * retried, a 502 usually is not. 1825 * 1826 * Cancelled once the header section ends, because from there on the 1827 * status is already on its way to the client and a deadline can no 1828 * longer produce one. `stall_check()' takes over. 1829 * 1830 * @param cls the `struct HttpRequest *` that ran out of time 1831 */ 1832 static void 1833 upstream_timeout (void *cls) 1834 { 1835 struct HttpRequest *hr = cls; 1836 1837 hr->timeout_task = NULL; 1838 hr->upstream_timed_out = true; 1839 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1840 "Upstream did not answer for `%s' within %s\n", 1841 hr->url, 1842 GNUNET_STRINGS_relative_time_to_string (PH_upstream_timeout, 1843 true)); 1844 fail_transfer (hr); 1845 } 1846 1847 1848 /** 1849 * Handle a chunk of the response body as it arrives from the origin. 1850 * 1851 * libcurl's write callback semantics leave no room for a partial take: 1852 * anything short of @a data_size fails the transfer. So the ring 1853 * either has room for the whole delivery or we pause and libcurl 1854 * offers the same bytes again after the resume. 1855 * 1856 * @param cls our `struct HttpRequest *` 1857 * @param data body bytes 1858 * @param data_size number of bytes in @a data 1859 * @return @a data_size, or #GNUNET_CURL_STREAM_PAUSE 1860 */ 1861 static size_t 1862 stream_body_cb (void *cls, 1863 const void *data, 1864 size_t data_size) 1865 { 1866 struct HttpRequest *hr = cls; 1867 1868 if (0 == data_size) 1869 return 0; 1870 if (DOWN_FAILED == hr->down_state) 1871 return 0; /* fail the transfer; we are already giving up */ 1872 if (! ring_put_all (&hr->down_ring, 1873 data, 1874 data_size, 1875 false)) 1876 { 1877 if (0 != hr->down_ring.len) 1878 { 1879 /* The client has not drained what we already hold. Stop reading 1880 from the origin until it does -- this is the backpressure the 1881 whole design turns on, and the reason the stall watchdog does 1882 not run while it is in effect. Nothing has been taken from 1883 @a data; libcurl offers it again after the resume. */ 1884 hr->curl_recv_paused = true; 1885 return GNUNET_CURL_STREAM_PAUSE; 1886 } 1887 /* Empty and still too small: the configured cap is below one 1888 libcurl delivery, or the allocation failed. Pausing here would 1889 deadlock, since nothing will ever free more room. */ 1890 if (! ring_put_all (&hr->down_ring, 1891 data, 1892 data_size, 1893 true)) 1894 { 1895 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1896 "Could not buffer %llu bytes of the response to `%s'\n", 1897 (unsigned long long) data_size, 1898 hr->url); 1899 return 0; /* fail the transfer */ 1900 } 1901 } 1902 note_progress (hr); 1903 wake_client (hr); 1904 return data_size; 1905 } 1906 1907 1908 /** 1909 * The origin transfer is over, one way or the other. 1910 * 1911 * @param cls our `struct HttpRequest *` 1912 * @param response_code HTTP status the origin returned, 0 if the 1913 * transfer never got that far 1914 * @param body unused (NULL for a streaming job) 1915 * @param body_size unused (0 for a streaming job) 1916 */ 1917 static void 1918 curl_done_cb (void *cls, 1919 long response_code, 1920 const void *body, 1921 size_t body_size) 1922 { 1923 struct HttpRequest *hr = cls; 1924 1925 (void) body; 1926 (void) body_size; 1927 /* libgnunetcurl frees the easy handle immediately after we return, 1928 so anything that might unpause it has to stop referring to it 1929 now. */ 1930 hr->job = NULL; 1931 if (NULL != hr->timeout_task) 1932 { 1933 GNUNET_SCHEDULER_cancel (hr->timeout_task); 1934 hr->timeout_task = NULL; 1935 } 1936 if (NULL != hr->stall_task) 1937 { 1938 GNUNET_SCHEDULER_cancel (hr->stall_task); 1939 hr->stall_task = NULL; 1940 } 1941 if (NULL != hr->unpause_task) 1942 { 1943 GNUNET_SCHEDULER_cancel (hr->unpause_task); 1944 hr->unpause_task = NULL; 1945 } 1946 if (UP_DONE != hr->up_state) 1947 /* Whatever the client still owes us is no longer of interest, but 1948 it has to be read out of the way before MHD will let us queue. 1949 See #UP_DRAINING. */ 1950 hr->up_state = UP_DRAINING; 1951 if (0 == response_code) 1952 { 1953 /* The transfer failed. Which of the two failures it was depends 1954 on how far we had already got, and only we can tell: a chunked 1955 response cut off before its terminating chunk fails here just 1956 like an unreachable origin, and libgnunetcurl reports both as 1957 zero precisely so that the difference is ours to draw. */ 1958 if (hr->headers_complete) 1959 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1960 "Upstream `%s' failed after %llu bytes of a response" 1961 " already in flight; it can only be truncated\n", 1962 hr->url, 1963 (unsigned long long) (hr->down_sent 1964 + hr->down_ring.len)); 1965 else 1966 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1967 "Failed to receive response from HTTP server\n"); 1968 hr->down_state = DOWN_FAILED; 1969 wake_client (hr); 1970 return; 1971 } 1972 if (! hr->headers_complete) 1973 { 1974 /* libcurl called the transfer a success without ever ending a 1975 header section. Should not happen; nothing has been promised 1976 to the client yet, so an ordinary gateway failure is the honest 1977 answer. */ 1978 GNUNET_break (0); 1979 hr->down_state = DOWN_FAILED; 1980 wake_client (hr); 1981 return; 1982 } 1983 if ( (PH_NO_CONTENT_LENGTH != hr->upstream_content_length) && 1984 (! hr->head_request) && 1985 (status_allows_body (hr->response_code)) && 1986 (hr->upstream_content_length != 1987 hr->down_sent + (uint64_t) hr->down_ring.len) ) 1988 { 1989 /* The origin promised a length and did not deliver it. The 1990 promise is already with the client, so the only signal left is 1991 to break the framing: see `body_reader()'. */ 1992 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1993 "Upstream declared %llu bytes for `%s' but delivered %llu;" 1994 " the response is already in flight, so it can only be" 1995 " truncated\n", 1996 (unsigned long long) hr->upstream_content_length, 1997 hr->url, 1998 (unsigned long long) (hr->down_sent + hr->down_ring.len)); 1999 hr->down_state = DOWN_FAILED; 2000 wake_client (hr); 2001 return; 2002 } 2003 hr->down_state = DOWN_COMPLETE; 2004 wake_client (hr); 2005 } 2006 2007 2008 /** 2009 * cURL callback asking for the next piece of the request body. 2010 * 2011 * @param buf where to write the data 2012 * @param size number of bytes per member 2013 * @param nmemb number of members available in @a buf 2014 * @param cls our `struct HttpRequest` that generated the data 2015 * @return number of bytes copied to @a buf, 0 for end of body, 2016 * #CURL_READFUNC_PAUSE or #CURL_READFUNC_ABORT 2017 */ 2018 static size_t 2019 curl_upload_cb (void *buf, 2020 size_t size, 2021 size_t nmemb, 2022 void *cls) 2023 { 2024 struct HttpRequest *hr = cls; 2025 size_t len = size * nmemb; 2026 size_t n; 2027 2028 n = ring_get (&hr->up_ring, 2029 buf, 2030 len); 2031 if (0 != n) 2032 { 2033 note_progress (hr); 2034 /* Room freed: MHD may hand us more of the client's body. */ 2035 wake_client (hr); 2036 return n; 2037 } 2038 switch (hr->up_state) 2039 { 2040 case UP_HEADERS_PENDING: 2041 case UP_STREAMING: 2042 /* The client has not caught up. Stop asking until it does. */ 2043 hr->curl_send_paused = true; 2044 return CURL_READFUNC_PAUSE; 2045 case UP_CLIENT_DONE: 2046 hr->up_state = UP_DONE; 2047 return 0; 2048 case UP_DONE: 2049 return 0; 2050 case UP_DRAINING: 2051 /* The client went away, or we gave up on it, in the middle of a 2052 body whose length we already declared upstream. We cannot 2053 deliver what we promised, so the origin has to be told the 2054 request is broken rather than left waiting for bytes that will 2055 never come. */ 2056 return CURL_READFUNC_ABORT; 2057 } 2058 GNUNET_break (0); 2059 return CURL_READFUNC_ABORT; 2060 } 2061 2062 2063 /** 2064 * MHD content reader: hand the client the next piece of the response. 2065 * 2066 * The contract, stated in our own terms rather than MHD 1.x's so that 2067 * the MHD2 port is a rewrite against something written down: return 2068 * however many bytes fit in @a buf, or suspend the connection and 2069 * return nothing, or declare the stream finished -- with an error if 2070 * the body is short of what was promised, cleanly if it is not. 2071 * 2072 * @param cls our `struct HttpRequest *` 2073 * @param pos byte offset in the response (unused; the ring is the 2074 * cursor) 2075 * @param[out] buf where to write 2076 * @param max capacity of @a buf 2077 * @return number of bytes written, 0 (suspended), 2078 * #MHD_CONTENT_READER_END_OF_STREAM or 2079 * #MHD_CONTENT_READER_END_WITH_ERROR 2080 */ 2081 static ssize_t 2082 body_reader (void *cls, 2083 uint64_t pos, 2084 char *buf, 2085 size_t max) 2086 { 2087 struct HttpRequest *hr = cls; 2088 size_t n; 2089 2090 if (pos != hr->down_sent) 2091 { 2092 /* The ring *is* the cursor, so it cannot go back to an offset it 2093 has already handed out. MHD 1.x never asks -- it caches a 2094 ready block itself and only ever advances, which is why this 2095 has held across every case in the suite -- but silently serving 2096 the wrong bytes would be far worse than failing the response. 2097 Anything that made this fire would also be the first thing to 2098 check when porting to MHD2. */ 2099 GNUNET_break (0); 2100 return MHD_CONTENT_READER_END_WITH_ERROR; 2101 } 2102 if (hr->mhd_gone) 2103 { 2104 /* Cannot happen: MHD does not read from a request it has already 2105 reported as completed. Answering rather than asserting keeps a 2106 surprise here from taking the daemon down. */ 2107 GNUNET_break (0); 2108 return MHD_CONTENT_READER_END_WITH_ERROR; 2109 } 2110 n = ring_get (&hr->down_ring, 2111 buf, 2112 max); 2113 if (0 != n) 2114 { 2115 hr->down_sent += n; 2116 /* Room freed: let the origin send again. */ 2117 schedule_recv_cont (hr); 2118 return (ssize_t) n; 2119 } 2120 switch (hr->down_state) 2121 { 2122 case DOWN_COMPLETE: 2123 return MHD_CONTENT_READER_END_OF_STREAM; 2124 case DOWN_FAILED: 2125 /* Never END_OF_STREAM here: under chunked framing that writes the 2126 terminating zero-length chunk, which tells the client a 2127 truncated body was complete. END_WITH_ERROR closes without it, 2128 and closes short of a declared Content-Length, both of which RFC 2129 9112 §8.1.2 requires the client to treat as a failure. */ 2130 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2131 "Truncating the response to `%s' after %llu bytes\n", 2132 hr->url, 2133 (unsigned long long) hr->down_sent); 2134 return MHD_CONTENT_READER_END_WITH_ERROR; 2135 case DOWN_WAITING_HEADERS: 2136 case DOWN_STREAMING: 2137 /* Nothing to give yet. Returning 0 without suspending would leave 2138 MHD in NORMAL_BODY_UNREADY with MHD_EVENT_LOOP_INFO_PROCESS, 2139 i.e. spinning. */ 2140 suspend_client (hr); 2141 schedule_recv_cont (hr); 2142 return 0; 2143 } 2144 GNUNET_break (0); 2145 return MHD_CONTENT_READER_END_WITH_ERROR; 2146 } 2147 2148 2149 /** 2150 * Release @a hr once neither MHD nor the content reader can reach it. 2151 * 2152 * @param[in] hr request to free 2153 */ 2154 static void 2155 reverse_destroy (struct HttpRequest *hr) 2156 { 2157 ring_free (&hr->up_ring); 2158 ring_free (&hr->down_ring); 2159 free_response_headers (hr); 2160 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2161 "Proxying of '%s' completely done\n", 2162 hr->url); 2163 GNUNET_free (hr->url); 2164 GNUNET_free (hr->client_via); 2165 GNUNET_free (hr->client_xff); 2166 GNUNET_free (hr->client_xfp); 2167 GNUNET_free (hr->client_xfh); 2168 GNUNET_free (hr->client_xfport); 2169 GNUNET_free (hr->max_forwards); 2170 GNUNET_free (hr->client_forwarded); 2171 GNUNET_free (hr->client_connection); 2172 GNUNET_free (hr->upstream_connection); 2173 GNUNET_CONTAINER_DLL_remove (hr_head, 2174 hr_tail, 2175 hr); 2176 GNUNET_free (hr); 2177 } 2178 2179 2180 /** 2181 * MHD is done with the streaming response and is dropping its 2182 * reference to our closure. 2183 * 2184 * The second half of the ownership handshake: MHD calls its completion 2185 * notifier -- and therefore `PAIVANA_HTTPD_reverse_cleanup()' -- while 2186 * it still holds this response, and destroys the response only 2187 * afterwards (see `connection.c' 1344-1354 and 7523-7532). Whichever 2188 * of the two runs last does the freeing. 2189 * 2190 * @param cls our `struct HttpRequest *` 2191 */ 2192 static void 2193 body_reader_free (void *cls) 2194 { 2195 struct HttpRequest *hr = cls; 2196 2197 hr->reader_gone = true; 2198 if (hr->mhd_gone) 2199 reverse_destroy (hr); 2200 } 2201 2202 2203 /* ************** helper functions ************* */ 2204 2205 /** 2206 * Append the field line @a line to the header list we will send 2207 * upstream for @a hr. 2208 * 2209 * Replaces the `hr->headers = curl_slist_append (hr->headers, ...)' 2210 * idiom, which is wrong in the one case it exists to handle: 2211 * curl_slist_append() returns NULL on allocation failure *without* 2212 * touching the list it was given, so the assignment both leaks every 2213 * header accumulated so far and silently drops them -- the request 2214 * would go upstream stripped of its Authorization, its Cookie and its 2215 * Host, and the origin would answer something plausible. Losing the 2216 * one line we could not allocate is the smaller error. 2217 * 2218 * @param[in,out] hr request whose upstream header list to extend 2219 * @param line complete "Name: value" line to append 2220 */ 2221 static void 2222 append_curl_header (struct HttpRequest *hr, 2223 const char *line) 2224 { 2225 struct curl_slist *ext; 2226 2227 ext = curl_slist_append (hr->headers, 2228 line); 2229 if (NULL == ext) 2230 { 2231 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2232 "Could not append `%s' to the upstream request headers\n", 2233 line); 2234 return; 2235 } 2236 hr->headers = ext; 2237 } 2238 2239 2240 /** 2241 * Build the `Host:` header line to send upstream, naming the 2242 * authority of @a url. 2243 * 2244 * The value is the host and — only if @a url states one explicitly — 2245 * the port. Everything else in the authority must be left out: RFC 2246 * 9110 §7.2 forbids userinfo in `Host:`, so a base URL of 2247 * "http://user:pw@origin/" still has to yield "Host: origin". An 2248 * IPv6 literal conversely has to *keep* its brackets (RFC 3986 2249 * §3.2.2), as the colons would otherwise be read as a port 2250 * delimiter. Both are why the authority is taken from libcurl's URL 2251 * parser instead of being cut out of the string by hand. 2252 * 2253 * @param url full fledged URL 2254 * @return the header line, or NULL if @a url has no usable 2255 * authority; to be freed by the caller 2256 */ 2257 static char * 2258 build_host_header (const char *url) 2259 { 2260 CURLU *h; 2261 char *host; 2262 char *port = NULL; 2263 char *header; 2264 2265 h = curl_url (); 2266 if (NULL == h) 2267 { 2268 GNUNET_break (0); 2269 return NULL; 2270 } 2271 if ( (CURLUE_OK != 2272 curl_url_set (h, 2273 CURLUPART_URL, 2274 url, 2275 0)) || 2276 (CURLUE_OK != 2277 curl_url_get (h, 2278 CURLUPART_HOST, 2279 &host, 2280 0)) ) 2281 { 2282 /* PH_target_server_base_url passed TALER_is_web_url() at 2283 startup, so failing to parse it here would be a bug. */ 2284 GNUNET_break (0); 2285 curl_url_cleanup (h); 2286 return NULL; 2287 } 2288 /* Absent unless the URL spells the port out; libcurl elides the 2289 scheme's default, which is what an origin expects to see. */ 2290 if (CURLUE_OK != 2291 curl_url_get (h, 2292 CURLUPART_PORT, 2293 &port, 2294 0)) 2295 port = NULL; 2296 GNUNET_asprintf (&header, 2297 "Host: %s%s%s", 2298 host, 2299 (NULL != port) ? ":" : "", 2300 (NULL != port) ? port : ""); 2301 curl_free (host); 2302 curl_free (port); 2303 curl_url_cleanup (h); 2304 return header; 2305 } 2306 2307 2308 /** 2309 * Remove our own access cookie from a client-supplied `Cookie:` 2310 * header value. 2311 * 2312 * #PAIVANA_COOKIE_NAME carries the token that proves payment to 2313 * *this* proxy; relaying it would hand the origin a credential it 2314 * has no business seeing and that it could replay against us. Every 2315 * other cookie-pair is the origin's own and is passed through 2316 * untouched, in the order and spelling the client used. 2317 * 2318 * The name comparison is case-insensitive because that is how MHD 2319 * matches it when we look the cookie up: anything MHD would accept 2320 * as our cookie must also be stripped here. 2321 * 2322 * @param value raw value of the client's `Cookie:` header 2323 * @return the remaining cookie string, or NULL if nothing is left to 2324 * forward; to be freed by the caller 2325 */ 2326 static char * 2327 strip_paivana_cookie (const char *value) 2328 { 2329 struct GNUNET_Buffer buf = { 0 }; 2330 const char *pos = value; 2331 bool empty = true; 2332 2333 while ('\0' != *pos) 2334 { 2335 const char *start = pos; 2336 const char *end = strchrnul (pos, 2337 ';'); 2338 const char *nend; 2339 2340 pos = ('\0' == *end) ? end : end + 1; 2341 /* trim the optional whitespace around the cookie-pair */ 2342 while ( (start < end) && 2343 ( (' ' == *start) || ('\t' == *start) ) ) 2344 start++; 2345 while ( (end > start) && 2346 ( (' ' == end[-1]) || ('\t' == end[-1]) ) ) 2347 end--; 2348 if (start == end) 2349 continue; /* empty element, e.g. from a stray ';' */ 2350 /* the cookie-name runs up to the first '=' (RFC 6265 §4.2.1) */ 2351 nend = memchr (start, 2352 '=', 2353 end - start); 2354 if (NULL == nend) 2355 nend = end; 2356 if ( (strlen (PAIVANA_COOKIE_NAME) == (size_t) (nend - start)) && 2357 (0 == strncasecmp (start, 2358 PAIVANA_COOKIE_NAME, 2359 nend - start)) ) 2360 { 2361 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2362 "Not forwarding our own access cookie upstream\n"); 2363 continue; 2364 } 2365 if (! empty) 2366 GNUNET_buffer_write_str (&buf, 2367 "; "); 2368 empty = false; 2369 GNUNET_buffer_write (&buf, 2370 start, 2371 end - start); 2372 } 2373 if (empty) 2374 { 2375 GNUNET_buffer_clear (&buf); 2376 return NULL; 2377 } 2378 return GNUNET_buffer_reap_str (&buf); 2379 } 2380 2381 2382 /* ************** main loop of cURL interaction ************* */ 2383 2384 2385 /** 2386 * "Filter" function that translates MHD request headers to 2387 * cURL's. 2388 * 2389 * @param cls our `struct HttpRequest` 2390 * @param kind value kind 2391 * @param key field key 2392 * @param value field value 2393 * @return #MHD_YES to continue to iterate 2394 */ 2395 static enum MHD_Result 2396 con_val_iter (void *cls, 2397 enum MHD_ValueKind kind, 2398 const char *key, 2399 const char *value) 2400 { 2401 struct HttpRequest *hr = cls; 2402 char *cookies = NULL; 2403 char *hdr; 2404 2405 (void) kind; 2406 if (NULL == value) 2407 return MHD_YES; 2408 if (0 == strcasecmp (MHD_HTTP_HEADER_HOST, 2409 key)) 2410 { 2411 /* We don't take the host header as given in the request. 2412 * We'll instead put the proxied service's hostname in it*/ 2413 return MHD_YES; 2414 } 2415 if (0 == strcasecmp (MHD_HTTP_HEADER_CONTENT_LENGTH, 2416 key)) 2417 { 2418 /* libcurl sets Content-Length itself from CURLOPT_POSTFIELDSIZE 2419 / CURLOPT_INFILESIZE. */ 2420 return MHD_YES; 2421 } 2422 if (0 == strcasecmp (MHD_HTTP_HEADER_EXPECT, 2423 key)) 2424 { 2425 /* libcurl manages Expect: 100-continue on its own. */ 2426 return MHD_YES; 2427 } 2428 if ( (NULL != hr->max_forwards) && 2429 (0 == strcasecmp (MHD_HTTP_HEADER_MAX_FORWARDS, 2430 key)) ) 2431 { 2432 /* Replaced by the decremented copy `configure_curl_method' built. */ 2433 return MHD_YES; 2434 } 2435 if (is_hop_by_hop_header (key)) 2436 return MHD_YES; 2437 /* RFC 9110 §7.6.1: suppress any header named by the client's 2438 Connection list (additional hop-by-hop headers). */ 2439 if (connection_lists_header (hr->client_connection, 2440 key)) 2441 return MHD_YES; 2442 if ( (0 == strncasecmp ("X-Forwarded-", 2443 key, 2444 strlen ("X-Forwarded-"))) || 2445 (0 == strcasecmp (MHD_HTTP_HEADER_FORWARDED, 2446 key)) ) 2447 { 2448 /* We will replace these with our own below. */ 2449 return MHD_YES; 2450 } 2451 if (0 == strcasecmp (MHD_HTTP_HEADER_VIA, 2452 key)) 2453 { 2454 /* The client's Via was captured in `collect_proxy_state` and 2455 will be emitted below with our own entry appended (RFC 9110 2456 §7.6.3). Drop the raw header here so we don't forward it 2457 twice. */ 2458 return MHD_YES; 2459 } 2460 if (0 == strcasecmp (MHD_HTTP_HEADER_COOKIE, 2461 key)) 2462 { 2463 /* Our access cookie is for us, not for the origin. */ 2464 cookies = strip_paivana_cookie (value); 2465 if (NULL == cookies) 2466 return MHD_YES; /* it was the only cookie: no header to forward */ 2467 value = cookies; 2468 } 2469 GNUNET_asprintf (&hdr, 2470 "%s: %s", 2471 key, 2472 value); 2473 GNUNET_free (cookies); 2474 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2475 "Adding header `%s' to HTTP request\n", 2476 hdr); 2477 append_curl_header (hr, 2478 hdr); 2479 GNUNET_free (hdr); 2480 return MHD_YES; 2481 } 2482 2483 2484 struct HttpRequest * 2485 PAIVANA_HTTPD_reverse_create (struct MHD_Connection *connection, 2486 const char *url) 2487 { 2488 struct HttpRequest *hr; 2489 2490 hr = GNUNET_new (struct HttpRequest); 2491 hr->up_state = UP_HEADERS_PENDING; 2492 hr->down_state = DOWN_WAITING_HEADERS; 2493 hr->upstream_content_length = PH_NO_CONTENT_LENGTH; 2494 /* No content reader is armed until the streaming response is 2495 created, so until then this half of the handshake is already 2496 satisfied. */ 2497 hr->reader_gone = true; 2498 /* A ring that cannot take one libcurl delivery would pause with an 2499 empty buffer and never be woken; the floor is what rules that 2500 out, whatever the operator configured. */ 2501 hr->up_ring.cap = GNUNET_MAX (PH_RING_MIN_SIZE, 2502 PH_request_buffer_max); 2503 hr->down_ring.cap = GNUNET_MAX (PH_RING_MIN_SIZE, 2504 PH_response_buffer_max); 2505 hr->con = connection; 2506 hr->url = GNUNET_strdup (url); 2507 GNUNET_CONTAINER_DLL_insert (hr_head, 2508 hr_tail, 2509 hr); 2510 return hr; 2511 } 2512 2513 2514 void 2515 PAIVANA_HTTPD_reverse_cleanup (struct HttpRequest *hr) 2516 { 2517 struct MHD_Response *resp; 2518 2519 if (NULL != hr->curl) 2520 { 2521 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2522 "Resetting cURL handle\n"); 2523 curl_easy_cleanup (hr->curl); 2524 hr->curl = NULL; 2525 } 2526 if (NULL != hr->timeout_task) 2527 { 2528 GNUNET_SCHEDULER_cancel (hr->timeout_task); 2529 hr->timeout_task = NULL; 2530 } 2531 if (NULL != hr->stall_task) 2532 { 2533 GNUNET_SCHEDULER_cancel (hr->stall_task); 2534 hr->stall_task = NULL; 2535 } 2536 if (NULL != hr->unpause_task) 2537 { 2538 GNUNET_SCHEDULER_cancel (hr->unpause_task); 2539 hr->unpause_task = NULL; 2540 } 2541 if (NULL != hr->job) 2542 { 2543 /* Also the point of cancelling here rather than letting the 2544 transfer run out: a download whose client has gone stops 2545 consuming the origin's bandwidth instead of being read to the 2546 end into a buffer nobody will ever look at. */ 2547 GNUNET_CURL_job_cancel (hr->job); 2548 hr->job = NULL; 2549 } 2550 if (NULL != hr->headers) 2551 { 2552 curl_slist_free_all (hr->headers); 2553 hr->headers = NULL; 2554 } 2555 /* The connection is MHD's again the moment we return from its 2556 completion notifier, so nothing may reach for it after this. */ 2557 hr->con = NULL; 2558 hr->suspended = GNUNET_NO; 2559 resp = hr->response; 2560 hr->response = NULL; 2561 if ( (NULL != resp) && 2562 (hr->own_response) ) 2563 /* The shared error responses belong to this module, not to the 2564 request that queued one; releasing them here would take a 2565 reference we never held. 2566 2567 Dropping our reference may run the content reader's free 2568 callback right here, when MHD held no reference of its own -- 2569 which is why @e mhd_gone is still false at this point. The 2570 callback then only records that it has fired, and the check 2571 below does the freeing. */ 2572 MHD_destroy_response (resp); 2573 hr->mhd_gone = true; 2574 if (hr->reader_gone) 2575 reverse_destroy (hr); 2576 } 2577 2578 2579 /** 2580 * How the client framed its request body, if it sent one at all. 2581 */ 2582 enum ClientBodyKind 2583 { 2584 /** 2585 * No body: neither `Content-Length` nor `Transfer-Encoding`, or a 2586 * `Content-Length` of zero. 2587 */ 2588 CLIENT_BODY_NONE, 2589 2590 /** 2591 * A body of a length the client stated. 2592 */ 2593 CLIENT_BODY_LENGTH, 2594 2595 /** 2596 * A body whose length is not known in advance. 2597 */ 2598 CLIENT_BODY_CHUNKED, 2599 2600 /** 2601 * A `Content-Length` we could not make sense of. Not the same as 2602 * #CLIENT_BODY_NONE: MHD has already accepted the request, so 2603 * something may still arrive. 2604 */ 2605 CLIENT_BODY_BAD 2606 }; 2607 2608 2609 /** 2610 * Work out whether the client is sending a body, and how it is framed. 2611 * 2612 * Replaces looking at how much body had been buffered, which is no 2613 * longer knowable at the time the origin request is built: it now 2614 * starts before the body arrives. Reading the client's own framing is 2615 * in any case the more correct test — a `Content-Length: 0` is a 2616 * declared empty body, not the absence of one. 2617 * 2618 * @param con MHD connection to look the headers up on 2619 * @param[out] len set to the declared length for #CLIENT_BODY_LENGTH 2620 * @return how the body is framed 2621 */ 2622 static enum ClientBodyKind 2623 client_body_kind (struct MHD_Connection *con, 2624 uint64_t *len) 2625 { 2626 const char *cl_str; 2627 char *endptr; 2628 unsigned long long cl; 2629 2630 *len = 0; 2631 cl_str = MHD_lookup_connection_value (con, 2632 MHD_HEADER_KIND, 2633 MHD_HTTP_HEADER_CONTENT_LENGTH); 2634 if (NULL == cl_str) 2635 { 2636 if (NULL != MHD_lookup_connection_value (con, 2637 MHD_HEADER_KIND, 2638 MHD_HTTP_HEADER_TRANSFER_ENCODING)) 2639 return CLIENT_BODY_CHUNKED; 2640 return CLIENT_BODY_NONE; 2641 } 2642 /* RFC 9110 §8.6: Content-Length = 1*DIGIT. strtoull() is more 2643 generous than that -- it skips leading whitespace and accepts a 2644 sign, so " +5" and even "-1" parse -- and the sign is the one that 2645 matters here, since strtoull() maps a negative value onto a huge 2646 unsigned one. Check the syntax ourselves before believing the 2647 number. */ 2648 if ('\0' == *cl_str) 2649 return CLIENT_BODY_BAD; 2650 for (const char *p = cl_str; '\0' != *p; p++) 2651 if ( ('0' > *p) || 2652 ('9' < *p) ) 2653 return CLIENT_BODY_BAD; 2654 errno = 0; 2655 cl = strtoull (cl_str, 2656 &endptr, 2657 10); 2658 if (endptr == cl_str) 2659 return CLIENT_BODY_BAD; 2660 if (ERANGE == errno) 2661 { 2662 /* All digits, but beyond ULLONG_MAX: larger than any limit we 2663 could be configured with, so it is a rejection rather than a 2664 parse failure. */ 2665 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2666 "Rejecting upload: Content-Length `%s' is out of range\n", 2667 cl_str); 2668 *len = UINT64_MAX; 2669 return CLIENT_BODY_LENGTH; 2670 } 2671 if (0 == cl) 2672 return CLIENT_BODY_NONE; 2673 *len = (uint64_t) cl; 2674 return CLIENT_BODY_LENGTH; 2675 } 2676 2677 2678 /** 2679 * Take as much of an upload chunk from the client as @e up_ring will 2680 * hold, and enforce #PH_max_request_size on the way. 2681 * 2682 * Unlike libcurl's write callback, MHD's access handler may take part 2683 * of what it is given: it panics only if the handler claims *more* 2684 * than it was handed, and warns only when the handler takes nothing 2685 * and does not suspend (`connection.c' 4774-4792). So the ring-full 2686 * case has to suspend, and does. 2687 * 2688 * @param[in,out] hr request we are handling 2689 * @param[in,out] upload_data_size in: bytes offered, out: bytes left 2690 * @param upload_data the bytes 2691 * @return true if the upload may continue, false if it is now over 2692 * #PH_max_request_size and the 413 path has to take over 2693 */ 2694 static bool 2695 relay_upload_chunk (struct HttpRequest *hr, 2696 size_t *upload_data_size, 2697 const char *upload_data) 2698 { 2699 size_t n; 2700 2701 if (hr->up_received + (uint64_t) *upload_data_size > PH_max_request_size) 2702 { 2703 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2704 "Rejecting upload: body exceeds the %llu byte limit\n", 2705 (unsigned long long) PH_max_request_size); 2706 return false; 2707 } 2708 n = ring_put (&hr->up_ring, 2709 upload_data, 2710 *upload_data_size, 2711 false); 2712 hr->up_received += n; 2713 *upload_data_size -= n; 2714 if (0 != n) 2715 { 2716 note_progress (hr); 2717 /* The origin may have run out of body to send; tell it there is 2718 more. */ 2719 schedule_send_cont (hr); 2720 } 2721 return true; 2722 } 2723 2724 2725 /** 2726 * Arrange for a request body to be forwarded under @a meth, and set 2727 * @a hr->up_state to match. 2728 * 2729 * For the methods whose curl option is otherwise bodyless 2730 * (CURLOPT_HTTPGET, CURLOPT_NOBODY, or a bare CURLOPT_CUSTOMREQUEST), 2731 * turning on CURLOPT_POST is what enables the read callback; 2732 * CURLOPT_CUSTOMREQUEST then restores the verb on the wire. This is 2733 * the same shape DELETE has always used. With no body announced 2734 * nothing is changed, so the common case still goes out as a plain 2735 * GET. 2736 * 2737 * @param[in,out] hr the request 2738 * @param meth HTTP method to keep on the wire 2739 * @param kind how the client framed its body 2740 */ 2741 static void 2742 forward_body_verbatim (struct HttpRequest *hr, 2743 const char *meth, 2744 enum ClientBodyKind kind) 2745 { 2746 if (CLIENT_BODY_NONE == kind) 2747 { 2748 hr->up_state = UP_DONE; 2749 return; 2750 } 2751 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2752 "Forwarding a request body with %s\n", 2753 meth); 2754 PH_SETOPT (hr->curl, 2755 CURLOPT_POST, 2756 1L); 2757 PH_SETOPT (hr->curl, 2758 CURLOPT_CUSTOMREQUEST, 2759 meth); 2760 hr->up_state = UP_STREAMING; 2761 } 2762 2763 2764 /** 2765 * Choose the curl options for the HTTP method we're proxying and set 2766 * @a hr->up_state accordingly. Queues an error response and returns 2767 * the corresponding MHD_Result for unsupported methods; otherwise 2768 * returns #MHD_YES with @a hr->up_state at #UP_STREAMING (there is a 2769 * body to relay) or #UP_DONE (there is not). 2770 * 2771 * On error, the "curl" handle is set to NULL (!). 2772 * 2773 * @param[in,out] hr the request 2774 * @param con client connection handle from MHD 2775 * @param meth HTTP method specified by the client 2776 * @param kind how the client framed its request body 2777 * @return MHD status to return 2778 */ 2779 static enum MHD_Result 2780 configure_curl_method (struct HttpRequest *hr, 2781 struct MHD_Connection *con, 2782 const char *meth, 2783 enum ClientBodyKind kind) 2784 { 2785 if (0 == strcasecmp (meth, 2786 MHD_HTTP_METHOD_GET)) 2787 { 2788 PH_SETOPT (hr->curl, 2789 CURLOPT_HTTPGET, 2790 1L); 2791 /* A GET body is unusual and its semantics are undefined (RFC 9110 2792 §9.3.1), but it is not forbidden and real APIs use it -- 2793 GraphQL-over-GET, several search endpoints. Dropping it while 2794 `con_val_iter()' still forwards the client's Content-Type left 2795 the origin with a request advertising content that never 2796 arrived, and the client with a parse error it could not 2797 explain. */ 2798 forward_body_verbatim (hr, 2799 MHD_HTTP_METHOD_GET, 2800 kind); 2801 return MHD_YES; 2802 } 2803 if (0 == strcasecmp (meth, 2804 MHD_HTTP_METHOD_POST)) 2805 { 2806 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2807 "Crafting a CURL POST request\n"); 2808 PH_SETOPT (hr->curl, 2809 CURLOPT_POST, 2810 1L); 2811 hr->up_state = UP_STREAMING; 2812 return MHD_YES; 2813 } 2814 if (0 == strcasecmp (meth, 2815 MHD_HTTP_METHOD_HEAD)) 2816 { 2817 /* The upstream still states the length the equivalent GET would 2818 have had; nothing follows it (RFC 9110 §9.3.2). */ 2819 hr->head_request = true; 2820 PH_SETOPT (hr->curl, 2821 CURLOPT_NOBODY, 2822 1L); 2823 /* HEAD must be forwarded with whatever body the equivalent GET 2824 carried, or the origin cannot answer the question HEAD asks. */ 2825 forward_body_verbatim (hr, 2826 MHD_HTTP_METHOD_HEAD, 2827 kind); 2828 return MHD_YES; 2829 } 2830 if (0 == strcasecmp (meth, 2831 MHD_HTTP_METHOD_PUT)) 2832 { 2833 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2834 "Crafting a CURL PUT request\n"); 2835 PH_SETOPT (hr->curl, 2836 CURLOPT_UPLOAD, 2837 1L); 2838 hr->up_state = UP_STREAMING; 2839 return MHD_YES; 2840 } 2841 if (0 == strcasecmp (meth, 2842 MHD_HTTP_METHOD_DELETE)) 2843 { 2844 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2845 "Crafting a CURL DELETE request\n"); 2846 PH_SETOPT (hr->curl, 2847 CURLOPT_CUSTOMREQUEST, 2848 "DELETE"); 2849 if (CLIENT_BODY_NONE != kind) 2850 { 2851 /* DELETE with a request body is unusual but legal. */ 2852 PH_SETOPT (hr->curl, 2853 CURLOPT_POST, 2854 1L); 2855 hr->up_state = UP_STREAMING; 2856 } 2857 else 2858 { 2859 hr->up_state = UP_DONE; 2860 } 2861 return MHD_YES; 2862 } 2863 if (0 == strcasecmp (meth, 2864 MHD_HTTP_METHOD_PATCH)) 2865 { 2866 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2867 "Crafting a CURL PATCH request\n"); 2868 /* CURLOPT_POST=1 turns on body upload via the read callback; 2869 CURLOPT_CUSTOMREQUEST then overrides the verb on the wire. */ 2870 PH_SETOPT (hr->curl, 2871 CURLOPT_POST, 2872 1L); 2873 PH_SETOPT (hr->curl, 2874 CURLOPT_CUSTOMREQUEST, 2875 "PATCH"); 2876 hr->up_state = UP_STREAMING; 2877 return MHD_YES; 2878 } 2879 if (0 == strcasecmp (meth, 2880 MHD_HTTP_METHOD_OPTIONS)) 2881 { 2882 const char *mf; 2883 2884 PH_SETOPT (hr->curl, 2885 CURLOPT_CUSTOMREQUEST, 2886 "OPTIONS"); 2887 /* RFC 9110 §7.6.2: "Each intermediary that receives a TRACE or 2888 OPTIONS request containing a Max-Forwards header field MUST 2889 check and update its value prior to forwarding ... If the 2890 received value is zero (0), the intermediary MUST NOT forward 2891 the request; instead, the intermediary MUST respond as the final 2892 recipient." TRACE we reject outright, so OPTIONS is the only 2893 method this can reach. */ 2894 mf = MHD_lookup_connection_value (con, 2895 MHD_HEADER_KIND, 2896 MHD_HTTP_HEADER_MAX_FORWARDS); 2897 if (NULL != mf) 2898 { 2899 char *endptr; 2900 unsigned long long hops; 2901 2902 errno = 0; 2903 hops = strtoull (mf, 2904 &endptr, 2905 10); 2906 if ( (0 != errno) || 2907 (endptr == mf) || 2908 ('\0' != *endptr) ) 2909 { 2910 /* Not a valid Max-Forwards. Neither forwarding it nor acting 2911 as the final recipient is defined for that, so treat it as 2912 the client's error rather than guessing. */ 2913 GNUNET_break_op (0); 2914 curl_easy_cleanup (hr->curl); 2915 hr->curl = NULL; 2916 return MHD_queue_response (con, 2917 MHD_HTTP_BAD_REQUEST, 2918 method_failure_response); 2919 } 2920 if (0 == hops) 2921 { 2922 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2923 "Answering OPTIONS as the final recipient:" 2924 " Max-Forwards is exhausted\n"); 2925 curl_easy_cleanup (hr->curl); 2926 hr->curl = NULL; 2927 return MHD_queue_response (con, 2928 MHD_HTTP_OK, 2929 options_response); 2930 } 2931 GNUNET_asprintf (&hr->max_forwards, 2932 "%s: %llu", 2933 MHD_HTTP_HEADER_MAX_FORWARDS, 2934 hops - 1); 2935 } 2936 forward_body_verbatim (hr, 2937 MHD_HTTP_METHOD_OPTIONS, 2938 kind); 2939 return MHD_YES; 2940 } 2941 /* TRACE leaks headers back to the client; CONNECT is for TLS 2942 tunnelling and doesn't fit the reverse-proxy model. Reject 2943 anything else with a proper 405. */ 2944 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2945 "Unsupported HTTP method `%s'\n", 2946 meth); 2947 curl_easy_cleanup (hr->curl); 2948 hr->curl = NULL; 2949 return MHD_queue_response (con, 2950 MHD_HTTP_METHOD_NOT_ALLOWED, 2951 method_failure_response); 2952 } 2953 2954 2955 /** 2956 * Format the address of the peer we accepted @a con from, or return 2957 * NULL if it has none to speak of. 2958 * 2959 * A Unix-domain peer deliberately yields NULL: there is no address 2960 * to name, and inventing one ("127.0.0.1") would be both unverifiable 2961 * and indistinguishable from a genuine loopback client. The hop is 2962 * still recorded — in `Via`, which does not require an address. 2963 * 2964 * @param con MHD connection we are processing 2965 * @param[out] buf scratch space to format into 2966 * @param buf_size number of bytes in @a buf 2967 * @return @a buf, or NULL if the peer has no IP address 2968 */ 2969 static const char * 2970 peer_address (struct MHD_Connection *con, 2971 char *buf, 2972 size_t buf_size) 2973 { 2974 const union MHD_ConnectionInfo *ci; 2975 2976 ci = MHD_get_connection_info (con, 2977 MHD_CONNECTION_INFO_CLIENT_ADDRESS); 2978 if ( (NULL == ci) || 2979 (NULL == ci->client_addr) ) 2980 return NULL; 2981 switch (ci->client_addr->sa_family) 2982 { 2983 case AF_INET: 2984 return inet_ntop ( 2985 AF_INET, 2986 &((const struct sockaddr_in *) ci->client_addr)->sin_addr, 2987 buf, 2988 buf_size); 2989 case AF_INET6: 2990 return inet_ntop ( 2991 AF_INET6, 2992 &((const struct sockaddr_in6 *) ci->client_addr)->sin6_addr, 2993 buf, 2994 buf_size); 2995 default: 2996 /* AF_UNIX: see above. */ 2997 return NULL; 2998 } 2999 } 3000 3001 3002 /** 3003 * Determine the scheme the *client* used to reach us, for 3004 * `X-Forwarded-Proto`. 3005 * 3006 * Without #PH_respect_forwarded_headers this is a property of our own 3007 * listener and nothing else: #TALER_mhd_is_https() is documented as 3008 * reporting https "either directly or via proxy", i.e. it also 3009 * believes an inbound `X-Forwarded-Proto`, which an untrusted client 3010 * can simply assert. So when we are not behind a trusted proxy we 3011 * ask MHD about the transport instead — a TLS session exists or it 3012 * does not. 3013 * 3014 * @param hr request we are handling, for the captured inbound headers 3015 * @param con MHD connection we are processing 3016 * @param[out] owned set to an allocated string the caller must free, 3017 * or NULL if the returned value is a literal or borrowed 3018 * @return "https" or "http" 3019 */ 3020 static const char * 3021 forwarded_proto (const struct HttpRequest *hr, 3022 struct MHD_Connection *con, 3023 char **owned) 3024 { 3025 *owned = NULL; 3026 if (PH_respect_forwarded_headers) 3027 { 3028 if (NULL != hr->client_xfp) 3029 return hr->client_xfp; 3030 /* A proxy that speaks only RFC 7239 states the scheme there and 3031 nowhere else; ignoring it would tell the origin "http" about a 3032 request the client made over TLS. */ 3033 if (NULL != hr->client_forwarded) 3034 { 3035 *owned = PAIVANA_HTTPD_forwarded_param (hr->client_forwarded, 3036 "proto"); 3037 if (NULL != *owned) 3038 return *owned; 3039 } 3040 return (GNUNET_YES == TALER_mhd_is_https (con)) 3041 ? "https" : "http"; 3042 } 3043 return (NULL != MHD_get_connection_info (con, 3044 MHD_CONNECTION_INFO_PROTOCOL)) 3045 ? "https" : "http"; 3046 } 3047 3048 3049 /** 3050 * Attach the reverse-proxy forwarding headers (X-Forwarded-For / 3051 * -Proto / -Host and Via) to `hr->headers`. 3052 * 3053 * Which of the two roles paivana plays here is decided by 3054 * #PH_respect_forwarded_headers, the same flag that decides where the 3055 * access cookie's client address comes from — the two must agree, or 3056 * the origin is told one thing about a request while we authenticated 3057 * another: 3058 * 3059 * - Without the flag we are the outermost proxy. Any inbound 3060 * `X-Forwarded-*` is a client assertion and is replaced with what we 3061 * can see for ourselves, so that a client cannot dictate what the 3062 * origin believes about it. 3063 * - With the flag we are behind a trusted proxy, and the values it 3064 * sent are the truthful ones. We *extend* the chain with the peer 3065 * we accepted from rather than discarding it, which is the whole 3066 * point of the header (and what RFC 9110 §7.6.3 requires of `Via` 3067 * in any case). 3068 * 3069 * `Forwarded` (RFC 7239) is treated exactly like `X-Forwarded-For`, 3070 * and both are emitted: the standardized header for origins that 3071 * speak it, the de-facto one for the many that only speak that. 3072 * 3073 * @param[in,out] hr the request 3074 * @param con MHD connection we are processing 3075 * @param ver HTTP version of the client, as given by MHD 3076 */ 3077 static void 3078 append_forwarded_headers (struct HttpRequest *hr, 3079 struct MHD_Connection *con, 3080 const char *ver) 3081 { 3082 char ipbuf[INET6_ADDRSTRLEN]; 3083 char *hdr; 3084 char *owned_proto; 3085 char *owned_host = NULL; 3086 char *owned_xff = NULL; 3087 const char *xff; 3088 const char *peer; 3089 const char *proto; 3090 const char *fhost = NULL; 3091 const char *via_ver = "1.1"; 3092 3093 peer = peer_address (con, 3094 ipbuf, 3095 sizeof (ipbuf)); 3096 xff = hr->client_xff; 3097 if ( (PH_respect_forwarded_headers) && 3098 (NULL == xff) && 3099 (NULL != hr->client_forwarded) ) 3100 { 3101 /* A proxy that speaks only RFC 7239 still has to be understood by 3102 an origin that speaks only X-Forwarded-For. */ 3103 owned_xff = PAIVANA_HTTPD_forwarded_for_chain (hr->client_forwarded); 3104 xff = owned_xff; 3105 } 3106 if ( (PH_respect_forwarded_headers) && 3107 (NULL != xff) ) 3108 { 3109 /* Extend the trusted chain. If our own peer has no address 3110 (Unix socket) the chain is passed on as it stands. */ 3111 if (NULL != peer) 3112 GNUNET_asprintf (&hdr, 3113 "%s: %s, %s", 3114 PH_HEADER_X_FORWARDED_FOR, 3115 xff, 3116 peer); 3117 else 3118 GNUNET_asprintf (&hdr, 3119 "%s: %s", 3120 PH_HEADER_X_FORWARDED_FOR, 3121 xff); 3122 append_curl_header (hr, 3123 hdr); 3124 GNUNET_free (hdr); 3125 } 3126 else if (NULL != peer) 3127 { 3128 GNUNET_asprintf (&hdr, 3129 "%s: %s", 3130 PH_HEADER_X_FORWARDED_FOR, 3131 peer); 3132 append_curl_header (hr, 3133 hdr); 3134 GNUNET_free (hdr); 3135 } 3136 if (NULL != hr->max_forwards) 3137 append_curl_header (hr, 3138 hr->max_forwards); 3139 proto = forwarded_proto (hr, 3140 con, 3141 &owned_proto); 3142 GNUNET_asprintf (&hdr, 3143 "%s: %s", 3144 PH_HEADER_X_FORWARDED_PROTO, 3145 proto); 3146 append_curl_header (hr, 3147 hdr); 3148 GNUNET_free (hdr); 3149 if (PH_respect_forwarded_headers) 3150 { 3151 fhost = hr->client_xfh; 3152 if ( (NULL == fhost) && 3153 (NULL != hr->client_forwarded) ) 3154 { 3155 owned_host = PAIVANA_HTTPD_forwarded_param (hr->client_forwarded, 3156 "host"); 3157 fhost = owned_host; 3158 } 3159 } 3160 if (NULL == fhost) 3161 fhost = MHD_lookup_connection_value (con, 3162 MHD_HEADER_KIND, 3163 MHD_HTTP_HEADER_HOST); 3164 if (NULL != fhost) 3165 { 3166 GNUNET_asprintf (&hdr, 3167 "%s: %s", 3168 PH_HEADER_X_FORWARDED_HOST, 3169 fhost); 3170 append_curl_header (hr, 3171 hdr); 3172 GNUNET_free (hdr); 3173 } 3174 { 3175 /* `con_val_iter' drops every X-Forwarded-* on the way in, so 3176 whatever the front end told us about the port has to be put back 3177 here or the origin never learns it. The documented nginx recipe 3178 sends `X-Forwarded-Port', and `$host' in `X-Forwarded-Host' 3179 omits the port -- so without this an origin behind 3180 https://example.com:8443 reconstructs https://example.com/ and 3181 every absolute URL it generates points at port 443. */ 3182 const char *fport = NULL; 3183 3184 if (PH_respect_forwarded_headers) 3185 fport = hr->client_xfport; 3186 if ( (NULL != fport) && 3187 (! valid_port_text (fport)) ) 3188 { 3189 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3190 "Ignoring unusable %s value `%s'\n", 3191 PH_HEADER_X_FORWARDED_PORT, 3192 fport); 3193 fport = NULL; 3194 } 3195 if ( (NULL == fport) && 3196 (NULL != fhost) ) 3197 fport = host_port_suffix (fhost); 3198 if (NULL != fport) 3199 { 3200 GNUNET_asprintf (&hdr, 3201 "%s: %s", 3202 PH_HEADER_X_FORWARDED_PORT, 3203 fport); 3204 append_curl_header (hr, 3205 hdr); 3206 GNUNET_free (hdr); 3207 } 3208 } 3209 /* RFC 7239. The element we add describes the hop we are completing: 3210 `for` is the peer we accepted from, `by` the interface we accepted 3211 on, and `proto`/`host` what the client used. A Unix-domain peer 3212 has no address, which §6.3 spells "unknown" -- unlike 3213 X-Forwarded-For, this header has somewhere to put that, so the hop 3214 need not go unrecorded. */ 3215 { 3216 char *elem; 3217 char *node; 3218 char *qproto; 3219 char *qhost = NULL; 3220 void *ca = NULL; 3221 size_t ca_len = 0; 3222 3223 if (NULL != peer) 3224 { 3225 struct in_addr a4; 3226 struct in6_addr a6; 3227 3228 if (1 == inet_pton (AF_INET, 3229 peer, 3230 &a4)) 3231 { 3232 ca = &a4; 3233 ca_len = sizeof (a4); 3234 } 3235 else if (1 == inet_pton (AF_INET6, 3236 peer, 3237 &a6)) 3238 { 3239 ca = &a6; 3240 ca_len = sizeof (a6); 3241 } 3242 node = PAIVANA_HTTPD_forwarded_node (ca, 3243 ca_len); 3244 } 3245 else 3246 { 3247 node = PAIVANA_HTTPD_forwarded_node (NULL, 3248 0); 3249 } 3250 /* `proto` and `host` may both be raw client input: without -f they 3251 come from the Host header, with it from X-Forwarded-Proto / 3252 X-Forwarded-Host, neither of which we validated. Splicing them 3253 in as-is would let a ';' in either open a parameter of ours, or 3254 a ',' an element of ours, in a header the origin reads as 3255 paivana's own statement about the hop -- so render them as an 3256 RFC 7239 section 4 `value` (token, or escaped quoted-string). 3257 PAIVANA_HTTPD_forwarded_value() returns NULL only for a control 3258 character, which no `value` production can carry at all; drop 3259 the parameter rather than emit something unparseable. */ 3260 qproto = PAIVANA_HTTPD_forwarded_value (proto); 3261 if (NULL != fhost) 3262 qhost = PAIVANA_HTTPD_forwarded_value (fhost); 3263 if (NULL == qproto) 3264 GNUNET_asprintf (&elem, 3265 "for=%s;by=_paivana", 3266 node); 3267 else if (NULL != qhost) 3268 GNUNET_asprintf (&elem, 3269 "for=%s;by=_paivana;proto=%s;host=%s", 3270 node, 3271 qproto, 3272 qhost); 3273 else 3274 GNUNET_asprintf (&elem, 3275 "for=%s;by=_paivana;proto=%s", 3276 node, 3277 qproto); 3278 GNUNET_free (qproto); 3279 GNUNET_free (qhost); 3280 GNUNET_free (node); 3281 if ( (PH_respect_forwarded_headers) && 3282 (NULL != hr->client_forwarded) ) 3283 GNUNET_asprintf (&hdr, 3284 "%s: %s, %s", 3285 MHD_HTTP_HEADER_FORWARDED, 3286 hr->client_forwarded, 3287 elem); 3288 else 3289 GNUNET_asprintf (&hdr, 3290 "%s: %s", 3291 MHD_HTTP_HEADER_FORWARDED, 3292 elem); 3293 GNUNET_free (elem); 3294 append_curl_header (hr, 3295 hdr); 3296 GNUNET_free (hdr); 3297 } 3298 /* MHD hands us e.g. "HTTP/1.1" but Via wants just "1.1". */ 3299 if ( (NULL != ver) && 3300 (0 == strncasecmp (ver, 3301 "HTTP/", 3302 strlen ("HTTP/"))) ) 3303 via_ver = ver + 5; 3304 if (NULL != hr->client_via) 3305 GNUNET_asprintf (&hdr, 3306 "%s: %s, %s paivana", 3307 MHD_HTTP_HEADER_VIA, 3308 hr->client_via, 3309 via_ver); 3310 else 3311 GNUNET_asprintf (&hdr, 3312 "%s: %s paivana", 3313 MHD_HTTP_HEADER_VIA, 3314 via_ver); 3315 append_curl_header (hr, 3316 hdr); 3317 GNUNET_free (hdr); 3318 GNUNET_free (owned_proto); 3319 GNUNET_free (owned_host); 3320 GNUNET_free (owned_xff); 3321 } 3322 3323 3324 /** 3325 * Initialize the curl handle, attach the forwarding headers, and hand 3326 * the request off to the curl multi loop. On success sets 3327 * @a hr->up_state to #UP_STREAMING or #UP_DONE and returns #MHD_YES. 3328 * On any failure queues an appropriate error response and returns its 3329 * MHD_Result. 3330 * 3331 * Called before the client's body has arrived -- that is the point of 3332 * streaming it -- so what the body will be is read out of the client's 3333 * own framing rather than out of a buffer. 3334 * 3335 * On error, the "curl" handle is set to NULL (!). 3336 * 3337 * @param[in,out] hr request we are handling 3338 * @param con MHD connection handle 3339 * @param meth HTTP method of the request 3340 * @param ver HTTP version to use 3341 * @param kind how the client framed its request body 3342 * @param body_len declared body length, for #CLIENT_BODY_LENGTH 3343 * @return MHD status code to return 3344 */ 3345 static enum MHD_Result 3346 start_curl_request (struct HttpRequest *hr, 3347 struct MHD_Connection *con, 3348 const char *meth, 3349 const char *ver, 3350 enum ClientBodyKind kind, 3351 uint64_t body_len) 3352 { 3353 enum MHD_Result r; 3354 3355 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3356 "Generating curl request\n"); 3357 hr->curl = curl_easy_init (); 3358 if (NULL == hr->curl) 3359 { 3360 PAIVANA_LOG_ERROR ("Could not init the curl handle\n"); 3361 return MHD_queue_response (con, 3362 MHD_HTTP_INTERNAL_SERVER_ERROR, 3363 internal_failure_response); 3364 } 3365 3366 /* The client's own framing is reproduced upstream: a declared length 3367 stays a declared length, and a chunked body stays chunked (libcurl 3368 chunks for us when the size is left unknown). Both options are 3369 set because which of the two libcurl reads depends on whether 3370 `configure_curl_method()' chose CURLOPT_POST or CURLOPT_UPLOAD. 3371 3372 The casts are load-bearing, not decoration. Both options are 3373 CURLOPTTYPE_OFF_T (<curl/curl.h>), so libcurl reads them with 3374 va_arg(curl_off_t), and curl_off_t is 64 bits on every platform 3375 (<curl/system.h>: "For any given platform/compiler curl_off_t MUST 3376 be typedef'ed to a 64-bit wide signed integral data type"). 3377 Passing a size_t is therefore only correct where size_t is 64 bits 3378 too: on i386 or armv7 libcurl would take four bytes of the length 3379 and four bytes of whatever the caller left on the stack next to 3380 them as the high half, and declare a request body of some absurd 3381 length that never arrives. */ 3382 if (CLIENT_BODY_LENGTH == kind) 3383 { 3384 PH_SETOPT (hr->curl, 3385 CURLOPT_POSTFIELDSIZE_LARGE, 3386 (curl_off_t) body_len); 3387 PH_SETOPT (hr->curl, 3388 CURLOPT_INFILESIZE_LARGE, 3389 (curl_off_t) body_len); 3390 } 3391 else 3392 { 3393 PH_SETOPT (hr->curl, 3394 CURLOPT_POSTFIELDSIZE_LARGE, 3395 (curl_off_t) -1); 3396 PH_SETOPT (hr->curl, 3397 CURLOPT_INFILESIZE_LARGE, 3398 (curl_off_t) -1); 3399 } 3400 PH_SETOPT (hr->curl, 3401 CURLOPT_FOLLOWLOCATION, 3402 0L); /* CURLOPTTYPE_LONG: read with va_arg(long) */ 3403 PH_SETOPT (hr->curl, 3404 CURLOPT_CONNECTTIMEOUT, 3405 PH_CURL_CONNECT_TIMEOUT_S); 3406 /* Deliberately no CURLOPT_TIMEOUT and no CURLOPT_LOW_SPEED_LIMIT / 3407 _TIME. The first is a deadline on the whole transfer, and a 3408 500 MiB download legitimately outlives any value that would be 3409 useful for anything else. The second looks like exactly the 3410 watchdog we want and is not; see `stall_check()' for the measured 3411 reason, which is that it would kill a slow client's own upload. */ 3412 PH_SETOPT (hr->curl, 3413 CURLOPT_NOSIGNAL, 3414 1L); 3415 /* No CURLOPT_PRIVATE here: GNUNET_CURL_job_add_stream() sets it to 3416 its own `struct GNUNET_CURL_Job *' and reads it back with 3417 GNUNET_assert() on the result, so anything we stored would be 3418 overwritten before the transfer starts -- and would abort the 3419 daemon if it were not. Nor CURLOPT_WRITEFUNCTION or 3420 CURLOPT_HEADERFUNCTION, which the streaming job installs its own 3421 trampolines into; CURLOPT_READFUNCTION it leaves to us. */ 3422 PH_SETOPT (hr->curl, 3423 CURLOPT_VERBOSE, 3424 0L); /* CURLOPTTYPE_LONG: read with va_arg(long) */ 3425 PH_SETOPT (hr->curl, 3426 CURLOPT_READFUNCTION, 3427 &curl_upload_cb); 3428 PH_SETOPT (hr->curl, 3429 CURLOPT_READDATA, 3430 hr); 3431 { 3432 char *curlurl; 3433 3434 GNUNET_asprintf (&curlurl, 3435 "%s%s", 3436 PH_target_server_base_url, 3437 hr->url); 3438 PH_SETOPT (hr->curl, 3439 CURLOPT_URL, 3440 curlurl); 3441 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3442 "Forwarding request to: %s\n", 3443 curlurl); 3444 GNUNET_free (curlurl); 3445 } 3446 3447 if (NULL != PH_target_server_unixpath) 3448 { 3449 PH_SETOPT (hr->curl, 3450 CURLOPT_UNIX_SOCKET_PATH, 3451 PH_target_server_unixpath); 3452 3453 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3454 "Forwarding using unixpath: %s\n", 3455 PH_target_server_unixpath); 3456 } 3457 3458 { 3459 char *host_hdr; 3460 3461 host_hdr = build_host_header (PH_target_server_base_url); 3462 if (NULL != host_hdr) 3463 { 3464 PAIVANA_LOG_DEBUG ("Faking the host header, %s\n", 3465 host_hdr); 3466 append_curl_header (hr, 3467 host_hdr); 3468 GNUNET_free (host_hdr); 3469 } 3470 } 3471 3472 r = configure_curl_method (hr, 3473 con, 3474 meth, 3475 kind); 3476 if (NULL == hr->curl) 3477 return r; /* unsupported method: response already queued */ 3478 /* First pass: collect Via / Connection so `con_val_iter` can 3479 honor them (append to Via; drop headers named by Connection). */ 3480 MHD_get_connection_values (con, 3481 MHD_HEADER_KIND, 3482 &collect_proxy_state, 3483 hr); 3484 MHD_get_connection_values (con, 3485 MHD_HEADER_KIND, 3486 &con_val_iter, 3487 hr); 3488 append_forwarded_headers (hr, 3489 con, 3490 ver); 3491 { 3492 struct GNUNET_CURL_StreamHandlers sh = { 3493 .hcb = &curl_check_hdr, 3494 .hcb_cls = hr, 3495 .scb = &stream_body_cb, 3496 .scb_cls = hr, 3497 .jcc = &curl_done_cb, 3498 .jcc_cls = hr 3499 }; 3500 3501 hr->job = GNUNET_CURL_job_add_stream (PH_proxy_ctx, 3502 hr->curl, 3503 hr->headers, 3504 &sh); 3505 } 3506 hr->curl = NULL; 3507 if (NULL == hr->job) 3508 { 3509 GNUNET_break (0); 3510 return MHD_queue_response (con, 3511 MHD_HTTP_BAD_GATEWAY, 3512 curl_failure_response); 3513 } 3514 hr->last_progress = GNUNET_TIME_absolute_get (); 3515 hr->timeout_task = GNUNET_SCHEDULER_add_delayed (PH_upstream_timeout, 3516 &upstream_timeout, 3517 hr); 3518 return MHD_YES; 3519 } 3520 3521 3522 /** 3523 * Build and queue the streaming response for @a hr. 3524 * 3525 * Reached once the origin's header section has ended *and* the 3526 * client's request body is out of the way; MHD will not accept a 3527 * response before the latter. The body itself has not arrived and 3528 * need not: `body_reader()' supplies it as it does. 3529 * 3530 * The size handed to MHD is what decides the framing the client sees, 3531 * and it is the origin's own: a declared `Content-Length` is relayed 3532 * as one, and an unknown length becomes chunked for an HTTP/1.1 client 3533 * and connection-delimited for an HTTP/1.0 one. Both are what the 3534 * origin chose, which is a conformance gain over recomputing a length 3535 * from an assembled buffer. It is also what makes a HEAD or a 304 3536 * report the length of the body it does not send: MHD skips the 3537 * content reader for those but still emits the size (RFC 9110 §9.3.2, 3538 * §8.6). 3539 * 3540 * @param[in,out] hr request whose response to queue 3541 * @param con MHD client connection to send the response on 3542 * @return the #MHD_Result of queuing it 3543 */ 3544 static enum MHD_Result 3545 queue_stream_response (struct HttpRequest *hr, 3546 struct MHD_Connection *con) 3547 { 3548 uint64_t size = MHD_SIZE_UNKNOWN; 3549 3550 if (PH_NO_CONTENT_LENGTH != hr->upstream_content_length) 3551 size = hr->upstream_content_length; 3552 hr->response = MHD_create_response_from_callback (size, 3553 PH_STREAM_BLOCK_SIZE, 3554 &body_reader, 3555 hr, 3556 &body_reader_free); 3557 if (NULL == hr->response) 3558 { 3559 GNUNET_break (0); 3560 hr->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 3561 hr->response = internal_failure_response; 3562 return MHD_queue_response (con, 3563 hr->response_code, 3564 hr->response); 3565 } 3566 hr->own_response = true; 3567 /* From here MHD holds a closure that outlives its completion 3568 notifier; see @e mhd_gone. */ 3569 hr->reader_gone = false; 3570 /* RFC 9110 §7.6.1: drop the headers named by the upstream's 3571 Connection list. Deferred to here (rather than done in 3572 `curl_check_hdr()') because Connection may arrive after the 3573 headers it names. */ 3574 { 3575 struct HttpResponseHeader *nxt; 3576 3577 for (struct HttpResponseHeader *header = hr->header_head; 3578 NULL != header; 3579 header = nxt) 3580 { 3581 nxt = header->next; 3582 if (! connection_lists_header (hr->upstream_connection, 3583 header->type)) 3584 continue; 3585 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3586 "Dropping connection-specific response header %s\n", 3587 header->type); 3588 GNUNET_CONTAINER_DLL_remove (hr->header_head, 3589 hr->header_tail, 3590 header); 3591 GNUNET_free (header->type); 3592 GNUNET_free (header->value); 3593 GNUNET_free (header); 3594 } 3595 } 3596 for (struct HttpResponseHeader *header = hr->header_head; 3597 NULL != header; 3598 header = header->next) 3599 { 3600 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3601 "Adding MHD response header %s->%s\n", 3602 header->type, 3603 header->value); 3604 if (MHD_YES != 3605 MHD_add_response_header (hr->response, 3606 header->type, 3607 header->value)) 3608 { 3609 /* MHD validates the field name (RFC 9112 §5 token) and the 3610 value; a malformed one is the upstream's fault, not ours, so 3611 GNUNET_break_op and not GNUNET_break -- the latter let an 3612 eccentric origin fill the log with "Assertion failed". */ 3613 GNUNET_break_op (0); 3614 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3615 "Upstream `%s' sent a header MHD rejected: `%s'\n", 3616 hr->url, 3617 header->type); 3618 } 3619 } 3620 return MHD_queue_response (con, 3621 hr->response_code, 3622 hr->response); 3623 } 3624 3625 3626 /** 3627 * Main MHD callback for reverse proxy. 3628 * 3629 * Two independent state machines, not one: since the origin request 3630 * now starts before the client's body has arrived, the two directions 3631 * run at the same time and neither is a phase of the other. This 3632 * function owns the upload half; `stream_body_cb()' and 3633 * `body_reader()' own the download half between them, and meet here 3634 * only to decide when the response may be queued. 3635 * 3636 * @param hr the HTTP request context 3637 * @param con MHD connection handle 3638 * @param url the url in the request (unused; kept for ABI symmetry 3639 * with the MHD access handler signature) 3640 * @param meth the HTTP method used ("GET", "PUT", etc.) 3641 * @param ver the HTTP version string (i.e. "HTTP/1.1") 3642 * @param upload_data the data being uploaded (excluding HEADERS) 3643 * @param upload_data_size set initially to the size of the 3644 * @a upload_data provided; the method must update this 3645 * value to the number of bytes NOT processed; 3646 * @return #MHD_YES if the connection was handled successfully, 3647 * #MHD_NO if the socket must be closed due to a serious 3648 * error while handling the request 3649 */ 3650 enum MHD_Result 3651 PAIVANA_HTTPD_reverse (struct HttpRequest *hr, 3652 struct MHD_Connection *con, 3653 const char *url, 3654 const char *meth, 3655 const char *ver, 3656 const char *upload_data, 3657 size_t *upload_data_size) 3658 { 3659 (void) url; 3660 3661 if (UP_HEADERS_PENDING == hr->up_state) 3662 { 3663 enum ClientBodyKind kind; 3664 uint64_t body_len; 3665 3666 /* MHD's HEADERS_PROCESSED callback. This is our only chance to 3667 refuse an upload before MHD auto-generates a 100 Continue and 3668 the client starts sending (RFC 7231 §5.1.1), so a declared 3669 length over the limit is answered here and now. */ 3670 kind = client_body_kind (con, 3671 &body_len); 3672 if ( (CLIENT_BODY_LENGTH == kind) && 3673 (body_len > PH_max_request_size) ) 3674 { 3675 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3676 "Rejecting upload: Content-Length %llu exceeds the" 3677 " %llu byte limit\n", 3678 (unsigned long long) body_len, 3679 (unsigned long long) PH_max_request_size); 3680 hr->reject_upload = true; 3681 hr->up_state = UP_DRAINING; 3682 return MHD_queue_response (con, 3683 MHD_HTTP_CONTENT_TOO_LARGE, 3684 upload_failure_response); 3685 } 3686 return start_curl_request (hr, 3687 con, 3688 meth, 3689 ver, 3690 kind, 3691 body_len); 3692 } 3693 3694 if (0 != *upload_data_size) 3695 { 3696 /* The client is sending its body. */ 3697 if (hr->reject_upload || 3698 (UP_DRAINING == hr->up_state)) 3699 { 3700 /* Nobody wants these bytes -- we refused the upload, or the 3701 origin finished without them -- but MHD will not let us queue 3702 a response while the body is still arriving, so they have to 3703 be read and dropped. See #UP_DRAINING. */ 3704 *upload_data_size = 0; 3705 return MHD_YES; 3706 } 3707 if (! relay_upload_chunk (hr, 3708 upload_data_size, 3709 upload_data)) 3710 { 3711 /* Over #PH_max_request_size with no Content-Length to have 3712 caught it earlier. The origin is told the request is broken; 3713 the client gets the 413 once it stops talking. */ 3714 hr->reject_upload = true; 3715 hr->up_state = UP_DRAINING; 3716 if (NULL != hr->job) 3717 { 3718 GNUNET_CURL_job_cancel (hr->job); 3719 hr->job = NULL; 3720 } 3721 *upload_data_size = 0; 3722 return MHD_YES; 3723 } 3724 if (0 != *upload_data_size) 3725 /* @e up_ring is full: what is left will be offered again once 3726 the origin has taken some of it (`curl_upload_cb()' resumes 3727 us). Suspending is not optional -- MHD warns about a handler 3728 that takes nothing and stays runnable, and spins. */ 3729 suspend_client (hr); 3730 return MHD_YES; 3731 } 3732 3733 /* `*upload_data_size == 0': the client's request body, if it had 3734 one, is complete. MHD will accept a response from here on. */ 3735 if (UP_STREAMING == hr->up_state) 3736 { 3737 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3738 "Finished receiving %llu bytes of request body\n", 3739 (unsigned long long) hr->up_received); 3740 hr->up_state = UP_CLIENT_DONE; 3741 /* The read callback may be parked waiting for bytes that will now 3742 never come; let it find that out and report end-of-body. */ 3743 schedule_send_cont (hr); 3744 } 3745 if (hr->reject_upload) 3746 return MHD_queue_response (con, 3747 MHD_HTTP_CONTENT_TOO_LARGE, 3748 upload_failure_response); 3749 switch (hr->down_state) 3750 { 3751 case DOWN_WAITING_HEADERS: 3752 /* No status to send yet. `curl_check_hdr()' or `curl_done_cb()' 3753 will wake us. */ 3754 suspend_client (hr); 3755 return MHD_YES; 3756 case DOWN_STREAMING: 3757 case DOWN_COMPLETE: 3758 return queue_stream_response (hr, 3759 con); 3760 case DOWN_FAILED: 3761 if (hr->headers_complete) 3762 { 3763 /* The status went out with the headers, so there is nothing 3764 left to say: the response can only be truncated. Queue it 3765 anyway -- `body_reader()' ends it with an error immediately, 3766 which breaks the framing where a bare close would have looked 3767 like a complete message. */ 3768 return queue_stream_response (hr, 3769 con); 3770 } 3771 return MHD_queue_response (con, 3772 hr->upstream_timed_out 3773 ? MHD_HTTP_GATEWAY_TIMEOUT 3774 : MHD_HTTP_BAD_GATEWAY, 3775 hr->upstream_timed_out 3776 ? timeout_failure_response 3777 : curl_failure_response); 3778 } 3779 GNUNET_break (0); 3780 return MHD_queue_response (con, 3781 MHD_HTTP_INTERNAL_SERVER_ERROR, 3782 internal_failure_response); 3783 } 3784 3785 3786 /* end of paivana-httpd_reverse.c */