gnunet

Main GNUnet Logic
Log | Files | Refs | Submodules | README | LICENSE

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 */