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