paivana-httpd.c (11224B)
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 (&paivana_secret, 310 sizeof (paivana_secret)); 311 } 312 else 313 { 314 GNUNET_CRYPTO_hash (secret, 315 strlen (secret), 316 &paivana_secret); 317 GNUNET_free (secret); 318 } 319 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 320 NULL); 321 PH_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 322 &ctx_rc); 323 GNUNET_assert (NULL != PH_ctx); 324 if (! PH_no_check) 325 { 326 char *merchant_access_token; 327 char *auth_header; 328 329 if (GNUNET_OK != 330 GNUNET_CONFIGURATION_get_value_string ( 331 c, 332 "paivana", 333 "MERCHANT_ACCESS_TOKEN", 334 &merchant_access_token)) 335 { 336 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 337 "paivana", 338 "MERCHANT_ACCESS_TOKEN"); 339 GNUNET_SCHEDULER_shutdown (); 340 return; 341 } 342 GNUNET_asprintf (&auth_header, 343 "%s: Bearer %s", 344 MHD_HTTP_HEADER_AUTHORIZATION, 345 merchant_access_token); 346 GNUNET_free (merchant_access_token); 347 GNUNET_assert (GNUNET_OK == 348 GNUNET_CURL_append_header (PH_ctx, 349 auth_header)); 350 GNUNET_free (auth_header); 351 } 352 ctx_rc = GNUNET_CURL_gnunet_rc_create (PH_ctx); 353 /* Once templates are done loading, this will 354 start the daemon as well. In -n (no-payment) mode we skip 355 the merchant round-trip entirely. */ 356 if (PH_no_check) 357 { 358 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 359 "Paywall disabled (-n), skipping template load\n"); 360 PAIVANA_HTTPD_serve_requests (); 361 return; 362 } 363 PAIVANA_HTTPD_load_templates (); 364 } 365 366 367 /** 368 * Main function. 369 */ 370 int 371 main (int argc, 372 char *const *argv) 373 { 374 struct GNUNET_GETOPT_CommandLineOption options[] = { 375 GNUNET_GETOPT_option_flag ( 376 'f', 377 "respect-forwarded-headers", 378 gettext_noop ( 379 "trust X-Forwarded-For for the client address (only safe behind a trusted reverse proxy)"), 380 &PH_respect_forwarded_headers), 381 GNUNET_GETOPT_option_flag ( 382 'g', 383 "global-payment", 384 gettext_noop ( 385 "disables per-page payment, useful if a single payment should grant access to the entire site"), 386 &PH_global_cookie), 387 GNUNET_GETOPT_option_flag ( 388 'n', 389 "no-payment", 390 gettext_noop ( 391 "disables payment, useful for testing reverse-proxy only"), 392 &PH_no_check), 393 GNUNET_GETOPT_option_ulong ( 394 'u', 395 "max-upload", 396 "BYTES", 397 gettext_noop ( 398 "maximum request body size to buffer before forwarding (default: 1048576)"), 399 &PH_request_buffer_max), 400 GNUNET_GETOPT_OPTION_END 401 }; 402 enum GNUNET_GenericReturnValue ret; 403 404 ret = GNUNET_PROGRAM_run ( 405 PAIVANA_project_data (), 406 argc, 407 argv, 408 "paivana-httpd", 409 "reverse proxy requesting Taler payment", 410 options, 411 &run, NULL); 412 if (GNUNET_SYSERR == ret) 413 return EXIT_INVALIDARGUMENT; 414 if (GNUNET_NO == ret) 415 return EXIT_SUCCESS; 416 return PH_global_ret; 417 } 418 419 420 /* end of paivana-httpd.c */