paivana-httpd.c (11285B)
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 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 General Public License for more details. 15 16 You should have received a copy of the GNU 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_base_url; 53 54 struct GNUNET_CURL_Context *PH_ctx; 55 56 int PH_no_check; 57 58 int PH_respect_forwarded_headers; 59 60 unsigned long long PH_request_buffer_max = 1024 * 1024; 61 62 int PH_global_ret; 63 64 int PH_global_cookie; 65 66 regex_t PH_whitelist_ex; 67 68 bool PH_have_whitelist_ex; 69 70 /** 71 * Our configuration. 72 */ 73 const struct GNUNET_CONFIGURATION_Handle *PH_cfg; 74 75 76 /** 77 * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule(). 78 */ 79 static struct GNUNET_CURL_RescheduleContext *ctx_rc; 80 81 82 /* *************** General / main code *************** */ 83 84 85 /** 86 * Task run on shutdown 87 * 88 * @param cls closure 89 */ 90 static void 91 do_shutdown (void *cls) 92 { 93 (void) cls; 94 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 95 "Shutting down...\n"); 96 TALER_MHD_daemons_halt (); 97 PAIVANA_HTTPD_payment_shutdown (); 98 PAIVANA_HTTPD_reverse_shutdown (); 99 TALER_MHD_daemons_destroy (); 100 PAIVANA_HTTPD_unload_templates (); 101 TALER_TEMPLATING_done (); 102 GNUNET_free (PH_target_server_base_url); 103 GNUNET_free (PH_target_server_unixpath); 104 GNUNET_free (PH_merchant_base_url); 105 GNUNET_free (PH_base_url); 106 if (PH_have_whitelist_ex) 107 { 108 regfree (&PH_whitelist_ex); 109 PH_have_whitelist_ex = false; 110 } 111 if (NULL != PH_ctx) 112 { 113 GNUNET_CURL_fini (PH_ctx); 114 PH_ctx = NULL; 115 } 116 if (NULL != ctx_rc) 117 { 118 GNUNET_CURL_gnunet_rc_destroy (ctx_rc); 119 ctx_rc = NULL; 120 } 121 } 122 123 124 /** 125 * Main function that will be run. Main tasks are (1) init. the 126 * curl infrastructure (curl_global_init() / curl_multi_init()), 127 * then fetch the HTTP port where its Web service should listen at, 128 * and finally start MHD on that port. 129 * 130 * @param cls closure 131 * @param args remaining command-line arguments 132 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 133 * @param c configuration 134 */ 135 static void 136 run (void *cls, 137 char *const *args, 138 const char *cfgfile, 139 const struct GNUNET_CONFIGURATION_Handle *c) 140 { 141 char *secret; 142 143 (void) cls; 144 (void) args; 145 (void) cfgfile; 146 PH_cfg = c; 147 148 if (! PH_no_check) 149 { 150 if (GNUNET_OK != 151 TALER_TEMPLATING_init (PAIVANA_project_data ())) 152 { 153 GNUNET_break (0); 154 GNUNET_SCHEDULER_shutdown (); 155 return; 156 } 157 } 158 if (! PAIVANA_HTTPD_reverse_init ()) 159 { 160 GNUNET_break (0); 161 GNUNET_SCHEDULER_shutdown (); 162 return; 163 } 164 165 /* No need to check return value. If given, we take, 166 * otherwise it stays zero. */ 167 if (GNUNET_OK != 168 GNUNET_CONFIGURATION_get_value_string ( 169 c, 170 "paivana", 171 "DESTINATION_BASE_URL", 172 &PH_target_server_base_url)) 173 { 174 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 175 "paivana", 176 "DESTINATION_BASE_URL"); 177 GNUNET_SCHEDULER_shutdown (); 178 return; 179 } 180 if (! TALER_is_web_url (PH_target_server_base_url)) 181 { 182 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 183 "paivana", 184 "DESTINATION_BASE_URL", 185 "not a web url"); 186 GNUNET_SCHEDULER_shutdown (); 187 return; 188 } 189 GNUNET_CONFIGURATION_get_value_filename ( 190 c, 191 "paivana", 192 "DESTINATION_UNIXPATH", 193 &PH_target_server_unixpath); 194 { 195 size_t tlen = strlen (PH_target_server_base_url); 196 197 if ( (tlen > 0) && 198 ('/' == PH_target_server_base_url[tlen - 1]) ) 199 PH_target_server_base_url[tlen - 1] = '\0'; 200 } 201 if (! PH_no_check) 202 { 203 if (GNUNET_OK != 204 GNUNET_CONFIGURATION_get_value_string ( 205 c, 206 "paivana", 207 "MERCHANT_BACKEND_URL", 208 &PH_merchant_base_url)) 209 { 210 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 211 "paivana", 212 "MERCHANT_BACKEND_URL"); 213 GNUNET_SCHEDULER_shutdown (); 214 return; 215 } 216 if (! TALER_is_web_url (PH_merchant_base_url)) 217 { 218 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 219 "paivana", 220 "MERCHANT_BACKEND_URL", 221 "not a web url"); 222 GNUNET_SCHEDULER_shutdown (); 223 return; 224 } 225 } 226 { 227 char *merchant_unix_path; 228 229 if (GNUNET_OK == 230 GNUNET_CONFIGURATION_get_value_string ( 231 c, 232 "paivana", 233 "MERCHANT_BACKEND_UNIX_PATH", 234 &merchant_unix_path)) 235 { 236 if (! TALER_MERCHANT_global_set_unixpath (merchant_unix_path)) 237 { 238 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, 239 "paivana", 240 "MERCHANT_BACKEND_UNIX_PATH", 241 "invalid path; ignoring the setting"); 242 } 243 GNUNET_free (merchant_unix_path); 244 } 245 } 246 { 247 char *whitelist; 248 249 if (GNUNET_OK == 250 GNUNET_CONFIGURATION_get_value_string ( 251 c, 252 "paivana", 253 "WHITELIST", 254 &whitelist)) 255 { 256 if (0 != regcomp (&PH_whitelist_ex, 257 whitelist, 258 REG_NOSUB | REG_EXTENDED)) 259 { 260 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 261 "paivana", 262 "WHITELIST", 263 "Invalid regular expression"); 264 GNUNET_free (whitelist); 265 GNUNET_SCHEDULER_shutdown (); 266 return; 267 } 268 PH_have_whitelist_ex = true; 269 GNUNET_free (whitelist); 270 } 271 } 272 273 if (GNUNET_OK != 274 GNUNET_CONFIGURATION_get_value_string ( 275 c, 276 "paivana", 277 "BASE_URL", 278 &PH_base_url)) 279 { 280 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 281 "paivana", 282 "BASE_URL"); 283 } 284 if (NULL != PH_base_url) 285 { 286 if (! TALER_is_web_url (PH_base_url)) 287 { 288 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 289 "paivana", 290 "BASE_URL", 291 "not a web url"); 292 GNUNET_SCHEDULER_shutdown (); 293 return; 294 } 295 if ('/' == PH_base_url[strlen (PH_base_url) - 1]) 296 PH_base_url[strlen (PH_base_url) - 1] = '\0'; 297 } 298 299 if (GNUNET_OK != 300 GNUNET_CONFIGURATION_get_value_string ( 301 c, 302 "paivana", 303 "SECRET", 304 &secret)) 305 { 306 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 307 "paivana", 308 "SECRET"); 309 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, 310 &paivana_secret, 311 sizeof (paivana_secret)); 312 } 313 else 314 { 315 GNUNET_CRYPTO_hash (secret, 316 strlen (secret), 317 &paivana_secret); 318 GNUNET_free (secret); 319 } 320 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 321 NULL); 322 PH_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 323 &ctx_rc); 324 GNUNET_assert (NULL != PH_ctx); 325 if (! PH_no_check) 326 { 327 char *merchant_access_token; 328 char *auth_header; 329 330 if (GNUNET_OK != 331 GNUNET_CONFIGURATION_get_value_string ( 332 c, 333 "paivana", 334 "MERCHANT_ACCESS_TOKEN", 335 &merchant_access_token)) 336 { 337 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 338 "paivana", 339 "MERCHANT_ACCESS_TOKEN"); 340 GNUNET_SCHEDULER_shutdown (); 341 return; 342 } 343 GNUNET_asprintf (&auth_header, 344 "%s: Bearer %s", 345 MHD_HTTP_HEADER_AUTHORIZATION, 346 merchant_access_token); 347 GNUNET_free (merchant_access_token); 348 GNUNET_assert (GNUNET_OK == 349 GNUNET_CURL_append_header (PH_ctx, 350 auth_header)); 351 GNUNET_free (auth_header); 352 } 353 ctx_rc = GNUNET_CURL_gnunet_rc_create (PH_ctx); 354 /* Once templates are done loading, this will 355 start the daemon as well. In -n (no-payment) mode we skip 356 the merchant round-trip entirely. */ 357 if (PH_no_check) 358 { 359 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 360 "Paywall disabled (-n), skipping template load\n"); 361 PAIVANA_HTTPD_serve_requests (); 362 return; 363 } 364 PAIVANA_HTTPD_load_templates (); 365 } 366 367 368 /** 369 * Main function. 370 */ 371 int 372 main (int argc, 373 char *const *argv) 374 { 375 struct GNUNET_GETOPT_CommandLineOption options[] = { 376 GNUNET_GETOPT_option_flag ( 377 'f', 378 "respect-forwarded-headers", 379 gettext_noop ( 380 "trust X-Forwarded-For for the client address (only safe behind a trusted reverse proxy)"), 381 &PH_respect_forwarded_headers), 382 GNUNET_GETOPT_option_flag ( 383 'g', 384 "global-payment", 385 gettext_noop ( 386 "disables per-page payment, useful if a single payment should grant access to the entire site"), 387 &PH_global_cookie), 388 GNUNET_GETOPT_option_flag ( 389 'n', 390 "no-payment", 391 gettext_noop ( 392 "disables payment, useful for testing reverse-proxy only"), 393 &PH_no_check), 394 GNUNET_GETOPT_option_ulong ( 395 'u', 396 "max-upload", 397 "BYTES", 398 gettext_noop ( 399 "maximum request body size to buffer before forwarding (default: 1048576)"), 400 &PH_request_buffer_max), 401 GNUNET_GETOPT_OPTION_END 402 }; 403 enum GNUNET_GenericReturnValue ret; 404 405 ret = GNUNET_PROGRAM_run ( 406 PAIVANA_project_data (), 407 argc, 408 argv, 409 "paivana-httpd", 410 "reverse proxy requesting Taler payment", 411 options, 412 &run, NULL); 413 if (GNUNET_SYSERR == ret) 414 return EXIT_INVALIDARGUMENT; 415 if (GNUNET_NO == ret) 416 return EXIT_SUCCESS; 417 return PH_global_ret; 418 } 419 420 421 /* end of paivana-httpd.c */