taler-twister-service.c (78258B)
1 /* 2 This file is part of GNUnet. 3 Copyright (C) 2012-2014 GNUnet e.V. 4 Copyright (C) 2018 Taler Systems SA 5 6 GNUnet is free software; you can redistribute it and/or 7 modify it under the terms of the GNU General Public License 8 as published by the Free Software Foundation; either version 9 3, or (at your option) any later version. 10 11 GNUnet is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public 17 License along with GNUnet; see the file COPYING. If not, 18 write to the Free Software Foundation, Inc., 51 Franklin 19 Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 */ 21 22 /** 23 * @author Martin Schanzenbach 24 * @author Christian Grothoff 25 * @author Marcello Stanisci 26 * @file src/twister/taler-twister-service.c 27 * @brief HTTP proxy that acts as a man in the 28 * middle making changes to requests or responses 29 */ 30 #include "platform.h" 31 #include <microhttpd.h> 32 #include <curl/curl.h> 33 #include <gnunet/gnunet_util_lib.h> 34 #include <gnunet/gnunet_json_lib.h> 35 #include <gnunet/gnunet_mhd_compat.h> 36 #include <gnunet/gnunet_mhd_lib.h> 37 #include "twister.h" 38 #include "taler_twister_service.h" 39 #include <jansson.h> 40 #include <microhttpd.h> 41 #include <zlib.h> 42 43 44 #define TWISTER_LOG_INFO(...) \ 45 GNUNET_log (GNUNET_ERROR_TYPE_INFO, __VA_ARGS__) 46 #define TWISTER_LOG_DEBUG(...) \ 47 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__) 48 #define TWISTER_LOG_WARNING(...) \ 49 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, __VA_ARGS__) 50 #define TWISTER_LOG_ERROR(...) \ 51 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, __VA_ARGS__) 52 53 #define REQUEST_BUFFER_MAX (1024 * 1024) 54 #define UNIX_BACKLOG 500 55 56 /** 57 * Log curl error. 58 * 59 * @param level log level 60 * @param fun name of curl_easy-function that gave the error 61 * @param rc return code from curl 62 */ 63 #define LOG_CURL_EASY(level,fun,rc) \ 64 GNUNET_log (level, _ ("%s failed at %s:%d: `%s'\n"), fun, __FILE__, \ 65 __LINE__, \ 66 curl_easy_strerror (rc)) 67 68 /* ******** Datastructures for HTTP handling ********** */ 69 70 71 /** 72 * State machine for HTTP requests (per request). 73 */ 74 enum RequestState 75 { 76 /** 77 * Starting state. 78 */ 79 REQUEST_STATE_WITH_MHD = 0, 80 81 /** 82 * We've started receiving upload data from MHD. 83 */ 84 REQUEST_STATE_CLIENT_UPLOAD_STARTED, 85 86 /** 87 * Wa have started uploading data to the proxied service. 88 */ 89 REQUEST_STATE_PROXY_UPLOAD_STARTED, 90 91 /** 92 * We're done with the upload from MHD. 93 */ 94 REQUEST_STATE_CLIENT_UPLOAD_DONE, 95 96 /** 97 * We're done uploading data to the proxied service. 98 */ 99 REQUEST_STATE_PROXY_UPLOAD_DONE, 100 101 /** 102 * We've finished uploading data via CURL and can now download. 103 */ 104 REQUEST_STATE_PROXY_DOWNLOAD_STARTED, 105 106 /** 107 * We've finished receiving download data from cURL. 108 */ 109 REQUEST_STATE_PROXY_DOWNLOAD_DONE 110 }; 111 112 113 /** 114 * A header list 115 */ 116 struct HttpResponseHeader 117 { 118 /** 119 * DLL 120 */ 121 struct HttpResponseHeader *next; 122 123 /** 124 * DLL 125 */ 126 struct HttpResponseHeader *prev; 127 128 /** 129 * Header type 130 */ 131 char *type; 132 133 /** 134 * Header value 135 */ 136 char *value; 137 }; 138 139 140 /** 141 * A structure for socks requests 142 */ 143 struct HttpRequest 144 { 145 146 /** 147 * Kept in DLL. 148 */ 149 struct HttpRequest *prev; 150 151 /** 152 * Kept in DLL. 153 */ 154 struct HttpRequest *next; 155 156 /** 157 * MHD request that triggered us. 158 */ 159 struct MHD_Connection *con; 160 161 /** 162 * Client socket read task 163 */ 164 struct GNUNET_SCHEDULER_Task *rtask; 165 166 /** 167 * Client socket write task 168 */ 169 struct GNUNET_SCHEDULER_Task *wtask; 170 171 /** 172 * Hold the response obtained by modifying the original one. 173 */ 174 struct MHD_Response *mod_response; 175 176 /** 177 * MHD response object for this request. 178 */ 179 struct MHD_Response *response; 180 181 /** 182 * The URL to fetch 183 */ 184 char *url; 185 186 /** 187 * JSON we use to parse payloads (in both directions). 188 */ 189 json_t *json; 190 191 /** 192 * Handle to cURL 193 */ 194 CURL *curl; 195 196 /** 197 * HTTP request headers for the curl request. 198 */ 199 struct curl_slist *headers; 200 201 /** 202 * Headers from response 203 */ 204 struct HttpResponseHeader *header_head; 205 206 /** 207 * Headers from response 208 */ 209 struct HttpResponseHeader *header_tail; 210 211 /** 212 * Buffer we use for moving data between MHD and 213 * curl (in both directions). 214 */ 215 char *io_buf; 216 217 /** 218 * Number of bytes already in the IO buffer. 219 */ 220 size_t io_len; 221 222 /** 223 * Number of bytes allocated for the IO buffer. 224 */ 225 unsigned int io_size; 226 227 /** 228 * HTTP response code to give to MHD for the response. 229 */ 230 unsigned int response_code; 231 232 /** 233 * Request processing state machine. 234 */ 235 enum RequestState state; 236 237 /** 238 * Did we suspend MHD processing? 239 */ 240 enum GNUNET_GenericReturnValue suspended; 241 242 /** 243 * Did we pause CURL processing? 244 */ 245 int curl_paused; 246 }; 247 248 249 /* *********************** Globals **************************** */ 250 251 /** 252 * The cURL download task (curl multi API). 253 */ 254 static struct GNUNET_SCHEDULER_Task *curl_download_task; 255 256 /** 257 * DLL of active HTTP requests. 258 */ 259 static struct HttpRequest *hr_head; 260 261 /** 262 * DLL of active HTTP requests. 263 */ 264 static struct HttpRequest *hr_tail; 265 266 /** 267 * The cURL multi handle 268 */ 269 static CURLM *curl_multi; 270 271 /** 272 * The daemon handle 273 */ 274 static struct MHD_Daemon *mhd_daemon; 275 276 /** 277 * The task ID 278 */ 279 static struct GNUNET_SCHEDULER_Task *httpd_task; 280 281 /** 282 * Response we return on cURL failures. 283 */ 284 static struct MHD_Response *curl_failure_response; 285 286 /** 287 * Our configuration. 288 */ 289 static const struct GNUNET_CONFIGURATION_Handle *cfg; 290 291 /** 292 * Destination to which HTTP server we forward requests to. 293 * Of the format "http://servername:PORT" 294 */ 295 static char *target_server_base_url; 296 297 /* ******************* Transformations ***************** */ 298 299 /** 300 * Chaos probability (in percent); this value is taken from 301 * the config and stays valid for all the Twister's lifetime. 302 */ 303 static long long unsigned int chaos_rate = 0; 304 305 306 /** 307 * Set to non-zero if we should change the next response code. 308 * In this case, this is the value to use. 309 */ 310 static unsigned int hack_response_code; 311 312 /** 313 * Will point to a JSON object to delete. Only cares about 314 * _download_ objects. 315 */ 316 static char *delete_path; 317 318 /** 319 * Will point to a JSON _string_ object 320 * which will get a character flipped. 321 * Only checked against _download_ objects. 322 */ 323 static char *flip_path_dl; 324 325 /** 326 * Will point to a JSON _string_ object 327 * which will get a character flipped. 328 * Only checked against _upload_ objects. 329 */ 330 static char *flip_path_ul; 331 332 /** 333 * Will point to a JSON object to modify. 334 * Only checked against _download_ objects. 335 */ 336 static char *modify_path_dl; 337 338 /** 339 * Will point to a JSON object to modify. 340 * Only checked against _upload_ objects. 341 */ 342 static char *modify_path_ul; 343 344 345 /** 346 * If not NULL, the name of the header value to 347 * modify in the response. 348 */ 349 static char *modify_header_dl; 350 351 352 /** 353 * If true, will randomly truncate the request body 354 * to upload to the proxied service. 355 */ 356 static unsigned int malform_upload; 357 358 /** 359 * If true, will randomly truncate the response body 360 * before returning to the client. 361 */ 362 static unsigned int malform; 363 364 /** 365 * New value to give the modified field. 366 * Both for upload and download mods. 367 */ 368 static char *modify_value; 369 370 /** 371 * Size of the malformed body to be uploaded to the 372 * proxied service. 373 */ 374 static size_t malformed_size; 375 376 /* ********************* Global helpers ****************** */ 377 378 /** 379 * Run MHD now, we have extra data ready for the callback. 380 */ 381 static void 382 run_mhd_now (void); 383 384 385 /* *************** HTTP handling with cURL ***************** */ 386 387 388 /** 389 * Transform _one_ CURL header (gotten from the request) into 390 * MHD format and put it into the response headers list; mostly 391 * copies the headers, but makes special adjustments based on 392 * control requests. 393 * 394 * @param buffer curl buffer with a single 395 * line of header data; not 0-terminated! 396 * @param size curl blocksize 397 * @param nmemb curl blocknumber 398 * @param cls our `struct HttpRequest *` 399 * @return size of processed bytes 400 */ 401 static size_t 402 curl_check_hdr (void *buffer, 403 size_t size, 404 size_t nmemb, 405 void *cls) 406 { 407 struct HttpRequest *hr = cls; 408 struct HttpResponseHeader *header; 409 size_t bytes = size * nmemb; 410 char *ndup; 411 const char *hdr_type; 412 char *hdr_val; 413 char *tok; 414 415 /* Raw line is not guaranteed to be null-terminated. */ 416 ndup = GNUNET_malloc (bytes + 1); 417 memcpy (ndup, 418 buffer, 419 bytes); 420 ndup[bytes] = '\0'; 421 422 hdr_type = strtok (ndup, ":"); 423 424 if (NULL == hdr_type) 425 { 426 GNUNET_free (ndup); 427 return bytes; 428 } 429 hdr_val = strtok (NULL, ""); 430 if (NULL == hdr_val) 431 { 432 GNUNET_free (ndup); 433 return bytes; 434 } 435 if (' ' == *hdr_val) 436 hdr_val++; 437 438 /* MHD does not allow certain characters in values, 439 * remove those, plus those could alter strings matching. */ 440 if (NULL != (tok = strchr (hdr_val, '\n'))) 441 *tok = '\0'; 442 if (NULL != (tok = strchr (hdr_val, '\r'))) 443 *tok = '\0'; 444 if (NULL != (tok = strchr (hdr_val, '\t'))) 445 *tok = '\0'; 446 447 TWISTER_LOG_DEBUG ("Parsed line: '%s: %s'\n", 448 hdr_type, 449 hdr_val); 450 451 /* Skip "Content-length:" header as it will be wrong, given 452 that we are man-in-the-middling the connection */ 453 if (0 == strcasecmp (hdr_type, 454 MHD_HTTP_HEADER_CONTENT_LENGTH)) 455 { 456 GNUNET_free (ndup); 457 return bytes; 458 } 459 /* Skip "Connection: Keep-Alive" header, it will be 460 done by MHD if possible */ 461 if ( (0 == strcasecmp (hdr_type, 462 MHD_HTTP_HEADER_CONNECTION)) && 463 (0 == strcasecmp (hdr_val, 464 "Keep-Alive")) ) 465 { 466 GNUNET_free (ndup); 467 return bytes; 468 } 469 if (0 != strlen (hdr_val)) /* Rely in MHD to set those */ 470 { 471 header = GNUNET_new (struct HttpResponseHeader); 472 header->type = GNUNET_strdup (hdr_type); 473 header->value = GNUNET_strdup (hdr_val); 474 GNUNET_CONTAINER_DLL_insert (hr->header_head, 475 hr->header_tail, 476 header); 477 } 478 GNUNET_free (ndup); 479 return bytes; 480 } 481 482 483 /** 484 * Create the MHD response with CURL's as starting base; 485 * mainly set the response code and parses the response into 486 * JSON, if it is such. 487 * 488 * @param hr pointer to where to store the new data. Despite 489 * its name, the struct contains response data as well. 490 * @return #GNUNET_OK if it succeeds. 491 */ 492 static enum GNUNET_GenericReturnValue 493 create_mhd_response_from_hr (struct HttpRequest *hr) 494 { 495 long resp_code; 496 json_error_t error; 497 498 if (NULL != hr->response) 499 { 500 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 501 "Response already set!\n"); 502 return GNUNET_SYSERR; 503 } 504 GNUNET_break (CURLE_OK == 505 curl_easy_getinfo (hr->curl, 506 CURLINFO_RESPONSE_CODE, 507 &resp_code)); 508 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 509 "Creating MHD response with code %u\n", 510 (unsigned int) resp_code); 511 hr->response_code = resp_code; 512 /* Note, will be NULL if io_buf does not represent 513 * a JSON value. */ 514 hr->json = json_loadb (hr->io_buf, 515 hr->io_len, 516 JSON_DECODE_ANY, 517 &error); 518 if (GNUNET_YES == hr->suspended) 519 { 520 MHD_resume_connection (hr->con); 521 hr->suspended = GNUNET_NO; 522 } 523 run_mhd_now (); 524 return GNUNET_OK; 525 } 526 527 528 /** 529 * Handle response payload data from cURL. 530 * Copies it into our `io_buf` to make it available to MHD. 531 * 532 * @param ptr pointer to the data 533 * @param size number of blocks of data 534 * @param nmemb blocksize 535 * @param ctx our `struct HttpRequest *` 536 * @return number of bytes handled 537 */ 538 static size_t 539 curl_download_cb (void *ptr, 540 size_t size, 541 size_t nmemb, 542 void *ctx) 543 { 544 struct HttpRequest *hr = ctx; 545 size_t total = size * nmemb; 546 547 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 548 "Curl download proceeding\n"); 549 550 if (REQUEST_STATE_PROXY_UPLOAD_STARTED == hr->state) 551 { 552 /* Web server started with response before we finished 553 the upload. In this case, current libcurl decides 554 to NOT complete the upload, so we should jump in the 555 state machine to process the download, dropping the 556 rest of the upload. This should only really happen 557 with uploads without "Expect: 100 Continue" and 558 Web servers responding with an error (i.e. upload 559 not allowed) */ 560 hr->state = REQUEST_STATE_PROXY_DOWNLOAD_STARTED; 561 GNUNET_log 562 (GNUNET_ERROR_TYPE_INFO, 563 "Stopping %u byte upload: we are already downloading...\n", 564 (unsigned int) hr->io_len); 565 hr->io_len = 0; 566 } 567 568 if (REQUEST_STATE_PROXY_DOWNLOAD_STARTED != hr->state) 569 { 570 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 571 "Download callback goes to sleep\n"); 572 hr->curl_paused = GNUNET_YES; 573 return CURL_WRITEFUNC_PAUSE; 574 } 575 GNUNET_assert (REQUEST_STATE_PROXY_DOWNLOAD_STARTED == 576 hr->state); 577 if (hr->io_size - hr->io_len < total) 578 { 579 GNUNET_assert (total + hr->io_size >= total); 580 GNUNET_assert (hr->io_size * 2 + 1024 > hr->io_size); 581 GNUNET_array_grow (hr->io_buf, 582 hr->io_size, 583 GNUNET_MAX (total + hr->io_len, 584 hr->io_size * 2 + 1024)); 585 } 586 GNUNET_memcpy (&hr->io_buf[hr->io_len], 587 ptr, 588 total); 589 hr->io_len += total; 590 return total; 591 } 592 593 594 /** 595 * Ask cURL for the select() sets and schedule cURL operations. 596 */ 597 static void 598 curl_download_prepare (void); 599 600 601 /** 602 * cURL callback for uploaded (PUT/POST) data. 603 * Copies from our `io_buf` to make it available to cURL. 604 * 605 * @param buf where to write the data 606 * @param size number of bytes per member 607 * @param nmemb number of members available in @a buf 608 * @param cls our `struct HttpRequest` that generated the data 609 * @return number of bytes copied to @a buf 610 */ 611 static size_t 612 curl_upload_cb (void *buf, 613 size_t size, 614 size_t nmemb, 615 void *cls) 616 { 617 struct HttpRequest *hr = cls; 618 size_t len = size * nmemb; 619 size_t to_copy; 620 621 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 622 "Upload cb is working...\n"); 623 624 if ( (REQUEST_STATE_PROXY_DOWNLOAD_STARTED == hr->state) || 625 (REQUEST_STATE_PROXY_DOWNLOAD_DONE == hr->state) ) 626 { 627 GNUNET_log 628 (GNUNET_ERROR_TYPE_INFO, 629 "Upload cb aborts: we are already downloading...\n"); 630 return CURL_READFUNC_ABORT; 631 } 632 633 if ( (0 == hr->io_len) && 634 (REQUEST_STATE_PROXY_UPLOAD_STARTED == hr->state) ) 635 { 636 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 637 "Pausing CURL UPLOAD, need more data\n"); 638 return CURL_READFUNC_PAUSE; 639 } 640 641 /** 642 * We got rescheduled because the download callback was asleep. 643 * FIXME: can this block be eliminated and the unpausing being 644 * moved in the last block where we return zero as well? 645 */ 646 if ( (0 == hr->io_len) && 647 (REQUEST_STATE_PROXY_DOWNLOAD_STARTED == hr->state) ) 648 { 649 if (GNUNET_YES == hr->curl_paused) 650 { 651 hr->curl_paused = GNUNET_NO; 652 curl_easy_pause (hr->curl, 653 CURLPAUSE_CONT); 654 } 655 curl_download_prepare (); 656 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 657 "Completed CURL UPLOAD\n"); 658 return 0; /* upload finished, can now download */ 659 } 660 to_copy = GNUNET_MIN (hr->io_len, 661 len); 662 GNUNET_memcpy (buf, 663 hr->io_buf, 664 to_copy); 665 /* shift remaining data back to the beginning of the buffer. */ 666 memmove (hr->io_buf, 667 &hr->io_buf[to_copy], 668 hr->io_len - to_copy); 669 hr->io_len -= to_copy; 670 if (0 == hr->io_len) 671 { 672 hr->state = REQUEST_STATE_PROXY_DOWNLOAD_STARTED; 673 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 674 "Completed CURL UPLOAD\n"); 675 } 676 return to_copy; 677 } 678 679 680 /* ************** helper functions ************* */ 681 682 /** 683 * Extract the hostname from a complete URL. 684 * 685 * @param url full fledged URL 686 * 687 * @return pointer to the 0-terminated hostname, to be freed 688 * by the caller. 689 */ 690 static char * 691 build_host_header (const char *url) 692 { 693 #define MARKER "://" 694 695 char *header; 696 char *end; 697 char *hostname; 698 char *dup = GNUNET_strdup (url); 699 700 hostname = strstr (dup, MARKER); 701 hostname += 3; 702 703 end = strchrnul (hostname, '/'); 704 *end = '\0'; 705 706 GNUNET_asprintf (&header, 707 "Host: %s", 708 hostname); 709 710 GNUNET_free (dup); 711 return header; 712 } 713 714 715 /* ************** main loop of cURL interaction ************* */ 716 717 718 /** 719 * Task that is run when we are ready to receive more data 720 * from curl 721 * 722 * @param cls closure 723 */ 724 static void 725 curl_task_download (void *cls); 726 727 728 /** 729 * Ask cURL for the select() sets and schedule cURL operations. 730 */ 731 static void 732 curl_download_prepare () 733 { 734 CURLMcode mret; 735 fd_set rs; 736 fd_set ws; 737 fd_set es; 738 int max; 739 struct GNUNET_NETWORK_FDSet *grs; 740 struct GNUNET_NETWORK_FDSet *gws; 741 long to; 742 struct GNUNET_TIME_Relative rtime; 743 744 if (NULL != curl_download_task) 745 { 746 GNUNET_SCHEDULER_cancel (curl_download_task); 747 curl_download_task = NULL; 748 } 749 max = -1; 750 FD_ZERO (&rs); 751 FD_ZERO (&ws); 752 FD_ZERO (&es); 753 if (CURLM_OK != (mret = curl_multi_fdset (curl_multi, 754 &rs, 755 &ws, 756 &es, 757 &max))) 758 { 759 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 760 "%s failed at %s:%d: `%s'\n", 761 "curl_multi_fdset", 762 __FILE__, 763 __LINE__, 764 curl_multi_strerror (mret)); 765 return; 766 } 767 to = -1; 768 GNUNET_break (CURLM_OK == 769 curl_multi_timeout (curl_multi, 770 &to)); 771 if (-1 == to) 772 rtime = GNUNET_TIME_UNIT_FOREVER_REL; 773 else 774 rtime = GNUNET_TIME_relative_multiply 775 (GNUNET_TIME_UNIT_MILLISECONDS, to); 776 if (-1 != max) 777 { 778 grs = GNUNET_NETWORK_fdset_create (); 779 gws = GNUNET_NETWORK_fdset_create (); 780 GNUNET_NETWORK_fdset_copy_native (grs, 781 &rs, 782 max + 1); 783 GNUNET_NETWORK_fdset_copy_native (gws, 784 &ws, 785 max + 1); 786 curl_download_task 787 = GNUNET_SCHEDULER_add_select ( 788 GNUNET_SCHEDULER_PRIORITY_DEFAULT, 789 rtime, 790 grs, gws, 791 &curl_task_download, 792 curl_multi); 793 GNUNET_NETWORK_fdset_destroy (gws); 794 GNUNET_NETWORK_fdset_destroy (grs); 795 } 796 else 797 { 798 curl_download_task = GNUNET_SCHEDULER_add_delayed 799 (rtime, 800 &curl_task_download, 801 curl_multi); 802 } 803 } 804 805 806 /** 807 * Task that is run when we are ready to receive 808 * more data from curl. 809 * 810 * @param cls closure, usually NULL. 811 */ 812 static void 813 curl_task_download (void *cls) 814 { 815 int running; 816 int msgnum; 817 struct CURLMsg *msg; 818 CURLMcode mret; 819 struct HttpRequest *hr; 820 821 (void) cls; 822 curl_download_task = NULL; 823 do 824 { 825 running = 0; 826 mret = curl_multi_perform (curl_multi, 827 &running); 828 while (NULL != (msg = curl_multi_info_read (curl_multi, 829 &msgnum))) 830 { 831 GNUNET_break 832 (CURLE_OK == curl_easy_getinfo 833 (msg->easy_handle, 834 CURLINFO_PRIVATE, 835 (char **) &hr)); 836 837 if (NULL == hr) 838 { 839 GNUNET_break (0); 840 continue; 841 } 842 switch (msg->msg) 843 { 844 case CURLMSG_NONE: 845 /* documentation says this is not used */ 846 GNUNET_break (0); 847 break; 848 case CURLMSG_DONE: 849 switch (msg->data.result) 850 { 851 case CURLE_OK: 852 case CURLE_GOT_NOTHING: 853 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 854 "CURL download completed.\n"); 855 hr->state = REQUEST_STATE_PROXY_DOWNLOAD_DONE; 856 if (NULL == hr->response) 857 GNUNET_assert (GNUNET_OK == 858 create_mhd_response_from_hr (hr)); 859 break; 860 default: 861 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 862 "Download curl failed: %s\n", 863 curl_easy_strerror (msg->data.result)); 864 /* FIXME: indicate error somehow? 865 * close MHD connection badly as well? */ 866 hr->state = REQUEST_STATE_PROXY_DOWNLOAD_DONE; 867 if (GNUNET_YES == hr->suspended) 868 { 869 MHD_resume_connection (hr->con); 870 hr->suspended = GNUNET_NO; 871 } 872 run_mhd_now (); 873 break; 874 } 875 if (NULL == hr->response) 876 hr->response = curl_failure_response; 877 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 878 "Curl request for `%s' finished (got the response)\n", 879 hr->url); 880 run_mhd_now (); 881 break; 882 case CURLMSG_LAST: 883 /* documentation says this is not used */ 884 GNUNET_break (0); 885 break; 886 default: 887 /* unexpected status code */ 888 GNUNET_break (0); 889 break; 890 } 891 } 892 ; 893 } while (mret == CURLM_CALL_MULTI_PERFORM); 894 if (CURLM_OK != mret) 895 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 896 "%s failed at %s:%d: `%s'\n", 897 "curl_multi_perform", 898 __FILE__, 899 __LINE__, 900 curl_multi_strerror (mret)); 901 if (0 == running) 902 { 903 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 904 "Suspending cURL multi loop," 905 " no more events pending\n"); 906 return; /* nothing more in progress */ 907 } 908 curl_download_prepare (); 909 } 910 911 912 /* *************** MHD response generation ***************** */ 913 914 915 /** 916 * "Filter" function that translates MHD request headers to 917 * cURL's. 918 * 919 * @param cls our `struct HttpRequest` 920 * @param kind value kind 921 * @param key field key 922 * @param value field value 923 * @return #MHD_YES to continue to iterate 924 */ 925 static MHD_RESULT 926 con_val_iter (void *cls, 927 enum MHD_ValueKind kind, 928 const char *key, 929 const char *value) 930 { 931 struct HttpRequest *hr = cls; 932 char *hdr; 933 char *new_value = NULL; 934 935 (void) kind; 936 if (0 == strcmp (MHD_HTTP_HEADER_HOST, 937 key)) 938 { 939 /* We don't take the host header as given in the request. 940 * We'll instead put the proxied service's hostname in it*/ 941 return MHD_YES; 942 } 943 944 if ((0 == strcmp (MHD_HTTP_HEADER_ACCEPT_ENCODING, 945 key)) || 946 (0 == strcmp (MHD_HTTP_HEADER_CONTENT_ENCODING, 947 key))) 948 { 949 TWISTER_LOG_INFO ("Do not re-compress request and/or do not" 950 " ask for compressed responses\n"); 951 return MHD_YES; 952 } 953 954 if ((0 == strcmp (MHD_HTTP_HEADER_CONTENT_LENGTH, 955 key))) 956 { 957 TWISTER_LOG_INFO ( 958 "Do not re-set Content-Length for request (CURLOPT_POSTFIELDSIZE did)\n"); 959 return MHD_YES; 960 } 961 962 GNUNET_asprintf (&hdr, 963 "%s: %s", 964 key, 965 value); 966 967 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 968 "Adding header `%s' to HTTP request\n", 969 hdr); 970 hr->headers = curl_slist_append (hr->headers, 971 hdr); 972 GNUNET_free (hdr); 973 GNUNET_free (new_value); 974 return MHD_YES; 975 } 976 977 978 /** 979 * Walk a JSON object preparing its modification. Name to 980 * be changed in 'walk_object'. 981 * 982 * @param path the path pointing to a object to modify. 983 * @param[out] parent will point to the parent of the targeted 984 * object. This parent will be the "handle" to pass 985 * to the jansson modification function. 986 * @param[out] target last token of the path. E.g. given a x.y.z, 987 * will point to 'z'. This value will also be passed 988 * to the jansson modification function. 989 * To be freed by the caller. 990 * @param json the object to be walked over. 991 * @return #GNUNET_OK if @a path was valid. 992 */ 993 static unsigned int 994 walk_object (const char *path, 995 json_t **parent, 996 char **target, 997 json_t *json) 998 { 999 json_t *element; 1000 json_t *cur; 1001 char *token; 1002 char *last_token; 1003 char *path_dup; 1004 1005 GNUNET_assert (NULL != json); 1006 GNUNET_asprintf (&path_dup, 1007 ".%s", /* Make sure path starts with dot. */ 1008 path); 1009 1010 last_token = strrchr (path_dup, '.') + 1; 1011 /* Give first nondelim char. */ 1012 token = strtok (path_dup, "."); 1013 if (NULL == (element = json)) 1014 { 1015 TWISTER_LOG_ERROR ("Attempting to walk a non JSON response!\n"); 1016 return GNUNET_SYSERR; 1017 } 1018 1019 while (last_token != token) 1020 { 1021 TWISTER_LOG_DEBUG ("token/last_token: %s@%p / %s@%p\n", 1022 token, token, 1023 last_token, last_token); 1024 if (NULL == token) 1025 return GNUNET_SYSERR; // path was ".", refuse to process it. 1026 if (NULL != (cur = json_object_get (element, 1027 token))) 1028 { 1029 element = cur; 1030 token = strtok (NULL, "."); 1031 continue; 1032 } 1033 if (NULL != (cur = json_array_get (element, 1034 (unsigned int) strtoul 1035 (token, NULL, 10)))) 1036 { 1037 element = cur; 1038 token = strtok (NULL, "."); 1039 continue; 1040 } 1041 TWISTER_LOG_WARNING ("Path token '%s' not found\n", 1042 token); 1043 GNUNET_free (path_dup); 1044 return GNUNET_NO; 1045 } 1046 1047 if ( (NULL == json_object_get (element, 1048 last_token) ) && 1049 /* NOTE: if token is bad but converts to either 0, or 1050 * ULONG max AND the array has such a index, then this 1051 * test won't detect any error. Likewise, the method for 1052 * deleting/modifying the response will operate on that 1053 * same random array element. */(NULL == json_array_get (element, (unsigned int) strtoul 1054 (token, NULL, 10))) ) 1055 { 1056 TWISTER_LOG_WARNING ("(Last) path token '%s' not found\n", 1057 last_token); 1058 GNUNET_free (path_dup); 1059 return GNUNET_NO; 1060 } 1061 1062 *target = GNUNET_strdup (last_token); 1063 *parent = element; 1064 GNUNET_free (path_dup); 1065 return GNUNET_OK; 1066 } 1067 1068 1069 /** 1070 * Modify a JSON object. NOTE, the new value to set is 1071 * taken from the global value `modify_value'. 1072 * 1073 * @param con HTTP connection handle. 1074 * FIXME: deprecated, to be removed. 1075 * @param json the JSON object to modify. 1076 * @param path the path to the field to modify. 1077 */ 1078 static int 1079 modify_object (struct MHD_Connection *con, 1080 json_t *json, 1081 char *path) 1082 { 1083 1084 char *target; 1085 int ret_modify; 1086 json_t *parent; 1087 json_t *new_value; 1088 json_error_t error; 1089 1090 if (GNUNET_OK != walk_object (path, 1091 &parent, 1092 &target, 1093 json)) 1094 { 1095 TWISTER_LOG_INFO ("Path (%s) was not found on this object\n", 1096 path); 1097 return GNUNET_NO; 1098 } 1099 1100 /* At this point, the parent and the target are pointed to. */ 1101 1102 if (0 == strcmp ("true", modify_value)) 1103 { 1104 TWISTER_LOG_DEBUG ("New value parsed as boolean true\n"); 1105 new_value = json_true (); 1106 goto perform_modbody; 1107 } 1108 1109 if (NULL != (new_value = json_loads (modify_value, 1110 JSON_REJECT_DUPLICATES 1111 | JSON_DISABLE_EOF_CHECK, 1112 &error))) 1113 { 1114 TWISTER_LOG_DEBUG ("New value parsed as object/array\n"); 1115 goto perform_modbody; 1116 } 1117 1118 if (NULL != (new_value = json_string (modify_value))) 1119 { 1120 TWISTER_LOG_DEBUG ("New value parsed as string\n"); 1121 goto perform_modbody; 1122 } 1123 1124 TWISTER_LOG_ERROR ("Invalid new value given: %s\n", 1125 modify_value); 1126 GNUNET_free (target); 1127 return GNUNET_SYSERR; 1128 1129 perform_modbody: 1130 ret_modify = -1; 1131 if (json_is_object (parent)) 1132 ret_modify = json_object_set (parent, 1133 target, 1134 new_value); 1135 if (json_is_array (parent)) 1136 ret_modify = json_array_set_new 1137 (parent, 1138 (unsigned int) strtoul (target, NULL, 10), 1139 new_value); 1140 1141 GNUNET_free (target); 1142 json_decref (new_value); 1143 1144 if (-1 == ret_modify) 1145 { 1146 TWISTER_LOG_WARNING ("Could not replace '%s'\n", target); 1147 return GNUNET_SYSERR; 1148 } 1149 1150 return GNUNET_OK; 1151 } 1152 1153 1154 /** 1155 * Flip a random character to the string pointed to by @a path. 1156 * 1157 * @param con FIXME deprecated. 1158 * @param json the object whose field will be flipped. 1159 * @param flip_path the path to the string-field to flip. 1160 * @return #GNUNET_OK when the path was found, and flipped. 1161 */ 1162 static enum GNUNET_GenericReturnValue 1163 flip_object (struct MHD_Connection *con, 1164 json_t *json, 1165 const char *flip_path) 1166 { 1167 char *target; 1168 json_t *parent; 1169 json_t *child = NULL; 1170 const char *current_value; 1171 char *current_value_flip; 1172 unsigned int crockford_index; 1173 unsigned int flip_index; 1174 size_t len; 1175 const char crockford_chars[] = { 1176 '0', '1', '2', '3', '4', '5', 1177 '6', '7', '8', '9', 'A', 'B', 1178 'C', 'D', 'E', 'F', 'G', 'H', 1179 'J', 'K', 'M', 'N', 'P', 'Q', 1180 'R', 'S', 'T', 'V', 'W', 'X', 1181 'Y', 'Z' 1182 }; /* index: 0-31 */ 1183 #define CROCKFORD_MAX_INDEX (sizeof (crockford_chars)) 1184 1185 if (NULL == json) 1186 return GNUNET_NO; 1187 if (GNUNET_OK != walk_object (flip_path, 1188 &parent, 1189 &target, 1190 json)) 1191 { 1192 /** 1193 * Not an error, as the user can "batch" 1194 * requests until the right object gets in the way. 1195 */ 1196 TWISTER_LOG_INFO ("Path (%s) was not found on this object\n", 1197 flip_path); 1198 return GNUNET_NO; 1199 } 1200 1201 1202 if (json_is_object (parent)) 1203 child = json_object_get (parent, 1204 target); 1205 if (json_is_array (parent)) 1206 child = json_array_get (parent, 1207 (unsigned int) strtoul (target, 1208 NULL, 1209 10)); 1210 /* json walker is such that at this point the 1211 * child's parent is always a object or array. */ 1212 GNUNET_assert (NULL != child); 1213 GNUNET_free (target); 1214 1215 current_value = json_string_value (child); 1216 if (NULL == current_value) 1217 { 1218 GNUNET_break_op (0); 1219 return GNUNET_SYSERR; 1220 } 1221 current_value_flip = GNUNET_strdup (current_value); 1222 len = strlen (current_value_flip); 1223 if (len > 1) 1224 len--; /* do NOT flip last character, as that 1225 one may not matter in base32 encoding */ 1226 do { 1227 flip_index = GNUNET_CRYPTO_random_u32 ( 1228 len); 1229 crockford_index = GNUNET_CRYPTO_random_u32 ( 1230 CROCKFORD_MAX_INDEX); 1231 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1232 "Try flipping %u to Crockford index: %u\n", 1233 flip_index, 1234 crockford_index); 1235 } 1236 while (current_value_flip[flip_index] == 1237 crockford_chars[crockford_index]); 1238 current_value_flip[flip_index] = 1239 crockford_chars[crockford_index]; 1240 1241 TWISTER_LOG_INFO ("Flipping %s to %s\n", 1242 current_value, 1243 current_value_flip); 1244 if (0 != json_string_set (child, 1245 current_value_flip)) 1246 { 1247 TWISTER_LOG_WARNING ("Could not flip '%s'\n", 1248 flip_path); 1249 GNUNET_free (current_value_flip); 1250 return GNUNET_SYSERR; 1251 } 1252 GNUNET_free (current_value_flip); 1253 return GNUNET_OK; 1254 } 1255 1256 1257 /** 1258 * Delete object within the proxied response. 1259 * Always queues a response; only deletes the object if it is 1260 * found within the response, otherwise return it verbatim (but 1261 * will look for it into the next response). Will flush the 1262 * operation once the wanted object has been found. 1263 * 1264 * @param con FIXME deprecated. 1265 * @param hr contains the object whose field will be deleted. 1266 * @return #GNUNET_OK when the path was found, and deleted. 1267 */ 1268 static enum GNUNET_GenericReturnValue 1269 delete_object (struct MHD_Connection *con, 1270 struct HttpRequest *hr) 1271 { 1272 char *target; 1273 json_t *parent; 1274 int ret_deletion = -1; 1275 1276 if (NULL == hr->json) 1277 return GNUNET_NO; 1278 if (GNUNET_OK != walk_object (delete_path, 1279 &parent, 1280 &target, 1281 hr->json)) 1282 { 1283 TWISTER_LOG_INFO ("Path (%s) was not found on this object\n", 1284 delete_path); 1285 return GNUNET_NO; 1286 } 1287 1288 /* here, element is the parent of the element to be deleted. */ 1289 if (json_is_object (parent)) 1290 ret_deletion = json_object_del (parent, target); 1291 1292 if (json_is_array (parent)) 1293 { 1294 ret_deletion = json_array_remove 1295 (parent, (unsigned int) strtoul (target, 1296 NULL, 1297 10)); 1298 } 1299 GNUNET_free (target); 1300 1301 if (-1 == ret_deletion) 1302 { 1303 TWISTER_LOG_WARNING ("Could not delete '%s'\n", 1304 target); 1305 return GNUNET_SYSERR; 1306 } 1307 return GNUNET_OK; 1308 } 1309 1310 1311 /** 1312 * Decompress data. 1313 * 1314 * @param request contains input data to inflate 1315 * @return result code indicating the status of the operation 1316 */ 1317 static int 1318 inflate_data (struct HttpRequest *request) 1319 { 1320 z_stream z; 1321 char *tmp; 1322 size_t tmp_size; 1323 int ret; 1324 1325 memset (&z, 0, sizeof (z)); 1326 z.next_in = (Bytef *) request->io_buf; 1327 z.avail_in = request->io_len; 1328 tmp_size = GNUNET_MIN (REQUEST_BUFFER_MAX, 1329 request->io_len * 4); 1330 tmp = GNUNET_malloc (tmp_size); 1331 z.next_out = (Bytef *) tmp; 1332 z.avail_out = tmp_size; 1333 ret = inflateInit (&z); 1334 switch (ret) 1335 { 1336 case Z_MEM_ERROR: 1337 GNUNET_break (0); 1338 return GNUNET_MHD_PR_OUT_OF_MEMORY; 1339 case Z_STREAM_ERROR: 1340 GNUNET_break_op (0); 1341 return GNUNET_MHD_PR_JSON_INVALID; 1342 case Z_OK: 1343 break; 1344 } 1345 while (1) 1346 { 1347 ret = inflate (&z, 0); 1348 switch (ret) 1349 { 1350 case Z_MEM_ERROR: 1351 GNUNET_break (0); 1352 GNUNET_break (Z_OK == inflateEnd (&z)); 1353 GNUNET_free (tmp); 1354 return GNUNET_MHD_PR_OUT_OF_MEMORY; 1355 case Z_DATA_ERROR: 1356 GNUNET_break (0); 1357 GNUNET_break (Z_OK == inflateEnd (&z)); 1358 GNUNET_free (tmp); 1359 return GNUNET_MHD_PR_JSON_INVALID; 1360 case Z_NEED_DICT: 1361 GNUNET_break (0); 1362 GNUNET_break (Z_OK == inflateEnd (&z)); 1363 GNUNET_free (tmp); 1364 return GNUNET_MHD_PR_JSON_INVALID; 1365 case Z_OK: 1366 if ((0 < z.avail_out) && (0 == z.avail_in)) 1367 { 1368 /* truncated input stream */ 1369 GNUNET_break (0); 1370 GNUNET_break (Z_OK == inflateEnd (&z)); 1371 GNUNET_free (tmp); 1372 return GNUNET_MHD_PR_JSON_INVALID; 1373 } 1374 if (0 < z.avail_out) 1375 continue; /* just call it again */ 1376 /* output buffer full, can we grow it? */ 1377 if (tmp_size == REQUEST_BUFFER_MAX) 1378 { 1379 /* already at max */ 1380 GNUNET_break (0); 1381 GNUNET_break (Z_OK == inflateEnd (&z)); 1382 GNUNET_free (tmp); 1383 return GNUNET_MHD_PR_OUT_OF_MEMORY; 1384 } 1385 if (tmp_size * 2 < tmp_size) 1386 tmp_size = REQUEST_BUFFER_MAX; 1387 else 1388 tmp_size = GNUNET_MIN (REQUEST_BUFFER_MAX, tmp_size * 2); 1389 tmp = GNUNET_realloc (tmp, tmp_size); 1390 z.next_out = (Bytef *) &tmp[z.total_out]; 1391 continue; 1392 case Z_STREAM_END: 1393 /* decompression successful, make 'tmp' the new 'data' */ 1394 GNUNET_free (request->io_buf); 1395 request->io_buf = tmp; 1396 request->io_size = tmp_size; 1397 request->io_len = z.total_out; 1398 GNUNET_break (Z_OK == inflateEnd (&z)); 1399 return GNUNET_MHD_PR_SUCCESS; /* at least for now */ 1400 } 1401 } /* while (1) */ 1402 } 1403 1404 1405 /** 1406 * Create the response object according to the "chaos rate". 1407 * If this latter strikes, then the response will be "503 Service 1408 * Unavailable" with a empty body (overriding every other mod that 1409 * the user might have given.) 1410 * 1411 * @param hr the HTTP object representing the current state. 1412 */ 1413 static void 1414 create_response_with_chaos_rate (struct HttpRequest *hr) 1415 { 1416 uint64_t random; 1417 const void *resp_buf; 1418 size_t resp_len; 1419 1420 random = GNUNET_CRYPTO_random_u64 (100); 1421 TWISTER_LOG_INFO ("p: %llu, random: %llu\n", 1422 (unsigned long long) chaos_rate, 1423 (unsigned long long) random); 1424 if (random < chaos_rate) 1425 { 1426 /* p won */ 1427 TWISTER_LOG_INFO ("Chaos probability won the case.\n"); 1428 resp_buf = "Service unavailable"; 1429 resp_len = strlen (resp_buf); 1430 hr->response_code = MHD_HTTP_SERVICE_UNAVAILABLE; 1431 } 1432 else 1433 { 1434 resp_len = hr->io_len; 1435 resp_buf = hr->io_buf; 1436 } 1437 hr->response 1438 = MHD_create_response_from_buffer_copy (resp_len, 1439 resp_buf); 1440 } 1441 1442 1443 /** 1444 * Main MHD callback for handling requests. 1445 * 1446 * @param cls unused 1447 * @param con MHD connection handle 1448 * @param url the url in the request 1449 * @param meth the HTTP method used ("GET", "PUT", etc.) 1450 * @param ver the HTTP version string (i.e. "HTTP/1.1") 1451 * @param upload_data the data being uploaded (excluding HEADERS, 1452 * for a POST that fits into memory and that is encoded 1453 * with a supported encoding, the POST data will NOT be 1454 * given in upload_data and is instead available as 1455 * part of MHD_get_connection_values; very large POST 1456 * data *will* be made available incrementally in 1457 * upload_data) 1458 * @param upload_data_size set initially to the size of the 1459 * @a upload_data provided; the method must update this 1460 * value to the number of bytes NOT processed; 1461 * @param con_cls pointer to location where we store the 1462 * 'struct Request' 1463 * @return #MHD_YES if the connection was handled successfully, 1464 * #MHD_NO if the socket must be closed due to a serious 1465 * error while handling the request 1466 */ 1467 static MHD_RESULT 1468 create_response (void *cls, 1469 struct MHD_Connection *con, 1470 const char *url, 1471 const char *meth, 1472 const char *ver, 1473 const char *upload_data, 1474 size_t *upload_data_size, 1475 void **con_cls) 1476 { 1477 struct HttpRequest *hr = *con_cls; 1478 1479 (void) cls; 1480 (void) url; 1481 1482 if (NULL == hr) 1483 { 1484 GNUNET_break (0); 1485 return MHD_NO; 1486 } 1487 1488 if (REQUEST_STATE_WITH_MHD == hr->state) 1489 { 1490 hr->state = REQUEST_STATE_CLIENT_UPLOAD_STARTED; 1491 /* TODO: hacks for 100 continue suppression would go here! */ 1492 return MHD_YES; 1493 } 1494 1495 /* continuing to process request */ 1496 if (0 != *upload_data_size) 1497 { 1498 GNUNET_assert 1499 (REQUEST_STATE_CLIENT_UPLOAD_STARTED == hr->state); 1500 1501 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1502 "Processing %u bytes UPLOAD\n", 1503 (unsigned int) *upload_data_size); 1504 1505 /* Grow the buffer if remaining space isn't enough. */ 1506 if (hr->io_size - hr->io_len < *upload_data_size) 1507 { 1508 /* How can this assertion be false? */ 1509 GNUNET_assert (hr->io_size * 2 + 1024 > hr->io_size); 1510 /* This asserts that upload_data_size > 0, ? */ 1511 GNUNET_assert (*upload_data_size + hr->io_len > hr->io_len); 1512 1513 GNUNET_array_grow (hr->io_buf, 1514 hr->io_size, 1515 GNUNET_MAX 1516 (hr->io_size * 2 + 1024, 1517 *upload_data_size + hr->io_len)); 1518 } 1519 1520 /* Finally copy upload data. */ 1521 GNUNET_memcpy (&hr->io_buf[hr->io_len], 1522 upload_data, 1523 *upload_data_size); 1524 1525 hr->io_len += *upload_data_size; 1526 *upload_data_size = 0; 1527 1528 return MHD_YES; 1529 } 1530 1531 /* Upload (*from the client*) finished or just a without-body 1532 * request. */ 1533 if (REQUEST_STATE_CLIENT_UPLOAD_STARTED == hr->state) 1534 { 1535 hr->state = REQUEST_STATE_CLIENT_UPLOAD_DONE; 1536 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1537 "Finished processing UPLOAD\n"); 1538 if (0 != hr->io_len) 1539 { 1540 const char *ce; 1541 1542 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1543 "Attempting to decompress\n"); 1544 ce = MHD_lookup_connection_value 1545 (con, 1546 MHD_HEADER_KIND, 1547 MHD_HTTP_HEADER_CONTENT_ENCODING); 1548 1549 if ((NULL != ce) && (0 == strcmp ("deflate", ce))) 1550 GNUNET_assert (Z_OK == inflate_data (hr)); 1551 1552 ce = MHD_lookup_connection_value 1553 (con, 1554 MHD_HEADER_KIND, 1555 MHD_HTTP_HEADER_CONTENT_TYPE); 1556 1557 if ((NULL != ce) && (0 == strcmp ("application/json", ce))) 1558 { 1559 json_error_t error; 1560 1561 hr->json = json_loadb (hr->io_buf, 1562 hr->io_len, 1563 JSON_DECODE_ANY, 1564 &error); 1565 if (NULL == hr->json) 1566 { 1567 TWISTER_LOG_ERROR 1568 ("Could not parse JSON from client: %s (%s)\n", 1569 error.text, 1570 error.source); 1571 /* Quick and dirty. */ 1572 return MHD_NO; 1573 } 1574 } 1575 } 1576 } 1577 1578 /* generate curl request to the proxied service. */ 1579 if (NULL == hr->curl) 1580 { 1581 /* Malform request body. Note, this flag will be 1582 * cleared only after having set the right malformed 1583 * body length in the request headers. */ 1584 if ( (GNUNET_YES == malform_upload) && 1585 (hr->io_len > 0) ) 1586 { 1587 TWISTER_LOG_DEBUG ("Will (badly) truncate the request.\n"); 1588 malformed_size = GNUNET_CRYPTO_random_u32 ( 1589 hr->io_len); 1590 hr->io_len = malformed_size; 1591 malform_upload = GNUNET_NO; 1592 } 1593 1594 if (NULL != flip_path_ul) 1595 { 1596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1597 "Will flip path in request: %s\n", 1598 flip_path_ul); 1599 1600 if (GNUNET_OK == 1601 flip_object (con, 1602 hr->json, 1603 flip_path_ul)) 1604 { 1605 GNUNET_free (flip_path_ul); 1606 flip_path_ul = NULL; 1607 } 1608 } 1609 1610 if (NULL != modify_path_ul) 1611 { 1612 int ret; 1613 1614 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1615 "Will try to modify: %s\n", 1616 flip_path_ul); 1617 1618 ret = modify_object (con, 1619 hr->json, 1620 modify_path_ul); 1621 1622 if ((GNUNET_OK == ret) || (GNUNET_SYSERR == ret)) 1623 { 1624 GNUNET_free (modify_path_ul); 1625 GNUNET_free (modify_value); 1626 1627 modify_path_ul = NULL; 1628 modify_value = NULL; 1629 } 1630 } 1631 1632 /* Existing io_len is enough to accommodate this encoding. */ 1633 json_dumpb (hr->json, 1634 hr->io_buf, 1635 hr->io_len, 1636 JSON_COMPACT); 1637 json_decref (hr->json); 1638 hr->json = NULL; 1639 1640 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1641 "Generating curl request\n"); 1642 hr->curl = curl_easy_init (); 1643 if (NULL == hr->curl) 1644 { 1645 TWISTER_LOG_ERROR ("Could not init the curl handle\n"); 1646 return MHD_queue_response (con, 1647 MHD_HTTP_INTERNAL_SERVER_ERROR, 1648 curl_failure_response); 1649 } 1650 1651 /* No need to check whether we're POSTing or PUTting. 1652 * If not needed, one of the following values will be 1653 * ignored.*/ 1654 curl_easy_setopt (hr->curl, 1655 CURLOPT_POSTFIELDSIZE, 1656 hr->io_len); 1657 curl_easy_setopt (hr->curl, 1658 CURLOPT_INFILESIZE, 1659 hr->io_len); 1660 curl_easy_setopt (hr->curl, 1661 CURLOPT_HEADERFUNCTION, 1662 &curl_check_hdr); 1663 curl_easy_setopt (hr->curl, 1664 CURLOPT_HEADERDATA, 1665 hr); 1666 curl_easy_setopt (hr->curl, 1667 CURLOPT_FOLLOWLOCATION, 1668 0); 1669 curl_easy_setopt (hr->curl, 1670 CURLOPT_CONNECTTIMEOUT, 1671 600L); 1672 curl_easy_setopt (hr->curl, 1673 CURLOPT_TIMEOUT, 1674 600L); 1675 curl_easy_setopt (hr->curl, 1676 CURLOPT_NOSIGNAL, 1677 1L); 1678 curl_easy_setopt (hr->curl, 1679 CURLOPT_PRIVATE, 1680 hr); 1681 curl_easy_setopt (hr->curl, 1682 CURLOPT_VERBOSE, 1683 0); 1684 1685 curl_easy_setopt (hr->curl, 1686 CURLOPT_READFUNCTION, 1687 &curl_upload_cb); 1688 1689 curl_easy_setopt (hr->curl, 1690 CURLOPT_READDATA, 1691 hr); 1692 1693 curl_easy_setopt (hr->curl, 1694 CURLOPT_WRITEFUNCTION, 1695 &curl_download_cb); 1696 1697 curl_easy_setopt (hr->curl, 1698 CURLOPT_WRITEDATA, 1699 hr); 1700 { 1701 char *curlurl; 1702 char *host_hdr; 1703 1704 GNUNET_asprintf (&curlurl, 1705 "%s%s", 1706 target_server_base_url, 1707 hr->url); 1708 curl_easy_setopt (hr->curl, 1709 CURLOPT_URL, 1710 curlurl); 1711 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1712 "Forwarding request to: %s\n", 1713 curlurl); 1714 GNUNET_free (curlurl); 1715 1716 host_hdr = build_host_header (target_server_base_url); 1717 TWISTER_LOG_DEBUG ("Faking the host header, %s\n", 1718 host_hdr); 1719 hr->headers = curl_slist_append (hr->headers, 1720 host_hdr); 1721 GNUNET_free (host_hdr); 1722 } 1723 1724 if (0 == strcasecmp (meth, 1725 MHD_HTTP_METHOD_PUT)) 1726 { 1727 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1728 "Crafting a CURL PUT request\n"); 1729 1730 curl_easy_setopt (hr->curl, 1731 CURLOPT_UPLOAD, 1732 1L); 1733 hr->state = REQUEST_STATE_PROXY_UPLOAD_STARTED; 1734 1735 } 1736 else if (0 == strcasecmp (meth, 1737 MHD_HTTP_METHOD_POST)) 1738 { 1739 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1740 "Crafting a CURL POST request\n"); 1741 curl_easy_setopt (hr->curl, 1742 CURLOPT_POST, 1743 1L); 1744 curl_easy_setopt (hr->curl, 1745 CURLOPT_VERBOSE, 1746 1L); 1747 hr->state = REQUEST_STATE_PROXY_UPLOAD_STARTED; 1748 1749 } 1750 else if (0 == strcasecmp (meth, 1751 MHD_HTTP_METHOD_HEAD)) 1752 { 1753 hr->state = REQUEST_STATE_PROXY_DOWNLOAD_STARTED; 1754 curl_easy_setopt (hr->curl, 1755 CURLOPT_NOBODY, 1756 1L); 1757 } 1758 else if (0 == strcasecmp (meth, 1759 MHD_HTTP_METHOD_OPTIONS)) 1760 { 1761 hr->state = REQUEST_STATE_PROXY_DOWNLOAD_STARTED; 1762 curl_easy_setopt (hr->curl, 1763 CURLOPT_CUSTOMREQUEST, 1764 "OPTIONS"); 1765 } 1766 else if (0 == strcasecmp (meth, 1767 MHD_HTTP_METHOD_GET)) 1768 { 1769 hr->state = REQUEST_STATE_PROXY_DOWNLOAD_STARTED; 1770 curl_easy_setopt (hr->curl, 1771 CURLOPT_HTTPGET, 1772 1L); 1773 } 1774 else 1775 { 1776 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1777 "Unsupported HTTP method `%s'\n", 1778 meth); 1779 curl_easy_cleanup (hr->curl); 1780 hr->curl = NULL; 1781 return MHD_NO; 1782 } 1783 1784 if (0 == strcasecmp (ver, 1785 MHD_HTTP_VERSION_1_0)) 1786 { 1787 curl_easy_setopt (hr->curl, 1788 CURLOPT_HTTP_VERSION, 1789 CURL_HTTP_VERSION_1_0); 1790 } 1791 else if (0 == strcasecmp (ver, 1792 MHD_HTTP_VERSION_1_1)) 1793 { 1794 curl_easy_setopt (hr->curl, 1795 CURLOPT_HTTP_VERSION, 1796 CURL_HTTP_VERSION_1_1); 1797 } 1798 else 1799 { 1800 curl_easy_setopt (hr->curl, 1801 CURLOPT_HTTP_VERSION, 1802 CURL_HTTP_VERSION_NONE); 1803 } 1804 1805 if (CURLM_OK != curl_multi_add_handle (curl_multi, 1806 hr->curl)) 1807 { 1808 GNUNET_break (0); 1809 curl_easy_cleanup (hr->curl); 1810 hr->curl = NULL; 1811 return MHD_NO; 1812 } 1813 1814 MHD_get_connection_values (con, 1815 MHD_HEADER_KIND, 1816 &con_val_iter, 1817 hr); 1818 1819 curl_easy_setopt (hr->curl, 1820 CURLOPT_HTTPHEADER, 1821 hr->headers); 1822 curl_download_prepare (); 1823 1824 return MHD_YES; 1825 } 1826 1827 if (REQUEST_STATE_PROXY_DOWNLOAD_DONE != hr->state) 1828 { 1829 GNUNET_assert (GNUNET_NO == hr->suspended); 1830 MHD_suspend_connection (con); 1831 hr->suspended = GNUNET_YES; 1832 return MHD_YES; /* wait for curl */ 1833 } 1834 1835 GNUNET_assert (REQUEST_STATE_PROXY_DOWNLOAD_DONE == hr->state); 1836 if (0 != hack_response_code) 1837 { 1838 hr->response_code = hack_response_code; 1839 hack_response_code = 0; /* reset for next request */ 1840 } 1841 1842 if (NULL != flip_path_dl) 1843 { 1844 TWISTER_LOG_DEBUG ("Will flip path" 1845 " in response: %s\n", 1846 flip_path_dl); 1847 1848 if (GNUNET_OK == flip_object (con, 1849 hr->json, 1850 flip_path_dl)) 1851 { 1852 GNUNET_free (flip_path_dl); 1853 flip_path_dl = NULL; 1854 } 1855 } 1856 1857 if (NULL != delete_path) 1858 { 1859 TWISTER_LOG_DEBUG ("Will delete path: %s\n", 1860 delete_path); 1861 if (GNUNET_OK == delete_object (con, 1862 hr)) 1863 { 1864 GNUNET_free (delete_path); 1865 delete_path = NULL; 1866 } 1867 } 1868 1869 if (NULL != modify_path_dl) 1870 { 1871 enum GNUNET_GenericReturnValue ret; 1872 1873 TWISTER_LOG_DEBUG ("Will modify path: %s to value %s\n", 1874 modify_path_dl, 1875 modify_value); 1876 ret = modify_object (con, 1877 hr->json, 1878 modify_path_dl); 1879 if ((GNUNET_OK == ret) || (GNUNET_SYSERR == ret)) 1880 { 1881 GNUNET_free (modify_path_dl); 1882 GNUNET_free (modify_value); 1883 1884 modify_path_dl = NULL; 1885 modify_value = NULL; 1886 } 1887 } 1888 1889 if (NULL != hr->json) 1890 { 1891 GNUNET_free (hr->io_buf); 1892 hr->io_buf = json_dumps (hr->json, 1893 JSON_COMPACT); 1894 if (NULL != hr->io_buf) 1895 { 1896 hr->io_len = strlen (hr->io_buf); 1897 } 1898 else 1899 { 1900 GNUNET_break (0); 1901 hr->io_len = 0; 1902 } 1903 json_decref (hr->json); 1904 hr->json = NULL; 1905 } 1906 1907 if (GNUNET_YES == malform) 1908 { 1909 TWISTER_LOG_DEBUG ("Will (badly) truncate the response.\n"); 1910 if (hr->io_len > 0) 1911 { 1912 size_t fake_len; 1913 1914 fake_len = GNUNET_CRYPTO_random_u32 ( 1915 hr->io_len); 1916 hr->io_len = fake_len; 1917 } 1918 malform = GNUNET_NO; 1919 } 1920 1921 create_response_with_chaos_rate (hr); 1922 1923 for (struct HttpResponseHeader *header = hr->header_head; 1924 NULL != header; 1925 header = header->next) 1926 { 1927 const char *value = header->value; 1928 1929 if ((NULL != modify_header_dl) && 1930 (0 == strcmp (header->type, 1931 modify_header_dl))) 1932 { 1933 value = modify_value; 1934 } 1935 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1936 "Adding MHD response header %s->%s\n", 1937 header->type, 1938 value); 1939 GNUNET_break (MHD_YES == 1940 MHD_add_response_header (hr->response, 1941 header->type, 1942 value)); 1943 } 1944 run_mhd_now (); 1945 1946 return MHD_queue_response (con, 1947 hr->response_code, 1948 hr->response); 1949 } 1950 1951 1952 /* ************ MHD HTTP setup and event loop *************** */ 1953 1954 1955 /** 1956 * Function called when MHD decides that we 1957 * are done with a request. 1958 * 1959 * @param cls NULL 1960 * @param connection connection handle 1961 * @param con_cls value as set by the last call to 1962 * the MHD_AccessHandlerCallback, should be 1963 * our `struct HttpRequest *` (set by `create_response()`) 1964 * @param toe reason for request termination (ignored) 1965 */ 1966 static void 1967 mhd_completed_cb (void *cls, 1968 struct MHD_Connection *connection, 1969 void **con_cls, 1970 enum MHD_RequestTerminationCode toe) 1971 { 1972 struct HttpRequest *hr = *con_cls; 1973 struct HttpResponseHeader *header; 1974 1975 (void) cls; 1976 (void) connection; 1977 if (NULL == hr) 1978 return; 1979 if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe) 1980 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1981 "MHD encountered error handling request: %d\n", 1982 toe); 1983 if (NULL != hr->curl) 1984 { 1985 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1986 "Resetting cURL handle\n"); 1987 curl_multi_remove_handle (curl_multi, 1988 hr->curl); 1989 curl_easy_cleanup (hr->curl); 1990 hr->curl = NULL; 1991 hr->io_len = 0; 1992 } 1993 if (NULL != hr->headers) 1994 { 1995 curl_slist_free_all (hr->headers); 1996 hr->headers = NULL; 1997 } 1998 if ( (NULL != hr->response) && 1999 (curl_failure_response != hr->response) ) 2000 /* Destroy non-error responses... (?) */ 2001 MHD_destroy_response (hr->response); 2002 2003 for (header = hr->header_head; 2004 header != NULL; 2005 header = hr->header_head) 2006 { 2007 GNUNET_CONTAINER_DLL_remove (hr->header_head, 2008 hr->header_tail, 2009 header); 2010 GNUNET_free (header->type); 2011 GNUNET_free (header->value); 2012 GNUNET_free (header); 2013 } 2014 2015 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2016 "Proxying of '%s' completely done\n", 2017 hr->url); 2018 2019 GNUNET_free (hr->url); 2020 GNUNET_free (hr->io_buf); 2021 GNUNET_CONTAINER_DLL_remove (hr_head, 2022 hr_tail, 2023 hr); 2024 GNUNET_free (hr); 2025 *con_cls = NULL; 2026 } 2027 2028 2029 /** 2030 * Function called when MHD first processes an incoming connection. 2031 * Gives us the respective URI information. 2032 * 2033 * We use this to associate the `struct MHD_Connection` with our 2034 * internal `struct HttpRequest` data structure (by checking 2035 * for matching sockets). 2036 * 2037 * @param cls the HTTP server handle (a `struct MhdHttpList`) 2038 * @param url the URL that is being requested 2039 * @param connection MHD connection object for the request 2040 * @return the `struct HttpRequest` that this @a connection is for 2041 */ 2042 static void * 2043 mhd_log_callback (void *cls, 2044 const char *url, 2045 struct MHD_Connection *connection) 2046 { 2047 struct HttpRequest *hr; 2048 const union MHD_ConnectionInfo *ci; 2049 2050 (void) cls; 2051 ci = MHD_get_connection_info (connection, 2052 MHD_CONNECTION_INFO_SOCKET_CONTEXT); 2053 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2054 "Processing %s\n", 2055 url); 2056 if (NULL == ci) 2057 { 2058 GNUNET_break (0); 2059 return NULL; 2060 } 2061 2062 hr = GNUNET_new (struct HttpRequest); 2063 hr->con = connection; 2064 hr->url = GNUNET_strdup (url); 2065 GNUNET_CONTAINER_DLL_insert (hr_head, 2066 hr_tail, 2067 hr); 2068 return hr; 2069 } 2070 2071 2072 /** 2073 * Kill the MHD daemon. 2074 */ 2075 static void 2076 kill_httpd (void) 2077 { 2078 MHD_stop_daemon (mhd_daemon); 2079 mhd_daemon = NULL; 2080 if (NULL != httpd_task) 2081 { 2082 GNUNET_SCHEDULER_cancel (httpd_task); 2083 httpd_task = NULL; 2084 } 2085 } 2086 2087 2088 /** 2089 * Task run whenever HTTP server operations are pending. 2090 * 2091 * @param cls the `struct MhdHttpList *` 2092 * of the daemon that is being run 2093 */ 2094 static void 2095 do_httpd (void *cls); 2096 2097 2098 /** 2099 * Schedule MHD. This function should be called initially when an 2100 * MHD is first getting its client socket, and will then 2101 * automatically always be called later whenever there is work to 2102 * be done. 2103 */ 2104 static void 2105 schedule_httpd (void) 2106 { 2107 fd_set rs; 2108 fd_set ws; 2109 fd_set es; 2110 struct GNUNET_NETWORK_FDSet *wrs; 2111 struct GNUNET_NETWORK_FDSet *wws; 2112 int max; 2113 int haveto; 2114 MHD_UNSIGNED_LONG_LONG timeout; 2115 struct GNUNET_TIME_Relative tv; 2116 2117 FD_ZERO (&rs); 2118 FD_ZERO (&ws); 2119 FD_ZERO (&es); 2120 max = -1; 2121 if (MHD_YES != 2122 MHD_get_fdset (mhd_daemon, 2123 &rs, 2124 &ws, 2125 &es, 2126 &max)) 2127 { 2128 kill_httpd (); 2129 return; 2130 } 2131 haveto = MHD_get_timeout (mhd_daemon, 2132 &timeout); 2133 if (MHD_YES == haveto) 2134 tv.rel_value_us = (uint64_t) timeout * 1000LL; 2135 else 2136 tv = GNUNET_TIME_UNIT_FOREVER_REL; 2137 if (-1 != max) 2138 { 2139 wrs = GNUNET_NETWORK_fdset_create (); 2140 wws = GNUNET_NETWORK_fdset_create (); 2141 GNUNET_NETWORK_fdset_copy_native (wrs, 2142 &rs, 2143 max + 1); 2144 GNUNET_NETWORK_fdset_copy_native (wws, 2145 &ws, 2146 max + 1); 2147 } 2148 else 2149 { 2150 wrs = NULL; 2151 wws = NULL; 2152 } 2153 if (NULL != httpd_task) 2154 { 2155 GNUNET_SCHEDULER_cancel (httpd_task); 2156 httpd_task = NULL; 2157 } 2158 httpd_task = 2159 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, 2160 tv, 2161 wrs, 2162 wws, 2163 &do_httpd, 2164 NULL); 2165 if (NULL != wrs) 2166 GNUNET_NETWORK_fdset_destroy (wrs); 2167 if (NULL != wws) 2168 GNUNET_NETWORK_fdset_destroy (wws); 2169 } 2170 2171 2172 /** 2173 * Task run whenever HTTP server operations are pending. 2174 * 2175 * @param cls NULL 2176 */ 2177 static void 2178 do_httpd (void *cls) 2179 { 2180 (void) cls; 2181 httpd_task = NULL; 2182 MHD_run (mhd_daemon); 2183 schedule_httpd (); 2184 } 2185 2186 2187 /** 2188 * Run MHD now, we have extra data ready for the callback. 2189 */ 2190 static void 2191 run_mhd_now (void) 2192 { 2193 if (NULL != httpd_task) 2194 GNUNET_SCHEDULER_cancel (httpd_task); 2195 httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, 2196 NULL); 2197 } 2198 2199 2200 /* *************** General / main code *************** */ 2201 2202 2203 /** 2204 * Task run on shutdown 2205 * 2206 * @param cls closure 2207 */ 2208 static void 2209 do_shutdown (void *cls) 2210 { 2211 (void) cls; 2212 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2213 "Shutting down...\n"); 2214 /* MHD requires resuming before destroying the daemons */ 2215 for (struct HttpRequest *hr = hr_head; 2216 NULL != hr; 2217 hr = hr->next) 2218 { 2219 if (GNUNET_YES == hr->suspended) 2220 { 2221 hr->suspended = GNUNET_NO; 2222 MHD_resume_connection (hr->con); 2223 } 2224 } 2225 kill_httpd (); 2226 if (NULL != curl_multi) 2227 { 2228 curl_multi_cleanup (curl_multi); 2229 curl_multi = NULL; 2230 } 2231 if (NULL != curl_download_task) 2232 { 2233 GNUNET_SCHEDULER_cancel (curl_download_task); 2234 curl_download_task = NULL; 2235 } 2236 GNUNET_free (target_server_base_url); 2237 GNUNET_free (delete_path); 2238 GNUNET_free (flip_path_dl); 2239 } 2240 2241 2242 /** 2243 * Connect to a unix domain socket. 2244 * 2245 * @param path the IPC path 2246 * @param mode the IPC path mode 2247 * @return the file descriptor of the connection. 2248 */ 2249 static int 2250 open_unix_path (const char *path, 2251 mode_t mode) 2252 { 2253 2254 struct GNUNET_NETWORK_Handle *nh; 2255 struct sockaddr_un *un; 2256 int fd; 2257 2258 if (sizeof (un->sun_path) <= strlen (path)) 2259 { 2260 fprintf (stderr, 2261 "path `%s' too long\n", 2262 path); 2263 return -1; 2264 } 2265 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2266 "Creating listen socket '%s' with mode %o\n", 2267 path, 2268 mode); 2269 2270 if (GNUNET_OK != 2271 GNUNET_DISK_directory_create_for_file (path)) 2272 { 2273 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 2274 "mkdir", 2275 path); 2276 } 2277 2278 un = GNUNET_new (struct sockaddr_un); 2279 un->sun_family = AF_UNIX; 2280 2281 strncpy (un->sun_path, 2282 path, 2283 sizeof (un->sun_path) - 1); 2284 GNUNET_NETWORK_unix_precheck (un); 2285 2286 if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX, 2287 SOCK_STREAM, 2288 0))) 2289 { 2290 fprintf (stderr, 2291 "create failed for AF_UNIX\n"); 2292 GNUNET_free (un); 2293 return -1; 2294 } 2295 if (GNUNET_OK != 2296 GNUNET_NETWORK_socket_bind (nh, 2297 (void *) un, 2298 sizeof (struct sockaddr_un))) 2299 { 2300 fprintf (stderr, 2301 "bind failed for AF_UNIX\n"); 2302 GNUNET_free (un); 2303 GNUNET_NETWORK_socket_close (nh); 2304 return -1; 2305 } 2306 GNUNET_free (un); 2307 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (nh, 2308 UNIX_BACKLOG)) 2309 { 2310 fprintf (stderr, 2311 "listen failed for AF_UNIX\n"); 2312 GNUNET_NETWORK_socket_close (nh); 2313 return -1; 2314 } 2315 2316 if (0 != chmod (path, 2317 mode)) 2318 { 2319 fprintf (stderr, 2320 "chmod failed: %s\n", 2321 strerror (errno)); 2322 GNUNET_NETWORK_socket_close (nh); 2323 return -1; 2324 } 2325 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2326 "set socket '%s' to mode %o\n", 2327 path, 2328 mode); 2329 fd = GNUNET_NETWORK_get_fd (nh); 2330 GNUNET_NETWORK_socket_free_memory_only_ (nh); 2331 return fd; 2332 } 2333 2334 2335 /** 2336 * Crawl the configuration file and extracts the serving 2337 * method, TCP vs IPC, and the respective details (port/path/mode) 2338 * 2339 * @param ccfg configuration handle. 2340 * @param port[out] port number to use 2341 * @param path[out] unix path for IPC. 2342 * @param mode[out] mode string for @a path. 2343 * @return #GNUNET_SYSERR if the parsing didn't succeed. 2344 */ 2345 static int 2346 parse_serving_mean (const struct GNUNET_CONFIGURATION_Handle *ccfg, 2347 uint16_t *port, 2348 char **path, 2349 mode_t *mode) 2350 { 2351 2352 const char *serve; 2353 const char *choices[] = {"tcp", "unix", NULL}; 2354 char *modestring; 2355 unsigned long long port_ull; 2356 2357 2358 if (GNUNET_OK != 2359 GNUNET_CONFIGURATION_get_value_choice (ccfg, 2360 "twister", 2361 "SERVE", 2362 choices, 2363 &serve)) 2364 { 2365 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2366 "twister", 2367 "SERVE"); 2368 return GNUNET_SYSERR; 2369 } 2370 2371 if (0 == strcmp ("tcp", serve)) 2372 { 2373 *path = NULL; 2374 2375 if (GNUNET_OK != 2376 GNUNET_CONFIGURATION_get_value_number (ccfg, 2377 "twister", 2378 "HTTP_PORT", 2379 &port_ull)) 2380 { 2381 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2382 "twister", 2383 "HTTP_PORT"); 2384 return GNUNET_SYSERR; 2385 } 2386 *port = (uint16_t) port_ull; 2387 return GNUNET_OK; 2388 } 2389 2390 /* serving via unix */ 2391 2392 if (GNUNET_OK != 2393 GNUNET_CONFIGURATION_get_value_filename (ccfg, 2394 "twister", 2395 "SERVE_UNIXPATH", 2396 path)) 2397 { 2398 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2399 "twister", 2400 "SERVE_UNIXPATH"); 2401 return GNUNET_SYSERR; 2402 } 2403 2404 if (GNUNET_OK != 2405 GNUNET_CONFIGURATION_get_value_string (ccfg, 2406 "twister", 2407 "SERVE_UNIXMODE", 2408 &modestring)) 2409 { 2410 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2411 "twister", 2412 "SERVE_UNIXMODE"); 2413 return GNUNET_SYSERR; 2414 } 2415 2416 errno = 0; 2417 *mode = (mode_t) strtoul (modestring, NULL, 8); 2418 2419 if (0 != errno) 2420 { 2421 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 2422 "twister", 2423 "SERVE_UNIXMODE", 2424 "must be octal number"); 2425 GNUNET_free (modestring); 2426 return GNUNET_SYSERR; 2427 } 2428 GNUNET_free (modestring); 2429 return GNUNET_OK; 2430 } 2431 2432 2433 /** 2434 * Main function that will be run. Main tasks are (1) init. the 2435 * curl infrastructure (curl_global_init() / curl_multi_init()), 2436 * then fetch the HTTP port where its Web service should listen at, 2437 * and finally start MHD on that port. 2438 * 2439 * @param cls closure 2440 * @param c configuration 2441 * @param service the initialized service 2442 */ 2443 static void 2444 run (void *cls, 2445 const struct GNUNET_CONFIGURATION_Handle *c, 2446 struct GNUNET_SERVICE_Handle *service) 2447 { 2448 uint16_t port = 0; 2449 int fh = -1; 2450 char *serve_unixpath = NULL; 2451 mode_t serve_unixmode = 0; 2452 2453 (void) cls; 2454 (void) service; 2455 cfg = c; 2456 2457 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 2458 { 2459 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2460 "cURL global init failed!\n"); 2461 GNUNET_SCHEDULER_shutdown (); 2462 return; 2463 } 2464 if (NULL == (curl_multi = curl_multi_init ())) 2465 { 2466 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2467 "Failed to create cURL multi handle!\n"); 2468 return; 2469 } 2470 2471 /* No need to check return value. If given, we take, 2472 * otherwise it stays zero. */ 2473 if (GNUNET_OK != 2474 GNUNET_CONFIGURATION_get_value_number (c, 2475 "twister", 2476 "CHAOS_RATE", 2477 &chaos_rate)) 2478 chaos_rate = 0; 2479 if (100 < chaos_rate) 2480 { 2481 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 2482 "twister", 2483 "CHAOS_RATE", 2484 "must be below 100"); 2485 GNUNET_SCHEDULER_shutdown (); 2486 return; 2487 } 2488 2489 if (GNUNET_OK != 2490 GNUNET_CONFIGURATION_get_value_string 2491 (c, 2492 "twister", 2493 "DESTINATION_BASE_URL", 2494 &target_server_base_url)) 2495 { 2496 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2497 "twister", 2498 "DESTINATION_BASE_URL"); 2499 GNUNET_SCHEDULER_shutdown (); 2500 return; 2501 } 2502 2503 if (GNUNET_SYSERR == 2504 parse_serving_mean (c, 2505 &port, 2506 &serve_unixpath, 2507 &serve_unixmode)) 2508 { 2509 GNUNET_break (0); 2510 GNUNET_SCHEDULER_shutdown (); 2511 return; 2512 } 2513 2514 if (NULL != serve_unixpath) 2515 { 2516 /* Connect the 'fh' socket. */ 2517 fh = open_unix_path (serve_unixpath, 2518 serve_unixmode); 2519 2520 GNUNET_assert (-1 != fh); 2521 } 2522 2523 /* start MHD daemon for HTTP */ 2524 mhd_daemon = MHD_start_daemon 2525 (MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME | MHD_USE_DUAL_STACK, 2526 (-1 == fh) ? (uint16_t) port : 0, 2527 NULL, NULL, 2528 &create_response, NULL, 2529 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16, 2530 MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL, 2531 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL, 2532 MHD_OPTION_LISTEN_SOCKET, fh, 2533 MHD_OPTION_END); 2534 2535 if (NULL == mhd_daemon) 2536 { 2537 GNUNET_break (0); 2538 GNUNET_SCHEDULER_shutdown (); 2539 return; 2540 } 2541 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 2542 NULL); 2543 run_mhd_now (); 2544 } 2545 2546 2547 /** 2548 * Callback called when a client connects to the service. 2549 * 2550 * @param cls closure for the service 2551 * @param c the new client that connected to the service 2552 * @param mq the message queue used to send messages to the client 2553 * @return @a c 2554 */ 2555 static void * 2556 client_connect_cb (void *cls, 2557 struct GNUNET_SERVICE_Client *c, 2558 struct GNUNET_MQ_Handle *mq) 2559 { 2560 (void) cls; 2561 (void) mq; 2562 return c; 2563 } 2564 2565 2566 /** 2567 * Callback called when a client disconnected from the service 2568 * 2569 * @param cls closure for the service 2570 * @param c the client that disconnected 2571 * @param internal_cls should be equal to @a c 2572 */ 2573 static void 2574 client_disconnect_cb (void *cls, 2575 struct GNUNET_SERVICE_Client *c, 2576 void *internal_cls) 2577 { 2578 /* intentionally empty */ 2579 (void) cls; 2580 (void) c; 2581 (void) internal_cls; 2582 } 2583 2584 2585 /** 2586 * Send confirmation that the operation was handled. 2587 * 2588 * @param c handle to the client waiting for confirmation. 2589 */ 2590 static void 2591 send_acknowledgement (struct GNUNET_SERVICE_Client *c) 2592 { 2593 struct GNUNET_MQ_Envelope *env; 2594 struct GNUNET_MessageHeader *hdr; 2595 2596 env = GNUNET_MQ_msg (hdr, 2597 TWISTER_MESSAGE_TYPE_ACKNOWLEDGEMENT); 2598 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (c), 2599 env); 2600 GNUNET_SERVICE_client_continue (c); 2601 } 2602 2603 2604 /** 2605 * Control handler for malforming responses. 2606 * 2607 * @param cls message queue for sending replies 2608 * @param src received message 2609 */ 2610 static void 2611 handle_malform (void *cls, 2612 const struct TWISTER_Malform *src) 2613 { 2614 struct GNUNET_SERVICE_Client *c = cls; 2615 2616 TWISTER_LOG_DEBUG ("Flagging response malformation\n"); 2617 malform = GNUNET_YES; 2618 send_acknowledgement (c); 2619 } 2620 2621 2622 /** 2623 * Control handler for malforming requests. 2624 * 2625 * @param cls message queue for sending replies 2626 * @param src received message 2627 */ 2628 static void 2629 handle_malform_upload (void *cls, 2630 const struct TWISTER_Malform *src) 2631 { 2632 struct GNUNET_SERVICE_Client *c = cls; 2633 2634 TWISTER_LOG_DEBUG ("Flagging request malformation\n"); 2635 malform_upload = GNUNET_YES; 2636 send_acknowledgement (c); 2637 } 2638 2639 2640 /** 2641 * Control handler for deleting JSON response objects 2642 * 2643 * @param cls message queue for sending replies 2644 * @param src received message 2645 */ 2646 static int 2647 check_modify_path_dl (void *cls, 2648 const struct TWISTER_ModifyPath *src) 2649 { 2650 return GNUNET_OK; 2651 } 2652 2653 2654 /** 2655 * Control handler for deleting JSON response objects 2656 * 2657 * @param cls message queue for sending replies 2658 * @param src received message 2659 */ 2660 static void 2661 handle_modify_path_dl (void *cls, 2662 const struct TWISTER_ModifyPath *src) 2663 { 2664 struct GNUNET_SERVICE_Client *c = cls; 2665 uint16_t tailsize; 2666 char *payload_path; 2667 char *payload_value; 2668 2669 tailsize = ntohs (src->header.size) - sizeof (*src); 2670 2671 if (tailsize != GNUNET_STRINGS_buffer_tokenize 2672 ((const char *) &src[1], 2673 tailsize, 2674 2, 2675 &payload_path, 2676 &payload_value)) 2677 { 2678 GNUNET_break_op (0); 2679 GNUNET_SERVICE_client_drop (c); 2680 return; 2681 } 2682 2683 modify_path_dl = GNUNET_strdup (payload_path); 2684 modify_value = GNUNET_strdup (payload_value); 2685 2686 send_acknowledgement (c); 2687 } 2688 2689 2690 /** 2691 * Control handler for deleting JSON response objects 2692 * 2693 * @param cls message queue for sending replies 2694 * @param src received message 2695 */ 2696 static int 2697 check_modify_path_ul (void *cls, 2698 const struct TWISTER_ModifyPath *src) 2699 { 2700 return GNUNET_OK; 2701 } 2702 2703 2704 /** 2705 * Control handler for deleting JSON request objects; 2706 * (means request to the proxied services) 2707 * 2708 * @param cls message queue for sending replies 2709 * @param src received message 2710 */ 2711 static void 2712 handle_modify_path_ul (void *cls, 2713 const struct TWISTER_ModifyPath *src) 2714 { 2715 struct GNUNET_SERVICE_Client *c = cls; 2716 uint16_t tailsize; 2717 char *payload_path; 2718 char *payload_value; 2719 2720 tailsize = ntohs (src->header.size) - sizeof (*src); 2721 2722 if (tailsize != GNUNET_STRINGS_buffer_tokenize 2723 ((const char *) &src[1], 2724 tailsize, 2725 2, 2726 &payload_path, 2727 &payload_value)) 2728 { 2729 GNUNET_break_op (0); 2730 GNUNET_SERVICE_client_drop (c); 2731 return; 2732 } 2733 2734 modify_path_dl = GNUNET_strdup (payload_path); 2735 modify_value = GNUNET_strdup (payload_value); 2736 2737 send_acknowledgement (c); 2738 } 2739 2740 2741 /** 2742 * Control handler for changing an HTTP response header. 2743 * 2744 * @param cls message queue for sending replies 2745 * @param src received message 2746 */ 2747 static int 2748 check_modify_header_dl (void *cls, 2749 const struct TWISTER_ModifyPath *src) 2750 { 2751 return GNUNET_OK; 2752 } 2753 2754 2755 /** 2756 * Control handler for changing an HTTP response header. 2757 * 2758 * @param cls message queue for sending replies 2759 * @param src received message 2760 */ 2761 static void 2762 handle_modify_header_dl (void *cls, 2763 const struct TWISTER_ModifyPath *src) 2764 { 2765 struct GNUNET_SERVICE_Client *c = cls; 2766 uint16_t tailsize; 2767 char *payload_path; 2768 char *payload_value; 2769 2770 tailsize = ntohs (src->header.size) - sizeof (*src); 2771 2772 if (tailsize != GNUNET_STRINGS_buffer_tokenize 2773 ((const char *) &src[1], 2774 tailsize, 2775 2, 2776 &payload_path, 2777 &payload_value)) 2778 { 2779 GNUNET_break_op (0); 2780 GNUNET_SERVICE_client_drop (c); 2781 return; 2782 } 2783 2784 modify_header_dl = GNUNET_strdup (payload_path); 2785 modify_value = GNUNET_strdup (payload_value); 2786 2787 send_acknowledgement (c); 2788 } 2789 2790 2791 /** 2792 * Control handler for flipping JSON strings into response objects 2793 * 2794 * @param cls message queue for sending replies 2795 * @param src received message 2796 */ 2797 static void 2798 handle_flip_path_dl (void *cls, 2799 const struct TWISTER_FlipPath *src) 2800 { 2801 struct GNUNET_SERVICE_Client *c = cls; 2802 uint16_t tailsize; 2803 char *payload; 2804 2805 tailsize = ntohs (src->header.size) - sizeof (*src); 2806 if (tailsize != 2807 GNUNET_STRINGS_buffer_tokenize ( 2808 (const char *) &src[1], 2809 tailsize, 2810 1, 2811 &payload)) 2812 { 2813 GNUNET_break_op (0); 2814 GNUNET_SERVICE_client_drop (c); 2815 return; 2816 } 2817 GNUNET_free (flip_path_dl); 2818 flip_path_dl = GNUNET_strdup (payload); 2819 send_acknowledgement (c); 2820 } 2821 2822 2823 /** 2824 * Control handler for flipping JSON strings into request objects 2825 * 2826 * @param cls message queue for sending replies 2827 * @param src received message 2828 */ 2829 static int 2830 check_flip_path_ul (void *cls, 2831 const struct TWISTER_FlipPath *src) 2832 { 2833 return GNUNET_OK; 2834 } 2835 2836 2837 /** 2838 * Control handler for flipping JSON strings into request objects 2839 * 2840 * @param cls message queue for sending replies 2841 * @param src received message 2842 */ 2843 static int 2844 check_flip_path_dl (void *cls, 2845 const struct TWISTER_FlipPath *src) 2846 { 2847 return GNUNET_OK; 2848 } 2849 2850 2851 /** 2852 * Control handler for flipping JSON strings into request objects 2853 * 2854 * @param cls message queue for sending replies 2855 * @param src received message 2856 */ 2857 static void 2858 handle_flip_path_ul (void *cls, 2859 const struct TWISTER_FlipPath *src) 2860 { 2861 struct GNUNET_SERVICE_Client *c = cls; 2862 uint16_t tailsize; 2863 char *payload; 2864 2865 tailsize = ntohs (src->header.size) - sizeof (*src); 2866 2867 if (tailsize != GNUNET_STRINGS_buffer_tokenize 2868 ((const char *) &src[1], 2869 tailsize, 2870 1, 2871 &payload)) 2872 { 2873 GNUNET_break_op (0); 2874 GNUNET_SERVICE_client_drop (c); 2875 return; 2876 } 2877 2878 flip_path_ul = GNUNET_strdup (payload); 2879 send_acknowledgement (c); 2880 } 2881 2882 2883 /** 2884 * Control handler for deleting JSON fields from response objects 2885 * 2886 * @param cls message queue for sending replies 2887 * @param src received message 2888 */ 2889 static int 2890 check_delete_path (void *cls, 2891 const struct TWISTER_DeletePath *src) 2892 { 2893 return GNUNET_OK; 2894 } 2895 2896 2897 /** 2898 * Control handler for deleting JSON fields from response objects 2899 * 2900 * @param cls message queue for sending replies 2901 * @param src received message 2902 */ 2903 static void 2904 handle_delete_path (void *cls, 2905 const struct TWISTER_DeletePath *src) 2906 { 2907 struct GNUNET_SERVICE_Client *c = cls; 2908 uint16_t tailsize; 2909 char *payload; 2910 2911 tailsize = ntohs (src->header.size) - sizeof (*src); 2912 2913 if (tailsize != GNUNET_STRINGS_buffer_tokenize 2914 ((const char *) &src[1], 2915 tailsize, 2916 1, 2917 &payload)) 2918 { 2919 GNUNET_break_op (0); 2920 GNUNET_SERVICE_client_drop (c); 2921 return; 2922 } 2923 GNUNET_free (delete_path); 2924 delete_path = GNUNET_strdup (payload); 2925 2926 send_acknowledgement (c); 2927 } 2928 2929 2930 /** 2931 * Control handler for changing the response code 2932 * 2933 * @param cls message queue for sending replies 2934 * @param src received message 2935 */ 2936 static void 2937 handle_set_response_code 2938 (void *cls, 2939 const struct TWISTER_SetResponseCode *src) 2940 { 2941 struct GNUNET_SERVICE_Client *c = cls; 2942 2943 hack_response_code = ntohl (src->response_code); 2944 send_acknowledgement (c); 2945 } 2946 2947 2948 /** 2949 * Main function. 2950 */ 2951 int 2952 main (int argc, 2953 char *const *argv) 2954 { 2955 struct GNUNET_MQ_MessageHandler mh[] = { 2956 GNUNET_MQ_hd_fixed_size (set_response_code, 2957 TWISTER_MESSAGE_TYPE_SET_RESPONSE_CODE, 2958 struct TWISTER_SetResponseCode, 2959 NULL), 2960 GNUNET_MQ_hd_var_size (modify_path_ul, 2961 TWISTER_MESSAGE_TYPE_MODIFY_PATH_UL, 2962 struct TWISTER_ModifyPath, 2963 NULL), 2964 GNUNET_MQ_hd_var_size (modify_path_dl, 2965 TWISTER_MESSAGE_TYPE_MODIFY_PATH_DL, 2966 struct TWISTER_ModifyPath, 2967 NULL), 2968 GNUNET_MQ_hd_var_size (modify_header_dl, 2969 TWISTER_MESSAGE_TYPE_MODIFY_HEADER_DL, 2970 struct TWISTER_ModifyPath, 2971 NULL), 2972 GNUNET_MQ_hd_fixed_size (malform_upload, 2973 TWISTER_MESSAGE_TYPE_MALFORM_UPLOAD, 2974 struct TWISTER_Malform, 2975 NULL), 2976 GNUNET_MQ_hd_fixed_size (malform, 2977 TWISTER_MESSAGE_TYPE_MALFORM, 2978 struct TWISTER_Malform, 2979 NULL), 2980 GNUNET_MQ_hd_var_size (delete_path, 2981 TWISTER_MESSAGE_TYPE_DELETE_PATH, 2982 struct TWISTER_DeletePath, 2983 NULL), 2984 GNUNET_MQ_hd_var_size (flip_path_ul, 2985 TWISTER_MESSAGE_TYPE_FLIP_PATH_UL, 2986 struct TWISTER_FlipPath, 2987 NULL), 2988 GNUNET_MQ_hd_var_size (flip_path_dl, 2989 TWISTER_MESSAGE_TYPE_FLIP_PATH_DL, 2990 struct TWISTER_FlipPath, 2991 NULL), 2992 GNUNET_MQ_handler_end () 2993 }; 2994 2995 return GNUNET_SERVICE_run_ (TWISTER_project_data (), 2996 argc, 2997 argv, 2998 "twister", 2999 GNUNET_SERVICE_OPTION_NONE, 3000 &run, 3001 &client_connect_cb, 3002 &client_disconnect_cb, 3003 NULL, 3004 mh); 3005 } 3006 3007 3008 /* end of taler-twister-service.c */