taler-exchange-httpd.c (91822B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2025 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file taler-exchange-httpd.c 18 * @brief Serve the HTTP interface of the exchange 19 * @defgroup request Request handling routines 20 * @author Florian Dold 21 * @author Benedikt Mueller 22 * @author Christian Grothoff 23 */ 24 #include "platform.h" 25 #include <gnunet/gnunet_util_lib.h> 26 #include <jansson.h> 27 #include <microhttpd.h> 28 #include <sched.h> 29 #include <sys/resource.h> 30 #include <limits.h> 31 #include "taler/taler_kyclogic_lib.h" 32 #include "taler/taler_templating_lib.h" 33 #include "taler/taler_mhd_lib.h" 34 #include "taler-exchange-httpd_post-withdraw.h" 35 #include "taler-exchange-httpd_get-aml-OFFICER_PUB-accounts.h" 36 #include \ 37 "taler-exchange-httpd_get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.h" 38 #include "taler-exchange-httpd_post-aml-OFFICER_PUB-decision.h" 39 #include "taler-exchange-httpd_get-aml-OFFICER_PUB-legitimizations.h" 40 #include "taler-exchange-httpd_get-aml-OFFICER_PUB-kyc-statistics-NAMES.h" 41 #include "taler-exchange-httpd_get-aml-OFFICER_PUB-transfers.h" 42 #include "taler-exchange-httpd_get-aml-OFFICER_PUB-measures.h" 43 #include "taler-exchange-httpd_post-auditors-AUDITOR_PUB-H_DENOM_PUB.h" 44 #include "taler-exchange-httpd_post-batch-deposit.h" 45 #include "taler-exchange-httpd_post-blinding-prepare.h" 46 #include "taler-exchange-httpd_get-coins-COIN_PUB-history.h" 47 #include "taler-exchange-httpd_get-config.h" 48 #include "taler-exchange-httpd_get-contracts-CONTRACT_PUB.h" 49 #include "taler-exchange-httpd_get-deposits.h" 50 #include "taler-exchange-httpd_get-keys.h" 51 #include "taler-exchange-httpd_get-kyc-check-H_NORMALIZED_PAYTO.h" 52 #include "taler-exchange-httpd_get-kyc-info-ACCESS_TOKEN.h" 53 #include "taler-exchange-httpd_get-kyc-proof-PROVIDER_NAME.h" 54 #include "taler-exchange-httpd_post-kyc-start-ID.h" 55 #include "taler-exchange-httpd_post-kyc-upload-ID.h" 56 #include "taler-exchange-httpd_post-kyc-wallet.h" 57 #include "taler-exchange-httpd_kyc-webhook.h" 58 #include "taler-exchange-httpd_post-aml-OFFICER_PUB-decision.h" 59 #include "taler-exchange-httpd_management.h" 60 #include "taler-exchange-httpd_post-melt.h" 61 #include "taler-exchange-httpd_get-metrics.h" 62 #include "taler-exchange-httpd_mhd.h" 63 #include "taler-exchange-httpd_post-purses-PURSE_PUB-create.h" 64 #include "taler-exchange-httpd_post-purses-PURSE_PUB-deposit.h" 65 #include "taler-exchange-httpd_get-purses-PURSE_PUB-merge.h" 66 #include "taler-exchange-httpd_delete-purses-PURSE_PUB.h" 67 #include "taler-exchange-httpd_post-purses-PURSE_PUB-merge.h" 68 #include "taler-exchange-httpd_post-recoup-withdraw.h" 69 #include "taler-exchange-httpd_post-recoup-refresh.h" 70 #include "taler-exchange-httpd_post-coins-COIN_PUB-refund.h" 71 #include "taler-exchange-httpd_post-reserves-RESERVE_PUB-attest.h" 72 #include "taler-exchange-httpd_post-reserves-RESERVE_PUB-close.h" 73 #include "taler-exchange-httpd_get-reserves-RESERVE_PUB.h" 74 #include "taler-exchange-httpd_get-reserves-RESERVE_PUB-attest.h" 75 #include "taler-exchange-httpd_get-reserves-RESERVE_PUB-history.h" 76 #include "taler-exchange-httpd_post-reserves-RESERVE_PUB-open.h" 77 #include "taler-exchange-httpd_post-reserves-RESERVE_PUB-purse.h" 78 #include "taler-exchange-httpd_post-reveal-withdraw.h" 79 #include "taler-exchange-httpd_post-reveal-melt.h" 80 #include "taler-exchange-httpd_get-SPA.h" 81 #include "taler-exchange-httpd_get-TERMS.h" 82 #include "taler-exchange-httpd_get-transfers-WTID.h" 83 #include <gnunet/gnunet_mhd_compat.h> 84 #include "exchange-database/get_exists_aml_officer.h" 85 #include "exchange-database/preflight.h" 86 87 /** 88 * Backlog for listen operation on unix domain sockets. 89 */ 90 #define UNIX_BACKLOG 50 91 92 /** 93 * How often will we try to connect to the database before giving up? 94 */ 95 #define MAX_DB_RETRIES 5 96 97 /** 98 * Above what request latency do we start to log? 99 */ 100 #define WARN_LATENCY GNUNET_TIME_relative_multiply ( \ 101 GNUNET_TIME_UNIT_MILLISECONDS, 500) 102 103 int TEH_allow_keys_timetravel; 104 105 /** 106 * Should we allow two HTTPDs to bind to the same port? 107 */ 108 static int allow_address_reuse; 109 110 /** 111 * The exchange's configuration (global) 112 */ 113 const struct GNUNET_CONFIGURATION_Handle *TEH_cfg; 114 115 bool TEH_age_restriction_enabled = false; 116 117 struct TALER_AgeMask TEH_age_restriction_mask = {0}; 118 119 /** 120 * Set to true if we started *any* HTTP daemons. 121 */ 122 static bool have_daemons; 123 124 struct GNUNET_TIME_Relative TEH_max_keys_caching; 125 126 struct GNUNET_TIME_Relative TEH_reserve_closing_delay; 127 128 struct GNUNET_TIME_Relative TEH_default_p2p_expiration; 129 130 struct GNUNET_TIME_Relative TEH_aml_program_timeout; 131 132 struct TALER_MasterPublicKeyP TEH_master_public_key; 133 134 struct TALER_AttributeEncryptionKeyP TEH_attribute_key; 135 136 struct TALER_EXCHANGEDB_PostgresContext *TEH_pg; 137 138 struct TALER_Amount TEH_stefan_abs; 139 140 struct TALER_Amount TEH_stefan_log; 141 142 struct TALER_Amount TEH_tiny_amount; 143 144 float TEH_stefan_lin; 145 146 json_t *TEH_hard_limits; 147 148 json_t *TEH_zero_limits; 149 150 char *TEH_tos_etag; 151 152 char *TEH_shopping_url; 153 154 char *TEH_aml_spa_dir; 155 156 char *TEH_kyc_spa_dir; 157 158 /** 159 * Where to redirect users from "/"? 160 */ 161 static char *toplevel_redirect_url; 162 163 json_t *TEH_global_pdf_form_data; 164 165 char *TEH_currency; 166 167 char *TEH_base_url; 168 169 /** 170 * Default timeout in seconds for HTTP requests. 171 */ 172 static unsigned int connection_timeout = 30; 173 174 /** 175 * -C command-line flag given? 176 */ 177 static int connection_close; 178 179 /** 180 * Allows banks to select a custom UI/UX for certain bank-specific 181 * wallet functions, such as specific texts for buttons based on 182 * legal requirements. 183 */ 184 char *TEH_bank_compliance_language; 185 186 /** 187 * Determines the set of AML forms (and other bank-specific settings) 188 * to be selected for the AML SPA. 189 * legal requirements. 190 */ 191 char *TEH_aml_spa_dialect; 192 193 /** 194 * Option set to #GNUNET_YES if KYC/AML are enabled. 195 */ 196 int TEH_enable_kyc; 197 198 /** 199 * Option set to #GNUNET_YES to disable direct deposits. 200 */ 201 int TEH_disable_direct_deposit; 202 203 /** 204 * Option set to #GNUNET_YES to signal to the merchant 205 * backend that it should swap the terms-of-service and 206 * KYC auth authentication steps in the user experience. 207 */ 208 int TEH_kyc_swap_tos_acceptance; 209 210 /** 211 * -I command-line flag given? 212 */ 213 int TEH_check_invariants_flag; 214 215 /** 216 * True if we should commit suicide once all active 217 * connections are finished. 218 */ 219 bool TEH_suicide; 220 221 /** 222 * Value to return from main() 223 */ 224 static int global_ret; 225 226 /** 227 * Counter for the number of requests this HTTP has processed so far. 228 */ 229 static unsigned long long req_count; 230 231 /** 232 * Counter for the number of open connections. 233 */ 234 static unsigned long long active_connections; 235 236 /** 237 * Limit for the number of requests this HTTP may process before restarting. 238 * (This was added as one way of dealing with unavoidable memory fragmentation 239 * happening slowly over time.) 240 */ 241 static unsigned long long req_max; 242 243 /** 244 * Length of the cspecs array. 245 */ 246 static unsigned int num_cspecs; 247 248 /** 249 * Rendering specs for currencies. 250 */ 251 static struct TALER_CurrencySpecification *cspecs; 252 253 /** 254 * Rendering spec for our currency. 255 */ 256 const struct TALER_CurrencySpecification *TEH_cspec; 257 258 259 /** 260 * Context for all CURL operations (useful to the event loop) 261 */ 262 struct GNUNET_CURL_Context *TEH_curl_ctx; 263 264 /** 265 * Context for integrating #TEH_curl_ctx with the 266 * GNUnet event loop. 267 */ 268 static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc; 269 270 /** 271 * Signature of functions that handle operations on coins. 272 * 273 * @param connection the MHD connection to handle 274 * @param coin_pub the public key of the coin 275 * @param root uploaded JSON data 276 * @return MHD result code 277 */ 278 typedef enum MHD_Result 279 (*CoinOpHandler)(struct MHD_Connection *connection, 280 const struct TALER_CoinSpendPublicKeyP *coin_pub, 281 const json_t *root); 282 283 284 /** 285 * Generate a 404 "not found" reply on @a connection with 286 * the hint @a details. 287 * 288 * @param connection where to send the reply on 289 * @param details details for the error message, can be NULL 290 */ 291 static enum MHD_Result 292 r404 (struct MHD_Connection *connection, 293 const char *details) 294 { 295 return TALER_MHD_reply_with_error (connection, 296 MHD_HTTP_NOT_FOUND, 297 TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, 298 details); 299 } 300 301 302 /** 303 * Handle a "/coins/$COIN_PUB/$OP" POST request. Parses the "coin_pub" 304 * EdDSA key of the coin and demultiplexes based on $OP. 305 * 306 * @param rc request context 307 * @param root uploaded JSON data 308 * @param args array of additional options 309 * @return MHD result code 310 */ 311 static enum MHD_Result 312 handle_post_coins (struct TEH_RequestContext *rc, 313 const json_t *root, 314 const char *const args[2]) 315 { 316 struct TALER_CoinSpendPublicKeyP coin_pub; 317 static const struct 318 { 319 /** 320 * Name of the operation (args[1]) 321 */ 322 const char *op; 323 324 /** 325 * Function to call to perform the operation. 326 */ 327 CoinOpHandler handler; 328 329 } h[] = { 330 #if FIXME_9828 331 { 332 .op = "recoup", 333 .handler = &TEH_handler_recoup 334 }, 335 { 336 .op = "recoup-refresh", 337 .handler = &TEH_handler_recoup_refresh 338 }, 339 #endif 340 { 341 .op = "refund", 342 .handler = &TEH_handler_refund 343 }, 344 { 345 .op = NULL, 346 .handler = NULL 347 }, 348 }; 349 350 if (GNUNET_OK != 351 GNUNET_STRINGS_string_to_data (args[0], 352 strlen (args[0]), 353 &coin_pub, 354 sizeof (coin_pub))) 355 { 356 GNUNET_break_op (0); 357 return TALER_MHD_reply_with_error (rc->connection, 358 MHD_HTTP_BAD_REQUEST, 359 TALER_EC_EXCHANGE_GENERIC_COINS_INVALID_COIN_PUB, 360 args[0]); 361 } 362 for (unsigned int i = 0; NULL != h[i].op; i++) 363 if (0 == strcmp (h[i].op, 364 args[1])) 365 return h[i].handler (rc->connection, 366 &coin_pub, 367 root); 368 return r404 (rc->connection, 369 args[1]); 370 } 371 372 373 /** 374 * Handle a GET "/coins/$COIN_PUB[/$OP]" request. Parses the "coin_pub" 375 * EdDSA key of the coin and demultiplexes based on $OP. 376 * 377 * @param rc request context 378 * @param args array of additional options 379 * @return MHD result code 380 */ 381 static enum MHD_Result 382 handle_get_coins (struct TEH_RequestContext *rc, 383 const char *const args[2]) 384 { 385 struct TALER_CoinSpendPublicKeyP coin_pub; 386 387 if (NULL == args[0]) 388 { 389 return TALER_MHD_reply_with_error (rc->connection, 390 MHD_HTTP_NOT_FOUND, 391 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 392 rc->url); 393 } 394 if (GNUNET_OK != 395 GNUNET_STRINGS_string_to_data (args[0], 396 strlen (args[0]), 397 &coin_pub, 398 sizeof (coin_pub))) 399 { 400 GNUNET_break_op (0); 401 return TALER_MHD_reply_with_error (rc->connection, 402 MHD_HTTP_BAD_REQUEST, 403 TALER_EC_EXCHANGE_GENERIC_COINS_INVALID_COIN_PUB, 404 args[0]); 405 } 406 if (NULL != args[1]) 407 { 408 if (0 == strcmp (args[1], 409 "history")) 410 return TEH_handler_coins_get (rc, 411 &coin_pub); 412 } 413 return TALER_MHD_reply_with_error (rc->connection, 414 MHD_HTTP_NOT_FOUND, 415 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 416 rc->url); 417 } 418 419 420 /** 421 * Signature of functions that handle operations 422 * authorized by AML officers. 423 * 424 * @param rc request context 425 * @param officer_pub the public key of the AML officer 426 * @param root uploaded JSON data 427 * @return MHD result code 428 */ 429 typedef enum MHD_Result 430 (*AmlOpPostHandler)(struct TEH_RequestContext *rc, 431 const struct TALER_AmlOfficerPublicKeyP *officer_pub, 432 const json_t *root); 433 434 435 /** 436 * Handle a "/aml/$OFFICER_PUB/$OP" POST request. Parses the "officer_pub" 437 * EdDSA key of the officer and demultiplexes based on $OP. 438 * 439 * @param rc request context 440 * @param root uploaded JSON data 441 * @param args array of additional options 442 * @return MHD result code 443 */ 444 static enum MHD_Result 445 handle_post_aml (struct TEH_RequestContext *rc, 446 const json_t *root, 447 const char *const args[2]) 448 { 449 struct TALER_AmlOfficerPublicKeyP officer_pub; 450 static const struct 451 { 452 /** 453 * Name of the operation (args[1]) 454 */ 455 const char *op; 456 457 /** 458 * Function to call to perform the operation. 459 */ 460 AmlOpPostHandler handler; 461 462 } h[] = { 463 { 464 .op = "decision", 465 .handler = &TEH_handler_post_aml_decision 466 }, 467 { 468 .op = NULL, 469 .handler = NULL 470 }, 471 }; 472 473 if (GNUNET_OK != 474 GNUNET_STRINGS_string_to_data (args[0], 475 strlen (args[0]), 476 &officer_pub, 477 sizeof (officer_pub))) 478 { 479 GNUNET_break_op (0); 480 return TALER_MHD_reply_with_error (rc->connection, 481 MHD_HTTP_BAD_REQUEST, 482 TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED, 483 args[0]); 484 } 485 486 { 487 enum GNUNET_DB_QueryStatus qs; 488 bool ro; 489 490 qs = TALER_EXCHANGEDB_get_exists_aml_officer (TEH_pg, 491 &officer_pub, 492 &ro); 493 switch (qs) 494 { 495 case GNUNET_DB_STATUS_HARD_ERROR: 496 case GNUNET_DB_STATUS_SOFT_ERROR: 497 GNUNET_break (0); 498 return TALER_MHD_reply_with_error (rc->connection, 499 MHD_HTTP_INTERNAL_SERVER_ERROR, 500 TALER_EC_GENERIC_DB_FETCH_FAILED, 501 NULL); 502 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 503 GNUNET_break_op (0); 504 return TALER_MHD_reply_with_error (rc->connection, 505 MHD_HTTP_FORBIDDEN, 506 TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED, 507 "unknown or inactive"); 508 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 509 break; 510 } 511 if (ro) 512 { 513 GNUNET_break_op (0); 514 return TALER_MHD_reply_with_error (rc->connection, 515 MHD_HTTP_CONFLICT, 516 TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED, 517 "read-only"); 518 } 519 } 520 521 for (unsigned int i = 0; NULL != h[i].op; i++) 522 if (0 == strcmp (h[i].op, 523 args[1])) 524 return h[i].handler (rc, 525 &officer_pub, 526 root); 527 return r404 (rc->connection, 528 args[1]); 529 } 530 531 532 /** 533 * Signature of functions that handle operations 534 * authorized by AML officers. 535 * 536 * @param rc request context 537 * @param officer_pub the public key of the AML officer 538 * @param args remaining arguments 539 * @return MHD result code 540 */ 541 typedef enum MHD_Result 542 (*AmlOpGetHandler)(struct TEH_RequestContext *rc, 543 const struct TALER_AmlOfficerPublicKeyP *officer_pub, 544 const char *const args[]); 545 546 547 /** 548 * Handle a "/aml/$OFFICER_PUB/$OP" GET request. Parses the "officer_pub" 549 * EdDSA key of the officer, checks the authentication signature, and 550 * demultiplexes based on $OP. 551 * 552 * @param rc request context 553 * @param args array of additional options 554 * @return MHD result code 555 */ 556 static enum MHD_Result 557 handle_get_aml (struct TEH_RequestContext *rc, 558 const char *const args[]) 559 { 560 struct TALER_AmlOfficerPublicKeyP officer_pub; 561 static const struct 562 { 563 /** 564 * Name of the operation (args[1]) 565 */ 566 const char *op; 567 568 /** 569 * Function to call to perform the operation. 570 */ 571 AmlOpGetHandler handler; 572 573 } h[] = { 574 { 575 .op = "accounts", 576 .handler = &TEH_handler_aml_accounts_get 577 }, 578 { 579 .op = "attributes", 580 .handler = &TEH_handler_aml_attributes_get 581 }, 582 { 583 .op = "decisions", 584 .handler = &TEH_handler_aml_decisions_get 585 }, 586 { 587 .op = "legitimizations", 588 .handler = &TEH_handler_aml_legitimization_measures_get 589 }, 590 { 591 .op = "kyc-statistics", 592 .handler = &TEH_handler_aml_kyc_statistics_get 593 }, 594 { 595 .op = "measures", 596 .handler = &TEH_handler_aml_measures_get 597 }, 598 { 599 .op = "transfers-credit", 600 .handler = &TEH_handler_aml_transfer_credit_get 601 }, 602 { 603 .op = "transfers-kycauth", 604 .handler = &TEH_handler_aml_transfer_kycauth_get 605 }, 606 { 607 .op = "transfers-debit", 608 .handler = &TEH_handler_aml_transfer_debit_get 609 }, 610 { 611 .op = "wallet-credit", 612 .handler = &TEH_handler_aml_wallet_credit_get 613 }, 614 { 615 .op = NULL, 616 .handler = NULL 617 }, 618 }; 619 620 if (NULL == args[0]) 621 { 622 GNUNET_break_op (0); 623 return TALER_MHD_reply_with_error ( 624 rc->connection, 625 MHD_HTTP_BAD_REQUEST, 626 TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED, 627 "argument missing"); 628 } 629 if (GNUNET_OK != 630 GNUNET_STRINGS_string_to_data (args[0], 631 strlen (args[0]), 632 &officer_pub, 633 sizeof (officer_pub))) 634 { 635 GNUNET_break_op (0); 636 return TALER_MHD_reply_with_error ( 637 rc->connection, 638 MHD_HTTP_BAD_REQUEST, 639 TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED, 640 args[0]); 641 } 642 if (NULL == args[1]) 643 { 644 GNUNET_break_op (0); 645 return TALER_MHD_reply_with_error ( 646 rc->connection, 647 MHD_HTTP_NOT_FOUND, 648 TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS, 649 "AML GET operations must specify an operation identifier"); 650 } 651 652 { 653 const char *sig_hdr; 654 struct TALER_AmlOfficerSignatureP officer_sig; 655 656 sig_hdr = MHD_lookup_connection_value ( 657 rc->connection, 658 MHD_HEADER_KIND, 659 TALER_AML_OFFICER_SIGNATURE_HEADER); 660 if ( (NULL == sig_hdr) || 661 (GNUNET_OK != 662 GNUNET_STRINGS_string_to_data (sig_hdr, 663 strlen (sig_hdr), 664 &officer_sig, 665 sizeof (officer_sig))) ) 666 { 667 GNUNET_break_op (0); 668 return TALER_MHD_reply_with_error ( 669 rc->connection, 670 MHD_HTTP_BAD_REQUEST, 671 TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, 672 TALER_AML_OFFICER_SIGNATURE_HEADER); 673 } 674 if (GNUNET_OK != 675 TALER_officer_aml_query_verify (&officer_pub, 676 &officer_sig)) 677 { 678 GNUNET_break_op (0); 679 return TALER_MHD_reply_with_error ( 680 rc->connection, 681 MHD_HTTP_FORBIDDEN, 682 TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID, 683 sig_hdr); 684 } 685 TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; 686 } 687 688 { 689 enum GNUNET_DB_QueryStatus qs; 690 bool ro; 691 692 qs = TALER_EXCHANGEDB_get_exists_aml_officer (TEH_pg, 693 &officer_pub, 694 &ro); 695 switch (qs) 696 { 697 case GNUNET_DB_STATUS_HARD_ERROR: 698 case GNUNET_DB_STATUS_SOFT_ERROR: 699 GNUNET_break (0); 700 return TALER_MHD_reply_with_error (rc->connection, 701 MHD_HTTP_INTERNAL_SERVER_ERROR, 702 TALER_EC_GENERIC_DB_FETCH_FAILED, 703 NULL); 704 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 705 return TALER_MHD_reply_with_error (rc->connection, 706 MHD_HTTP_FORBIDDEN, 707 TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED, 708 NULL); 709 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 710 break; 711 } 712 } 713 for (unsigned int i = 0; NULL != h[i].op; i++) 714 if (0 == strcmp (h[i].op, 715 args[1])) 716 return h[i].handler (rc, 717 &officer_pub, 718 &args[2]); 719 return r404 (rc->connection, 720 args[1]); 721 } 722 723 724 /** 725 * Signature of functions that handle operations on reserves. 726 * 727 * @param rc request context 728 * @param reserve_pub the public key of the reserve 729 * @param root uploaded JSON data 730 * @return MHD result code 731 */ 732 typedef enum MHD_Result 733 (*ReserveOpHandler)(struct TEH_RequestContext *rc, 734 const struct TALER_ReservePublicKeyP *reserve_pub, 735 const json_t *root); 736 737 738 /** 739 * Handle a "/reserves/$RESERVE_PUB/$OP" POST request. Parses the "reserve_pub" 740 * EdDSA key of the reserve and demultiplexes based on $OP. 741 * 742 * @param rc request context 743 * @param root uploaded JSON data 744 * @param args array of additional options 745 * @return MHD result code 746 */ 747 static enum MHD_Result 748 handle_post_reserves (struct TEH_RequestContext *rc, 749 const json_t *root, 750 const char *const args[2]) 751 { 752 struct TALER_ReservePublicKeyP reserve_pub; 753 static const struct 754 { 755 /** 756 * Name of the operation (args[1]) 757 */ 758 const char *op; 759 760 /** 761 * Function to call to perform the operation. 762 */ 763 ReserveOpHandler handler; 764 765 } h[] = { 766 { 767 .op = "purse", 768 .handler = &TEH_handler_reserves_purse 769 }, 770 { 771 .op = "open", 772 .handler = &TEH_handler_reserves_open 773 }, 774 { 775 .op = "close", 776 .handler = &TEH_handler_reserves_close 777 }, 778 { 779 .op = "attest", 780 .handler = &TEH_handler_reserves_attest 781 }, 782 { 783 .op = NULL, 784 .handler = NULL 785 }, 786 }; 787 788 if (GNUNET_OK != 789 GNUNET_STRINGS_string_to_data (args[0], 790 strlen (args[0]), 791 &reserve_pub, 792 sizeof (reserve_pub))) 793 { 794 GNUNET_break_op (0); 795 return TALER_MHD_reply_with_error (rc->connection, 796 MHD_HTTP_BAD_REQUEST, 797 TALER_EC_GENERIC_RESERVE_PUB_MALFORMED, 798 args[0]); 799 } 800 for (unsigned int i = 0; NULL != h[i].op; i++) 801 if (0 == strcmp (h[i].op, 802 args[1])) 803 return h[i].handler (rc, 804 &reserve_pub, 805 root); 806 return r404 (rc->connection, 807 args[1]); 808 } 809 810 811 /** 812 * Signature of functions that handle GET operations on reserves. 813 * 814 * @param rc request context 815 * @param reserve_pub the public key of the reserve 816 * @return MHD result code 817 */ 818 typedef enum MHD_Result 819 (*ReserveGetOpHandler)(struct TEH_RequestContext *rc, 820 const struct TALER_ReservePublicKeyP *reserve_pub); 821 822 823 /** 824 * Handle a "GET /reserves/$RESERVE_PUB[/$OP]" request. Parses the "reserve_pub" 825 * EdDSA key of the reserve and demultiplexes based on $OP. 826 * 827 * @param rc request context 828 * @param args NULL-terminated array of additional options, zero, one or two 829 * @return MHD result code 830 */ 831 static enum MHD_Result 832 handle_get_reserves (struct TEH_RequestContext *rc, 833 const char *const args[]) 834 { 835 struct TALER_ReservePublicKeyP reserve_pub; 836 static const struct 837 { 838 /** 839 * Name of the operation (args[1]), optional 840 */ 841 const char *op; 842 843 /** 844 * Function to call to perform the operation. 845 */ 846 ReserveGetOpHandler handler; 847 848 } h[] = { 849 { 850 .op = NULL, 851 .handler = &TEH_handler_reserves_get 852 }, 853 { 854 .op = "history", 855 .handler = &TEH_handler_reserves_history 856 }, 857 { 858 .op = "attest", 859 .handler = &TEH_handler_get_reserves_attest 860 }, 861 { 862 .op = NULL, 863 .handler = NULL 864 }, 865 }; 866 867 if ( (NULL == args[0]) || 868 (GNUNET_OK != 869 GNUNET_STRINGS_string_to_data (args[0], 870 strlen (args[0]), 871 &reserve_pub, 872 sizeof (reserve_pub))) ) 873 { 874 GNUNET_break_op (0); 875 return TALER_MHD_reply_with_error (rc->connection, 876 MHD_HTTP_BAD_REQUEST, 877 TALER_EC_GENERIC_RESERVE_PUB_MALFORMED, 878 args[0]); 879 } 880 for (unsigned int i = 0; NULL != h[i].handler; i++) 881 { 882 if ( ( (NULL == args[1]) && 883 (NULL == h[i].op) ) || 884 ( (NULL != args[1]) && 885 (NULL != h[i].op) && 886 (0 == strcmp (h[i].op, 887 args[1])) ) ) 888 return h[i].handler (rc, 889 &reserve_pub); 890 } 891 return r404 (rc->connection, 892 args[1]); 893 } 894 895 896 /** 897 * Signature of functions that handle operations on purses. 898 * 899 * @param rc request handle 900 * @param purse_pub the public key of the purse 901 * @param root uploaded JSON data 902 * @return MHD result code 903 */ 904 typedef enum MHD_Result 905 (*PurseOpHandler)(struct TEH_RequestContext *rc, 906 const struct TALER_PurseContractPublicKeyP *purse_pub, 907 const json_t *root); 908 909 910 /** 911 * Handle a "/purses/$RESERVE_PUB/$OP" POST request. Parses the "purse_pub" 912 * EdDSA key of the purse and demultiplexes based on $OP. 913 * 914 * @param rc request context 915 * @param root uploaded JSON data 916 * @param args array of additional options 917 * @return MHD result code 918 */ 919 static enum MHD_Result 920 handle_post_purses (struct TEH_RequestContext *rc, 921 const json_t *root, 922 const char *const args[2]) 923 { 924 struct TALER_PurseContractPublicKeyP purse_pub; 925 static const struct 926 { 927 /** 928 * Name of the operation (args[1]) 929 */ 930 const char *op; 931 932 /** 933 * Function to call to perform the operation. 934 */ 935 PurseOpHandler handler; 936 937 } h[] = { 938 { 939 .op = "create", 940 .handler = &TEH_handler_purses_create 941 }, 942 { 943 .op = "deposit", 944 .handler = &TEH_handler_purses_deposit 945 }, 946 { 947 .op = "merge", 948 .handler = &TEH_handler_purses_merge 949 }, 950 { 951 .op = NULL, 952 .handler = NULL 953 }, 954 }; 955 956 if (GNUNET_OK != 957 GNUNET_STRINGS_string_to_data (args[0], 958 strlen (args[0]), 959 &purse_pub, 960 sizeof (purse_pub))) 961 { 962 GNUNET_break_op (0); 963 return TALER_MHD_reply_with_error (rc->connection, 964 MHD_HTTP_BAD_REQUEST, 965 TALER_EC_EXCHANGE_GENERIC_PURSE_PUB_MALFORMED, 966 args[0]); 967 } 968 for (unsigned int i = 0; NULL != h[i].op; i++) 969 if (0 == strcmp (h[i].op, 970 args[1])) 971 return h[i].handler (rc, 972 &purse_pub, 973 root); 974 return r404 (rc->connection, 975 args[1]); 976 } 977 978 979 /** 980 * Increments our request counter and checks if this 981 * process should commit suicide. 982 */ 983 static void 984 check_suicide (void) 985 { 986 pid_t chld; 987 unsigned long long cnt; 988 989 cnt = req_count++; 990 if (req_max != cnt) 991 return; 992 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 993 "Restarting exchange service after %llu requests\n", 994 cnt); 995 /* Stop accepting new connections */ 996 TALER_MHD_daemons_quiesce (); 997 /* Continue handling existing connections in child, 998 so that this process can die and be replaced by 999 systemd with a fresh one */ 1000 chld = fork (); 1001 if (-1 == chld) 1002 { 1003 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 1004 "fork"); 1005 _exit (1); 1006 } 1007 if (0 != chld) 1008 { 1009 /* We are the parent, instant-suicide! */ 1010 _exit (0); 1011 } 1012 TEH_suicide = true; 1013 } 1014 1015 1016 /** 1017 * Function called whenever MHD is done with a request. If the 1018 * request was a POST, we may have stored a `struct Buffer *` in the 1019 * @a con_cls that might still need to be cleaned up. Call the 1020 * respective function to free the memory. 1021 * 1022 * @param cls client-defined closure 1023 * @param connection connection handle 1024 * @param con_cls value as set by the last call to 1025 * the #MHD_AccessHandlerCallback 1026 * @param toe reason for request termination 1027 * @see #MHD_OPTION_NOTIFY_COMPLETED 1028 * @ingroup request 1029 */ 1030 static void 1031 handle_mhd_completion_callback (void *cls, 1032 struct MHD_Connection *connection, 1033 void **con_cls, 1034 enum MHD_RequestTerminationCode toe) 1035 { 1036 struct TEH_RequestContext *rc = *con_cls; 1037 struct GNUNET_AsyncScopeSave old_scope; 1038 1039 (void) cls; 1040 if (NULL == rc) 1041 return; 1042 GNUNET_async_scope_enter (&rc->async_scope_id, 1043 &old_scope); 1044 check_suicide (); 1045 TEH_check_invariants (); 1046 if (NULL != rc->rh_cleaner) 1047 rc->rh_cleaner (rc); 1048 if (NULL != rc->root) 1049 { 1050 json_decref (rc->root); 1051 rc->root = NULL; 1052 } 1053 TEH_check_invariants (); 1054 { 1055 #if MHD_VERSION >= 0x00097304 1056 const union MHD_ConnectionInfo *ci; 1057 unsigned int http_status = 0; 1058 1059 ci = MHD_get_connection_info (connection, 1060 MHD_CONNECTION_INFO_HTTP_STATUS); 1061 if (NULL != ci) 1062 http_status = ci->http_status; 1063 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1064 "Request for `%s' completed with HTTP status %u (%d)\n", 1065 rc->url, 1066 http_status, 1067 toe); 1068 #else 1069 (void) connection; 1070 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1071 "Request for `%s' completed (%d)\n", 1072 rc->url, 1073 toe); 1074 #endif 1075 } 1076 1077 TALER_MHD_parse_post_cleanup_callback (rc->opaque_post_parsing_context); 1078 /* Sanity-check that we didn't leave any transactions hanging */ 1079 GNUNET_break (GNUNET_OK == 1080 TALER_EXCHANGEDB_preflight (TEH_pg)); 1081 { 1082 struct GNUNET_TIME_Relative latency; 1083 1084 latency = GNUNET_TIME_absolute_get_duration (rc->start_time); 1085 if (latency.rel_value_us > 1086 WARN_LATENCY.rel_value_us) 1087 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1088 "Request for `%s' took %s\n", 1089 rc->url, 1090 GNUNET_STRINGS_relative_time_to_string (latency, 1091 GNUNET_YES)); 1092 } 1093 GNUNET_free (rc); 1094 *con_cls = NULL; 1095 GNUNET_async_scope_restore (&old_scope); 1096 } 1097 1098 1099 /** 1100 * We found a request handler responsible for handling a request. Parse the 1101 * @a upload_data (if applicable) and the @a url and call the 1102 * handler. 1103 * 1104 * @param rc request context 1105 * @param url rest of the URL to parse 1106 * @param upload_data upload data to parse (if available) 1107 * @param[in,out] upload_data_size number of bytes in @a upload_data 1108 * @return MHD result code 1109 */ 1110 static enum MHD_Result 1111 proceed_with_handler (struct TEH_RequestContext *rc, 1112 const char *url, 1113 const char *upload_data, 1114 size_t *upload_data_size) 1115 { 1116 const struct TEH_RequestHandler *rh = rc->rh; 1117 const char *args[rh->nargs + 2]; 1118 size_t ulen = strlen (url) + 1; 1119 enum MHD_Result ret; 1120 1121 /* We do check for "ulen" here, because we'll later stack-allocate a buffer 1122 of that size and don't want to enable malicious clients to cause us 1123 huge stack allocations. */ 1124 if (ulen > 512) 1125 { 1126 /* 512 is simply "big enough", as it is bigger than "6 * 54", 1127 which is the longest URL format we ever get (for 1128 /deposits/). The value should be adjusted if we ever define protocol 1129 endpoints with plausibly longer inputs. */ 1130 GNUNET_break_op (0); 1131 return TALER_MHD_reply_with_error (rc->connection, 1132 MHD_HTTP_URI_TOO_LONG, 1133 TALER_EC_GENERIC_URI_TOO_LONG, 1134 url); 1135 } 1136 1137 /* All POST endpoints come with a body in JSON format. So we parse 1138 the JSON here. */ 1139 if ( (0 == strcasecmp (rh->method, 1140 MHD_HTTP_METHOD_POST)) && 1141 (NULL == rc->root) ) 1142 { 1143 enum GNUNET_GenericReturnValue res; 1144 1145 res = TALER_MHD_parse_post_json (rc->connection, 1146 &rc->opaque_post_parsing_context, 1147 upload_data, 1148 upload_data_size, 1149 &rc->root); 1150 if (GNUNET_SYSERR == res) 1151 { 1152 GNUNET_assert (NULL == rc->root); 1153 return MHD_NO; /* bad upload, could not even generate error */ 1154 } 1155 if ( (GNUNET_NO == res) || 1156 (NULL == rc->root) ) 1157 { 1158 GNUNET_assert (NULL == rc->root); 1159 return MHD_YES; /* so far incomplete upload or parser error */ 1160 } 1161 } 1162 1163 { 1164 char d[ulen]; 1165 unsigned int i; 1166 char *sp; 1167 1168 /* Parse command-line arguments */ 1169 /* make a copy of 'url' because 'strtok_r()' will modify */ 1170 GNUNET_memcpy (d, 1171 url, 1172 ulen); 1173 i = 0; 1174 args[i++] = strtok_r (d, "/", &sp); 1175 while ( (NULL != args[i - 1]) && 1176 (i <= rh->nargs + 1) ) 1177 args[i++] = strtok_r (NULL, "/", &sp); 1178 /* make sure above loop ran nicely until completion, and also 1179 that there is no excess data in 'd' afterwards */ 1180 if ( ( (rh->nargs_is_upper_bound) && 1181 (i - 1 > rh->nargs) ) || 1182 ( (! rh->nargs_is_upper_bound) && 1183 (i - 1 != rh->nargs) ) ) 1184 { 1185 char emsg[128 + 512]; 1186 1187 GNUNET_snprintf (emsg, 1188 sizeof (emsg), 1189 "Got %u+/%u segments for `%s' request (`%s')", 1190 i - 1, 1191 rh->nargs, 1192 rh->url, 1193 url); 1194 GNUNET_break_op (0); 1195 return TALER_MHD_reply_with_error (rc->connection, 1196 MHD_HTTP_NOT_FOUND, 1197 TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS, 1198 emsg); 1199 } 1200 GNUNET_assert (NULL == args[i - 1]); 1201 1202 /* Above logic ensures that 'root' is exactly non-NULL for POST operations, 1203 so we test for 'root' to decide which handler to invoke. */ 1204 if (0 == strcasecmp (rh->method, 1205 MHD_HTTP_METHOD_POST)) 1206 ret = rh->handler.post (rc, 1207 rc->root, 1208 args); 1209 else if (0 == strcasecmp (rh->method, 1210 MHD_HTTP_METHOD_DELETE)) 1211 ret = rh->handler.delete (rc, 1212 args); 1213 else /* Only GET left */ 1214 ret = rh->handler.get (rc, 1215 args); 1216 } 1217 return ret; 1218 } 1219 1220 1221 /** 1222 * Handle a "/seed" request. 1223 * 1224 * @param rc request context 1225 * @param args array of additional options (must be empty for this function) 1226 * @return MHD result code 1227 */ 1228 static enum MHD_Result 1229 handler_seed (struct TEH_RequestContext *rc, 1230 const char *const args[]) 1231 { 1232 #define SEED_SIZE 32 1233 char *body; 1234 enum MHD_Result ret; 1235 struct MHD_Response *resp; 1236 1237 (void) args; 1238 body = malloc (SEED_SIZE); /* must use malloc(), because MHD will use free() */ 1239 if (NULL == body) 1240 return MHD_NO; 1241 GNUNET_CRYPTO_random_block (body, 1242 SEED_SIZE); 1243 resp = MHD_create_response_from_buffer (SEED_SIZE, 1244 body, 1245 MHD_RESPMEM_MUST_FREE); 1246 TALER_MHD_add_global_headers (resp, 1247 false); 1248 ret = MHD_queue_response (rc->connection, 1249 MHD_HTTP_OK, 1250 resp); 1251 GNUNET_break (MHD_YES == ret); 1252 MHD_destroy_response (resp); 1253 return ret; 1254 #undef SEED_SIZE 1255 } 1256 1257 1258 /** 1259 * Signature of functions that handle simple 1260 * POST operations for the management API. 1261 * 1262 * @param connection the MHD connection to handle 1263 * @param root uploaded JSON data 1264 * @return MHD result code 1265 */ 1266 typedef enum MHD_Result 1267 (*ManagementPostHandler)( 1268 struct MHD_Connection *connection, 1269 const json_t *root); 1270 1271 1272 /** 1273 * Handle POST "/management/..." requests. 1274 * 1275 * @param rc request context 1276 * @param root uploaded JSON data 1277 * @param args array of additional options 1278 * @return MHD result code 1279 */ 1280 static enum MHD_Result 1281 handle_post_management (struct TEH_RequestContext *rc, 1282 const json_t *root, 1283 const char *const args[]) 1284 { 1285 static const struct 1286 { 1287 const char *arg0; 1288 const char *arg1; 1289 ManagementPostHandler handler; 1290 } plain_posts[] = { 1291 { 1292 .arg0 = "keys", 1293 .handler = &TEH_handler_management_post_keys 1294 }, 1295 { 1296 .arg0 = "wire", 1297 .handler = &TEH_handler_management_post_wire 1298 }, 1299 { 1300 .arg0 = "wire", 1301 .arg1 = "disable", 1302 .handler = &TEH_handler_management_post_wire_disable 1303 }, 1304 { 1305 .arg0 = "wire-fee", 1306 .handler = &TEH_handler_management_post_wire_fees 1307 }, 1308 { 1309 .arg0 = "global-fee", 1310 .handler = &TEH_handler_management_post_global_fees 1311 }, 1312 { 1313 .arg0 = "drain", 1314 .handler = &TEH_handler_management_post_drain 1315 }, 1316 { 1317 .arg0 = "aml-officers", 1318 .handler = &TEH_handler_management_aml_officers 1319 }, 1320 { 1321 .arg0 = "partners", 1322 .handler = &TEH_handler_management_partners 1323 }, 1324 { 1325 NULL, 1326 NULL, 1327 NULL 1328 } 1329 }; 1330 if (NULL == args[0]) 1331 { 1332 GNUNET_break_op (0); 1333 return r404 (rc->connection, 1334 "/management"); 1335 } 1336 if (0 == strcmp (args[0], 1337 "auditors")) 1338 { 1339 struct TALER_AuditorPublicKeyP auditor_pub; 1340 1341 if (NULL == args[1]) 1342 return TEH_handler_management_auditors (rc->connection, 1343 root); 1344 if ( (NULL == args[1]) || 1345 (NULL == args[2]) || 1346 (0 != strcmp (args[2], 1347 "disable")) || 1348 (NULL != args[3]) ) 1349 return r404 (rc->connection, 1350 "/management/auditors/$AUDITOR_PUB/disable"); 1351 if (GNUNET_OK != 1352 GNUNET_STRINGS_string_to_data (args[1], 1353 strlen (args[1]), 1354 &auditor_pub, 1355 sizeof (auditor_pub))) 1356 { 1357 GNUNET_break_op (0); 1358 return TALER_MHD_reply_with_error (rc->connection, 1359 MHD_HTTP_BAD_REQUEST, 1360 TALER_EC_GENERIC_PARAMETER_MALFORMED, 1361 args[1]); 1362 } 1363 return TEH_handler_management_auditors_AP_disable (rc->connection, 1364 &auditor_pub, 1365 root); 1366 } 1367 if (0 == strcmp (args[0], 1368 "denominations")) 1369 { 1370 struct TALER_DenominationHashP h_denom_pub; 1371 1372 if ( (NULL == args[0]) || 1373 (NULL == args[1]) || 1374 (NULL == args[2]) || 1375 (0 != strcmp (args[2], 1376 "revoke")) || 1377 (NULL != args[3]) ) 1378 return r404 (rc->connection, 1379 "/management/denominations/$HDP/revoke"); 1380 if (GNUNET_OK != 1381 GNUNET_STRINGS_string_to_data (args[1], 1382 strlen (args[1]), 1383 &h_denom_pub, 1384 sizeof (h_denom_pub))) 1385 { 1386 GNUNET_break_op (0); 1387 return TALER_MHD_reply_with_error (rc->connection, 1388 MHD_HTTP_BAD_REQUEST, 1389 TALER_EC_GENERIC_PARAMETER_MALFORMED, 1390 args[1]); 1391 } 1392 return TEH_handler_management_denominations_HDP_revoke (rc->connection, 1393 &h_denom_pub, 1394 root); 1395 } 1396 if (0 == strcmp (args[0], 1397 "signkeys")) 1398 { 1399 struct TALER_ExchangePublicKeyP exchange_pub; 1400 1401 if ( (NULL == args[0]) || 1402 (NULL == args[1]) || 1403 (NULL == args[2]) || 1404 (0 != strcmp (args[2], 1405 "revoke")) || 1406 (NULL != args[3]) ) 1407 return r404 (rc->connection, 1408 "/management/signkeys/$HDP/revoke"); 1409 if (GNUNET_OK != 1410 GNUNET_STRINGS_string_to_data (args[1], 1411 strlen (args[1]), 1412 &exchange_pub, 1413 sizeof (exchange_pub))) 1414 { 1415 GNUNET_break_op (0); 1416 return TALER_MHD_reply_with_error (rc->connection, 1417 MHD_HTTP_BAD_REQUEST, 1418 TALER_EC_GENERIC_PARAMETER_MALFORMED, 1419 args[1]); 1420 } 1421 return TEH_handler_management_signkeys_EP_revoke (rc->connection, 1422 &exchange_pub, 1423 root); 1424 } 1425 for (unsigned int i = 0; 1426 NULL != plain_posts[i].handler; 1427 i++) 1428 { 1429 if (0 == strcmp (args[0], 1430 plain_posts[i].arg0)) 1431 { 1432 if ( ( (NULL == args[1]) && 1433 (NULL == plain_posts[i].arg1) ) || 1434 ( (NULL != args[1]) && 1435 (NULL != plain_posts[i].arg1) && 1436 (0 == strcmp (args[1], 1437 plain_posts[i].arg1)) ) ) 1438 return plain_posts[i].handler (rc->connection, 1439 root); 1440 } 1441 } 1442 GNUNET_break_op (0); 1443 return r404 (rc->connection, 1444 "/management/*"); 1445 } 1446 1447 1448 /** 1449 * Handle a GET "/management" request. 1450 * 1451 * @param rc request context 1452 * @param args array of additional options (must be [0] == "keys") 1453 * @return MHD result code 1454 */ 1455 static enum MHD_Result 1456 handle_get_management (struct TEH_RequestContext *rc, 1457 const char *const args[2]) 1458 { 1459 if ( (NULL != args[0]) && 1460 (0 == strcmp (args[0], 1461 "keys")) && 1462 (NULL == args[1]) ) 1463 { 1464 return TEH_keys_management_get_keys_handler (rc->rh, 1465 rc->connection); 1466 } 1467 GNUNET_break_op (0); 1468 return r404 (rc->connection, 1469 "/management/*"); 1470 } 1471 1472 1473 /** 1474 * Handle POST "/auditors/..." requests. 1475 * 1476 * @param rc request context 1477 * @param root uploaded JSON data 1478 * @param args array of additional options 1479 * @return MHD result code 1480 */ 1481 static enum MHD_Result 1482 handle_post_auditors (struct TEH_RequestContext *rc, 1483 const json_t *root, 1484 const char *const args[]) 1485 { 1486 struct TALER_AuditorPublicKeyP auditor_pub; 1487 struct TALER_DenominationHashP h_denom_pub; 1488 1489 if ( (NULL == args[0]) || 1490 (NULL == args[1]) || 1491 (NULL != args[2]) ) 1492 { 1493 GNUNET_break_op (0); 1494 return r404 (rc->connection, 1495 "/auditors/$AUDITOR_PUB/$H_DENOM_PUB"); 1496 } 1497 1498 if (GNUNET_OK != 1499 GNUNET_STRINGS_string_to_data (args[0], 1500 strlen (args[0]), 1501 &auditor_pub, 1502 sizeof (auditor_pub))) 1503 { 1504 GNUNET_break_op (0); 1505 return TALER_MHD_reply_with_error (rc->connection, 1506 MHD_HTTP_BAD_REQUEST, 1507 TALER_EC_GENERIC_PARAMETER_MALFORMED, 1508 args[0]); 1509 } 1510 if (GNUNET_OK != 1511 GNUNET_STRINGS_string_to_data (args[1], 1512 strlen (args[1]), 1513 &h_denom_pub, 1514 sizeof (h_denom_pub))) 1515 { 1516 GNUNET_break_op (0); 1517 return TALER_MHD_reply_with_error (rc->connection, 1518 MHD_HTTP_BAD_REQUEST, 1519 TALER_EC_GENERIC_PARAMETER_MALFORMED, 1520 args[1]); 1521 } 1522 return TEH_handler_auditors (rc->connection, 1523 &auditor_pub, 1524 &h_denom_pub, 1525 root); 1526 } 1527 1528 1529 /** 1530 * Generates the response for "/", redirecting the 1531 * client to the ``toplevel_redirect_url``. 1532 * 1533 * @param rc request context 1534 * @param args remaining arguments (should be empty) 1535 * @return MHD result code 1536 */ 1537 static enum MHD_Result 1538 toplevel_redirect (struct TEH_RequestContext *rc, 1539 const char *const args[]) 1540 { 1541 char *text; 1542 struct MHD_Response *response; 1543 1544 GNUNET_asprintf (&text, 1545 "Redirecting to %s", 1546 toplevel_redirect_url); 1547 response = MHD_create_response_from_buffer (strlen (text), 1548 text, 1549 MHD_RESPMEM_MUST_COPY); 1550 GNUNET_free (text); 1551 if (NULL == response) 1552 { 1553 GNUNET_break (0); 1554 return MHD_NO; 1555 } 1556 TALER_MHD_add_global_headers (response, 1557 true); 1558 GNUNET_break (MHD_YES == 1559 MHD_add_response_header (response, 1560 MHD_HTTP_HEADER_CONTENT_TYPE, 1561 "text/plain")); 1562 if (MHD_NO == 1563 MHD_add_response_header (response, 1564 MHD_HTTP_HEADER_LOCATION, 1565 toplevel_redirect_url)) 1566 { 1567 GNUNET_break (0); 1568 MHD_destroy_response (response); 1569 return MHD_NO; 1570 } 1571 1572 { 1573 enum MHD_Result ret; 1574 1575 ret = MHD_queue_response (rc->connection, 1576 MHD_HTTP_FOUND, 1577 response); 1578 MHD_destroy_response (response); 1579 return ret; 1580 } 1581 } 1582 1583 1584 /** 1585 * Handle incoming HTTP request. 1586 * 1587 * @param cls closure for MHD daemon (unused) 1588 * @param connection the connection 1589 * @param url the requested url 1590 * @param method the method (POST, GET, ...) 1591 * @param version HTTP version (ignored) 1592 * @param upload_data request data 1593 * @param upload_data_size size of @a upload_data in bytes 1594 * @param con_cls closure for request (a `struct TEH_RequestContext *`) 1595 * @return MHD result code 1596 */ 1597 static enum MHD_Result 1598 handle_mhd_request (void *cls, 1599 struct MHD_Connection *connection, 1600 const char *url, 1601 const char *method, 1602 const char *version, 1603 const char *upload_data, 1604 size_t *upload_data_size, 1605 void **con_cls) 1606 { 1607 static struct TEH_RequestHandler handlers[] = { 1608 /* /robots.txt: disallow everything */ 1609 { 1610 .url = "robots.txt", 1611 .method = MHD_HTTP_METHOD_GET, 1612 .handler.get = &TEH_handler_static_response, 1613 .mime_type = "text/plain", 1614 .data = "User-agent: *\nDisallow: /\n", 1615 .response_code = MHD_HTTP_OK 1616 }, 1617 /* Landing page, redirect to toplevel_redirect_url */ 1618 { 1619 .url = "", 1620 .method = MHD_HTTP_METHOD_GET, 1621 .handler.get = &toplevel_redirect 1622 }, 1623 /* AGPL licensing page, redirect to source. As per the AGPL-license, every 1624 deployment is required to offer the user a download of the source of 1625 the actual deployment. We make this easy by including a redirect to the 1626 source here. */ 1627 { 1628 .url = "agpl", 1629 .method = MHD_HTTP_METHOD_GET, 1630 .handler.get = &TEH_handler_agpl_redirect 1631 }, 1632 { 1633 .url = "seed", 1634 .method = MHD_HTTP_METHOD_GET, 1635 .handler.get = &handler_seed 1636 }, 1637 /* Configuration */ 1638 { 1639 .url = "config", 1640 .method = MHD_HTTP_METHOD_GET, 1641 .handler.get = &TEH_handler_config 1642 }, 1643 /* Performance metrics */ 1644 { 1645 .url = "metrics", 1646 .method = MHD_HTTP_METHOD_GET, 1647 .handler.get = &TEH_handler_metrics 1648 }, 1649 /* Terms of service */ 1650 { 1651 .url = "terms", 1652 .method = MHD_HTTP_METHOD_GET, 1653 .handler.get = &TEH_handler_terms 1654 }, 1655 /* Privacy policy */ 1656 { 1657 .url = "privacy", 1658 .method = MHD_HTTP_METHOD_GET, 1659 .handler.get = &TEH_handler_privacy 1660 }, 1661 /* Return key material and fundamental properties for this exchange */ 1662 { 1663 .url = "keys", 1664 .method = MHD_HTTP_METHOD_GET, 1665 .handler.get = &TEH_keys_get_handler, 1666 }, 1667 { 1668 .url = "batch-deposit", 1669 .method = MHD_HTTP_METHOD_POST, 1670 .handler.post = &TEH_handler_batch_deposit, 1671 .nargs = 0 1672 }, 1673 /* request R's input for Clause-Schnorr signatures in batches */ 1674 { 1675 .url = "blinding-prepare", 1676 .method = MHD_HTTP_METHOD_POST, 1677 .handler.post = &TEH_handler_blinding_prepare, 1678 .nargs = 0 1679 }, 1680 /* withdraw request, available since v26 of the API */ 1681 { 1682 .url = "withdraw", 1683 .method = MHD_HTTP_METHOD_POST, 1684 .handler.post = &TEH_handler_withdraw, 1685 .nargs = 0 1686 }, 1687 { 1688 .url = "reserves", 1689 .method = MHD_HTTP_METHOD_GET, 1690 .handler.get = &handle_get_reserves, 1691 .nargs = 2, 1692 .nargs_is_upper_bound = true 1693 }, 1694 { 1695 .url = "reserves", 1696 .method = MHD_HTTP_METHOD_POST, 1697 .handler.post = &handle_post_reserves, 1698 .nargs = 2 1699 }, 1700 /* reveal-withdraw operation, introduced with v26 */ 1701 { 1702 .url = "reveal-withdraw", 1703 .method = MHD_HTTP_METHOD_POST, 1704 .handler.post = &TEH_handler_reveal_withdraw, 1705 .nargs = 0 1706 }, 1707 /* reveal-melt operation, introduced with v27 */ 1708 { 1709 .url = "reveal-melt", 1710 .method = MHD_HTTP_METHOD_POST, 1711 .handler.post = &TEH_handler_reveal_melt, 1712 .nargs = 0 1713 }, 1714 /* coins */ 1715 { 1716 .url = "coins", 1717 .method = MHD_HTTP_METHOD_POST, 1718 .handler.post = &handle_post_coins, 1719 .nargs = 2 1720 }, 1721 { 1722 .url = "coins", 1723 .method = MHD_HTTP_METHOD_GET, 1724 .handler.get = &handle_get_coins, 1725 .nargs = 2, 1726 .nargs_is_upper_bound = true 1727 }, 1728 /* melting operation, introduced with v26 */ 1729 { 1730 .url = "melt", 1731 .method = MHD_HTTP_METHOD_POST, 1732 .handler.post = &TEH_handler_melt, 1733 .nargs = 0 1734 }, 1735 /* tracking transfers */ 1736 { 1737 .url = "transfers", 1738 .method = MHD_HTTP_METHOD_GET, 1739 .handler.get = &TEH_handler_transfers_get, 1740 .nargs = 1 1741 }, 1742 /* tracking deposits */ 1743 { 1744 .url = "deposits", 1745 .method = MHD_HTTP_METHOD_GET, 1746 .handler.get = &TEH_handler_deposits_get, 1747 .nargs = 4 1748 }, 1749 /* Operating on purses */ 1750 { 1751 .url = "purses", 1752 .method = MHD_HTTP_METHOD_POST, 1753 .handler.post = &handle_post_purses, 1754 .nargs = 2 1755 }, 1756 /* Getting purse status */ 1757 { 1758 .url = "purses", 1759 .method = MHD_HTTP_METHOD_GET, 1760 .handler.get = &TEH_handler_purses_get, 1761 .nargs = 2 1762 }, 1763 /* Deleting purse */ 1764 { 1765 .url = "purses", 1766 .method = MHD_HTTP_METHOD_DELETE, 1767 .handler.delete = &TEH_handler_purses_delete, 1768 .nargs = 1 1769 }, 1770 /* Getting contracts */ 1771 { 1772 .url = "contracts", 1773 .method = MHD_HTTP_METHOD_GET, 1774 .handler.get = &TEH_handler_contracts_get, 1775 .nargs = 1 1776 }, 1777 /* KYC endpoints */ 1778 { 1779 .url = "kyc-check", 1780 .method = MHD_HTTP_METHOD_GET, 1781 .handler.get = &TEH_handler_kyc_check, 1782 .nargs = 1 1783 }, 1784 { 1785 .url = "kyc-proof", 1786 .method = MHD_HTTP_METHOD_GET, 1787 .handler.get = &TEH_handler_kyc_proof, 1788 .nargs = 1 1789 }, 1790 { 1791 .url = "kyc-start", 1792 .method = MHD_HTTP_METHOD_POST, 1793 .handler.post = &TEH_handler_kyc_start, 1794 .nargs = 1 1795 }, 1796 { 1797 .url = "kyc-wallet", 1798 .method = MHD_HTTP_METHOD_POST, 1799 .handler.post = &TEH_handler_kyc_wallet, 1800 .nargs = 0 1801 }, 1802 { 1803 .url = "kyc-webhook", 1804 .method = MHD_HTTP_METHOD_GET, 1805 .handler.get = &TEH_handler_kyc_webhook_get, 1806 .nargs = 16, /* more is not plausible */ 1807 .nargs_is_upper_bound = true 1808 }, 1809 { 1810 .url = "kyc-info", 1811 .method = MHD_HTTP_METHOD_GET, 1812 .handler.get = &TEH_handler_kyc_info, 1813 .nargs = 1 1814 }, 1815 { 1816 .url = "kyc-upload", 1817 .method = MHD_HTTP_METHOD_POST, 1818 .handler.post = &TEH_handler_kyc_upload, 1819 .nargs = 1, 1820 .nargs_is_upper_bound = true 1821 }, 1822 { 1823 .url = "kyc-webhook", 1824 .method = MHD_HTTP_METHOD_POST, 1825 .handler.post = &TEH_handler_kyc_webhook_post, 1826 .nargs = 16, /* more is not plausible */ 1827 .nargs_is_upper_bound = true 1828 }, 1829 /* POST management endpoints */ 1830 { 1831 .url = "management", 1832 .method = MHD_HTTP_METHOD_POST, 1833 .handler.post = &handle_post_management, 1834 .nargs = 4, 1835 .nargs_is_upper_bound = true 1836 }, 1837 /* GET management endpoints (we only really have "/management/keys") */ 1838 { 1839 .url = "management", 1840 .method = MHD_HTTP_METHOD_GET, 1841 .handler.get = &handle_get_management, 1842 .nargs = 1 1843 }, 1844 /* auditor endpoints */ 1845 { 1846 .url = "auditors", 1847 .method = MHD_HTTP_METHOD_POST, 1848 .handler.post = &handle_post_auditors, 1849 .nargs = 4, 1850 .nargs_is_upper_bound = true 1851 }, 1852 /* AML endpoints */ 1853 { 1854 .url = "aml", 1855 .method = MHD_HTTP_METHOD_GET, 1856 .handler.get = &handle_get_aml, 1857 .nargs = 4, 1858 .nargs_is_upper_bound = true 1859 }, 1860 { 1861 .url = "aml", 1862 .method = MHD_HTTP_METHOD_POST, 1863 .handler.post = &handle_post_aml, 1864 .nargs = 2 1865 }, 1866 { 1867 .url = "aml-spa", 1868 .method = MHD_HTTP_METHOD_GET, 1869 .handler.get = &TEH_handler_aml_spa, 1870 .nargs = 1, 1871 .nargs_is_upper_bound = true 1872 }, 1873 { 1874 .url = "kyc-spa", 1875 .method = MHD_HTTP_METHOD_GET, 1876 .handler.get = &TEH_handler_kyc_spa, 1877 .nargs = 1, 1878 .nargs_is_upper_bound = true 1879 }, 1880 1881 /* mark end of list */ 1882 { 1883 .url = NULL 1884 } 1885 }; 1886 struct TEH_RequestContext *rc = *con_cls; 1887 struct GNUNET_AsyncScopeSave old_scope; 1888 const char *correlation_id = NULL; 1889 1890 (void) cls; 1891 (void) version; 1892 if (NULL == rc) 1893 { 1894 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1895 "Handling new request\n"); 1896 /* We're in a new async scope! */ 1897 rc = *con_cls = GNUNET_new (struct TEH_RequestContext); 1898 rc->start_time = GNUNET_TIME_absolute_get (); 1899 GNUNET_async_scope_fresh (&rc->async_scope_id); 1900 TEH_check_invariants (); 1901 rc->url = url; 1902 rc->connection = connection; 1903 /* We only read the correlation ID on the first callback for every client */ 1904 correlation_id = MHD_lookup_connection_value (connection, 1905 MHD_HEADER_KIND, 1906 "Taler-Correlation-Id"); 1907 if ( (NULL != correlation_id) && 1908 (GNUNET_YES != 1909 GNUNET_CURL_is_valid_scope_id (correlation_id)) ) 1910 { 1911 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1912 "illegal incoming correlation ID\n"); 1913 correlation_id = NULL; 1914 } 1915 1916 /* Check if upload is in bounds */ 1917 if ( (0 == strcasecmp (method, 1918 MHD_HTTP_METHOD_POST)) || 1919 (0 == strcasecmp (method, 1920 MHD_HTTP_METHOD_PATCH)) ) 1921 { 1922 TALER_MHD_check_content_length (connection, 1923 TALER_MHD_REQUEST_BUFFER_MAX); 1924 } 1925 } 1926 1927 GNUNET_async_scope_enter (&rc->async_scope_id, 1928 &old_scope); 1929 TEH_check_invariants (); 1930 if (NULL != correlation_id) 1931 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1932 "Handling request (%s) for URL '%s', correlation_id=%s\n", 1933 method, 1934 url, 1935 correlation_id); 1936 else 1937 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1938 "Handling request (%s) for URL '%s'\n", 1939 method, 1940 url); 1941 /* on repeated requests, check our cache first */ 1942 if (NULL != rc->rh) 1943 { 1944 enum MHD_Result ret; 1945 const char *start; 1946 1947 if ('\0' == url[0]) 1948 /* strange, should start with '/', treat as just "/" */ 1949 url = "/"; 1950 start = strchr (url + 1, '/'); 1951 if (NULL == start) 1952 start = ""; 1953 ret = proceed_with_handler (rc, 1954 start, 1955 upload_data, 1956 upload_data_size); 1957 if (GNUNET_OK != 1958 TALER_EXCHANGEDB_preflight (TEH_pg)) 1959 { 1960 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1961 "Handler %s left open database transaction behind!\n", 1962 url); 1963 GNUNET_assert (0); 1964 } 1965 GNUNET_async_scope_restore (&old_scope); 1966 return ret; 1967 } 1968 1969 if ( (0 == strcasecmp (method, 1970 MHD_HTTP_METHOD_OPTIONS)) && 1971 (0 == strcmp ("*", 1972 url)) ) 1973 { 1974 enum MHD_Result ret; 1975 1976 ret = TALER_MHD_reply_cors_preflight (connection); 1977 GNUNET_async_scope_restore (&old_scope); 1978 return ret; 1979 } 1980 if (0 == strcasecmp (method, 1981 MHD_HTTP_METHOD_HEAD)) 1982 method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */ 1983 1984 /* parse first part of URL */ 1985 { 1986 bool found = false; 1987 size_t tok_size; 1988 const char *tok; 1989 const char *rest; 1990 1991 if ('\0' == url[0]) 1992 /* strange, should start with '/', treat as just "/" */ 1993 url = "/"; 1994 tok = url + 1; 1995 rest = strchr (tok, '/'); 1996 if (NULL == rest) 1997 { 1998 tok_size = strlen (tok); 1999 } 2000 else 2001 { 2002 tok_size = rest - tok; 2003 rest++; /* skip over '/' */ 2004 } 2005 for (unsigned int i = 0; NULL != handlers[i].url; i++) 2006 { 2007 struct TEH_RequestHandler *rh = &handlers[i]; 2008 2009 if ( (0 != strncmp (tok, 2010 rh->url, 2011 tok_size)) || 2012 (tok_size != strlen (rh->url) ) ) 2013 continue; 2014 found = true; 2015 /* The URL is a match! What we now do depends on the method. */ 2016 if (0 == strcasecmp (method, 2017 MHD_HTTP_METHOD_OPTIONS)) 2018 { 2019 GNUNET_async_scope_restore (&old_scope); 2020 return TALER_MHD_reply_cors_preflight (connection); 2021 } 2022 GNUNET_assert (NULL != rh->method); 2023 if (0 == strcasecmp (method, 2024 rh->method)) 2025 { 2026 enum MHD_Result ret; 2027 2028 /* cache to avoid the loop next time */ 2029 rc->rh = rh; 2030 /* run handler */ 2031 ret = proceed_with_handler (rc, 2032 url + tok_size + 1, 2033 upload_data, 2034 upload_data_size); 2035 if (GNUNET_OK != 2036 TALER_EXCHANGEDB_preflight (TEH_pg)) 2037 { 2038 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2039 "Handler %s left open database transaction behind!\n", 2040 url); 2041 GNUNET_assert (0); 2042 } 2043 GNUNET_async_scope_restore (&old_scope); 2044 return ret; 2045 } 2046 } 2047 2048 if (found) 2049 { 2050 /* we found a matching address, but the method is wrong */ 2051 struct MHD_Response *reply; 2052 enum MHD_Result ret; 2053 char *allowed = NULL; 2054 2055 GNUNET_break_op (0); 2056 for (unsigned int i = 0; NULL != handlers[i].url; i++) 2057 { 2058 struct TEH_RequestHandler *rh = &handlers[i]; 2059 2060 if ( (0 != strncmp (tok, 2061 rh->url, 2062 tok_size)) || 2063 (tok_size != strlen (rh->url) ) ) 2064 continue; 2065 if (NULL == allowed) 2066 { 2067 allowed = GNUNET_strdup (rh->method); 2068 } 2069 else 2070 { 2071 char *tmp; 2072 2073 GNUNET_asprintf (&tmp, 2074 "%s, %s", 2075 allowed, 2076 rh->method); 2077 GNUNET_free (allowed); 2078 allowed = tmp; 2079 } 2080 if (0 == strcasecmp (rh->method, 2081 MHD_HTTP_METHOD_GET)) 2082 { 2083 char *tmp; 2084 2085 GNUNET_asprintf (&tmp, 2086 "%s, %s", 2087 allowed, 2088 MHD_HTTP_METHOD_HEAD); 2089 GNUNET_free (allowed); 2090 allowed = tmp; 2091 } 2092 } 2093 reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID, 2094 method); 2095 GNUNET_break (MHD_YES == 2096 MHD_add_response_header (reply, 2097 MHD_HTTP_HEADER_ALLOW, 2098 allowed)); 2099 GNUNET_free (allowed); 2100 ret = MHD_queue_response (connection, 2101 MHD_HTTP_METHOD_NOT_ALLOWED, 2102 reply); 2103 MHD_destroy_response (reply); 2104 GNUNET_async_scope_restore (&old_scope); 2105 return ret; 2106 } 2107 } 2108 2109 /* No handler matches, generate not found */ 2110 { 2111 enum MHD_Result ret; 2112 2113 ret = TALER_MHD_reply_with_error (connection, 2114 MHD_HTTP_NOT_FOUND, 2115 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 2116 url); 2117 GNUNET_async_scope_restore (&old_scope); 2118 return ret; 2119 } 2120 } 2121 2122 2123 /** 2124 * Load configuration parameters for the exchange 2125 * server into the corresponding global variables. 2126 * 2127 * @param cfg_fn name of our configuration file 2128 * @return #GNUNET_OK on success 2129 */ 2130 static enum GNUNET_GenericReturnValue 2131 exchange_serve_process_config (const char *cfg_fn) 2132 { 2133 TEH_disable_direct_deposit 2134 = GNUNET_CONFIGURATION_get_value_yesno ( 2135 TEH_cfg, 2136 "exchange", 2137 "DISABLE_DIRECT_DEPOSIT"); 2138 if (GNUNET_SYSERR == TEH_disable_direct_deposit) 2139 { 2140 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 2141 "exchange", 2142 "DISABLE_DIRECT_DEPOSIT", 2143 "Need YES or NO\n"); 2144 return GNUNET_SYSERR; 2145 } 2146 TEH_enable_kyc 2147 = GNUNET_CONFIGURATION_get_value_yesno ( 2148 TEH_cfg, 2149 "exchange", 2150 "ENABLE_KYC"); 2151 if (GNUNET_SYSERR == TEH_enable_kyc) 2152 { 2153 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 2154 "exchange", 2155 "ENABLE_KYC", 2156 "Need YES or NO\n"); 2157 return GNUNET_SYSERR; 2158 } 2159 TEH_kyc_swap_tos_acceptance 2160 = GNUNET_CONFIGURATION_get_value_yesno ( 2161 TEH_cfg, 2162 "exchange", 2163 "KYC_SWAP_TOS_ACCEPTANCE"); 2164 if (GNUNET_SYSERR == TEH_kyc_swap_tos_acceptance) 2165 TEH_kyc_swap_tos_acceptance = GNUNET_NO; 2166 if (GNUNET_OK != 2167 TALER_KYCLOGIC_kyc_init (TEH_cfg, 2168 cfg_fn)) 2169 { 2170 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2171 "Failed to load configuration `%s'. Exiting.\n", 2172 cfg_fn); 2173 return GNUNET_SYSERR; 2174 } 2175 TEH_hard_limits 2176 = TALER_KYCLOGIC_get_hard_limits (); 2177 TEH_zero_limits 2178 = TALER_KYCLOGIC_get_zero_limits (); 2179 if (GNUNET_OK != 2180 GNUNET_CONFIGURATION_get_value_number (TEH_cfg, 2181 "exchange", 2182 "MAX_REQUESTS", 2183 &req_max)) 2184 { 2185 req_max = ULLONG_MAX; 2186 } 2187 if (GNUNET_OK != 2188 GNUNET_CONFIGURATION_get_value_time (TEH_cfg, 2189 "exchangedb", 2190 "IDLE_RESERVE_EXPIRATION_TIME", 2191 &TEH_reserve_closing_delay)) 2192 { 2193 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2194 "exchangedb", 2195 "IDLE_RESERVE_EXPIRATION_TIME"); 2196 /* use default */ 2197 TEH_reserve_closing_delay 2198 = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS, 2199 4); 2200 } 2201 if (GNUNET_OK != 2202 GNUNET_CONFIGURATION_get_value_time (TEH_cfg, 2203 "exchange", 2204 "DEFAULT_P2P_EXPIRATION", 2205 &TEH_default_p2p_expiration)) 2206 { 2207 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 2208 "exchange", 2209 "DEFAULT_P2P_EXPIRATION"); 2210 /* use default */ 2211 TEH_default_p2p_expiration 2212 = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS, 2213 4); 2214 } 2215 if (GNUNET_OK != 2216 GNUNET_CONFIGURATION_get_value_time (TEH_cfg, 2217 "exchangedb", 2218 "MAX_AML_PROGRAM_RUNTIME", 2219 &TEH_aml_program_timeout)) 2220 { 2221 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2222 "exchangedb", 2223 "MAX_AML_PROGRAM_RUNTIME"); 2224 /* use default */ 2225 TEH_aml_program_timeout 2226 = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2227 1); 2228 } 2229 2230 if (GNUNET_OK != 2231 GNUNET_CONFIGURATION_get_value_time (TEH_cfg, 2232 "exchange", 2233 "MAX_KEYS_CACHING", 2234 &TEH_max_keys_caching)) 2235 { 2236 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 2237 "exchange", 2238 "MAX_KEYS_CACHING", 2239 "valid relative time expected"); 2240 return GNUNET_SYSERR; 2241 } 2242 if (GNUNET_OK != 2243 GNUNET_CONFIGURATION_get_value_string (TEH_cfg, 2244 "exchange", 2245 "TOPLEVEL_REDIRECT_URL", 2246 &toplevel_redirect_url)) 2247 { 2248 toplevel_redirect_url = GNUNET_strdup ("/terms"); 2249 } 2250 (void) 2251 GNUNET_CONFIGURATION_get_value_string (TEH_cfg, 2252 "exchange", 2253 "BANK_COMPLIANCE_LANGUAGE", 2254 &TEH_bank_compliance_language); 2255 (void) 2256 GNUNET_CONFIGURATION_get_value_string (TEH_cfg, 2257 "exchange", 2258 "AML_SPA_DIALECT", 2259 &TEH_aml_spa_dialect); 2260 if (GNUNET_OK != 2261 TALER_config_get_currency (TEH_cfg, 2262 "exchange", 2263 &TEH_currency)) 2264 { 2265 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2266 "exchange", 2267 "CURRENCY"); 2268 return GNUNET_SYSERR; 2269 } 2270 2271 if (GNUNET_OK != 2272 TALER_CONFIG_parse_currencies (TEH_cfg, 2273 TEH_currency, 2274 &num_cspecs, 2275 &cspecs)) 2276 return GNUNET_SYSERR; 2277 for (unsigned int i = 0; i<num_cspecs; i++) 2278 { 2279 struct TALER_CurrencySpecification *cspec; 2280 2281 cspec = &cspecs[i]; 2282 if (0 == strcasecmp (TEH_currency, 2283 cspec->currency)) 2284 { 2285 TEH_cspec = cspec; 2286 break; 2287 } 2288 } 2289 /* currency parser must provide default spec for main currency */ 2290 GNUNET_assert (NULL != TEH_cspec); 2291 GNUNET_assert (GNUNET_OK == 2292 TALER_amount_set_zero (TEH_currency, 2293 &TEH_stefan_abs)); 2294 if (GNUNET_SYSERR == 2295 TALER_config_get_amount (TEH_cfg, 2296 "exchange", 2297 "STEFAN_ABS", 2298 &TEH_stefan_abs)) 2299 { 2300 GNUNET_break (0); 2301 return GNUNET_SYSERR; 2302 } 2303 GNUNET_assert (GNUNET_OK == 2304 TALER_amount_set_zero (TEH_currency, 2305 &TEH_stefan_log)); 2306 if (GNUNET_SYSERR == 2307 TALER_config_get_amount (TEH_cfg, 2308 "exchange", 2309 "STEFAN_LOG", 2310 &TEH_stefan_log)) 2311 { 2312 GNUNET_break (0); 2313 return GNUNET_SYSERR; 2314 } 2315 if ( (GNUNET_OK != 2316 TALER_config_get_amount (TEH_cfg, 2317 "exchange", 2318 "TINY_AMOUNT", 2319 &TEH_tiny_amount)) && 2320 (GNUNET_OK != 2321 TALER_config_get_amount (TEH_cfg, 2322 "auditor", 2323 "TINY_AMOUNT", 2324 &TEH_tiny_amount)) ) 2325 { 2326 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2327 "exchange", 2328 "TINY_AMOUNT"); 2329 return GNUNET_SYSERR; 2330 } 2331 TEH_stefan_lin = 0.0f; 2332 if (GNUNET_SYSERR == 2333 GNUNET_CONFIGURATION_get_value_float (TEH_cfg, 2334 "exchange", 2335 "STEFAN_LIN", 2336 &TEH_stefan_lin)) 2337 { 2338 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 2339 "exchange", 2340 "STEFAN_LIN", 2341 "must be a floating point"); 2342 return GNUNET_SYSERR; 2343 } 2344 2345 if (GNUNET_OK != 2346 GNUNET_CONFIGURATION_get_value_string (TEH_cfg, 2347 "exchange", 2348 "BASE_URL", 2349 &TEH_base_url)) 2350 { 2351 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2352 "exchange", 2353 "BASE_URL"); 2354 return GNUNET_SYSERR; 2355 } 2356 if ( (! TALER_url_valid_charset (TEH_base_url)) || 2357 (! TALER_is_web_url (TEH_base_url) ) ) 2358 { 2359 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 2360 "exchange", 2361 "BASE_URL", 2362 "invalid URL"); 2363 return GNUNET_SYSERR; 2364 } 2365 if (GNUNET_OK != 2366 GNUNET_CONFIGURATION_get_value_string (TEH_cfg, 2367 "exchange", 2368 "SHOPPING_URL", 2369 &TEH_shopping_url)) 2370 { 2371 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 2372 "exchange", 2373 "SHOPPING_URL"); 2374 } 2375 if (GNUNET_OK != 2376 GNUNET_CONFIGURATION_get_value_string (TEH_cfg, 2377 "exchange", 2378 "TERMS_ETAG", 2379 &TEH_tos_etag)) 2380 { 2381 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 2382 "exchange", 2383 "TERMS_ETAG"); 2384 } 2385 if ( (NULL != TEH_shopping_url) && 2386 (! TALER_is_web_url (TEH_shopping_url)) ) 2387 { 2388 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 2389 "exchange", 2390 "SHOPPING_URL", 2391 "invalid URL"); 2392 return GNUNET_SYSERR; 2393 } 2394 if (GNUNET_OK == 2395 GNUNET_CONFIGURATION_get_value_filename (TEH_cfg, 2396 "exchange", 2397 "AML_SPA_DIR", 2398 &TEH_aml_spa_dir)) 2399 { 2400 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2401 "Loading exchange AML SPA from %s\n", 2402 TEH_aml_spa_dir); 2403 } 2404 else 2405 { 2406 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2407 "Loading exchange AML SPA from default location\n"); 2408 } 2409 if (GNUNET_OK == 2410 GNUNET_CONFIGURATION_get_value_filename (TEH_cfg, 2411 "exchange", 2412 "KYC_SPA_DIR", 2413 &TEH_kyc_spa_dir)) 2414 { 2415 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2416 "Loading exchange KYC SPA from %s\n", 2417 TEH_kyc_spa_dir); 2418 } 2419 else 2420 { 2421 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2422 "Loading exchange KYC SPA from default location\n"); 2423 } 2424 2425 { 2426 char *master_public_key_str; 2427 2428 if (GNUNET_OK != 2429 GNUNET_CONFIGURATION_get_value_string (TEH_cfg, 2430 "exchange", 2431 "MASTER_PUBLIC_KEY", 2432 &master_public_key_str)) 2433 { 2434 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2435 "exchange", 2436 "MASTER_PUBLIC_KEY"); 2437 return GNUNET_SYSERR; 2438 } 2439 if (GNUNET_OK != 2440 GNUNET_CRYPTO_eddsa_public_key_from_string ( 2441 master_public_key_str, 2442 strlen (master_public_key_str), 2443 &TEH_master_public_key.eddsa_pub)) 2444 { 2445 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 2446 "exchange", 2447 "MASTER_PUBLIC_KEY", 2448 "invalid base32 encoding for a master public key"); 2449 GNUNET_free (master_public_key_str); 2450 return GNUNET_SYSERR; 2451 } 2452 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2453 "Launching exchange with public key `%s'...\n", 2454 master_public_key_str); 2455 GNUNET_free (master_public_key_str); 2456 } 2457 2458 { 2459 char *attr_enc_key_str; 2460 2461 if (GNUNET_OK != 2462 GNUNET_CONFIGURATION_get_value_string (TEH_cfg, 2463 "exchange", 2464 "ATTRIBUTE_ENCRYPTION_KEY", 2465 &attr_enc_key_str)) 2466 { 2467 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2468 "exchange", 2469 "ATTRIBUTE_ENCRYPTION_KEY"); 2470 return GNUNET_SYSERR; 2471 } 2472 GNUNET_CRYPTO_hash (attr_enc_key_str, 2473 strlen (attr_enc_key_str), 2474 &TEH_attribute_key.hash); 2475 GNUNET_free (attr_enc_key_str); 2476 } 2477 2478 { 2479 char *pdf_data; 2480 2481 if (GNUNET_OK == 2482 GNUNET_CONFIGURATION_get_value_string (TEH_cfg, 2483 "exchange", 2484 "GLOBAL_PDF_FORM_DATA", 2485 &pdf_data)) 2486 { 2487 json_error_t err; 2488 2489 TEH_global_pdf_form_data = json_loads (pdf_data, 2490 JSON_REJECT_DUPLICATES, 2491 &err); 2492 GNUNET_free (pdf_data); 2493 if (NULL == TEH_global_pdf_form_data) 2494 { 2495 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 2496 "exchange", 2497 "GLOBAL_PDF_FORM_DATA", 2498 err.text); 2499 return GNUNET_SYSERR; 2500 } 2501 } 2502 } 2503 2504 for (unsigned int i = 0; i<MAX_DB_RETRIES; i++) 2505 { 2506 TEH_pg = TALER_EXCHANGEDB_connect (TEH_cfg); 2507 if (NULL != TEH_pg) 2508 break; 2509 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2510 "Failed to connect to DB, will try again %u times\n", 2511 MAX_DB_RETRIES - i); 2512 sleep (1); 2513 } 2514 if (NULL == TEH_pg) 2515 { 2516 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2517 "Failed to initialize DB subsystem. Giving up.\n"); 2518 return GNUNET_SYSERR; 2519 } 2520 return GNUNET_OK; 2521 } 2522 2523 2524 /** 2525 * Called when the main thread exits, writes out performance 2526 * stats if requested. 2527 */ 2528 static void 2529 write_stats (void) 2530 { 2531 struct GNUNET_DISK_FileHandle *fh; 2532 pid_t pid = getpid (); 2533 char *benchmark_dir; 2534 char *s; 2535 struct rusage usage; 2536 2537 benchmark_dir = getenv ("GNUNET_BENCHMARK_DIR"); 2538 if (NULL == benchmark_dir) 2539 return; 2540 GNUNET_asprintf (&s, 2541 "%s/taler-exchange-%llu.txt", 2542 benchmark_dir, 2543 (unsigned long long) pid); 2544 fh = GNUNET_DISK_file_open (s, 2545 (GNUNET_DISK_OPEN_WRITE 2546 | GNUNET_DISK_OPEN_TRUNCATE 2547 | GNUNET_DISK_OPEN_CREATE), 2548 (GNUNET_DISK_PERM_USER_READ 2549 | GNUNET_DISK_PERM_USER_WRITE)); 2550 GNUNET_free (s); 2551 if (NULL == fh) 2552 return; /* permission denied? */ 2553 2554 /* Collect stats, summed up for all threads */ 2555 GNUNET_assert (0 == 2556 getrusage (RUSAGE_SELF, 2557 &usage)); 2558 GNUNET_asprintf (&s, 2559 "time_exchange sys %llu user %llu\n", 2560 (unsigned long long) (usage.ru_stime.tv_sec * 1000 * 1000 2561 + usage.ru_stime.tv_usec), 2562 (unsigned long long) (usage.ru_utime.tv_sec * 1000 * 1000 2563 + usage.ru_utime.tv_usec)); 2564 GNUNET_assert (GNUNET_SYSERR != 2565 GNUNET_DISK_file_write_blocking (fh, 2566 s, 2567 strlen (s))); 2568 GNUNET_free (s); 2569 GNUNET_assert (GNUNET_OK == 2570 GNUNET_DISK_file_close (fh)); 2571 } 2572 2573 2574 /* Developer logic for supporting the `-f' option. */ 2575 #if HAVE_DEVELOPER 2576 2577 /** 2578 * Option `-f' (specifies an input file to give to the HTTP server). 2579 */ 2580 static char *input_filename; 2581 2582 2583 /** 2584 * Run 'nc' or 'ncat' as a fake HTTP client using #input_filename 2585 * as the input for the request. If launching the client worked, 2586 * run the #TEH_KS_loop() event loop as usual. 2587 * 2588 * @return child pid 2589 */ 2590 static pid_t 2591 run_fake_client (void) 2592 { 2593 unsigned long long serve_port; 2594 pid_t cld; 2595 char ports[6]; 2596 int fd; 2597 bool use_stdin; 2598 2599 if (GNUNET_OK != 2600 GNUNET_CONFIGURATION_get_value_number (TEH_cfg, 2601 "exchange", 2602 "PORT", 2603 &serve_port)) 2604 { 2605 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 2606 "exchange", 2607 "PORT", 2608 "port number required (for nc)"); 2609 return -1; 2610 } 2611 /* Duping to STDIN and fork() mess up gcc's analysis 2612 badly, disable diagnostics. */ 2613 #pragma GCC diagnostic ignored "-Wanalyzer-fd-leak" 2614 #pragma GCC diagnostic ignored "-Wanalyzer-fd-use-without-check" 2615 #pragma GCC diagnostic ignored "-Wanalyzer-fd-double-close" 2616 #pragma GCC diagnostic ignored "-Wanalyzer-fd-use-after-close" 2617 use_stdin = (0 == strcmp (input_filename, 2618 "-")); 2619 if (use_stdin) 2620 fd = STDIN_FILENO; 2621 else 2622 fd = open (input_filename, 2623 O_RDONLY); 2624 if (-1 == fd) 2625 { 2626 fprintf (stderr, 2627 "Failed to open `%s': %s\n", 2628 input_filename, 2629 strerror (errno)); 2630 return -1; 2631 } 2632 /* Fake HTTP client request with #input_filename as input. 2633 We do this using the nc tool. */ 2634 GNUNET_snprintf (ports, 2635 sizeof (ports), 2636 "%u", 2637 (unsigned int) serve_port); 2638 if (0 == (cld = fork ())) 2639 { 2640 if (! use_stdin) 2641 { 2642 GNUNET_break (0 == close (STDIN_FILENO)); 2643 GNUNET_break (STDIN_FILENO == 2644 dup2 (fd, 2645 STDIN_FILENO)); 2646 GNUNET_break (0 == close (fd)); 2647 } 2648 if ( (0 != execlp ("nc", 2649 "nc", 2650 "localhost", 2651 ports, 2652 "-w", "30", 2653 NULL)) && 2654 (0 != execlp ("ncat", 2655 "ncat", 2656 "localhost", 2657 ports, 2658 "-i", "30", 2659 NULL)) ) 2660 { 2661 fprintf (stderr, 2662 "Failed to run both `nc' and `ncat': %s\n", 2663 strerror (errno)); 2664 } 2665 _exit (1); 2666 } 2667 /* parent process */ 2668 if (! use_stdin) 2669 GNUNET_break (0 == close (fd)); 2670 return cld; 2671 #pragma GCC diagnostic pop 2672 #pragma GCC diagnostic pop 2673 #pragma GCC diagnostic pop 2674 #pragma GCC diagnostic pop 2675 } 2676 2677 2678 /** 2679 * Run the exchange to serve a single request only, without threads. 2680 * 2681 * @return #GNUNET_OK on success 2682 */ 2683 static void 2684 run_single_request (void) 2685 { 2686 pid_t xfork; 2687 2688 xfork = fork (); 2689 if (-1 == xfork) 2690 { 2691 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 2692 "fork"); 2693 global_ret = EXIT_NO_RESTART; 2694 GNUNET_SCHEDULER_shutdown (); 2695 return; 2696 } 2697 if (0 == xfork) 2698 { 2699 pid_t cld; 2700 2701 cld = run_fake_client (); 2702 if (-1 == cld) 2703 _exit (EXIT_FAILURE); 2704 _exit (EXIT_SUCCESS); 2705 } 2706 2707 { 2708 int status; 2709 2710 if (xfork != waitpid (xfork, 2711 &status, 2712 0)) 2713 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2714 "Waiting for `nc' child failed: %s\n", 2715 strerror (errno)); 2716 } 2717 } 2718 2719 2720 /* end of HAVE_DEVELOPER */ 2721 #endif 2722 2723 2724 /** 2725 * Signature of the callback used by MHD to notify the application 2726 * about completed connections. If we are running in test-mode with 2727 * an input_filename, this function is used to terminate the HTTPD 2728 * after the first request has been processed. 2729 * 2730 * @param cls client-defined closure, NULL 2731 * @param connection connection handle (ignored) 2732 * @param socket_context socket-specific pointer (ignored) 2733 * @param toe reason for connection notification 2734 */ 2735 static void 2736 connection_done (void *cls, 2737 struct MHD_Connection *connection, 2738 void **socket_context, 2739 enum MHD_ConnectionNotificationCode toe) 2740 { 2741 (void) cls; 2742 (void) connection; 2743 (void) socket_context; 2744 2745 switch (toe) 2746 { 2747 case MHD_CONNECTION_NOTIFY_STARTED: 2748 active_connections++; 2749 break; 2750 case MHD_CONNECTION_NOTIFY_CLOSED: 2751 active_connections--; 2752 if (TEH_suicide && 2753 (0 == active_connections) ) 2754 GNUNET_SCHEDULER_shutdown (); 2755 break; 2756 } 2757 #if HAVE_DEVELOPER 2758 /* We only act if the connection is closed. */ 2759 if (MHD_CONNECTION_NOTIFY_CLOSED != toe) 2760 return; 2761 if (NULL != input_filename) 2762 GNUNET_SCHEDULER_shutdown (); 2763 #endif 2764 } 2765 2766 2767 /** 2768 * Function run on shutdown. 2769 * 2770 * @param cls NULL 2771 */ 2772 static void 2773 do_shutdown (void *cls) 2774 { 2775 (void) cls; 2776 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2777 "Shutdown of taler-exchange-httpd\n"); 2778 TALER_MHD_daemons_halt (); 2779 TEH_resume_keys_requests (true); 2780 TEH_batch_deposit_cleanup (); 2781 TEH_withdraw_cleanup (); 2782 TEH_reserves_close_cleanup (); 2783 TEH_reserves_purse_cleanup (); 2784 TEH_purses_merge_cleanup (); 2785 TEH_kyc_wallet_cleanup (); 2786 TEH_kyc_upload_cleanup (); 2787 TEH_deposits_get_cleanup (); 2788 TEH_reserves_get_cleanup (); 2789 TEH_purses_get_cleanup (); 2790 TEH_kyc_check_cleanup (); 2791 TEH_kyc_info_cleanup (); 2792 TEH_kyc_proof_cleanup (); 2793 TEH_kyc_start_cleanup (); 2794 TEH_aml_decision_cleanup (); 2795 TEH_handler_aml_attributes_get_cleanup (); 2796 TALER_KYCLOGIC_kyc_done (); 2797 TALER_MHD_daemons_destroy (); 2798 TEH_keys_finished (); 2799 if (NULL != TEH_pg) 2800 { 2801 TALER_EXCHANGEDB_disconnect (TEH_pg); 2802 TEH_pg = NULL; 2803 } 2804 if (NULL != TEH_curl_ctx) 2805 { 2806 GNUNET_CURL_fini (TEH_curl_ctx); 2807 TEH_curl_ctx = NULL; 2808 } 2809 if (NULL != exchange_curl_rc) 2810 { 2811 GNUNET_CURL_gnunet_rc_destroy (exchange_curl_rc); 2812 exchange_curl_rc = NULL; 2813 } 2814 TALER_TEMPLATING_done (); 2815 TEH_cspec = NULL; 2816 TALER_CONFIG_free_currencies (num_cspecs, 2817 cspecs); 2818 num_cspecs = 0; 2819 cspecs = NULL; 2820 } 2821 2822 2823 /** 2824 * Callback invoked on every listen socket to start the 2825 * respective MHD HTTP daemon. 2826 * 2827 * @param cls unused 2828 * @param lsock the listen socket 2829 */ 2830 static void 2831 start_daemon (void *cls, 2832 int lsock) 2833 { 2834 struct MHD_Daemon *mhd; 2835 2836 (void) cls; 2837 GNUNET_assert (-1 != lsock); 2838 mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME 2839 | MHD_USE_PIPE_FOR_SHUTDOWN 2840 | MHD_USE_DEBUG | MHD_USE_DUAL_STACK 2841 | MHD_USE_TCP_FASTOPEN, 2842 0, /* already bound */ 2843 NULL, NULL, 2844 &handle_mhd_request, NULL, 2845 MHD_OPTION_LISTEN_SOCKET, 2846 lsock, 2847 MHD_OPTION_EXTERNAL_LOGGER, 2848 &TALER_MHD_handle_logs, 2849 NULL, 2850 MHD_OPTION_NOTIFY_COMPLETED, 2851 &handle_mhd_completion_callback, 2852 NULL, 2853 MHD_OPTION_NOTIFY_CONNECTION, 2854 &connection_done, 2855 NULL, 2856 MHD_OPTION_CONNECTION_TIMEOUT, 2857 connection_timeout, 2858 (0 == allow_address_reuse) 2859 ? MHD_OPTION_END 2860 : MHD_OPTION_LISTENING_ADDRESS_REUSE, 2861 (unsigned int) allow_address_reuse, 2862 MHD_OPTION_END); 2863 if (NULL == mhd) 2864 { 2865 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2866 "Failed to launch HTTP service!\n"); 2867 GNUNET_SCHEDULER_shutdown (); 2868 return; 2869 } 2870 have_daemons = true; 2871 TALER_MHD_daemon_start (mhd); 2872 } 2873 2874 2875 /** 2876 * Main function that will be run by the scheduler. 2877 * 2878 * @param cls closure 2879 * @param args remaining command-line arguments 2880 * @param cfgfile name of the configuration file used (for saving, can be 2881 * NULL!) 2882 * @param config configuration 2883 */ 2884 static void 2885 run (void *cls, 2886 char *const *args, 2887 const char *cfgfile, 2888 const struct GNUNET_CONFIGURATION_Handle *config) 2889 { 2890 enum TALER_MHD_GlobalOptions go; 2891 enum GNUNET_GenericReturnValue ret; 2892 2893 (void) cls; 2894 (void) args; 2895 (void ) cfgfile; 2896 go = TALER_MHD_GO_NONE; 2897 if (connection_close) 2898 go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE; 2899 TALER_MHD_setup (go); 2900 TEH_cfg = config; 2901 2902 if (GNUNET_OK != 2903 exchange_serve_process_config (cfgfile)) 2904 { 2905 global_ret = EXIT_NOTCONFIGURED; 2906 GNUNET_SCHEDULER_shutdown (); 2907 return; 2908 } 2909 if (GNUNET_OK != 2910 TEH_spa_init ()) 2911 { 2912 global_ret = EXIT_NOTCONFIGURED; 2913 GNUNET_SCHEDULER_shutdown (); 2914 return; 2915 } 2916 if (GNUNET_OK != 2917 TALER_TEMPLATING_init (TALER_EXCHANGE_project_data ())) 2918 { 2919 global_ret = EXIT_NOTINSTALLED; 2920 GNUNET_SCHEDULER_shutdown (); 2921 return; 2922 } 2923 if (GNUNET_SYSERR == 2924 TALER_EXCHANGEDB_preflight (TEH_pg)) 2925 { 2926 GNUNET_break (0); 2927 global_ret = EXIT_NO_RESTART; 2928 GNUNET_SCHEDULER_shutdown (); 2929 return; 2930 } 2931 /* Initialize age restriction from [exchange] section */ 2932 TEH_age_restriction_enabled = false; 2933 memset (&TEH_age_restriction_mask, 2934 0, 2935 sizeof (TEH_age_restriction_mask)); 2936 if (GNUNET_YES == 2937 GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg, 2938 "exchange", 2939 "AGE_RESTRICTION_ENABLED")) 2940 { 2941 char *groups = NULL; 2942 struct TALER_AgeMask mask = {0}; 2943 2944 if (GNUNET_OK != 2945 GNUNET_CONFIGURATION_get_value_string (TEH_cfg, 2946 "exchange", 2947 "AGE_GROUPS", 2948 &groups)) 2949 groups = GNUNET_strdup ( 2950 "8:10:12:14:16:18:21"); 2951 if (GNUNET_OK != 2952 TALER_parse_age_group_string (groups, 2953 &mask)) 2954 { 2955 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2956 "Invalid AGE_GROUPS value in [exchange]: %s\n", 2957 groups); 2958 GNUNET_free (groups); 2959 global_ret = EXIT_NOTINSTALLED; 2960 GNUNET_SCHEDULER_shutdown (); 2961 return; 2962 } 2963 GNUNET_free (groups); 2964 TEH_age_restriction_mask = mask; 2965 TEH_age_restriction_enabled = true; 2966 } 2967 if (GNUNET_OK != 2968 TEH_keys_init ()) 2969 { 2970 GNUNET_break (0); 2971 global_ret = EXIT_NO_RESTART; 2972 GNUNET_SCHEDULER_shutdown (); 2973 return; 2974 } 2975 2976 TEH_load_terms (TEH_cfg); 2977 TEH_curl_ctx 2978 = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 2979 &exchange_curl_rc); 2980 if (NULL == TEH_curl_ctx) 2981 { 2982 GNUNET_break (0); 2983 global_ret = EXIT_NO_RESTART; 2984 GNUNET_SCHEDULER_shutdown (); 2985 return; 2986 } 2987 exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEH_curl_ctx); 2988 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 2989 NULL); 2990 ret = TALER_MHD_listen_bind (TEH_cfg, 2991 "exchange", 2992 &start_daemon, 2993 NULL); 2994 switch (ret) 2995 { 2996 case GNUNET_SYSERR: 2997 global_ret = EXIT_NOTCONFIGURED; 2998 GNUNET_SCHEDULER_shutdown (); 2999 return; 3000 case GNUNET_NO: 3001 if (! have_daemons) 3002 { 3003 global_ret = EXIT_NOTCONFIGURED; 3004 GNUNET_SCHEDULER_shutdown (); 3005 return; 3006 } 3007 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3008 "Could not open all configured listen sockets\n"); 3009 break; 3010 case GNUNET_OK: 3011 break; 3012 } 3013 global_ret = EXIT_SUCCESS; 3014 atexit (&write_stats); 3015 #if HAVE_DEVELOPER 3016 if (NULL != input_filename) 3017 run_single_request (); 3018 #endif 3019 } 3020 3021 3022 /** 3023 * The main function of the taler-exchange-httpd server ("the exchange"). 3024 * 3025 * @param argc number of arguments from the command line 3026 * @param argv command line arguments 3027 * @return 0 ok, 1 on error 3028 */ 3029 int 3030 main (int argc, 3031 char *const *argv) 3032 { 3033 const struct GNUNET_GETOPT_CommandLineOption options[] = { 3034 GNUNET_GETOPT_option_flag ('a', 3035 "allow-timetravel", 3036 "allow clients to request /keys for arbitrary timestamps (for testing and development only)", 3037 &TEH_allow_keys_timetravel), 3038 GNUNET_GETOPT_option_flag ('C', 3039 "connection-close", 3040 "force HTTP connections to be closed after each request", 3041 &connection_close), 3042 GNUNET_GETOPT_option_flag ('I', 3043 "check-invariants", 3044 "enable expensive invariant checks", 3045 &TEH_check_invariants_flag), 3046 GNUNET_GETOPT_option_flag ('r', 3047 "allow-reuse-address", 3048 "allow multiple HTTPDs to listen to the same port", 3049 &allow_address_reuse), 3050 GNUNET_GETOPT_option_uint ('t', 3051 "timeout", 3052 "SECONDS", 3053 "after how long do connections timeout by default (in seconds)", 3054 &connection_timeout), 3055 GNUNET_GETOPT_option_timetravel ('T', 3056 "timetravel"), 3057 #if HAVE_DEVELOPER 3058 GNUNET_GETOPT_option_filename ('f', 3059 "file-input", 3060 "FILENAME", 3061 "run in test-mode using FILENAME as the HTTP request to process, use '-' to read from stdin", 3062 &input_filename), 3063 #endif 3064 GNUNET_GETOPT_option_help ( 3065 TALER_EXCHANGE_project_data (), 3066 "HTTP server providing a RESTful API to access a Taler exchange"), 3067 GNUNET_GETOPT_OPTION_END 3068 }; 3069 enum GNUNET_GenericReturnValue ret; 3070 3071 ret = GNUNET_PROGRAM_run ( 3072 TALER_EXCHANGE_project_data (), 3073 argc, argv, 3074 "taler-exchange-httpd", 3075 "Taler exchange HTTP service", 3076 options, 3077 &run, NULL); 3078 if (GNUNET_SYSERR == ret) 3079 return EXIT_INVALIDARGUMENT; 3080 if (GNUNET_NO == ret) 3081 return EXIT_SUCCESS; 3082 return global_ret; 3083 } 3084 3085 3086 /* end of taler-exchange-httpd.c */