fix only cache variable long/short
[oweals/gnunet.git] / src / gns / gnunet-gns-proxy.c
index d0e230fc2fc1e012e7bc6dd35f832b84bade2acc..e65cd61101c115e4e64fec26498700b9d59a67ef 100644 (file)
  *        with legacy browsers
  *
  * TODO:
- * - make DNS lookup asynchronous
- * - simplify POST/PUT processing
  * - double-check queueing logic
- * - figure out what to do with the 'authority' issue
- * - document better
  */
 #include "platform.h"
 #include <microhttpd.h>
@@ -38,6 +34,9 @@
 #include <gnutls/x509.h>
 #include <gnutls/abstract.h>
 #include <gnutls/crypto.h>
+#if HAVE_GNUTLS_DANE
+#include <gnutls/dane.h>
+#endif
 #include <regex.h>
 #include "gnunet_util_lib.h"
 #include "gnunet_gns_service.h"
@@ -47,7 +46,7 @@
 
 /**
  * Default Socks5 listen port.
- */ 
+ */
 #define GNUNET_GNS_PROXY_PORT 7777
 
 /**
 #define MAX_HTTP_URI_LENGTH 2048
 
 /**
- * Some buffer size. @deprecated
+ * Size of the buffer for the data upload / download.  Must be
+ * enough for curl, thus CURL_MAX_WRITE_SIZE is needed here (16k).
  */
-#define POSTBUFFERSIZE 4096
-
+#define IO_BUFFERSIZE CURL_MAX_WRITE_SIZE
 
 /**
  * Size of the read/write buffers for Socks.   Uses
 
 /**
  * Commands in Socks5.
- */ 
+ */
 enum Socks5Commands
 {
   /**
@@ -143,7 +142,7 @@ enum Socks5Commands
 
 /**
  * Address types in Socks5.
- */ 
+ */
 enum Socks5AddressType
 {
   /**
@@ -244,7 +243,7 @@ struct Socks5ClientRequestMessage
    */
   uint8_t addr_type;
 
-  /* 
+  /*
    * Followed by either an ip4/ipv6 address or a domain name with a
    * length field (uint8_t) in front (depending on @e addr_type).
    * followed by port number in network byte order (uint16_t).
@@ -277,7 +276,7 @@ struct Socks5ServerResponseMessage
    */
   uint8_t addr_type;
 
-  /* 
+  /*
    * Followed by either an ip4/ipv6 address or a domain name with a
    * length field (uint8_t) in front (depending on @e addr_type).
    * followed by port number in network byte order (uint16_t).
@@ -286,140 +285,6 @@ struct Socks5ServerResponseMessage
 };
 
 
-/* ***************** Datastructures for Socks handling **************** */
-
-
-/**
- * The socks phases.  
- */
-enum SocksPhase
-{
-  /**
-   * We're waiting to get the client hello.
-   */
-  SOCKS5_INIT,
-
-  /**
-   * We're waiting to get the initial request.
-   */
-  SOCKS5_REQUEST,
-
-  /**
-   * We are currently resolving the destination.
-   */
-  SOCKS5_RESOLVING,
-
-  /**
-   * We're in transfer mode.
-   */
-  SOCKS5_DATA_TRANSFER,
-
-  /**
-   * Finish writing the write buffer, then clean up.
-   */
-  SOCKS5_WRITE_THEN_CLEANUP,
-
-  /**
-   * Socket has been passed to MHD, do not close it anymore.
-   */
-  SOCKS5_SOCKET_WITH_MHD
-};
-
-
-
-/**
- * A structure for socks requests
- */
-struct Socks5Request
-{
-
-  /**
-   * DLL.
-   */
-  struct Socks5Request *next;
-
-  /**
-   * DLL.
-   */
-  struct Socks5Request *prev;
-
-  /**
-   * The client socket 
-   */
-  struct GNUNET_NETWORK_Handle *sock;
-
-  /**
-   * Handle to GNS lookup, during #SOCKS5_RESOLVING phase.
-   */
-  struct GNUNET_GNS_LookupRequest *gns_lookup;
-
-  /**
-   * Client socket read task 
-   */
-  GNUNET_SCHEDULER_TaskIdentifier rtask;
-
-  /**
-   * Client socket write task 
-   */
-  GNUNET_SCHEDULER_TaskIdentifier wtask;
-
-  /**
-   * Timeout task 
-   */
-  GNUNET_SCHEDULER_TaskIdentifier timeout_task;
-
-  /**
-   * Read buffer 
-   */
-  char rbuf[SOCKS_BUFFERSIZE];
-
-  /**
-   * Write buffer 
-   */
-  char wbuf[SOCKS_BUFFERSIZE];
-
-  /**
-   * the domain name to server (only important for SSL) 
-   */
-  char *domain;
-
-  /**
-   * DNS Legacy Host Name as given by GNS, NULL if not given.
-   */
-  char *leho;
-
-  /**
-   * The URL to fetch 
-   */
-  char *url;
-
-  /**
-   * Number of bytes already in read buffer 
-   */
-  size_t rbuf_len;
-
-  /**
-   * Number of bytes already in write buffer 
-   */
-  size_t wbuf_len;
-  
-  /**
-   * Once known, what's the target address for the connection?
-   */
-  struct sockaddr_storage destination_address;
-
-  /**
-   * The socks state 
-   */
-  enum SocksPhase state;
-
-  /**
-   * Desired destination port.
-   */
-  uint16_t port;
-
-};
-
 
 /* *********************** Datastructures for HTTP handling ****************** */
 
@@ -429,12 +294,12 @@ struct Socks5Request
 struct ProxyCA
 {
   /**
-   * The certificate 
+   * The certificate
    */
   gnutls_x509_crt_t cert;
 
   /**
-   * The private key 
+   * The private key
    */
   gnutls_x509_privkey_t key;
 };
@@ -446,12 +311,12 @@ struct ProxyCA
 struct ProxyGNSCertificate
 {
   /**
-   * The certificate as PEM 
+   * The certificate as PEM
    */
   char cert[MAX_PEM_SIZE];
 
   /**
-   * The private key as PEM 
+   * The private key as PEM
    */
   char key[MAX_PEM_SIZE];
 };
@@ -464,22 +329,22 @@ struct ProxyGNSCertificate
 struct MhdHttpList
 {
   /**
-   * DLL for httpds 
+   * DLL for httpds
    */
   struct MhdHttpList *prev;
 
   /**
-   * DLL for httpds 
+   * DLL for httpds
    */
   struct MhdHttpList *next;
 
   /**
-   * the domain name to server (only important for SSL) 
+   * the domain name to server (only important for SSL)
    */
   char *domain;
 
   /**
-   * The daemon handle 
+   * The daemon handle
    */
   struct MHD_Daemon *daemon;
 
@@ -489,319 +354,274 @@ struct MhdHttpList
   struct ProxyGNSCertificate *proxy_cert;
 
   /**
-   * The task ID 
+   * The task ID
    */
   GNUNET_SCHEDULER_TaskIdentifier httpd_task;
 
   /**
-   * is this an ssl daemon? 
+   * is this an ssl daemon?
    */
   int is_ssl;
 
 };
 
 
-/* ***************** possibly deprecated data structures ****************** */
-
-
-/**
- * State machine for the IO buffer.
- */
-enum BufferStatus
-  {
-    BUF_WAIT_FOR_CURL,
-    BUF_WAIT_FOR_MHD
-  };
+/* ***************** Datastructures for Socks handling **************** */
 
 
 /**
- * A structure for MHD<->cURL streams
+ * The socks phases.
  */
-struct ProxyCurlTask
+enum SocksPhase
 {
   /**
-   * DLL for tasks 
+   * We're waiting to get the client hello.
    */
-  struct ProxyCurlTask *prev;
+  SOCKS5_INIT,
 
   /**
-   * DLL for tasks 
+   * We're waiting to get the initial request.
    */
-  struct ProxyCurlTask *next;
+  SOCKS5_REQUEST,
 
   /**
-   * Handle to cURL 
+   * We are currently resolving the destination.
    */
-  CURL *curl;
+  SOCKS5_RESOLVING,
 
   /**
-   * Optional header replacements for curl (LEHO) 
+   * We're in transfer mode.
    */
-  struct curl_slist *headers;
+  SOCKS5_DATA_TRANSFER,
 
   /**
-   * Optional resolver replacements for curl (LEHO) 
+   * Finish writing the write buffer, then clean up.
    */
-  struct curl_slist *resolver;
+  SOCKS5_WRITE_THEN_CLEANUP,
 
   /**
-   * curl response code 
+   * Socket has been passed to MHD, do not close it anymore.
    */
-  long curl_response_code;
+  SOCKS5_SOCKET_WITH_MHD,
 
   /**
-   * The cURL write buffer / MHD read buffer 
+   * We've finished receiving upload data from MHD.
    */
-  char buffer[CURL_MAX_WRITE_SIZE];
+  SOCKS5_SOCKET_UPLOAD_STARTED,
 
   /**
-   * Should die. @deprecated
+   * We've finished receiving upload data from MHD.
    */
-  char url[MAX_HTTP_URI_LENGTH];
+  SOCKS5_SOCKET_UPLOAD_DONE,
 
   /**
-   * Read pos of the data in the buffer 
+   * We've finished uploading data via CURL and can now download.
    */
-  char *buffer_read_ptr;
+  SOCKS5_SOCKET_DOWNLOAD_STARTED,
 
   /**
-   * Write pos in the buffer 
+   * We've finished receiving download data from cURL.
    */
-  char *buffer_write_ptr;
+  SOCKS5_SOCKET_DOWNLOAD_DONE
+};
+
+
+
+/**
+ * A structure for socks requests
+ */
+struct Socks5Request
+{
 
   /**
-   * connection 
+   * DLL.
    */
-  struct MHD_Connection *connection;
+  struct Socks5Request *next;
 
   /**
-   * put
+   * DLL.
    */
-  size_t put_read_offset;
-  size_t put_read_size;
+  struct Socks5Request *prev;
 
   /**
-   *post
+   * The client socket
    */
-  struct MHD_PostProcessor *post_handler;
-
-  /* post data */
-  struct ProxyUploadData *upload_data_head;
-  struct ProxyUploadData *upload_data_tail;
+  struct GNUNET_NETWORK_Handle *sock;
 
   /**
-   * the type of POST encoding 
+   * Handle to GNS lookup, during #SOCKS5_RESOLVING phase.
    */
-  char* post_type;
-
-  struct curl_httppost *httppost;
-
-  struct curl_httppost *httppost_last;
+  struct GNUNET_GNS_LookupRequest *gns_lookup;
 
   /**
-   * Number of bytes in buffer 
+   * Client socket read task
    */
-  unsigned int bytes_in_buffer;
-
-  /* PP task */
-  GNUNET_SCHEDULER_TaskIdentifier pp_task;
-
-  /* The associated daemon list entry */
-  struct MhdHttpList *mhd;
-
-  /* The associated response */
-  struct MHD_Response *response;
-
-  /* Cookies to set */
-  struct ProxySetCookieHeader *set_cookies_head;
-
-  /* Cookies to set */
-  struct ProxySetCookieHeader *set_cookies_tail;
+  GNUNET_SCHEDULER_TaskIdentifier rtask;
 
   /**
-   * The authority of the corresponding host (site of origin) 
+   * Client socket write task
    */
-  char authority[256];
+  GNUNET_SCHEDULER_TaskIdentifier wtask;
 
   /**
-   * The hostname (Host header field) 
+   * Timeout task
    */
-  char host[256];
+  GNUNET_SCHEDULER_TaskIdentifier timeout_task;
 
   /**
-   * The LEgacy HOstname (can be empty) 
+   * Read buffer
    */
-  char leho[256];
+  char rbuf[SOCKS_BUFFERSIZE];
 
   /**
-   * The port 
+   * Write buffer
    */
-  uint16_t port;
+  char wbuf[SOCKS_BUFFERSIZE];
 
   /**
-   * The buffer status (BUF_WAIT_FOR_CURL or BUF_WAIT_FOR_MHD) 
+   * Buffer we use for moving data between MHD and curl (in both directions).
    */
-  enum BufferStatus buf_status;
+  char io_buf[IO_BUFFERSIZE];
 
   /**
-   * connection status 
+   * MHD HTTP instance handling this request, NULL for none.
    */
-  int ready_to_queue;
+  struct MhdHttpList *hd;
 
   /**
-   * is curl running? 
+   * MHD response object for this request.
    */
-  int curl_running;
-  
+  struct MHD_Response *response;
+
   /**
-   * are we done 
+   * the domain name to server (only important for SSL)
    */
-  int fin;
+  char *domain;
 
   /**
-   * Already accepted 
+   * DNS Legacy Host Name as given by GNS, NULL if not given.
    */
-  int accepted;
+  char *leho;
 
   /**
-   * Indicates wheather the download is in progress 
+   * Payload of the (last) DANE record encountered.
    */
-  int download_in_progress;
+  char *dane_data;
 
   /**
-   * Indicates wheather the download was successful 
+   * The URL to fetch
    */
-  int download_is_finished;
+  char *url;
 
   /**
-   * Indicates wheather the download failed 
+   * Handle to cURL
    */
-  int download_error;
-
-  int post_done;
-
-  int is_httppost;
-  
-};
-
+  CURL *curl;
 
-/**
- * Struct for set-cookies
- */
-struct ProxySetCookieHeader
-{
   /**
-   * DLL 
+   * HTTP request headers for the curl request.
    */
-  struct ProxySetCookieHeader *next;
+  struct curl_slist *headers;
 
   /**
-   * DLL 
+   * HTTP response code to give to MHD for the response.
    */
-  struct ProxySetCookieHeader *prev;
+  unsigned int response_code;
 
   /**
-   * the cookie 
+   * Number of bytes in @e dane_data.
    */
-  char *cookie;
-};
+  size_t dane_data_len;
 
-
-/**
- * Post data structure
- */
-struct ProxyUploadData
-{
   /**
-   * DLL 
+   * Number of bytes already in read buffer
    */
-  struct ProxyUploadData *next;
+  size_t rbuf_len;
 
   /**
-   * DLL 
+   * Number of bytes already in write buffer
    */
-  struct ProxyUploadData *prev;
-
-  char *key;
-
-  char *filename;
+  size_t wbuf_len;
 
-  char *content_type;
+  /**
+   * Number of bytes already in the IO buffer.
+   */
+  size_t io_len;
 
-  size_t content_length;
-  
   /**
-   * value 
+   * Once known, what's the target address for the connection?
    */
-  char *value;
+  struct sockaddr_storage destination_address;
 
   /**
-   * to copy 
+   * The socks state
    */
-  size_t bytes_left;
+  enum SocksPhase state;
 
   /**
-   * size 
+   * Desired destination port.
    */
-  size_t total_bytes;
+  uint16_t port;
+
 };
 
 
+
 /* *********************** Globals **************************** */
 
 
 /**
- * The port the proxy is running on (default 7777) 
+ * The port the proxy is running on (default 7777)
  */
 static unsigned long port = GNUNET_GNS_PROXY_PORT;
 
 /**
- * The CA file (pem) to use for the proxy CA 
+ * The CA file (pem) to use for the proxy CA
  */
 static char *cafile_opt;
 
 /**
- * The listen socket of the proxy 
+ * The listen socket of the proxy for IPv4
  */
-static struct GNUNET_NETWORK_Handle *lsock;
+static struct GNUNET_NETWORK_Handle *lsock4;
 
 /**
- * The listen task ID 
+ * The listen socket of the proxy for IPv6
  */
-static GNUNET_SCHEDULER_TaskIdentifier ltask;
+static struct GNUNET_NETWORK_Handle *lsock6;
 
 /**
- * The cURL download task (curl multi API).
+ * The listen task ID for IPv4
  */
-static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
+static GNUNET_SCHEDULER_TaskIdentifier ltask4;
 
 /**
- * The cURL multi handle 
+ * The listen task ID for IPv6
  */
-static CURLM *curl_multi;
+static GNUNET_SCHEDULER_TaskIdentifier ltask6;
 
 /**
- * Handle to the GNS service 
+ * The cURL download task (curl multi API).
  */
-static struct GNUNET_GNS_Handle *gns_handle;
+static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
 
 /**
- * DLL for ProxyCurlTasks 
+ * The cURL multi handle
  */
-static struct ProxyCurlTask *ctasks_head;
+static CURLM *curl_multi;
 
 /**
- * DLL for ProxyCurlTasks 
+ * Handle to the GNS service
  */
-static struct ProxyCurlTask *ctasks_tail;
+static struct GNUNET_GNS_Handle *gns_handle;
 
 /**
- * DLL for http/https daemons 
+ * DLL for http/https daemons
  */
 static struct MhdHttpList *mhd_httpd_head;
 
 /**
- * DLL for http/https daemons 
+ * DLL for http/https daemons
  */
 static struct MhdHttpList *mhd_httpd_tail;
 
@@ -822,14 +642,14 @@ static struct Socks5Request *s5r_head;
 static struct Socks5Request *s5r_tail;
 
 /**
- * The users local GNS master zone 
+ * The users local GNS master zone
  */
-static struct GNUNET_CRYPTO_EccPublicSignKey local_gns_zone;
+static struct GNUNET_CRYPTO_EcdsaPublicKey local_gns_zone;
 
 /**
- * The users local shorten zone 
+ * The users local shorten zone
  */
-static struct GNUNET_CRYPTO_EccPrivateKey local_shorten_zone;
+static struct GNUNET_CRYPTO_EcdsaPrivateKey local_shorten_zone;
 
 /**
  * Is shortening enabled?
@@ -837,7 +657,7 @@ static struct GNUNET_CRYPTO_EccPrivateKey local_shorten_zone;
 static int do_shorten;
 
 /**
- * The CA for SSL certificate generation 
+ * The CA for SSL certificate generation
  */
 static struct ProxyCA proxy_ca;
 
@@ -865,6 +685,15 @@ static const struct GNUNET_CONFIGURATION_Handle *cfg;
 /* ************************* Global helpers ********************* */
 
 
+/**
+ * Run MHD now, we have extra data ready for the callback.
+ *
+ * @param hd the daemon to run now.
+ */
+static void
+run_mhd_now (struct MhdHttpList *hd);
+
+
 /**
  * Clean up s5r handles.
  *
@@ -873,6 +702,20 @@ static const struct GNUNET_CONFIGURATION_Handle *cfg;
 static void
 cleanup_s5r (struct Socks5Request *s5r)
 {
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Cleaning up socks request\n");
+  if (NULL != s5r->curl)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Cleaning up cURL handle\n");
+    curl_multi_remove_handle (curl_multi, s5r->curl);
+    curl_easy_cleanup (s5r->curl);
+    s5r->curl = NULL;
+  }
+  curl_slist_free_all (s5r->headers);
+  if ( (NULL != s5r->response) &&
+       (curl_failure_response != s5r->response) )
+    MHD_destroy_response (s5r->response);
   if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
     GNUNET_SCHEDULER_cancel (s5r->rtask);
   if (GNUNET_SCHEDULER_NO_TASK != s5r->timeout_task)
@@ -881,9 +724,9 @@ cleanup_s5r (struct Socks5Request *s5r)
     GNUNET_SCHEDULER_cancel (s5r->wtask);
   if (NULL != s5r->gns_lookup)
     GNUNET_GNS_lookup_cancel (s5r->gns_lookup);
-  if (NULL != s5r->sock) 
+  if (NULL != s5r->sock)
   {
-    if (SOCKS5_SOCKET_WITH_MHD == s5r->state)
+    if (SOCKS5_SOCKET_WITH_MHD <= s5r->state)
       GNUNET_NETWORK_socket_free_memory_only_ (s5r->sock);
     else
       GNUNET_NETWORK_socket_close (s5r->sock);
@@ -894,792 +737,508 @@ cleanup_s5r (struct Socks5Request *s5r)
   GNUNET_free_non_null (s5r->domain);
   GNUNET_free_non_null (s5r->leho);
   GNUNET_free_non_null (s5r->url);
+  GNUNET_free_non_null (s5r->dane_data);
   GNUNET_free (s5r);
 }
 
 
-/**
- * Run MHD now, we have extra data ready for the callback.
- *
- * @param hd the daemon to run now.
- */
-static void
-run_mhd_now (struct MhdHttpList *hd);
-
-
-/* *************************** netcat mode *********************** */
-
-#if 0
-
-
+/* ************************* HTTP handling with cURL *********************** */
 
 
 /**
- * Given a TCP stream and a destination address, forward the stream
- * in both directions.
+ * Callback for MHD response generation.  This function is called from
+ * MHD whenever MHD expects to get data back.  Copies data from the
+ * io_buf, if available.
  *
- * @param cls FIXME
- * @param tc  FIXME
+ * @param cls closure with our `struct Socks5Request`
+ * @param pos in buffer
+ * @param buf where to copy data
+ * @param max available space in @a buf
+ * @return number of bytes written to @a buf
  */
-static void
-forward_socket_like_ncat (void *cls,
-                         const struct GNUNET_SCHEDULER_TaskContext *tc)
+static ssize_t
+mhd_content_cb (void *cls,
+                uint64_t pos,
+                char* buf,
+                size_t max)
 {
-  struct hostent *phost;
-  uint32_t remote_ip;
-  struct sockaddr_in remote_addr;
-  struct in_addr *r_sin_addr;
-
-  s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
-                                                  SOCK_STREAM,
-                                                  0);
-  r_sin_addr = (struct in_addr*)(phost->h_addr);
-  remote_ip = r_sin_addr->s_addr;
-  memset(&remote_addr, 0, sizeof(remote_addr));
-  remote_addr.sin_family = AF_INET;
-#if HAVE_SOCKADDR_IN_SIN_LEN
-  remote_addr.sin_len = sizeof (remote_addr);
-#endif
-  remote_addr.sin_addr.s_addr = remote_ip;
-  remote_addr.sin_port = *port;
-  
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "target server: %s:%u\n", 
-             inet_ntoa(remote_addr.sin_addr),
-             ntohs(*port));
-  
-  if ((GNUNET_OK !=
-       GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
-                                      (const struct sockaddr*)&remote_addr,
-                                      sizeof (remote_addr)))
-      && (errno != EINPROGRESS))
-    {
-      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
-      signal_socks_failure (s5r,
-                           SOCKS5_STATUS_NETWORK_UNREACHABLE);
-      return;
-    }
-}
-#endif
-
-
-/* ************************* HTTP handling with cURL *********************** */
-
-static int
-con_post_data_iter (void *cls,
-                  enum MHD_ValueKind kind,
-                  const char *key,
-                  const char *filename,
-                  const char *content_type,
-                  const char *transfer_encoding,
-                  const char *data,
-                  uint64_t off,
-                  size_t size)
-{
-  struct ProxyCurlTask* ctask = cls;
-  struct ProxyUploadData* pdata;
-  char* enc;
-  char* new_value;
-  
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Got POST data (file: %s, content type: %s): '%s=%.*s' at offset %llu size %llu\n",
-             filename, content_type,
-              key, (int) size, data, 
-             (unsigned long long) off, 
-             (unsigned long long) size);
-  GNUNET_assert (NULL != ctask->post_type);
-
-  if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
-                       ctask->post_type))
-  {
-    ctask->is_httppost = GNUNET_YES;
-    /* new part */
-    if (0 == off)
-    {
-      pdata = GNUNET_new (struct ProxyUploadData);
-      pdata->key = GNUNET_strdup (key);
-
-      if (NULL != filename)
-        pdata->filename = GNUNET_strdup (filename);
-      if (NULL != content_type)
-        pdata->content_type = GNUNET_strdup (content_type);
-      pdata->value = GNUNET_malloc (size);
-      pdata->total_bytes = size;
-      memcpy (pdata->value, data, size);
-      GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
-                                        ctask->upload_data_tail,
-                                        pdata);
-
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Copied %llu bytes of POST Data\n", 
-                 (unsigned long long) size);
-      return MHD_YES;
-    }
-    
-    pdata = ctask->upload_data_tail;
-    new_value = GNUNET_malloc (size + pdata->total_bytes);
-    memcpy (new_value, pdata->value, pdata->total_bytes);
-    memcpy (new_value+off, data, size);
-    GNUNET_free (pdata->value);
-    pdata->value = new_value;
-    pdata->total_bytes += size;
+  struct Socks5Request *s5r = cls;
+  size_t bytes_to_copy;
 
-    return MHD_YES;
+  if ( (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) ||
+       (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
+  {
+    /* we're still not done with the upload, do not yet
+       start the download, the IO buffer is still full
+       with upload data. */
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Pausing MHD download, not yet ready for download\n");
+    return 0; /* not yet ready for data download */
   }
-
-  if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
-                       ctask->post_type))
+  bytes_to_copy = GNUNET_MIN (max,
+                             s5r->io_len);
+  if ( (0 == bytes_to_copy) &&
+       (SOCKS5_SOCKET_DOWNLOAD_DONE != s5r->state) )
   {
-    return MHD_NO;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Pausing MHD download, no data available\n");
+    return 0; /* more data later */
   }
-
-  ctask->is_httppost = GNUNET_NO;
-  
-  if (NULL != ctask->curl)
-    curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
-
-  if (0 == off)
+  if ( (0 == bytes_to_copy) &&
+       (SOCKS5_SOCKET_DOWNLOAD_DONE == s5r->state) )
   {
-    enc = curl_easy_escape (ctask->curl, key, 0);
-    if (NULL == enc)
-      {
-       GNUNET_break (0);
-       return MHD_NO;
-      }
-    /* a key */
-    pdata = GNUNET_new (struct ProxyUploadData);
-    pdata->value = GNUNET_malloc (strlen (enc) + 3);
-    if (NULL != ctask->upload_data_head)
-    {
-      pdata->value[0] = '&';
-      memcpy (pdata->value+1, enc, strlen (enc));
-    }
-    else
-      memcpy (pdata->value, enc, strlen (enc));
-    pdata->value[strlen (pdata->value)] = '=';
-    pdata->bytes_left = strlen (pdata->value);
-    pdata->total_bytes = pdata->bytes_left;
-    curl_free (enc);
-
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Escaped POST key: '%s'\n",
-                pdata->value);
-
-    GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
-                                      ctask->upload_data_tail,
-                                      pdata);
+               "Completed MHD download\n");
+    return MHD_CONTENT_READER_END_OF_STREAM;
   }
-
-  /* a value */
-  enc = curl_easy_escape (ctask->curl, data, 0);
-  if (NULL == enc)
-    {
-      GNUNET_break (0);
-      return MHD_NO;
-    }
-  pdata = GNUNET_new (struct ProxyUploadData);
-  pdata->value = GNUNET_malloc (strlen (enc) + 1);
-  memcpy (pdata->value, enc, strlen (enc));
-  pdata->bytes_left = strlen (pdata->value);
-  pdata->total_bytes = pdata->bytes_left;
-  curl_free (enc);
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Escaped POST value: '%s'\n",
-              pdata->value);
-
-  GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
-                                    ctask->upload_data_tail,
-                                    pdata);
-  return MHD_YES;
+  memcpy (buf, s5r->io_buf, bytes_to_copy);
+  memmove (s5r->io_buf,
+          &s5r->io_buf[bytes_to_copy],
+          s5r->io_len - bytes_to_copy);
+  s5r->io_len -= bytes_to_copy;
+  if (NULL != s5r->curl)
+    curl_easy_pause (s5r->curl, CURLPAUSE_CONT);
+  return bytes_to_copy;
 }
 
 
 /**
- * Read HTTP request header field 'Host'
+ * Check that the website has presented us with a valid SSL certificate.
+ * The certificate must either match the domain name or the LEHO name
+ * (or, if available, the TLSA record).
  *
- * @param cls buffer to write to
- * @param kind value kind
- * @param key field key
- * @param value field value
- * @return #MHD_NO when Host found
+ * @param s5r request to check for.
+ * @return #GNUNET_OK if the certificate is valid
  */
 static int
-con_val_iter (void *cls,
-              enum MHD_ValueKind kind,
-              const char *key,
-              const char *value)
+check_ssl_certificate (struct Socks5Request *s5r)
 {
-  struct ProxyCurlTask *ctask = cls;
-  char* buf = ctask->host;
-  char* port;
-  char* cstr;
-  const char* hdr_val;
-  unsigned int uport;
-
-  if (0 == strcmp ("Host", key))
+  unsigned int cert_list_size;
+  const gnutls_datum_t *chainp;
+  const struct curl_tlssessioninfo *tlsinfo;
+  char certdn[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 3];
+  size_t size;
+  gnutls_x509_crt_t x509_cert;
+  int rc;
+  const char *name;
+
+  if (CURLE_OK !=
+      curl_easy_getinfo (s5r->curl,
+                        CURLINFO_TLS_SESSION,
+                        (struct curl_slist **) &tlsinfo))
+    return GNUNET_SYSERR;
+  if (CURLSSLBACKEND_GNUTLS != tlsinfo->backend)
   {
-    port = strchr (value, ':');
-    if (NULL != port)
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Unsupported CURL SSL backend %d\n"),
+                tlsinfo->backend);
+    return GNUNET_SYSERR;
+  }
+  chainp = gnutls_certificate_get_peers (tlsinfo->internals, &cert_list_size);
+  if ( (! chainp) || (0 == cert_list_size) )
+    return GNUNET_SYSERR;
+
+  size = sizeof (certdn);
+  /* initialize an X.509 certificate structure. */
+  gnutls_x509_crt_init (&x509_cert);
+  gnutls_x509_crt_import (x509_cert,
+                          chainp,
+                          GNUTLS_X509_FMT_DER);
+
+  if (0 != (rc = gnutls_x509_crt_get_dn_by_oid (x509_cert,
+                                                GNUTLS_OID_X520_COMMON_NAME,
+                                                0, /* the first and only one */
+                                                0 /* no DER encoding */,
+                                                certdn,
+                                                &size)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                _("Failed to fetch CN from cert: %s\n"),
+                gnutls_strerror(rc));
+    gnutls_x509_crt_deinit (x509_cert);
+    return GNUNET_SYSERR;
+  }
+  /* check for TLSA/DANE records */
+#if HAVE_GNUTLS_DANE
+  if (NULL != s5r->dane_data)
+  {
+    char *dd[] = { s5r->dane_data, NULL };
+    int dlen[] = { s5r->dane_data_len, 0};
+    dane_state_t dane_state;
+    dane_query_t dane_query;
+    unsigned int verify;
+
+    /* FIXME: add flags to gnutls to NOT read UNBOUND_ROOT_KEY_FILE here! */
+    if (0 != (rc = dane_state_init (&dane_state,
+#ifdef DANE_F_IGNORE_DNSSEC
+                                    DANE_F_IGNORE_DNSSEC |
+#endif
+                                    DANE_F_IGNORE_LOCAL_RESOLVER)))
     {
-      strncpy (buf, value, port-value);
-      port++;
-      if ((1 != sscanf (port, "%u", &uport)) ||
-           (uport > UINT16_MAX) ||
-           (0 == uport))
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Unable to parse port!\n");
-      else
-        ctask->port = (uint16_t) uport;
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  _("Failed to initialize DANE: %s\n"),
+                  dane_strerror(rc));
+      gnutls_x509_crt_deinit (x509_cert);
+      return GNUNET_SYSERR;
     }
-    else
-      strcpy (buf, value);
-    return MHD_YES;
+    if (0 != (rc = dane_raw_tlsa (dane_state,
+                                  &dane_query,
+                                  dd,
+                                  dlen,
+                                  GNUNET_YES,
+                                  GNUNET_NO)))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  _("Failed to parse DANE record: %s\n"),
+                  dane_strerror(rc));
+      dane_state_deinit (dane_state);
+      gnutls_x509_crt_deinit (x509_cert);
+      return GNUNET_SYSERR;
+    }
+    if (0 != (rc = dane_verify_crt_raw (dane_state,
+                                        chainp,
+                                        cert_list_size,
+                                        gnutls_certificate_type_get (tlsinfo->internals),
+                                        dane_query,
+                                        0, 0,
+                                        &verify)))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  _("Failed to verify TLS connection using DANE: %s\n"),
+                  dane_strerror(rc));
+      dane_query_deinit (dane_query);
+      dane_state_deinit (dane_state);
+      gnutls_x509_crt_deinit (x509_cert);
+      return GNUNET_SYSERR;
+    }
+    if (0 != verify)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  _("Failed DANE verification failed with GnuTLS verify status code: %u\n"),
+                  verify);
+      dane_query_deinit (dane_query);
+      dane_state_deinit (dane_state);
+      gnutls_x509_crt_deinit (x509_cert);
+      return GNUNET_SYSERR;
+    }
+    dane_query_deinit (dane_query);
+    dane_state_deinit (dane_state);
+    /* success! */
   }
-
-  if (0 == strcmp (MHD_HTTP_HEADER_ACCEPT_ENCODING, key))
-    hdr_val = "";
   else
-    hdr_val = value;
-
-  if (0 == strcasecmp (MHD_HTTP_HEADER_CONTENT_TYPE,
-                   key))
-  {
-    if (0 == strncasecmp (value,
-                     MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
-                     strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
-      ctask->post_type = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
-    else if (0 == strncasecmp (value,
-                          MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
-                          strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
-      ctask->post_type = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
+#endif
+  {
+    /* try LEHO or ordinary domain name X509 verification */
+    name = s5r->domain;
+    if (NULL != s5r->leho)
+      name = s5r->leho;
+    if (NULL != name)
+    {
+      if (0 == (rc = gnutls_x509_crt_check_hostname (x509_cert,
+                                                     name)))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    _("SSL certificate subject name (%s) does not match `%s'\n"),
+                    certdn,
+                    name);
+        gnutls_x509_crt_deinit (x509_cert);
+        return GNUNET_SYSERR;
+      }
+    }
     else
-      ctask->post_type = NULL;
-
+    {
+      /* we did not even have the domain name!? */
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
   }
-
-  cstr = GNUNET_malloc (strlen (key) + strlen (hdr_val) + 3);
-  GNUNET_snprintf (cstr, strlen (key) + strlen (hdr_val) + 3,
-                   "%s: %s", key, hdr_val);
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Client Header: %s\n", cstr);
-
-  ctask->headers = curl_slist_append (ctask->headers, cstr);
-  GNUNET_free (cstr);
-
-  return MHD_YES;
+  gnutls_x509_crt_deinit (x509_cert);
+  return GNUNET_OK;
 }
 
 
 /**
- * Callback for MHD response
+ * We're getting an HTTP response header from cURL.  Convert it to the
+ * MHD response headers.  Mostly copies the headers, but makes special
+ * adjustments to "Set-Cookie" and "Location" headers as those may need
+ * to be changed from the LEHO to the domain the browser expects.
  *
- * @param cls closure
- * @param pos in buffer
- * @param buf buffer
- * @param max space in buffer
- * @return number of bytes written
- */
-static ssize_t
-mhd_content_cb (void *cls,
-                uint64_t pos,
-                char* buf,
-                size_t max);
-
-
-/**
- * Check HTTP response header for mime
- *
- * @param buffer curl buffer
+ * @param buffer curl buffer with a single line of header data; not 0-terminated!
  * @param size curl blocksize
  * @param nmemb curl blocknumber
- * @param cls handle
- * @return size of read bytes
+ * @param cls our `struct Socks5Request *`
+ * @return size of processed bytes
  */
 static size_t
 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
 {
+  struct Socks5Request *s5r = cls;
   size_t bytes = size * nmemb;
-  struct ProxyCurlTask *ctask = cls;
-  int cookie_hdr_len = strlen (MHD_HTTP_HEADER_SET_COOKIE);
-  char hdr_generic[bytes+1];
-  char new_cookie_hdr[bytes+strlen (ctask->leho)+1];
-  char new_location[MAX_HTTP_URI_LENGTH+500];
-  char real_host[264];
-  char leho_host[264];
-  char* ndup;
-  char* tok;
-  char* cookie_domain;
-  char* hdr_type;
-  char* hdr_val;
-  int delta_cdomain;
-  size_t offset = 0;
-  char cors_hdr[strlen (ctask->leho) + strlen ("https://")];
-  
-  if (NULL == ctask->response)
-  {
-    /* FIXME: get total size from curl (if available) */
+  char *ndup;
+  const char *hdr_type;
+  const char *cookie_domain;
+  char *hdr_val;
+  long resp_code;
+  char *new_cookie_hdr;
+  char *new_location;
+  size_t offset;
+  size_t delta_cdomain;
+  int domain_matched;
+  char *tok;
+
+  if (NULL == s5r->response)
+  {
+    /* first, check SSL certificate */
+    if ( (HTTPS_PORT == s5r->port) &&
+        (GNUNET_OK != check_ssl_certificate (s5r)) )
+      return 0;
+
+    GNUNET_break (CURLE_OK ==
+                 curl_easy_getinfo (s5r->curl,
+                                    CURLINFO_RESPONSE_CODE,
+                                    &resp_code));
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Creating response for %s\n", ctask->url);
-    ctask->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
-                                                        sizeof (ctask->buffer),
-                                                        &mhd_content_cb,
-                                                        ctask,
-                                                        NULL);
-
-    /* if we have a leho add a CORS header */
-    if (0 != strcmp ("", ctask->leho))
+               "Creating MHD response with code %d\n",
+               (int) resp_code);
+    s5r->response_code = resp_code;
+    s5r->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
+                                                      IO_BUFFERSIZE,
+                                                      &mhd_content_cb,
+                                                      s5r,
+                                                      NULL);
+    if (NULL != s5r->leho)
     {
-      /* We could also allow ssl and http here */
-      if (ctask->mhd->is_ssl)
-        sprintf (cors_hdr, "https://%s", ctask->leho);
-      else
-        sprintf (cors_hdr, "http://%s", ctask->leho);
-
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "MHD: Adding CORS header field %s\n",
-                  cors_hdr);
-
-      if (GNUNET_NO == MHD_add_response_header (ctask->response,
-                                              "Access-Control-Allow-Origin",
-                                              cors_hdr))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "MHD: Error adding CORS header field %s\n",
-                  cors_hdr);
-      }
+      char *cors_hdr;
+
+      GNUNET_asprintf (&cors_hdr,
+                      (HTTPS_PORT == s5r->port)
+                      ? "https://%s"
+                      : "http://%s",
+                      s5r->leho);
+
+      GNUNET_break (MHD_YES ==
+                   MHD_add_response_header (s5r->response,
+                                            MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
+                                            cors_hdr));
+      GNUNET_free (cors_hdr);
     }
-    ctask->ready_to_queue = GNUNET_YES;
+    /* force connection to be closed after each request, as we
+       do not support HTTP pipelining */
+    GNUNET_break (MHD_YES ==
+                 MHD_add_response_header (s5r->response,
+                                          MHD_HTTP_HEADER_CONNECTION,
+                                          "close"));
   }
-  if (cookie_hdr_len > bytes)
+
+  ndup = GNUNET_strndup (buffer, bytes);
+  hdr_type = strtok (ndup, ":");
+  if (NULL == hdr_type)
+  {
+    GNUNET_free (ndup);
+    return bytes;
+  }
+  hdr_val = strtok (NULL, "");
+  if (NULL == hdr_val)
+  {
+    GNUNET_free (ndup);
     return bytes;
+  }
+  if (' ' == *hdr_val)
+    hdr_val++;
 
-  memcpy (hdr_generic, buffer, bytes);
-  hdr_generic[bytes] = '\0';
-  /* remove crlf */
-  if ('\n' == hdr_generic[bytes-1])
-    hdr_generic[bytes-1] = '\0';
+  /* custom logic for certain header types */
+  new_cookie_hdr = NULL;
+  if ( (NULL != s5r->leho) &&
+       (0 == strcasecmp (hdr_type,
+                        MHD_HTTP_HEADER_SET_COOKIE)) )
 
-  if (hdr_generic[bytes-2] == '\r')
-    hdr_generic[bytes-2] = '\0';
-  
-  if (0 == memcmp (hdr_generic,
-                   MHD_HTTP_HEADER_SET_COOKIE,
-                   cookie_hdr_len))
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Looking for cookie in: `%s'\n", hdr_generic);    
-    ndup = GNUNET_strdup (hdr_generic+cookie_hdr_len+1);
-    memset (new_cookie_hdr, 0, sizeof (new_cookie_hdr));
-    for (tok = strtok (ndup, ";"); tok != NULL; tok = strtok (NULL, ";"))
+    new_cookie_hdr = GNUNET_malloc (strlen (hdr_val) +
+                                   strlen (s5r->domain) + 1);
+    offset = 0;
+    domain_matched = GNUNET_NO; /* make sure we match domain at most once */
+    for (tok = strtok (hdr_val, ";"); NULL != tok; tok = strtok (NULL, ";"))
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Got Cookie token: %s\n", tok);
-      //memcpy (new_cookie_hdr+offset, tok, strlen (tok));
-      if (0 == memcmp (tok, " domain", strlen (" domain")))
+      if ( (0 == strncasecmp (tok, " domain", strlen (" domain"))) &&
+          (GNUNET_NO == domain_matched) )
       {
+       domain_matched = GNUNET_YES;
         cookie_domain = tok + strlen (" domain") + 1;
-
-        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                    "Got Set-Cookie Domain: %s\n", cookie_domain);
-
-        if (strlen (cookie_domain) < strlen (ctask->leho))
+        if (strlen (cookie_domain) < strlen (s5r->leho))
         {
-          delta_cdomain = strlen (ctask->leho) - strlen (cookie_domain);
-          if (0 == strcmp (cookie_domain, ctask->leho + (delta_cdomain)))
-          {
-            GNUNET_snprintf (new_cookie_hdr+offset,
-                             sizeof (new_cookie_hdr),
-                             " domain=%s", ctask->authority);
-            offset += strlen (" domain=") + strlen (ctask->authority);
-            new_cookie_hdr[offset] = ';';
-            offset++;
+          delta_cdomain = strlen (s5r->leho) - strlen (cookie_domain);
+          if (0 == strcasecmp (cookie_domain, s5r->leho + delta_cdomain))
+         {
+            offset += sprintf (new_cookie_hdr + offset,
+                              " domain=%s;",
+                              s5r->domain);
             continue;
           }
         }
-        else if (strlen (cookie_domain) == strlen (ctask->leho))
+        else if (0 == strcmp (cookie_domain, s5r->leho))
         {
-          if (0 == strcmp (cookie_domain, ctask->leho))
-          {
-            GNUNET_snprintf (new_cookie_hdr+offset,
-                             sizeof (new_cookie_hdr),
-                             " domain=%s", ctask->host);
-            offset += strlen (" domain=") + strlen (ctask->host);
-            new_cookie_hdr[offset] = ';';
-            offset++;
-            continue;
-          }
+         offset += sprintf (new_cookie_hdr + offset,
+                            " domain=%s;",
+                            s5r->domain);
+         continue;
         }
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Cookie domain invalid\n");
-
-        
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    _("Cookie domain `%s' supplied by server is invalid\n"),
+                   tok);
       }
-      memcpy (new_cookie_hdr+offset, tok, strlen (tok));
+      memcpy (new_cookie_hdr + offset, tok, strlen (tok));
       offset += strlen (tok);
-      new_cookie_hdr[offset] = ';';
-      offset++;
-    }
-    
-    GNUNET_free (ndup);
-
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Got Set-Cookie HTTP header %s\n", new_cookie_hdr);
-
-    if (GNUNET_NO == MHD_add_response_header (ctask->response,
-                                              MHD_HTTP_HEADER_SET_COOKIE,
-                                              new_cookie_hdr))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "MHD: Error adding set-cookie header field %s\n",
-                  hdr_generic+cookie_hdr_len+1);
+      new_cookie_hdr[offset++] = ';';
     }
-    return bytes;
-  }
-
-  ndup = GNUNET_strdup (hdr_generic);
-  hdr_type = strtok (ndup, ":");
-
-  if (NULL == hdr_type)
-  {
-    GNUNET_free (ndup);
-    return bytes;
-  }
-
-  hdr_val = strtok (NULL, "");
-
-  if (NULL == hdr_val)
-  {
-    GNUNET_free (ndup);
-    return bytes;
+    hdr_val = new_cookie_hdr;
   }
 
-  hdr_val++;
-
+  new_location = NULL;
   if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
   {
-    if (ctask->mhd->is_ssl)
-    {
-      sprintf (leho_host, "https://%s", ctask->leho);
-      sprintf (real_host, "https://%s", ctask->host);
-    }
-    else
-    {
-      sprintf (leho_host, "http://%s", ctask->leho);
-      sprintf (real_host, "http://%s", ctask->host);
-    }
+    char *leho_host;
 
-    if (0 == memcmp (leho_host, hdr_val, strlen (leho_host)))
+    GNUNET_asprintf (&leho_host,
+                    (HTTPS_PORT != s5r->port)
+                    ? "http://%s"
+                    : "https://%s",
+                    s5r->leho);
+    if (0 == strncmp (leho_host,
+                     hdr_val,
+                     strlen (leho_host)))
     {
-      sprintf (new_location, "%s%s", real_host, hdr_val+strlen (leho_host));
+      GNUNET_asprintf (&new_location,
+                      "%s%s%s",
+                      (HTTPS_PORT != s5r->port)
+                      ? "http://"
+                      : "https://",
+                      s5r->domain,
+                      hdr_val + strlen (leho_host));
       hdr_val = new_location;
     }
+    GNUNET_free (leho_host);
   }
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Trying to set %s: %s\n",
-              hdr_type,
-              hdr_val);
-  if (GNUNET_NO == MHD_add_response_header (ctask->response,
-                                            hdr_type,
-                                            hdr_val))
+  /* MHD does not allow certain characters in values, remove those */
+  if (NULL != (tok = strchr (hdr_val, '\n')))
+    *tok = '\0';
+  if (NULL != (tok = strchr (hdr_val, '\r')))
+    *tok = '\0';
+  if (NULL != (tok = strchr (hdr_val, '\t')))
+    *tok = '\0';
+  if (0 != strlen (hdr_val))
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "MHD: Error adding %s header field %s\n",
-                hdr_type,
-                hdr_val);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Adding header %s: %s to MHD response\n",
+               hdr_type,
+               hdr_val);
+    GNUNET_break (MHD_YES ==
+                 MHD_add_response_header (s5r->response,
+                                          hdr_type,
+                                          hdr_val));
   }
   GNUNET_free (ndup);
+  GNUNET_free_non_null (new_cookie_hdr);
+  GNUNET_free_non_null (new_location);
   return bytes;
 }
 
 
-
-/**
- * Ask cURL for the select sets and schedule download
- */
-static void
-curl_download_prepare (void);
-
-
 /**
- * Callback to free content
- *
- * @param cls content to free
- * @param tc task context
- */
-static void
-mhd_content_free (void *cls,
-                  const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
-  struct ProxyCurlTask *ctask = cls;
-  struct ProxyUploadData *pdata;
-
-  if (NULL != ctask->headers)
-    curl_slist_free_all (ctask->headers);
-
-  if (NULL != ctask->headers)
-    curl_slist_free_all (ctask->resolver);
-
-  if (NULL != ctask->response)
-    MHD_destroy_response (ctask->response);
-
-  if (NULL != ctask->post_handler)
-    MHD_destroy_post_processor (ctask->post_handler);
-
-  if (GNUNET_SCHEDULER_NO_TASK != ctask->pp_task)
-    GNUNET_SCHEDULER_cancel (ctask->pp_task);
-
-  for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
-  {
-    GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
-                                 ctask->upload_data_tail,
-                                 pdata);
-    GNUNET_free_non_null (pdata->filename);
-    GNUNET_free_non_null (pdata->content_type);
-    GNUNET_free_non_null (pdata->key);
-    GNUNET_free_non_null (pdata->value);
-    GNUNET_free (pdata);
-  }
-  GNUNET_free (ctask);
-}
-
-
-/**
- * Callback for MHD response
- *
- * @param cls closure
- * @param pos in buffer
- * @param buf buffer
- * @param max space in buffer
- * @return number of bytes written
- */
-static ssize_t
-mhd_content_cb (void *cls,
-                uint64_t pos,
-                char* buf,
-                size_t max)
-{
-  struct ProxyCurlTask *ctask = cls;
-  ssize_t copied = 0;
-  size_t bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "MHD: content cb for %s. To copy: %u\n",
-              ctask->url, (unsigned int) bytes_to_copy);
-  if ((GNUNET_YES == ctask->download_is_finished) &&
-      (GNUNET_NO == ctask->download_error) &&
-      (0 == bytes_to_copy))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "MHD: sending response for %s\n", ctask->url);
-    ctask->download_in_progress = GNUNET_NO;
-    run_mhd_now (ctask->mhd);
-    GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
-    return MHD_CONTENT_READER_END_OF_STREAM;
-  }
-  
-  if ((GNUNET_YES == ctask->download_error) &&
-      (GNUNET_YES == ctask->download_is_finished) &&
-      (0 == bytes_to_copy))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "MHD: sending error response\n");
-    ctask->download_in_progress = GNUNET_NO;
-    run_mhd_now (ctask->mhd);
-    GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
-    return MHD_CONTENT_READER_END_WITH_ERROR;
-  }
-
-  if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
-    return 0;
-  
-  copied = 0;
-  bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "MHD: copied: %d left: %u, space left in buf: %d\n",
-              copied,
-              (unsigned int) bytes_to_copy, (int) (max - copied));
-  
-  if (GNUNET_NO == ctask->download_is_finished)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "MHD: Purging buffer\n");
-    memmove (ctask->buffer, ctask->buffer_read_ptr, bytes_to_copy);
-    ctask->buffer_read_ptr = ctask->buffer;
-    ctask->buffer_write_ptr = ctask->buffer + bytes_to_copy;
-    ctask->buffer[bytes_to_copy] = '\0';
-  }
-  
-  if (bytes_to_copy + copied > max)
-    bytes_to_copy = max - copied;
-  memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
-  ctask->buffer_read_ptr += bytes_to_copy;
-  copied += bytes_to_copy;
-  ctask->buf_status = BUF_WAIT_FOR_CURL;
-  
-  if (NULL != ctask->curl)
-    curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "MHD: copied %d bytes\n", (int) copied);
-  run_mhd_now (ctask->mhd);
-  return copied;
-}
-
-
-/**
- * Handle data from cURL
+ * Handle response payload data from cURL.  Copies it into our `io_buf` to make
+ * it available to MHD.
  *
  * @param ptr pointer to the data
  * @param size number of blocks of data
  * @param nmemb blocksize
- * @param ctx the curlproxytask
+ * @param ctx our `struct Socks5Request *`
  * @return number of bytes handled
  */
 static size_t
 curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx)
 {
-  const char *cbuf = ptr;
+  struct Socks5Request *s5r = ctx;
   size_t total = size * nmemb;
-  struct ProxyCurlTask *ctask = ctx;
-  size_t buf_space = sizeof (ctask->buffer) - (ctask->buffer_write_ptr - ctask->buffer);
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "CURL: Got %d. %d free in buffer\n",
-              (int) total,
-             (int) buf_space);
-  if (0 == buf_space)
+  if ( (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) ||
+       (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
   {
-    ctask->buf_status = BUF_WAIT_FOR_MHD;
-    run_mhd_now (ctask->mhd);
-    return CURL_WRITEFUNC_PAUSE;
+    /* we're still not done with the upload, do not yet
+       start the download, the IO buffer is still full
+       with upload data. */
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Pausing CURL download, waiting for UPLOAD to finish\n");
+    return CURL_WRITEFUNC_PAUSE; /* not yet ready for data download */
   }
-  if (total > buf_space)
-    total = buf_space;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "CURL: Copying %d bytes to buffer (%s)\n", 
-             total, ctask->url);
-  memcpy (ctask->buffer_write_ptr, cbuf, total);
-  ctask->bytes_in_buffer += total;
-  ctask->buffer_write_ptr += total;
-  if (ctask->bytes_in_buffer > 0)
+  if (sizeof (s5r->io_buf) - s5r->io_len < total)
   {
-    ctask->buf_status = BUF_WAIT_FOR_MHD;
-    run_mhd_now (ctask->mhd);
-  }
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Pausing CURL download, not enough space\n");
+    return CURL_WRITEFUNC_PAUSE; /* not enough space */
+  }
+  memcpy (&s5r->io_buf[s5r->io_len],
+         ptr,
+         total);
+  s5r->io_len += total;
+  if (s5r->io_len == total)
+    run_mhd_now (s5r->hd);
   return total;
 }
 
 
 /**
- * cURL callback for put data
+ * cURL callback for uploaded (PUT/POST) data.  Copies it into our `io_buf`
+ * to make it available to MHD.
+ *
+ * @param buf where to write the data
+ * @param size number of bytes per member
+ * @param nmemb number of members available in @a buf
+ * @param cls our `struct Socks5Request` that generated the data
+ * @return number of bytes copied to @a buf
  */
 static size_t
-put_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
+curl_upload_cb (void *buf, size_t size, size_t nmemb, void *cls)
 {
-  struct ProxyCurlTask *ctask = cls;
-  struct ProxyUploadData *pdata = ctask->upload_data_head;
+  struct Socks5Request *s5r = cls;
   size_t len = size * nmemb;
   size_t to_copy;
-  char* pos;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "CURL: put read callback\n");
 
-  if (NULL == pdata)
-    return CURL_READFUNC_PAUSE;
-  
-  //fin
-  if (NULL == pdata->value)
+  if ( (0 == s5r->io_len) &&
+       (SOCKS5_SOCKET_UPLOAD_DONE != s5r->state) )
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "CURL: Terminating PUT\n");
-
-    GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
-                                 ctask->upload_data_tail,
-                                 pdata);
-    GNUNET_free (pdata);
-    return 0;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "CURL: read callback value %s\n", pdata->value); 
-  
-  to_copy = pdata->bytes_left;
-  if (to_copy > len)
-    to_copy = len;
-  
-  pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
-  memcpy (buf, pos, to_copy);
-  pdata->bytes_left -= to_copy;
-  if (pdata->bytes_left <= 0)
-  {
-    GNUNET_free (pdata->value);
-    GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
-                                 ctask->upload_data_tail,
-                                 pdata);
-    GNUNET_free (pdata);
-  }
-  return to_copy;
-}
-
-
-/**
- * cURL callback for post data
- */
-static size_t
-post_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
-{
-  struct ProxyCurlTask *ctask = cls;
-  struct ProxyUploadData *pdata = ctask->upload_data_head;
-  size_t len = size * nmemb;
-  size_t to_copy;
-  char* pos;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "CURL: read callback\n");
-
-  if (NULL == pdata)
+               "Pausing CURL UPLOAD, need more data\n");
     return CURL_READFUNC_PAUSE;
-  
-  //fin
-  if (NULL == pdata->value)
+  }
+  if ( (0 == s5r->io_len) &&
+       (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
   {
+    s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "CURL: Terminating POST data\n");
-
-    GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
-                                 ctask->upload_data_tail,
-                                 pdata);
-    GNUNET_free (pdata);
-    return 0;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "CURL: read callback value %s\n", pdata->value); 
-  
-  to_copy = pdata->bytes_left;
-  if (to_copy > len)
-    to_copy = len;
-  
-  pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
-  memcpy (buf, pos, to_copy);
-  pdata->bytes_left -= to_copy;
-  if (pdata->bytes_left <= 0)
-  {
-    GNUNET_free (pdata->value);
-    GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
-                                 ctask->upload_data_tail,
-                                 pdata);
-    GNUNET_free (pdata);
+               "Completed CURL UPLOAD\n");
+    return 0; /* upload finished, can now download */
   }
+  if ( (SOCKS5_SOCKET_UPLOAD_STARTED != s5r->state) ||
+       (SOCKS5_SOCKET_UPLOAD_DONE != s5r->state) )
+  {
+    GNUNET_break (0);
+    return CURL_READFUNC_ABORT;
+  }
+  to_copy = GNUNET_MIN (s5r->io_len,
+                       len);
+  memcpy (buf, s5r->io_buf, to_copy);
+  memmove (s5r->io_buf,
+          &s5r->io_buf[to_copy],
+          s5r->io_len - to_copy);
+  s5r->io_len -= to_copy;
+  if (s5r->io_len + to_copy == sizeof (s5r->io_buf))
+    run_mhd_now (s5r->hd); /* got more space for upload now */
   return to_copy;
 }
 
 
+/* ************************** main loop of cURL interaction ****************** */
+
+
 /**
  * Task that is run when we are ready to receive more data
  * from curl
@@ -1692,7 +1251,7 @@ curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
 
 
 /**
- * Ask cURL for the select sets and schedule download
+ * Ask cURL for the select() sets and schedule cURL operations.
  */
 static void
 curl_download_prepare ()
@@ -1707,6 +1266,11 @@ curl_download_prepare ()
   long to;
   struct GNUNET_TIME_Relative rtime;
 
+  if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
+  {
+    GNUNET_SCHEDULER_cancel (curl_download_task);
+    curl_download_task = GNUNET_SCHEDULER_NO_TASK;
+  }
   max = -1;
   FD_ZERO (&rs);
   FD_ZERO (&ws);
@@ -1717,311 +1281,169 @@ curl_download_prepare ()
                 "%s failed at %s:%d: `%s'\n",
                 "curl_multi_fdset", __FILE__, __LINE__,
                 curl_multi_strerror (mret));
-    //TODO cleanup here?
     return;
   }
   to = -1;
   GNUNET_break (CURLM_OK == curl_multi_timeout (curl_multi, &to));
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "cURL multi fds: max=%d timeout=%lld\n", max, (long long) to);
   if (-1 == to)
     rtime = GNUNET_TIME_UNIT_FOREVER_REL;
   else
     rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
-  grs = GNUNET_NETWORK_fdset_create ();
-  gws = GNUNET_NETWORK_fdset_create ();
-  GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
-  GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
-  if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
-    GNUNET_SCHEDULER_cancel (curl_download_task);  
   if (-1 != max)
   {
-    curl_download_task =
-      GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
-                                   rtime,
-                                   grs, gws,
-                                   &curl_task_download, curl_multi);
+    grs = GNUNET_NETWORK_fdset_create ();
+    gws = GNUNET_NETWORK_fdset_create ();
+    GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
+    GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
+    curl_download_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+                                                     rtime,
+                                                     grs, gws,
+                                                     &curl_task_download, curl_multi);
+    GNUNET_NETWORK_fdset_destroy (gws);
+    GNUNET_NETWORK_fdset_destroy (grs);
   }
-  else if (NULL != ctasks_head)
+  else
   {
-    /* as specified in curl docs */
-    curl_download_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
+    curl_download_task = GNUNET_SCHEDULER_add_delayed (rtime,
                                                        &curl_task_download,
                                                        curl_multi);
   }
-  GNUNET_NETWORK_fdset_destroy (gws);
-  GNUNET_NETWORK_fdset_destroy (grs);
 }
 
 
 /**
- * Task that is run when we are ready to receive more data
- * from curl
+ * Task that is run when we are ready to receive more data from curl.
  *
- * @param cls closure
+ * @param cls closure, NULL
  * @param tc task context
  */
 static void
-curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+curl_task_download (void *cls,
+                   const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   int running;
   int msgnum;
   struct CURLMsg *msg;
   CURLMcode mret;
-  struct ProxyCurlTask *ctask;
-  int num_ctasks;
-  long resp_code;
-  struct ProxyCurlTask *clean_head = NULL;
-  struct ProxyCurlTask *clean_tail = NULL;
+  struct Socks5Request *s5r;
 
   curl_download_task = GNUNET_SCHEDULER_NO_TASK;
-
-  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Shutdown requested while trying to download\n");
-    //TODO cleanup
-    return;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Ready to dl\n");
-
   do
   {
     running = 0;
-    num_ctasks = 0;
-    
     mret = curl_multi_perform (curl_multi, &running);
-
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Running curl tasks: %d\n", running);
-    for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
+    while (NULL != (msg = curl_multi_info_read (curl_multi, &msgnum)))
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "CTask: %s\n", ctask->url);
-      num_ctasks++;
-    }
-
-    do
-    {
-      
-      msg = curl_multi_info_read (curl_multi, &msgnum);
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Messages left: %d\n", msgnum);
-      
-      if (msg == NULL)
-        break;
-      switch (msg->msg)
+      GNUNET_break (CURLE_OK ==
+                   curl_easy_getinfo (msg->easy_handle,
+                                      CURLINFO_PRIVATE,
+                                      (char **) &s5r ));
+      if (NULL == s5r)
       {
-       case CURLMSG_DONE:
-         if ((msg->data.result != CURLE_OK) &&
-             (msg->data.result != CURLE_GOT_NOTHING))
-         {
-           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                       "Download curl failed");
-            
-           for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
-           {
-             if (NULL == ctask->curl)
-               continue;
-
-             if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
-               continue;
-             
-             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                         "CURL: Download failed for task %s: %s.\n",
-                         ctask->url,
-                         curl_easy_strerror (msg->data.result));
-             ctask->download_is_finished = GNUNET_YES;
-             ctask->download_error = GNUNET_YES;
-             if (CURLE_OK == curl_easy_getinfo (ctask->curl,
-                                                CURLINFO_RESPONSE_CODE,
-                                                &resp_code))
-               ctask->curl_response_code = resp_code;
-             ctask->ready_to_queue = MHD_YES;
-             ctask->buf_status = BUF_WAIT_FOR_MHD;
-             run_mhd_now (ctask->mhd);
-             
-             GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
-                                          ctask);
-             GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
-             break;
-           }
-           GNUNET_assert (ctask != NULL);
-         }
-         else
-         {
-           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                       "CURL: download completed.\n");
-
-           for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
-           {
-             if (NULL == ctask->curl)
-               continue;
-
-             if (0 != memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)))
-               continue;
-             
-             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                         "CURL: completed task %s found.\n", ctask->url);
-             if (CURLE_OK == curl_easy_getinfo (ctask->curl,
-                                                CURLINFO_RESPONSE_CODE,
-                                                &resp_code))
-               ctask->curl_response_code = resp_code;
-
-
-             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                         "CURL: Completed ctask!\n");
-             if (GNUNET_SCHEDULER_NO_TASK == ctask->pp_task)
-            {
-              ctask->buf_status = BUF_WAIT_FOR_MHD;
-              run_mhd_now (ctask->mhd);
-             }
-
-             ctask->ready_to_queue = MHD_YES;
-             ctask->download_is_finished = GNUNET_YES;
-
-             /* We MUST not modify the multi handle else we loose messages */
-             GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
-                                          ctask);
-             GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
-
-             break;
-           }
-           GNUNET_assert (ctask != NULL);
-         }
-         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                     "CURL: %s\n", curl_easy_strerror(msg->data.result));
-         break;
-       default:
-         GNUNET_assert (0);
-         break;
+       GNUNET_break (0);
+       continue;
       }
-    } while (msgnum > 0);
-
-    for (ctask=clean_head; NULL != ctask; ctask = ctask->next)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "CURL: Removing task %s.\n", ctask->url);
-      curl_multi_remove_handle (curl_multi, ctask->curl);
-      curl_easy_cleanup (ctask->curl);
-      ctask->curl = NULL;
-    }
-    
-    num_ctasks=0;
-    for (ctask=ctasks_head; NULL != ctask; ctask = ctask->next)    
-      num_ctasks++; 
-    GNUNET_assert (num_ctasks == running);
-
-  } while (mret == CURLM_CALL_MULTI_PERFORM);
-  
-  if (mret != CURLM_OK)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CURL: %s failed at %s:%d: `%s'\n",
-                "curl_multi_perform", __FILE__, __LINE__,
-                curl_multi_strerror (mret));
-  }
-  curl_download_prepare();
-}
-
-
-/**
- * Process LEHO lookup
- *
- * @param cls the ctask
- * @param rd_count number of records returned
- * @param rd record data
- */
-static void
-process_leho_lookup (void *cls,
-                     uint32_t rd_count,
-                     const struct GNUNET_NAMESTORE_RecordData *rd)
-{
-  struct ProxyCurlTask *ctask = cls;
-  char hosthdr[262]; //256 + "Host: "
-  int i;
-  CURLcode ret;
-  CURLMcode mret;
-  struct hostent *phost;
-  char *ssl_ip;
-  char resolvename[512];
-  char curlurl[512];
-
-  
-  strcpy (ctask->leho, "");
-
-  if (rd_count == 0)
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "No LEHO present!\n");
-
-  for (i=0; i<rd_count; i++)
-  {
-    if (rd[i].record_type != GNUNET_NAMESTORE_TYPE_LEHO)
-      continue;
-
-    memcpy (ctask->leho, rd[i].data, rd[i].data_size);
-
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Found LEHO %s for %s\n", ctask->leho, ctask->url);
-  }
-
-  if (0 != strcmp (ctask->leho, ""))
-  {
-    sprintf (hosthdr, "%s%s:%d", "Host: ", ctask->leho, ctask->port);
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "New HTTP header value: %s\n", hosthdr);
-    ctask->headers = curl_slist_append (ctask->headers, hosthdr);
-    GNUNET_assert (NULL != ctask->headers);
-    if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers)))
-      LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
-  }
-
-  if (ctask->mhd->is_ssl)
-  {
-    phost = (struct hostent*)gethostbyname (ctask->host);
-
-    if (phost!=NULL)
-    {
-      ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "SSL target server: %s\n", ssl_ip);
-      sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Curl resolve: %s\n", resolvename);
-      ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
-      if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver)))
-       LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
-      sprintf (curlurl, "https://%s:%d%s", ctask->leho, ctask->port, ctask->url);
-      if (CURLE_OK != (ret = curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl)))
-       LOG_CURL_EASY(GNUNET_ERROR_TYPE_WARNING,"curl_easy_setopt",ret);
-    }
-    else
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "gethostbyname failed for %s!\n",
-                 ctask->host);
-      ctask->download_is_finished = GNUNET_YES;
-      ctask->download_error = GNUNET_YES;
-      return;
-    }
-  }
-
-  if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
-  {
+      switch (msg->msg)
+      {
+      case CURLMSG_NONE:
+       /* documentation says this is not used */
+       GNUNET_break (0);
+       break;
+      case CURLMSG_DONE:
+       switch (msg->data.result)
+       {
+       case CURLE_OK:
+       case CURLE_GOT_NOTHING:
+         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                     "CURL download completed.\n");
+         s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE;
+         run_mhd_now (s5r->hd);
+         break;
+       default:
+         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                     "Download curl failed: %s\n",
+                     curl_easy_strerror (msg->data.result));
+         /* FIXME: indicate error somehow? close MHD connection badly as well? */
+         s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE;
+         run_mhd_now (s5r->hd);
+         break;
+       }
+       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                   "Cleaning up cURL handle\n");
+       curl_multi_remove_handle (curl_multi, s5r->curl);
+       curl_easy_cleanup (s5r->curl);
+       s5r->curl = NULL;
+       if (NULL == s5r->response)
+         s5r->response = curl_failure_response;
+       break;
+      case CURLMSG_LAST:
+       /* documentation says this is not used */
+       GNUNET_break (0);
+       break;
+      default:
+       /* unexpected status code */
+       GNUNET_break (0);
+       break;
+      }
+    };
+  } while (mret == CURLM_CALL_MULTI_PERFORM);
+  if (CURLM_OK != mret)
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "%s failed at %s:%d: `%s'\n",
-                "curl_multi_add_handle", __FILE__, __LINE__,
+               "%s failed at %s:%d: `%s'\n",
+                "curl_multi_perform", __FILE__, __LINE__,
                 curl_multi_strerror (mret));
-    ctask->download_is_finished = GNUNET_YES;
-    ctask->download_error = GNUNET_YES;
-    return;
+  if (0 == running)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Suspending cURL multi loop, no more events pending\n");
+    return; /* nothing more in progress */
   }
-  GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
-
   curl_download_prepare ();
 }
 
 
+/* ********************************* MHD response generation ******************* */
+
+
+/**
+ * Read HTTP request header field from the request.  Copies the fields
+ * over to the 'headers' that will be given to curl.  However, 'Host'
+ * is substituted with the LEHO if present.  We also change the
+ * 'Connection' header value to "close" as the proxy does not support
+ * pipelining.
+ *
+ * @param cls our `struct Socks5Request`
+ * @param kind value kind
+ * @param key field key
+ * @param value field value
+ * @return MHD_YES to continue to iterate
+ */
+static int
+con_val_iter (void *cls,
+              enum MHD_ValueKind kind,
+              const char *key,
+              const char *value)
+{
+  struct Socks5Request *s5r = cls;
+  char *hdr;
+
+  if ( (0 == strcasecmp (MHD_HTTP_HEADER_HOST, key)) &&
+       (NULL != s5r->leho) )
+    value = s5r->leho;
+  if (0 == strcasecmp (MHD_HTTP_HEADER_CONNECTION, key))
+    value = "Close";
+  GNUNET_asprintf (&hdr,
+                  "%s: %s",
+                  key,
+                  value);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Adding HEADER `%s' to HTTP request\n",
+             hdr);
+  s5r->headers = curl_slist_append (s5r->headers,
+                                   hdr);
+  GNUNET_free (hdr);
+  return MHD_YES;
+}
 
 
 /**
@@ -2043,8 +1465,8 @@ process_leho_lookup (void *cls,
  *        @a upload_data provided; the method must update this
  *        value to the number of bytes NOT processed;
  * @param con_cls pointer to location where we store the 'struct Request'
- * @return #MHD_YES if the connection was handled successfully,
- *         #MHD_NO if the socket must be closed due to a serious
+ * @return MHD_YES if the connection was handled successfully,
+ *         MHD_NO if the socket must be closed due to a serious
  *         error while handling the request
  */
 static int
@@ -2057,246 +1479,202 @@ create_response (void *cls,
                  size_t *upload_data_size,
                  void **con_cls)
 {
-  struct MhdHttpList* hd = cls;  
-  char curlurl[MAX_HTTP_URI_LENGTH]; // buffer overflow!
-  int ret = MHD_YES;
-  int i;
-  struct ProxyCurlTask *ctask = *con_cls;
-  struct ProxyUploadData *fin_post;
-  struct curl_forms forms[5];
-  struct ProxyUploadData *upload_data_iter;
-  
-  //FIXME handle
-  if ((0 != strcasecmp (meth, MHD_HTTP_METHOD_GET)) &&
-      (0 != strcasecmp (meth, MHD_HTTP_METHOD_PUT)) &&
-      (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST)) &&
-      (0 != strcasecmp (meth, MHD_HTTP_METHOD_HEAD)))
+  struct Socks5Request *s5r = *con_cls;
+  char *curlurl;
+  char ipstring[INET6_ADDRSTRLEN];
+  char ipaddr[INET6_ADDRSTRLEN + 2];
+  const struct sockaddr *sa;
+  const struct sockaddr_in *s4;
+  const struct sockaddr_in6 *s6;
+  uint16_t port;
+  size_t left;
+
+  if (NULL == s5r)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "MHD: %s NOT IMPLEMENTED!\n", meth);
+    GNUNET_break (0);
     return MHD_NO;
   }
-
-
-  if (GNUNET_NO == ctask->accepted)
+  if ( (NULL == s5r->curl) &&
+       (SOCKS5_SOCKET_WITH_MHD == s5r->state) )
   {
+    /* first time here, initialize curl handle */
+    sa = (const struct sockaddr *) &s5r->destination_address;
+    switch (sa->sa_family)
+    {
+    case AF_INET:
+      s4 = (const struct sockaddr_in *) &s5r->destination_address;
+      if (NULL == inet_ntop (AF_INET,
+                            &s4->sin_addr,
+                            ipstring,
+                            sizeof (ipstring)))
+      {
+       GNUNET_break (0);
+       return MHD_NO;
+      }
+      GNUNET_snprintf (ipaddr,
+                      sizeof (ipaddr),
+                      "%s",
+                      ipstring);
+      port = ntohs (s4->sin_port);
+      break;
+    case AF_INET6:
+      s6 = (const struct sockaddr_in6 *) &s5r->destination_address;
+      if (NULL == inet_ntop (AF_INET6,
+                            &s6->sin6_addr,
+                            ipstring,
+                            sizeof (ipstring)))
+      {
+       GNUNET_break (0);
+       return MHD_NO;
+      }
+      GNUNET_snprintf (ipaddr,
+                      sizeof (ipaddr),
+                      "[%s]",
+                      ipstring);
+      port = ntohs (s6->sin6_port);
+      break;
+    default:
+      GNUNET_break (0);
+      return MHD_NO;
+    }
+    s5r->curl = curl_easy_init ();
+    if (NULL == s5r->curl)
+      return MHD_queue_response (con,
+                                MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                curl_failure_response);
+    curl_easy_setopt (s5r->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
+    curl_easy_setopt (s5r->curl, CURLOPT_HEADERDATA, s5r);
+    curl_easy_setopt (s5r->curl, CURLOPT_FOLLOWLOCATION, 0);
+    curl_easy_setopt (s5r->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+    curl_easy_setopt (s5r->curl, CURLOPT_CONNECTTIMEOUT, 600L);
+    curl_easy_setopt (s5r->curl, CURLOPT_TIMEOUT, 600L);
+    curl_easy_setopt (s5r->curl, CURLOPT_NOSIGNAL, 1L);
+    curl_easy_setopt (s5r->curl, CURLOPT_HTTP_CONTENT_DECODING, 0);
+    curl_easy_setopt (s5r->curl, CURLOPT_HTTP_TRANSFER_DECODING, 0);
+    curl_easy_setopt (s5r->curl, CURLOPT_NOSIGNAL, 1L);
+    curl_easy_setopt (s5r->curl, CURLOPT_PRIVATE, s5r);
+    curl_easy_setopt (s5r->curl, CURLOPT_VERBOSE, 0);
+    GNUNET_asprintf (&curlurl,
+                    (HTTPS_PORT != s5r->port)
+                    ? "http://%s:%d%s"
+                    : "https://%s:%d%s",
+                    ipaddr,
+                    port,
+                    s5r->url);
+    curl_easy_setopt (s5r->curl,
+                     CURLOPT_URL,
+                     curlurl);
+    GNUNET_free (curlurl);
 
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Got %s request for %s\n", meth, url);
-    ctask->mhd = hd;
-    ctask->curl = curl_easy_init();
-    ctask->curl_running = GNUNET_NO;
-    if (NULL == ctask->curl)
+    if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
+    {
+      s5r->state = SOCKS5_SOCKET_UPLOAD_STARTED;
+      curl_easy_setopt (s5r->curl, CURLOPT_UPLOAD, 1);
+      curl_easy_setopt (s5r->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
+      curl_easy_setopt (s5r->curl, CURLOPT_WRITEDATA, s5r);
+      curl_easy_setopt (s5r->curl, CURLOPT_READFUNCTION, &curl_upload_cb);
+      curl_easy_setopt (s5r->curl, CURLOPT_READDATA, s5r);
+    }
+    else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
+    {
+      s5r->state = SOCKS5_SOCKET_UPLOAD_STARTED;
+      curl_easy_setopt (s5r->curl, CURLOPT_POST, 1);
+      curl_easy_setopt (s5r->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
+      curl_easy_setopt (s5r->curl, CURLOPT_WRITEDATA, s5r);
+      curl_easy_setopt (s5r->curl, CURLOPT_READFUNCTION, &curl_upload_cb);
+      curl_easy_setopt (s5r->curl, CURLOPT_READDATA, s5r);
+    }
+    else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
     {
-      ret = MHD_queue_response (con,
-                                MHD_HTTP_OK,
-                                curl_failure_response);
-      GNUNET_free (ctask);
-      return ret;
+      s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
+      curl_easy_setopt (s5r->curl, CURLOPT_NOBODY, 1);
+    }
+    else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_GET))
+    {
+      s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
+      curl_easy_setopt (s5r->curl, CURLOPT_HTTPGET, 1);
+      curl_easy_setopt (s5r->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
+      curl_easy_setopt (s5r->curl, CURLOPT_WRITEDATA, s5r);
     }
-    
-    if (ctask->mhd->is_ssl)
-      ctask->port = HTTPS_PORT;
     else
-      ctask->port = HTTP_PORT;
-
-    MHD_get_connection_values (con,
-                               MHD_HEADER_KIND,
-                               &con_val_iter, ctask);
-    
-    curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
-    curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
-    curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
-    curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
-    curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 0);
-    curl_easy_setopt (ctask->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
-
-    if (GNUNET_NO == ctask->mhd->is_ssl)
     {
-      sprintf (curlurl, "http://%s:%d%s", ctask->host, ctask->port, ctask->url);
-      curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                 _("Unsupported HTTP method `%s'\n"),
+                 meth);
+      curl_easy_cleanup (s5r->curl);
+      s5r->curl = NULL;
+      return MHD_NO;
     }
-    
-
-    curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
-    curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
-    curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
-    
-    /* Add GNS header */
-    ctask->headers = curl_slist_append (ctask->headers,
-                                          "GNS: YES");
-    ctask->accepted = GNUNET_YES;
-    ctask->download_in_progress = GNUNET_YES;
-    ctask->buf_status = BUF_WAIT_FOR_CURL;
-    ctask->connection = con;
-    ctask->curl_response_code = MHD_HTTP_OK;
-    ctask->buffer_read_ptr = ctask->buffer;
-    ctask->buffer_write_ptr = ctask->buffer;
-    ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
-    
 
-    if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
+    if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_0))
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Setting up PUT\n");
-      
-      curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1);
-      curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
-      curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &put_read_callback);
-      ctask->headers = curl_slist_append (ctask->headers,
-                                          "Transfer-Encoding: chunked");
+      curl_easy_setopt (s5r->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
     }
-
-    if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
+    else if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_1))
     {
-      //FIXME handle multipart
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Setting up POST processor\n");
-      ctask->post_handler = MHD_create_post_processor (con,
-                                                      POSTBUFFERSIZE,
-                                                      &con_post_data_iter,
-                                                      ctask);
-      ctask->headers = curl_slist_append (ctask->headers,
-                                         "Transfer-Encoding: chunked");
-      return MHD_YES;
+      curl_easy_setopt (s5r->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
     }
-
-    if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
+    else
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Setting NOBODY\n");
-      curl_easy_setopt (ctask->curl, CURLOPT_NOBODY, 1);
+      curl_easy_setopt (s5r->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE);
     }
 
-    
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "MHD: Adding new curl task for %s\n", ctask->host);
-#if 0
-    GNUNET_GNS_get_authority (gns_handle,
-                              ctask->host,
-                              &process_get_authority,
-                              ctask);
-#endif
-    ctask->ready_to_queue = GNUNET_NO;
-    ctask->fin = GNUNET_NO;
-    ctask->curl_running = GNUNET_YES;
-    return MHD_YES;
-  }
-
-  ctask = (struct ProxyCurlTask *) *con_cls;
-  if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
-  {
-    if (0 != *upload_data_size)
+    if (HTTPS_PORT == s5r->port)
     {
-      
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Invoking POST processor\n");
-      MHD_post_process (ctask->post_handler,
-                        upload_data, *upload_data_size);
-      *upload_data_size = 0;
-      if ((GNUNET_NO == ctask->is_httppost) &&
-          (GNUNET_NO == ctask->curl_running))
-      {
-        curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
-        curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION,
-                          &post_read_callback);
-        curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
-        
-        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "MHD: Adding new curl task for %s\n", ctask->host);
-#if 0
-        GNUNET_GNS_get_authority (gns_handle,
-                                  ctask->host,
-                                  &process_get_authority,
-                                  ctask);
-#endif
-        ctask->ready_to_queue = GNUNET_NO;
-        ctask->fin = GNUNET_NO;
-        ctask->curl_running = GNUNET_YES;
-      }
-      return MHD_YES;
+      curl_easy_setopt (s5r->curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
+      curl_easy_setopt (s5r->curl, CURLOPT_SSL_VERIFYPEER, 1L);
+      /* Disable cURL checking the hostname, as we will check ourselves
+        as only we have the domain name or the LEHO or the DANE record */
+      curl_easy_setopt (s5r->curl, CURLOPT_SSL_VERIFYHOST, 0L);
     }
-    else if (GNUNET_NO == ctask->post_done)
+    else
     {
-      if (GNUNET_YES == ctask->is_httppost)
-      {
-        for (upload_data_iter = ctask->upload_data_head;
-             NULL != upload_data_iter;
-             upload_data_iter = upload_data_iter->next)
-        {
-          i = 0;
-          if (NULL != upload_data_iter->filename)
-          {
-            forms[i].option = CURLFORM_FILENAME;
-            forms[i].value = upload_data_iter->filename;
-            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                        "Adding filename %s\n",
-                        forms[i].value);
-            i++;
-          }
-          if (NULL != upload_data_iter->content_type)
-          {
-            forms[i].option = CURLFORM_CONTENTTYPE;
-            forms[i].value = upload_data_iter->content_type;
-            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                        "Adding content type %s\n",
-                        forms[i].value);
-            i++;
-          }
-          forms[i].option = CURLFORM_PTRCONTENTS;
-          forms[i].value = upload_data_iter->value;
-          forms[i+1].option = CURLFORM_END;
-
-          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                      "Adding formdata for %s (len=%lld)\n",
-                      upload_data_iter->key,
-                      upload_data_iter->total_bytes);
-
-          curl_formadd(&ctask->httppost, &ctask->httppost_last,
-                       CURLFORM_COPYNAME, upload_data_iter->key,
-                       CURLFORM_CONTENTSLENGTH, upload_data_iter->total_bytes,
-                       CURLFORM_ARRAY, forms,
-                       CURLFORM_END);
-        }
-        curl_easy_setopt (ctask->curl, CURLOPT_HTTPPOST,
-                          ctask->httppost);
-
-        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "MHD: Adding new curl task for %s\n", ctask->host);
-#if 0
-        GNUNET_GNS_get_authority (gns_handle,
-                                  ctask->host,
-                                  &process_get_authority,
-                                  ctask);
-#endif
-        ctask->ready_to_queue = GNUNET_YES;
-        ctask->fin = GNUNET_NO;
-        ctask->curl_running = GNUNET_YES;
-        ctask->post_done = GNUNET_YES;
-        return MHD_YES;
-      }
+      curl_easy_setopt (s5r->curl, CURLOPT_USE_SSL, CURLUSESSL_NONE);
+    }
 
-      fin_post = GNUNET_new (struct ProxyUploadData);
-      GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
-                                        ctask->upload_data_tail,
-                                        fin_post);
-      ctask->post_done = GNUNET_YES;
-      return MHD_YES;
+    if (CURLM_OK != curl_multi_add_handle (curl_multi, s5r->curl))
+    {
+      GNUNET_break (0);
+      curl_easy_cleanup (s5r->curl);
+      s5r->curl = NULL;
+      return MHD_NO;
     }
-  }
-  
-  if (GNUNET_YES != ctask->ready_to_queue)
-    return MHD_YES; /* wait longer */
-  
-  if (GNUNET_YES == ctask->fin)
+    MHD_get_connection_values (con,
+                              MHD_HEADER_KIND,
+                              &con_val_iter, s5r);
+    curl_easy_setopt (s5r->curl, CURLOPT_HTTPHEADER, s5r->headers);
+    curl_download_prepare ();
     return MHD_YES;
+  }
 
-  ctask->fin = GNUNET_YES;
+  /* continuing to process request */
+  if (0 != *upload_data_size)
+  {
+    left = GNUNET_MIN (*upload_data_size,
+                      sizeof (s5r->io_buf) - s5r->io_len);
+    memcpy (&s5r->io_buf[s5r->io_len],
+           upload_data,
+           left);
+    s5r->io_len += left;
+    *upload_data_size -= left;
+    GNUNET_assert (NULL != s5r->curl);
+    curl_easy_pause (s5r->curl, CURLPAUSE_CONT);
+    curl_download_prepare ();
+    return MHD_YES;
+  }
+  if (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Finished processing UPLOAD\n");
+    s5r->state = SOCKS5_SOCKET_UPLOAD_DONE;
+  }
+  if (NULL == s5r->response)
+    return MHD_YES; /* too early to queue response, did not yet get headers from cURL */
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "MHD: Queueing response for %s\n", ctask->url);
-  ret = MHD_queue_response (con, ctask->curl_response_code, ctask->response);
-  run_mhd_now (ctask->mhd);
-  return ret;
+             "Queueing response with MHD\n");
+  return MHD_queue_response (con,
+                            s5r->response_code,
+                            s5r->response);
 }
 
 
@@ -2309,7 +1687,7 @@ create_response (void *cls,
  * @param cls NULL
  * @param connection connection handle
  * @param con_cls value as set by the last call to
- *        the #MHD_AccessHandlerCallback, should be our `struct Socks5Request`
+ *        the MHD_AccessHandlerCallback, should be our `struct Socks5Request`
  * @param toe reason for request termination (ignored)
  */
 static void
@@ -2322,8 +1700,12 @@ mhd_completed_cb (void *cls,
 
   if (NULL == s5r)
     return;
+  if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+               "MHD encountered error handling request: %d\n",
+               toe);
   cleanup_s5r (s5r);
-  *con_cls = NULL;  
+  *con_cls = NULL;
 }
 
 
@@ -2331,7 +1713,7 @@ mhd_completed_cb (void *cls,
  * Function called when MHD first processes an incoming connection.
  * Gives us the respective URI information.
  *
- * We use this to associate the `struct MHD_Connection` with our 
+ * We use this to associate the `struct MHD_Connection` with our
  * internal `struct Socks5Request` data structure (by checking
  * for matching sockets).
  *
@@ -2341,7 +1723,7 @@ mhd_completed_cb (void *cls,
  * @return the `struct Socks5Request` that this @a connection is for
  */
 static void *
-mhd_log_callback (void *cls, 
+mhd_log_callback (void *cls,
                  const char *url,
                  struct MHD_Connection *connection)
 {
@@ -2351,7 +1733,7 @@ mhd_log_callback (void *cls,
 
   ci = MHD_get_connection_info (connection,
                                MHD_CONNECTION_INFO_CONNECTION_FD);
-  if (NULL == ci) 
+  if (NULL == ci)
   {
     GNUNET_break (0);
     return NULL;
@@ -2367,9 +1749,12 @@ mhd_log_callback (void *cls,
        return NULL;
       }
       s5r->url = GNUNET_strdup (url);
+      GNUNET_SCHEDULER_cancel (s5r->timeout_task);
+      s5r->timeout_task = GNUNET_SCHEDULER_NO_TASK;
       return s5r;
     }
   }
+  GNUNET_break (0);
   return NULL;
 }
 
@@ -2388,7 +1773,10 @@ kill_httpd (struct MhdHttpList *hd)
   GNUNET_free_non_null (hd->domain);
   MHD_stop_daemon (hd->daemon);
   if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
+  {
     GNUNET_SCHEDULER_cancel (hd->httpd_task);
+    hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
+  }
   GNUNET_free_non_null (hd->proxy_cert);
   if (hd == httpd)
     httpd = NULL;
@@ -2407,7 +1795,8 @@ kill_httpd_task (void *cls,
                 const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct MhdHttpList *hd = cls;
-  
+
+  hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
   kill_httpd (hd);
 }
 
@@ -2505,8 +1894,8 @@ do_httpd (void *cls,
           const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct MhdHttpList *hd = cls;
-  
-  hd->httpd_task = GNUNET_SCHEDULER_NO_TASK; 
+
+  hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
   MHD_run (hd->daemon);
   schedule_httpd (hd);
 }
@@ -2520,10 +1909,10 @@ do_httpd (void *cls,
 static void
 run_mhd_now (struct MhdHttpList *hd)
 {
-  if (GNUNET_SCHEDULER_NO_TASK != 
+  if (GNUNET_SCHEDULER_NO_TASK !=
       hd->httpd_task)
     GNUNET_SCHEDULER_cancel (hd->httpd_task);
-  hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, 
+  hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd,
                                             hd);
 }
 
@@ -2536,7 +1925,7 @@ run_mhd_now (struct MhdHttpList *hd)
  * @return NULL on error
  */
 static void*
-load_file (const char* filename, 
+load_file (const char* filename,
           unsigned int* size)
 {
   void *buffer;
@@ -2567,13 +1956,15 @@ load_file (const char* filename,
  * @return #GNUNET_OK on success
  */
 static int
-load_key_from_file (gnutls_x509_privkey_t key, 
+load_key_from_file (gnutls_x509_privkey_t key,
                    const char* keyfile)
 {
   gnutls_datum_t key_data;
   int ret;
 
   key_data.data = load_file (keyfile, &key_data.size);
+  if (NULL == key_data.data)
+    return GNUNET_SYSERR;
   ret = gnutls_x509_privkey_import (key, &key_data,
                                     GNUTLS_X509_FMT_PEM);
   if (GNUTLS_E_SUCCESS != ret)
@@ -2581,9 +1972,8 @@ load_key_from_file (gnutls_x509_privkey_t key,
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 _("Unable to import private key from file `%s'\n"),
                keyfile);
-    GNUNET_break (0);
   }
-  GNUNET_free (key_data.data);
+  GNUNET_free_non_null (key_data.data);
   return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
 }
 
@@ -2596,22 +1986,23 @@ load_key_from_file (gnutls_x509_privkey_t key,
  * @return #GNUNET_OK on success
  */
 static int
-load_cert_from_file (gnutls_x509_crt_t crt, 
+load_cert_from_file (gnutls_x509_crt_t crt,
                     const char* certfile)
 {
   gnutls_datum_t cert_data;
   int ret;
 
   cert_data.data = load_file (certfile, &cert_data.size);
+  if (NULL == cert_data.data)
+    return GNUNET_SYSERR;
   ret = gnutls_x509_crt_import (crt, &cert_data,
                                 GNUTLS_X509_FMT_PEM);
   if (GNUTLS_E_SUCCESS != ret)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("Unable to import certificate %s\n"), certfile);
-    GNUNET_break (0);
   }
-  GNUNET_free (cert_data.data);
+  GNUNET_free_non_null (cert_data.data);
   return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
 }
 
@@ -2633,14 +2024,14 @@ generate_gns_certificate (const char *name)
   struct tm *tm_data;
   struct ProxyGNSCertificate *pgc;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
-             "Generating TLS/SSL certificate for `%s'\n", 
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Generating TLS/SSL certificate for `%s'\n",
              name);
   GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_init (&request));
   GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
   pgc = GNUNET_new (struct ProxyGNSCertificate);
   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
-                                 0, "TNR", 2);
+                                 0, "ZZ", 2);
   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
                                  0, "GNU Name System", 4);
   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
@@ -2651,15 +2042,15 @@ generate_gns_certificate (const char *name)
                              &serial,
                              sizeof (serial));
   etime = time (NULL);
-  tm_data = localtime (&etime);  
+  tm_data = localtime (&etime);
   gnutls_x509_crt_set_activation_time (request,
                                       etime);
   tm_data->tm_year++;
   etime = mktime (tm_data);
   gnutls_x509_crt_set_expiration_time (request,
                                       etime);
-  gnutls_x509_crt_sign (request, 
-                       proxy_ca.cert, 
+  gnutls_x509_crt_sign (request,
+                       proxy_ca.cert,
                        proxy_ca.key);
   key_buf_size = sizeof (pgc->key);
   cert_buf_size = sizeof (pgc->cert);
@@ -2672,11 +2063,27 @@ generate_gns_certificate (const char *name)
 }
 
 
+/**
+ * Function called by MHD with errors, suppresses them all.
+ *
+ * @param cls closure
+ * @param fm format string (`printf()`-style)
+ * @param ap arguments to @a fm
+ */
+static void
+mhd_error_log_callback (void *cls,
+                        const char *fm,
+                        va_list ap)
+{
+  /* do nothing */
+}
+
+
 /**
  * Lookup (or create) an SSL MHD instance for a particular domain.
  *
  * @param domain the domain the SSL daemon has to serve
- * @return NULL on errro
+ * @return NULL on error
  */
 static struct MhdHttpList *
 lookup_ssl_httpd (const char* domain)
@@ -2684,16 +2091,22 @@ lookup_ssl_httpd (const char* domain)
   struct MhdHttpList *hd;
   struct ProxyGNSCertificate *pgc;
 
+  if (NULL == domain)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
   for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
-    if (0 == strcmp (hd->domain, domain))
+    if ( (NULL != hd->domain) &&
+        (0 == strcmp (hd->domain, domain)) )
       return hd;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Starting fresh MHD HTTPS instance for domain `%s'\n",
              domain);
-  pgc = generate_gns_certificate (domain);   
+  pgc = generate_gns_certificate (domain);
   hd = GNUNET_new (struct MhdHttpList);
   hd->is_ssl = GNUNET_YES;
-  hd->domain = GNUNET_strdup (domain); 
+  hd->domain = GNUNET_strdup (domain);
   hd->proxy_cert = pgc;
   hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_NO_LISTEN_SOCKET,
                                 0,
@@ -2702,6 +2115,7 @@ lookup_ssl_httpd (const char* domain)
                                 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
                                 MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL,
                                 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
+                                 MHD_OPTION_EXTERNAL_LOGGER, &mhd_error_log_callback, NULL,
                                 MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
                                 MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
                                 MHD_OPTION_END);
@@ -2711,8 +2125,8 @@ lookup_ssl_httpd (const char* domain)
     GNUNET_free (hd);
     return NULL;
   }
-  GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, 
-                              mhd_httpd_tail, 
+  GNUNET_CONTAINER_DLL_insert (mhd_httpd_head,
+                              mhd_httpd_tail,
                               hd);
   return hd;
 }
@@ -2737,31 +2151,9 @@ timeout_s5r_handshake (void *cls,
 }
 
 
-/**
- * Checks if name is in given @a tld.
- *
- * @param name the name to check 
- * @param tld the TLD to check for (must NOT begin with ".")
- * @return #GNUNET_YES or #GNUNET_NO
- */
-static int
-is_tld (const char* name, const char* tld)
-{
-  size_t name_len = strlen (name);
-  size_t tld_len = strlen (tld);
-
-  GNUNET_break ('.' != tld[0]);
-  return ( (tld_len < name_len) &&
-          ( ('.' == name[name_len - tld_len - 1]) || (name_len == tld_len) ) &&
-          (0 == memcmp (tld,
-                        name + (name_len - tld_len),
-                        tld_len)) );
-}
-
-
 /**
  * We're done with the Socks5 protocol, now we need to pass the
- * connection data through to the final destination, either 
+ * connection data through to the final destination, either
  * direct (if the protocol might not be HTTP), or via MHD
  * (if the port looks like it should be HTTP).
  *
@@ -2775,60 +2167,41 @@ setup_data_transfer (struct Socks5Request *s5r)
   const struct sockaddr *addr;
   socklen_t len;
 
-  if (is_tld (s5r->domain, GNUNET_GNS_TLD) ||
-      is_tld (s5r->domain, GNUNET_GNS_TLD_ZKEY))
-  {
-    /* GNS TLD, setup MHD server for destination */
-    switch (s5r->port)
-    {
-    case HTTPS_PORT:
-      hd = lookup_ssl_httpd (s5r->domain);
-      if (NULL == hd)
-      {
-       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                   _("Failed to start HTTPS server for `%s'\n"),
-                   s5r->domain);
-       cleanup_s5r (s5r);
-       return;
-      }
-      break;
-    case HTTP_PORT:
-      GNUNET_assert (NULL == httpd);
-      hd = httpd;
-      break;
-    default:
-      hd = NULL; /* netcat */
-      break;
-    }
-  }
-  else
-  {
-    hd = NULL; /* netcat */
-  }
-  if (NULL != hd)
+  switch (s5r->port)
   {
-    fd = GNUNET_NETWORK_get_fd (s5r->sock);
-    addr = GNUNET_NETWORK_get_addr (s5r->sock);
-    len = GNUNET_NETWORK_get_addrlen (s5r->sock);
-    s5r->state = SOCKS5_SOCKET_WITH_MHD;
-    if (MHD_YES != MHD_add_connection (hd->daemon, fd, addr, len))
+  case HTTPS_PORT:
+    hd = lookup_ssl_httpd (s5r->domain);
+    if (NULL == hd)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                 _("Failed to pass client to MHD\n"));
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("Failed to start HTTPS server for `%s'\n"),
+                 s5r->domain);
       cleanup_s5r (s5r);
       return;
     }
-    schedule_httpd (hd);
-    s5r->timeout_task = GNUNET_SCHEDULER_add_delayed (HTTP_HANDSHAKE_TIMEOUT,
-                                                     &timeout_s5r_handshake,
-                                                     s5r);
+    break;
+  case HTTP_PORT:
+  default:
+    GNUNET_assert (NULL != httpd);
+    hd = httpd;
+    break;
   }
-  else
+  fd = GNUNET_NETWORK_get_fd (s5r->sock);
+  addr = GNUNET_NETWORK_get_addr (s5r->sock);
+  len = GNUNET_NETWORK_get_addrlen (s5r->sock);
+  s5r->state = SOCKS5_SOCKET_WITH_MHD;
+  if (MHD_YES != MHD_add_connection (hd->daemon, fd, addr, len))
   {
-    // FIXME: not implemented
-    GNUNET_break (0);
-    /* start netcat mode here! */
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("Failed to pass client to MHD\n"));
+    cleanup_s5r (s5r);
+    return;
   }
+  s5r->hd = hd;
+  schedule_httpd (hd);
+  s5r->timeout_task = GNUNET_SCHEDULER_add_delayed (HTTP_HANDSHAKE_TIMEOUT,
+                                                   &timeout_s5r_handshake,
+                                                   s5r);
 }
 
 
@@ -2855,7 +2228,7 @@ do_write (void *cls,
   if (len <= 0)
   {
     /* write error: connection closed, shutdown, etc.; just clean up */
-    cleanup_s5r (s5r); 
+    cleanup_s5r (s5r);
     return;
   }
   memmove (s5r->wbuf,
@@ -2876,10 +2249,10 @@ do_write (void *cls,
 
   switch (s5r->state)
   {
-  case SOCKS5_INIT:    
+  case SOCKS5_INIT:
     GNUNET_assert (0);
     break;
-  case SOCKS5_REQUEST:    
+  case SOCKS5_REQUEST:
     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s5r->rtask);
     break;
   case SOCKS5_DATA_TRANSFER:
@@ -2913,7 +2286,7 @@ signal_socks_failure (struct Socks5Request *s5r,
   s_resp->reply = sc;
   s5r->state = SOCKS5_WRITE_THEN_CLEANUP;
   if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
-    s5r->wtask = 
+    s5r->wtask =
       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
                                      s5r->sock,
                                      &do_write, s5r);
@@ -2936,34 +2309,34 @@ signal_socks_success (struct Socks5Request *s5r)
   s_resp->reserved = 0;
   s_resp->addr_type = SOCKS5_AT_IPV4;
   /* zero out IPv4 address and port */
-  memset (&s_resp[1], 
-         0, 
+  memset (&s_resp[1],
+         0,
          sizeof (struct in_addr) + sizeof (uint16_t));
   s5r->wbuf_len += sizeof (struct Socks5ServerResponseMessage) +
-    sizeof (struct in_addr) + sizeof (uint16_t);  
-  if (GNUNET_SCHEDULER_NO_TASK == s5r->wtask)      
+    sizeof (struct in_addr) + sizeof (uint16_t);
+  if (GNUNET_SCHEDULER_NO_TASK == s5r->wtask)
     s5r->wtask =
       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
                                      s5r->sock,
-                                     &do_write, s5r); 
+                                     &do_write, s5r);
 }
 
 
 /**
  * Process GNS results for target domain.
  *
- * @param cls the ctask
+ * @param cls the `struct Socks5Request`
  * @param rd_count number of records returned
  * @param rd record data
  */
 static void
 handle_gns_result (void *cls,
                   uint32_t rd_count,
-                  const struct GNUNET_NAMESTORE_RecordData *rd)
+                  const struct GNUNET_GNSRECORD_Data *rd)
 {
   struct Socks5Request *s5r = cls;
   uint32_t i;
-  const struct GNUNET_NAMESTORE_RecordData *r;
+  const struct GNUNET_GNSRECORD_Data *r;
   int got_ip;
 
   s5r->gns_lookup = NULL;
@@ -2984,7 +2357,7 @@ handle_gns_result (void *cls,
        }
        if (GNUNET_YES == got_ip)
          break;
-       if (GNUNET_OK != 
+       if (GNUNET_OK !=
            GNUNET_NETWORK_test_pf (PF_INET))
          break;
        got_ip = GNUNET_YES;
@@ -2999,7 +2372,7 @@ handle_gns_result (void *cls,
 #endif
       }
       break;
-    case GNUNET_DNSPARSER_TYPE_AAAA: 
+    case GNUNET_DNSPARSER_TYPE_AAAA:
       {
        struct sockaddr_in6 *in;
 
@@ -3009,8 +2382,8 @@ handle_gns_result (void *cls,
          break;
        }
        if (GNUNET_YES == got_ip)
-         break; 
-       if (GNUNET_OK != 
+         break;
+       if (GNUNET_OK !=
            GNUNET_NETWORK_test_pf (PF_INET))
          break;
        /* FIXME: allow user to disable IPv6 per configuration option... */
@@ -3025,15 +2398,23 @@ handle_gns_result (void *cls,
        in->sin6_len = sizeof (*in);
 #endif
       }
-      break;      
-    case GNUNET_NAMESTORE_TYPE_VPN:
+      break;
+    case GNUNET_GNSRECORD_TYPE_VPN:
       GNUNET_break (0); /* should have been translated within GNS */
       break;
-    case GNUNET_NAMESTORE_TYPE_LEHO:
+    case GNUNET_GNSRECORD_TYPE_LEHO:
       GNUNET_free_non_null (s5r->leho);
       s5r->leho = GNUNET_strndup (r->data,
                                  r->data_size);
       break;
+    case GNUNET_DNSPARSER_TYPE_TLSA:
+      GNUNET_free_non_null (s5r->dane_data);
+      s5r->dane_data_len = r->data_size;
+      s5r->dane_data = GNUNET_malloc (r->data_size);
+      memcpy (s5r->dane_data,
+              r->data,
+              r->data_size);
+      break;
     default:
       /* don't care */
       break;
@@ -3041,14 +2422,14 @@ handle_gns_result (void *cls,
   }
   if (GNUNET_YES != got_ip)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Name resolution failed to yield useful IP address.\n");
     signal_socks_failure (s5r,
                          SOCKS5_STATUS_GENERAL_FAILURE);
     return;
   }
   s5r->state = SOCKS5_DATA_TRANSFER;
-  signal_socks_success (s5r);  
+  signal_socks_success (s5r);
 }
 
 
@@ -3091,12 +2472,12 @@ do_s5r_read (void *cls,
   if ( (NULL != tc->read_ready) &&
        (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) )
   {
-    rlen = GNUNET_NETWORK_socket_recv (s5r->sock, 
+    rlen = GNUNET_NETWORK_socket_recv (s5r->sock,
                                       &s5r->rbuf[s5r->rbuf_len],
                                       sizeof (s5r->rbuf) - s5r->rbuf_len);
     if (rlen <= 0)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                  "socks5 client disconnected.\n");
       cleanup_s5r (s5r);
       return;
@@ -3164,6 +2545,14 @@ do_s5r_read (void *cls,
        struct sockaddr_in *in;
 
        s5r->port = ntohs (*port);
+        if (HTTPS_PORT == s5r->port)
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      _("SSL connection to plain IPv4 address requested\n"));
+          signal_socks_failure (s5r,
+                                SOCKS5_STATUS_CONNECTION_NOT_ALLOWED_BY_RULE);
+          return;
+        }
        alen = sizeof (struct in_addr);
        if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
            alen + sizeof (uint16_t))
@@ -3185,6 +2574,14 @@ do_s5r_read (void *cls,
        struct sockaddr_in6 *in;
 
        s5r->port = ntohs (*port);
+        if (HTTPS_PORT == s5r->port)
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      _("SSL connection to plain IPv4 address requested\n"));
+          signal_socks_failure (s5r,
+                                SOCKS5_STATUS_CONNECTION_NOT_ALLOWED_BY_RULE);
+          return;
+        }
        alen = sizeof (struct in6_addr);
        if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
            alen + sizeof (uint16_t))
@@ -3203,8 +2600,8 @@ do_s5r_read (void *cls,
       {
        const uint8_t *dom_len;
        const char *dom_name;
-       const uint16_t *port;   
-       
+       const uint16_t *port;
+
        dom_len = (const uint8_t *) &c_req[1];
        alen = *dom_len + 1;
        if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
@@ -3226,7 +2623,7 @@ do_s5r_read (void *cls,
                                             GNUNET_NO /* only cached */,
                                             (GNUNET_YES == do_shorten) ? &local_shorten_zone : NULL,
                                             &handle_gns_result,
-                                            s5r);                                           
+                                            s5r);
        break;
       }
     default:
@@ -3246,7 +2643,7 @@ do_s5r_read (void *cls,
       GNUNET_break_op (0);
       signal_socks_failure (s5r,
                            SOCKS5_STATUS_GENERAL_FAILURE);
-      return;      
+      return;
     }
     if (SOCKS5_DATA_TRANSFER == s5r->state)
     {
@@ -3255,7 +2652,7 @@ do_s5r_read (void *cls,
     }
     /* We are done reading right now */
     GNUNET_SCHEDULER_cancel (s5r->rtask);
-    s5r->rtask = GNUNET_SCHEDULER_NO_TASK;    
+    s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
     return;
   case SOCKS5_RESOLVING:
     GNUNET_assert (0);
@@ -3273,19 +2670,31 @@ do_s5r_read (void *cls,
 /**
  * Accept new incoming connections
  *
- * @param cls the closure
+ * @param cls the closure with the lsock4 or lsock6
  * @param tc the scheduler context
  */
 static void
-do_accept (void *cls, 
+do_accept (void *cls,
           const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
+  struct GNUNET_NETWORK_Handle *lsock = cls;
   struct GNUNET_NETWORK_Handle *s;
   struct Socks5Request *s5r;
 
-  ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                         lsock,
-                                         &do_accept, NULL);
+  if (lsock == lsock4)
+    ltask4 = GNUNET_SCHEDULER_NO_TASK;
+  else
+    ltask6 = GNUNET_SCHEDULER_NO_TASK;
+  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+    return;
+  if (lsock == lsock4)
+    ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                            lsock,
+                                            &do_accept, lsock);
+  else
+    ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                            lsock,
+                                            &do_accept, lsock);
   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
   if (NULL == s)
   {
@@ -3319,54 +2728,21 @@ static void
 do_shutdown (void *cls,
              const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  struct ProxyCurlTask *ctask;
-  struct ProxyCurlTask *ctask_tmp;
-  struct ProxyUploadData *pdata;
-  
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Shutting down...\n");
   while (NULL != mhd_httpd_head)
     kill_httpd (mhd_httpd_head);
-  for (ctask=ctasks_head; NULL != ctask; ctask=ctask_tmp)
+  while (NULL != s5r_head)
+    cleanup_s5r (s5r_head);
+  if (NULL != lsock4)
   {
-    ctask_tmp = ctask->next;
-    if (NULL != ctask->curl)
-    {
-      curl_easy_cleanup (ctask->curl);
-      ctask->curl = NULL;
-    }
-    if (NULL != ctask->headers)
-    {
-      curl_slist_free_all (ctask->headers);
-      ctask->headers = NULL;
-    }
-    if (NULL != ctask->resolver)
-    {
-      curl_slist_free_all (ctask->resolver);
-      ctask->resolver = NULL;
-    }
-    if (NULL != ctask->response)
-    {
-      MHD_destroy_response (ctask->response);
-      ctask->response = NULL;
-    }    
-    for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
-    {
-      GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
-                                   ctask->upload_data_tail,
-                                   pdata);
-      GNUNET_free_non_null (pdata->filename);
-      GNUNET_free_non_null (pdata->content_type);
-      GNUNET_free_non_null (pdata->key);
-      GNUNET_free_non_null (pdata->value);
-      GNUNET_free (pdata);
-    }
-    GNUNET_free (ctask);
+    GNUNET_NETWORK_socket_close (lsock4);
+    lsock4 = NULL;
   }
-  if (NULL != lsock)
+  if (NULL != lsock6)
   {
-    GNUNET_NETWORK_socket_close (lsock);
-    lsock = NULL;
+    GNUNET_NETWORK_socket_close (lsock6);
+    lsock6 = NULL;
   }
   if (NULL != id_op)
   {
@@ -3393,57 +2769,143 @@ do_shutdown (void *cls,
     GNUNET_SCHEDULER_cancel (curl_download_task);
     curl_download_task = GNUNET_SCHEDULER_NO_TASK;
   }
-  if (GNUNET_SCHEDULER_NO_TASK != ltask)
+  if (GNUNET_SCHEDULER_NO_TASK != ltask4)
   {
-    GNUNET_SCHEDULER_cancel (ltask);
-    ltask = GNUNET_SCHEDULER_NO_TASK;
+    GNUNET_SCHEDULER_cancel (ltask4);
+    ltask4 = GNUNET_SCHEDULER_NO_TASK;
   }
+  if (GNUNET_SCHEDULER_NO_TASK != ltask6)
+  {
+    GNUNET_SCHEDULER_cancel (ltask6);
+    ltask6 = GNUNET_SCHEDULER_NO_TASK;
+  }
+  gnutls_x509_crt_deinit (proxy_ca.cert);
+  gnutls_x509_privkey_deinit (proxy_ca.key);
   gnutls_global_deinit ();
 }
 
 
 /**
- * Continue initialization after we have our zone information.
+ * Create an IPv4 listen socket bound to our port.
+ *
+ * @return NULL on error
  */
-static void 
-run_cont () 
+static struct GNUNET_NETWORK_Handle *
+bind_v4 ()
 {
-  struct MhdHttpList *hd;
-  struct sockaddr_in sa;
+  struct GNUNET_NETWORK_Handle *ls;
+  struct sockaddr_in sa4;
+  int eno;
 
-  /* Open listen socket for socks proxy */
-  /* FIXME: support IPv6! */
-  memset (&sa, 0, sizeof (sa));
-  sa.sin_family = AF_INET;
-  sa.sin_port = htons (port);
+  memset (&sa4, 0, sizeof (sa4));
+  sa4.sin_family = AF_INET;
+  sa4.sin_port = htons (port);
 #if HAVE_SOCKADDR_IN_SIN_LEN
-  sa.sin_len = sizeof (sa);
+  sa4.sin_len = sizeof (sa4);
 #endif
-  lsock = GNUNET_NETWORK_socket_create (AF_INET,
-                                       SOCK_STREAM,
-                                       0);
-  if (NULL == lsock) 
+  ls = GNUNET_NETWORK_socket_create (AF_INET,
+                                     SOCK_STREAM,
+                                     0);
+  if (NULL == ls)
+    return NULL;
+  if (GNUNET_OK !=
+      GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa4,
+                                 sizeof (sa4)))
   {
-    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
-    GNUNET_SCHEDULER_shutdown ();
-    return;
+    eno = errno;
+    GNUNET_NETWORK_socket_close (ls);
+    errno = eno;
+    return NULL;
   }
+  return ls;
+}
+
+
+/**
+ * Create an IPv6 listen socket bound to our port.
+ *
+ * @return NULL on error
+ */
+static struct GNUNET_NETWORK_Handle *
+bind_v6 ()
+{
+  struct GNUNET_NETWORK_Handle *ls;
+  struct sockaddr_in6 sa6;
+  int eno;
+
+  memset (&sa6, 0, sizeof (sa6));
+  sa6.sin6_family = AF_INET6;
+  sa6.sin6_port = htons (port);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+  sa6.sin6_len = sizeof (sa6);
+#endif
+  ls = GNUNET_NETWORK_socket_create (AF_INET6,
+                                     SOCK_STREAM,
+                                     0);
+  if (NULL == ls)
+    return NULL;
   if (GNUNET_OK !=
-      GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
-                                 sizeof (sa), 0))
+      GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa6,
+                                 sizeof (sa6)))
+  {
+    eno = errno;
+    GNUNET_NETWORK_socket_close (ls);
+    errno = eno;
+    return NULL;
+  }
+  return ls;
+}
+
+
+/**
+ * Continue initialization after we have our zone information.
+ */
+static void
+run_cont ()
+{
+  struct MhdHttpList *hd;
+
+  /* Open listen socket for socks proxy */
+  lsock6 = bind_v6 ();
+  if (NULL == lsock6)
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
+  else
   {
+    if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock6, 5))
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
+      GNUNET_NETWORK_socket_close (lsock6);
+      lsock6 = NULL;
+    }
+    else
+    {
+      ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                              lsock6, &do_accept, lsock6);
+    }
+  }
+  lsock4 = bind_v4 ();
+  if (NULL == lsock4)
     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
-    GNUNET_SCHEDULER_shutdown ();
-    return;
+  else
+  {
+    if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock4, 5))
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
+      GNUNET_NETWORK_socket_close (lsock4);
+      lsock4 = NULL;
+    }
+    else
+    {
+      ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                              lsock4, &do_accept, lsock4);
+    }
   }
-  if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
+  if ( (NULL == lsock4) &&
+       (NULL == lsock6) )
   {
-    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
+    GNUNET_SCHEDULER_shutdown ();
     return;
   }
-  ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                         lsock, &do_accept, NULL);
-
   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -3476,7 +2938,7 @@ run_cont ()
 }
 
 
-/** 
+/**
  * Method called to inform about the egos of the shorten zone of this peer.
  *
  * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
@@ -3484,7 +2946,7 @@ run_cont ()
  * @a ego does indicate an error (i.e. name is taken or no default
  * value is known).  If @a ego is non-NULL and if '*ctx'
  * is set in those callbacks, the value WILL be passed to a subsequent
- * call to the identity callback of #GNUNET_IDENTITY_connect (if 
+ * call to the identity callback of #GNUNET_IDENTITY_connect (if
  * that one was not NULL).
  *
  * @param cls closure, NULL
@@ -3516,7 +2978,7 @@ identity_shorten_cb (void *cls,
 }
 
 
-/** 
+/**
  * Method called to inform about the egos of the master zone of this peer.
  *
  * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
@@ -3524,7 +2986,7 @@ identity_shorten_cb (void *cls,
  * @a ego does indicate an error (i.e. name is taken or no default
  * value is known).  If @a ego is non-NULL and if '*ctx'
  * is set in those callbacks, the value WILL be passed to a subsequent
- * call to the identity callback of #GNUNET_IDENTITY_connect (if 
+ * call to the identity callback of #GNUNET_IDENTITY_connect (if
  * that one was not NULL).
  *
  * @param cls closure, NULL
@@ -3545,14 +3007,15 @@ identity_master_cb (void *cls,
   if (NULL == ego)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-               _("No ego configured for `master-zone`\n"));
+               _("No ego configured for `%s`\n"),
+               "gns-proxy");
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
   GNUNET_IDENTITY_ego_get_public_key (ego,
                                      &local_gns_zone);
   id_op = GNUNET_IDENTITY_get (identity,
-                              "shorten-zone",
+                              "gns-short",
                               &identity_shorten_cb,
                               NULL);
 }
@@ -3574,12 +3037,13 @@ run (void *cls, char *const *args, const char *cfgfile,
   char* cafile;
 
   cfg = c;
+
   if (NULL == (curl_multi = curl_multi_init ()))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Failed to create cURL multi handle!\n");
     return;
-  } 
+  }
   cafile = cafile_opt;
   if (NULL == cafile)
   {
@@ -3596,19 +3060,21 @@ run (void *cls, char *const *args, const char *cfgfile,
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Using %s as CA\n", cafile);
-  
+
   gnutls_global_init ();
   gnutls_x509_crt_init (&proxy_ca.cert);
   gnutls_x509_privkey_init (&proxy_ca.key);
-  
+
   if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
        (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                _("Failed to load SSL/TLS key and certificate from `%s'\n"),
                cafile);
-    // FIXME: release resources...
-    GNUNET_free_non_null (cafile_cfg);  
+    gnutls_x509_crt_deinit (proxy_ca.cert);
+    gnutls_x509_privkey_deinit (proxy_ca.key);
+    gnutls_global_deinit ();
+    GNUNET_free_non_null (cafile_cfg);
     return;
   }
   GNUNET_free_non_null (cafile_cfg);
@@ -3616,14 +3082,17 @@ run (void *cls, char *const *args, const char *cfgfile,
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Unable to connect to GNS!\n");
+    gnutls_x509_crt_deinit (proxy_ca.cert);
+    gnutls_x509_privkey_deinit (proxy_ca.key);
+    gnutls_global_deinit ();
     return;
   }
   identity = GNUNET_IDENTITY_connect (cfg,
-                                     NULL, NULL);  
+                                     NULL, NULL);
   id_op = GNUNET_IDENTITY_get (identity,
-                              "master-zone",
+                              "gns-proxy",
                               &identity_master_cb,
-                              NULL);  
+                              NULL);
   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
                                 &do_shutdown, NULL);
 }
@@ -3648,7 +3117,7 @@ main (int argc, char *const *argv)
       &GNUNET_GETOPT_set_string, &cafile_opt},
     GNUNET_GETOPT_OPTION_END
   };
-  static const char* page = 
+  static const char* page =
     "<html><head><title>gnunet-gns-proxy</title>"
     "</head><body>cURL fail</body></html>";
   int ret;
@@ -3668,6 +3137,7 @@ main (int argc, char *const *argv)
                            &run, NULL)) ? 0 : 1;
   MHD_destroy_response (curl_failure_response);
   GNUNET_free_non_null ((char *) argv);
+  GNUNET_CRYPTO_ecdsa_key_clear (&local_shorten_zone);
   return ret;
 }