sync-httpd2.c (27422B)
1 /* 2 This file is part of TALER 3 (C) 2019--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 General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file sync/sync-httpd2.c 18 * @brief HTTP serving layer intended to provide basic backup operations 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include <gnunet/gnunet_util_lib.h> 23 #include "sync/sync_util.h" 24 #include "sync-httpd2.h" 25 #include "sync/sync_database_lib.h" 26 #include "sync-httpd2_config.h" 27 #include "sync-httpd2_post-backups-KEY-blocks-NONCE.h" 28 #include "sync-httpd2_delete-backups-KEY-blocks-NONCE.h" 29 #include "sync-httpd2_get-backups-KEY-objects-UID.h" 30 #include "sync-httpd2_post-backups-KEY-objects-UID.h" 31 #include "sync-httpd2_get-backups-KEY.h" 32 #include "sync-httpd2_get-backups-KEY-blocks.h" 33 34 35 /** 36 * @brief A handler for a backup sub-operation under /backups/$KEY/. 37 * 38 * Each entry maps an operation name ("block" or "object") and an 39 * HTTP method to a handler function. 40 */ 41 struct SH_BackupOperationHandler 42 { 43 44 /** 45 * Operation name, e.g. "block" or "object". 46 */ 47 const char *operation; 48 49 /** 50 * HTTP method this handler is for. 51 */ 52 enum MHD_HTTP_Method method; 53 54 /** 55 * Function to call. 56 * 57 * @param request the MHD request to handle 58 * @param account_pub public key of the account 59 * @param args path segments after the operation name 60 * @param upload_size number of bytes being uploaded 61 * @param is_update whether the operation is an update 62 * @return MHD action 63 */ 64 const struct MHD_Action *(*handler)( 65 struct MHD_Request *request, 66 const struct SYNC_AccountPublicKeyP *account_pub, 67 const char *const args[], 68 uint_fast64_t upload_size, 69 bool is_update); 70 71 /** 72 * Number of path segments this handler expects 73 * after the operation name. 74 */ 75 unsigned int nargs; 76 77 /** 78 * Is @e nargs only an upper bound? 79 */ 80 bool nargs_is_upper_bound; 81 }; 82 83 84 /** 85 * Should a "Connection: close" header be added to each HTTP response? 86 */ 87 static int SH_sync_connection_close; 88 89 /** 90 * Upload limit to the service, in megabytes. 91 */ 92 unsigned long long int SH_upload_limit_mb; 93 94 /** 95 * How many uploads of the maximum permitted size the shared upload 96 * buffer pool must have room for. 97 * 98 * Block uploads are handed to the handler in one piece 99 * (#MHD_action_process_upload_full()), so MHD buffers the whole body 100 * out of a daemon-wide pool. The pool is a budget, not a 101 * pre-allocation -- MHD mallocs per request and gives the space back 102 * when the request ends -- so this bounds the worst-case resident size 103 * of concurrent uploads rather than costing anything up front. 104 */ 105 #define UPLOAD_POOL_CONCURRENCY 8 106 107 108 bool 109 SH_upload_too_large (uint_fast64_t upload_size, 110 size_t *buff_size) 111 { 112 uint_fast64_t limit; 113 114 limit = ((uint_fast64_t) SH_upload_limit_mb) * 1024 * 1024; 115 if (MHD_SIZE_UNKNOWN == upload_size) 116 { 117 /* Chunked upload: the length is not known yet, so bound the buffer 118 by the limit and let MHD fail the request if it does not fit. */ 119 *buff_size = (size_t) GNUNET_MIN (limit, 120 (uint_fast64_t) SIZE_MAX); 121 return false; 122 } 123 if ( (upload_size >= limit) || 124 (upload_size > SIZE_MAX) ) 125 return true; 126 *buff_size = (size_t) upload_size; 127 return false; 128 } 129 130 131 /** 132 * Annual fee for the backup account. 133 */ 134 struct TALER_Amount SH_annual_fee; 135 136 /** 137 * Our Taler backend to process payments. 138 */ 139 char *SH_backend_url; 140 141 /** 142 * Our fulfillment URL. 143 */ 144 char *SH_fulfillment_url; 145 146 /** 147 * Our context for making HTTP requests. 148 */ 149 struct GNUNET_CURL_Context *SH_ctx; 150 151 /** 152 * Reschedule context for #SH_ctx. 153 */ 154 static struct GNUNET_CURL_RescheduleContext *rc; 155 156 /** 157 * Global return code 158 */ 159 static int global_ret; 160 161 /** 162 * Set to true if we have started an MHD daemons. 163 */ 164 static bool have_daemons; 165 166 /** 167 * Username and password to use for client authentication 168 * (optional). 169 */ 170 static char *userpass; 171 172 /** 173 * Type of the client's TLS certificate (optional). 174 */ 175 static char *certtype; 176 177 /** 178 * File with the client's TLS certificate (optional). 179 */ 180 static char *certfile; 181 182 /** 183 * File with the client's TLS private key (optional). 184 */ 185 static char *keyfile; 186 187 /** 188 * This value goes in the Authorization:-header. 189 */ 190 static char *apikey; 191 192 /** 193 * Passphrase to decrypt client's TLS private key file (optional). 194 */ 195 static char *keypass; 196 197 /** 198 * Amount of insurance. 199 */ 200 struct TALER_Amount SH_insurance; 201 202 203 /** 204 * Function to respond to GET requests on '/'. 205 * 206 * @param request the MHD request to handle 207 * @param upload_size number of bytes uploaded 208 * @return MHD action 209 */ 210 static const struct MHD_Action * 211 respond_root (struct MHD_Request *request, 212 uint_fast64_t upload_size) 213 { 214 const char *msg = "Hello, I'm sync. This HTTP server is not for humans.\n"; 215 struct MHD_Response *resp; 216 217 GNUNET_break_op (0 == upload_size); 218 resp = MHD_response_from_buffer_static ( 219 MHD_HTTP_STATUS_OK, 220 strlen (msg), 221 msg); 222 GNUNET_break (MHD_SC_OK == 223 MHD_response_add_header (resp, 224 MHD_HTTP_HEADER_CONTENT_TYPE, 225 "text/plain")); 226 return MHD_action_from_response (request, 227 resp); 228 } 229 230 231 /** 232 * Function to respond to GET requests on unknown URLs. 233 * 234 * @param request the MHD request to handle 235 * @param upload_size number of bytes uploaded 236 * @return MHD action 237 */ 238 static const struct MHD_Action * 239 respond_404 (struct MHD_Request *request, 240 uint_fast64_t upload_size) 241 { 242 const char *msg = "<html><title>404: not found</title></html>"; 243 struct MHD_Response *resp; 244 245 GNUNET_break_op (0 == upload_size); 246 resp = MHD_response_from_buffer_static ( 247 MHD_HTTP_STATUS_NOT_FOUND, 248 strlen (msg), 249 msg); 250 GNUNET_break (MHD_SC_OK == 251 MHD_response_add_header (resp, 252 MHD_HTTP_HEADER_CONTENT_TYPE, 253 "text/html")); 254 return MHD_action_from_response (request, 255 resp); 256 } 257 258 259 /** 260 * Function to respond to GET requests on '/agpl'. 261 * 262 * @param request the MHD request to handle 263 * @param upload_size number of bytes uploaded 264 * @return MHD action 265 */ 266 static const struct MHD_Action * 267 respond_agpl (struct MHD_Request *request, 268 uint_fast64_t upload_size) 269 { 270 GNUNET_break_op (0 == upload_size); 271 return TALER_MHD2_reply_agpl (request, 272 "https://git.taler.net/sync.git/"); 273 } 274 275 276 /** 277 * Maximum length of the encoded account public key in the URL. 278 * An EdDSA public key encodes to 52 characters. 279 */ 280 #define ACCOUNT_KEY_MAX 64 281 282 /** 283 * Maximum reasonable subpath length after /backups/$KEY/. 284 */ 285 #define SUBPATH_MAX 256 286 287 /** 288 * Maximum number of path segments we split a subpath into, 289 * including the operation name. 290 */ 291 #define SUBPATH_MAX_SEGMENTS 3 292 293 294 /** 295 * Handle a request on a subpath under /backups/$KEY/. 296 * 297 * Parses the subpath (e.g. "block/NONCE" or "object/UID"), 298 * splits it into segments, and dispatches to the appropriate 299 * handler using the data-driven #SH_BackupOperationHandler 300 * table. 301 * 302 * @param request the MHD request to handle 303 * @param account_pub public key of the account 304 * @param method HTTP method of the request 305 * @param subpath remainder of the URL after /backups/$KEY/ 306 * @param upload_size number of bytes being uploaded 307 * @return MHD action 308 */ 309 static const struct MHD_Action * 310 handle_backup_subpath (struct MHD_Request *request, 311 const struct SYNC_AccountPublicKeyP *account_pub, 312 enum MHD_HTTP_Method method, 313 const char *subpath, 314 uint_fast64_t upload_size) 315 { 316 static struct SH_BackupOperationHandler bhandlers[] = { 317 { 318 .operation = "blocks", 319 .method = MHD_HTTP_METHOD_POST, 320 .handler = &SH_backup_block_post, 321 .nargs = 1, 322 }, 323 { 324 .operation = "blocks", 325 /* FIXME: replace with PATCH when mhd2 supports it */ 326 .method = MHD_HTTP_METHOD_PUT, 327 .handler = &SH_backup_block_post, 328 .nargs = 1, 329 }, 330 { 331 .operation = "blocks", 332 .method = MHD_HTTP_METHOD_DELETE, 333 .handler = &SH_backup_block_delete, 334 .nargs = 1, 335 }, 336 { 337 .operation = "blocks", 338 .method = MHD_HTTP_METHOD_GET, 339 .handler = &SH_backup_blocks_get, 340 .nargs = 0, 341 }, 342 { 343 .operation = "objects", 344 .method = MHD_HTTP_METHOD_GET, 345 .handler = &SH_backup_object_get, 346 .nargs = 1, 347 }, 348 { 349 .operation = "objects", 350 .method = MHD_HTTP_METHOD_POST, 351 .handler = &SH_backup_object_post, 352 .nargs = 1, 353 }, 354 { .operation = NULL } 355 }; 356 size_t slen; 357 char d[SUBPATH_MAX]; 358 char *sp; 359 /* NULL-terminated, hence one more entry than we ever fill */ 360 const char *args[SUBPATH_MAX_SEGMENTS + 1] = { NULL }; 361 unsigned int i; 362 363 GNUNET_assert (NULL != request); 364 GNUNET_assert (NULL != subpath); 365 slen = strlen (subpath); 366 if (slen >= SUBPATH_MAX) 367 { 368 GNUNET_break_op (0); 369 return TALER_MHD2_reply_with_error ( 370 request, 371 MHD_HTTP_STATUS_URI_TOO_LONG, 372 TALER_EC_GENERIC_URI_TOO_LONG, 373 subpath); 374 } 375 memcpy (d, 376 subpath, 377 slen + 1); 378 379 i = 0; 380 args[i++] = strtok_r (d, 381 "/", 382 &sp); 383 while ( (NULL != args[i - 1]) && 384 (i < SUBPATH_MAX_SEGMENTS) ) 385 args[i++] = strtok_r (NULL, 386 "/", 387 &sp); 388 389 if (NULL == args[0]) 390 { 391 GNUNET_break_op (0); 392 return TALER_MHD2_reply_with_error ( 393 request, 394 MHD_HTTP_STATUS_NOT_FOUND, 395 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 396 subpath); 397 } 398 399 if (MHD_HTTP_METHOD_OPTIONS == method) 400 return TALER_MHD2_reply_cors_preflight (request); 401 402 for (unsigned int j = 0; NULL != bhandlers[j].operation; j++) 403 { 404 const struct SH_BackupOperationHandler *bh = &bhandlers[j]; 405 unsigned int nargs; 406 407 if (0 != strcmp (args[0], 408 bh->operation)) 409 continue; 410 if (bh->method != method) 411 continue; 412 413 /* Count path segments after the operation name */ 414 nargs = 0; 415 while (NULL != args[nargs + 1]) 416 nargs++; 417 418 if (bh->nargs_is_upper_bound 419 ? (nargs > bh->nargs) 420 : (nargs != bh->nargs)) 421 { 422 GNUNET_break_op (0); 423 return TALER_MHD2_reply_with_error ( 424 request, 425 MHD_HTTP_STATUS_NOT_FOUND, 426 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 427 subpath); 428 } 429 return bh->handler (request, 430 account_pub, 431 &args[1], 432 upload_size, 433 /* FIXME: replace with PATCH when mhd2 supports it */ 434 MHD_HTTP_METHOD_PUT == bh->method); 435 } 436 437 GNUNET_break_op (0); 438 return TALER_MHD2_reply_with_error ( 439 request, 440 MHD_HTTP_STATUS_NOT_FOUND, 441 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 442 args[0]); 443 } 444 445 446 /** 447 * A client has requested the given url using the given method 448 * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, 449 * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback 450 * must call MHD callbacks to provide content to give back to the 451 * client. 452 * 453 * @param cls argument given together with the function 454 * pointer when the handler was registered with MHD 455 * @param request the request to handle 456 * @param path the requested uri (without arguments after "?") 457 * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, 458 * #MHD_HTTP_METHOD_PUT, etc.) 459 * @param upload_size the size of the message upload content payload, 460 * #MHD_SIZE_UNKNOWN for chunked uploads (if the final chunk 461 * has not been processed yet) 462 * @return next action 463 */ 464 static const struct MHD_Action * 465 url_handler (void *cls, 466 struct MHD_Request *request, 467 const struct MHD_String *path, 468 enum MHD_HTTP_Method method, 469 uint_fast64_t upload_size) 470 { 471 static struct SH_RequestHandler handlers[] = { 472 /* Landing page, tell humans to go away. */ 473 { 474 .url = "/", 475 .method = MHD_HTTP_METHOD_GET, 476 .handler = &respond_root 477 }, 478 { 479 .url = "/agpl", 480 .method = MHD_HTTP_METHOD_GET, 481 .handler = &respond_agpl, 482 }, 483 { 484 .url = "/config", 485 .method = MHD_HTTP_METHOD_GET, 486 .handler = &SH_handler_config, 487 }, 488 { 489 .url = NULL 490 } 491 }; 492 struct SYNC_AccountPublicKeyP account_pub; 493 494 (void) cls; 495 /* Backup API */ 496 if (0 == strncmp (path->cstr, 497 "/backups/", 498 strlen ("/backups/"))) 499 { 500 const char *ac = path->cstr + strlen ("/backups/"); 501 const char *next_slash; 502 size_t ac_len; 503 504 /* Find end of account key (next '/' or end of string) */ 505 next_slash = strchr (ac, 506 '/'); 507 if (NULL == next_slash) 508 ac_len = strlen (ac); 509 else 510 ac_len = next_slash - ac; 511 512 { 513 /* Bound before we put it on the stack; the segment is 514 client-controlled */ 515 char ac_str[ACCOUNT_KEY_MAX + 1]; 516 517 if (ac_len > ACCOUNT_KEY_MAX) 518 { 519 GNUNET_break_op (0); 520 return TALER_MHD2_reply_with_error ( 521 request, 522 MHD_HTTP_STATUS_BAD_REQUEST, 523 TALER_EC_GENERIC_PARAMETER_MALFORMED, 524 "account public key"); 525 } 526 memcpy (ac_str, 527 ac, 528 ac_len); 529 ac_str[ac_len] = '\0'; 530 if (GNUNET_OK != 531 GNUNET_CRYPTO_eddsa_public_key_from_string ( 532 ac_str, 533 ac_len, 534 &account_pub.eddsa_pub)) 535 { 536 GNUNET_break_op (0); 537 return TALER_MHD2_reply_with_error ( 538 request, 539 MHD_HTTP_STATUS_BAD_REQUEST, 540 TALER_EC_GENERIC_PARAMETER_MALFORMED, 541 ac_str); 542 } 543 } 544 545 /* Without a subpath this is the account itself; only GET makes 546 sense on it, everything else needs an operation */ 547 if ( (NULL == next_slash) || 548 ('\0' == next_slash[1]) ) 549 { 550 if (MHD_HTTP_METHOD_OPTIONS == method) 551 return TALER_MHD2_reply_cors_preflight (request); 552 if (MHD_HTTP_METHOD_GET == method) 553 return SH_backup_account_get (request, 554 &account_pub); 555 GNUNET_break_op (0); 556 return TALER_MHD2_reply_with_error ( 557 request, 558 MHD_HTTP_STATUS_NOT_FOUND, 559 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 560 path->cstr); 561 } 562 563 /* Dispatch to sub-handler */ 564 return handle_backup_subpath (request, 565 &account_pub, 566 method, 567 next_slash + 1, 568 upload_size); 569 } 570 571 for (unsigned int i = 0; NULL != handlers[i].url; i++) 572 { 573 struct SH_RequestHandler *rh = &handlers[i]; 574 575 if (0 == strcmp (path->cstr, 576 rh->url)) 577 { 578 if (MHD_HTTP_METHOD_OPTIONS == method) 579 { 580 return TALER_MHD2_reply_cors_preflight (request); 581 } 582 if (rh->method == method) 583 { 584 return rh->handler (request, 585 upload_size); 586 } 587 } 588 } 589 return respond_404 (request, 590 upload_size); 591 } 592 593 594 /** 595 * Shutdown task. Invoked when the application is being terminated. 596 * 597 * @param cls NULL 598 */ 599 static void 600 do_shutdown (void *cls) 601 { 602 (void) cls; 603 TALER_MHD2_daemons_halt (); 604 if (NULL != SH_ctx) 605 { 606 GNUNET_CURL_fini (SH_ctx); 607 SH_ctx = NULL; 608 } 609 if (NULL != rc) 610 { 611 GNUNET_CURL_gnunet_rc_destroy (rc); 612 rc = NULL; 613 } 614 TALER_MHD2_daemons_destroy (); 615 SYNCDB_fini (); 616 } 617 618 619 /** 620 * Kick MHD to run now, to be called after MHD_request_resume(). 621 * Basically, we need to explicitly resume MHD's event loop whenever 622 * we made progress serving a request. This function re-schedules 623 * the task processing MHD's activities to run immediately. 624 */ 625 /* FIXME: replace with direct call... */ 626 void 627 SH_trigger_daemon (void) 628 { 629 TALER_MHD2_daemons_trigger (); 630 } 631 632 633 /** 634 * Kick GNUnet Curl scheduler to begin curl interactions. 635 */ 636 void 637 SH_trigger_curl (void) 638 { 639 GNUNET_CURL_gnunet_scheduler_reschedule (&rc); 640 } 641 642 643 /** 644 * Callback invoked on every listen socket to start the 645 * respective MHD HTTP daemon. 646 * 647 * @param cls unused 648 * @param lsock the listen socket 649 */ 650 static void 651 start_daemon (void *cls, 652 int lsock) 653 { 654 struct MHD_Daemon *mhd; 655 656 (void) cls; 657 GNUNET_assert (-1 != lsock); 658 mhd = MHD_daemon_create (&url_handler, 659 NULL); 660 if (NULL == mhd) 661 { 662 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 663 "Failed to launch HTTP service, exiting.\n"); 664 global_ret = EXIT_NO_RESTART; 665 GNUNET_SCHEDULER_shutdown (); 666 return; 667 } 668 /* Without this, the pool defaults to a fraction of the memory the 669 daemon would use for connections -- about 1 MB, far below 670 UPLOAD_LIMIT_MB -- and MHD answers anything bigger with its own 671 413 before the handler ever runs, whatever the configured limit 672 says. See the FIXME in sync-httpd2_post-backups-KEY-blocks-NONCE.c 673 for the incremental-upload alternative that would drop the pool 674 dependency entirely. */ 675 GNUNET_assert (MHD_SC_OK == 676 MHD_DAEMON_SET_OPTIONS ( 677 mhd, 678 MHD_D_OPTION_DEFAULT_TIMEOUT_MILSEC (10000), 679 MHD_D_OPTION_LARGE_POOL_SIZE ( 680 (size_t) GNUNET_MIN ( 681 ((uint_fast64_t) SH_upload_limit_mb) 682 * 1024 * 1024 683 * UPLOAD_POOL_CONCURRENCY, 684 (uint_fast64_t) SIZE_MAX)), 685 MHD_D_OPTION_LISTEN_SOCKET (lsock))); 686 have_daemons = true; 687 TALER_MHD2_daemon_start (mhd); 688 } 689 690 691 /** 692 * Main function that will be run by the scheduler. 693 * 694 * @param cls closure 695 * @param args remaining command-line arguments 696 * @param cfgfile name of the configuration file used (for saving, can be 697 * NULL!) 698 * @param config configuration 699 */ 700 static void 701 run (void *cls, 702 char *const *args, 703 const char *cfgfile, 704 const struct GNUNET_CONFIGURATION_Handle *config) 705 { 706 enum TALER_MHD2_GlobalOptions go; 707 708 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 709 "Starting sync-httpd2\n"); 710 go = TALER_MHD2_GO_NONE; 711 if (SH_sync_connection_close) 712 go |= TALER_MHD2_GO_FORCE_CONNECTION_CLOSE; 713 TALER_MHD2_setup (go); 714 global_ret = EXIT_NOTCONFIGURED; 715 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 716 NULL); 717 if (GNUNET_OK != 718 GNUNET_CONFIGURATION_get_value_number (config, 719 "sync", 720 "UPLOAD_LIMIT_MB", 721 &SH_upload_limit_mb)) 722 { 723 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 724 "sync", 725 "UPLOAD_LIMIT_MB"); 726 GNUNET_SCHEDULER_shutdown (); 727 return; 728 } 729 if (GNUNET_OK != 730 TALER_config_get_amount (config, 731 "sync", 732 "INSURANCE", 733 &SH_insurance)) 734 { 735 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 736 "sync", 737 "INSURANCE"); 738 GNUNET_SCHEDULER_shutdown (); 739 return; 740 } 741 if (GNUNET_OK != 742 TALER_config_get_amount (config, 743 "sync", 744 "ANNUAL_FEE", 745 &SH_annual_fee)) 746 { 747 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 748 "sync", 749 "ANNUAL_FEE"); 750 GNUNET_SCHEDULER_shutdown (); 751 return; 752 } 753 if (GNUNET_OK != 754 GNUNET_CONFIGURATION_get_value_string (config, 755 "sync", 756 "PAYMENT_BACKEND_URL", 757 &SH_backend_url)) 758 { 759 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 760 "sync", 761 "PAYMENT_BACKEND_URL"); 762 GNUNET_SCHEDULER_shutdown (); 763 return; 764 } 765 /* The merchant backend API expects the base URL to end with '/' 766 (see TALER_url_join), so ensure it does. */ 767 if ('/' != SH_backend_url[strlen (SH_backend_url) - 1]) 768 { 769 char *tmp; 770 771 GNUNET_asprintf (&tmp, 772 "%s/", 773 SH_backend_url); 774 GNUNET_free (SH_backend_url); 775 SH_backend_url = tmp; 776 } 777 /* The payment flow turns this into a "taler://" URI, so it needs a 778 scheme and a host */ 779 { 780 const char *host = NULL; 781 782 if (0 == strncasecmp ("https://", 783 SH_backend_url, 784 strlen ("https://"))) 785 host = &SH_backend_url[strlen ("https://")]; 786 else if (0 == strncasecmp ("http://", 787 SH_backend_url, 788 strlen ("http://"))) 789 host = &SH_backend_url[strlen ("http://")]; 790 if ( (NULL == host) || 791 ('\0' == *host) ) 792 { 793 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 794 "sync", 795 "PAYMENT_BACKEND_URL", 796 "must be an http:// or https:// URL" 797 " with a host name"); 798 GNUNET_SCHEDULER_shutdown (); 799 return; 800 } 801 } 802 if (GNUNET_OK != 803 GNUNET_CONFIGURATION_get_value_string (config, 804 "sync", 805 "FULFILLMENT_URL", 806 &SH_fulfillment_url)) 807 { 808 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 809 "sync", 810 "FULFILLMENT_URL"); 811 GNUNET_SCHEDULER_shutdown (); 812 return; 813 } 814 815 /* setup HTTP client event loop */ 816 SH_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 817 &rc); 818 if (NULL == SH_ctx) 819 { 820 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 821 "Failed to initialize HTTP client, exiting.\n"); 822 global_ret = EXIT_NO_RESTART; 823 GNUNET_SCHEDULER_shutdown (); 824 return; 825 } 826 rc = GNUNET_CURL_gnunet_rc_create (SH_ctx); 827 if (NULL != userpass) 828 GNUNET_CURL_set_userpass (SH_ctx, 829 userpass); 830 if (NULL != keyfile) 831 GNUNET_CURL_set_tlscert (SH_ctx, 832 certtype, 833 certfile, 834 keyfile, 835 keypass); 836 if (GNUNET_OK == 837 GNUNET_CONFIGURATION_get_value_string (config, 838 "sync", 839 "API_KEY", 840 &apikey)) 841 { 842 char *auth_header; 843 844 GNUNET_asprintf (&auth_header, 845 "%s: %s", 846 MHD_HTTP_HEADER_AUTHORIZATION, 847 apikey); 848 if (GNUNET_OK != 849 GNUNET_CURL_append_header (SH_ctx, 850 auth_header)) 851 { 852 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 853 "Failed to set %s header, trying without\n", 854 MHD_HTTP_HEADER_AUTHORIZATION); 855 } 856 GNUNET_free (auth_header); 857 } 858 859 if (GNUNET_OK != 860 SYNCDB_init (config)) 861 { 862 global_ret = EXIT_NOTCONFIGURED; 863 GNUNET_SCHEDULER_shutdown (); 864 return; 865 } 866 if (GNUNET_OK != 867 SYNCDB_preflight ()) 868 { 869 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 870 "Database not setup. Did you run sync-dbinit?\n"); 871 GNUNET_SCHEDULER_shutdown (); 872 return; 873 } 874 { 875 enum GNUNET_GenericReturnValue ret; 876 877 ret = TALER_MHD_listen_bind (config, 878 "sync", 879 &start_daemon, 880 NULL); 881 switch (ret) 882 { 883 case GNUNET_SYSERR: 884 global_ret = EXIT_NOTCONFIGURED; 885 GNUNET_SCHEDULER_shutdown (); 886 return; 887 case GNUNET_NO: 888 if (! have_daemons) 889 { 890 global_ret = EXIT_NOTCONFIGURED; 891 GNUNET_SCHEDULER_shutdown (); 892 return; 893 } 894 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 895 "Could not open all configured listen sockets\n"); 896 break; 897 case GNUNET_OK: 898 break; 899 } 900 } 901 global_ret = EXIT_SUCCESS; 902 } 903 904 905 /** 906 * The main function of the serve tool 907 * 908 * @param argc number of arguments from the command line 909 * @param argv command line arguments 910 * @return 0 ok, 1 on error 911 */ 912 int 913 main (int argc, 914 char *const *argv) 915 { 916 struct GNUNET_GETOPT_CommandLineOption options[] = { 917 GNUNET_GETOPT_option_string ('A', 918 "auth", 919 "USERNAME:PASSWORD", 920 "use the given USERNAME and PASSWORD for client authentication", 921 &userpass), 922 /* Note: -c, -h, -L, -l and -v are reserved by GNUNET_PROGRAM_run(). 923 We use -E for the client certificate, as curl does. */ 924 GNUNET_GETOPT_option_string ('E', 925 "cert", 926 "CERTFILE", 927 "file with the TLS client certificate for TLS client authentication", 928 &certfile), 929 GNUNET_GETOPT_option_flag ('C', 930 "connection-close", 931 "force HTTP connections to be closed after each request", 932 &SH_sync_connection_close), 933 GNUNET_GETOPT_option_string ('k', 934 "key", 935 "KEYFILE", 936 "file with the private TLS key for TLS client authentication", 937 &keyfile), 938 GNUNET_GETOPT_option_string ('p', 939 "pass", 940 "KEYFILEPASSPHRASE", 941 "passphrase needed to decrypt the TLS client private key file", 942 &keypass), 943 GNUNET_GETOPT_option_string ('t', 944 "type", 945 "CERTTYPE", 946 "type of the TLS client certificate, defaults to PEM if not specified", 947 &certtype), 948 GNUNET_GETOPT_OPTION_END 949 }; 950 enum GNUNET_GenericReturnValue ret; 951 952 ret = GNUNET_PROGRAM_run (SYNC_project_data (), 953 argc, argv, 954 "sync-httpd2", 955 "sync HTTP interface", 956 options, 957 &run, NULL); 958 if (GNUNET_NO == ret) 959 return EXIT_SUCCESS; 960 if (GNUNET_SYSERR == ret) 961 return EXIT_INVALIDARGUMENT; 962 return global_ret; 963 }