paivana

HTTP paywall reverse proxy
Log | Files | Refs | Submodules | README | LICENSE

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 */