paivana-httpd.c (48017B)
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.c 27 * @brief HTTP proxy that acts as a GNU Taler paywall 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 <taler/taler_templating_lib.h> 35 #include <taler/merchant/common.h> 36 #include "paivana-httpd.h" 37 #include "paivana-httpd_cookie.h" 38 #include "paivana-httpd_daemon.h" 39 #include "paivana-httpd_helper.h" 40 #include "paivana-httpd_pay.h" 41 #include "paivana-httpd_reverse.h" 42 #include "paivana-httpd_templates.h" 43 #include "paivana_pd.h" 44 45 46 char *PH_target_server_base_url; 47 48 char *PH_target_server_unixpath; 49 50 char *PH_merchant_base_url; 51 52 char *PH_merchant_internal_url; 53 54 char *PH_merchant_unixpath; 55 56 char *PH_base_url; 57 58 struct GNUNET_CURL_Context *PH_merchant_ctx; 59 60 struct GNUNET_CURL_Context *PH_proxy_ctx; 61 62 int PH_no_check; 63 64 int PH_respect_forwarded_headers; 65 66 struct GNUNET_STRINGS_IPv4NetworkPolicy *PH_trusted_proxies4; 67 68 struct GNUNET_STRINGS_IPv6NetworkPolicy *PH_trusted_proxies6; 69 70 bool PH_have_trusted_proxies; 71 72 /** 73 * Compiled-in default for #PH_request_buffer_max. Named because 74 * `run()` has to be able to tell it apart from a value the operator 75 * wrote, which is what decides whether #PH_max_request_size inherits 76 * it. 77 */ 78 #define PH_DEFAULT_REQUEST_BUFFER_MAX (256 * 1024) 79 80 /** 81 * File descriptors deliberately kept outside the client-connection budget. 82 * 83 * The current GNUnet/Taler event-loop integration uses native `fd_set`s, so 84 * descriptors numbered #FD_SETSIZE or higher cannot be serviced even when 85 * RLIMIT_NOFILE is larger. 256 descriptors leave one quarter of the usual 86 * 1024-entry table for listen sockets, the scheduler, logs, resolver work, 87 * libcurl's reusable connections and short-lived overlap while sockets are 88 * being replaced. This is deliberately a safety allowance rather than a 89 * claim that those users always consume exactly 256 descriptors. 90 */ 91 #define PH_DESCRIPTOR_RESERVE 256U 92 93 /** 94 * Worst-case sockets charged to one active client connection: the accepted 95 * client socket and one simultaneous origin or merchant socket. 96 */ 97 #define PH_DESCRIPTORS_PER_CONNECTION 2U 98 99 /** 100 * Largest connection budget that fits the select-based descriptor table 101 * after #PH_DESCRIPTOR_RESERVE has been removed. 102 */ 103 #define PH_MAX_CONNECTION_LIMIT \ 104 ((FD_SETSIZE - PH_DESCRIPTOR_RESERVE) \ 105 / PH_DESCRIPTORS_PER_CONNECTION) 106 107 unsigned long long PH_request_buffer_max = PH_DEFAULT_REQUEST_BUFFER_MAX; 108 109 unsigned long long PH_response_buffer_max = 256 * 1024; 110 111 unsigned long long PH_max_request_size = 1024 * 1024; 112 113 struct GNUNET_TIME_Relative PH_upstream_timeout; 114 115 struct GNUNET_TIME_Relative PH_upstream_stall_timeout; 116 117 unsigned int PH_connection_limit = PH_MAX_CONNECTION_LIMIT; 118 119 /** 120 * Thirty-two payment requests are enough to cover a burst while bounding a 121 * malicious client's ability to pin suspended MHD connections for the 122 * five-second merchant long poll. The remaining 352 slots in the default 123 * 384-connection budget stay available for ordinary proxy traffic. 124 */ 125 unsigned int PH_payment_connection_limit = 32; 126 127 unsigned int PH_per_ip_connection_limit = 32; 128 129 /** 130 * Aggregate budget for the two streaming rings of ordinary requests. 131 * 256 MiB admits the defaults (352 * (256 KiB + 256 KiB) = 176 MiB) while 132 * leaving memory for templates, cached responses, merchant replies, libcurl 133 * and allocator overhead. It is a validation budget, not a pre-allocation. 134 */ 135 unsigned long long PH_relay_memory_limit = 256ULL * 1024 * 1024; 136 137 /** 138 * Time accepted requests may finish after SIGTERM. Sixty seconds matches 139 * the two upstream progress timeouts: a healthy request gets a useful chance 140 * to complete, while systemd can still enforce a finite stop deadline. 141 */ 142 struct GNUNET_TIME_Relative PH_shutdown_grace_period; 143 144 int PH_global_ret; 145 146 int PH_global_cookie; 147 148 regex_t PH_whitelist_ex; 149 150 bool PH_have_whitelist_ex; 151 152 /** 153 * Our configuration. 154 */ 155 const struct GNUNET_CONFIGURATION_Handle *PH_cfg; 156 157 158 /** 159 * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule() of 160 * #PH_merchant_ctx. 161 */ 162 static struct GNUNET_CURL_RescheduleContext *merchant_ctx_rc; 163 164 /** 165 * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule() of 166 * #PH_proxy_ctx. 167 */ 168 static struct GNUNET_CURL_RescheduleContext *proxy_ctx_rc; 169 170 /** 171 * Wall-clock start of graceful shutdown, used to enforce 172 * #PH_shutdown_grace_period. 173 */ 174 static struct GNUNET_TIME_Absolute shutdown_started; 175 176 /** 177 * Poll task waiting for active MHD requests to drain. 178 */ 179 static struct GNUNET_SCHEDULER_Task *shutdown_poll_task; 180 181 /** 182 * Guards the final cleanup against repeated shutdown signals or callbacks. 183 */ 184 static bool shutdown_cleanup_done; 185 186 187 /* *************** General / main code *************** */ 188 189 190 /** 191 * Validate the descriptor and aggregate relay-memory budgets. 192 * 193 * These are startup errors rather than warnings. A configuration that can 194 * allocate an fd which `select()` cannot represent fails intermittently and 195 * most visibly in merchant payment checks, where the client API reports the 196 * transport failure as HTTP status zero. 197 * 198 * @return true if the configured budgets are safe 199 */ 200 static bool 201 check_resource_limits (void) 202 { 203 const unsigned int ordinary_limit 204 = PH_connection_limit - PH_payment_connection_limit; 205 const unsigned long long ring_bytes 206 = PH_request_buffer_max + PH_response_buffer_max; 207 208 if (PH_connection_limit > PH_MAX_CONNECTION_LIMIT) 209 { 210 GNUNET_log_config_invalid ( 211 GNUNET_ERROR_TYPE_ERROR, 212 "paivana", 213 "CONNECTION_LIMIT", 214 "exceeds the select()-safe maximum: (FD_SETSIZE 1024 - 256" 215 " reserved descriptors) / 2 descriptors per connection = 384"); 216 return false; 217 } 218 if (ring_bytes > PH_relay_memory_limit / ordinary_limit) 219 { 220 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 221 "REQUEST_BUFFER_MAX (%llu) + RESPONSE_BUFFER_MAX (%llu)," 222 " multiplied by the %u ordinary request slots, exceeds" 223 " RELAY_MEMORY_LIMIT (%llu bytes)\n", 224 PH_request_buffer_max, 225 PH_response_buffer_max, 226 ordinary_limit, 227 PH_relay_memory_limit); 228 return false; 229 } 230 #if HAVE_SYS_RESOURCE_H 231 struct rlimit lim; 232 const unsigned long long required 233 = (unsigned long long) PH_DESCRIPTORS_PER_CONNECTION 234 * PH_connection_limit 235 + PH_DESCRIPTOR_RESERVE; 236 237 if (0 != getrlimit (RLIMIT_NOFILE, 238 &lim)) 239 { 240 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 241 "getrlimit"); 242 return false; 243 } 244 if ( (RLIM_INFINITY != lim.rlim_cur) && 245 ((unsigned long long) lim.rlim_cur < required) ) 246 { 247 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 248 "Open-file soft limit %llu is below the required %llu" 249 " for CONNECTION_LIMIT=%u (two descriptors per connection" 250 " plus a 256-descriptor safety reserve)\n", 251 (unsigned long long) lim.rlim_cur, 252 required, 253 PH_connection_limit); 254 return false; 255 } 256 #endif 257 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 258 "Resource budgets: %u connections (%u ordinary, %u payment)," 259 " %llu/%llu relay bytes, %u descriptor slots reserved\n", 260 PH_connection_limit, 261 ordinary_limit, 262 PH_payment_connection_limit, 263 ring_bytes * ordinary_limit, 264 PH_relay_memory_limit, 265 PH_DESCRIPTOR_RESERVE); 266 return true; 267 } 268 269 270 /** 271 * Load one of the `TRUSTED_PROXIES` options. 272 * 273 * The GNUnet policy parsers are lenient in ways that matter here, so 274 * a non-NULL return is not on its own evidence that anything was 275 * understood: 276 * 277 * - the list is terminated by an all-zero entry, so a network of 278 * 0.0.0.0/0 or ::/0 *is* the terminator and silently truncates 279 * everything after it. "Trust everyone" is therefore inexpressible 280 * — and would be a strange thing to write anyway; 281 * - the v4 parser accepts a value whose last entry is not terminated 282 * by ';' -- the operator wrote the separators but not the 283 * terminator -- and drops that entry without a word, along with 284 * anything else after the final ';'. (Its v6 counterpart refuses 285 * such a value outright, so this one is the v4 side's alone.) 286 * 287 * A genuinely malformed entry, including a v6 network handed to the 288 * v4 parser, does come back as NULL rather than as a shorter list; it 289 * is the only one of these that announces itself. 290 * 291 * The first comes out as "parsed, but nothing usable", which we reject 292 * along with NULL: quietly trusting nobody would send every visitor to 293 * the socket address, and the operator would have no hint why. The 294 * second is worse, because it succeeds: a single missing ';' would 295 * leave the clients behind an unlisted proxy sharing that proxy's 296 * address as their identity, i.e. sharing one paid cookie. So the 297 * entries that came back are counted against the ';' that went in, and 298 * the value has to end in one. 299 * 300 * Not checked for here: an entry may also carry a port policy 301 * ("10.0.0.0/8:80;"), which parses into a perfectly usable entry whose 302 * port range nothing in paivana ever consults -- it reads as a 303 * restriction on the trust and is not one. 304 * 305 * @param c configuration to read from 306 * @param option name of the option 307 * @param[out] count set to the number of usable entries 308 * @return false if the option is present but unusable 309 */ 310 static bool 311 load_trusted_proxies (const struct GNUNET_CONFIGURATION_Handle *c, 312 const char *option, 313 unsigned int *count) 314 { 315 char *opt; 316 bool v6 = (0 != strcmp (option, 317 "TRUSTED_PROXIES")); 318 unsigned int want = 0; 319 size_t len; 320 321 *count = 0; 322 if (GNUNET_OK != 323 GNUNET_CONFIGURATION_get_value_string (c, 324 "paivana", 325 option, 326 &opt)) 327 return true; /* not configured at all: fine */ 328 len = strlen (opt); 329 while ( (len > 0) && 330 ( (' ' == opt[len - 1]) || 331 ('\t' == opt[len - 1]) ) ) 332 opt[--len] = '\0'; 333 for (const char *p = strchr (opt, ';'); NULL != p; p = strchr (p + 1, ';')) 334 want++; 335 if (v6) 336 { 337 PH_trusted_proxies6 = GNUNET_STRINGS_parse_ipv6_policy (opt); 338 if (NULL != PH_trusted_proxies6) 339 while (! GNUNET_is_zero (&PH_trusted_proxies6[*count].network)) 340 (*count)++; 341 } 342 else 343 { 344 PH_trusted_proxies4 = GNUNET_STRINGS_parse_ipv4_policy (opt); 345 if (NULL != PH_trusted_proxies4) 346 while (0 != PH_trusted_proxies4[*count].network.s_addr) 347 (*count)++; 348 } 349 if ( (0 == *count) || 350 (*count != want) || 351 (0 == len) || 352 (';' != opt[len - 1]) ) 353 { 354 if ( (0 != *count) && 355 (*count != want) ) 356 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 357 "Only %u of the %u entries of `%s' were understood;" 358 " refusing to trust a prefix of the list\n", 359 *count, 360 want, 361 option); 362 else if (0 != *count) 363 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 364 "`%s' does not end in the ';' that terminates the last" 365 " entry; anything after the final one is dropped\n", 366 option); 367 GNUNET_log_config_invalid ( 368 GNUNET_ERROR_TYPE_ERROR, 369 "paivana", 370 option, 371 v6 372 ? "not a usable IPv6 network list; entries are separated *and*" 373 " terminated by ';' and must not contain spaces, e.g." 374 " \"2001:db8::/32;fe80::/10;\" (note that ::/0 is indistinguishable" 375 " from the end of the list and cannot be used)" 376 : "not a usable IPv4 network list; entries are separated *and*" 377 " terminated by ';', e.g. \"10.0.0.0/8;192.168.0.0/16;\"" 378 " (note that 0.0.0.0/0 is indistinguishable from the end of the" 379 " list and cannot be used)"); 380 GNUNET_free (opt); 381 return false; 382 } 383 GNUNET_free (opt); 384 PH_have_trusted_proxies = true; 385 return true; 386 } 387 388 389 /** 390 * Final cleanup after graceful draining has completed or reached its deadline. 391 */ 392 static void 393 finish_shutdown (void) 394 { 395 if (shutdown_cleanup_done) 396 return; 397 shutdown_cleanup_done = true; 398 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 399 "Finishing shutdown\n"); 400 /* MHD_stop_daemon() must never see a suspended connection. Stop the 401 scheduler integration first so that resumed requests cannot be dispatched 402 between the following cleanup calls, then cancel/resume every suspended 403 payment and reverse-proxy request before destroying the daemons. This 404 ordering matters specifically when the graceful-drain deadline expires: 405 in that case active upstream operations still exist by definition. */ 406 TALER_MHD_daemons_halt (); 407 PAIVANA_HTTPD_payment_shutdown (); 408 PAIVANA_HTTPD_reverse_shutdown (); 409 PAIVANA_HTTPD_daemons_destroy (); 410 PAIVANA_HTTPD_unload_templates (); 411 TALER_TEMPLATING_done (); 412 GNUNET_free (PH_target_server_base_url); 413 GNUNET_free (PH_target_server_unixpath); 414 GNUNET_free (PH_trusted_proxies4); 415 GNUNET_free (PH_trusted_proxies6); 416 GNUNET_free (PH_merchant_base_url); 417 GNUNET_free (PH_merchant_internal_url); 418 GNUNET_free (PH_merchant_unixpath); 419 GNUNET_free (PH_base_url); 420 if (PH_have_whitelist_ex) 421 { 422 regfree (&PH_whitelist_ex); 423 PH_have_whitelist_ex = false; 424 } 425 if (NULL != PH_merchant_ctx) 426 { 427 GNUNET_CURL_fini (PH_merchant_ctx); 428 PH_merchant_ctx = NULL; 429 } 430 if (NULL != merchant_ctx_rc) 431 { 432 GNUNET_CURL_gnunet_rc_destroy (merchant_ctx_rc); 433 merchant_ctx_rc = NULL; 434 } 435 if (NULL != PH_proxy_ctx) 436 { 437 GNUNET_CURL_fini (PH_proxy_ctx); 438 PH_proxy_ctx = NULL; 439 } 440 if (NULL != proxy_ctx_rc) 441 { 442 GNUNET_CURL_gnunet_rc_destroy (proxy_ctx_rc); 443 proxy_ctx_rc = NULL; 444 } 445 } 446 447 448 /** 449 * Check whether accepted requests have drained. 450 * 451 * A 100 ms poll interval bounds shutdown-completion latency without placing a 452 * callback on every request-completion path. At the 60-second default this 453 * is at most 600 cheap counter reads and does not touch the request list. 454 * 455 * @param cls unused 456 */ 457 static void 458 poll_shutdown_drain (void *cls) 459 { 460 struct GNUNET_TIME_Relative elapsed; 461 unsigned int active; 462 463 (void) cls; 464 shutdown_poll_task = NULL; 465 active = PAIVANA_HTTPD_active_requests (); 466 elapsed = GNUNET_TIME_absolute_get_duration (shutdown_started); 467 if (0 == active) 468 { 469 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 470 "Graceful shutdown drained all requests in %s\n", 471 GNUNET_STRINGS_relative_time_to_string (elapsed, 472 true)); 473 finish_shutdown (); 474 return; 475 } 476 if (GNUNET_TIME_relative_cmp (elapsed, 477 >=, 478 PH_shutdown_grace_period)) 479 { 480 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 481 "Graceful shutdown deadline reached with %u active" 482 " request%s; terminating them now\n", 483 active, 484 (1 == active) ? "" : "s"); 485 finish_shutdown (); 486 return; 487 } 488 shutdown_poll_task = GNUNET_SCHEDULER_add_delayed ( 489 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 490 100), 491 &poll_shutdown_drain, 492 NULL); 493 } 494 495 496 /** 497 * Task run on shutdown: close listeners, then allow accepted requests to 498 * complete before final cleanup. 499 * 500 * @param cls closure 501 */ 502 static void 503 do_shutdown (void *cls) 504 { 505 unsigned int active; 506 507 (void) cls; 508 shutdown_started = GNUNET_TIME_absolute_get (); 509 active = PAIVANA_HTTPD_begin_drain (); 510 if ( (0 == active) || 511 (0 == PH_shutdown_grace_period.rel_value_us) ) 512 { 513 finish_shutdown (); 514 return; 515 } 516 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 517 "Shutdown quiesced listeners; allowing %u active request%s" 518 " up to %s to finish\n", 519 active, 520 (1 == active) ? "" : "s", 521 GNUNET_STRINGS_relative_time_to_string ( 522 PH_shutdown_grace_period, 523 true)); 524 shutdown_poll_task = GNUNET_SCHEDULER_add_now (&poll_shutdown_drain, 525 NULL); 526 } 527 528 529 /** 530 * Remove trailing slashes from the web URL @a url, in place. 531 * 532 * Our configuration syntax prefers base URLs to be written with a 533 * trailing '/', while everything we append to one -- a request path, 534 * a "/.well-known/..." endpoint -- brings a leading '/' of its own. 535 * Dropping them here is what keeps the concatenation from yielding 536 * "//", which would otherwise reach the upstream verbatim and, for 537 * BASE_URL, end up in the string the access cookie is keyed on and in 538 * the URL the templates' regular expressions are matched against. 539 * 540 * Never strips below "scheme://h", so that a URL consisting of 541 * nothing but a scheme and a host keeps its host. 542 * 543 * Refuses a URL carrying a query or a fragment instead of mangling it: 544 * the trailing '/' would then be part of the query ("http://h/?p=a/") 545 * and stripping it changes what the query says. Neither has a sensible 546 * reading in a base URL we concatenate a request path onto anyway -- 547 * "http://h/?p=a" + "/x" is not a request for /x -- so the operator is 548 * better told than quietly corrected. 549 * 550 * @param[in,out] url URL to canonicalize; must have passed 551 * TALER_is_web_url() 552 * @return false if @a url carries a query or a fragment, in which case 553 * it is left untouched 554 */ 555 static bool 556 strip_trailing_slashes (char *url) 557 { 558 size_t len = strlen (url); 559 const char *sep; 560 size_t min_len; 561 562 if ( (NULL != strchr (url, 563 '?')) || 564 (NULL != strchr (url, 565 '#')) ) 566 return false; 567 sep = strstr (url, 568 "://"); 569 GNUNET_assert (NULL != sep); /* was a web URL after all! */ 570 min_len = (size_t) (sep - url) + strlen ("://") + 1; 571 while ( (len > min_len) && 572 ('/' == url[len - 1]) ) 573 url[--len] = '\0'; 574 return true; 575 } 576 577 578 /** 579 * Main function that will be run. Main tasks are (1) init. the 580 * curl infrastructure (curl_global_init() / curl_multi_init()), 581 * then fetch the HTTP port where its Web service should listen at, 582 * and finally start MHD on that port. 583 * 584 * @param cls closure 585 * @param args remaining command-line arguments 586 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 587 * @param c configuration 588 */ 589 static void 590 run (void *cls, 591 char *const *args, 592 const char *cfgfile, 593 const struct GNUNET_CONFIGURATION_Handle *c) 594 { 595 char *secret; 596 bool buffer_max_explicit; 597 598 (void) cls; 599 (void) args; 600 (void) cfgfile; 601 PH_cfg = c; 602 PH_upstream_timeout 603 = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 604 60); 605 PH_upstream_stall_timeout 606 = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 607 60); 608 PH_shutdown_grace_period 609 = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 610 60); 611 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 612 NULL); 613 if ( (0 == PH_request_buffer_max) || 614 (PH_request_buffer_max > GNUNET_MAX_MALLOC_CHECKED) ) 615 { 616 /* 0 would leave no room to move a body through at all; above the 617 hard allocation cap the buffer simply cannot be made. A negative 618 argument ends up here too: GNUnet parses the option with 619 sscanf("%llu"), which reads "-1" as ULLONG_MAX rather than 620 complaining. */ 621 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 622 "-u/--max-upload must be between 1 and %llu:" 623 " it sizes the buffer a request body is relayed through\n", 624 (unsigned long long) GNUNET_MAX_MALLOC_CHECKED); 625 PH_global_ret = EXIT_INVALIDARGUMENT; 626 GNUNET_SCHEDULER_shutdown (); 627 return; 628 } 629 /* `-u' is applied by GNUNET_PROGRAM_run() before we are called, so a 630 value differing from the compiled-in default is one the operator 631 wrote. See PH_max_request_size for why that has to be visible. */ 632 buffer_max_explicit = (PH_DEFAULT_REQUEST_BUFFER_MAX 633 != PH_request_buffer_max); 634 if (! PH_no_check) 635 { 636 if (GNUNET_OK != 637 TALER_TEMPLATING_init (PAIVANA_project_data ())) 638 { 639 /* Almost always a missing or unreadable $PREFIX/share/paivana/ 640 templates/ -- an installation problem, not a bug, so say so 641 instead of adding a second "Assertion failed" to the one 642 TALER_TEMPLATING_init() already logged. */ 643 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 644 "Failed to load the paywall templates; is paivana" 645 " installed, and does PAIVANA_PREFIX point at the" 646 " installation?\n"); 647 PH_global_ret = EXIT_NOTINSTALLED; 648 GNUNET_SCHEDULER_shutdown (); 649 return; 650 } 651 if (! PAIVANA_HTTPD_init_template_languages ()) 652 { 653 PH_global_ret = EXIT_NOTINSTALLED; 654 GNUNET_SCHEDULER_shutdown (); 655 return; 656 } 657 } 658 if (! PAIVANA_HTTPD_reverse_init ()) 659 { 660 GNUNET_break (0); 661 PH_global_ret = EXIT_FAILURE; 662 GNUNET_SCHEDULER_shutdown (); 663 return; 664 } 665 666 if (GNUNET_OK != 667 GNUNET_CONFIGURATION_get_value_string ( 668 c, 669 "paivana", 670 "DESTINATION_BASE_URL", 671 &PH_target_server_base_url)) 672 { 673 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 674 "paivana", 675 "DESTINATION_BASE_URL"); 676 PH_global_ret = EXIT_NOTCONFIGURED; 677 GNUNET_SCHEDULER_shutdown (); 678 return; 679 } 680 if (! TALER_is_web_url (PH_target_server_base_url)) 681 { 682 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 683 "paivana", 684 "DESTINATION_BASE_URL", 685 "not a web url"); 686 PH_global_ret = EXIT_NOTCONFIGURED; 687 GNUNET_SCHEDULER_shutdown (); 688 return; 689 } 690 { 691 unsigned long long v; 692 693 if (GNUNET_OK == 694 GNUNET_CONFIGURATION_get_value_number (c, 695 "paivana", 696 "CONNECTION_LIMIT", 697 &v)) 698 { 699 if ( (0 == v) || 700 (v > UINT_MAX) ) 701 { 702 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 703 "paivana", 704 "CONNECTION_LIMIT", 705 "must be between 1 and UINT_MAX"); 706 PH_global_ret = EXIT_NOTCONFIGURED; 707 GNUNET_SCHEDULER_shutdown (); 708 return; 709 } 710 PH_connection_limit = (unsigned int) v; 711 } 712 if (GNUNET_OK == 713 GNUNET_CONFIGURATION_get_value_number (c, 714 "paivana", 715 "PAYMENT_CONNECTION_LIMIT", 716 &v)) 717 { 718 if ( (0 == v) || 719 (v > UINT_MAX) ) 720 { 721 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 722 "paivana", 723 "PAYMENT_CONNECTION_LIMIT", 724 "must be between 1 and UINT_MAX"); 725 PH_global_ret = EXIT_NOTCONFIGURED; 726 GNUNET_SCHEDULER_shutdown (); 727 return; 728 } 729 PH_payment_connection_limit = (unsigned int) v; 730 } 731 if (GNUNET_OK == 732 GNUNET_CONFIGURATION_get_value_number (c, 733 "paivana", 734 "PER_IP_CONNECTION_LIMIT", 735 &v)) 736 { 737 if (v > UINT_MAX) 738 { 739 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 740 "paivana", 741 "PER_IP_CONNECTION_LIMIT", 742 "must not exceed UINT_MAX"); 743 PH_global_ret = EXIT_NOTCONFIGURED; 744 GNUNET_SCHEDULER_shutdown (); 745 return; 746 } 747 PH_per_ip_connection_limit = (unsigned int) v; 748 } 749 if (GNUNET_OK == 750 GNUNET_CONFIGURATION_get_value_number (c, 751 "paivana", 752 "REQUEST_BUFFER_MAX", 753 &v)) 754 { 755 if ( (0 == v) || 756 (v > GNUNET_MAX_MALLOC_CHECKED) ) 757 { 758 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 759 "paivana", 760 "REQUEST_BUFFER_MAX", 761 "must be between 1 and 40 MiB"); 762 PH_global_ret = EXIT_NOTCONFIGURED; 763 GNUNET_SCHEDULER_shutdown (); 764 return; 765 } 766 PH_request_buffer_max = v; 767 buffer_max_explicit = true; 768 } 769 if (GNUNET_OK == 770 GNUNET_CONFIGURATION_get_value_number (c, 771 "paivana", 772 "RESPONSE_BUFFER_MAX", 773 &v)) 774 { 775 if ( (0 == v) || 776 (v > GNUNET_MAX_MALLOC_CHECKED) ) 777 { 778 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 779 "paivana", 780 "RESPONSE_BUFFER_MAX", 781 "must be between 1 and 40 MiB"); 782 PH_global_ret = EXIT_NOTCONFIGURED; 783 GNUNET_SCHEDULER_shutdown (); 784 return; 785 } 786 PH_response_buffer_max = v; 787 } 788 if (GNUNET_OK == 789 GNUNET_CONFIGURATION_get_value_number (c, 790 "paivana", 791 "MAX_REQUEST_SIZE", 792 &v)) 793 { 794 PH_max_request_size = v; 795 } 796 else if (buffer_max_explicit) 797 { 798 /* Before streaming these were one number: the buffer a body was 799 assembled in *was* the largest body we would accept. An 800 operator who raised it to permit large uploads meant the limit, 801 so honour that reading rather than silently tightening their 802 configuration back down to the 1 MiB default. */ 803 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 804 "Taking MAX_REQUEST_SIZE from the configured request" 805 " buffer size (%llu bytes); set it explicitly to say" 806 " otherwise\n", 807 PH_request_buffer_max); 808 PH_max_request_size = PH_request_buffer_max; 809 } 810 if (GNUNET_OK == 811 GNUNET_CONFIGURATION_get_value_number (c, 812 "paivana", 813 "RELAY_MEMORY_LIMIT", 814 &v)) 815 { 816 if (0 == v) 817 { 818 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 819 "paivana", 820 "RELAY_MEMORY_LIMIT", 821 "must be at least one byte"); 822 PH_global_ret = EXIT_NOTCONFIGURED; 823 GNUNET_SCHEDULER_shutdown (); 824 return; 825 } 826 PH_relay_memory_limit = v; 827 } 828 } 829 if (PH_payment_connection_limit >= PH_connection_limit) 830 { 831 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 832 "paivana", 833 "PAYMENT_CONNECTION_LIMIT", 834 "must be smaller than CONNECTION_LIMIT so" 835 " ordinary requests retain capacity"); 836 PH_global_ret = EXIT_NOTCONFIGURED; 837 GNUNET_SCHEDULER_shutdown (); 838 return; 839 } 840 { 841 struct GNUNET_TIME_Relative st; 842 843 if (GNUNET_OK == 844 GNUNET_CONFIGURATION_get_value_time (c, 845 "paivana", 846 "UPSTREAM_TIMEOUT", 847 &st)) 848 { 849 if (0 == st.rel_value_us) 850 { 851 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 852 "paivana", 853 "UPSTREAM_TIMEOUT", 854 "must not be zero"); 855 PH_global_ret = EXIT_NOTCONFIGURED; 856 GNUNET_SCHEDULER_shutdown (); 857 return; 858 } 859 PH_upstream_timeout = st; 860 } 861 if (GNUNET_OK == 862 GNUNET_CONFIGURATION_get_value_time (c, 863 "paivana", 864 "UPSTREAM_STALL_TIMEOUT", 865 &st)) 866 { 867 if (0 == st.rel_value_us) 868 { 869 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 870 "paivana", 871 "UPSTREAM_STALL_TIMEOUT", 872 "must not be zero"); 873 PH_global_ret = EXIT_NOTCONFIGURED; 874 GNUNET_SCHEDULER_shutdown (); 875 return; 876 } 877 PH_upstream_stall_timeout = st; 878 } 879 if (GNUNET_OK == 880 GNUNET_CONFIGURATION_get_value_time (c, 881 "paivana", 882 "SHUTDOWN_GRACE_PERIOD", 883 &st)) 884 PH_shutdown_grace_period = st; 885 } 886 if (! check_resource_limits ()) 887 { 888 PH_global_ret = EXIT_NOTCONFIGURED; 889 GNUNET_SCHEDULER_shutdown (); 890 return; 891 } 892 { 893 unsigned int n4; 894 unsigned int n6; 895 896 if ( (! load_trusted_proxies (c, 897 "TRUSTED_PROXIES", 898 &n4)) || 899 (! load_trusted_proxies (c, 900 "TRUSTED_PROXIES6", 901 &n6)) ) 902 { 903 PH_global_ret = EXIT_NOTCONFIGURED; 904 GNUNET_SCHEDULER_shutdown (); 905 return; 906 } 907 if (PH_have_trusted_proxies) 908 { 909 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 910 "Trusting %u IPv4 and %u IPv6 network(s) as reverse proxies\n", 911 n4, 912 n6); 913 if (! PH_respect_forwarded_headers) 914 { 915 /* The policy says which proxies may speak for a client; it is 916 the -f flag that says we listen at all. Configuring one 917 without the other is a mistake in either direction, but only 918 this one leaves the policy inert. */ 919 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 920 "TRUSTED_PROXIES configured but -f/--respect-forwarded-headers" 921 " is not set; forwarded headers are ignored entirely\n"); 922 } 923 } 924 else if (PH_respect_forwarded_headers) 925 { 926 /* Not the same mistake in reverse: a single proxy in front, one 927 that writes the forwarding headers itself, needs no policy at 928 all. PAIVANA_HTTPD_resolve_forwarding() walks the chain from 929 the right and stops at the first hop it does not trust, so 930 with no policy it stops on its first step -- at the element 931 our peer wrote, which nothing the client prepends can displace. 932 What the policy adds is the ability to keep stepping leftwards 933 through hops that are named there, which is what a chain of 934 more than one proxy needs. The hazard worth warning about is 935 narrower, and it is upstream of us. */ 936 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 937 "-f/--respect-forwarded-headers is set without TRUSTED_PROXIES:" 938 " the client address is taken from the rightmost element of the" 939 " forwarding chain, which is the client's own address only if" 940 " the server in front sets or appends these headers itself." 941 " One that passes the client's `%s' through unchanged -- as" 942 " nginx does, and `%s' is the header we prefer over `%s' --" 943 " leaves the client choosing the element we believe." 944 " TRUSTED_PROXIES is needed only for a chain of more than one" 945 " proxy\n", 946 MHD_HTTP_HEADER_FORWARDED, 947 MHD_HTTP_HEADER_FORWARDED, 948 PH_HEADER_X_FORWARDED_FOR); 949 } 950 } 951 /* No need to check the return value. If given, we take it, 952 * otherwise it stays NULL. */ 953 GNUNET_CONFIGURATION_get_value_filename ( 954 c, 955 "paivana", 956 "DESTINATION_UNIXPATH", 957 &PH_target_server_unixpath); 958 if (! strip_trailing_slashes (PH_target_server_base_url)) 959 { 960 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 961 "paivana", 962 "DESTINATION_BASE_URL", 963 "must not carry a query or a fragment"); 964 PH_global_ret = EXIT_NOTCONFIGURED; 965 GNUNET_SCHEDULER_shutdown (); 966 return; 967 } 968 if (! PH_no_check) 969 { 970 if (GNUNET_OK != 971 GNUNET_CONFIGURATION_get_value_string ( 972 c, 973 "paivana", 974 "MERCHANT_BACKEND_URL", 975 &PH_merchant_base_url)) 976 { 977 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 978 "paivana", 979 "MERCHANT_BACKEND_URL"); 980 PH_global_ret = EXIT_NOTCONFIGURED; 981 GNUNET_SCHEDULER_shutdown (); 982 return; 983 } 984 if (! TALER_is_web_url (PH_merchant_base_url)) 985 { 986 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 987 "paivana", 988 "MERCHANT_BACKEND_URL", 989 "not a web url"); 990 PH_global_ret = EXIT_NOTCONFIGURED; 991 GNUNET_SCHEDULER_shutdown (); 992 return; 993 } 994 /* Deliberately *not* run through strip_trailing_slashes(): a Taler 995 merchant API base URL is joined with relative paths, so it wants 996 the '/' the other base URLs above shed. TALER_url_join() insists 997 on it and returns NULL without it, which surfaces much later as 998 an assertion failure on the handle built from the joined URL -- 999 so check for it here, where we can still name the option that is 1000 wrong. */ 1001 if ('/' != PH_merchant_base_url[strlen (PH_merchant_base_url) - 1]) 1002 { 1003 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1004 "paivana", 1005 "MERCHANT_BACKEND_URL", 1006 "must end with a '/'"); 1007 PH_global_ret = EXIT_NOTCONFIGURED; 1008 GNUNET_SCHEDULER_shutdown (); 1009 return; 1010 } 1011 if (GNUNET_OK != 1012 GNUNET_CONFIGURATION_get_value_string ( 1013 c, 1014 "paivana", 1015 "MERCHANT_BACKEND_INTERNAL_URL", 1016 &PH_merchant_internal_url)) 1017 PH_merchant_internal_url = GNUNET_strdup (PH_merchant_base_url); 1018 if (! TALER_is_web_url (PH_merchant_internal_url)) 1019 { 1020 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1021 "paivana", 1022 "MERCHANT_BACKEND_INTERNAL_URL", 1023 "not a web url"); 1024 PH_global_ret = EXIT_NOTCONFIGURED; 1025 GNUNET_SCHEDULER_shutdown (); 1026 return; 1027 } 1028 if ('/' != PH_merchant_internal_url[ 1029 strlen (PH_merchant_internal_url) - 1]) 1030 { 1031 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1032 "paivana", 1033 "MERCHANT_BACKEND_INTERNAL_URL", 1034 "must end with a '/'"); 1035 PH_global_ret = EXIT_NOTCONFIGURED; 1036 GNUNET_SCHEDULER_shutdown (); 1037 return; 1038 } 1039 } 1040 { 1041 char *merchant_unix_path; 1042 1043 if (GNUNET_OK == 1044 GNUNET_CONFIGURATION_get_value_string ( 1045 c, 1046 "paivana", 1047 "MERCHANT_BACKEND_UNIX_PATH", 1048 &merchant_unix_path)) 1049 { 1050 if (! TALER_MERCHANT_global_set_unixpath (merchant_unix_path)) 1051 { 1052 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, 1053 "paivana", 1054 "MERCHANT_BACKEND_UNIX_PATH", 1055 "invalid path; ignoring the setting"); 1056 } 1057 else 1058 { 1059 PH_merchant_unixpath = merchant_unix_path; 1060 merchant_unix_path = NULL; 1061 } 1062 GNUNET_free (merchant_unix_path); 1063 } 1064 } 1065 { 1066 char *whitelist; 1067 1068 if (GNUNET_OK == 1069 GNUNET_CONFIGURATION_get_value_string ( 1070 c, 1071 "paivana", 1072 "WHITELIST", 1073 &whitelist)) 1074 { 1075 regex_t bare; 1076 int rc; 1077 1078 /* Compile the expression exactly as written first, and refuse 1079 the configuration if that fails. The wrapping below is a 1080 textual splice, and a value like "a)|(b" splices into 1081 "^(a)|(b)$" -- which compiles, but means "^a" OR "b$", each 1082 anchored on one side only. Getting out of the group that way 1083 needs parentheses that do not balance on their own, and that 1084 is precisely what a bare regcomp() rejects; an expression that 1085 is valid by itself is unaffected and keeps matching what it 1086 always did. (The merchant backend validates the website_regex 1087 of a template the same way.) */ 1088 rc = regcomp (&bare, 1089 whitelist, 1090 REG_NOSUB | REG_EXTENDED); 1091 if (0 == rc) 1092 { 1093 char *anchored; 1094 1095 regfree (&bare); 1096 /* Anchor the expression: regexec(3) is unanchored, so a 1097 WHITELIST of "/free/" would otherwise waive payment for every 1098 URL merely *containing* it -- including one an attacker 1099 appends to a path they want for free. Wrapping in a group 1100 keeps alternations ("a|b") from binding the anchors to only 1101 the first and last branch. An expression that already 1102 anchors itself is unaffected, as ^ and $ inside still match 1103 at string start/end. */ 1104 GNUNET_asprintf (&anchored, 1105 "^(%s)$", 1106 whitelist); 1107 rc = regcomp (&PH_whitelist_ex, 1108 anchored, 1109 REG_NOSUB | REG_EXTENDED); 1110 GNUNET_free (anchored); 1111 } 1112 if (0 != rc) 1113 { 1114 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1115 "paivana", 1116 "WHITELIST", 1117 "Invalid regular expression"); 1118 GNUNET_free (whitelist); 1119 PH_global_ret = EXIT_NOTCONFIGURED; 1120 GNUNET_SCHEDULER_shutdown (); 1121 return; 1122 } 1123 PH_have_whitelist_ex = true; 1124 GNUNET_free (whitelist); 1125 } 1126 } 1127 1128 if (GNUNET_OK != 1129 GNUNET_CONFIGURATION_get_value_string ( 1130 c, 1131 "paivana", 1132 "BASE_URL", 1133 &PH_base_url)) 1134 { 1135 /* Without BASE_URL we reconstruct our own URL from the request, and 1136 the only thing that makes that safe is a reverse proxy in front 1137 of us that enforced a correct Host: -- which is exactly what -f 1138 asserts. Talking to clients directly, Host: is whatever the 1139 client typed, and it decides both the string the access cookie is 1140 keyed on and the string the templates' website_regex is matched 1141 against: a client sending "Host: anything.invalid" would match no 1142 template and be served for free. The scheme has the same 1143 problem in reverse: direct connections have no X-Forwarded-Proto 1144 to consult, so a site reached over https would generate http:// 1145 URLs and again match no template. */ 1146 if ( (! PH_respect_forwarded_headers) && 1147 (! PH_no_check) ) 1148 { 1149 /* -n has no paywall, hence no access decision that the site's 1150 own identity could be got wrong for; the base URL is then only 1151 cosmetic and the "pure reverse proxy" mode stays configurable 1152 with nothing but DESTINATION_BASE_URL. */ 1153 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1154 "paivana", 1155 "BASE_URL"); 1156 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1157 "BASE_URL is required unless -f is given: without a" 1158 " reverse proxy vouching for it, the client's Host" 1159 " header cannot be used to identify this site\n"); 1160 PH_global_ret = EXIT_NOTCONFIGURED; 1161 GNUNET_SCHEDULER_shutdown (); 1162 return; 1163 } 1164 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1165 "paivana", 1166 "BASE_URL"); 1167 } 1168 if (NULL != PH_base_url) 1169 { 1170 if (! TALER_is_web_url (PH_base_url)) 1171 { 1172 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1173 "paivana", 1174 "BASE_URL", 1175 "not a web url"); 1176 PH_global_ret = EXIT_NOTCONFIGURED; 1177 GNUNET_SCHEDULER_shutdown (); 1178 return; 1179 } 1180 if (! strip_trailing_slashes (PH_base_url)) 1181 { 1182 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1183 "paivana", 1184 "BASE_URL", 1185 "must not carry a query or a fragment"); 1186 PH_global_ret = EXIT_NOTCONFIGURED; 1187 GNUNET_SCHEDULER_shutdown (); 1188 return; 1189 } 1190 } 1191 1192 if (GNUNET_OK != 1193 GNUNET_CONFIGURATION_get_value_string ( 1194 c, 1195 "paivana", 1196 "SECRET", 1197 &secret)) 1198 { 1199 if (! PH_no_check) 1200 { 1201 /* The key is the only input to the access-cookie MAC that the 1202 client does not supply, so a fresh random one invalidates 1203 every access anyone has paid for. That used to be a warning 1204 and a random key, which is survivable only if restarts are 1205 rare -- and the shipped unit restarts hourly (RuntimeMaxSec), 1206 so a customer paying at 10:59 was shown the paywall again at 1207 11:01. Refuse to start instead: the packaging generates one 1208 at install time, and an operator configuring by hand needs to 1209 be told rather than silently sold a paywall that forgets. `-n' 1210 mints no cookies at all and so needs no key. */ 1211 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1212 "paivana", 1213 "SECRET"); 1214 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1215 "Refusing to start without `SECRET': every restart" 1216 " would invalidate all access already paid for." 1217 " Generate one with `gpg --gen-random 0 32 | base64'" 1218 " (or pass -n to serve without a paywall).\n"); 1219 PH_global_ret = EXIT_NOTCONFIGURED; 1220 GNUNET_SCHEDULER_shutdown (); 1221 return; 1222 } 1223 GNUNET_CRYPTO_random_block (&paivana_secret, 1224 sizeof (paivana_secret)); 1225 } 1226 else 1227 { 1228 GNUNET_CRYPTO_hash (secret, 1229 strlen (secret), 1230 &paivana_secret); 1231 GNUNET_free (secret); 1232 } 1233 PH_proxy_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 1234 &proxy_ctx_rc); 1235 GNUNET_assert (NULL != PH_proxy_ctx); 1236 proxy_ctx_rc = GNUNET_CURL_gnunet_rc_create (PH_proxy_ctx); 1237 if (! PH_no_check) 1238 { 1239 char *merchant_access_token; 1240 char *auth_header; 1241 1242 if (GNUNET_OK != 1243 GNUNET_CONFIGURATION_get_value_string ( 1244 c, 1245 "paivana", 1246 "MERCHANT_ACCESS_TOKEN", 1247 &merchant_access_token)) 1248 { 1249 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1250 "paivana", 1251 "MERCHANT_ACCESS_TOKEN"); 1252 PH_global_ret = EXIT_NOTCONFIGURED; 1253 GNUNET_SCHEDULER_shutdown (); 1254 return; 1255 } 1256 if ('\0' == merchant_access_token[0]) 1257 { 1258 /* An empty value builds "Authorization: Bearer " and the backend 1259 answers 401 to every request we make with it, i.e. no template 1260 ever loads and no payment is ever confirmed -- and nothing 1261 says why. The option being present but blank is the shape a 1262 half-finished install has. */ 1263 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1264 "paivana", 1265 "MERCHANT_ACCESS_TOKEN", 1266 "must not be empty"); 1267 GNUNET_free (merchant_access_token); 1268 PH_global_ret = EXIT_NOTCONFIGURED; 1269 GNUNET_SCHEDULER_shutdown (); 1270 return; 1271 } 1272 /* A second context, because the credential below is appended to 1273 *every* request the context makes: on a shared context we would 1274 hand our merchant bearer token to the origin server (and to 1275 whoever it redirects to) on each forwarded request. */ 1276 PH_merchant_ctx 1277 = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 1278 &merchant_ctx_rc); 1279 GNUNET_assert (NULL != PH_merchant_ctx); 1280 merchant_ctx_rc = GNUNET_CURL_gnunet_rc_create (PH_merchant_ctx); 1281 GNUNET_asprintf (&auth_header, 1282 "%s: Bearer %s", 1283 MHD_HTTP_HEADER_AUTHORIZATION, 1284 merchant_access_token); 1285 GNUNET_free (merchant_access_token); 1286 GNUNET_assert (GNUNET_OK == 1287 GNUNET_CURL_append_header (PH_merchant_ctx, 1288 auth_header)); 1289 GNUNET_free (auth_header); 1290 } 1291 /* Once templates are done loading, this will 1292 start the daemon as well. In -n (no-payment) mode we skip 1293 the merchant round-trip entirely. */ 1294 if (PH_no_check) 1295 { 1296 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1297 "Paywall disabled (-n), skipping template load\n"); 1298 PAIVANA_HTTPD_serve_requests (); 1299 return; 1300 } 1301 PAIVANA_HTTPD_load_templates (); 1302 } 1303 1304 1305 /** 1306 * Main function. 1307 */ 1308 int 1309 main (int argc, 1310 char *const *argv) 1311 { 1312 struct GNUNET_GETOPT_CommandLineOption options[] = { 1313 GNUNET_GETOPT_option_flag ( 1314 'f', 1315 "respect-forwarded-headers", 1316 gettext_noop ( 1317 "trust X-Forwarded-For for the client address (only safe behind a trusted reverse proxy)"), 1318 &PH_respect_forwarded_headers), 1319 GNUNET_GETOPT_option_flag ( 1320 'g', 1321 "global-payment", 1322 gettext_noop ( 1323 "disables per-page payment, useful if a single payment should grant access to the entire site"), 1324 &PH_global_cookie), 1325 GNUNET_GETOPT_option_flag ( 1326 'n', 1327 "no-payment", 1328 gettext_noop ( 1329 "disables payment, useful for testing reverse-proxy only"), 1330 &PH_no_check), 1331 GNUNET_GETOPT_option_ulong ( 1332 'u', 1333 "max-upload", 1334 "BYTES", 1335 gettext_noop ( 1336 "bytes of a request body to hold in memory while relaying it upstream (default: 262144); the largest body accepted is MAX_REQUEST_SIZE"), 1337 &PH_request_buffer_max), 1338 GNUNET_GETOPT_OPTION_END 1339 }; 1340 enum GNUNET_GenericReturnValue ret; 1341 1342 ret = GNUNET_PROGRAM_run ( 1343 PAIVANA_project_data (), 1344 argc, 1345 argv, 1346 "paivana-httpd", 1347 "reverse proxy requesting Taler payment", 1348 options, 1349 &run, NULL); 1350 if (GNUNET_SYSERR == ret) 1351 return EXIT_INVALIDARGUMENT; 1352 if (GNUNET_NO == ret) 1353 return EXIT_SUCCESS; 1354 return PH_global_ret; 1355 } 1356 1357 1358 /* end of paivana-httpd.c */