gnunet-gns-proxy.c (107094B)
1 /* 2 This file is part of GNUnet. 3 Copyright (C) 2012-2018 GNUnet e.V. 4 5 GNUnet is free software: you can redistribute it and/or modify it 6 under the terms of the GNU Affero General Public License as published 7 by the Free Software Foundation, either version 3 of the License, 8 or (at your option) any later version. 9 10 GNUnet is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Affero General Public License for more details. 14 15 You should have received a copy of the GNU Affero General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 SPDX-License-Identifier: AGPL3.0-or-later 19 */ 20 /** 21 * @author Martin Schanzenbach 22 * @author Christian Grothoff 23 * @file src/gns/gnunet-gns-proxy.c 24 * @brief HTTP(S) proxy that rewrites URIs and fakes certificates to make GNS work 25 * with legacy browsers 26 * 27 * TODO: 28 * - double-check queueing logic 29 */ 30 #include "platform.h" 31 #include <microhttpd.h> 32 /* Just included for the right curl.h */ 33 #include "gnunet_curl_lib.h" 34 #include <gnutls/gnutls.h> 35 #include <gnutls/x509.h> 36 #include <gnutls/abstract.h> 37 #include <gnutls/crypto.h> 38 #if HAVE_GNUTLS_DANE 39 #include <gnutls/dane.h> 40 #endif 41 #include <regex.h> 42 #include "gnunet_util_lib.h" 43 #include "gnunet_gns_service.h" 44 #include "gnunet_mhd_compat.h" 45 46 /** 47 * Default Socks5 listen port. 48 */ 49 #define GNUNET_GNS_PROXY_PORT 7777 50 51 /** 52 * Maximum supported length for a URI. 53 * Should die. @deprecated 54 */ 55 #define MAX_HTTP_URI_LENGTH 2048 56 57 /** 58 * Maximum number of DANE records we support 59 * per domain name (and port and protocol). 60 */ 61 #define MAX_DANES 32 62 63 /** 64 * Size of the buffer for the data upload / download. Must be 65 * enough for curl, thus CURL_MAX_WRITE_SIZE is needed here (16k). 66 */ 67 #define IO_BUFFERSIZE CURL_MAX_WRITE_SIZE 68 69 /** 70 * Size of the read/write buffers for Socks. Uses 71 * 256 bytes for the hostname (at most), plus a few 72 * bytes overhead for the messages. 73 */ 74 #define SOCKS_BUFFERSIZE (256 + 32) 75 76 /** 77 * Port for plaintext HTTP. 78 */ 79 #define HTTP_PORT 80 80 81 /** 82 * Port for HTTPS. 83 */ 84 #define HTTPS_PORT 443 85 86 /** 87 * Largest allowed size for a PEM certificate. 88 */ 89 #define MAX_PEM_SIZE (10 * 1024) 90 91 /** 92 * After how long do we clean up unused MHD TLS instances? 93 */ 94 #define MHD_CACHE_TIMEOUT GNUNET_TIME_relative_multiply ( \ 95 GNUNET_TIME_UNIT_MINUTES, 5) 96 97 /** 98 * After how long do we clean up Socks5 handles that failed to show any activity 99 * with their respective MHD instance? 100 */ 101 #define HTTP_HANDSHAKE_TIMEOUT GNUNET_TIME_relative_multiply ( \ 102 GNUNET_TIME_UNIT_SECONDS, 15) 103 104 105 /** 106 * Log curl error. 107 * 108 * @param level log level 109 * @param fun name of curl_easy-function that gave the error 110 * @param rc return code from curl 111 */ 112 #define LOG_CURL_EASY(level, fun, rc) \ 113 GNUNET_log (level, \ 114 _ ("%s failed at %s:%d: `%s'\n"), \ 115 fun, \ 116 __FILE__, \ 117 __LINE__, \ 118 curl_easy_strerror (rc)) 119 120 121 /* *************** Socks protocol definitions (move to TUN?) ****************** */ 122 123 /** 124 * Which SOCKS version do we speak? 125 */ 126 #define SOCKS_VERSION_5 0x05 127 128 /** 129 * Flag to set for 'no authentication'. 130 */ 131 #define SOCKS_AUTH_NONE 0 132 133 134 /** 135 * Commands in Socks5. 136 */ 137 enum Socks5Commands 138 { 139 /** 140 * Establish TCP/IP stream. 141 */ 142 SOCKS5_CMD_TCP_STREAM = 1, 143 144 /** 145 * Establish TCP port binding. 146 */ 147 SOCKS5_CMD_TCP_PORT = 2, 148 149 /** 150 * Establish UDP port binding. 151 */ 152 SOCKS5_CMD_UDP_PORT = 3 153 }; 154 155 156 /** 157 * Address types in Socks5. 158 */ 159 enum Socks5AddressType 160 { 161 /** 162 * IPv4 address. 163 */ 164 SOCKS5_AT_IPV4 = 1, 165 166 /** 167 * IPv4 address. 168 */ 169 SOCKS5_AT_DOMAINNAME = 3, 170 171 /** 172 * IPv6 address. 173 */ 174 SOCKS5_AT_IPV6 = 4 175 }; 176 177 178 /** 179 * Status codes in Socks5 response. 180 */ 181 enum Socks5StatusCode 182 { 183 SOCKS5_STATUS_REQUEST_GRANTED = 0, 184 SOCKS5_STATUS_GENERAL_FAILURE = 1, 185 SOCKS5_STATUS_CONNECTION_NOT_ALLOWED_BY_RULE = 2, 186 SOCKS5_STATUS_NETWORK_UNREACHABLE = 3, 187 SOCKS5_STATUS_HOST_UNREACHABLE = 4, 188 SOCKS5_STATUS_CONNECTION_REFUSED_BY_HOST = 5, 189 SOCKS5_STATUS_TTL_EXPIRED = 6, 190 SOCKS5_STATUS_COMMAND_NOT_SUPPORTED = 7, 191 SOCKS5_STATUS_ADDRESS_TYPE_NOT_SUPPORTED = 8 192 }; 193 194 195 /** 196 * Client hello in Socks5 protocol. 197 */ 198 struct Socks5ClientHelloMessage 199 { 200 /** 201 * Should be #SOCKS_VERSION_5. 202 */ 203 uint8_t version; 204 205 /** 206 * How many authentication methods does the client support. 207 */ 208 uint8_t num_auth_methods; 209 210 /* followed by supported authentication methods, 1 byte per method */ 211 }; 212 213 214 /** 215 * Server hello in Socks5 protocol. 216 */ 217 struct Socks5ServerHelloMessage 218 { 219 /** 220 * Should be #SOCKS_VERSION_5. 221 */ 222 uint8_t version; 223 224 /** 225 * Chosen authentication method, for us always #SOCKS_AUTH_NONE, 226 * which skips the authentication step. 227 */ 228 uint8_t auth_method; 229 }; 230 231 232 /** 233 * Client socks request in Socks5 protocol. 234 */ 235 struct Socks5ClientRequestMessage 236 { 237 /** 238 * Should be #SOCKS_VERSION_5. 239 */ 240 uint8_t version; 241 242 /** 243 * Command code, we only uspport #SOCKS5_CMD_TCP_STREAM. 244 */ 245 uint8_t command; 246 247 /** 248 * Reserved, always zero. 249 */ 250 uint8_t resvd; 251 252 /** 253 * Address type, an `enum Socks5AddressType`. 254 */ 255 uint8_t addr_type; 256 257 /* 258 * Followed by either an ip4/ipv6 address or a domain name with a 259 * length field (uint8_t) in front (depending on @e addr_type). 260 * followed by port number in network byte order (uint16_t). 261 */ 262 }; 263 264 265 /** 266 * Server response to client requests in Socks5 protocol. 267 */ 268 struct Socks5ServerResponseMessage 269 { 270 /** 271 * Should be #SOCKS_VERSION_5. 272 */ 273 uint8_t version; 274 275 /** 276 * Status code, an `enum Socks5StatusCode` 277 */ 278 uint8_t reply; 279 280 /** 281 * Always zero. 282 */ 283 uint8_t reserved; 284 285 /** 286 * Address type, an `enum Socks5AddressType`. 287 */ 288 uint8_t addr_type; 289 290 /* 291 * Followed by either an ip4/ipv6 address or a domain name with a 292 * length field (uint8_t) in front (depending on @e addr_type). 293 * followed by port number in network byte order (uint16_t). 294 */ 295 }; 296 297 298 /* *********************** Datastructures for HTTP handling ****************** */ 299 300 /** 301 * A structure for CA cert/key 302 */ 303 struct ProxyCA 304 { 305 /** 306 * The certificate 307 */ 308 gnutls_x509_crt_t cert; 309 310 /** 311 * The private key 312 */ 313 gnutls_x509_privkey_t key; 314 }; 315 316 317 /** 318 * Structure for GNS certificates 319 */ 320 struct ProxyGNSCertificate 321 { 322 /** 323 * The certificate as PEM 324 */ 325 char cert[MAX_PEM_SIZE]; 326 327 /** 328 * The private key as PEM 329 */ 330 char key[MAX_PEM_SIZE]; 331 }; 332 333 334 /** 335 * A structure for all running Httpds 336 */ 337 struct MhdHttpList 338 { 339 /** 340 * DLL for httpds 341 */ 342 struct MhdHttpList *prev; 343 344 /** 345 * DLL for httpds 346 */ 347 struct MhdHttpList *next; 348 349 /** 350 * the domain name to server (only important for TLS) 351 */ 352 char *domain; 353 354 /** 355 * The daemon handle 356 */ 357 struct MHD_Daemon *daemon; 358 359 /** 360 * Optional proxy certificate used 361 */ 362 struct ProxyGNSCertificate *proxy_cert; 363 364 /** 365 * The task ID 366 */ 367 struct GNUNET_SCHEDULER_Task *httpd_task; 368 369 /** 370 * is this an ssl daemon? 371 */ 372 int is_ssl; 373 }; 374 375 376 /* ***************** Datastructures for Socks handling **************** */ 377 378 379 /** 380 * The socks phases. 381 */ 382 enum SocksPhase 383 { 384 /** 385 * We're waiting to get the client hello. 386 */ 387 SOCKS5_INIT, 388 389 /** 390 * We're waiting to get the initial request. 391 */ 392 SOCKS5_REQUEST, 393 394 /** 395 * We are currently resolving the destination. 396 */ 397 SOCKS5_RESOLVING, 398 399 /** 400 * We're in transfer mode. 401 */ 402 SOCKS5_DATA_TRANSFER, 403 404 /** 405 * Finish writing the write buffer, then clean up. 406 */ 407 SOCKS5_WRITE_THEN_CLEANUP, 408 409 /** 410 * Socket has been passed to MHD, do not close it anymore. 411 */ 412 SOCKS5_SOCKET_WITH_MHD, 413 414 /** 415 * We've started receiving upload data from MHD. 416 */ 417 SOCKS5_SOCKET_UPLOAD_STARTED, 418 419 /** 420 * We've finished receiving upload data from MHD. 421 */ 422 SOCKS5_SOCKET_UPLOAD_DONE, 423 424 /** 425 * We've finished uploading data via CURL and can now download. 426 */ 427 SOCKS5_SOCKET_DOWNLOAD_STARTED, 428 429 /** 430 * We've finished receiving download data from cURL. 431 */ 432 SOCKS5_SOCKET_DOWNLOAD_DONE 433 }; 434 435 436 /** 437 * A header list 438 */ 439 struct HttpResponseHeader 440 { 441 /** 442 * DLL 443 */ 444 struct HttpResponseHeader *next; 445 446 /** 447 * DLL 448 */ 449 struct HttpResponseHeader *prev; 450 451 /** 452 * Header type 453 */ 454 char *type; 455 456 /** 457 * Header value 458 */ 459 char *value; 460 }; 461 462 /** 463 * A structure for socks requests 464 */ 465 struct Socks5Request 466 { 467 /** 468 * DLL. 469 */ 470 struct Socks5Request *next; 471 472 /** 473 * DLL. 474 */ 475 struct Socks5Request *prev; 476 477 /** 478 * The client socket 479 */ 480 struct GNUNET_NETWORK_Handle *sock; 481 482 /** 483 * Handle to GNS lookup, during #SOCKS5_RESOLVING phase. 484 */ 485 struct GNUNET_GNS_LookupWithTldRequest *gns_lookup; 486 487 /** 488 * Client socket read task 489 */ 490 struct GNUNET_SCHEDULER_Task *rtask; 491 492 /** 493 * Client socket write task 494 */ 495 struct GNUNET_SCHEDULER_Task *wtask; 496 497 /** 498 * Timeout task 499 */ 500 struct GNUNET_SCHEDULER_Task *timeout_task; 501 502 /** 503 * Read buffer 504 */ 505 char rbuf[SOCKS_BUFFERSIZE]; 506 507 /** 508 * Write buffer 509 */ 510 char wbuf[SOCKS_BUFFERSIZE]; 511 512 /** 513 * Buffer we use for moving data between MHD and curl (in both directions). 514 */ 515 char io_buf[IO_BUFFERSIZE]; 516 517 /** 518 * MHD HTTP instance handling this request, NULL for none. 519 */ 520 struct MhdHttpList *hd; 521 522 /** 523 * MHD connection for this request. 524 */ 525 struct MHD_Connection *con; 526 527 /** 528 * MHD response object for this request. 529 */ 530 struct MHD_Response *response; 531 532 /** 533 * the domain name to server (only important for TLS) 534 */ 535 char *domain; 536 537 /** 538 * DNS Legacy Host Name as given by GNS, NULL if not given. 539 */ 540 char *leho; 541 542 /** 543 * Payload of the DANE records encountered. 544 */ 545 char *dane_data[MAX_DANES + 1]; 546 547 /** 548 * The URL to fetch 549 */ 550 char *url; 551 552 /** 553 * Handle to cURL 554 */ 555 CURL *curl; 556 557 /** 558 * HTTP request headers for the curl request. 559 */ 560 struct curl_slist *headers; 561 562 /** 563 * DNS->IP mappings resolved through GNS 564 */ 565 struct curl_slist *hosts; 566 567 /** 568 * HTTP response code to give to MHD for the response. 569 */ 570 unsigned int response_code; 571 572 /** 573 * Number of bytes in @e dane_data. 574 */ 575 int dane_data_len[MAX_DANES + 1]; 576 577 /** 578 * Number of entries used in @e dane_data_len 579 * and @e dane_data. 580 */ 581 unsigned int num_danes; 582 583 /** 584 * Number of bytes already in read buffer 585 */ 586 size_t rbuf_len; 587 588 /** 589 * Number of bytes already in write buffer 590 */ 591 size_t wbuf_len; 592 593 /** 594 * Number of bytes already in the IO buffer. 595 */ 596 size_t io_len; 597 598 /** 599 * Once known, what's the target address for the connection? 600 */ 601 struct sockaddr_storage destination_address; 602 603 /** 604 * The socks state 605 */ 606 enum SocksPhase state; 607 608 /** 609 * Desired destination port. 610 */ 611 uint16_t port; 612 613 /** 614 * Headers from response 615 */ 616 struct HttpResponseHeader *header_head; 617 618 /** 619 * Headers from response 620 */ 621 struct HttpResponseHeader *header_tail; 622 623 /** 624 * X.509 Certificate status 625 */ 626 int ssl_checked; 627 628 /** 629 * Was the hostname resolved via GNS? 630 */ 631 int is_gns; 632 633 /** 634 * This is (probably) a TLS connection 635 */ 636 int is_tls; 637 638 /** 639 * Did we suspend MHD processing? 640 */ 641 int suspended; 642 643 /** 644 * Did we pause CURL processing? 645 */ 646 int curl_paused; 647 }; 648 649 650 /* *********************** Globals **************************** */ 651 652 /** 653 * The address to bind to 654 */ 655 static in_addr_t address; 656 657 /** 658 * The IPv6 address to bind to 659 */ 660 static struct in6_addr address6; 661 662 /** 663 * The port the proxy is running on (default 7777) 664 */ 665 static uint16_t port = GNUNET_GNS_PROXY_PORT; 666 667 /** 668 * The CA file (pem) to use for the proxy CA 669 */ 670 static char *cafile_opt; 671 672 /** 673 * The listen socket of the proxy for IPv4 674 */ 675 static struct GNUNET_NETWORK_Handle *lsock4; 676 677 /** 678 * The listen socket of the proxy for IPv6 679 */ 680 static struct GNUNET_NETWORK_Handle *lsock6; 681 682 /** 683 * The listen task ID for IPv4 684 */ 685 static struct GNUNET_SCHEDULER_Task *ltask4; 686 687 /** 688 * The listen task ID for IPv6 689 */ 690 static struct GNUNET_SCHEDULER_Task *ltask6; 691 692 /** 693 * The cURL download task (curl multi API). 694 */ 695 static struct GNUNET_SCHEDULER_Task *curl_download_task; 696 697 /** 698 * The cURL multi handle 699 */ 700 static CURLM *curl_multi; 701 702 /** 703 * Handle to the GNS service 704 */ 705 static struct GNUNET_GNS_Handle *gns_handle; 706 707 /** 708 * Disable IPv6. 709 */ 710 static int disable_v6; 711 712 /** 713 * DLL for http/https daemons 714 */ 715 static struct MhdHttpList *mhd_httpd_head; 716 717 /** 718 * DLL for http/https daemons 719 */ 720 static struct MhdHttpList *mhd_httpd_tail; 721 722 /** 723 * Daemon for HTTP (we have one per X.509 certificate, and then one for 724 * all HTTP connections; this is the one for HTTP, not HTTPS). 725 */ 726 static struct MhdHttpList *httpd; 727 728 /** 729 * DLL of active socks requests. 730 */ 731 static struct Socks5Request *s5r_head; 732 733 /** 734 * DLL of active socks requests. 735 */ 736 static struct Socks5Request *s5r_tail; 737 738 /** 739 * The CA for X.509 certificate generation 740 */ 741 static struct ProxyCA proxy_ca; 742 743 /** 744 * Response we return on cURL failures. 745 */ 746 static struct MHD_Response *curl_failure_response; 747 748 /** 749 * Our configuration. 750 */ 751 static const struct GNUNET_CONFIGURATION_Handle *cfg; 752 753 754 /* ************************* Global helpers ********************* */ 755 756 757 /** 758 * Run MHD now, we have extra data ready for the callback. 759 * 760 * @param hd the daemon to run now. 761 */ 762 static void 763 run_mhd_now (struct MhdHttpList *hd); 764 765 766 /** 767 * Clean up s5r handles. 768 * 769 * @param s5r the handle to destroy 770 */ 771 static void 772 cleanup_s5r (struct Socks5Request *s5r) 773 { 774 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 775 "Cleaning up socks request\n"); 776 if (NULL != s5r->curl) 777 { 778 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 779 "Cleaning up cURL handle\n"); 780 curl_multi_remove_handle (curl_multi, 781 s5r->curl); 782 curl_easy_cleanup (s5r->curl); 783 s5r->curl = NULL; 784 } 785 if (s5r->suspended) 786 { 787 s5r->suspended = GNUNET_NO; 788 MHD_resume_connection (s5r->con); 789 } 790 curl_slist_free_all (s5r->headers); 791 if (NULL != s5r->hosts) 792 { 793 curl_slist_free_all (s5r->hosts); 794 } 795 if ((NULL != s5r->response) && 796 (curl_failure_response != s5r->response)) 797 { 798 MHD_destroy_response (s5r->response); 799 s5r->response = NULL; 800 } 801 if (NULL != s5r->rtask) 802 { 803 GNUNET_SCHEDULER_cancel (s5r->rtask); 804 s5r->rtask = NULL; 805 } 806 if (NULL != s5r->timeout_task) 807 { 808 GNUNET_SCHEDULER_cancel (s5r->timeout_task); 809 s5r->timeout_task = NULL; 810 } 811 if (NULL != s5r->wtask) 812 { 813 GNUNET_SCHEDULER_cancel (s5r->wtask); 814 s5r->wtask = NULL; 815 } 816 if (NULL != s5r->gns_lookup) 817 { 818 GNUNET_GNS_lookup_with_tld_cancel (s5r->gns_lookup); 819 s5r->gns_lookup = NULL; 820 } 821 if (NULL != s5r->sock) 822 { 823 if (SOCKS5_SOCKET_WITH_MHD <= s5r->state) 824 GNUNET_NETWORK_socket_free_memory_only_ (s5r->sock); 825 else 826 GNUNET_NETWORK_socket_close (s5r->sock); 827 s5r->sock = NULL; 828 } 829 GNUNET_CONTAINER_DLL_remove (s5r_head, 830 s5r_tail, 831 s5r); 832 GNUNET_free (s5r->domain); 833 GNUNET_free (s5r->leho); 834 GNUNET_free (s5r->url); 835 for (unsigned int i = 0; i < s5r->num_danes; i++) 836 GNUNET_free (s5r->dane_data[i]); 837 GNUNET_free (s5r); 838 } 839 840 841 /* ************************* HTTP handling with cURL *********************** */ 842 843 static void 844 curl_download_prepare (void); 845 846 847 /** 848 * Callback for MHD response generation. This function is called from 849 * MHD whenever MHD expects to get data back. Copies data from the 850 * io_buf, if available. 851 * 852 * @param cls closure with our `struct Socks5Request` 853 * @param pos in buffer 854 * @param buf where to copy data 855 * @param max available space in @a buf 856 * @return number of bytes written to @a buf 857 */ 858 static ssize_t 859 mhd_content_cb (void *cls, 860 uint64_t pos, 861 char*buf, 862 size_t max) 863 { 864 struct Socks5Request *s5r = cls; 865 size_t bytes_to_copy; 866 867 if ((SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) || 868 (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state)) 869 { 870 /* we're still not done with the upload, do not yet 871 start the download, the IO buffer is still full 872 with upload data. */ 873 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 874 "Pausing MHD download %s%s, not yet ready for download\n", 875 s5r->domain, 876 s5r->url); 877 return 0; /* not yet ready for data download */ 878 } 879 bytes_to_copy = GNUNET_MIN (max, 880 s5r->io_len); 881 if ((0 == bytes_to_copy) && 882 (SOCKS5_SOCKET_DOWNLOAD_DONE != s5r->state)) 883 { 884 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 885 "Pausing MHD download %s%s, no data available\n", 886 s5r->domain, 887 s5r->url); 888 if (NULL != s5r->curl) 889 { 890 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 891 "Continuing CURL interaction for %s%s\n", 892 s5r->domain, 893 s5r->url); 894 if (GNUNET_YES == s5r->curl_paused) 895 { 896 s5r->curl_paused = GNUNET_NO; 897 curl_easy_pause (s5r->curl, 898 CURLPAUSE_CONT); 899 } 900 curl_download_prepare (); 901 } 902 if (GNUNET_NO == s5r->suspended) 903 { 904 MHD_suspend_connection (s5r->con); 905 s5r->suspended = GNUNET_YES; 906 } 907 return 0; /* more data later */ 908 } 909 if ((0 == bytes_to_copy) && 910 (SOCKS5_SOCKET_DOWNLOAD_DONE == s5r->state)) 911 { 912 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 913 "Completed MHD download %s%s\n", 914 s5r->domain, 915 s5r->url); 916 return MHD_CONTENT_READER_END_OF_STREAM; 917 } 918 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 919 "Writing %llu/%llu bytes to %s%s\n", 920 (unsigned long long) bytes_to_copy, 921 (unsigned long long) s5r->io_len, 922 s5r->domain, 923 s5r->url); 924 GNUNET_memcpy (buf, 925 s5r->io_buf, 926 bytes_to_copy); 927 memmove (s5r->io_buf, 928 &s5r->io_buf[bytes_to_copy], 929 s5r->io_len - bytes_to_copy); 930 s5r->io_len -= bytes_to_copy; 931 if ((NULL != s5r->curl) && 932 (GNUNET_YES == s5r->curl_paused)) 933 { 934 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 935 "Continuing CURL interaction for %s%s\n", 936 s5r->domain, 937 s5r->url); 938 s5r->curl_paused = GNUNET_NO; 939 curl_easy_pause (s5r->curl, 940 CURLPAUSE_CONT); 941 } 942 return bytes_to_copy; 943 } 944 945 946 /** 947 * Check that the website has presented us with a valid X.509 certificate. 948 * The certificate must either match the domain name or the LEHO name 949 * (or, if available, the TLSA record). 950 * 951 * @param s5r request to check for. 952 * @return #GNUNET_OK if the certificate is valid 953 */ 954 static int 955 check_ssl_certificate (struct Socks5Request *s5r) 956 { 957 unsigned int cert_list_size; 958 const gnutls_datum_t *chainp; 959 const struct curl_tlssessioninfo *tlsinfo; 960 char certdn[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 3]; 961 size_t size; 962 gnutls_x509_crt_t x509_cert; 963 int rc; 964 const char *name; 965 966 s5r->ssl_checked = GNUNET_YES; 967 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 968 "Checking X.509 certificate\n"); 969 if (CURLE_OK != 970 curl_easy_getinfo (s5r->curl, 971 CURLINFO_TLS_SSL_PTR, 972 &tlsinfo)) 973 return GNUNET_SYSERR; 974 if (CURLSSLBACKEND_GNUTLS != tlsinfo->backend) 975 { 976 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 977 _ ("Unsupported CURL TLS backend %d\n"), 978 tlsinfo->backend); 979 return GNUNET_SYSERR; 980 } 981 chainp = gnutls_certificate_get_peers (tlsinfo->internals, 982 &cert_list_size); 983 if ((! chainp) || 984 (0 == cert_list_size)) 985 return GNUNET_SYSERR; 986 987 size = sizeof(certdn); 988 /* initialize an X.509 certificate structure. */ 989 gnutls_x509_crt_init (&x509_cert); 990 gnutls_x509_crt_import (x509_cert, 991 chainp, 992 GNUTLS_X509_FMT_DER); 993 994 if (0 != (rc = gnutls_x509_crt_get_dn_by_oid (x509_cert, 995 GNUTLS_OID_X520_COMMON_NAME, 996 0, /* the first and only one */ 997 0 /* no DER encoding */, 998 certdn, 999 &size))) 1000 { 1001 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1002 _ ("Failed to fetch CN from cert: %s\n"), 1003 gnutls_strerror (rc)); 1004 gnutls_x509_crt_deinit (x509_cert); 1005 return GNUNET_SYSERR; 1006 } 1007 /* check for TLSA/DANE records */ 1008 #if HAVE_GNUTLS_DANE 1009 if (0 != s5r->num_danes) 1010 { 1011 dane_state_t dane_state; 1012 dane_query_t dane_query; 1013 unsigned int verify; 1014 1015 /* FIXME: add flags to gnutls to NOT read UNBOUND_ROOT_KEY_FILE here! */ 1016 if (0 != (rc = dane_state_init (&dane_state, 1017 #ifdef DANE_F_IGNORE_DNSSEC 1018 DANE_F_IGNORE_DNSSEC | 1019 #endif 1020 DANE_F_IGNORE_LOCAL_RESOLVER))) 1021 { 1022 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1023 _ ("Failed to initialize DANE: %s\n"), 1024 dane_strerror (rc)); 1025 gnutls_x509_crt_deinit (x509_cert); 1026 return GNUNET_SYSERR; 1027 } 1028 s5r->dane_data[s5r->num_danes] = NULL; 1029 s5r->dane_data_len[s5r->num_danes] = 0; 1030 if (0 != (rc = dane_raw_tlsa (dane_state, 1031 &dane_query, 1032 s5r->dane_data, 1033 s5r->dane_data_len, 1034 GNUNET_YES, 1035 GNUNET_NO))) 1036 { 1037 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1038 _ ("Failed to parse DANE record: %s\n"), 1039 dane_strerror (rc)); 1040 dane_state_deinit (dane_state); 1041 gnutls_x509_crt_deinit (x509_cert); 1042 return GNUNET_SYSERR; 1043 } 1044 if (0 != (rc = dane_verify_crt_raw (dane_state, 1045 chainp, 1046 cert_list_size, 1047 gnutls_certificate_type_get ( 1048 tlsinfo->internals), 1049 dane_query, 1050 0, 0, 1051 &verify))) 1052 { 1053 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1054 _ ("Failed to verify TLS connection using DANE: %s\n"), 1055 dane_strerror (rc)); 1056 dane_query_deinit (dane_query); 1057 dane_state_deinit (dane_state); 1058 gnutls_x509_crt_deinit (x509_cert); 1059 return GNUNET_SYSERR; 1060 } 1061 if (0 != verify) 1062 { 1063 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1064 _ ( 1065 "Failed DANE verification failed with GnuTLS verify status code: %u\n"), 1066 verify); 1067 dane_query_deinit (dane_query); 1068 dane_state_deinit (dane_state); 1069 gnutls_x509_crt_deinit (x509_cert); 1070 return GNUNET_SYSERR; 1071 } 1072 dane_query_deinit (dane_query); 1073 dane_state_deinit (dane_state); 1074 /* success! */ 1075 } 1076 else 1077 #endif 1078 { 1079 /* try LEHO or ordinary domain name X509 verification */ 1080 name = s5r->domain; 1081 if (NULL != s5r->leho) 1082 name = s5r->leho; 1083 if (NULL != name) 1084 { 1085 if (0 == (rc = gnutls_x509_crt_check_hostname (x509_cert, 1086 name))) 1087 { 1088 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1089 _ ( 1090 "TLS certificate subject name (%s) does not match `%s': %d\n"), 1091 certdn, 1092 name, 1093 rc); 1094 gnutls_x509_crt_deinit (x509_cert); 1095 return GNUNET_SYSERR; 1096 } 1097 } 1098 else 1099 { 1100 /* we did not even have the domain name!? */ 1101 GNUNET_break (0); 1102 return GNUNET_SYSERR; 1103 } 1104 } 1105 gnutls_x509_crt_deinit (x509_cert); 1106 return GNUNET_OK; 1107 } 1108 1109 1110 /** 1111 * We're getting an HTTP response header from cURL. Convert it to the 1112 * MHD response headers. Mostly copies the headers, but makes special 1113 * adjustments to "Set-Cookie" and "Location" headers as those may need 1114 * to be changed from the LEHO to the domain the browser expects. 1115 * 1116 * @param buffer curl buffer with a single line of header data; not 0-terminated! 1117 * @param size curl blocksize 1118 * @param nmemb curl blocknumber 1119 * @param cls our `struct Socks5Request *` 1120 * @return size of processed bytes 1121 */ 1122 static size_t 1123 curl_check_hdr (void *buffer, 1124 size_t size, 1125 size_t nmemb, 1126 void *cls) 1127 { 1128 struct Socks5Request *s5r = cls; 1129 struct HttpResponseHeader *header; 1130 size_t bytes = size * nmemb; 1131 char *ndup; 1132 const char *hdr_type; 1133 const char *cookie_domain; 1134 char *hdr_val; 1135 char *new_cookie_hdr; 1136 char *new_location; 1137 size_t offset; 1138 size_t delta_cdomain; 1139 int domain_matched; 1140 char *tok; 1141 1142 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1143 "Receiving HTTP response header from CURL\n"); 1144 /* first, check TLS certificate */ 1145 if ((GNUNET_YES != s5r->ssl_checked) && 1146 (GNUNET_YES == s5r->is_tls)) 1147 // (HTTPS_PORT == s5r->port)) 1148 { 1149 if (GNUNET_OK != check_ssl_certificate (s5r)) 1150 return 0; 1151 } 1152 ndup = GNUNET_strndup (buffer, 1153 bytes); 1154 hdr_type = strtok (ndup, 1155 ":"); 1156 if (NULL == hdr_type) 1157 { 1158 GNUNET_free (ndup); 1159 return bytes; 1160 } 1161 hdr_val = strtok (NULL, 1162 ""); 1163 if (NULL == hdr_val) 1164 { 1165 GNUNET_free (ndup); 1166 return bytes; 1167 } 1168 if (' ' == *hdr_val) 1169 hdr_val++; 1170 1171 /* custom logic for certain header types */ 1172 new_cookie_hdr = NULL; 1173 if ((NULL != s5r->leho) && 1174 (0 == strcasecmp (hdr_type, 1175 MHD_HTTP_HEADER_SET_COOKIE))) 1176 1177 { 1178 new_cookie_hdr = GNUNET_malloc (strlen (hdr_val) 1179 + strlen (s5r->domain) + 1); 1180 offset = 0; 1181 domain_matched = GNUNET_NO; /* make sure we match domain at most once */ 1182 for (tok = strtok (hdr_val, ";"); NULL != tok; tok = strtok (NULL, ";")) 1183 { 1184 if ((0 == strncasecmp (tok, 1185 " domain", 1186 strlen (" domain"))) && 1187 (GNUNET_NO == domain_matched)) 1188 { 1189 domain_matched = GNUNET_YES; 1190 cookie_domain = tok + strlen (" domain") + 1; 1191 if (strlen (cookie_domain) < strlen (s5r->leho)) 1192 { 1193 delta_cdomain = strlen (s5r->leho) - strlen (cookie_domain); 1194 if (0 == strcasecmp (cookie_domain, 1195 s5r->leho + delta_cdomain)) 1196 { 1197 offset += sprintf (new_cookie_hdr + offset, 1198 " domain=%s;", 1199 s5r->domain); 1200 continue; 1201 } 1202 } 1203 else if (0 == strcmp (cookie_domain, 1204 s5r->leho)) 1205 { 1206 offset += sprintf (new_cookie_hdr + offset, 1207 " domain=%s;", 1208 s5r->domain); 1209 continue; 1210 } 1211 else if (('.' == cookie_domain[0]) && 1212 (0 == strcmp (&cookie_domain[1], 1213 s5r->leho))) 1214 { 1215 offset += sprintf (new_cookie_hdr + offset, 1216 " domain=.%s;", 1217 s5r->domain); 1218 continue; 1219 } 1220 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1221 _ ("Cookie domain `%s' supplied by server is invalid\n"), 1222 tok); 1223 } 1224 GNUNET_memcpy (new_cookie_hdr + offset, 1225 tok, 1226 strlen (tok)); 1227 offset += strlen (tok); 1228 new_cookie_hdr[offset++] = ';'; 1229 } 1230 hdr_val = new_cookie_hdr; 1231 hdr_val[offset] = '\0'; 1232 } 1233 1234 new_location = NULL; 1235 if (0 == strcasecmp (MHD_HTTP_HEADER_TRANSFER_ENCODING, 1236 hdr_type)) 1237 { 1238 /* Ignore transfer encoding, set automatically by MHD if required */ 1239 goto cleanup; 1240 } 1241 if ((0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, 1242 hdr_type))) 1243 { 1244 char *leho_host; 1245 1246 GNUNET_asprintf (&leho_host, 1247 (GNUNET_YES != s5r->is_tls) // (HTTPS_PORT != s5r->port) 1248 ? "http://%s" 1249 : "https://%s", 1250 s5r->leho); 1251 if (0 == strncmp (leho_host, 1252 hdr_val, 1253 strlen (leho_host))) 1254 { 1255 GNUNET_asprintf (&new_location, 1256 "%s%s%s", 1257 (GNUNET_YES != s5r->is_tls) // (HTTPS_PORT != s5r->port) 1258 ? "http://" 1259 : "https://", 1260 s5r->domain, 1261 hdr_val + strlen (leho_host)); 1262 hdr_val = new_location; 1263 } 1264 GNUNET_free (leho_host); 1265 } 1266 else if (0 == strcasecmp (MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, 1267 hdr_type)) 1268 { 1269 char *leho_host; 1270 1271 GNUNET_asprintf (&leho_host, 1272 (GNUNET_YES != s5r->is_tls) // (HTTPS_PORT != s5r->port) 1273 ? "http://%s" 1274 : "https://%s", 1275 s5r->leho); 1276 if (0 == strncmp (leho_host, 1277 hdr_val, 1278 strlen (leho_host))) 1279 { 1280 GNUNET_asprintf (&new_location, 1281 "%s%s", 1282 (GNUNET_YES != s5r->is_tls) // (HTTPS_PORT != s5r->port) 1283 ? "http://" 1284 : "https://", 1285 s5r->domain); 1286 hdr_val = new_location; 1287 } 1288 GNUNET_free (leho_host); 1289 } 1290 1291 /* MHD does not allow certain characters in values, remove those */ 1292 if (NULL != (tok = strchr (hdr_val, '\n'))) 1293 *tok = '\0'; 1294 if (NULL != (tok = strchr (hdr_val, '\r'))) 1295 *tok = '\0'; 1296 if (NULL != (tok = strchr (hdr_val, '\t'))) 1297 *tok = '\0'; 1298 if (0 != strlen (hdr_val)) /* Rely in MHD to set those */ 1299 { 1300 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1301 "Adding header %s: %s to MHD response\n", 1302 hdr_type, 1303 hdr_val); 1304 header = GNUNET_new (struct HttpResponseHeader); 1305 header->type = GNUNET_strdup (hdr_type); 1306 header->value = GNUNET_strdup (hdr_val); 1307 GNUNET_CONTAINER_DLL_insert (s5r->header_head, 1308 s5r->header_tail, 1309 header); 1310 } 1311 cleanup: 1312 GNUNET_free (ndup); 1313 GNUNET_free (new_cookie_hdr); 1314 GNUNET_free (new_location); 1315 return bytes; 1316 } 1317 1318 1319 /** 1320 * Create an MHD response object in @a s5r matching the 1321 * information we got from curl. 1322 * 1323 * @param s5r the request for which we convert the response 1324 * @return #GNUNET_OK on success, #GNUNET_SYSERR if response was 1325 * already initialized before 1326 */ 1327 static int 1328 create_mhd_response_from_s5r (struct Socks5Request *s5r) 1329 { 1330 long resp_code; 1331 double content_length; 1332 1333 if (NULL != s5r->response) 1334 { 1335 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1336 "Response already set!\n"); 1337 return GNUNET_SYSERR; 1338 } 1339 1340 GNUNET_break (CURLE_OK == 1341 curl_easy_getinfo (s5r->curl, 1342 CURLINFO_RESPONSE_CODE, 1343 &resp_code)); 1344 GNUNET_break (CURLE_OK == 1345 curl_easy_getinfo (s5r->curl, 1346 CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, 1347 &content_length)); 1348 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1349 "Creating MHD response with code %d and size %d for %s%s\n", 1350 (int) resp_code, 1351 (int) content_length, 1352 s5r->domain, 1353 s5r->url); 1354 s5r->response_code = resp_code; 1355 s5r->response = MHD_create_response_from_callback ((0 > content_length) 1356 ? MHD_SIZE_UNKNOWN 1357 : content_length, 1358 IO_BUFFERSIZE, 1359 &mhd_content_cb, 1360 s5r, 1361 NULL); 1362 for (struct HttpResponseHeader *header = s5r->header_head; 1363 NULL != header; 1364 header = header->next) 1365 { 1366 if (0 == strcasecmp (header->type, 1367 MHD_HTTP_HEADER_CONTENT_LENGTH)) 1368 continue; /* MHD won't let us mess with those, for good reason */ 1369 if ((0 == strcasecmp (header->type, 1370 MHD_HTTP_HEADER_TRANSFER_ENCODING)) && 1371 ((0 == strcasecmp (header->value, 1372 "identity")) || 1373 (0 == strcasecmp (header->value, 1374 "chunked")))) 1375 continue; /* MHD won't let us mess with those, for good reason */ 1376 if (MHD_YES != 1377 MHD_add_response_header (s5r->response, 1378 header->type, 1379 header->value)) 1380 { 1381 GNUNET_break (0); 1382 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1383 "Failed to add header `%s:%s'\n", 1384 header->type, 1385 header->value); 1386 } 1387 } 1388 /* force connection to be closed after each request, as we 1389 do not support HTTP pipelining (yet, FIXME!) */ 1390 /*GNUNET_break (MHD_YES == 1391 MHD_add_response_header (s5r->response, 1392 MHD_HTTP_HEADER_CONNECTION, 1393 "close"));*/ 1394 MHD_resume_connection (s5r->con); 1395 s5r->suspended = GNUNET_NO; 1396 return GNUNET_OK; 1397 } 1398 1399 1400 /** 1401 * Handle response payload data from cURL. Copies it into our `io_buf` to make 1402 * it available to MHD. 1403 * 1404 * @param ptr pointer to the data 1405 * @param size number of blocks of data 1406 * @param nmemb blocksize 1407 * @param ctx our `struct Socks5Request *` 1408 * @return number of bytes handled 1409 */ 1410 static size_t 1411 curl_download_cb (void *ptr, 1412 size_t size, 1413 size_t nmemb, 1414 void*ctx) 1415 { 1416 struct Socks5Request *s5r = ctx; 1417 size_t total = size * nmemb; 1418 1419 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1420 "Receiving %ux%u bytes for `%s%s' from cURL to download\n", 1421 (unsigned int) size, 1422 (unsigned int) nmemb, 1423 s5r->domain, 1424 s5r->url); 1425 if (NULL == s5r->response) 1426 GNUNET_assert (GNUNET_OK == 1427 create_mhd_response_from_s5r (s5r)); 1428 if ((SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) && 1429 (0 == s5r->io_len)) 1430 { 1431 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1432 "Previous upload finished... starting DOWNLOAD.\n"); 1433 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED; 1434 } 1435 if ((SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) || 1436 (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state)) 1437 { 1438 /* we're still not done with the upload, do not yet 1439 start the download, the IO buffer is still full 1440 with upload data. */ 1441 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1442 "Pausing CURL download `%s%s', waiting for UPLOAD to finish\n", 1443 s5r->domain, 1444 s5r->url); 1445 s5r->curl_paused = GNUNET_YES; 1446 return CURL_WRITEFUNC_PAUSE; /* not yet ready for data download */ 1447 } 1448 if (sizeof(s5r->io_buf) - s5r->io_len < total) 1449 { 1450 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1451 "Pausing CURL `%s%s' download, not enough space %llu %llu %llu\n", 1452 s5r->domain, 1453 s5r->url, 1454 (unsigned long long) sizeof(s5r->io_buf), 1455 (unsigned long long) s5r->io_len, 1456 (unsigned long long) total); 1457 s5r->curl_paused = GNUNET_YES; 1458 return CURL_WRITEFUNC_PAUSE; /* not enough space */ 1459 } 1460 GNUNET_memcpy (&s5r->io_buf[s5r->io_len], 1461 ptr, 1462 total); 1463 s5r->io_len += total; 1464 if (GNUNET_YES == s5r->suspended) 1465 { 1466 MHD_resume_connection (s5r->con); 1467 s5r->suspended = GNUNET_NO; 1468 } 1469 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1470 "Received %llu bytes of payload via cURL from %s\n", 1471 (unsigned long long) total, 1472 s5r->domain); 1473 if (s5r->io_len == total) 1474 run_mhd_now (s5r->hd); 1475 return total; 1476 } 1477 1478 1479 /** 1480 * cURL callback for uploaded (PUT/POST) data. Copies it into our `io_buf` 1481 * to make it available to MHD. 1482 * 1483 * @param buf where to write the data 1484 * @param size number of bytes per member 1485 * @param nmemb number of members available in @a buf 1486 * @param cls our `struct Socks5Request` that generated the data 1487 * @return number of bytes copied to @a buf 1488 */ 1489 static size_t 1490 curl_upload_cb (void *buf, 1491 size_t size, 1492 size_t nmemb, 1493 void *cls) 1494 { 1495 struct Socks5Request *s5r = cls; 1496 size_t len = size * nmemb; 1497 size_t to_copy; 1498 1499 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1500 "Receiving %ux%u bytes for `%s%s' from cURL to upload\n", 1501 (unsigned int) size, 1502 (unsigned int) nmemb, 1503 s5r->domain, 1504 s5r->url); 1505 1506 if ((0 == s5r->io_len) && 1507 (SOCKS5_SOCKET_UPLOAD_DONE != s5r->state)) 1508 { 1509 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1510 "Pausing CURL UPLOAD %s%s, need more data\n", 1511 s5r->domain, 1512 s5r->url); 1513 return CURL_READFUNC_PAUSE; 1514 } 1515 if ((0 == s5r->io_len) && 1516 (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state)) 1517 { 1518 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED; 1519 if (GNUNET_YES == s5r->curl_paused) 1520 { 1521 s5r->curl_paused = GNUNET_NO; 1522 curl_easy_pause (s5r->curl, 1523 CURLPAUSE_CONT); 1524 } 1525 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1526 "Completed CURL UPLOAD %s%s\n", 1527 s5r->domain, 1528 s5r->url); 1529 return 0; /* upload finished, can now download */ 1530 } 1531 if ((SOCKS5_SOCKET_UPLOAD_STARTED != s5r->state) && 1532 (SOCKS5_SOCKET_UPLOAD_DONE != s5r->state)) 1533 { 1534 GNUNET_break (0); 1535 return CURL_READFUNC_ABORT; 1536 } 1537 to_copy = GNUNET_MIN (s5r->io_len, 1538 len); 1539 GNUNET_memcpy (buf, 1540 s5r->io_buf, 1541 to_copy); 1542 memmove (s5r->io_buf, 1543 &s5r->io_buf[to_copy], 1544 s5r->io_len - to_copy); 1545 s5r->io_len -= to_copy; 1546 if (s5r->io_len + to_copy == sizeof(s5r->io_buf)) 1547 run_mhd_now (s5r->hd); /* got more space for upload now */ 1548 return to_copy; 1549 } 1550 1551 1552 /* ************************** main loop of cURL interaction ****************** */ 1553 1554 1555 /** 1556 * Task that is run when we are ready to receive more data 1557 * from curl 1558 * 1559 * @param cls closure 1560 */ 1561 static void 1562 curl_task_download (void *cls); 1563 1564 1565 /** 1566 * Ask cURL for the select() sets and schedule cURL operations. 1567 */ 1568 static void 1569 curl_download_prepare (void) 1570 { 1571 CURLMcode mret; 1572 fd_set rs; 1573 fd_set ws; 1574 fd_set es; 1575 int max; 1576 struct GNUNET_NETWORK_FDSet *grs; 1577 struct GNUNET_NETWORK_FDSet *gws; 1578 long to; 1579 struct GNUNET_TIME_Relative rtime; 1580 1581 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1582 "Scheduling CURL interaction\n"); 1583 if (NULL != curl_download_task) 1584 { 1585 GNUNET_SCHEDULER_cancel (curl_download_task); 1586 curl_download_task = NULL; 1587 } 1588 max = -1; 1589 FD_ZERO (&rs); 1590 FD_ZERO (&ws); 1591 FD_ZERO (&es); 1592 if (CURLM_OK != (mret = curl_multi_fdset (curl_multi, 1593 &rs, 1594 &ws, 1595 &es, 1596 &max))) 1597 { 1598 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1599 "%s failed at %s:%d: `%s'\n", 1600 "curl_multi_fdset", __FILE__, __LINE__, 1601 curl_multi_strerror (mret)); 1602 return; 1603 } 1604 to = -1; 1605 GNUNET_break (CURLM_OK == 1606 curl_multi_timeout (curl_multi, 1607 &to)); 1608 if (-1 == to) 1609 rtime = GNUNET_TIME_UNIT_FOREVER_REL; 1610 else 1611 rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 1612 to); 1613 if (-1 != max) 1614 { 1615 grs = GNUNET_NETWORK_fdset_create (); 1616 gws = GNUNET_NETWORK_fdset_create (); 1617 GNUNET_NETWORK_fdset_copy_native (grs, 1618 &rs, 1619 max + 1); 1620 GNUNET_NETWORK_fdset_copy_native (gws, 1621 &ws, 1622 max + 1); 1623 curl_download_task = GNUNET_SCHEDULER_add_select ( 1624 GNUNET_SCHEDULER_PRIORITY_DEFAULT, 1625 rtime, 1626 grs, 1627 gws, 1628 &curl_task_download, 1629 curl_multi); 1630 GNUNET_NETWORK_fdset_destroy (gws); 1631 GNUNET_NETWORK_fdset_destroy (grs); 1632 } 1633 else 1634 { 1635 curl_download_task = GNUNET_SCHEDULER_add_delayed (rtime, 1636 &curl_task_download, 1637 curl_multi); 1638 } 1639 } 1640 1641 1642 /** 1643 * Task that is run when we are ready to receive more data from curl. 1644 * 1645 * @param cls closure, NULL 1646 */ 1647 static void 1648 curl_task_download (void *cls) 1649 { 1650 int running; 1651 int msgnum; 1652 struct CURLMsg *msg; 1653 CURLMcode mret; 1654 struct Socks5Request *s5r; 1655 1656 curl_download_task = NULL; 1657 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1658 "Running CURL interaction\n"); 1659 do 1660 { 1661 running = 0; 1662 mret = curl_multi_perform (curl_multi, 1663 &running); 1664 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1665 "Checking CURL multi status: %d\n", 1666 mret); 1667 while (NULL != (msg = curl_multi_info_read (curl_multi, 1668 &msgnum))) 1669 { 1670 GNUNET_break (CURLE_OK == 1671 curl_easy_getinfo (msg->easy_handle, 1672 CURLINFO_PRIVATE, 1673 (char **) &s5r)); 1674 if (NULL == s5r) 1675 { 1676 GNUNET_break (0); 1677 continue; 1678 } 1679 switch (msg->msg) 1680 { 1681 case CURLMSG_NONE: 1682 /* documentation says this is not used */ 1683 GNUNET_break (0); 1684 break; 1685 1686 case CURLMSG_DONE: 1687 switch (msg->data.result) 1688 { 1689 case CURLE_OK: 1690 case CURLE_GOT_NOTHING: 1691 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1692 "CURL download %s%s completed.\n", 1693 s5r->domain, 1694 s5r->url); 1695 if (NULL == s5r->response) 1696 { 1697 GNUNET_assert (GNUNET_OK == 1698 create_mhd_response_from_s5r (s5r)); 1699 } 1700 s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE; 1701 if (GNUNET_YES == s5r->suspended) 1702 { 1703 MHD_resume_connection (s5r->con); 1704 s5r->suspended = GNUNET_NO; 1705 } 1706 run_mhd_now (s5r->hd); 1707 break; 1708 1709 default: 1710 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1711 "Download curl %s%s failed: %s\n", 1712 s5r->domain, 1713 s5r->url, 1714 curl_easy_strerror (msg->data.result)); 1715 /* FIXME: indicate error somehow? close MHD connection badly as well? */ 1716 s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE; 1717 if (GNUNET_YES == s5r->suspended) 1718 { 1719 MHD_resume_connection (s5r->con); 1720 s5r->suspended = GNUNET_NO; 1721 } 1722 run_mhd_now (s5r->hd); 1723 break; 1724 } 1725 if (NULL == s5r->response) 1726 s5r->response = curl_failure_response; 1727 break; 1728 1729 case CURLMSG_LAST: 1730 /* documentation says this is not used */ 1731 GNUNET_break (0); 1732 break; 1733 1734 default: 1735 /* unexpected status code */ 1736 GNUNET_break (0); 1737 break; 1738 } 1739 } 1740 ; 1741 } 1742 while (mret == CURLM_CALL_MULTI_PERFORM); 1743 if (CURLM_OK != mret) 1744 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1745 "%s failed at %s:%d: `%s'\n", 1746 "curl_multi_perform", __FILE__, __LINE__, 1747 curl_multi_strerror (mret)); 1748 if (0 == running) 1749 { 1750 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1751 "Suspending cURL multi loop, no more events pending\n"); 1752 if (NULL != curl_download_task) 1753 { 1754 GNUNET_SCHEDULER_cancel (curl_download_task); 1755 curl_download_task = NULL; 1756 } 1757 return; /* nothing more in progress */ 1758 } 1759 curl_download_prepare (); 1760 } 1761 1762 1763 /* ********************************* MHD response generation ******************* */ 1764 1765 1766 /** 1767 * Read HTTP request header field from the request. Copies the fields 1768 * over to the 'headers' that will be given to curl. However, 'Host' 1769 * is substituted with the LEHO if present. We also change the 1770 * 'Connection' header value to "close" as the proxy does not support 1771 * pipelining. 1772 * 1773 * @param cls our `struct Socks5Request` 1774 * @param kind value kind 1775 * @param key field key 1776 * @param value field value 1777 * @return #MHD_YES to continue to iterate 1778 */ 1779 static int 1780 con_val_iter (void *cls, 1781 enum MHD_ValueKind kind, 1782 const char *key, 1783 const char *value) 1784 { 1785 struct Socks5Request *s5r = cls; 1786 char *hdr; 1787 1788 if ((0 == strcasecmp (MHD_HTTP_HEADER_HOST, 1789 key)) && 1790 (NULL != s5r->leho)) 1791 value = s5r->leho; 1792 GNUNET_asprintf (&hdr, 1793 "%s: %s", 1794 key, 1795 value); 1796 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1797 "Adding HEADER `%s' to HTTP request\n", 1798 hdr); 1799 s5r->headers = curl_slist_append (s5r->headers, 1800 hdr); 1801 GNUNET_free (hdr); 1802 return MHD_YES; 1803 } 1804 1805 1806 /** 1807 * Main MHD callback for handling requests. 1808 * 1809 * @param cls unused 1810 * @param con MHD connection handle 1811 * @param url the url in the request 1812 * @param meth the HTTP method used ("GET", "PUT", etc.) 1813 * @param ver the HTTP version string ("HTTP/1.1" for version 1.1, etc.) 1814 * @param upload_data the data being uploaded (excluding HEADERS, 1815 * for a POST that fits into memory and that is encoded 1816 * with a supported encoding, the POST data will NOT be 1817 * given in upload_data and is instead available as 1818 * part of MHD_get_connection_values; very large POST 1819 * data *will* be made available incrementally in 1820 * upload_data) 1821 * @param upload_data_size set initially to the size of the 1822 * @a upload_data provided; the method must update this 1823 * value to the number of bytes NOT processed; 1824 * @param con_cls pointer to location where we store the `struct Request` 1825 * @return #MHD_YES if the connection was handled successfully, 1826 * #MHD_NO if the socket must be closed due to a serious 1827 * error while handling the request 1828 */ 1829 static MHD_RESULT 1830 create_response (void *cls, 1831 struct MHD_Connection *con, 1832 const char *url, 1833 const char *meth, 1834 const char *ver, 1835 const char *upload_data, 1836 size_t *upload_data_size, 1837 void **con_cls) 1838 { 1839 struct Socks5Request *s5r = *con_cls; 1840 char *curlurl; 1841 char ipstring[INET6_ADDRSTRLEN]; 1842 char ipaddr[INET6_ADDRSTRLEN + 2]; 1843 char *curl_hosts; 1844 const struct sockaddr *sa; 1845 const struct sockaddr_in *s4; 1846 const struct sockaddr_in6 *s6; 1847 uint16_t port_tmp; 1848 size_t left; 1849 1850 if (NULL == s5r) 1851 { 1852 GNUNET_break (0); 1853 return MHD_NO; 1854 } 1855 s5r->con = con; 1856 /* Fresh connection. */ 1857 if (SOCKS5_SOCKET_WITH_MHD == s5r->state) 1858 { 1859 /* first time here, initialize curl handle */ 1860 if (s5r->is_gns) 1861 { 1862 sa = (const struct sockaddr *) &s5r->destination_address; 1863 switch (sa->sa_family) 1864 { 1865 case AF_INET: 1866 s4 = (const struct sockaddr_in *) &s5r->destination_address; 1867 if (NULL == inet_ntop (AF_INET, 1868 &s4->sin_addr, 1869 ipstring, 1870 sizeof(ipstring))) 1871 { 1872 GNUNET_break (0); 1873 return MHD_NO; 1874 } 1875 GNUNET_snprintf (ipaddr, 1876 sizeof(ipaddr), 1877 "%s", 1878 ipstring); 1879 port_tmp = ntohs (s4->sin_port); 1880 break; 1881 1882 case AF_INET6: 1883 s6 = (const struct sockaddr_in6 *) &s5r->destination_address; 1884 if (NULL == inet_ntop (AF_INET6, 1885 &s6->sin6_addr, 1886 ipstring, 1887 sizeof(ipstring))) 1888 { 1889 GNUNET_break (0); 1890 return MHD_NO; 1891 } 1892 GNUNET_snprintf (ipaddr, 1893 sizeof(ipaddr), 1894 "%s", 1895 ipstring); 1896 port_tmp = ntohs (s6->sin6_port); 1897 break; 1898 1899 default: 1900 GNUNET_break (0); 1901 return MHD_NO; 1902 } 1903 GNUNET_asprintf (&curl_hosts, 1904 "%s:%d:%s", 1905 s5r->leho, 1906 port_tmp, 1907 ipaddr); 1908 s5r->hosts = curl_slist_append (NULL, 1909 curl_hosts); 1910 GNUNET_free (curl_hosts); 1911 } 1912 else 1913 { 1914 port_tmp = s5r->port; 1915 } 1916 if (NULL == s5r->curl) 1917 s5r->curl = curl_easy_init (); 1918 if (NULL == s5r->curl) 1919 return MHD_queue_response (con, 1920 MHD_HTTP_INTERNAL_SERVER_ERROR, 1921 curl_failure_response); 1922 curl_easy_setopt (s5r->curl, 1923 CURLOPT_HEADERFUNCTION, 1924 &curl_check_hdr); 1925 curl_easy_setopt (s5r->curl, 1926 CURLOPT_HEADERDATA, 1927 s5r); 1928 curl_easy_setopt (s5r->curl, 1929 CURLOPT_FOLLOWLOCATION, 1930 0); 1931 if (s5r->is_gns) 1932 curl_easy_setopt (s5r->curl, 1933 CURLOPT_IPRESOLVE, 1934 CURL_IPRESOLVE_V4); 1935 curl_easy_setopt (s5r->curl, 1936 CURLOPT_CONNECTTIMEOUT, 1937 600L); 1938 curl_easy_setopt (s5r->curl, 1939 CURLOPT_TIMEOUT, 1940 600L); 1941 curl_easy_setopt (s5r->curl, 1942 CURLOPT_NOSIGNAL, 1943 1L); 1944 curl_easy_setopt (s5r->curl, 1945 CURLOPT_HTTP_CONTENT_DECODING, 1946 0); 1947 curl_easy_setopt (s5r->curl, 1948 CURLOPT_NOSIGNAL, 1949 1L); 1950 curl_easy_setopt (s5r->curl, 1951 CURLOPT_PRIVATE, 1952 s5r); 1953 curl_easy_setopt (s5r->curl, 1954 CURLOPT_VERBOSE, 1955 0L); 1956 /** 1957 * Pre-populate cache to resolve Hostname. 1958 * This is necessary as the DNS name in the CURLOPT_URL is used 1959 * for SNI http://de.wikipedia.org/wiki/Server_Name_Indication 1960 */ 1961 if ((NULL != s5r->leho) && 1962 (NULL != s5r->hosts)) 1963 { 1964 curl_easy_setopt (s5r->curl, 1965 CURLOPT_RESOLVE, 1966 s5r->hosts); 1967 } 1968 if (s5r->is_gns) 1969 { 1970 GNUNET_asprintf (&curlurl, 1971 (GNUNET_YES != s5r->is_tls) // (HTTPS_PORT != s5r->port) 1972 ? "http://%s:%d%s" 1973 : "https://%s:%d%s", 1974 (NULL != s5r->leho) 1975 ? s5r->leho 1976 : ipaddr, 1977 port_tmp, 1978 s5r->url); 1979 } 1980 else 1981 { 1982 GNUNET_asprintf (&curlurl, 1983 (GNUNET_YES != s5r->is_tls) // (HTTPS_PORT != s5r->port) 1984 ? "http://%s:%d%s" 1985 : "https://%s:%d%s", 1986 s5r->domain, 1987 port_tmp, 1988 s5r->url); 1989 } 1990 curl_easy_setopt (s5r->curl, 1991 CURLOPT_URL, 1992 curlurl); 1993 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1994 "Launching %s CURL interaction, fetching `%s'\n", 1995 (s5r->is_gns) ? "GNS" : "DNS", 1996 curlurl); 1997 GNUNET_free (curlurl); 1998 if (0 == strcasecmp (meth, 1999 MHD_HTTP_METHOD_PUT)) 2000 { 2001 s5r->state = SOCKS5_SOCKET_UPLOAD_STARTED; 2002 curl_easy_setopt (s5r->curl, 2003 CURLOPT_UPLOAD, 2004 1L); 2005 curl_easy_setopt (s5r->curl, 2006 CURLOPT_WRITEFUNCTION, 2007 &curl_download_cb); 2008 curl_easy_setopt (s5r->curl, 2009 CURLOPT_WRITEDATA, 2010 s5r); 2011 GNUNET_assert (CURLE_OK == 2012 curl_easy_setopt (s5r->curl, 2013 CURLOPT_READFUNCTION, 2014 &curl_upload_cb)); 2015 curl_easy_setopt (s5r->curl, 2016 CURLOPT_READDATA, 2017 s5r); 2018 { 2019 const char *us; 2020 long upload_size = 0; 2021 2022 us = MHD_lookup_connection_value (con, 2023 MHD_HEADER_KIND, 2024 MHD_HTTP_HEADER_CONTENT_LENGTH); 2025 if ((1 == sscanf (us, 2026 "%ld", 2027 &upload_size)) && 2028 (upload_size >= 0)) 2029 { 2030 curl_easy_setopt (s5r->curl, 2031 CURLOPT_INFILESIZE, 2032 upload_size); 2033 } 2034 } 2035 } 2036 else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST)) 2037 { 2038 s5r->state = SOCKS5_SOCKET_UPLOAD_STARTED; 2039 curl_easy_setopt (s5r->curl, 2040 CURLOPT_POST, 2041 1L); 2042 curl_easy_setopt (s5r->curl, 2043 CURLOPT_WRITEFUNCTION, 2044 &curl_download_cb); 2045 curl_easy_setopt (s5r->curl, 2046 CURLOPT_WRITEDATA, 2047 s5r); 2048 curl_easy_setopt (s5r->curl, 2049 CURLOPT_READFUNCTION, 2050 &curl_upload_cb); 2051 curl_easy_setopt (s5r->curl, 2052 CURLOPT_READDATA, 2053 s5r); 2054 { 2055 const char *us; 2056 long upload_size; 2057 2058 upload_size = 0; 2059 us = MHD_lookup_connection_value (con, 2060 MHD_HEADER_KIND, 2061 MHD_HTTP_HEADER_CONTENT_LENGTH); 2062 if ((NULL != us) && 2063 (1 == sscanf (us, 2064 "%ld", 2065 &upload_size)) && 2066 (upload_size >= 0)) 2067 { 2068 curl_easy_setopt (s5r->curl, 2069 CURLOPT_INFILESIZE, 2070 upload_size); 2071 } 2072 else 2073 { 2074 curl_easy_setopt (s5r->curl, 2075 CURLOPT_INFILESIZE, 2076 upload_size); 2077 } 2078 } 2079 } 2080 else if (0 == strcasecmp (meth, 2081 MHD_HTTP_METHOD_HEAD)) 2082 { 2083 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED; 2084 curl_easy_setopt (s5r->curl, 2085 CURLOPT_NOBODY, 2086 1L); 2087 } 2088 else if (0 == strcasecmp (meth, 2089 MHD_HTTP_METHOD_OPTIONS)) 2090 { 2091 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED; 2092 curl_easy_setopt (s5r->curl, 2093 CURLOPT_CUSTOMREQUEST, 2094 "OPTIONS"); 2095 curl_easy_setopt (s5r->curl, 2096 CURLOPT_WRITEFUNCTION, 2097 &curl_download_cb); 2098 curl_easy_setopt (s5r->curl, 2099 CURLOPT_WRITEDATA, 2100 s5r); 2101 } 2102 else if (0 == strcasecmp (meth, 2103 MHD_HTTP_METHOD_GET)) 2104 { 2105 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED; 2106 curl_easy_setopt (s5r->curl, 2107 CURLOPT_HTTPGET, 2108 1L); 2109 curl_easy_setopt (s5r->curl, 2110 CURLOPT_WRITEFUNCTION, 2111 &curl_download_cb); 2112 curl_easy_setopt (s5r->curl, 2113 CURLOPT_WRITEDATA, 2114 s5r); 2115 } 2116 else if (0 == strcasecmp (meth, 2117 MHD_HTTP_METHOD_DELETE)) 2118 { 2119 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED; 2120 curl_easy_setopt (s5r->curl, 2121 CURLOPT_CUSTOMREQUEST, 2122 "DELETE"); 2123 curl_easy_setopt (s5r->curl, 2124 CURLOPT_WRITEFUNCTION, 2125 &curl_download_cb); 2126 curl_easy_setopt (s5r->curl, 2127 CURLOPT_WRITEDATA, 2128 s5r); 2129 } 2130 else 2131 { 2132 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2133 _ ("Unsupported HTTP method `%s'\n"), 2134 meth); 2135 curl_easy_cleanup (s5r->curl); 2136 s5r->curl = NULL; 2137 return MHD_NO; 2138 } 2139 2140 if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_0)) 2141 { 2142 curl_easy_setopt (s5r->curl, 2143 CURLOPT_HTTP_VERSION, 2144 CURL_HTTP_VERSION_1_0); 2145 } 2146 else if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_1)) 2147 { 2148 curl_easy_setopt (s5r->curl, 2149 CURLOPT_HTTP_VERSION, 2150 CURL_HTTP_VERSION_1_1); 2151 } 2152 else 2153 { 2154 curl_easy_setopt (s5r->curl, 2155 CURLOPT_HTTP_VERSION, 2156 CURL_HTTP_VERSION_NONE); 2157 } 2158 2159 if (GNUNET_YES == s5r->is_tls) // (HTTPS_PORT == s5r->port) 2160 { 2161 curl_easy_setopt (s5r->curl, 2162 CURLOPT_USE_SSL, 2163 CURLUSESSL_ALL); 2164 if (0 < s5r->num_danes) 2165 curl_easy_setopt (s5r->curl, 2166 CURLOPT_SSL_VERIFYPEER, 2167 0L); 2168 else 2169 curl_easy_setopt (s5r->curl, 2170 CURLOPT_SSL_VERIFYPEER, 2171 1L); 2172 /* Disable cURL checking the hostname, as we will check ourselves 2173 as only we have the domain name or the LEHO or the DANE record */ 2174 curl_easy_setopt (s5r->curl, 2175 CURLOPT_SSL_VERIFYHOST, 2176 0L); 2177 } 2178 else 2179 { 2180 curl_easy_setopt (s5r->curl, 2181 CURLOPT_USE_SSL, 2182 CURLUSESSL_NONE); 2183 } 2184 2185 if (CURLM_OK != 2186 curl_multi_add_handle (curl_multi, 2187 s5r->curl)) 2188 { 2189 GNUNET_break (0); 2190 curl_easy_cleanup (s5r->curl); 2191 s5r->curl = NULL; 2192 return MHD_NO; 2193 } 2194 MHD_get_connection_values (con, 2195 MHD_HEADER_KIND, 2196 (MHD_KeyValueIterator) & con_val_iter, 2197 s5r); 2198 curl_easy_setopt (s5r->curl, 2199 CURLOPT_HTTPHEADER, 2200 s5r->headers); 2201 curl_download_prepare (); 2202 return MHD_YES; 2203 } 2204 2205 /* continuing to process request */ 2206 if (0 != *upload_data_size) 2207 { 2208 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2209 "Processing %u bytes UPLOAD\n", 2210 (unsigned int) *upload_data_size); 2211 2212 /* FIXME: This must be set or a header with Transfer-Encoding: chunked. Else 2213 * upload callback is not called! 2214 */ 2215 curl_easy_setopt (s5r->curl, 2216 CURLOPT_POSTFIELDSIZE, 2217 *upload_data_size); 2218 2219 left = GNUNET_MIN (*upload_data_size, 2220 sizeof(s5r->io_buf) - s5r->io_len); 2221 GNUNET_memcpy (&s5r->io_buf[s5r->io_len], 2222 upload_data, 2223 left); 2224 s5r->io_len += left; 2225 *upload_data_size -= left; 2226 GNUNET_assert (NULL != s5r->curl); 2227 if (GNUNET_YES == s5r->curl_paused) 2228 { 2229 s5r->curl_paused = GNUNET_NO; 2230 curl_easy_pause (s5r->curl, 2231 CURLPAUSE_CONT); 2232 } 2233 return MHD_YES; 2234 } 2235 if (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) 2236 { 2237 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2238 "Finished processing UPLOAD\n"); 2239 s5r->state = SOCKS5_SOCKET_UPLOAD_DONE; 2240 } 2241 if (NULL == s5r->response) 2242 { 2243 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2244 "Waiting for HTTP response for %s%s...\n", 2245 s5r->domain, 2246 s5r->url); 2247 MHD_suspend_connection (con); 2248 s5r->suspended = GNUNET_YES; 2249 return MHD_YES; 2250 } 2251 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2252 "Queueing response for %s%s with MHD\n", 2253 s5r->domain, 2254 s5r->url); 2255 run_mhd_now (s5r->hd); 2256 return MHD_queue_response (con, 2257 s5r->response_code, 2258 s5r->response); 2259 } 2260 2261 2262 /* ******************** MHD HTTP setup and event loop ******************** */ 2263 2264 2265 /** 2266 * Function called when MHD decides that we are done with a request. 2267 * 2268 * @param cls NULL 2269 * @param connection connection handle 2270 * @param con_cls value as set by the last call to 2271 * the MHD_AccessHandlerCallback, should be our `struct Socks5Request *` 2272 * @param toe reason for request termination (ignored) 2273 */ 2274 static void 2275 mhd_completed_cb (void *cls, 2276 struct MHD_Connection *connection, 2277 void **con_cls, 2278 enum MHD_RequestTerminationCode toe) 2279 { 2280 struct Socks5Request *s5r = *con_cls; 2281 2282 if (NULL == s5r) 2283 return; 2284 if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe) 2285 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2286 "MHD encountered error handling request: %d\n", 2287 toe); 2288 if (NULL != s5r->curl) 2289 { 2290 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2291 "Removing cURL handle (MHD interaction complete)\n"); 2292 curl_multi_remove_handle (curl_multi, 2293 s5r->curl); 2294 curl_slist_free_all (s5r->headers); 2295 s5r->headers = NULL; 2296 curl_easy_reset (s5r->curl); 2297 s5r->rbuf_len = 0; 2298 s5r->wbuf_len = 0; 2299 s5r->io_len = 0; 2300 curl_download_prepare (); 2301 } 2302 if ((NULL != s5r->response) && 2303 (curl_failure_response != s5r->response)) 2304 MHD_destroy_response (s5r->response); 2305 for (struct HttpResponseHeader *header = s5r->header_head; 2306 NULL != header; 2307 header = s5r->header_head) 2308 { 2309 GNUNET_CONTAINER_DLL_remove (s5r->header_head, 2310 s5r->header_tail, 2311 header); 2312 GNUNET_free (header->type); 2313 GNUNET_free (header->value); 2314 GNUNET_free (header); 2315 } 2316 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2317 "Finished request for %s\n", 2318 s5r->url); 2319 GNUNET_free (s5r->url); 2320 s5r->state = SOCKS5_SOCKET_WITH_MHD; 2321 s5r->url = NULL; 2322 s5r->response = NULL; 2323 *con_cls = NULL; 2324 } 2325 2326 2327 /** 2328 * Function called when MHD connection is opened or closed. 2329 * 2330 * @param cls NULL 2331 * @param connection connection handle 2332 * @param con_cls value as set by the last call to 2333 * the MHD_AccessHandlerCallback, should be our `struct Socks5Request *` 2334 * @param toe connection notification type 2335 */ 2336 static void 2337 mhd_connection_cb (void *cls, 2338 struct MHD_Connection *connection, 2339 void **con_cls, 2340 enum MHD_ConnectionNotificationCode cnc) 2341 { 2342 struct Socks5Request *s5r; 2343 const union MHD_ConnectionInfo *ci; 2344 int sock; 2345 2346 switch (cnc) 2347 { 2348 case MHD_CONNECTION_NOTIFY_STARTED: 2349 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection started...\n"); 2350 ci = MHD_get_connection_info (connection, 2351 MHD_CONNECTION_INFO_CONNECTION_FD); 2352 if (NULL == ci) 2353 { 2354 GNUNET_break (0); 2355 return; 2356 } 2357 sock = ci->connect_fd; 2358 for (s5r = s5r_head; NULL != s5r; s5r = s5r->next) 2359 { 2360 if (GNUNET_NETWORK_get_fd (s5r->sock) == sock) 2361 { 2362 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2363 "Context set...\n"); 2364 s5r->ssl_checked = GNUNET_NO; 2365 *con_cls = s5r; 2366 break; 2367 } 2368 } 2369 break; 2370 2371 case MHD_CONNECTION_NOTIFY_CLOSED: 2372 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2373 "Connection closed... cleaning up\n"); 2374 s5r = *con_cls; 2375 if (NULL == s5r) 2376 { 2377 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2378 "Connection stale!\n"); 2379 return; 2380 } 2381 cleanup_s5r (s5r); 2382 curl_download_prepare (); 2383 *con_cls = NULL; 2384 break; 2385 2386 default: 2387 GNUNET_break (0); 2388 } 2389 } 2390 2391 2392 /** 2393 * Function called when MHD first processes an incoming connection. 2394 * Gives us the respective URI information. 2395 * 2396 * We use this to associate the `struct MHD_Connection` with our 2397 * internal `struct Socks5Request` data structure (by checking 2398 * for matching sockets). 2399 * 2400 * @param cls the HTTP server handle (a `struct MhdHttpList`) 2401 * @param url the URL that is being requested 2402 * @param connection MHD connection object for the request 2403 * @return the `struct Socks5Request` that this @a connection is for 2404 */ 2405 static void * 2406 mhd_log_callback (void *cls, 2407 const char *url, 2408 struct MHD_Connection *connection) 2409 { 2410 struct Socks5Request *s5r; 2411 const union MHD_ConnectionInfo *ci; 2412 2413 ci = MHD_get_connection_info (connection, 2414 MHD_CONNECTION_INFO_SOCKET_CONTEXT); 2415 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Processing %s\n", url); 2416 if (NULL == ci) 2417 { 2418 GNUNET_break (0); 2419 return NULL; 2420 } 2421 s5r = ci->socket_context; 2422 if (NULL != s5r->url) 2423 { 2424 GNUNET_break (0); 2425 return NULL; 2426 } 2427 s5r->url = GNUNET_strdup (url); 2428 if (NULL != s5r->timeout_task) 2429 { 2430 GNUNET_SCHEDULER_cancel (s5r->timeout_task); 2431 s5r->timeout_task = NULL; 2432 } 2433 GNUNET_assert (s5r->state == SOCKS5_SOCKET_WITH_MHD); 2434 return s5r; 2435 } 2436 2437 2438 /** 2439 * Kill the given MHD daemon. 2440 * 2441 * @param hd daemon to stop 2442 */ 2443 static void 2444 kill_httpd (struct MhdHttpList *hd) 2445 { 2446 GNUNET_CONTAINER_DLL_remove (mhd_httpd_head, 2447 mhd_httpd_tail, 2448 hd); 2449 GNUNET_free (hd->domain); 2450 MHD_stop_daemon (hd->daemon); 2451 if (NULL != hd->httpd_task) 2452 { 2453 GNUNET_SCHEDULER_cancel (hd->httpd_task); 2454 hd->httpd_task = NULL; 2455 } 2456 GNUNET_free (hd->proxy_cert); 2457 if (hd == httpd) 2458 httpd = NULL; 2459 GNUNET_free (hd); 2460 } 2461 2462 2463 /** 2464 * Task run whenever HTTP server is idle for too long. Kill it. 2465 * 2466 * @param cls the `struct MhdHttpList *` 2467 */ 2468 static void 2469 kill_httpd_task (void *cls) 2470 { 2471 struct MhdHttpList *hd = cls; 2472 2473 hd->httpd_task = NULL; 2474 kill_httpd (hd); 2475 } 2476 2477 2478 /** 2479 * Task run whenever HTTP server operations are pending. 2480 * 2481 * @param cls the `struct MhdHttpList *` of the daemon that is being run 2482 */ 2483 static void 2484 do_httpd (void *cls); 2485 2486 2487 /** 2488 * Schedule MHD. This function should be called initially when an 2489 * MHD is first getting its client socket, and will then automatically 2490 * always be called later whenever there is work to be done. 2491 * 2492 * @param hd the daemon to schedule 2493 */ 2494 static void 2495 schedule_httpd (struct MhdHttpList *hd) 2496 { 2497 fd_set rs; 2498 fd_set ws; 2499 fd_set es; 2500 struct GNUNET_NETWORK_FDSet *wrs; 2501 struct GNUNET_NETWORK_FDSet *wws; 2502 int max; 2503 int haveto; 2504 MHD_UNSIGNED_LONG_LONG timeout; 2505 struct GNUNET_TIME_Relative tv; 2506 2507 FD_ZERO (&rs); 2508 FD_ZERO (&ws); 2509 FD_ZERO (&es); 2510 max = -1; 2511 if (MHD_YES != 2512 MHD_get_fdset (hd->daemon, 2513 &rs, 2514 &ws, 2515 &es, 2516 &max)) 2517 { 2518 kill_httpd (hd); 2519 return; 2520 } 2521 haveto = MHD_get_timeout (hd->daemon, 2522 &timeout); 2523 if (MHD_YES == haveto) 2524 tv.rel_value_us = (uint64_t) timeout * 1000LL; 2525 else 2526 tv = GNUNET_TIME_UNIT_FOREVER_REL; 2527 if (-1 != max) 2528 { 2529 wrs = GNUNET_NETWORK_fdset_create (); 2530 wws = GNUNET_NETWORK_fdset_create (); 2531 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1); 2532 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1); 2533 } 2534 else 2535 { 2536 wrs = NULL; 2537 wws = NULL; 2538 } 2539 if (NULL != hd->httpd_task) 2540 { 2541 GNUNET_SCHEDULER_cancel (hd->httpd_task); 2542 hd->httpd_task = NULL; 2543 } 2544 if ((MHD_YES != haveto) && 2545 (-1 == max) && 2546 (hd != httpd)) 2547 { 2548 /* daemon is idle, kill after timeout */ 2549 hd->httpd_task = GNUNET_SCHEDULER_add_delayed (MHD_CACHE_TIMEOUT, 2550 &kill_httpd_task, 2551 hd); 2552 } 2553 else 2554 { 2555 hd->httpd_task = 2556 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, 2557 tv, wrs, wws, 2558 &do_httpd, hd); 2559 } 2560 if (NULL != wrs) 2561 GNUNET_NETWORK_fdset_destroy (wrs); 2562 if (NULL != wws) 2563 GNUNET_NETWORK_fdset_destroy (wws); 2564 } 2565 2566 2567 static void 2568 do_httpd (void *cls) 2569 { 2570 struct MhdHttpList *hd = cls; 2571 2572 hd->httpd_task = NULL; 2573 MHD_run (hd->daemon); 2574 schedule_httpd (hd); 2575 } 2576 2577 2578 /** 2579 * Run MHD now, we have extra data ready for the callback. 2580 * 2581 * @param hd the daemon to run now. 2582 */ 2583 static void 2584 run_mhd_now (struct MhdHttpList *hd) 2585 { 2586 if (NULL != hd->httpd_task) 2587 GNUNET_SCHEDULER_cancel (hd->httpd_task); 2588 hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, 2589 hd); 2590 } 2591 2592 2593 /** 2594 * Read file in filename 2595 * 2596 * @param filename file to read 2597 * @param size pointer where filesize is stored 2598 * @return NULL on error 2599 */ 2600 static void* 2601 load_file (const char*filename, 2602 unsigned int*size) 2603 { 2604 void *buffer; 2605 uint64_t fsize; 2606 2607 if (GNUNET_OK != 2608 GNUNET_DISK_file_size (filename, 2609 &fsize, 2610 GNUNET_YES, 2611 GNUNET_YES)) 2612 return NULL; 2613 if (fsize > MAX_PEM_SIZE) 2614 return NULL; 2615 *size = (unsigned int) fsize; 2616 buffer = GNUNET_malloc (*size); 2617 if (fsize != 2618 GNUNET_DISK_fn_read (filename, 2619 buffer, 2620 (size_t) fsize)) 2621 { 2622 GNUNET_free (buffer); 2623 return NULL; 2624 } 2625 return buffer; 2626 } 2627 2628 2629 /** 2630 * Load PEM key from file 2631 * 2632 * @param key where to store the data 2633 * @param keyfile path to the PEM file 2634 * @return #GNUNET_OK on success 2635 */ 2636 static int 2637 load_key_from_file (gnutls_x509_privkey_t key, 2638 const char*keyfile) 2639 { 2640 gnutls_datum_t key_data; 2641 int ret; 2642 2643 key_data.data = load_file (keyfile, 2644 &key_data.size); 2645 if (NULL == key_data.data) 2646 return GNUNET_SYSERR; 2647 ret = gnutls_x509_privkey_import (key, &key_data, 2648 GNUTLS_X509_FMT_PEM); 2649 if (GNUTLS_E_SUCCESS != ret) 2650 { 2651 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2652 _ ("Unable to import private key from file `%s'\n"), 2653 keyfile); 2654 } 2655 GNUNET_free (key_data.data); 2656 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK; 2657 } 2658 2659 2660 /** 2661 * Load cert from file 2662 * 2663 * @param crt struct to store data in 2664 * @param certfile path to pem file 2665 * @return #GNUNET_OK on success 2666 */ 2667 static int 2668 load_cert_from_file (gnutls_x509_crt_t crt, 2669 const char*certfile) 2670 { 2671 gnutls_datum_t cert_data; 2672 int ret; 2673 2674 cert_data.data = load_file (certfile, 2675 &cert_data.size); 2676 if (NULL == cert_data.data) 2677 return GNUNET_SYSERR; 2678 ret = gnutls_x509_crt_import (crt, 2679 &cert_data, 2680 GNUTLS_X509_FMT_PEM); 2681 if (GNUTLS_E_SUCCESS != ret) 2682 { 2683 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2684 _ ("Unable to import certificate from `%s'\n"), 2685 certfile); 2686 } 2687 GNUNET_free (cert_data.data); 2688 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK; 2689 } 2690 2691 2692 /** 2693 * Generate new certificate for specific name 2694 * 2695 * @param name the subject name to generate a cert for 2696 * @return a struct holding the PEM data, NULL on error 2697 */ 2698 static struct ProxyGNSCertificate * 2699 generate_gns_certificate (const char *name) 2700 { 2701 unsigned int serial; 2702 size_t key_buf_size; 2703 size_t cert_buf_size; 2704 gnutls_x509_crt_t request; 2705 time_t etime; 2706 struct tm *tm_data; 2707 struct ProxyGNSCertificate *pgc; 2708 2709 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2710 "Generating x.509 certificate for `%s'\n", 2711 name); 2712 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_init (&request)); 2713 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, 2714 proxy_ca.key)); 2715 pgc = GNUNET_new (struct ProxyGNSCertificate); 2716 gnutls_x509_crt_set_dn_by_oid (request, 2717 GNUTLS_OID_X520_COUNTRY_NAME, 2718 0, 2719 "ZZ", 2720 strlen ("ZZ")); 2721 gnutls_x509_crt_set_dn_by_oid (request, 2722 GNUTLS_OID_X520_ORGANIZATION_NAME, 2723 0, 2724 "GNU Name System", 2725 strlen ("GNU Name System")); 2726 gnutls_x509_crt_set_dn_by_oid (request, 2727 GNUTLS_OID_X520_COMMON_NAME, 2728 0, 2729 name, 2730 strlen (name)); 2731 gnutls_x509_crt_set_subject_alternative_name (request, 2732 GNUTLS_SAN_DNSNAME, 2733 name); 2734 GNUNET_break (GNUTLS_E_SUCCESS == 2735 gnutls_x509_crt_set_version (request, 2736 3)); 2737 gnutls_rnd (GNUTLS_RND_NONCE, 2738 &serial, 2739 sizeof(serial)); 2740 gnutls_x509_crt_set_serial (request, 2741 &serial, 2742 sizeof(serial)); 2743 etime = time (NULL); 2744 tm_data = localtime (&etime); 2745 tm_data->tm_hour--; 2746 etime = mktime (tm_data); 2747 gnutls_x509_crt_set_activation_time (request, 2748 etime); 2749 tm_data->tm_year++; 2750 etime = mktime (tm_data); 2751 gnutls_x509_crt_set_expiration_time (request, 2752 etime); 2753 gnutls_x509_crt_sign2 (request, 2754 proxy_ca.cert, 2755 proxy_ca.key, 2756 GNUTLS_DIG_SHA512, 2757 0); 2758 key_buf_size = sizeof(pgc->key); 2759 cert_buf_size = sizeof(pgc->cert); 2760 gnutls_x509_crt_export (request, 2761 GNUTLS_X509_FMT_PEM, 2762 pgc->cert, 2763 &cert_buf_size); 2764 gnutls_x509_privkey_export (proxy_ca.key, 2765 GNUTLS_X509_FMT_PEM, 2766 pgc->key, 2767 &key_buf_size); 2768 gnutls_x509_crt_deinit (request); 2769 return pgc; 2770 } 2771 2772 2773 /** 2774 * Function called by MHD with errors, suppresses them all. 2775 * 2776 * @param cls closure 2777 * @param fm format string (`printf()`-style) 2778 * @param ap arguments to @a fm 2779 */ 2780 static void 2781 mhd_error_log_callback (void *cls, 2782 const char *fm, 2783 va_list ap) 2784 { 2785 /* do nothing */ 2786 } 2787 2788 2789 /** 2790 * Lookup (or create) an TLS MHD instance for a particular domain. 2791 * 2792 * @param domain the domain the TLS daemon has to serve 2793 * @return NULL on error 2794 */ 2795 static struct MhdHttpList * 2796 lookup_ssl_httpd (const char*domain) 2797 { 2798 struct MhdHttpList *hd; 2799 struct ProxyGNSCertificate *pgc; 2800 2801 if (NULL == domain) 2802 { 2803 GNUNET_break (0); 2804 return NULL; 2805 } 2806 for (hd = mhd_httpd_head; NULL != hd; hd = hd->next) 2807 if ((NULL != hd->domain) && 2808 (0 == strcmp (hd->domain, domain))) 2809 return hd; 2810 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2811 "Starting fresh MHD HTTPS instance for domain `%s'\n", 2812 domain); 2813 pgc = generate_gns_certificate (domain); 2814 hd = GNUNET_new (struct MhdHttpList); 2815 hd->is_ssl = GNUNET_YES; 2816 hd->domain = GNUNET_strdup (domain); 2817 hd->proxy_cert = pgc; 2818 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL 2819 | MHD_USE_NO_LISTEN_SOCKET 2820 | MHD_ALLOW_SUSPEND_RESUME, 2821 0, 2822 NULL, NULL, 2823 &create_response, hd, 2824 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned 2825 int) 16, 2826 MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, 2827 NULL, 2828 MHD_OPTION_NOTIFY_CONNECTION, 2829 &mhd_connection_cb, NULL, 2830 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, 2831 NULL, 2832 MHD_OPTION_EXTERNAL_LOGGER, 2833 &mhd_error_log_callback, NULL, 2834 MHD_OPTION_HTTPS_MEM_KEY, pgc->key, 2835 MHD_OPTION_HTTPS_MEM_CERT, pgc->cert, 2836 MHD_OPTION_END); 2837 if (NULL == hd->daemon) 2838 { 2839 GNUNET_free (pgc); 2840 GNUNET_free (hd); 2841 return NULL; 2842 } 2843 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, 2844 mhd_httpd_tail, 2845 hd); 2846 return hd; 2847 } 2848 2849 2850 /** 2851 * Task run when a Socks5Request somehow fails to be associated with 2852 * an MHD connection (e.g. because the client never speaks HTTP after 2853 * the SOCKS5 handshake). Clean up. 2854 * 2855 * @param cls the `struct Socks5Request *` 2856 */ 2857 static void 2858 timeout_s5r_handshake (void *cls) 2859 { 2860 struct Socks5Request *s5r = cls; 2861 2862 s5r->timeout_task = NULL; 2863 cleanup_s5r (s5r); 2864 } 2865 2866 2867 /** 2868 * We're done with the Socks5 protocol, now we need to pass the 2869 * connection data through to the final destination, either 2870 * direct (if the protocol might not be HTTP), or via MHD 2871 * (if the port looks like it should be HTTP). 2872 * 2873 * @param s5r socks request that has reached the final stage 2874 */ 2875 static void 2876 setup_data_transfer (struct Socks5Request *s5r) 2877 { 2878 struct MhdHttpList *hd; 2879 int fd; 2880 const struct sockaddr *addr; 2881 socklen_t len; 2882 char *domain; 2883 2884 if (GNUNET_YES == s5r->is_tls) 2885 { 2886 GNUNET_asprintf (&domain, 2887 "%s", 2888 s5r->domain); 2889 hd = lookup_ssl_httpd (domain); 2890 if (NULL == hd) 2891 { 2892 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2893 _ ("Failed to start HTTPS server for `%s'\n"), 2894 s5r->domain); 2895 cleanup_s5r (s5r); 2896 GNUNET_free (domain); 2897 return; 2898 } 2899 } 2900 else 2901 { 2902 domain = NULL; 2903 GNUNET_assert (NULL != httpd); 2904 hd = httpd; 2905 } 2906 fd = GNUNET_NETWORK_get_fd (s5r->sock); 2907 addr = GNUNET_NETWORK_get_addr (s5r->sock); 2908 len = GNUNET_NETWORK_get_addrlen (s5r->sock); 2909 s5r->state = SOCKS5_SOCKET_WITH_MHD; 2910 if (MHD_YES != 2911 MHD_add_connection (hd->daemon, 2912 fd, 2913 addr, 2914 len)) 2915 { 2916 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2917 _ ("Failed to pass client to MHD\n")); 2918 cleanup_s5r (s5r); 2919 GNUNET_free (domain); 2920 return; 2921 } 2922 s5r->hd = hd; 2923 schedule_httpd (hd); 2924 s5r->timeout_task = GNUNET_SCHEDULER_add_delayed (HTTP_HANDSHAKE_TIMEOUT, 2925 &timeout_s5r_handshake, 2926 s5r); 2927 GNUNET_free (domain); 2928 } 2929 2930 2931 /* ********************* SOCKS handling ************************* */ 2932 2933 2934 /** 2935 * Write data from buffer to socks5 client, then continue with state machine. 2936 * 2937 * @param cls the closure with the `struct Socks5Request` 2938 */ 2939 static void 2940 do_write (void *cls) 2941 { 2942 struct Socks5Request *s5r = cls; 2943 ssize_t len; 2944 2945 s5r->wtask = NULL; 2946 len = GNUNET_NETWORK_socket_send (s5r->sock, 2947 s5r->wbuf, 2948 s5r->wbuf_len); 2949 if (len <= 0) 2950 { 2951 /* write error: connection closed, shutdown, etc.; just clean up */ 2952 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2953 "Write Error\n"); 2954 cleanup_s5r (s5r); 2955 return; 2956 } 2957 memmove (s5r->wbuf, 2958 &s5r->wbuf[len], 2959 s5r->wbuf_len - len); 2960 s5r->wbuf_len -= len; 2961 if (s5r->wbuf_len > 0) 2962 { 2963 /* not done writing */ 2964 s5r->wtask = 2965 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, 2966 s5r->sock, 2967 &do_write, s5r); 2968 return; 2969 } 2970 2971 /* we're done writing, continue with state machine! */ 2972 2973 switch (s5r->state) 2974 { 2975 case SOCKS5_INIT: 2976 GNUNET_assert (0); 2977 break; 2978 2979 case SOCKS5_REQUEST: 2980 GNUNET_assert (NULL != s5r->rtask); 2981 break; 2982 2983 case SOCKS5_DATA_TRANSFER: 2984 setup_data_transfer (s5r); 2985 return; 2986 2987 case SOCKS5_WRITE_THEN_CLEANUP: 2988 cleanup_s5r (s5r); 2989 return; 2990 2991 default: 2992 GNUNET_break (0); 2993 break; 2994 } 2995 } 2996 2997 2998 /** 2999 * Return a server response message indicating a failure to the client. 3000 * 3001 * @param s5r request to return failure code for 3002 * @param sc status code to return 3003 */ 3004 static void 3005 signal_socks_failure (struct Socks5Request *s5r, 3006 enum Socks5StatusCode sc) 3007 { 3008 struct Socks5ServerResponseMessage *s_resp; 3009 3010 GNUNET_break (0 == s5r->wbuf_len); /* Should happen first in any transmission, right? */ 3011 GNUNET_assert (SOCKS_BUFFERSIZE - s5r->wbuf_len >= 3012 sizeof(struct Socks5ServerResponseMessage)); 3013 s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf[s5r->wbuf_len]; 3014 memset (s_resp, 0, sizeof(struct Socks5ServerResponseMessage)); 3015 s_resp->version = SOCKS_VERSION_5; 3016 s_resp->reply = sc; 3017 s5r->state = SOCKS5_WRITE_THEN_CLEANUP; 3018 if (NULL != s5r->wtask) 3019 s5r->wtask = 3020 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, 3021 s5r->sock, 3022 &do_write, s5r); 3023 } 3024 3025 3026 /** 3027 * Return a server response message indicating success. 3028 * 3029 * @param s5r request to return success status message for 3030 */ 3031 static void 3032 signal_socks_success (struct Socks5Request *s5r) 3033 { 3034 struct Socks5ServerResponseMessage *s_resp; 3035 3036 s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf[s5r->wbuf_len]; 3037 s_resp->version = SOCKS_VERSION_5; 3038 s_resp->reply = SOCKS5_STATUS_REQUEST_GRANTED; 3039 s_resp->reserved = 0; 3040 s_resp->addr_type = SOCKS5_AT_IPV4; 3041 /* zero out IPv4 address and port */ 3042 memset (&s_resp[1], 3043 0, 3044 sizeof(struct in_addr) + sizeof(uint16_t)); 3045 s5r->wbuf_len += sizeof(struct Socks5ServerResponseMessage) 3046 + sizeof(struct in_addr) + sizeof(uint16_t); 3047 if (NULL == s5r->wtask) 3048 s5r->wtask = 3049 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, 3050 s5r->sock, 3051 &do_write, s5r); 3052 } 3053 3054 3055 /** 3056 * Process GNS results for target domain. 3057 * 3058 * @param cls the `struct Socks5Request *` 3059 * @param tld #GNUNET_YES if this was a GNS TLD. 3060 * @param rd_count number of records returned 3061 * @param rd record data 3062 */ 3063 static void 3064 handle_gns_result (void *cls, 3065 int tld, 3066 uint32_t rd_count, 3067 const struct GNUNET_GNSRECORD_Data *rd) 3068 { 3069 struct Socks5Request *s5r = cls; 3070 const struct GNUNET_GNSRECORD_Data *r; 3071 int got_ip; 3072 3073 s5r->gns_lookup = NULL; 3074 s5r->is_gns = tld; 3075 got_ip = GNUNET_NO; 3076 for (uint32_t i = 0; i < rd_count; i++) 3077 { 3078 r = &rd[i]; 3079 switch (r->record_type) 3080 { 3081 case GNUNET_DNSPARSER_TYPE_A: 3082 { 3083 struct sockaddr_in *in; 3084 3085 if (sizeof(struct in_addr) != r->data_size) 3086 { 3087 GNUNET_break_op (0); 3088 break; 3089 } 3090 if (GNUNET_YES == got_ip) 3091 break; 3092 if (GNUNET_OK != 3093 GNUNET_NETWORK_test_pf (PF_INET)) 3094 break; 3095 got_ip = GNUNET_YES; 3096 in = (struct sockaddr_in *) &s5r->destination_address; 3097 in->sin_family = AF_INET; 3098 GNUNET_memcpy (&in->sin_addr, 3099 r->data, 3100 r->data_size); 3101 in->sin_port = htons (s5r->port); 3102 #if HAVE_SOCKADDR_IN_SIN_LEN 3103 in->sin_len = sizeof(*in); 3104 #endif 3105 } 3106 break; 3107 3108 case GNUNET_DNSPARSER_TYPE_AAAA: 3109 { 3110 struct sockaddr_in6 *in; 3111 3112 if (sizeof(struct in6_addr) != r->data_size) 3113 { 3114 GNUNET_break_op (0); 3115 break; 3116 } 3117 if (GNUNET_YES == got_ip) 3118 break; 3119 if (GNUNET_YES == disable_v6) 3120 break; 3121 if (GNUNET_OK != 3122 GNUNET_NETWORK_test_pf (PF_INET6)) 3123 break; 3124 /* FIXME: allow user to disable IPv6 per configuration option... */ 3125 got_ip = GNUNET_YES; 3126 in = (struct sockaddr_in6 *) &s5r->destination_address; 3127 in->sin6_family = AF_INET6; 3128 GNUNET_memcpy (&in->sin6_addr, 3129 r->data, 3130 r->data_size); 3131 in->sin6_port = htons (s5r->port); 3132 #if HAVE_SOCKADDR_IN_SIN_LEN 3133 in->sin6_len = sizeof(*in); 3134 #endif 3135 } 3136 break; 3137 3138 case GNUNET_GNSRECORD_TYPE_VPN: 3139 GNUNET_break (0); /* should have been translated within GNS */ 3140 break; 3141 3142 case GNUNET_GNSRECORD_TYPE_LEHO: 3143 GNUNET_free (s5r->leho); 3144 s5r->leho = GNUNET_strndup (r->data, 3145 r->data_size); 3146 break; 3147 3148 case GNUNET_GNSRECORD_TYPE_BOX: 3149 { 3150 const struct GNUNET_GNSRECORD_BoxRecord *box; 3151 3152 if (r->data_size < sizeof(struct GNUNET_GNSRECORD_BoxRecord)) 3153 { 3154 GNUNET_break_op (0); 3155 break; 3156 } 3157 box = r->data; 3158 if ((ntohl (box->record_type) != GNUNET_DNSPARSER_TYPE_TLSA) || 3159 (ntohs (box->protocol) != IPPROTO_TCP) || 3160 (ntohs (box->service) != s5r->port)) 3161 break; /* BOX record does not apply */ 3162 if (s5r->num_danes >= MAX_DANES) 3163 { 3164 GNUNET_break (0); /* MAX_DANES too small */ 3165 break; 3166 } 3167 s5r->is_tls = GNUNET_YES; /* This should be TLS */ 3168 s5r->dane_data_len[s5r->num_danes] 3169 = r->data_size - sizeof(struct GNUNET_GNSRECORD_BoxRecord); 3170 s5r->dane_data[s5r->num_danes] 3171 = GNUNET_memdup (&box[1], 3172 s5r->dane_data_len[s5r->num_danes]); 3173 s5r->num_danes++; 3174 break; 3175 } 3176 3177 default: 3178 /* don't care */ 3179 break; 3180 } 3181 } 3182 if ((GNUNET_YES != got_ip) && 3183 (GNUNET_YES == tld)) 3184 { 3185 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3186 "Name resolution failed to yield useful IP address.\n"); 3187 signal_socks_failure (s5r, 3188 SOCKS5_STATUS_GENERAL_FAILURE); 3189 return; 3190 } 3191 s5r->state = SOCKS5_DATA_TRANSFER; 3192 signal_socks_success (s5r); 3193 } 3194 3195 3196 /** 3197 * Remove the first @a len bytes from the beginning of the read buffer. 3198 * 3199 * @param s5r the handle clear the read buffer for 3200 * @param len number of bytes in read buffer to advance 3201 */ 3202 static void 3203 clear_from_s5r_rbuf (struct Socks5Request *s5r, 3204 size_t len) 3205 { 3206 GNUNET_assert (len <= s5r->rbuf_len); 3207 memmove (s5r->rbuf, 3208 &s5r->rbuf[len], 3209 s5r->rbuf_len - len); 3210 s5r->rbuf_len -= len; 3211 } 3212 3213 3214 /** 3215 * Read data from incoming Socks5 connection 3216 * 3217 * @param cls the closure with the `struct Socks5Request` 3218 */ 3219 static void 3220 do_s5r_read (void *cls) 3221 { 3222 struct Socks5Request *s5r = cls; 3223 const struct Socks5ClientHelloMessage *c_hello; 3224 struct Socks5ServerHelloMessage *s_hello; 3225 const struct Socks5ClientRequestMessage *c_req; 3226 ssize_t rlen; 3227 size_t alen; 3228 const struct GNUNET_SCHEDULER_TaskContext *tc; 3229 3230 s5r->rtask = NULL; 3231 tc = GNUNET_SCHEDULER_get_task_context (); 3232 if ((NULL != tc->read_ready) && 3233 (GNUNET_NETWORK_fdset_isset (tc->read_ready, 3234 s5r->sock))) 3235 { 3236 rlen = GNUNET_NETWORK_socket_recv (s5r->sock, 3237 &s5r->rbuf[s5r->rbuf_len], 3238 sizeof(s5r->rbuf) - s5r->rbuf_len); 3239 if (rlen <= 0) 3240 { 3241 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3242 "socks5 client disconnected.\n"); 3243 cleanup_s5r (s5r); 3244 return; 3245 } 3246 s5r->rbuf_len += rlen; 3247 } 3248 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 3249 s5r->sock, 3250 &do_s5r_read, s5r); 3251 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3252 "Processing %zu bytes of socks data in state %d\n", 3253 s5r->rbuf_len, 3254 s5r->state); 3255 switch (s5r->state) 3256 { 3257 case SOCKS5_INIT: 3258 c_hello = (const struct Socks5ClientHelloMessage*) &s5r->rbuf; 3259 if ((s5r->rbuf_len < sizeof(struct Socks5ClientHelloMessage)) || 3260 (s5r->rbuf_len < sizeof(struct Socks5ClientHelloMessage) 3261 + c_hello->num_auth_methods)) 3262 return; /* need more data */ 3263 if (SOCKS_VERSION_5 != c_hello->version) 3264 { 3265 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3266 _ ("Unsupported socks version %d\n"), 3267 (int) c_hello->version); 3268 cleanup_s5r (s5r); 3269 return; 3270 } 3271 clear_from_s5r_rbuf (s5r, 3272 sizeof(struct Socks5ClientHelloMessage) 3273 + c_hello->num_auth_methods); 3274 GNUNET_assert (0 == s5r->wbuf_len); 3275 s_hello = (struct Socks5ServerHelloMessage *) &s5r->wbuf; 3276 s5r->wbuf_len = sizeof(struct Socks5ServerHelloMessage); 3277 s_hello->version = SOCKS_VERSION_5; 3278 s_hello->auth_method = SOCKS_AUTH_NONE; 3279 GNUNET_assert (NULL == s5r->wtask); 3280 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, 3281 s5r->sock, 3282 &do_write, s5r); 3283 s5r->state = SOCKS5_REQUEST; 3284 return; 3285 3286 case SOCKS5_REQUEST: 3287 c_req = (const struct Socks5ClientRequestMessage *) &s5r->rbuf; 3288 if (s5r->rbuf_len < sizeof(struct Socks5ClientRequestMessage)) 3289 return; 3290 switch (c_req->command) 3291 { 3292 case SOCKS5_CMD_TCP_STREAM: 3293 /* handled below */ 3294 break; 3295 3296 default: 3297 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3298 _ ("Unsupported socks command %d\n"), 3299 (int) c_req->command); 3300 signal_socks_failure (s5r, 3301 SOCKS5_STATUS_COMMAND_NOT_SUPPORTED); 3302 return; 3303 } 3304 switch (c_req->addr_type) 3305 { 3306 case SOCKS5_AT_IPV4: 3307 { 3308 const struct in_addr *v4 = (const struct in_addr *) &c_req[1]; 3309 const uint16_t *v4port = (const uint16_t *) &v4[1]; 3310 struct sockaddr_in *in; 3311 3312 s5r->port = ntohs (*v4port); 3313 alen = sizeof(struct in_addr); 3314 if (s5r->rbuf_len < sizeof(struct Socks5ClientRequestMessage) 3315 + alen + sizeof(uint16_t)) 3316 return; /* need more data */ 3317 in = (struct sockaddr_in *) &s5r->destination_address; 3318 in->sin_family = AF_INET; 3319 in->sin_addr = *v4; 3320 in->sin_port = *v4port; 3321 #if HAVE_SOCKADDR_IN_SIN_LEN 3322 in->sin_len = sizeof(*in); 3323 #endif 3324 s5r->state = SOCKS5_DATA_TRANSFER; 3325 } 3326 break; 3327 3328 case SOCKS5_AT_IPV6: 3329 { 3330 const struct in6_addr *v6 = (const struct in6_addr *) &c_req[1]; 3331 const uint16_t *v6port = (const uint16_t *) &v6[1]; 3332 struct sockaddr_in6 *in; 3333 3334 s5r->port = ntohs (*v6port); 3335 alen = sizeof(struct in6_addr); 3336 if (s5r->rbuf_len < sizeof(struct Socks5ClientRequestMessage) 3337 + alen + sizeof(uint16_t)) 3338 return; /* need more data */ 3339 in = (struct sockaddr_in6 *) &s5r->destination_address; 3340 in->sin6_family = AF_INET6; 3341 in->sin6_addr = *v6; 3342 in->sin6_port = *v6port; 3343 #if HAVE_SOCKADDR_IN_SIN_LEN 3344 in->sin6_len = sizeof(*in); 3345 #endif 3346 s5r->state = SOCKS5_DATA_TRANSFER; 3347 } 3348 break; 3349 3350 case SOCKS5_AT_DOMAINNAME: 3351 { 3352 const uint8_t *dom_len; 3353 const char *dom_name; 3354 const uint16_t *dport; 3355 3356 dom_len = (const uint8_t *) &c_req[1]; 3357 alen = *dom_len + 1; 3358 if (s5r->rbuf_len < sizeof(struct Socks5ClientRequestMessage) 3359 + alen + sizeof(uint16_t)) 3360 return; /* need more data */ 3361 dom_name = (const char *) &dom_len[1]; 3362 dport = (const uint16_t *) &dom_name[*dom_len]; 3363 s5r->domain = GNUNET_strndup (dom_name, 3364 *dom_len); 3365 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3366 "Requested connection is to %s:%d\n", 3367 // (HTTPS_PORT == s5r->port) ? "s" : "", 3368 s5r->domain, 3369 ntohs (*dport)); 3370 s5r->state = SOCKS5_RESOLVING; 3371 s5r->port = ntohs (*dport); 3372 s5r->is_tls = (HTTPS_PORT == s5r->port) ? GNUNET_YES : GNUNET_NO; 3373 s5r->gns_lookup = GNUNET_GNS_lookup_with_tld (gns_handle, 3374 s5r->domain, 3375 GNUNET_DNSPARSER_TYPE_A, 3376 GNUNET_GNS_LO_LOCAL_MASTER /* only cached */ 3377 , 3378 &handle_gns_result, 3379 s5r); 3380 break; 3381 } 3382 3383 default: 3384 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3385 _ ("Unsupported socks address type %d\n"), 3386 (int) c_req->addr_type); 3387 signal_socks_failure (s5r, 3388 SOCKS5_STATUS_ADDRESS_TYPE_NOT_SUPPORTED); 3389 return; 3390 } 3391 clear_from_s5r_rbuf (s5r, 3392 sizeof(struct Socks5ClientRequestMessage) 3393 + alen + sizeof(uint16_t)); 3394 if (0 != s5r->rbuf_len) 3395 { 3396 /* read more bytes than healthy, why did the client send more!? */ 3397 GNUNET_break_op (0); 3398 signal_socks_failure (s5r, 3399 SOCKS5_STATUS_GENERAL_FAILURE); 3400 return; 3401 } 3402 if (SOCKS5_DATA_TRANSFER == s5r->state) 3403 { 3404 /* if we are not waiting for GNS resolution, signal success */ 3405 signal_socks_success (s5r); 3406 } 3407 /* We are done reading right now */ 3408 GNUNET_SCHEDULER_cancel (s5r->rtask); 3409 s5r->rtask = NULL; 3410 return; 3411 3412 case SOCKS5_RESOLVING: 3413 GNUNET_assert (0); 3414 return; 3415 3416 case SOCKS5_DATA_TRANSFER: 3417 GNUNET_assert (0); 3418 return; 3419 3420 default: 3421 GNUNET_assert (0); 3422 return; 3423 } 3424 } 3425 3426 3427 /** 3428 * Accept new incoming connections 3429 * 3430 * @param cls the closure with the lsock4 or lsock6 3431 */ 3432 static void 3433 do_accept (void *cls) 3434 { 3435 struct GNUNET_NETWORK_Handle *lsock = cls; 3436 struct GNUNET_NETWORK_Handle *s; 3437 struct Socks5Request *s5r; 3438 3439 GNUNET_assert (NULL != lsock); 3440 if (lsock == lsock4) 3441 ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 3442 lsock, 3443 &do_accept, 3444 lsock); 3445 else if (lsock == lsock6) 3446 ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 3447 lsock, 3448 &do_accept, 3449 lsock); 3450 else 3451 GNUNET_assert (0); 3452 s = GNUNET_NETWORK_socket_accept (lsock, 3453 NULL, 3454 NULL); 3455 if (NULL == s) 3456 { 3457 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 3458 "accept"); 3459 return; 3460 } 3461 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3462 "Got an inbound connection, waiting for data\n"); 3463 s5r = GNUNET_new (struct Socks5Request); 3464 GNUNET_CONTAINER_DLL_insert (s5r_head, 3465 s5r_tail, 3466 s5r); 3467 s5r->sock = s; 3468 s5r->state = SOCKS5_INIT; 3469 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 3470 s5r->sock, 3471 &do_s5r_read, 3472 s5r); 3473 } 3474 3475 3476 /* ******************* General / main code ********************* */ 3477 3478 3479 /** 3480 * Task run on shutdown 3481 * 3482 * @param cls closure 3483 */ 3484 static void 3485 do_shutdown (void *cls) 3486 { 3487 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3488 "Shutting down...\n"); 3489 /* MHD requires resuming before destroying the daemons */ 3490 for (struct Socks5Request *s5r = s5r_head; 3491 NULL != s5r; 3492 s5r = s5r->next) 3493 { 3494 if (s5r->suspended) 3495 { 3496 s5r->suspended = GNUNET_NO; 3497 MHD_resume_connection (s5r->con); 3498 } 3499 } 3500 while (NULL != mhd_httpd_head) 3501 kill_httpd (mhd_httpd_head); 3502 while (NULL != s5r_head) 3503 cleanup_s5r (s5r_head); 3504 if (NULL != lsock4) 3505 { 3506 GNUNET_NETWORK_socket_close (lsock4); 3507 lsock4 = NULL; 3508 } 3509 if (NULL != lsock6) 3510 { 3511 GNUNET_NETWORK_socket_close (lsock6); 3512 lsock6 = NULL; 3513 } 3514 if (NULL != curl_multi) 3515 { 3516 curl_multi_cleanup (curl_multi); 3517 curl_multi = NULL; 3518 } 3519 if (NULL != gns_handle) 3520 { 3521 GNUNET_GNS_disconnect (gns_handle); 3522 gns_handle = NULL; 3523 } 3524 if (NULL != curl_download_task) 3525 { 3526 GNUNET_SCHEDULER_cancel (curl_download_task); 3527 curl_download_task = NULL; 3528 } 3529 if (NULL != ltask4) 3530 { 3531 GNUNET_SCHEDULER_cancel (ltask4); 3532 ltask4 = NULL; 3533 } 3534 if (NULL != ltask6) 3535 { 3536 GNUNET_SCHEDULER_cancel (ltask6); 3537 ltask6 = NULL; 3538 } 3539 gnutls_x509_crt_deinit (proxy_ca.cert); 3540 gnutls_x509_privkey_deinit (proxy_ca.key); 3541 gnutls_global_deinit (); 3542 } 3543 3544 3545 /** 3546 * Create an IPv4 listen socket bound to our port. 3547 * 3548 * @return NULL on error 3549 */ 3550 static struct GNUNET_NETWORK_Handle * 3551 bind_v4 () 3552 { 3553 struct GNUNET_NETWORK_Handle *ls; 3554 struct sockaddr_in sa4; 3555 int eno; 3556 3557 memset (&sa4, 0, sizeof(sa4)); 3558 sa4.sin_family = AF_INET; 3559 sa4.sin_port = htons (port); 3560 sa4.sin_addr.s_addr = address; 3561 #if HAVE_SOCKADDR_IN_SIN_LEN 3562 sa4.sin_len = sizeof(sa4); 3563 #endif 3564 ls = GNUNET_NETWORK_socket_create (AF_INET, 3565 SOCK_STREAM, 3566 0); 3567 if (NULL == ls) 3568 return NULL; 3569 if (GNUNET_OK != 3570 GNUNET_NETWORK_socket_bind (ls, 3571 (const struct sockaddr *) &sa4, 3572 sizeof(sa4))) 3573 { 3574 eno = errno; 3575 GNUNET_NETWORK_socket_close (ls); 3576 errno = eno; 3577 return NULL; 3578 } 3579 return ls; 3580 } 3581 3582 3583 /** 3584 * Create an IPv6 listen socket bound to our port. 3585 * 3586 * @return NULL on error 3587 */ 3588 static struct GNUNET_NETWORK_Handle * 3589 bind_v6 () 3590 { 3591 struct GNUNET_NETWORK_Handle *ls; 3592 struct sockaddr_in6 sa6; 3593 int eno; 3594 3595 memset (&sa6, 0, sizeof(sa6)); 3596 sa6.sin6_family = AF_INET6; 3597 sa6.sin6_port = htons (port); 3598 sa6.sin6_addr = address6; 3599 #if HAVE_SOCKADDR_IN_SIN_LEN 3600 sa6.sin6_len = sizeof(sa6); 3601 #endif 3602 ls = GNUNET_NETWORK_socket_create (AF_INET6, 3603 SOCK_STREAM, 3604 0); 3605 if (NULL == ls) 3606 return NULL; 3607 if (GNUNET_OK != 3608 GNUNET_NETWORK_socket_bind (ls, 3609 (const struct sockaddr *) &sa6, 3610 sizeof(sa6))) 3611 { 3612 eno = errno; 3613 GNUNET_NETWORK_socket_close (ls); 3614 errno = eno; 3615 return NULL; 3616 } 3617 return ls; 3618 } 3619 3620 3621 /** 3622 * Main function that will be run 3623 * 3624 * @param cls closure 3625 * @param args remaining command-line arguments 3626 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 3627 * @param c configuration 3628 */ 3629 static void 3630 run (void *cls, 3631 char *const *args, 3632 const char *cfgfile, 3633 const struct GNUNET_CONFIGURATION_Handle *c) 3634 { 3635 char*cafile_cfg = NULL; 3636 char*cafile; 3637 char*addr_str; 3638 struct MhdHttpList *hd; 3639 3640 cfg = c; 3641 3642 /* Get address to bind to */ 3643 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "gns-proxy", 3644 "BIND_TO", 3645 &addr_str)) 3646 { 3647 // No address specified 3648 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3649 "Don't know what to bind to...\n"); 3650 GNUNET_free (addr_str); 3651 GNUNET_SCHEDULER_shutdown (); 3652 return; 3653 } 3654 if (1 != inet_pton (AF_INET, addr_str, &address)) 3655 { 3656 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3657 "Unable to parse address %s\n", 3658 addr_str); 3659 GNUNET_free (addr_str); 3660 GNUNET_SCHEDULER_shutdown (); 3661 return; 3662 } 3663 GNUNET_free (addr_str); 3664 /* Get address to bind to */ 3665 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "gns-proxy", 3666 "BIND_TO6", 3667 &addr_str)) 3668 { 3669 // No address specified 3670 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3671 "Don't know what to bind6 to...\n"); 3672 GNUNET_free (addr_str); 3673 GNUNET_SCHEDULER_shutdown (); 3674 return; 3675 } 3676 if (1 != inet_pton (AF_INET6, addr_str, &address6)) 3677 { 3678 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3679 "Unable to parse IPv6 address %s\n", 3680 addr_str); 3681 GNUNET_free (addr_str); 3682 GNUNET_SCHEDULER_shutdown (); 3683 return; 3684 } 3685 GNUNET_free (addr_str); 3686 3687 if (NULL == (curl_multi = curl_multi_init ())) 3688 { 3689 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3690 "Failed to create cURL multi handle!\n"); 3691 return; 3692 } 3693 cafile = cafile_opt; 3694 if (NULL == cafile) 3695 { 3696 if (GNUNET_OK != 3697 GNUNET_CONFIGURATION_get_value_filename (cfg, 3698 "gns-proxy", 3699 "PROXY_CACERT", 3700 &cafile_cfg)) 3701 { 3702 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 3703 "gns-proxy", 3704 "PROXY_CACERT"); 3705 return; 3706 } 3707 cafile = cafile_cfg; 3708 } 3709 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3710 "Using `%s' as CA\n", 3711 cafile); 3712 3713 gnutls_global_init (); 3714 gnutls_x509_crt_init (&proxy_ca.cert); 3715 gnutls_x509_privkey_init (&proxy_ca.key); 3716 3717 if ((GNUNET_OK != 3718 load_cert_from_file (proxy_ca.cert, 3719 cafile)) || 3720 (GNUNET_OK != 3721 load_key_from_file (proxy_ca.key, 3722 cafile))) 3723 { 3724 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3725 _ ("Failed to load X.509 key and certificate from `%s'\n"), 3726 cafile); 3727 gnutls_x509_crt_deinit (proxy_ca.cert); 3728 gnutls_x509_privkey_deinit (proxy_ca.key); 3729 gnutls_global_deinit (); 3730 GNUNET_free (cafile_cfg); 3731 return; 3732 } 3733 GNUNET_free (cafile_cfg); 3734 if (NULL == (gns_handle = GNUNET_GNS_connect (cfg))) 3735 { 3736 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3737 "Unable to connect to GNS!\n"); 3738 gnutls_x509_crt_deinit (proxy_ca.cert); 3739 gnutls_x509_privkey_deinit (proxy_ca.key); 3740 gnutls_global_deinit (); 3741 return; 3742 } 3743 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 3744 NULL); 3745 3746 /* Open listen socket for socks proxy */ 3747 lsock6 = bind_v6 (); 3748 if (NULL == lsock6) 3749 { 3750 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 3751 "bind"); 3752 } 3753 else 3754 { 3755 if (GNUNET_OK != 3756 GNUNET_NETWORK_socket_listen (lsock6, 3757 5)) 3758 { 3759 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 3760 "listen"); 3761 GNUNET_NETWORK_socket_close (lsock6); 3762 lsock6 = NULL; 3763 } 3764 else 3765 { 3766 ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 3767 lsock6, 3768 &do_accept, 3769 lsock6); 3770 } 3771 } 3772 lsock4 = bind_v4 (); 3773 if (NULL == lsock4) 3774 { 3775 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 3776 "bind"); 3777 } 3778 else 3779 { 3780 if (GNUNET_OK != 3781 GNUNET_NETWORK_socket_listen (lsock4, 3782 5)) 3783 { 3784 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 3785 "listen"); 3786 GNUNET_NETWORK_socket_close (lsock4); 3787 lsock4 = NULL; 3788 } 3789 else 3790 { 3791 ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 3792 lsock4, 3793 &do_accept, 3794 lsock4); 3795 } 3796 } 3797 if ((NULL == lsock4) && 3798 (NULL == lsock6)) 3799 { 3800 GNUNET_SCHEDULER_shutdown (); 3801 return; 3802 } 3803 if (CURLSSLSET_OK != curl_global_sslset (CURLSSLBACKEND_GNUTLS, 3804 NULL, 3805 NULL)) 3806 { 3807 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3808 "cURL does not support the GnuTLS backend\n"); 3809 3810 } 3811 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 3812 { 3813 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3814 "cURL global init failed!\n"); 3815 GNUNET_SCHEDULER_shutdown (); 3816 return; 3817 } 3818 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3819 "Proxy listens on port %u\n", 3820 (unsigned int) port); 3821 3822 /* start MHD daemon for HTTP */ 3823 hd = GNUNET_new (struct MhdHttpList); 3824 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET 3825 | MHD_ALLOW_SUSPEND_RESUME, 3826 0, 3827 NULL, NULL, 3828 &create_response, hd, 3829 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned 3830 int) 16, 3831 MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, 3832 NULL, 3833 MHD_OPTION_NOTIFY_CONNECTION, 3834 &mhd_connection_cb, NULL, 3835 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, 3836 NULL, 3837 MHD_OPTION_END); 3838 if (NULL == hd->daemon) 3839 { 3840 GNUNET_free (hd); 3841 GNUNET_SCHEDULER_shutdown (); 3842 return; 3843 } 3844 httpd = hd; 3845 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, 3846 mhd_httpd_tail, 3847 hd); 3848 } 3849 3850 3851 /** 3852 * The main function for gnunet-gns-proxy. 3853 * 3854 * @param argc number of arguments from the command line 3855 * @param argv command line arguments 3856 * @return 0 ok, 1 on error 3857 */ 3858 int 3859 main (int argc, 3860 char *const *argv) 3861 { 3862 struct GNUNET_GETOPT_CommandLineOption options[] = { 3863 GNUNET_GETOPT_option_uint16 ('p', 3864 "port", 3865 NULL, 3866 gettext_noop ( 3867 "listen on specified port (default: 7777)"), 3868 &port), 3869 GNUNET_GETOPT_option_string ('a', 3870 "authority", 3871 NULL, 3872 gettext_noop ("pem file to use as CA"), 3873 &cafile_opt), 3874 GNUNET_GETOPT_option_flag ('6', 3875 "disable-ivp6", 3876 gettext_noop ("disable use of IPv6"), 3877 &disable_v6), 3878 3879 GNUNET_GETOPT_OPTION_END 3880 }; 3881 static const char*page = 3882 "<html><head><title>gnunet-gns-proxy</title>" 3883 "</head><body>cURL fail</body></html>"; 3884 int ret; 3885 3886 GNUNET_log_setup ("gnunet-gns-proxy", 3887 "WARNING", 3888 NULL); 3889 curl_failure_response 3890 = MHD_create_response_from_buffer (strlen (page), 3891 (void *) page, 3892 MHD_RESPMEM_PERSISTENT); 3893 3894 ret = 3895 (GNUNET_OK == 3896 GNUNET_PROGRAM_run (GNUNET_OS_project_data_gnunet (), 3897 argc, argv, 3898 "gnunet-gns-proxy", 3899 _ ("GNUnet GNS proxy"), 3900 options, 3901 &run, NULL)) ? 0 : 1; 3902 MHD_destroy_response (curl_failure_response); 3903 return ret; 3904 } 3905 3906 3907 /* end of gnunet-gns-proxy.c */