-fix #3037: support IPv6
[oweals/gnunet.git] / src / gns / gnunet-gns-proxy.c
index a6fc47e947cb09f8972b4ba4f3dc59fee8866bd8..a28ab98dcdb34891f96abac7da4f544838886bdf 100644 (file)
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     (C) 2012 Christian Grothoff (and other contributing authors)
+     (C) 2012-2013 Christian Grothoff (and other contributing authors)
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      Boston, MA 02111-1307, USA.
 */
-
+/**
+ * @author Martin Schanzenbach
+ * @author Christian Grothoff
+ * @file src/gns/gnunet-gns-proxy.c
+ * @brief HTTP(S) proxy that rewrites URIs and fakes certificats to make GNS work
+ *        with legacy browsers
+ *
+ * TODO:
+ * - double-check queueing logic
+ * - actually check SSL certificates (#3038)
+ */
 #include "platform.h"
-#include <gnunet_util_lib.h>
-#include <gnunet_gns_service.h>
 #include <microhttpd.h>
 #include <curl/curl.h>
-#include <regex.h>
-#include "gns_proxy_proto.h"
-#include "gns.h"
-
-/** SSL **/
 #include <gnutls/gnutls.h>
 #include <gnutls/x509.h>
 #include <gnutls/abstract.h>
 #include <gnutls/crypto.h>
-#include <time.h>
+#include <regex.h>
+#include "gnunet_util_lib.h"
+#include "gnunet_gns_service.h"
+#include "gnunet_identity_service.h"
+#include "gns.h"
+
 
+/**
+ * Default Socks5 listen port.
+ */
 #define GNUNET_GNS_PROXY_PORT 7777
-#define MHD_MAX_CONNECTIONS 300
-#define MAX_HTTP_URI_LENGTH 2048
-#define POSTBUFFERSIZE 4096
 
-/* MHD/cURL defines */
-#define BUF_WAIT_FOR_CURL 0
-#define BUF_WAIT_FOR_MHD 1
-#define BUF_WAIT_FOR_PP 2
-#define HTML_HDR_CONTENT "Content-Type: text/html"
+/**
+ * Maximum supported length for a URI.
+ * Should die. @deprecated
+ */
+#define MAX_HTTP_URI_LENGTH 2048
 
-/* buffer padding for proper RE matching */
-#define CURL_BUF_PADDING 1000
+/**
+ * Size of the buffer for the data upload / download.  Must be
+ * enough for curl, thus CURL_MAX_WRITE_SIZE is needed here (16k).
+ */
+#define IO_BUFFERSIZE CURL_MAX_WRITE_SIZE
 
-/* regexp */
-//#define RE_DOTPLUS "<a href=\"http://(([A-Za-z]+[.])+)([+])"
-#define RE_A_HREF  "href=\"https?://(([A-Za-z0-9]+[.])+)([+]|[a-z]+)"
-#define RE_N_MATCHES 4
+/**
+ * Size of the read/write buffers for Socks.   Uses
+ * 256 bytes for the hostname (at most), plus a few
+ * bytes overhead for the messages.
+ */
+#define SOCKS_BUFFERSIZE (256 + 32)
 
-/* The usual suspects */
+/**
+ * Port for plaintext HTTP.
+ */
 #define HTTP_PORT 80
-#define HTTPS_PORT 443
-
 
 /**
- * A structure for CA cert/key
+ * Port for HTTPS.
  */
-struct ProxyCA
-{
-  /* The certificate */
-  gnutls_x509_crt_t cert;
-
-  /* The private key */
-  gnutls_x509_privkey_t key;
-};
+#define HTTPS_PORT 443
 
+/**
+ * Largest allowed size for a PEM certificate.
+ */
 #define MAX_PEM_SIZE (10 * 1024)
 
 /**
- * Structure for GNS certificates
+ * After how long do we clean up unused MHD SSL/TLS instances?
  */
-struct ProxyGNSCertificate
-{
-  /* The certificate as PEM */
-  char cert[MAX_PEM_SIZE];
-
-  /* The private key as PEM */
-  char key[MAX_PEM_SIZE];
-};
-
+#define MHD_CACHE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
 
 /**
- * A structure for socks requests
+ * After how long do we clean up Socks5 handles that failed to show any activity
+ * with their respective MHD instance?
  */
-struct Socks5Request
-{
-  /* The client socket */
-  struct GNUNET_NETWORK_Handle *sock;
-
-  /* The server socket */
-  struct GNUNET_NETWORK_Handle *remote_sock;
-  
-  /* The socks state */
-  int state;
-  
-  /* Client socket read task */
-  GNUNET_SCHEDULER_TaskIdentifier rtask;
+#define HTTP_HANDSHAKE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
 
-  /* Server socket read task */
-  GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
 
-  /* Client socket write task */
-  GNUNET_SCHEDULER_TaskIdentifier wtask;
-
-  /* Server socket write task */
-  GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
-
 /* Read buffer */
-  char rbuf[2048];
+/**
+ * Log curl error.
+ *
+ * @param level log level
+ * @param fun name of curl_easy-function that gave the error
+ * @param rc return code from curl
+ */
+#define LOG_CURL_EASY(level,fun,rc) GNUNET_log(level, _("%s failed at %s:%d: `%s'\n"), fun, __FILE__, __LINE__, curl_easy_strerror (rc))
 
-  /* Write buffer */
-  char wbuf[2048];
 
-  /* Length of data in read buffer */
-  unsigned int rbuf_len;
+/* *************** Socks protocol definitions (move to TUN?) ****************** */
 
-  /* Length of data in write buffer */
-  unsigned int wbuf_len;
+/**
+ * Which SOCKS version do we speak?
+ */
+#define SOCKS_VERSION_5 0x05
 
-  /* This handle is scheduled for cleanup? */
-  int cleanup;
+/**
+ * Flag to set for 'no authentication'.
+ */
+#define SOCKS_AUTH_NONE 0
 
-  /* Shall we close the client socket on cleanup? */
-  int cleanup_sock;
-};
 
 /**
- * DLL for Network Handles
+ * Commands in Socks5.
  */
-struct NetworkHandleList
+enum Socks5Commands
 {
-  /*DLL*/
-  struct NetworkHandleList *next;
-
-  /*DLL*/
-  struct NetworkHandleList *prev;
-
-  /* The handle */
-  struct GNUNET_NETWORK_Handle *h;
+  /**
+   * Establish TCP/IP stream.
+   */
+  SOCKS5_CMD_TCP_STREAM = 1,
+
+  /**
+   * Establish TCP port binding.
+   */
+  SOCKS5_CMD_TCP_PORT = 2,
+
+  /**
+   * Establish UDP port binding.
+   */
+  SOCKS5_CMD_UDP_PORT = 3
 };
 
+
 /**
- * A structure for all running Httpds
+ * Address types in Socks5.
  */
-struct MhdHttpList
+enum Socks5AddressType
 {
-  /* DLL for httpds */
-  struct MhdHttpList *prev;
-
-  /* DLL for httpds */
-  struct MhdHttpList *next;
-
-  /* is this an ssl daemon? */
-  int is_ssl;
+  /**
+   * IPv4 address.
+   */
+  SOCKS5_AT_IPV4 = 1,
 
-  /* the domain name to server (only important for SSL) */
-  char domain[256];
+  /**
+   * IPv4 address.
+   */
+  SOCKS5_AT_DOMAINNAME = 3,
 
-  /* The daemon handle */
-  struct MHD_Daemon *daemon;
+  /**
+   * IPv6 address.
+   */
+  SOCKS5_AT_IPV6 = 4
 
-  /* Optional proxy certificate used */
-  struct ProxyGNSCertificate *proxy_cert;
+};
 
-  /* The task ID */
-  GNUNET_SCHEDULER_TaskIdentifier httpd_task;
 
-  /* Handles associated with this daemon */
-  struct NetworkHandleList *socket_handles_head;
-  
-  /* Handles associated with this daemon */
-  struct NetworkHandleList *socket_handles_tail;
+/**
+ * Status codes in Socks5 response.
+ */
+enum Socks5StatusCode
+{
+  SOCKS5_STATUS_REQUEST_GRANTED = 0,
+  SOCKS5_STATUS_GENERAL_FAILURE = 1,
+  SOCKS5_STATUS_CONNECTION_NOT_ALLOWED_BY_RULE = 2,
+  SOCKS5_STATUS_NETWORK_UNREACHABLE = 3,
+  SOCKS5_STATUS_HOST_UNREACHABLE = 4,
+  SOCKS5_STATUS_CONNECTION_REFUSED_BY_HOST = 5,
+  SOCKS5_STATUS_TTL_EXPIRED = 6,
+  SOCKS5_STATUS_COMMAND_NOT_SUPPORTED = 7,
+  SOCKS5_STATUS_ADDRESS_TYPE_NOT_SUPPORTED = 8
 };
 
+
 /**
- * A structure for MHD<->cURL streams
+ * Client hello in Socks5 protocol.
  */
-struct ProxyCurlTask
+struct Socks5ClientHelloMessage
 {
-  /* DLL for tasks */
-  struct ProxyCurlTask *prev;
-
-  /* DLL for tasks */
-  struct ProxyCurlTask *next;
+  /**
+   * Should be #SOCKS_VERSION_5.
+   */
+  uint8_t version;
 
-  /* Handle to cURL */
-  CURL *curl;
+  /**
+   * How many authentication methods does the client support.
+   */
+  uint8_t num_auth_methods;
 
-  /* Optional header replacements for curl (LEHO) */
-  struct curl_slist *headers;
+  /* followed by supported authentication methods, 1 byte per method */
 
-  /* Optional resolver replacements for curl (LEHO) */
-  struct curl_slist *resolver;
+};
 
-  /* curl response code */
-  long curl_response_code;
 
-  /* The URL to fetch */
-  char url[MAX_HTTP_URI_LENGTH];
+/**
+ * Server hello in Socks5 protocol.
+ */
+struct Socks5ServerHelloMessage
+{
+  /**
+   * Should be #SOCKS_VERSION_5.
+   */
+  uint8_t version;
+
+  /**
+   * Chosen authentication method, for us always #SOCKS_AUTH_NONE,
+   * which skips the authentication step.
+   */
+  uint8_t auth_method;
+};
 
-  /* The cURL write buffer / MHD read buffer */
-  char buffer[CURL_MAX_WRITE_SIZE + CURL_BUF_PADDING];
 
-  /* Read pos of the data in the buffer */
-  char *buffer_read_ptr;
+/**
+ * Client socks request in Socks5 protocol.
+ */
+struct Socks5ClientRequestMessage
+{
+  /**
+   * Should be #SOCKS_VERSION_5.
+   */
+  uint8_t version;
+
+  /**
+   * Command code, we only uspport #SOCKS5_CMD_TCP_STREAM.
+   */
+  uint8_t command;
+
+  /**
+   * Reserved, always zero.
+   */
+  uint8_t resvd;
+
+  /**
+   * Address type, an `enum Socks5AddressType`.
+   */
+  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).
+   */
+};
 
-  /* Write pos in the buffer */
-  char *buffer_write_ptr;
 
-  /* The buffer status (BUF_WAIT_FOR_CURL or BUF_WAIT_FOR_MHD) */
-  int buf_status;
+/**
+ * Server response to client requests in Socks5 protocol.
+ */
+struct Socks5ServerResponseMessage
+{
+  /**
+   * Should be #SOCKS_VERSION_5.
+   */
+  uint8_t version;
+
+  /**
+   * Status code, an `enum Socks5StatusCode`
+   */
+  uint8_t reply;
+
+  /**
+   * Always zero.
+   */
+  uint8_t reserved;
+
+  /**
+   * Address type, an `enum Socks5AddressType`.
+   */
+  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).
+   */
 
-  /* Number of bytes in buffer */
-  unsigned int bytes_in_buffer;
+};
 
-  /* Indicates wheather the download is in progress */
-  int download_in_progress;
 
-  /* Indicates wheather the download was successful */
-  int download_is_finished;
 
-  /* Indicates wheather the download failed */
-  int download_error;
+/* *********************** Datastructures for HTTP handling ****************** */
 
-  /* Indicates wheather we need to parse HTML */
-  int parse_content;
+/**
+ * A structure for CA cert/key
+ */
+struct ProxyCA
+{
+  /**
+   * The certificate
+   */
+  gnutls_x509_crt_t cert;
 
-  /* Indicates wheather we are postprocessing the HTML right now */
-  int is_postprocessing;
+  /**
+   * The private key
+   */
+  gnutls_x509_privkey_t key;
+};
 
-  /* Indicates wheather postprocessing has finished */
-  int pp_finished;
 
-  /* PP task */
-  GNUNET_SCHEDULER_TaskIdentifier pp_task;
+/**
+ * Structure for GNS certificates
+ */
+struct ProxyGNSCertificate
+{
+  /**
+   * The certificate as PEM
+   */
+  char cert[MAX_PEM_SIZE];
 
-  /* PP match list */
-  struct ProxyREMatch *pp_match_head;
+  /**
+   * The private key as PEM
+   */
+  char key[MAX_PEM_SIZE];
+};
 
-  /* PP match list */
-  struct ProxyREMatch *pp_match_tail;
 
-  /* The authority of the corresponding host (site of origin) */
-  char authority[256];
 
-  /* The hostname (Host header field) */
-  char host[256];
+/**
+ * A structure for all running Httpds
+ */
+struct MhdHttpList
+{
+  /**
+   * DLL for httpds
+   */
+  struct MhdHttpList *prev;
 
-  /* The port */
-  int port;
+  /**
+   * DLL for httpds
+   */
+  struct MhdHttpList *next;
 
-  /* The LEgacy HOstname (can be empty) */
-  char leho[256];
+  /**
+   * the domain name to server (only important for SSL)
+   */
+  char *domain;
 
-  /* The associated daemon list entry */
-  struct MhdHttpList *mhd;
+  /**
+   * The daemon handle
+   */
+  struct MHD_Daemon *daemon;
 
-  /* The associated response */
-  struct MHD_Response *response;
+  /**
+   * Optional proxy certificate used
+   */
+  struct ProxyGNSCertificate *proxy_cert;
 
-  /* Cookies to set */
-  struct ProxySetCookieHeader *set_cookies_head;
+  /**
+   * The task ID
+   */
+  GNUNET_SCHEDULER_TaskIdentifier httpd_task;
 
-  /* Cookies to set */
-  struct ProxySetCookieHeader *set_cookies_tail;
+  /**
+   * is this an ssl daemon?
+   */
+  int is_ssl;
 
-  /* connection status */
-  int ready_to_queue;
-  
-  /* are we done */
-  int fin;
+};
 
-  /* connection */
-  struct MHD_Connection *connection;
 
-  /*put*/
-  size_t put_read_offset;
-  size_t put_read_size;
+/* ***************** Datastructures for Socks handling **************** */
 
-  /*post*/
-  struct MHD_PostProcessor *post_handler;
 
-  /* post data */
-  struct ProxyPostData *post_data_head;
-  struct ProxyPostData *post_data_tail;
+/**
+ * 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,
+
+  /**
+   * We've finished receiving upload data from MHD.
+   */
+  SOCKS5_SOCKET_UPLOAD_STARTED,
+
+  /**
+   * We've finished receiving upload data from MHD.
+   */
+  SOCKS5_SOCKET_UPLOAD_DONE,
+
+  /**
+   * We've finished uploading data via CURL and can now download.
+   */
+  SOCKS5_SOCKET_DOWNLOAD_STARTED,
+
+  /**
+   * We've finished receiving download data from cURL.
+   */
+  SOCKS5_SOCKET_DOWNLOAD_DONE
+};
 
-  int post_done;
 
-  /* the type of POST encoding */
-  char* post_type;
-  
-};
 
 /**
- * Struct for RE matches in postprocessing of HTML
+ * A structure for socks requests
  */
-struct ProxyREMatch
+struct Socks5Request
 {
-  /* DLL */
-  struct ProxyREMatch *next;
 
-  /* DLL */
-  struct ProxyREMatch *prev;
+  /**
+   * DLL.
+   */
+  struct Socks5Request *next;
 
-  /* hostname found */
-  char hostname[255];
+  /**
+   * DLL.
+   */
+  struct Socks5Request *prev;
 
-  /* PP result */
-  char result[255];
+  /**
+   * The client socket
+   */
+  struct GNUNET_NETWORK_Handle *sock;
 
-  /* shorten task */
-  struct GNUNET_GNS_ShortenRequest *shorten_task;
+  /**
+   * Handle to GNS lookup, during #SOCKS5_RESOLVING phase.
+   */
+  struct GNUNET_GNS_LookupRequest *gns_lookup;
 
-  /* are we done */
-  int done;
+  /**
+   * Client socket read task
+   */
+  GNUNET_SCHEDULER_TaskIdentifier rtask;
 
-  /* start of match in buffer */
-  char* start;
+  /**
+   * Client socket write task
+   */
+  GNUNET_SCHEDULER_TaskIdentifier wtask;
 
-  /* end of match in buffer */
-  char* end;
+  /**
+   * Timeout task
+   */
+  GNUNET_SCHEDULER_TaskIdentifier timeout_task;
+
+  /**
+   * Read buffer
+   */
+  char rbuf[SOCKS_BUFFERSIZE];
+
+  /**
+   * Write buffer
+   */
+  char wbuf[SOCKS_BUFFERSIZE];
+
+  /**
+   * Buffer we use for moving data between MHD and curl (in both directions).
+   */
+  char io_buf[IO_BUFFERSIZE];
+
+  /**
+   * MHD HTTP instance handling this request, NULL for none.
+   */
+  struct MhdHttpList *hd;
 
-  /* associated proxycurltask */
-  struct ProxyCurlTask *ctask;
-};
+  /**
+   * MHD response object for this request.
+   */
+  struct MHD_Response *response;
 
-/**
- * Struct for set-cookies
- */
-struct ProxySetCookieHeader
-{
-  /* DLL */
-  struct ProxySetCookieHeader *next;
+  /**
+   * the domain name to server (only important for SSL)
+   */
+  char *domain;
 
-  /* DLL */
-  struct ProxySetCookieHeader *prev;
+  /**
+   * DNS Legacy Host Name as given by GNS, NULL if not given.
+   */
+  char *leho;
 
-  /* the cookie */
-  char *cookie;
-};
+  /**
+   * The URL to fetch
+   */
+  char *url;
 
-/**
- * Post data structure
- */
-struct ProxyPostData
-{
-  /* DLL */
-  struct ProxyPostData *next;
+  /**
+   * Handle to cURL
+   */
+  CURL *curl;
 
-  /* DLL */
-  struct ProxyPostData *prev;
-  
-  /* value */
-  char *value;
+  /**
+   * HTTP request headers for the curl request.
+   */
+  struct curl_slist *headers;
 
-  /* to copy */
-  size_t bytes_left;
+  /**
+   * HTTP response code to give to MHD for the response.
+   */
+  unsigned int response_code;
+
+  /**
+   * Number of bytes already in read buffer
+   */
+  size_t rbuf_len;
+
+  /**
+   * Number of bytes already in write buffer
+   */
+  size_t wbuf_len;
+
+  /**
+   * Number of bytes already in the IO buffer.
+   */
+  size_t io_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;
 
-  /* size */
-  size_t total_bytes;
 };
 
 
-/* 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 */
-static char* cafile_opt;
+/* *********************** Globals **************************** */
 
-/* The listen socket of the proxy */
-static struct GNUNET_NETWORK_Handle *lsock;
 
-/* The listen task ID */
-GNUNET_SCHEDULER_TaskIdentifier ltask;
+/**
+ * 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
+ */
+static char *cafile_opt;
 
-/* The cURL download task */
-GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
+/**
+ * The listen socket of the proxy
+ */
+static struct GNUNET_NETWORK_Handle *lsock;
 
-/* The non SSL httpd daemon handle */
-static struct MHD_Daemon *httpd;
+/**
+ * The listen task ID
+ */
+static GNUNET_SCHEDULER_TaskIdentifier ltask;
 
-/* Number of current mhd connections */
-static unsigned int total_mhd_connections;
+/**
+ * The cURL download task (curl multi API).
+ */
+static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
 
-/* The cURL multi handle */
+/**
+ * The cURL multi handle
+ */
 static CURLM *curl_multi;
 
-/* Handle to the GNS service */
+/**
+ * Handle to the GNS service
+ */
 static struct GNUNET_GNS_Handle *gns_handle;
 
-/* DLL for ProxyCurlTasks */
-static struct ProxyCurlTask *ctasks_head;
-
-/* DLL for ProxyCurlTasks */
-static struct ProxyCurlTask *ctasks_tail;
-
-/* DLL for http daemons */
+/**
+ * DLL for http/https daemons
+ */
 static struct MhdHttpList *mhd_httpd_head;
 
-/* DLL for http daemons */
+/**
+ * DLL for http/https daemons
+ */
 static struct MhdHttpList *mhd_httpd_tail;
 
-/* Handle to the regex for dotplus (.+) replacement in HTML */
-static regex_t re_dotplus;
+/**
+ * Daemon for HTTP (we have one per SSL certificate, and then one for
+ * all HTTP connections; this is the one for HTTP, not HTTPS).
+ */
+static struct MhdHttpList *httpd;
+
+/**
+ * DLL of active socks requests.
+ */
+static struct Socks5Request *s5r_head;
+
+/**
+ * DLL of active socks requests.
+ */
+static struct Socks5Request *s5r_tail;
 
-/* The users local GNS zone hash */
-static struct GNUNET_CRYPTO_ShortHashCode *local_gns_zone;
+/**
+ * The users local GNS master zone
+ */
+static struct GNUNET_CRYPTO_EcdsaPublicKey local_gns_zone;
 
-/* The users local private zone */
-static struct GNUNET_CRYPTO_ShortHashCode *local_private_zone;
+/**
+ * The users local shorten zone
+ */
+static struct GNUNET_CRYPTO_EcdsaPrivateKey local_shorten_zone;
 
-/* The users local shorten zone */
-static struct GNUNET_CRYPTO_ShortHashCode *local_shorten_zone;
+/**
+ * Is shortening enabled?
+ */
+static int do_shorten;
 
-/* The CA for SSL certificate generation */
+/**
+ * The CA for SSL certificate generation
+ */
 static struct ProxyCA proxy_ca;
 
-/* UNIX domain socket for mhd */
-struct GNUNET_NETWORK_Handle *mhd_unix_socket;
+/**
+ * Response we return on cURL failures.
+ */
+static struct MHD_Response *curl_failure_response;
 
-/* Shorten zone private key */
-struct GNUNET_CRYPTO_RsaPrivateKey *shorten_zonekey;
+/**
+ * Connection to identity service.
+ */
+static struct GNUNET_IDENTITY_Handle *identity;
 
 /**
- * Checks if name is in tld
- *
- * @param name the name to check
- * @param tld the TLD to check for
- * @return GNUNET_YES or GNUNET_NO
+ * Request for our ego.
  */
-int
-is_tld(const char* name, const char* tld)
-{
-  size_t offset;
+static struct GNUNET_IDENTITY_Operation *id_op;
 
-  if (strlen(name) <= strlen(tld))  
-    return GNUNET_NO;  
+/**
+ * Our configuration.
+ */
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
 
-  offset = strlen(name) - strlen(tld);
-  if (0 != strcmp (name+offset, tld))
-  {
-    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
-               "%s is not in .%s TLD\n", name, tld);
-    return GNUNET_NO;
-  }
 
-  return GNUNET_YES;
-}
+/* ************************* Global helpers ********************* */
+
 
 /**
- * convert integer to string representation
+ * Run MHD now, we have extra data ready for the callback.
  *
- * @param i integer
- * @return the character
+ * @param hd the daemon to run now.
  */
-char i_to_hexchar (char i)
-{
-  static char hexmap[] = "0123456789abcdef";
-  GNUNET_assert (sizeof (hexmap) > (i & 15));
-  return hexmap[i & 15];
-}
+static void
+run_mhd_now (struct MhdHttpList *hd);
+
 
 /**
- * Escape giben 0-terminated string
+ * Clean up s5r handles.
  *
- * @param to_esc string to escapse
- * @return allocated new escaped string (MUST free!)
+ * @param s5r the handle to destroy
  */
-static char*
-escape_to_urlenc (const char *to_esc)
+static void
+cleanup_s5r (struct Socks5Request *s5r)
 {
-  char *pos = (char*)to_esc;
-  char *res = GNUNET_malloc (strlen (to_esc) * 3 + 1);
-  char *rpos = res;
-
-  while ('\0' != *pos)
-  {
-    if (isalnum (*pos) ||
-        ('-' == *pos) || ('_' == *pos) ||
-        ('.' == *pos) || ('~' == *pos))
-        *rpos++ = *pos;
-    else if (' ' == *pos)
-      *rpos++ = '+';
+  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)
+    GNUNET_SCHEDULER_cancel (s5r->timeout_task);
+  if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
+    GNUNET_SCHEDULER_cancel (s5r->wtask);
+  if (NULL != s5r->gns_lookup)
+    GNUNET_GNS_lookup_cancel (s5r->gns_lookup);
+  if (NULL != s5r->sock)
+  {
+    if (SOCKS5_SOCKET_WITH_MHD <= s5r->state)
+      GNUNET_NETWORK_socket_free_memory_only_ (s5r->sock);
     else
-    {
-      *rpos++ = '%';
-      *rpos++ = i_to_hexchar (*pos >> 4);
-      *rpos++ = i_to_hexchar (*pos >> 15);
-    }
-    pos++;
+      GNUNET_NETWORK_socket_close (s5r->sock);
   }
-  *rpos = '\0';
-  return res;
+  GNUNET_CONTAINER_DLL_remove (s5r_head,
+                              s5r_tail,
+                              s5r);
+  GNUNET_free_non_null (s5r->domain);
+  GNUNET_free_non_null (s5r->leho);
+  GNUNET_free_non_null (s5r->url);
+  GNUNET_free (s5r);
 }
 
-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)
+
+/* ************************* HTTP handling with cURL *********************** */
+
+
+/**
+ * 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 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 ssize_t
+mhd_content_cb (void *cls,
+                uint64_t pos,
+                char* buf,
+                size_t max)
 {
-  struct ProxyCurlTask* ctask = cls;
-  struct ProxyPostData* pdata;
-  char* enc;
-  
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Got POST data: '%s : %s' at offset %llu size %lld\n",
-              key, data, off, size);
+  struct Socks5Request *s5r = cls;
+  size_t bytes_to_copy;
 
-  if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
-                       ctask->post_type))
+  if ( (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) ||
+       (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
   {
-    return MHD_NO;
+    /* 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 == off)
+  bytes_to_copy = GNUNET_MIN (max,
+                             s5r->io_len);
+  if ( (0 == bytes_to_copy) &&
+       (SOCKS5_SOCKET_DOWNLOAD_DONE != s5r->state) )
   {
-    /* a key */
-    pdata = GNUNET_malloc (sizeof (struct ProxyPostData));
-    enc = escape_to_urlenc (key);
-    pdata->value = GNUNET_malloc (strlen (enc) + 3);
-    if (NULL != ctask->post_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;
-    GNUNET_free (enc);
-
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Escaped POST key: '%s'\n",
-                pdata->value);
-
-    GNUNET_CONTAINER_DLL_insert_tail (ctask->post_data_head,
-                                      ctask->post_data_tail,
-                                      pdata);
+               "Pausing MHD download, no data available\n");
+    return 0; /* more data later */
   }
-
-  /* a value */
-  pdata = GNUNET_malloc (sizeof (struct ProxyPostData));
-  enc = escape_to_urlenc (data);
-  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;
-  GNUNET_free (enc);
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Escaped POST value: '%s'\n",
-              pdata->value);
-
-  GNUNET_CONTAINER_DLL_insert_tail (ctask->post_data_head,
-                                    ctask->post_data_tail,
-                                    pdata);
-  return MHD_YES;
+  if ( (0 == bytes_to_copy) &&
+       (SOCKS5_SOCKET_DOWNLOAD_DONE == s5r->state) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "Completed MHD download\n");
+    return MHD_CONTENT_READER_END_OF_STREAM;
+  }
+  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;
 }
 
 
+/**
+ * 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 s5r request to check for.
+ * @return #GNUNET_OK if the certificate is valid
+ */
 static int
-get_uri_val_iter (void *cls,
-                  enum MHD_ValueKind kind,
-                  const char *key,
-                  const char *value)
+check_ssl_certificate (struct Socks5Request *s5r)
 {
-  char* buf = cls;
-  
-  if (strlen (buf) + strlen (value) + 3 > MAX_HTTP_URI_LENGTH)
-    return MHD_NO;
-  sprintf (buf+strlen (buf), "?%s=%s", key, value);
-
-  return MHD_YES;
+  unsigned int i;
+  union {
+    gnutls_session_t session;
+    struct curl_slist    * to_slist;
+  } gptr;
+  unsigned int cert_list_size;
+  const gnutls_datum_t *chainp;
+
+  gptr.to_slist = NULL;
+  if (CURLE_OK !=
+      curl_easy_getinfo (s5r->curl,
+                        CURLINFO_GNUTLS_SESSION,
+                        &gptr))
+    return GNUNET_SYSERR;
+
+  chainp = gnutls_certificate_get_peers(gptr.session, &cert_list_size);
+  if(!chainp)
+    return GNUNET_SYSERR;
+
+  for(i=0;i<cert_list_size;i++) {
+    gnutls_x509_crt_t cert;
+    gnutls_datum_t dn;
+
+    if(GNUTLS_E_SUCCESS == gnutls_x509_crt_init (&cert)) {
+      if((GNUTLS_E_SUCCESS ==
+         gnutls_x509_crt_import (cert, &chainp[i],
+                                 GNUTLS_X509_FMT_DER)) &&
+        (GNUTLS_E_SUCCESS ==
+         gnutls_x509_crt_print (cert,
+                                GNUTLS_CRT_PRINT_FULL,
+                                &dn))) {
+       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                   "Certificate #%d: %.*s", i, dn.size, dn.data);
+       gnutls_free (dn.data);
+        gnutls_x509_crt_deinit (cert);
+      }
+    }
+  }
+  return GNUNET_OK;
 }
 
+
 /**
- * Read HTTP request header field 'Host'
+ * 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 buffer to write to
- * @param kind value kind
- * @param key field key
- * @param value field value
- * @return MHD_NO when Host found
- */
-static int
-con_val_iter (void *cls,
-              enum MHD_ValueKind kind,
-              const char *key,
-              const char *value)
-{
-  struct ProxyCurlTask *ctask = cls;
-  char* buf = ctask->host;
-  char* port;
-  char* cstr;
-  const char* hdr_val;
-
-  if (0 == strcmp ("Host", key))
-  {
-    port = strstr (value, ":");
-    if (NULL != port)
-    {
-      strncpy (buf, value, port-value);
-      ctask->port = atoi (++port);
-    }
-    else
-      strcpy (buf, value);
-    return MHD_YES;
-  }
-
-  if (0 == strcmp ("Accept-Encoding", key))
-    hdr_val = "";
-  else
-    hdr_val = value;
-
-  if (0 == strcmp (MHD_HTTP_HEADER_CONTENT_TYPE,
-                   key))
-  {
-    if (0 == strcmp (value,
-                     MHD_HTTP_POST_ENCODING_FORM_URLENCODED))
-      ctask->post_type = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
-    else if (0 == strcmp (value,
-                          MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA))
-      ctask->post_type = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
-    else
-      ctask->post_type = NULL;
-
-  }
-
-  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;
-}
-
-
-/**
- * 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);
-
-/**
- * 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 html_mime_len = strlen (HTML_HDR_CONTENT);
-  int cookie_hdr_len = strlen (MHD_HTTP_HEADER_SET_COOKIE);
-  char hdr_mime[html_mime_len+1];
-  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;
-  
-  if (NULL == ctask->response)
-  {
-    /* FIXME: get total size from curl (if available) */
-    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);
-    ctask->ready_to_queue = GNUNET_YES;
-    
-  }
-  
-  if (html_mime_len <= bytes)
-  {
-    memcpy (hdr_mime, buffer, html_mime_len);
-    hdr_mime[html_mime_len] = '\0';
-
-    if (0 == strcmp (hdr_mime, HTML_HDR_CONTENT))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Got HTML HTTP response header\n");
-      ctask->parse_content = GNUNET_YES;
-    }
-  }
-
-  if (cookie_hdr_len > bytes)
-    return bytes;
-
-  memcpy (hdr_generic, buffer, bytes);
-  hdr_generic[bytes] = '\0';
-  /* remove crlf */
-  if ('\n' == hdr_generic[bytes-1])
-    hdr_generic[bytes-1] = '\0';
+  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 (hdr_generic[bytes-2] == '\r')
-    hdr_generic[bytes-2] = '\0';
-  
-  if (0 == memcmp (hdr_generic,
-                   MHD_HTTP_HEADER_SET_COOKIE,
-                   cookie_hdr_len))
+  if (NULL == s5r->response)
   {
-    ndup = GNUNET_strdup (hdr_generic+cookie_hdr_len+1);
-    memset (new_cookie_hdr, 0, sizeof (new_cookie_hdr));
-    tok = strtok (ndup, ";");
+    /* first, check SSL certificate */
+    if ( (HTTPS_PORT == s5r->port) &&
+        (GNUNET_OK != check_ssl_certificate (s5r)) )
+      return 0;
 
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Looking for cookie in : %s\n", hdr_generic);
-    
-    for (; tok != NULL; 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")))
-      {
-        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))
-        {
-          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++;
-            continue;
-          }
-        }
-        else if (strlen (cookie_domain) == strlen (ctask->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;
-          }
-        }
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Cookie domain invalid\n");
-
-        
-      }
-      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_break (CURLE_OK ==
+                 curl_easy_getinfo (s5r->curl,
+                                    CURLINFO_RESPONSE_CODE,
+                                    &resp_code));
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "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)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "MHD: Error adding set-cookie header field %s\n",
-                  hdr_generic+cookie_hdr_len+1);
+      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);
     }
-    return bytes;
+    /* 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"));
   }
 
-  ndup = GNUNET_strdup (hdr_generic);
+  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++;
 
-  hdr_val++;
+  /* custom logic for certain header types */
+  new_cookie_hdr = NULL;
+  if ( (NULL != s5r->leho) &&
+       (0 == strcasecmp (hdr_type,
+                        MHD_HTTP_HEADER_SET_COOKIE)) )
 
-  if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
   {
-    if (ctask->mhd->is_ssl)
+    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, ";"))
     {
-      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);
-    }
-
-    if (0 == memcmp (leho_host, hdr_val, strlen (leho_host)))
-    {
-      sprintf (new_location, "%s%s", real_host, hdr_val+strlen (leho_host));
-      hdr_val = new_location;
+      if ( (0 == strncasecmp (tok, " domain", strlen (" domain"))) &&
+          (GNUNET_NO == domain_matched) )
+      {
+       domain_matched = GNUNET_YES;
+        cookie_domain = tok + strlen (" domain") + 1;
+        if (strlen (cookie_domain) < strlen (s5r->leho))
+        {
+          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 (0 == strcmp (cookie_domain, s5r->leho))
+        {
+         offset += sprintf (new_cookie_hdr + offset,
+                            " domain=%s;",
+                            s5r->domain);
+         continue;
+        }
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    _("Cookie domain `%s' supplied by server is invalid\n"),
+                   tok);
+      }
+      memcpy (new_cookie_hdr + offset, tok, strlen (tok));
+      offset += strlen (tok);
+      new_cookie_hdr[offset++] = ';';
     }
+    hdr_val = new_cookie_hdr;
   }
 
-  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))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "MHD: Error adding %s header field %s\n",
-                hdr_type,
-                hdr_val);
-  }
-  GNUNET_free (ndup);
-  return bytes;
-}
-
-/**
- * schedule mhd
- *
- * @param hd a http daemon list entry
- */
-static void
-run_httpd (struct MhdHttpList *hd);
-
-
-/**
- * schedule all mhds
- *
- */
-static void
-run_httpds (void);
-
-/**
- * Task run whenever HTTP server operations are pending.
- *
- * @param cls unused
- * @param tc sched context
- */
-static void
-do_httpd (void *cls,
-          const struct GNUNET_SCHEDULER_TaskContext *tc);
-
-static void
-run_mhd_now (struct MhdHttpList *hd)
-{
-  if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "MHD: killing old task\n");
-    GNUNET_SCHEDULER_cancel (hd->httpd_task);
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "MHD: Scheduling MHD now\n");
-  hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, hd);
-}
-
-/**
- * Ask cURL for the select sets and schedule download
- */
-static void
-curl_download_prepare ();
-
-/**
- * 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;
-  GNUNET_assert (NULL == ctask->pp_match_head);
-
-  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);
-
-
-  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;
-  struct ProxyREMatch *re_match = ctask->pp_match_head;
-  ssize_t copied = 0;
-  long long int bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "MHD: content cb for %s. To copy: %lld\n",
-              ctask->url, bytes_to_copy);
-  GNUNET_assert (bytes_to_copy >= 0);
-
-  if ((GNUNET_YES == ctask->download_is_finished) &&
-      (GNUNET_NO == ctask->download_error) &&
-      (0 == bytes_to_copy)) /* &&
-      (BUF_WAIT_FOR_CURL == ctask->buf_status))*/
-  {
-    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);
-    total_mhd_connections--;
-    return MHD_CONTENT_READER_END_OF_STREAM;
-  }
-  
-  if ((GNUNET_YES == ctask->download_error) &&
-      (GNUNET_YES == ctask->download_is_finished) &&
-      (0 == bytes_to_copy)) /* &&
-      (BUF_WAIT_FOR_CURL == ctask->buf_status))*/
-  {
-    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);
-    total_mhd_connections--;
-    return MHD_CONTENT_READER_END_WITH_ERROR;
-  }
-
-  if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
-    return 0;
-  
-  copied = 0;
-  for (; NULL != re_match; re_match = ctask->pp_match_head)
+  new_location = NULL;
+  if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "MHD: Processing PP %s\n",
-                re_match->hostname);
-    bytes_to_copy = re_match->start - ctask->buffer_read_ptr;
-    GNUNET_assert (bytes_to_copy >= 0);
+    char *leho_host;
 
-    if (bytes_to_copy+copied > max)
+    GNUNET_asprintf (&leho_host,
+                    (HTTPS_PORT != s5r->port)
+                    ? "http://%s"
+                    : "https://%s",
+                    s5r->leho);
+    if (0 == strncmp (leho_host,
+                     hdr_val,
+                     strlen (leho_host)))
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "MHD: buffer in response too small for %d. Using available space (%d). (%s)\n",
-             bytes_to_copy,
-             max,
-             ctask->url);
-      memcpy (buf+copied, ctask->buffer_read_ptr, max-copied);
-      ctask->buffer_read_ptr += max-copied;
-      copied = max;
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "MHD: copied %d bytes\n", copied);
-      return copied;
-    }
-
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "MHD: copying %d bytes to mhd response at offset %d\n",
-                bytes_to_copy, ctask->buffer_read_ptr);
-    memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
-    copied += bytes_to_copy;
-
-    if (GNUNET_NO == re_match->done)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "MHD: Waiting for PP of %s\n", re_match->hostname);
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "MHD: copied %d bytes\n", copied);
-      ctask->buffer_read_ptr += bytes_to_copy;
-      return copied;
-    }
-    
-    if (strlen (re_match->result) > (max - copied))
-    {
-      //FIXME partially copy domain here
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "MHD: buffer in response too small for %s! (%s)\n",
-                  re_match->result,
-                  ctask->url);
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "MHD: copied %d bytes\n", copied);
-      ctask->buffer_read_ptr += bytes_to_copy;
-      return copied;
+      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_log (GNUNET_ERROR_TYPE_DEBUG,
-                "MHD: Adding PP result %s to buffer\n",
-                re_match->result);
-    memcpy (buf+copied, re_match->result, strlen (re_match->result));
-    copied += strlen (re_match->result);
-    ctask->buffer_read_ptr = re_match->end;
-    GNUNET_CONTAINER_DLL_remove (ctask->pp_match_head,
-                                 ctask->pp_match_tail,
-                                 re_match);
-    GNUNET_free (re_match);
+    GNUNET_free (leho_host);
   }
-
-  bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "MHD: copied: %d left: %d, space left in buf: %d\n",
-              copied,
-              bytes_to_copy, max-copied);
-  
-  GNUNET_assert (0 <= bytes_to_copy);
-
-  if (GNUNET_NO == ctask->download_is_finished)
+  /* 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_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;
-
-  if (0 > bytes_to_copy)
-    bytes_to_copy = 0;
-  
-  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", copied);
-  run_mhd_now (ctask->mhd);
-  return copied;
-}
-
-/**
- * Shorten result callback
- *
- * @param cls the proxycurltask
- * @param short_name the shortened name (NULL on error)
- */
-static void
-process_shorten (void* cls, const char* short_name)
-{
-  struct ProxyREMatch *re_match = cls;
-  char result[sizeof (re_match->result)];
-  
-  if (NULL == short_name)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "PP: Unable to shorten %s\n",
-                re_match->hostname);
-    GNUNET_CONTAINER_DLL_remove (re_match->ctask->pp_match_head,
-                                 re_match->ctask->pp_match_tail,
-                                 re_match);
-    GNUNET_free (re_match);
-    return;
+               "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));
   }
-
-  if (0 == strcmp (short_name, re_match->ctask->leho))
-    strcpy (result, re_match->ctask->host);
-  else
-    strcpy (result, short_name);
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "PP: Shorten %s -> %s\n",
-              re_match->hostname,
-              result);
-  
-  if (re_match->ctask->mhd->is_ssl)
-    sprintf (re_match->result, "href=\"https://%s", result);
-  else
-    sprintf (re_match->result, "href=\"http://%s", result);
-
-  re_match->done = GNUNET_YES;
-  run_mhd_now (re_match->ctask->mhd);
+  GNUNET_free (ndup);
+  GNUNET_free_non_null (new_cookie_hdr);
+  GNUNET_free_non_null (new_location);
+  return bytes;
 }
 
 
 /**
- * Postprocess data in buffer. From read ptr to write ptr
- *
- * @param cls the curlproxytask
- * @param tc task context
- */
-static void
-postprocess_buffer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
-  struct ProxyCurlTask *ctask = cls;
-  struct ProxyREMatch *re_match;
-  char* re_ptr = ctask->buffer_read_ptr;
-  char re_hostname[255];
-  regmatch_t m[RE_N_MATCHES];
-
-  ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
-
-  if (GNUNET_YES != ctask->parse_content)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "PP: Not parsing content\n");
-    ctask->buf_status = BUF_WAIT_FOR_MHD;
-    run_mhd_now (ctask->mhd);
-    return;
-  }
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "PP: We need to parse the HTML\n");
-
-  /* 0 means match found */
-  while (0 == regexec (&re_dotplus, re_ptr, RE_N_MATCHES, m, 0))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "PP: regex match\n");
-
-    GNUNET_assert (m[1].rm_so != -1);
-
-    memset (re_hostname, 0, sizeof (re_hostname));
-    memcpy (re_hostname, re_ptr+m[1].rm_so, (m[3].rm_eo-m[1].rm_so));
-
-    re_match = GNUNET_malloc (sizeof (struct ProxyREMatch));
-    re_match->start = re_ptr + m[0].rm_so;
-    re_match->end = re_ptr + m[3].rm_eo;
-    re_match->done = GNUNET_NO;
-    re_match->ctask = ctask;
-    strcpy (re_match->hostname, re_hostname);
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "PP: Got hostname %s\n", re_hostname);
-    re_ptr += m[3].rm_eo;
-
-    if (GNUNET_YES == is_tld (re_match->hostname, GNUNET_GNS_TLD_PLUS))
-    {
-      re_match->hostname[strlen(re_match->hostname)-1] = '\0';
-      strcpy (re_match->hostname+strlen(re_match->hostname),
-              ctask->authority);
-    }
-
-    re_match->shorten_task = GNUNET_GNS_shorten_zone (gns_handle,
-                             re_match->hostname,
-                             local_private_zone,
-                             local_shorten_zone,
-                             local_gns_zone,
-                             &process_shorten,
-                             re_match); //FIXME cancel appropriately
-
-    GNUNET_CONTAINER_DLL_insert_tail (ctask->pp_match_head,
-                                      ctask->pp_match_tail,
-                                      re_match);
-  }
-  
-  ctask->buf_status = BUF_WAIT_FOR_MHD;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "PP: No more matches\n");
-  run_mhd_now (ctask->mhd);
-}
-
-/**
- * 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",
-              total, buf_space);
-
-  if (total > (buf_space - CURL_BUF_PADDING))
+  if ( (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) ||
+       (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
   {
-    if (ctask->buf_status == BUF_WAIT_FOR_CURL)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "CURL: Buffer full starting postprocessing\n");
-      ctask->buf_status = BUF_WAIT_FOR_PP;
-      ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer,
-                                                 ctask);
-      return CURL_WRITEFUNC_PAUSE;
-    }
-
-    /* we should not get called in that case */
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "CURL: called out of context and no space in buffer!\n");
-    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 */
   }
-
-  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;
-  ctask->buffer_write_ptr[0] = '\0';
-
+  if (sizeof (s5r->io_buf) - s5r->io_len < total)
+  {
+    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 post 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
-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 ProxyPostData *pdata = ctask->post_data_head;
+  struct Socks5Request *s5r = cls;
   size_t len = size * nmemb;
   size_t to_copy;
-  char* pos;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "CURL: read callback\n");
-
-  if (NULL == pdata)
-    return 0;
-  
-  //fin
-  if (NULL == pdata->value)
+  if ( (0 == s5r->io_len) &&
+       (SOCKS5_SOCKET_UPLOAD_DONE != s5r->state) )
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "CURL: Terminating POST data\n");
-
-    GNUNET_CONTAINER_DLL_remove (ctask->post_data_head,
-                                 ctask->post_data_tail,
-                                 pdata);
-    GNUNET_free (pdata);
-    return 0;
+               "Pausing CURL UPLOAD, need more data\n");
+    return CURL_READFUNC_PAUSE;
   }
-  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->post_data_head,
-                                 ctask->post_data_tail,
-                                 pdata);
-    GNUNET_free (pdata);
+  if ( (0 == s5r->io_len) &&
+       (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
+  {
+    s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+               "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
@@ -1338,8 +1124,9 @@ read_callback (void *buf, size_t size, size_t nmemb, void *cls)
 static void
 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 ()
@@ -1354,383 +1141,183 @@ 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);
   FD_ZERO (&es);
-  mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
-
-  if (mret != CURLM_OK)
+  if (CURLM_OK != (mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max)))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "%s failed at %s:%d: `%s'\n",
                 "curl_multi_fdset", __FILE__, __LINE__,
-                curl_multi_strerror (mret));
-    //TODO cleanup here?
-    return;
-  }
-
-  mret = curl_multi_timeout (curl_multi, &to);
-  rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "cURL multi fds: max=%d timeout=%llu\n", max, 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);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Scheduling task cURL\n");
-
-  if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
-    GNUNET_SCHEDULER_cancel (curl_download_task);
-  
+                curl_multi_strerror (mret));
+    return;
+  }
+  to = -1;
+  GNUNET_break (CURLM_OK == curl_multi_timeout (curl_multi, &to));
+  if (-1 == to)
+    rtime = GNUNET_TIME_UNIT_FOREVER_REL;
+  else
+    rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
   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_SECONDS,
+    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);
-
-    ctask = ctasks_head;
-    for (; ctask != NULL; 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++;
-    }
-
-    if (num_ctasks != running)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "%d ctasks, %d curl running\n", num_ctasks, running);
-    }
-    
-    do
-    {
-      ctask = ctasks_head;
-      msg = curl_multi_info_read (curl_multi, &msgnum);
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Messages left: %d\n", msgnum);
-      
-      if (msg == NULL)
-        break;
+      GNUNET_break (CURLE_OK ==
+                   curl_easy_getinfo (msg->easy_handle,
+                                      CURLINFO_PRIVATE,
+                                      &s5r));
+      if (NULL == s5r)
+      {
+       GNUNET_break (0);
+       continue;
+      }
       switch (msg->msg)
       {
-       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 != NULL; 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 != NULL; ctask = ctask->next)
-           {
-             if (NULL == ctask->curl)
-               continue;
-
-             if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
-               continue;
-             
-             if (ctask->buf_status != BUF_WAIT_FOR_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_PP;
-              ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer,
-                                                          ctask);
-             }
-
-             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;
+      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 (msgnum > 0);
-
-    for (ctask=clean_head; ctask != NULL; 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; ctask != NULL; ctask = ctask->next)
-    {
-      num_ctasks++;
-    }
-    
-    if (num_ctasks != running)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "CURL: %d tasks, %d running\n", num_ctasks, running);
-    }
-
-    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",
+  if (CURLM_OK != mret)
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "%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_GNS_RECORD_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, ""))
+  if (0 == running)
   {
-    sprintf (hosthdr, "%s%s", "Host: ", ctask->leho);
     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);
-    ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers);
-    if (CURLE_OK != ret)
-    {
-      GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s failed at %s:%d: `%s'\n",
-                           "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(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);
-      curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver);
-      sprintf (curlurl, "https://%s:%d%s", ctask->leho, ctask->port, ctask->url);
-      curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
-    }
-    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;
-    }
+               "Suspending cURL multi loop, no more events pending\n");
+    return; /* nothing more in progress */
   }
+  curl_download_prepare();
+}
 
-  if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "%s failed at %s:%d: `%s'\n",
-                "curl_multi_add_handle", __FILE__, __LINE__,
-                curl_multi_strerror (mret));
-    ctask->download_is_finished = GNUNET_YES;
-    ctask->download_error = GNUNET_YES;
-    return;
-  }
-  GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
 
-  curl_download_prepare ();
+/* ********************************* MHD response generation ******************* */
 
-}
 
 /**
- * Initialize download and trigger curl
- *
- * @param cls the proxycurltask
- * @param auth_name the name of the authority (site of origin) of ctask->host
+ * 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 void
-process_get_authority (void *cls,
-                       const char* auth_name)
+static int
+con_val_iter (void *cls,
+              enum MHD_ValueKind kind,
+              const char *key,
+              const char *value)
 {
-  struct ProxyCurlTask *ctask = cls;
-
-  if (NULL == auth_name)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Get authority failed!\n");
-    strcpy (ctask->authority, "");
-  }
-  else
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Get authority yielded %s\n", auth_name);
-    strcpy (ctask->authority, auth_name);
-  }
-
-  GNUNET_GNS_lookup_zone (gns_handle,
-                          ctask->host,
-                          local_gns_zone,
-                          GNUNET_GNS_RECORD_LEHO,
-                          GNUNET_YES, //Only cached for performance
-                          shorten_zonekey,
-                          &process_leho_lookup,
-                          ctask);
+  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;
 }
 
 
@@ -1750,7 +1337,7 @@ process_get_authority (void *cls,
  *        data *will* be made available incrementally in
  *        upload_data)
  * @param upload_data_size set initially to the size of the
- *        upload_data provided; the method must update this
+ *        @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,
@@ -1767,485 +1354,456 @@ create_response (void *cls,
                  size_t *upload_data_size,
                  void **con_cls)
 {
-  struct MhdHttpList* hd = cls;
-  const char* page = "<html><head><title>gnoxy</title>"\
-                      "</head><body>cURL fail</body></html>";
-  
-  char curlurl[MAX_HTTP_URI_LENGTH]; // buffer overflow!
-  int ret = MHD_YES;
-
-  struct ProxyCurlTask *ctask;
-  struct ProxyPostData *fin_post;
-  
-  //FIXME handle
-  if ((0 != strcasecmp (meth, MHD_HTTP_METHOD_GET)) &&
-      (0 != strcasecmp (meth, MHD_HTTP_METHOD_PUT)) &&
-      (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST)))
+  /* struct MhdHttpList* hd = cls;  */
+  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 (NULL == *con_cls)
+  if ( (NULL == s5r->curl) &&
+       (SOCKS5_SOCKET_WITH_MHD == s5r->state) )
   {
-
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Got %s request for %s\n", meth, url);
-    ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
-    ctask->mhd = hd;
-    *con_cls = ctask;
-    
-    ctask->curl = curl_easy_init();
-    if (NULL == ctask->curl)
+    /* first time here, initialize curl handle */
+    sa = (const struct sockaddr *) &s5r->destination_address;
+    switch (sa->sa_family)
     {
-      ctask->response = MHD_create_response_from_buffer (strlen (page),
-                                                (void*)page,
-                                                MHD_RESPMEM_PERSISTENT);
-      ret = MHD_queue_response (con,
-                                MHD_HTTP_OK,
-                                ctask->response);
-      MHD_destroy_response (ctask->response);
-      GNUNET_free (ctask);
-      return ret;
+    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;
     }
-  
-    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;
-    ctask->port = HTTP_PORT;
-
-    MHD_get_connection_values (con,
-                               MHD_HEADER_KIND,
-                               &con_val_iter, ctask);
+    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); // FIXME: remove later
+    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);
 
     if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
     {
-      if (0 == *upload_data_size)
-      {
-        curl_easy_cleanup (ctask->curl);
-        GNUNET_free (ctask);
-        return MHD_NO;
-      }
-      ctask->put_read_offset = 0;
-      ctask->put_read_size = *upload_data_size;
-      curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1);
-      curl_easy_setopt (ctask->curl, CURLOPT_READDATA, upload_data);
-      //curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &curl_read_cb);
-      
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Got PUT data: %s\n", upload_data);
-      curl_easy_cleanup (ctask->curl);
-      GNUNET_free (ctask);
+      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))
+    {
+      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);
+    }
+    else
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                 _("Unsupported HTTP method `%s'\n"),
+                 meth);
+      curl_easy_cleanup (s5r->curl);
+      s5r->curl = NULL;
       return MHD_NO;
     }
 
-    if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
+    if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_0))
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Setting up POST processor\n");
-      ctask->post_handler = MHD_create_post_processor (con,
-                                 POSTBUFFERSIZE,
-                                 &con_post_data_iter,
-                                 ctask);
-      curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
-      curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION,
-                        &read_callback);
-      curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
-      ctask->headers = curl_slist_append (ctask->headers,
-                                          "Transfer-Encoding: chunked");
-      /*curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
-      curl_easy_setopt (ctask->curl, CURLOPT_POSTFIELDSIZE, *upload_data_size);
-      curl_easy_setopt (ctask->curl, CURLOPT_COPYPOSTFIELDS, upload_data);
-      
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Got POST data: %s\n", upload_data);
-      curl_easy_cleanup (ctask->curl);
-      GNUNET_free (ctask);
-      return MHD_NO;*/
+      curl_easy_setopt (s5r->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
     }
-
-    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)
+    else if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_1))
     {
-      sprintf (curlurl, "http://%s:%d%s", ctask->host, ctask->port, url);
-      MHD_get_connection_values (con,
-                                 MHD_GET_ARGUMENT_KIND,
-                                 &get_uri_val_iter, curlurl);
-      curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
+      curl_easy_setopt (s5r->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+    }
+    else
+    {
+      curl_easy_setopt (s5r->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE);
     }
-    strcpy (ctask->url, url);
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "MHD: Adding new curl task for %s%s\n", ctask->host, url);
-    MHD_get_connection_values (con,
-                               MHD_GET_ARGUMENT_KIND,
-                               &get_uri_val_iter, ctask->url);
-  
-    curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
-    curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
-    curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
-
-    GNUNET_GNS_get_authority (gns_handle,
-                              ctask->host,
-                              &process_get_authority,
-                              ctask);
-    ctask->ready_to_queue = GNUNET_NO;
-    ctask->fin = GNUNET_NO;
-    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;
-      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
     {
-      fin_post = GNUNET_malloc (sizeof (struct ProxyPostData));
-      GNUNET_CONTAINER_DLL_insert_tail (ctask->post_data_head,
-                                        ctask->post_data_tail,
-                                        fin_post);
-      ctask->post_done = GNUNET_YES;
-      return MHD_YES;
+      curl_easy_setopt (s5r->curl, CURLOPT_USE_SSL, CURLUSESSL_NONE);
     }
-  }
-  
-  if (GNUNET_YES != ctask->ready_to_queue)
-    return MHD_YES; /* wait longer */
-  
-  if (GNUNET_YES == ctask->fin)
+
+    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;
+    }
+    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);
 }
 
 
-/**
- * run all httpd
- */
-static void
-run_httpds ()
-{
-  struct MhdHttpList *hd;
-
-  for (hd=mhd_httpd_head; NULL != hd; hd = hd->next)
-    run_httpd (hd);
+/* ******************** MHD HTTP setup and event loop ******************** */
 
-}
 
 /**
- * schedule mhd
+ * Function called when MHD decides that we are done with a connection.
  *
- * @param hd the daemon to run
+ * @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`
+ * @param toe reason for request termination (ignored)
  */
 static void
-run_httpd (struct MhdHttpList *hd)
+mhd_completed_cb (void *cls,
+                 struct MHD_Connection *connection,
+                 void **con_cls,
+                 enum MHD_RequestTerminationCode toe)
 {
-  fd_set rs;
-  fd_set ws;
-  fd_set es;
-  struct GNUNET_NETWORK_FDSet *wrs;
-  struct GNUNET_NETWORK_FDSet *wws;
-  struct GNUNET_NETWORK_FDSet *wes;
-  int max;
-  int haveto;
-  unsigned MHD_LONG_LONG timeout;
-  struct GNUNET_TIME_Relative tv;
-
-  FD_ZERO (&rs);
-  FD_ZERO (&ws);
-  FD_ZERO (&es);
-  wrs = GNUNET_NETWORK_fdset_create ();
-  wes = GNUNET_NETWORK_fdset_create ();
-  wws = GNUNET_NETWORK_fdset_create ();
-  max = -1;
-  GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
-  
-  
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "MHD fds: max=%d\n", max);
-  
-  haveto = MHD_get_timeout (hd->daemon, &timeout);
+  struct Socks5Request *s5r = *con_cls;
 
-  if (MHD_YES == haveto)
-    tv.rel_value = (uint64_t) timeout;
-  else
-    tv = GNUNET_TIME_UNIT_FOREVER_REL;
-  GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
-  GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
-  GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
-  
-  if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
-    GNUNET_SCHEDULER_cancel (hd->httpd_task);
-  hd->httpd_task =
-    GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
-                                 tv, wrs, wws,
-                                 &do_httpd, hd);
-  GNUNET_NETWORK_fdset_destroy (wrs);
-  GNUNET_NETWORK_fdset_destroy (wws);
-  GNUNET_NETWORK_fdset_destroy (wes);
+  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;
 }
 
 
 /**
- * Task run whenever HTTP server operations are pending.
+ * Function called when MHD first processes an incoming connection.
+ * Gives us the respective URI information.
  *
- * @param cls unused
- * @param tc sched context
+ * We use this to associate the `struct MHD_Connection` with our
+ * internal `struct Socks5Request` data structure (by checking
+ * for matching sockets).
+ *
+ * @param cls the HTTP server handle (a `struct MhdHttpList`)
+ * @param url the URL that is being requested
+ * @param connection MHD connection object for the request
+ * @return the `struct Socks5Request` that this @a connection is for
  */
-static void
-do_httpd (void *cls,
-          const struct GNUNET_SCHEDULER_TaskContext *tc)
+static void *
+mhd_log_callback (void *cls,
+                 const char *url,
+                 struct MHD_Connection *connection)
 {
-  struct MhdHttpList *hd = cls;
-  
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "MHD: Main loop\n");
-  hd->httpd_task = GNUNET_SCHEDULER_NO_TASK; 
-  MHD_run (hd->daemon);
-  run_httpd (hd);
-}
+  struct Socks5Request *s5r;
+  const union MHD_ConnectionInfo *ci;
+  int sock;
 
+  ci = MHD_get_connection_info (connection,
+                               MHD_CONNECTION_INFO_CONNECTION_FD);
+  if (NULL == ci)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  sock = ci->connect_fd;
+  for (s5r = s5r_head; NULL != s5r; s5r = s5r->next)
+  {
+    if (GNUNET_NETWORK_get_fd (s5r->sock) == sock)
+    {
+      if (NULL != s5r->url)
+      {
+       GNUNET_break (0);
+       return NULL;
+      }
+      s5r->url = GNUNET_strdup (url);
+      GNUNET_SCHEDULER_cancel (s5r->timeout_task);
+      s5r->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+      return s5r;
+    }
+  }
+  return NULL;
+}
 
 
 /**
- * Read data from socket
+ * Kill the given MHD daemon.
  *
- * @param cls the closure
- * @param tc scheduler context
+ * @param hd daemon to stop
  */
 static void
-do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+kill_httpd (struct MhdHttpList *hd)
+{
+  GNUNET_CONTAINER_DLL_remove (mhd_httpd_head,
+                              mhd_httpd_tail,
+                              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;
+  GNUNET_free (hd);
+}
 
-/**
- * Read from remote end
- *
- * @param cls closure
- * @param tc scheduler context
- */
-static void
-do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
 
 /**
- * Write data to remote socket
+ * Task run whenever HTTP server is idle for too long. Kill it.
  *
- * @param cls the closure
- * @param tc scheduler context
+ * @param cls the `struct MhdHttpList *`
+ * @param tc sched context
  */
 static void
-do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+kill_httpd_task (void *cls,
+                const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  struct Socks5Request *s5r = cls;
-  unsigned int len;
-
-  s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
-
-  if ((NULL != tc->read_ready) &&
-      (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
-      ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
-                                         s5r->rbuf_len)>0)))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Successfully sent %d bytes to remote socket\n",
-                len);
-  }
-  else
-  {
-    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
-    if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
-      GNUNET_SCHEDULER_cancel (s5r->rtask);
-    if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
-      GNUNET_SCHEDULER_cancel (s5r->wtask);
-    if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
-      GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
-    GNUNET_NETWORK_socket_close (s5r->remote_sock);
-    GNUNET_NETWORK_socket_close (s5r->sock);
-    GNUNET_free(s5r);
-    return;
-  }
+  struct MhdHttpList *hd = cls;
 
-  s5r->rtask =
-    GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                   s5r->sock,
-                                   &do_read, s5r);
+  hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
+  kill_httpd (hd);
 }
 
 
 /**
- * Clean up s5r handles
+ * Task run whenever HTTP server operations are pending.
  *
- * @param s5r the handle to destroy
+ * @param cls the `struct MhdHttpList *` of the daemon that is being run
+ * @param tc sched context
  */
 static void
-cleanup_s5r (struct Socks5Request *s5r)
-{
-  if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
-    GNUNET_SCHEDULER_cancel (s5r->rtask);
-  if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdwtask)
-    GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
-  if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
-    GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
-  
-  if (NULL != s5r->remote_sock)
-    GNUNET_NETWORK_socket_close (s5r->remote_sock);
-  if ((NULL != s5r->sock) && (s5r->cleanup_sock == GNUNET_YES))
-    GNUNET_NETWORK_socket_close (s5r->sock);
-  
-  GNUNET_free(s5r);
-}
+do_httpd (void *cls,
+          const struct GNUNET_SCHEDULER_TaskContext *tc);
+
 
 /**
- * Write data to socket
+ * Schedule MHD.  This function should be called initially when an
+ * MHD is first getting its client socket, and will then automatically
+ * always be called later whenever there is work to be done.
  *
- * @param cls the closure
- * @param tc scheduler context
+ * @param hd the daemon to schedule
  */
 static void
-do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+schedule_httpd (struct MhdHttpList *hd)
 {
-  struct Socks5Request *s5r = cls;
-  unsigned int len;
-
-  s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
+  fd_set rs;
+  fd_set ws;
+  fd_set es;
+  struct GNUNET_NETWORK_FDSet *wrs;
+  struct GNUNET_NETWORK_FDSet *wws;
+  int max;
+  int haveto;
+  MHD_UNSIGNED_LONG_LONG timeout;
+  struct GNUNET_TIME_Relative tv;
 
-  if ((NULL != tc->read_ready) &&
-      (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
-      ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
-                                         s5r->wbuf_len)>0)))
+  FD_ZERO (&rs);
+  FD_ZERO (&ws);
+  FD_ZERO (&es);
+  max = -1;
+  if (MHD_YES != MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max))
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Successfully sent %d bytes to socket\n",
-                len);
+    kill_httpd (hd);
+    return;
   }
+  haveto = MHD_get_timeout (hd->daemon, &timeout);
+  if (MHD_YES == haveto)
+    tv.rel_value_us = (uint64_t) timeout * 1000LL;
   else
+    tv = GNUNET_TIME_UNIT_FOREVER_REL;
+  if (-1 != max)
   {
-    
-    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
-    s5r->cleanup = GNUNET_YES;
-    s5r->cleanup_sock = GNUNET_YES;
-    cleanup_s5r (s5r);
-    
-    return;
+    wrs = GNUNET_NETWORK_fdset_create ();
+    wws = GNUNET_NETWORK_fdset_create ();
+    GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
+    GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
   }
-
-  if (GNUNET_YES == s5r->cleanup)
+  else
   {
-    cleanup_s5r (s5r);
-    return;
+    wrs = NULL;
+    wws = NULL;
   }
-
-  if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
-      (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
-    s5r->fwdrtask =
-      GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                     s5r->remote_sock,
-                                     &do_read_remote, s5r);
+  if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
+    GNUNET_SCHEDULER_cancel (hd->httpd_task);
+  if ( (MHD_YES != haveto) &&
+       (-1 == max) &&
+       (hd != httpd) )
+  {
+    /* daemon is idle, kill after timeout */
+    hd->httpd_task = GNUNET_SCHEDULER_add_delayed (MHD_CACHE_TIMEOUT,
+                                                  &kill_httpd_task,
+                                                  hd);
+  }
+  else
+  {
+    hd->httpd_task =
+      GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+                                  tv, wrs, wws,
+                                  &do_httpd, hd);
+  }
+  if (NULL != wrs)
+    GNUNET_NETWORK_fdset_destroy (wrs);
+  if (NULL != wws)
+    GNUNET_NETWORK_fdset_destroy (wws);
 }
 
+
 /**
- * Read from remote end
+ * Task run whenever HTTP server operations are pending.
  *
- * @param cls closure
+ * @param cls the `struct MhdHttpList` of the daemon that is being run
  * @param tc scheduler context
  */
 static void
-do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_httpd (void *cls,
+          const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  struct Socks5Request *s5r = cls;
-  
-  s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
-
-
-  if ((NULL != tc->write_ready) &&
-      (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
-      (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
-                                         sizeof (s5r->wbuf))))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Successfully read %d bytes from remote socket\n",
-                s5r->wbuf_len);
-  }
-  else
-  {
-    if (s5r->wbuf_len == 0)
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "0 bytes received from remote... graceful shutdown!\n");
-    if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
-      GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
-    if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
-      GNUNET_SCHEDULER_cancel (s5r->rtask);
-    
-    GNUNET_NETWORK_socket_close (s5r->remote_sock);
-    s5r->remote_sock = NULL;
-    GNUNET_NETWORK_socket_close (s5r->sock);
-    GNUNET_free(s5r);
+  struct MhdHttpList *hd = cls;
 
-    return;
-  }
-  
-  s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                               s5r->sock,
-                                               &do_write, s5r);
-  
+  hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
+  MHD_run (hd->daemon);
+  schedule_httpd (hd);
 }
 
 
 /**
- * Adds a socket to MHD
+ * Run MHD now, we have extra data ready for the callback.
  *
- * @param h the handle to the socket to add
- * @param daemon the daemon to add the fd to
- * @return whatever MHD_add_connection returns
+ * @param hd the daemon to run now.
  */
-static int
-add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon)
+static void
+run_mhd_now (struct MhdHttpList *hd)
 {
-  int fd;
-  struct sockaddr *addr;
-  socklen_t len;
-
-  fd = dup (GNUNET_NETWORK_get_fd (h));
-  addr = GNUNET_NETWORK_get_addr (h);
-  len = GNUNET_NETWORK_get_addrlen (h);
-
-  return MHD_add_connection (daemon, fd, addr, len);
+  if (GNUNET_SCHEDULER_NO_TASK !=
+      hd->httpd_task)
+    GNUNET_SCHEDULER_cancel (hd->httpd_task);
+  hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd,
+                                            hd);
 }
 
+
 /**
  * Read file in filename
  *
  * @param filename file to read
  * @param size pointer where filesize is stored
- * @return data
+ * @return NULL on error
  */
-static char*
-load_file (const char* filename, 
+static void*
+load_file (const char* filename,
           unsigned int* size)
 {
-  char *buffer;
+  void *buffer;
   uint64_t fsize;
 
   if (GNUNET_OK !=
@@ -2270,15 +1828,16 @@ load_file (const char* filename,
  *
  * @param key where to store the data
  * @param keyfile path to the PEM file
- * @return GNUNET_OK on success
+ * @return #GNUNET_OK on success
  */
 static int
-load_key_from_file (gnutls_x509_privkey_t key, const char* keyfile)
+load_key_from_file (gnutls_x509_privkey_t key,
+                   const char* keyfile)
 {
   gnutls_datum_t key_data;
   int ret;
 
-  key_data.data = (unsigned char*) load_file (keyfile, &key_data.size);
+  key_data.data = load_file (keyfile, &key_data.size);
   ret = gnutls_x509_privkey_import (key, &key_data,
                                     GNUTLS_X509_FMT_PEM);
   if (GNUTLS_E_SUCCESS != ret)
@@ -2286,9 +1845,8 @@ load_key_from_file (gnutls_x509_privkey_t key, const char* keyfile)
     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;
 }
 
@@ -2298,25 +1856,24 @@ load_key_from_file (gnutls_x509_privkey_t key, const char* keyfile)
  *
  * @param crt struct to store data in
  * @param certfile path to pem file
- * @return GNUNET_OK on success
+ * @return #GNUNET_OK on success
  */
 static int
-load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
+load_cert_from_file (gnutls_x509_crt_t crt,
+                    const char* certfile)
 {
   gnutls_datum_t cert_data;
-  cert_data.data = NULL;
   int ret;
 
-  cert_data.data = (unsigned char*) load_file (certfile, &cert_data.size);
+  cert_data.data = load_file (certfile, &cert_data.size);
   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;
 }
 
@@ -2325,469 +1882,613 @@ load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
  * Generate new certificate for specific name
  *
  * @param name the subject name to generate a cert for
- * @return a struct holding the PEM data
+ * @return a struct holding the PEM data, NULL on error
  */
 static struct ProxyGNSCertificate *
 generate_gns_certificate (const char *name)
 {
-
-  int ret;
   unsigned int serial;
   size_t key_buf_size;
   size_t cert_buf_size;
   gnutls_x509_crt_t request;
   time_t etime;
   struct tm *tm_data;
+  struct ProxyGNSCertificate *pgc;
 
-  ret = gnutls_x509_crt_init (&request);
-
-  if (GNUTLS_E_SUCCESS != ret)
-  {
-    GNUNET_break (0);
-  }
-
-  ret = gnutls_x509_crt_set_key (request, proxy_ca.key);
-
-  if (GNUTLS_E_SUCCESS != ret)
-  {
-    GNUNET_break (0);
-  }
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
-
-  struct ProxyGNSCertificate *pgc =
-    GNUNET_malloc (sizeof (struct ProxyGNSCertificate));
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\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, "DE", 2);
-
+                                 0, "TNR", 2);
   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
-                                 0, "GNUnet", 6);
-
+                                 0, "GNU Name System", 4);
   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
                                  0, name, strlen (name));
-
-  ret = gnutls_x509_crt_set_version (request, 3);
-
-  ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
-
+  GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_version (request, 3));
+  gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
+  gnutls_x509_crt_set_serial (request,
+                             &serial,
+                             sizeof (serial));
   etime = time (NULL);
   tm_data = localtime (&etime);
-  
-
-  ret = gnutls_x509_crt_set_serial (request,
-                                    &serial,
-                                    sizeof (serial));
-
-  ret = gnutls_x509_crt_set_activation_time (request,
-                                             etime);
+  gnutls_x509_crt_set_activation_time (request,
+                                      etime);
   tm_data->tm_year++;
   etime = mktime (tm_data);
-
-  if (-1 == etime)
-  {
-    GNUNET_break (0);
-  }
-
-  ret = gnutls_x509_crt_set_expiration_time (request,
-                                             etime);
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
-
-  ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
-
+  gnutls_x509_crt_set_expiration_time (request,
+                                      etime);
+  gnutls_x509_crt_sign (request,
+                       proxy_ca.cert,
+                       proxy_ca.key);
   key_buf_size = sizeof (pgc->key);
   cert_buf_size = sizeof (pgc->cert);
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Exporting certificate...\n");
-  
   gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
                           pgc->cert, &cert_buf_size);
-
   gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
-                          pgc->key, &key_buf_size);
-
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
+                             pgc->key, &key_buf_size);
   gnutls_x509_crt_deinit (request);
-
   return pgc;
-
 }
 
 
-/*
- * Accept policy for mhdaemons
+/**
+ * Lookup (or create) an SSL MHD instance for a particular domain.
  *
- * @param cls NULL
- * @param addr the sockaddr
- * @param addrlen the sockaddr length
- * @return MHD_NO if sockaddr is wrong or #conns too high
+ * @param domain the domain the SSL daemon has to serve
+ * @return NULL on errro
  */
-static int
-accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen)
+static struct MhdHttpList *
+lookup_ssl_httpd (const char* domain)
 {
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "In MHD accept policy cb\n");
-
-  if (addr != NULL)
-  {
-    if (addr->sa_family == AF_UNIX)
-      return MHD_NO;
-  }
+  struct MhdHttpList *hd;
+  struct ProxyGNSCertificate *pgc;
 
+  for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
+    if ( (NULL != hd->domain) &&
+        (0 == strcmp (hd->domain, domain)) )
+      return hd;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Connection accepted\n");
-
-  return MHD_YES;
+             "Starting fresh MHD HTTPS instance for domain `%s'\n",
+             domain);
+  pgc = generate_gns_certificate (domain);
+  hd = GNUNET_new (struct MhdHttpList);
+  hd->is_ssl = GNUNET_YES;
+  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,
+                                NULL, NULL,
+                                &create_response, hd,
+                                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_HTTPS_MEM_KEY, pgc->key,
+                                MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
+                                MHD_OPTION_END);
+  if (NULL == hd->daemon)
+  {
+    GNUNET_free (pgc);
+    GNUNET_free (hd);
+    return NULL;
+  }
+  GNUNET_CONTAINER_DLL_insert (mhd_httpd_head,
+                              mhd_httpd_tail,
+                              hd);
+  return hd;
 }
 
 
 /**
- * Adds a socket to an SSL MHD instance
- * It is important the the domain name is
- * correct. In most cases we need to start a new daemon
+ * Task run when a Socks5Request somehow fails to be associated with
+ * an MHD connection (i.e. because the client never speaks HTTP after
+ * the SOCKS5 handshake).  Clean up.
  *
- * @param h the handle to add to a daemon
- * @param domain the domain the ssl daemon has to serve
- * @return MHD_YES on success
+ * @param cls the `struct Socks5Request *`
+ * @param tc sched context
  */
-static int
-add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, const char* domain)
+static void
+timeout_s5r_handshake (void *cls,
+                      const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  struct MhdHttpList *hd = NULL;
-  struct ProxyGNSCertificate *pgc;
-  struct NetworkHandleList *nh;
-
-  for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
-    if (0 == strcmp (hd->domain, domain))
-      break;
+  struct Socks5Request *s5r = cls;
 
-  if (NULL == hd)
-  {    
-    pgc = generate_gns_certificate (domain);
-    
-    hd = GNUNET_malloc (sizeof (struct MhdHttpList));
-    hd->is_ssl = GNUNET_YES;
-    strcpy (hd->domain, domain);
-    hd->proxy_cert = pgc;
+  s5r->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+  cleanup_s5r (s5r);
+}
 
-    /* Start new MHD */
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "No previous SSL instance found... starting new one for %s\n",
-                domain);
-
-    hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL, 4444,
-            &accept_cb, NULL,
-            &create_response, hd,
-            MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
-            MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
-            MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
-            MHD_OPTION_NOTIFY_COMPLETED,
-            NULL, NULL,
-            MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
-            MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
-            MHD_OPTION_END);
-
-    GNUNET_assert (hd->daemon != NULL);
-    hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
-    
-    GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
-  }
 
-  nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
-  nh->h = h;
+/**
+ * We're done with the Socks5 protocol, now we need to pass the
+ * 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).
+ *
+ * @param s5r socks request that has reached the final stage
+ */
+static void
+setup_data_transfer (struct Socks5Request *s5r)
+{
+  struct MhdHttpList *hd;
+  int fd;
+  const struct sockaddr *addr;
+  socklen_t len;
 
-  GNUNET_CONTAINER_DLL_insert (hd->socket_handles_head,
-                               hd->socket_handles_tail,
-                               nh);
-  
-  return add_handle_to_mhd (h, hd->daemon);
+  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:
+  default:
+    GNUNET_assert (NULL != httpd);
+    hd = httpd;
+    break;
+  }
+  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))
+  {
+    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);
 }
 
 
+/* ********************* SOCKS handling ************************* */
+
 
 /**
- * Read data from incoming connection
+ * Write data from buffer to socks5 client, then continue with state machine.
  *
- * @param cls the closure
- * @param tc the scheduler context
+ * @param cls the closure with the `struct Socks5Request`
+ * @param tc scheduler context
  */
 static void
-do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_write (void *cls,
+         const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct Socks5Request *s5r = cls;
-  struct socks5_client_hello *c_hello;
-  struct socks5_server_hello *s_hello;
-  struct socks5_client_request *c_req;
-  struct socks5_server_response *s_resp;
-
-  int ret;
-  char domain[256];
-  uint8_t dom_len;
-  uint16_t req_port;
-  struct hostent *phost;
-  uint32_t remote_ip;
-  struct sockaddr_in remote_addr;
-  struct in_addr *r_sin_addr;
-
-  struct NetworkHandleList *nh;
+  ssize_t len;
 
-  s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
-
-  if ((NULL != tc->write_ready) &&
-      (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
-      (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
-                                         sizeof (s5r->rbuf))))
+  s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
+  len = GNUNET_NETWORK_socket_send (s5r->sock,
+                                   s5r->wbuf,
+                                   s5r->wbuf_len);
+  if (len <= 0)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Successfully read %d bytes from socket\n",
-                s5r->rbuf_len);
+    /* write error: connection closed, shutdown, etc.; just clean up */
+    cleanup_s5r (s5r);
+    return;
   }
-  else
+  memmove (s5r->wbuf,
+          &s5r->wbuf[len],
+          s5r->wbuf_len - len);
+  s5r->wbuf_len -= len;
+  if (s5r->wbuf_len > 0)
   {
-    if (s5r->rbuf_len != 0)
-      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
-    else
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
-
-    if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
-      GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
-    if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
-      GNUNET_SCHEDULER_cancel (s5r->wtask);
-    if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
-      GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
-    GNUNET_NETWORK_socket_close (s5r->remote_sock);
-    GNUNET_NETWORK_socket_close (s5r->sock);
-    GNUNET_free(s5r);
+    /* not done writing */
+    s5r->wtask =
+      GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                     s5r->sock,
+                                     &do_write, s5r);
     return;
   }
 
-  if (s5r->state == SOCKS5_INIT)
+  /* we're done writing, continue with state machine! */
+
+  switch (s5r->state)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "SOCKS5 init\n");
-    c_hello = (struct socks5_client_hello*)&s5r->rbuf;
+  case SOCKS5_INIT:
+    GNUNET_assert (0);
+    break;
+  case SOCKS5_REQUEST:
+    GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s5r->rtask);
+    break;
+  case SOCKS5_DATA_TRANSFER:
+    setup_data_transfer (s5r);
+    return;
+  case SOCKS5_WRITE_THEN_CLEANUP:
+    cleanup_s5r (s5r);
+    return;
+  default:
+    GNUNET_break (0);
+    break;
+  }
+}
 
-    GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
 
-    s_hello = (struct socks5_server_hello*)&s5r->wbuf;
-    s5r->wbuf_len = sizeof( struct socks5_server_hello );
+/**
+ * Return a server response message indicating a failure to the client.
+ *
+ * @param s5r request to return failure code for
+ * @param sc status code to return
+ */
+static void
+signal_socks_failure (struct Socks5Request *s5r,
+                     enum Socks5StatusCode sc)
+{
+  struct Socks5ServerResponseMessage *s_resp;
+
+  s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf[s5r->wbuf_len];
+  memset (s_resp, 0, sizeof (struct Socks5ServerResponseMessage));
+  s_resp->version = SOCKS_VERSION_5;
+  s_resp->reply = sc;
+  s5r->state = SOCKS5_WRITE_THEN_CLEANUP;
+  if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
+    s5r->wtask =
+      GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                     s5r->sock,
+                                     &do_write, s5r);
+}
 
-    s_hello->version = c_hello->version;
-    s_hello->auth_method = SOCKS_AUTH_NONE;
 
-    /* Write response to client */
-    s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                                s5r->sock,
-                                                &do_write, s5r);
+/**
+ * Return a server response message indicating success.
+ *
+ * @param s5r request to return success status message for
+ */
+static void
+signal_socks_success (struct Socks5Request *s5r)
+{
+  struct Socks5ServerResponseMessage *s_resp;
+
+  s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf[s5r->wbuf_len];
+  s_resp->version = SOCKS_VERSION_5;
+  s_resp->reply = SOCKS5_STATUS_REQUEST_GRANTED;
+  s_resp->reserved = 0;
+  s_resp->addr_type = SOCKS5_AT_IPV4;
+  /* zero out IPv4 address and port */
+  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)
+    s5r->wtask =
+      GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                     s5r->sock,
+                                     &do_write, s5r);
+}
 
-    s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                                s5r->sock,
-                                                &do_read, s5r);
 
-    s5r->state = SOCKS5_REQUEST;
-    return;
-  }
+/**
+ * Process GNS results for target domain.
+ *
+ * @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)
+{
+  struct Socks5Request *s5r = cls;
+  uint32_t i;
+  const struct GNUNET_NAMESTORE_RecordData *r;
+  int got_ip;
 
-  if (s5r->state == SOCKS5_REQUEST)
+  s5r->gns_lookup = NULL;
+  got_ip = GNUNET_NO;
+  for (i=0;i<rd_count;i++)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Processing SOCKS5 request\n");
-    c_req = (struct socks5_client_request*)&s5r->rbuf;
-    s_resp = (struct socks5_server_response*)&s5r->wbuf;
-    //Only 10byte for ipv4 response!
-    s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
-
-    GNUNET_assert (c_req->addr_type == 3);
-
-    dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
-    memset(domain, 0, sizeof(domain));
-    strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
-    req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
-
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Requested connection is %s:%d\n",
-                domain,
-                ntohs(req_port));
-
-    if (is_tld (domain, GNUNET_GNS_TLD) ||
-        is_tld (domain, GNUNET_GNS_TLD_ZKEY))
+    r = &rd[i];
+    switch (r->record_type)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Requested connection is gnunet tld\n",
-                  domain);
-      
-      ret = MHD_NO;
-      if (ntohs(req_port) == HTTPS_PORT)
+    case GNUNET_DNSPARSER_TYPE_A:
       {
-        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                    "Requested connection is HTTPS\n");
-        ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
+       struct sockaddr_in *in;
+
+       if (sizeof (struct in_addr) != r->data_size)
+       {
+         GNUNET_break_op (0);
+         break;
+       }
+       if (GNUNET_YES == got_ip)
+         break;
+       if (GNUNET_OK !=
+           GNUNET_NETWORK_test_pf (PF_INET))
+         break;
+       got_ip = GNUNET_YES;
+       in = (struct sockaddr_in *) &s5r->destination_address;
+       in->sin_family = AF_INET;
+       memcpy (&in->sin_addr,
+               r->data,
+               r->data_size);
+       in->sin_port = htons (s5r->port);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+       in->sin_len = sizeof (*in);
+#endif
       }
-      else if (NULL != httpd)
+      break;
+    case GNUNET_DNSPARSER_TYPE_AAAA:
       {
-        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                    "Requested connection is HTTP\n");
-        nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
-        nh->h = s5r->sock;
+       struct sockaddr_in6 *in;
+
+       if (sizeof (struct in6_addr) != r->data_size)
+       {
+         GNUNET_break_op (0);
+         break;
+       }
+       if (GNUNET_YES == got_ip)
+         break;
+       if (GNUNET_OK !=
+           GNUNET_NETWORK_test_pf (PF_INET))
+         break;
+       /* FIXME: allow user to disable IPv6 per configuration option... */
+       got_ip = GNUNET_YES;
+       in = (struct sockaddr_in6 *) &s5r->destination_address;
+       in->sin6_family = AF_INET6;
+       memcpy (&in->sin6_addr,
+               r->data,
+               r->data_size);
+       in->sin6_port = htons (s5r->port);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+       in->sin6_len = sizeof (*in);
+#endif
+      }
+      break;
+    case GNUNET_NAMESTORE_TYPE_VPN:
+      GNUNET_break (0); /* should have been translated within GNS */
+      break;
+    case GNUNET_NAMESTORE_TYPE_LEHO:
+      GNUNET_free_non_null (s5r->leho);
+      s5r->leho = GNUNET_strndup (r->data,
+                                 r->data_size);
+      break;
+    default:
+      /* don't care */
+      break;
+    }
+  }
+  if (GNUNET_YES != got_ip)
+  {
+    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);
+}
 
-        GNUNET_CONTAINER_DLL_insert (mhd_httpd_head->socket_handles_head,
-                               mhd_httpd_head->socket_handles_tail,
-                               nh);
 
-        ret = add_handle_to_mhd ( s5r->sock, httpd );
-      }
+/**
+ * Remove the first @a len bytes from the beginning of the read buffer.
+ *
+ * @param s5r the handle clear the read buffer for
+ * @param len number of bytes in read buffer to advance
+ */
+static void
+clear_from_s5r_rbuf (struct Socks5Request *s5r,
+                    size_t len)
+{
+  GNUNET_assert (len <= s5r->rbuf_len);
+  memmove (s5r->rbuf,
+          &s5r->rbuf[len],
+          s5r->rbuf_len - len);
+  s5r->rbuf_len -= len;
+}
 
-      if (ret != MHD_YES)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    _("Failed to start HTTP server\n"));
-        s_resp->version = 0x05;
-        s_resp->reply = 0x01;
-        s5r->cleanup = GNUNET_YES;
-        s5r->cleanup_sock = GNUNET_YES;
-        s5r->wtask = 
-          GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                        s5r->sock,
-                                        &do_write, s5r);
-        return;
-      }
-      
-      /* Signal success */
-      s_resp->version = 0x05;
-      s_resp->reply = 0x00;
-      s_resp->reserved = 0x00;
-      s_resp->addr_type = 0x01;
-      
-      s5r->cleanup = GNUNET_YES;
-      s5r->cleanup_sock = GNUNET_NO;
-      s5r->wtask =
-        GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                        s5r->sock,
-                                        &do_write, s5r);
-      run_httpds ();
+
+/**
+ * Read data from incoming Socks5 connection
+ *
+ * @param cls the closure with the `struct Socks5Request`
+ * @param tc the scheduler context
+ */
+static void
+do_s5r_read (void *cls,
+            const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct Socks5Request *s5r = cls;
+  const struct Socks5ClientHelloMessage *c_hello;
+  struct Socks5ServerHelloMessage *s_hello;
+  const struct Socks5ClientRequestMessage *c_req;
+  ssize_t rlen;
+  size_t alen;
+
+  s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
+  if ( (NULL != tc->read_ready) &&
+       (GNUNET_NETWORK_fdset_isset (tc->read_ready, 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,
+                 "socks5 client disconnected.\n");
+      cleanup_s5r (s5r);
+      return;
+    }
+    s5r->rbuf_len += rlen;
+  }
+  s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                             s5r->sock,
+                                             &do_s5r_read, s5r);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Processing %u bytes of socks data in state %d\n",
+             s5r->rbuf_len,
+             s5r->state);
+  switch (s5r->state)
+  {
+  case SOCKS5_INIT:
+    c_hello = (const struct Socks5ClientHelloMessage*) &s5r->rbuf;
+    if ( (s5r->rbuf_len < sizeof (struct Socks5ClientHelloMessage)) ||
+        (s5r->rbuf_len < sizeof (struct Socks5ClientHelloMessage) + c_hello->num_auth_methods) )
+      return; /* need more data */
+    if (SOCKS_VERSION_5 != c_hello->version)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("Unsupported socks version %d\n"),
+                 (int) c_hello->version);
+      cleanup_s5r (s5r);
       return;
     }
-    else
+    clear_from_s5r_rbuf (s5r,
+                        sizeof (struct Socks5ClientHelloMessage) + c_hello->num_auth_methods);
+    GNUNET_assert (0 == s5r->wbuf_len);
+    s_hello = (struct Socks5ServerHelloMessage *) &s5r->wbuf;
+    s5r->wbuf_len = sizeof (struct Socks5ServerHelloMessage);
+    s_hello->version = SOCKS_VERSION_5;
+    s_hello->auth_method = SOCKS_AUTH_NONE;
+    GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == s5r->wtask);
+    s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                                s5r->sock,
+                                                &do_write, s5r);
+    s5r->state = SOCKS5_REQUEST;
+    return;
+  case SOCKS5_REQUEST:
+    c_req = (const struct Socks5ClientRequestMessage *) &s5r->rbuf;
+    if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage))
+      return;
+    switch (c_req->command)
+    {
+    case SOCKS5_CMD_TCP_STREAM:
+      /* handled below */
+      break;
+    default:
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("Unsupported socks command %d\n"),
+                 (int) c_req->command);
+      signal_socks_failure (s5r,
+                           SOCKS5_STATUS_COMMAND_NOT_SUPPORTED);
+      return;
+    }
+    switch (c_req->addr_type)
     {
-      phost = (struct hostent*)gethostbyname (domain);
-      if (phost == NULL)
+    case SOCKS5_AT_IPV4:
       {
-        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                    "Resolve %s error!\n", domain );
-        s_resp->version = 0x05;
-        s_resp->reply = 0x01;
-        s5r->cleanup = GNUNET_YES;
-        s5r->cleanup_sock = GNUNET_YES;
-        s5r->wtask = 
-          GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                          s5r->sock,
-                                          &do_write, s5r);
-        return;
+       const struct in_addr *v4 = (const struct in_addr *) &c_req[1];
+       const uint16_t *port = (const uint16_t *) &v4[1];
+       struct sockaddr_in *in;
+
+       s5r->port = ntohs (*port);
+       alen = sizeof (struct in_addr);
+       if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
+           alen + sizeof (uint16_t))
+         return; /* need more data */
+       in = (struct sockaddr_in *) &s5r->destination_address;
+       in->sin_family = AF_INET;
+       in->sin_addr = *v4;
+       in->sin_port = *port;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+       in->sin_len = sizeof (*in);
+#endif
+       s5r->state = SOCKS5_DATA_TRANSFER;
       }
-
-      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;
+      break;
+    case SOCKS5_AT_IPV6:
+      {
+       const struct in6_addr *v6 = (const struct in6_addr *) &c_req[1];
+       const uint16_t *port = (const uint16_t *) &v6[1];
+       struct sockaddr_in6 *in;
+
+       s5r->port = ntohs (*port);
+       alen = sizeof (struct in6_addr);
+       if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
+           alen + sizeof (uint16_t))
+         return; /* need more data */
+       in = (struct sockaddr_in6 *) &s5r->destination_address;
+       in->sin6_family = AF_INET6;
+       in->sin6_addr = *v6;
+       in->sin6_port = *port;
 #if HAVE_SOCKADDR_IN_SIN_LEN
-      remote_addr.sin_len = sizeof (remote_addr);
+       in->sin6_len = sizeof (*in);
 #endif
-      remote_addr.sin_addr.s_addr = remote_ip;
-      remote_addr.sin_port = req_port;
-      
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
-                  ntohs(req_port));
-
-      if ((GNUNET_OK !=
-          GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
-                                          (const struct sockaddr*)&remote_addr,
-                                          sizeof (remote_addr)))
-          && (errno != EINPROGRESS))
+       s5r->state = SOCKS5_DATA_TRANSFER;
+      }
+      break;
+    case SOCKS5_AT_DOMAINNAME:
       {
-        GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
-        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                    "socket request error...\n");
-        s_resp->version = 0x05;
-        s_resp->reply = 0x01;
-        s5r->wtask =
-          GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                          s5r->sock,
-                                          &do_write, s5r);
-        //TODO see above
-        return;
+       const uint8_t *dom_len;
+       const char *dom_name;
+       const uint16_t *port;
+
+       dom_len = (const uint8_t *) &c_req[1];
+       alen = *dom_len + 1;
+       if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
+           alen + sizeof (uint16_t))
+         return; /* need more data */
+       dom_name = (const char *) &dom_len[1];
+       port = (const uint16_t*) &dom_name[*dom_len];
+       s5r->domain = GNUNET_strndup (dom_name, *dom_len);
+       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                   "Requested connection is to %s:%d\n",
+                   s5r->domain,
+                   ntohs (*port));
+       s5r->state = SOCKS5_RESOLVING;
+       s5r->port = ntohs (*port);
+       s5r->gns_lookup = GNUNET_GNS_lookup (gns_handle,
+                                            s5r->domain,
+                                            &local_gns_zone,
+                                            GNUNET_DNSPARSER_TYPE_A,
+                                            GNUNET_NO /* only cached */,
+                                            (GNUNET_YES == do_shorten) ? &local_shorten_zone : NULL,
+                                            &handle_gns_result,
+                                            s5r);
+       break;
       }
-
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "new remote connection\n");
-
-      s_resp->version = 0x05;
-      s_resp->reply = 0x00;
-      s_resp->reserved = 0x00;
-      s_resp->addr_type = 0x01;
-
-      s5r->state = SOCKS5_DATA_TRANSFER;
-
-      s5r->wtask =
-        GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                        s5r->sock,
-                                        &do_write, s5r);
-      s5r->rtask =
-        GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                       s5r->sock,
-                                       &do_read, s5r);
-
+    default:
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("Unsupported socks address type %d\n"),
+                 (int) c_req->addr_type);
+      signal_socks_failure (s5r,
+                           SOCKS5_STATUS_ADDRESS_TYPE_NOT_SUPPORTED);
+      return;
     }
-    return;
-  }
-
-  if (s5r->state == SOCKS5_DATA_TRANSFER)
-  {
-    if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
+    clear_from_s5r_rbuf (s5r,
+                        sizeof (struct Socks5ClientRequestMessage) +
+                        alen + sizeof (uint16_t));
+    if (0 != s5r->rbuf_len)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Closing connection to client\n");
-      if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
-        GNUNET_SCHEDULER_cancel (s5r->rtask);
-      if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
-        GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
-      if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
-        GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
-      if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
-        GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
-      
-      if (s5r->remote_sock != NULL)
-        GNUNET_NETWORK_socket_close (s5r->remote_sock);
-      GNUNET_NETWORK_socket_close (s5r->sock);
-      GNUNET_free(s5r);
+      /* read more bytes than healthy, why did the client send more!? */
+      GNUNET_break_op (0);
+      signal_socks_failure (s5r,
+                           SOCKS5_STATUS_GENERAL_FAILURE);
       return;
     }
-
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "forwarding %d bytes from client\n", s5r->rbuf_len);
-
-    s5r->fwdwtask =
-      GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                      s5r->remote_sock,
-                                      &do_write_remote, s5r);
-
-    if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
+    if (SOCKS5_DATA_TRANSFER == s5r->state)
     {
-      s5r->fwdrtask =
-        GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                       s5r->remote_sock,
-                                       &do_read_remote, s5r);
+      /* if we are not waiting for GNS resolution, signal success */
+      signal_socks_success (s5r);
     }
-
-
+    /* We are done reading right now */
+    GNUNET_SCHEDULER_cancel (s5r->rtask);
+    s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
+    return;
+  case SOCKS5_RESOLVING:
+    GNUNET_assert (0);
+    return;
+  case SOCKS5_DATA_TRANSFER:
+    GNUNET_assert (0);
+    return;
+  default:
+    GNUNET_assert (0);
+    return;
   }
-
 }
 
 
@@ -2798,7 +2499,8 @@ do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  * @param tc the scheduler context
  */
 static void
-do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_accept (void *cls,
+          const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   struct GNUNET_NETWORK_Handle *s;
   struct Socks5Request *s5r;
@@ -2806,34 +2508,32 @@ do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   ltask = GNUNET_SCHEDULER_NO_TASK;
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
     return;
-
   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
                                          lsock,
                                          &do_accept, NULL);
-
   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
-
   if (NULL == s)
   {
     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
     return;
   }
-
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Got an inbound connection, waiting for data\n");
-
-  s5r = GNUNET_malloc (sizeof (struct Socks5Request));
+  s5r = GNUNET_new (struct Socks5Request);
+  GNUNET_CONTAINER_DLL_insert (s5r_head,
+                              s5r_tail,
+                              s5r);
   s5r->sock = s;
   s5r->state = SOCKS5_INIT;
-  s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
-  s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
-  s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
                                               s5r->sock,
-                                              &do_read, s5r);
+                                              &do_s5r_read, s5r);
 }
 
 
+/* ******************* General / main code ********************* */
+
+
 /**
  * Task run on shutdown
  *
@@ -2844,438 +2544,340 @@ static void
 do_shutdown (void *cls,
              const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-
-  struct MhdHttpList *hd;
-  struct MhdHttpList *tmp_hd;
-  struct NetworkHandleList *nh;
-  struct NetworkHandleList *tmp_nh;
-  struct ProxyCurlTask *ctask;
-  struct ProxyCurlTask *ctask_tmp;
-  
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Shutting down...\n");
-
-  gnutls_global_deinit ();
-  if (NULL != local_gns_zone)
-    GNUNET_free (local_gns_zone); 
-  if (NULL != local_private_zone)
-    GNUNET_free (local_private_zone);
-  if (NULL != local_shorten_zone)
-    GNUNET_free (local_shorten_zone);
-
+  while (NULL != mhd_httpd_head)
+    kill_httpd (mhd_httpd_head);
+  while (NULL != s5r_head)
+    cleanup_s5r (s5r_head);
+  if (NULL != lsock)
+  {
+    GNUNET_NETWORK_socket_close (lsock);
+    lsock = NULL;
+  }
+  if (NULL != id_op)
+  {
+    GNUNET_IDENTITY_cancel (id_op);
+    id_op = NULL;
+  }
+  if (NULL != identity)
+  {
+    GNUNET_IDENTITY_disconnect (identity);
+    identity = NULL;
+  }
+  if (NULL != curl_multi)
+  {
+    curl_multi_cleanup (curl_multi);
+    curl_multi = NULL;
+  }
+  if (NULL != gns_handle)
+  {
+    GNUNET_GNS_disconnect (gns_handle);
+    gns_handle = NULL;
+  }
   if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
   {
     GNUNET_SCHEDULER_cancel (curl_download_task);
     curl_download_task = GNUNET_SCHEDULER_NO_TASK;
   }
-
-  for (hd = mhd_httpd_head; hd != NULL; hd = tmp_hd)
+  if (GNUNET_SCHEDULER_NO_TASK != ltask)
   {
-    tmp_hd = hd->next;
-
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Stopping daemon\n");
-
-    if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Stopping select task %d\n",
-                  hd->httpd_task);
-      GNUNET_SCHEDULER_cancel (hd->httpd_task);
-      hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
-    }
-
-    if (NULL != hd->daemon)
-    {
-      MHD_stop_daemon (hd->daemon);
-      hd->daemon = NULL;
-    }
-
-    for (nh = hd->socket_handles_head; nh != NULL; nh = tmp_nh)
-    {
-      tmp_nh = nh->next;
-
-      GNUNET_NETWORK_socket_close (nh->h);
-
-      GNUNET_free (nh);
-    }
-
-    if (NULL != hd->proxy_cert)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Free certificate\n");
-      GNUNET_free (hd->proxy_cert);
-    }
-
-    GNUNET_free (hd);
+    GNUNET_SCHEDULER_cancel (ltask);
+    ltask = GNUNET_SCHEDULER_NO_TASK;
   }
+  gnutls_x509_crt_deinit (proxy_ca.cert);
+  gnutls_x509_privkey_deinit (proxy_ca.key);
+  gnutls_global_deinit ();
+}
 
-  for (ctask=ctasks_head; ctask != NULL; ctask=ctask_tmp)
-  {
-    ctask_tmp = ctask->next;
-
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Cleaning up cURL task\n");
-
-    if (ctask->curl != NULL)
-      curl_easy_cleanup (ctask->curl);
-    ctask->curl = NULL;
-    if (NULL != ctask->headers)
-      curl_slist_free_all (ctask->headers);
-    if (NULL != ctask->resolver)
-      curl_slist_free_all (ctask->resolver);
-
-    if (NULL != ctask->response)
-      MHD_destroy_response (ctask->response);
 
+/**
+ * Create an IPv4 listen socket bound to our port.
+ *
+ * @return NULL on error
+ */
+static struct GNUNET_NETWORK_Handle *
+bind_v4 ()
+{
+  struct GNUNET_NETWORK_Handle *ls;
+  struct sockaddr_in sa4;
+  int eno;
 
-    GNUNET_free (ctask);
+  memset (&sa4, 0, sizeof (sa4));
+  sa4.sin_family = AF_INET;
+  sa4.sin_port = htons (port);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+  sa4.sin_len = sizeof (sa4);
+#endif
+  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)))
+  {
+    eno = errno;
+    GNUNET_NETWORK_socket_close (ls);
+    errno = eno;
+    return NULL;
   }
-  curl_multi_cleanup (curl_multi);
-
-  GNUNET_GNS_disconnect (gns_handle);
+  return ls;
 }
 
 
 /**
- * Compiles a regex for us
+ * Create an IPv6 listen socket bound to our port.
  *
- * @param re ptr to re struct
- * @param rt the expression to compile
- * @return 0 on success
+ * @return NULL on error
  */
-static int
-compile_regex (regex_t *re, const char* rt)
+static struct GNUNET_NETWORK_Handle *
+bind_v6 ()
 {
-  int status;
-  char err[1024];
+  struct GNUNET_NETWORK_Handle *ls;
+  struct sockaddr_in6 sa6;
+  int eno;
 
-  status = regcomp (re, rt, REG_EXTENDED|REG_NEWLINE);
-  if (status)
+  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 (ls, (const struct sockaddr *) &sa6,
+                                 sizeof (sa6)))
   {
-    regerror (status, re, err, 1024);
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Regex error compiling '%s': %s\n", rt, err);
-    return 1;
+    eno = errno;
+    GNUNET_NETWORK_socket_close (ls);
+    errno = eno;
+    return NULL;
   }
-  return 0;
+  return ls;
 }
 
 
 /**
- * Loads the users local zone key
- *
- * @return GNUNET_YES on success
+ * Continue initialization after we have our zone information.
  */
-static int
-load_local_zone_key (const struct GNUNET_CONFIGURATION_Handle *cfg)
+static void
+run_cont ()
 {
-  char *keyfile;
-  struct GNUNET_CRYPTO_RsaPrivateKey *key = NULL;
-  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
-  struct GNUNET_CRYPTO_ShortHashCode *zone = NULL;
-  struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename;
+  struct MhdHttpList *hd;
 
-  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
-                                                            "ZONEKEY", &keyfile))
+  /* Open listen socket for socks proxy */
+  lsock = bind_v6 ();
+  if (NULL == lsock)
+    lsock = bind_v4 ();
+  if (NULL == lsock)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unable to load zone key config value!\n");
-    return GNUNET_NO;
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+  if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
+  {
+    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 (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
+  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unable to load zone key %s!\n", keyfile);
-    GNUNET_free(keyfile);
-    return GNUNET_NO;
-  }
-
-  key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
-  GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
-  local_gns_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
-  GNUNET_CRYPTO_short_hash(&pkey,
-                           sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
-                           local_gns_zone);
-  zone = local_gns_zone;
-  GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
+                "cURL global init failed!\n");
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Using zone: %s!\n", &zonename);
-  GNUNET_CRYPTO_rsa_key_free(key);
-  GNUNET_free(keyfile);
-  keyfile = NULL;
+              "Proxy listens on port %u\n",
+              port);
 
-  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
-                                                   "PRIVATE_ZONEKEY", &keyfile))
+  /* start MHD daemon for HTTP */
+  hd = GNUNET_new (struct MhdHttpList);
+  hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
+                                0,
+                                NULL, NULL,
+                                &create_response, hd,
+                                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_END);
+  if (NULL == hd->daemon)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unable to load private zone key config value!\n");
+    GNUNET_free (hd);
+    GNUNET_SCHEDULER_shutdown ();
+    return;
   }
+  httpd = hd;
+  GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
+}
+
 
-  if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile)))
+/**
+ * Method called to inform about the egos of the shorten zone of this peer.
+ *
+ * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
+ * this function is only called ONCE, and 'NULL' being passed in
+ * @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
+ * that one was not NULL).
+ *
+ * @param cls closure, NULL
+ * @param ego ego handle
+ * @param ctx context for application to store data for this ego
+ *                 (during the lifetime of this process, initially NULL)
+ * @param name name assigned by the user for this ego,
+ *                   NULL if the user just deleted the ego and it
+ *                   must thus no longer be used
+ */
+static void
+identity_shorten_cb (void *cls,
+                    struct GNUNET_IDENTITY_Ego *ego,
+                    void **ctx,
+                    const char *name)
+{
+  id_op = NULL;
+  if (NULL == ego)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unable to load private zone key %s!\n", keyfile);
-    GNUNET_free(keyfile);
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+               _("No ego configured for `shorten-zone`\n"));
   }
   else
   {
-    key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
-    GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
-    local_private_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
-    GNUNET_CRYPTO_short_hash(&pkey,
-                             sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
-                             local_private_zone);
-    GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Using private zone: %s!\n", &zonename);
-    GNUNET_CRYPTO_rsa_key_free(key);
-    GNUNET_free(keyfile);
+    local_shorten_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
+    do_shorten = GNUNET_YES;
   }
-  keyfile = NULL;
+  run_cont ();
+}
 
-  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
-                                                   "SHORTEN_ZONEKEY", &keyfile))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unable to load shorten zone key config value!\n");
-  }
 
-  if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile)))
+/**
+ * Method called to inform about the egos of the master zone of this peer.
+ *
+ * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
+ * this function is only called ONCE, and 'NULL' being passed in
+ * @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
+ * that one was not NULL).
+ *
+ * @param cls closure, NULL
+ * @param ego ego handle
+ * @param ctx context for application to store data for this ego
+ *                 (during the lifetime of this process, initially NULL)
+ * @param name name assigned by the user for this ego,
+ *                   NULL if the user just deleted the ego and it
+ *                   must thus no longer be used
+ */
+static void
+identity_master_cb (void *cls,
+                   struct GNUNET_IDENTITY_Ego *ego,
+                   void **ctx,
+                   const char *name)
+{
+  id_op = NULL;
+  if (NULL == ego)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unable to load shorten zone key %s!\n", keyfile);
-    GNUNET_free(keyfile);
-  }
-  else
-  {
-    key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
-    GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
-    local_shorten_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
-    GNUNET_CRYPTO_short_hash(&pkey,
-                             sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
-                             local_shorten_zone);
-    GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Using shorten zone: %s!\n", &zonename);
-    GNUNET_CRYPTO_rsa_key_free(key);
-    GNUNET_free(keyfile);
+               _("No ego configured for `%s`\n"),
+               "gns-proxy");
+    GNUNET_SCHEDULER_shutdown ();
+    return;
   }
-
-  return GNUNET_YES;
+  GNUNET_IDENTITY_ego_get_public_key (ego,
+                                     &local_gns_zone);
+  id_op = GNUNET_IDENTITY_get (identity,
+                              "gns-short",
+                              &identity_shorten_cb,
+                              NULL);
 }
 
+
 /**
  * Main function that will be run
  *
  * @param cls closure
  * @param args remaining command-line arguments
  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
- * @param cfg configuration
+ * @param c configuration
  */
 static void
 run (void *cls, char *const *args, const char *cfgfile,
-     const struct GNUNET_CONFIGURATION_Handle *cfg)
+     const struct GNUNET_CONFIGURATION_Handle *c)
 {
-  struct sockaddr_in sa;
-  struct MhdHttpList *hd;
-  struct sockaddr_un mhd_unix_sock_addr;
-  size_t len;
-  char* proxy_sockfile;
   char* cafile_cfg = NULL;
   char* cafile;
 
-  curl_multi = curl_multi_init ();
-
-  if (NULL == curl_multi)
+  cfg = c;
+  if (NULL == (curl_multi = curl_multi_init ()))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to create cURL multo handle!\n");
+                "Failed to create cURL multi handle!\n");
     return;
   }
-  
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Loading CA\n");
   cafile = cafile_opt;
   if (NULL == cafile)
   {
     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
-                                                          "PROXY_CACERT",
-                                                          &cafile_cfg))
+                                                             "PROXY_CACERT",
+                                                             &cafile_cfg))
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Unable to load proxy CA config value!\n");
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "No proxy CA provided!\n");
+      GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                                "gns-proxy",
+                                "PROXY_CACERT");
       return;
     }
     cafile = cafile_cfg;
   }
-  
+  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)) )
-  {
-    // FIXME: release resources...
-    return;
-  }
-
-  GNUNET_free_non_null (cafile_cfg);
-  
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Loading Template\n");
-  
-  compile_regex (&re_dotplus, (char*) RE_A_HREF);
-
-  gns_handle = GNUNET_GNS_connect (cfg);
-
-  if (GNUNET_NO == load_local_zone_key (cfg))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unable to load zone!\n");
+               _("Failed to load SSL/TLS key and certificate from `%s'\n"),
+               cafile);
+    gnutls_x509_crt_deinit (proxy_ca.cert);
+    gnutls_x509_privkey_deinit (proxy_ca.key);
+    gnutls_global_deinit ();
+    GNUNET_free_non_null (cafile_cfg);
     return;
   }
-
-  if (NULL == gns_handle)
+  GNUNET_free_non_null (cafile_cfg);
+  if (NULL == (gns_handle = GNUNET_GNS_connect (cfg)))
   {
     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;
   }
-
-  memset (&sa, 0, sizeof (sa));
-  sa.sin_family = AF_INET;
-  sa.sin_port = htons (port);
-#if HAVE_SOCKADDR_IN_SIN_LEN
-  sa.sin_len = sizeof (sa);
-#endif
-
-  lsock = GNUNET_NETWORK_socket_create (AF_INET,
-                                        SOCK_STREAM,
-                                        0);
-
-  if ((NULL == lsock) ||
-      (GNUNET_OK !=
-       GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
-                                   sizeof (sa))))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to create listen socket bound to `%s'",
-                GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
-    if (NULL != lsock)
-      GNUNET_NETWORK_socket_close (lsock);
-    return;
-  }
-
-  if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to listen on socket bound to `%s'",
-                GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
-    return;
-  }
-
-  ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                         lsock, &do_accept, NULL);
-
-  ctasks_head = NULL;
-  ctasks_tail = NULL;
-
-  if (0 != curl_global_init (CURL_GLOBAL_WIN32))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "cURL global init failed!\n");
-    return;
-  }
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Proxy listens on port %u\n",
-              port);
-
-  mhd_httpd_head = NULL;
-  mhd_httpd_tail = NULL;
-  total_mhd_connections = 0;
-
-  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
-                                                            "PROXY_UNIXPATH",
-                                                            &proxy_sockfile))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Specify PROXY_UNIXPATH in gns-proxy config section!\n");
-    return;
-  }
-  
-  mhd_unix_socket = GNUNET_NETWORK_socket_create (AF_UNIX,
-                                                SOCK_STREAM,
-                                                0);
-
-  if (NULL == mhd_unix_socket)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unable to create unix domain socket!\n");
-    return;
-  }
-
-  mhd_unix_sock_addr.sun_family = AF_UNIX;
-  strcpy (mhd_unix_sock_addr.sun_path, proxy_sockfile);
-
-#if LINUX
-  mhd_unix_sock_addr.sun_path[0] = '\0';
-#endif
-#if HAVE_SOCKADDR_IN_SIN_LEN
-  mhd_unix_sock_addr.sun_len = (u_char) sizeof (struct sockaddr_un);
-#endif
-
-  len = strlen (proxy_sockfile) + sizeof(AF_UNIX);
-
-  GNUNET_free (proxy_sockfile);
-
-  if (GNUNET_OK != GNUNET_NETWORK_socket_bind (mhd_unix_socket,
-                               (struct sockaddr*)&mhd_unix_sock_addr,
-                               len))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unable to bind unix domain socket!\n");
-    return;
-  }
-
-  if (GNUNET_OK != GNUNET_NETWORK_socket_listen (mhd_unix_socket,
-                                                 1))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unable to listen on unix domain socket!\n");
-    return;
-  }
-
-  hd = GNUNET_malloc (sizeof (struct MhdHttpList));
-  hd->is_ssl = GNUNET_NO;
-  strcpy (hd->domain, "");
-  httpd = MHD_start_daemon (MHD_USE_DEBUG, 4444, //Dummy port
-            &accept_cb, NULL,
-            &create_response, hd,
-            MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
-            MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
-            MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
-            MHD_OPTION_NOTIFY_COMPLETED,
-            NULL, NULL,
-            MHD_OPTION_END);
-
-  GNUNET_assert (httpd != NULL);
-  hd->daemon = httpd;
-  hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
-
-  GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
-
-  run_httpds ();
-
+  identity = GNUNET_IDENTITY_connect (cfg,
+                                     NULL, NULL);
+  id_op = GNUNET_IDENTITY_get (identity,
+                              "gns-proxy",
+                              &identity_master_cb,
+                              NULL);
   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
                                 &do_shutdown, NULL);
-
 }
 
 
@@ -3291,32 +2893,35 @@ main (int argc, char *const *argv)
 {
   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
     {'p', "port", NULL,
-     gettext_noop ("listen on specified port"), 1,
-     &GNUNET_GETOPT_set_string, &port},
+     gettext_noop ("listen on specified port (default: 7777)"), 1,
+     &GNUNET_GETOPT_set_ulong, &port},
     {'a', "authority", NULL,
       gettext_noop ("pem file to use as CA"), 1,
       &GNUNET_GETOPT_set_string, &cafile_opt},
     GNUNET_GETOPT_OPTION_END
   };
-
+  static const char* page =
+    "<html><head><title>gnunet-gns-proxy</title>"
+    "</head><body>cURL fail</body></html>";
   int ret;
 
   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
     return 2;
-
-
   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
+  curl_failure_response = MHD_create_response_from_buffer (strlen (page),
+                                                          (void*)page,
+                                                          MHD_RESPMEM_PERSISTENT);
+
   ret =
       (GNUNET_OK ==
        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
                            _("GNUnet GNS proxy"),
                            options,
                            &run, NULL)) ? 0 : 1;
-  GNUNET_free_non_null ((char*)argv);
-
+  MHD_destroy_response (curl_failure_response);
+  GNUNET_free_non_null ((char *) argv);
+  GNUNET_CRYPTO_ecdsa_key_clear (&local_shorten_zone);
   return ret;
 }
 
-
-
-
+/* end of gnunet-gns-proxy.c */