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