gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r29357 - gnunet/src/gns


From: gnunet
Subject: [GNUnet-SVN] r29357 - gnunet/src/gns
Date: Tue, 17 Sep 2013 22:12:04 +0200

Author: grothoff
Date: 2013-09-17 22:12:04 +0200 (Tue, 17 Sep 2013)
New Revision: 29357

Modified:
   gnunet/src/gns/gnunet-gns-proxy.c
Log:
-hacking more on proxy

Modified: gnunet/src/gns/gnunet-gns-proxy.c
===================================================================
--- gnunet/src/gns/gnunet-gns-proxy.c   2013-09-17 19:34:14 UTC (rev 29356)
+++ gnunet/src/gns/gnunet-gns-proxy.c   2013-09-17 20:12:04 UTC (rev 29357)
@@ -50,15 +50,18 @@
  */ 
 #define GNUNET_GNS_PROXY_PORT 7777
 
-#define MHD_MAX_CONNECTIONS 300
-
 /**
  * Maximum supported length for a URI.
+ * Should die. @deprecated
  */
 #define MAX_HTTP_URI_LENGTH 2048
 
+/**
+ * Some buffer size. @deprecated
+ */
 #define POSTBUFFERSIZE 4096
 
+
 /**
  * Size of the read/write buffers for Socks.   Uses
  * 256 bytes for the hostname (at most), plus a few
@@ -81,8 +84,19 @@
  */
 #define MAX_PEM_SIZE (10 * 1024)
 
+/**
+ * After how long do we clean up unused MHD SSL/TLS instances?
+ */
+#define MHD_CACHE_TIMEOUT GNUNET_TIME_relative_multiply 
(GNUNET_TIME_UNIT_MINUTES, 5)
 
 /**
+ * After how long do we clean up Socks5 handles that failed to show any 
activity
+ * with their respective MHD instance?
+ */
+#define HTTP_HANDSHAKE_TIMEOUT GNUNET_TIME_relative_multiply 
(GNUNET_TIME_UNIT_SECONDS, 15)
+
+
+/**
  * Log curl error.
  *
  * @param level log level
@@ -92,6 +106,8 @@
 #define LOG_CURL_EASY(level,fun,rc) GNUNET_log(level, _("%s failed at %s:%d: 
`%s'\n"), fun, __FILE__, __LINE__, curl_easy_strerror (rc))
 
 
+/* *************** Socks protocol definitions (move to TUN?) 
****************** */
+
 /**
  * Which SOCKS version do we speak?
  */
@@ -166,77 +182,151 @@
 
 
 /**
- * The socks phases.  
+ * Client hello in Socks5 protocol.
  */
-enum SocksPhase
+struct Socks5ClientHelloMessage
 {
   /**
-   * We're waiting to get the client hello.
+   * Should be #SOCKS_VERSION_5.
    */
-  SOCKS5_INIT,
+  uint8_t version;
 
   /**
-   * We're waiting to get the initial request.
+   * How many authentication methods does the client support.
    */
-  SOCKS5_REQUEST,
+  uint8_t num_auth_methods;
 
+  /* followed by supported authentication methods, 1 byte per method */
+
+};
+
+
+/**
+ * Server hello in Socks5 protocol.
+ */
+struct Socks5ServerHelloMessage
+{
   /**
-   * We are currently resolving the destination.
+   * Should be #SOCKS_VERSION_5.
    */
-  SOCKS5_RESOLVING,
+  uint8_t version;
 
   /**
-   * We're in transfer mode.
+   * Chosen authentication method, for us always #SOCKS_AUTH_NONE,
+   * which skips the authentication step.
    */
-  SOCKS5_DATA_TRANSFER
+  uint8_t auth_method;
 };
 
 
-
 /**
- * State machine for the IO buffer.
+ * Client socks request in Socks5 protocol.
  */
-enum BufferStatus
-  {
-    BUF_WAIT_FOR_CURL,
-    BUF_WAIT_FOR_MHD
-  };
+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).
+   */
+};
+
+
 /**
- * A structure for CA cert/key
+ * Server response to client requests in Socks5 protocol.
  */
-struct ProxyCA
+struct Socks5ServerResponseMessage
 {
   /**
-   * The certificate 
+   * Should be #SOCKS_VERSION_5.
    */
-  gnutls_x509_crt_t cert;
+  uint8_t version;
 
   /**
-   * The private key 
+   * Status code, an `enum Socks5StatusCode`
    */
-  gnutls_x509_privkey_t key;
+  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).
+   */
+
 };
 
 
+/* ***************** Datastructures for Socks handling **************** */
+
+
 /**
- * Structure for GNS certificates
+ * The socks phases.  
  */
-struct ProxyGNSCertificate
+enum SocksPhase
 {
   /**
-   * The certificate as PEM 
+   * We're waiting to get the client hello.
    */
-  char cert[MAX_PEM_SIZE];
+  SOCKS5_INIT,
 
   /**
-   * The private key as PEM 
+   * We're waiting to get the initial request.
    */
-  char key[MAX_PEM_SIZE];
+  SOCKS5_REQUEST,
+
+  /**
+   * We are currently resolving the destination.
+   */
+  SOCKS5_RESOLVING,
+
+  /**
+   * We're in transfer mode.
+   */
+  SOCKS5_DATA_TRANSFER,
+
+  /**
+   * Finish writing the write buffer, then clean up.
+   */
+  SOCKS5_WRITE_THEN_CLEANUP,
+
+  /**
+   * Socket has been passed to MHD, do not close it anymore.
+   */
+  SOCKS5_SOCKET_WITH_MHD
 };
 
 
+
 /**
  * A structure for socks requests
  */
@@ -259,29 +349,24 @@
   struct GNUNET_NETWORK_Handle *sock;
 
   /**
-   * The server socket 
+   * Handle to GNS lookup, during #SOCKS5_RESOLVING phase.
    */
-  struct GNUNET_NETWORK_Handle *remote_sock;
-  
+  struct GNUNET_GNS_LookupRequest *gns_lookup;
+
   /**
    * Client socket read task 
    */
   GNUNET_SCHEDULER_TaskIdentifier rtask;
 
   /**
-   * Server socket read task 
-   */
-  GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
-
-  /**
    * Client socket write task 
    */
   GNUNET_SCHEDULER_TaskIdentifier wtask;
 
   /**
-   * Server socket write task 
+   * Timeout task 
    */
-  GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
+  GNUNET_SCHEDULER_TaskIdentifier timeout_task;
 
   /**
    * Read buffer 
@@ -294,6 +379,21 @@
   char wbuf[SOCKS_BUFFERSIZE];
 
   /**
+   * the domain name to server (only important for SSL) 
+   */
+  char *domain;
+
+  /**
+   * DNS Legacy Host Name as given by GNS, NULL if not given.
+   */
+  char *leho;
+
+  /**
+   * The URL to fetch 
+   */
+  char *url;
+
+  /**
    * Number of bytes already in read buffer 
    */
   size_t rbuf_len;
@@ -314,18 +414,51 @@
   enum SocksPhase state;
 
   /**
-   * This handle is scheduled for cleanup? 
+   * Desired destination port.
    */
-  int cleanup;
+  uint16_t port;
 
+};
+
+
+/* *********************** Datastructures for HTTP handling ****************** 
*/
+
+/**
+ * A structure for CA cert/key
+ */
+struct ProxyCA
+{
   /**
-   * Shall we close the client socket on cleanup? 
+   * The certificate 
    */
-  int cleanup_sock;
+  gnutls_x509_crt_t cert;
+
+  /**
+   * The private key 
+   */
+  gnutls_x509_privkey_t key;
 };
 
 
 /**
+ * Structure for GNS certificates
+ */
+struct ProxyGNSCertificate
+{
+  /**
+   * The certificate as PEM 
+   */
+  char cert[MAX_PEM_SIZE];
+
+  /**
+   * The private key as PEM 
+   */
+  char key[MAX_PEM_SIZE];
+};
+
+
+
+/**
  * A structure for all running Httpds
  */
 struct MhdHttpList
@@ -341,14 +474,9 @@
   struct MhdHttpList *next;
 
   /**
-   * is this an ssl daemon? 
-   */
-  int is_ssl;
-
-  /**
    * the domain name to server (only important for SSL) 
    */
-  char domain[256];
+  char *domain;
 
   /**
    * The daemon handle 
@@ -365,10 +493,28 @@
    */
   GNUNET_SCHEDULER_TaskIdentifier httpd_task;
 
+  /**
+   * is this an ssl daemon? 
+   */
+  int is_ssl;
+
 };
 
 
+/* ***************** possibly deprecated data structures ****************** */
+
+
 /**
+ * State machine for the IO buffer.
+ */
+enum BufferStatus
+  {
+    BUF_WAIT_FOR_CURL,
+    BUF_WAIT_FOR_MHD
+  };
+
+
+/**
  * A structure for MHD<->cURL streams
  */
 struct ProxyCurlTask
@@ -404,14 +550,14 @@
   long curl_response_code;
 
   /**
-   * The URL to fetch 
+   * The cURL write buffer / MHD read buffer 
    */
-  char url[MAX_HTTP_URI_LENGTH];
+  char buffer[CURL_MAX_WRITE_SIZE];
 
   /**
-   * The cURL write buffer / MHD read buffer 
+   * Should die. @deprecated
    */
-  char buffer[CURL_MAX_WRITE_SIZE];
+  char url[MAX_HTTP_URI_LENGTH];
 
   /**
    * Read pos of the data in the buffer 
@@ -601,113 +747,10 @@
 };
 
 
+/* *********************** Globals **************************** */
 
-/**
- * Client hello in Socks5 protocol.
- */
-struct Socks5ClientHelloMessage
-{
-  /**
-   * Should be #SOCKS_VERSION_5.
-   */
-  uint8_t version;
 
-  /**
-   * How many authentication methods does the client support.
-   */
-  uint8_t num_auth_methods;
-
-  /* followed by supported authentication methods, 1 byte per method */
-
-};
-
-
 /**
- * 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;
-};
-
-
-/**
- * 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).
-   */
-};
-
-
-/**
- * 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).
-   */
-
-};
-
-
-/**
  * The port the proxy is running on (default 7777) 
  */
 static unsigned long port = GNUNET_GNS_PROXY_PORT;
@@ -715,7 +758,7 @@
 /**
  * The CA file (pem) to use for the proxy CA 
  */
-static char* cafile_opt;
+static char *cafile_opt;
 
 /**
  * The listen socket of the proxy 
@@ -728,16 +771,11 @@
 static GNUNET_SCHEDULER_TaskIdentifier ltask;
 
 /**
- * The cURL download task 
+ * The cURL download task (curl multi API).
  */
 static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
 
 /**
- * Number of current mhd connections 
- */
-static unsigned int total_mhd_connections;
-
-/**
  * The cURL multi handle 
  */
 static CURLM *curl_multi;
@@ -768,6 +806,12 @@
 static struct MhdHttpList *mhd_httpd_tail;
 
 /**
+ * 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;
@@ -798,17 +842,6 @@
 static struct ProxyCA proxy_ca;
 
 /**
- * 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 MHD_Daemon *httpd;
-
-/**
- * Shorten zone private key 
- */
-static struct GNUNET_CRYPTO_EccPrivateKey shorten_zonekey;
-
-/**
  * Response we return on cURL failures.
  */
 static struct MHD_Response *curl_failure_response;
@@ -829,8 +862,11 @@
 static const struct GNUNET_CONFIGURATION_Handle *cfg;
 
 
+/* ************************* Global helpers ********************* */
+
+
 /**
- * Clean up s5r handles
+ * Clean up s5r handles.
  *
  * @param s5r the handle to destroy
  */
@@ -839,62 +875,96 @@
 {
   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) && 
-       (GNUNET_YES == s5r->cleanup_sock) )
-    GNUNET_NETWORK_socket_close (s5r->sock);
+  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
+      GNUNET_NETWORK_socket_close (s5r->sock);
+  }
   GNUNET_CONTAINER_DLL_remove (s5r_head,
                               s5r_tail,
                               s5r);
-  GNUNET_free(s5r);
+  GNUNET_free_non_null (s5r->domain);
+  GNUNET_free_non_null (s5r->leho);
+  GNUNET_free_non_null (s5r->url);
+  GNUNET_free (s5r);
 }
 
 
 /**
- * Remove the first @a len bytes from the beginning of the read buffer.
+ * Run MHD now, we have extra data ready for the callback.
  *
- * @param s5r the handle clear the read buffer for
- * @param len number of bytes in read buffer to advance
+ * @param hd the daemon to run now.
  */
 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;
-}
+run_mhd_now (struct MhdHttpList *hd);
 
 
+/* *************************** netcat mode *********************** */
+
+#if 0
+
+
+
+
 /**
- * Checks if name is in tld
+ * Given a TCP stream and a destination address, forward the stream
+ * in both directions.
  *
- * @param name the name to check 
- * @param tld the TLD to check for (must NOT begin with ".")
- * @return #GNUNET_YES or #GNUNET_NO
+ * @param cls FIXME
+ * @param tc  FIXME
  */
-static int
-is_tld (const char* name, const char* tld)
+static void
+forward_socket_like_ncat (void *cls,
+                         const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  size_t name_len = strlen (name);
-  size_t tld_len = strlen (tld);
+  struct hostent *phost;
+  uint32_t remote_ip;
+  struct sockaddr_in remote_addr;
+  struct in_addr *r_sin_addr;
 
-  GNUNET_break ('.' != tld[0]);
-  return ( (tld_len < name_len) &&
-          ( ('.' == name[name_len - tld_len - 1]) || (name_len == tld_len) ) &&
-          (0 == memcmp (tld,
-                        name + (name_len - tld_len),
-                        tld_len)) );
+  s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
+                                                  SOCK_STREAM,
+                                                  0);
+  r_sin_addr = (struct in_addr*)(phost->h_addr);
+  remote_ip = r_sin_addr->s_addr;
+  memset(&remote_addr, 0, sizeof(remote_addr));
+  remote_addr.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+  remote_addr.sin_len = sizeof (remote_addr);
+#endif
+  remote_addr.sin_addr.s_addr = remote_ip;
+  remote_addr.sin_port = *port;
+  
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "target server: %s:%u\n", 
+             inet_ntoa(remote_addr.sin_addr),
+             ntohs(*port));
+  
+  if ((GNUNET_OK !=
+       GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
+                                      (const struct sockaddr*)&remote_addr,
+                                      sizeof (remote_addr)))
+      && (errno != EINPROGRESS))
+    {
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
+      signal_socks_failure (s5r,
+                           SOCKS5_STATUS_NETWORK_UNREACHABLE);
+      return;
+    }
 }
+#endif
 
 
+/* ************************* HTTP handling with cURL *********************** */
+
 static int
 con_post_data_iter (void *cls,
                   enum MHD_ValueKind kind,
@@ -1325,50 +1395,8 @@
 }
 
 
-/**
- * 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
@@ -1449,7 +1477,6 @@
     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;
   }
   
@@ -1462,7 +1489,6 @@
     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;
   }
 
@@ -1921,6 +1947,7 @@
   char resolvename[512];
   char curlurl[512];
 
+  
   strcpy (ctask->leho, "");
 
   if (rd_count == 0)
@@ -1995,54 +2022,8 @@
 }
 
 
-/**
- * Initialize download and trigger curl
- *
- * @param cls the proxycurltask
- * @param auth_name the name of the authority (site of origin) of ctask->host
- */
-static void
-process_get_authority (void *cls,
-                       const char* auth_name)
-{
-  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 (gns_handle,
-                    ctask->host,
-                    &local_gns_zone,
-                    GNUNET_NAMESTORE_TYPE_LEHO,
-                    GNUNET_YES /* Only cached for performance */,
-                    &shorten_zonekey,
-                    &process_leho_lookup,
-                    ctask);
-}
-
-
-static void*
-mhd_log_callback (void* cls, 
-                 const char* url)
-{
-  struct ProxyCurlTask *ctask;
-
-  ctask = GNUNET_new (struct ProxyCurlTask);
-  strcpy (ctask->url, url);
-  return ctask;
-}
-
-
 /**
  * Main MHD callback for handling requests.
  *
@@ -2319,255 +2300,231 @@
 }
 
 
+/* ******************** MHD HTTP setup and event loop ******************** */
+
+
 /**
- * run all httpd
+ * Function called when MHD decides that we are done with a connection.
+ *
+ * @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_httpds ()
+mhd_completed_cb (void *cls,
+                 struct MHD_Connection *connection,
+                 void **con_cls,
+                 enum MHD_RequestTerminationCode toe)
 {
-  struct MhdHttpList *hd;
+  struct Socks5Request *s5r = *con_cls;
 
-  for (hd=mhd_httpd_head; NULL != hd; hd = hd->next)
-    run_httpd (hd);
+  if (NULL == s5r)
+    return;
+  cleanup_s5r (s5r);
+  *con_cls = NULL;  
+}
 
+
+/**
+ * Function called when MHD first processes an incoming connection.
+ * Gives us the respective URI information.
+ *
+ * We use this to associate the `struct MHD_Connection` with our 
+ * 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 *
+mhd_log_callback (void *cls, 
+                 const char *url,
+                 struct MHD_Connection *connection)
+{
+  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);
+      return s5r;
+    }
+  }
+  return NULL;
 }
 
 
 /**
- * schedule mhd
+ * Kill the given MHD daemon.
  *
- * @param hd the daemon to run
+ * @param hd daemon to stop
  */
 static void
-run_httpd (struct MhdHttpList *hd)
+kill_httpd (struct MhdHttpList *hd)
 {
-  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;
-  MHD_UNSIGNED_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);
-
-  if (MHD_YES == haveto)
-    tv.rel_value_us = (uint64_t) timeout * 1000LL;
-  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);
-  
+  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_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);
+  GNUNET_free_non_null (hd->proxy_cert);
+  if (hd == httpd)
+    httpd = NULL;
+  GNUNET_free (hd);
 }
 
 
 /**
- * Task run whenever HTTP server operations are pending.
+ * Task run whenever HTTP server is idle for too long. Kill it.
  *
- * @param cls unused
+ * @param cls the `struct MhdHttpList *`
  * @param tc sched context
  */
 static void
-do_httpd (void *cls,
-          const struct GNUNET_SCHEDULER_TaskContext *tc)
+kill_httpd_task (void *cls,
+                const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
   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);
+  kill_httpd (hd);
 }
 
 
 /**
- * Read data from socket
+ * Task run whenever HTTP server operations are pending.
  *
- * @param cls the closure
- * @param tc scheduler context
+ * @param cls the `struct MhdHttpList *` of the daemon that is being run
+ * @param tc sched context
  */
 static void
-do_s5r_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+do_httpd (void *cls,
+          const struct GNUNET_SCHEDULER_TaskContext *tc);
 
 
 /**
- * Read from remote end
+ * 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 closure
- * @param tc scheduler context
+ * @param hd the daemon to schedule
  */
 static void
-do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
-
-
-/**
- * Write data to remote socket
- *
- * @param cls the closure
- * @param tc scheduler context
- */
-static void
-do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+schedule_httpd (struct MhdHttpList *hd)
 {
-  struct Socks5Request *s5r = cls;
-  unsigned int len;
+  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;
 
-  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)))
+  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 remote 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, "send");
-    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);
   }
-
-  s5r->rtask =
-    GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                   s5r->sock,
-                                   &do_s5r_read, s5r);
-}
-
-
-/**
- * Write data to socket
- *
- * @param cls the closure
- * @param tc scheduler context
- */
-static void
-do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
-  struct Socks5Request *s5r = cls;
-  unsigned int len;
-
-  s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
-
-  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)))
+  else
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Successfully sent %d bytes to socket\n",
-                len);
+    wrs = NULL;
+    wws = NULL;
   }
+  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
-  {    
-    GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
-    s5r->cleanup = GNUNET_YES;
-    s5r->cleanup_sock = GNUNET_YES;
-    cleanup_s5r (s5r); 
-    return;
-  }
-
-  if (GNUNET_YES == s5r->cleanup)
   {
-    cleanup_s5r (s5r);
-    return;
+    hd->httpd_task =
+      GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+                                  tv, wrs, wws,
+                                  &do_httpd, hd);
   }
-
-  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 (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;
+  struct MhdHttpList *hd = 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 (0 == s5r->wbuf_len)
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "0 bytes received from remote... graceful shutdown!\n");
-    cleanup_s5r (s5r);
-    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;
-  int ret;
-
-  fd = GNUNET_NETWORK_get_fd (h);
-  addr = GNUNET_NETWORK_get_addr (h);
-  len = GNUNET_NETWORK_get_addrlen (h);
-  ret = MHD_add_connection (daemon, fd, addr, len);
-  GNUNET_NETWORK_socket_free_memory_only_ (h);
-  return ret;
+  if (GNUNET_SCHEDULER_NO_TASK != 
+      hd->httpd_task)
+    GNUNET_SCHEDULER_cancel (hd->httpd_task);
+  hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, 
+                                            hd);
 }
 
 
@@ -2663,7 +2620,7 @@
  * 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)
@@ -2677,7 +2634,7 @@
   struct ProxyGNSCertificate *pgc;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
-             "Generating cert for `%s'\n", 
+             "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));
@@ -2716,55 +2673,229 @@
 
 
 /**
- * Adds a socket to an SSL MHD instance It is important that the
- * domain name is correct. In most cases we need to start a new daemon.
+ * Lookup (or create) an SSL MHD instance for a particular domain.
  *
- * @param h the handle to add to a daemon
  * @param domain the domain the SSL daemon has to serve
- * @return #MHD_YES on success
+ * @return NULL on errro
  */
-static int
-add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, 
-                      const char* domain)
+static struct MhdHttpList *
+lookup_ssl_httpd (const char* domain)
 {
   struct MhdHttpList *hd;
   struct ProxyGNSCertificate *pgc;
 
   for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
     if (0 == strcmp (hd->domain, domain))
+      return hd;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Starting fresh MHD HTTPS instance for domain `%s'\n",
+             domain);
+  pgc = generate_gns_certificate (domain);   
+  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;
+}
+
+
+/**
+ * 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 cls the `struct Socks5Request *`
+ * @param tc sched context
+ */
+static void
+timeout_s5r_handshake (void *cls,
+                      const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct Socks5Request *s5r = cls;
+
+  s5r->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+  cleanup_s5r (s5r);
+}
+
+
+/**
+ * Checks if name is in given @a tld.
+ *
+ * @param name the name to check 
+ * @param tld the TLD to check for (must NOT begin with ".")
+ * @return #GNUNET_YES or #GNUNET_NO
+ */
+static int
+is_tld (const char* name, const char* tld)
+{
+  size_t name_len = strlen (name);
+  size_t tld_len = strlen (tld);
+
+  GNUNET_break ('.' != tld[0]);
+  return ( (tld_len < name_len) &&
+          ( ('.' == name[name_len - tld_len - 1]) || (name_len == tld_len) ) &&
+          (0 == memcmp (tld,
+                        name + (name_len - tld_len),
+                        tld_len)) );
+}
+
+
+/**
+ * We're done with the Socks5 protocol, now we need to pass the
+ * connection data through to the final destination, either 
+ * 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;
+
+  if (is_tld (s5r->domain, GNUNET_GNS_TLD) ||
+      is_tld (s5r->domain, GNUNET_GNS_TLD_ZKEY))
+  {
+    /* GNS TLD, setup MHD server for destination */
+    switch (s5r->port)
+    {
+    case HTTPS_PORT:
+      hd = lookup_ssl_httpd (s5r->domain);
+      if (NULL == hd)
+      {
+       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                   _("Failed to start HTTPS server for `%s'\n"),
+                   s5r->domain);
+       cleanup_s5r (s5r);
+       return;
+      }
       break;
-  if (NULL == hd)
-  {    
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "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;
-    strcpy (hd->domain, domain); /* FIXME: avoid fixed-sized buffers... */
-    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_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_URI_LOG_CALLBACK, 
&mhd_log_callback,
-                                  NULL,
-                                  MHD_OPTION_END);
-    /* FIXME: rather than assert, handle error! */
-    GNUNET_assert (NULL != hd->daemon);
-    GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
+    case HTTP_PORT:
+      GNUNET_assert (NULL == httpd);
+      hd = httpd;
+      break;
+    default:
+      hd = NULL; /* netcat */
+      break;
+    }
   }
-  return add_handle_to_mhd (h, hd->daemon);
+  else
+  {
+    hd = NULL; /* netcat */
+  }
+  if (NULL != hd)
+  {
+    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;
+    }
+    schedule_httpd (hd);
+    s5r->timeout_task = GNUNET_SCHEDULER_add_delayed (HTTP_HANDSHAKE_TIMEOUT,
+                                                     &timeout_s5r_handshake,
+                                                     s5r);
+  }
+  else
+  {
+    // FIXME: not implemented
+    GNUNET_break (0);
+    /* start netcat mode here! */
+  }
 }
 
 
+/* ********************* SOCKS handling ************************* */
+
+
 /**
+ * Write data from buffer to socks5 client, then continue with state machine.
+ *
+ * @param cls the closure with the `struct Socks5Request`
+ * @param tc scheduler context
+ */
+static void
+do_write (void *cls,
+         const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct Socks5Request *s5r = cls;
+  ssize_t len;
+
+  s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
+  len = GNUNET_NETWORK_socket_send (s5r->sock,
+                                   s5r->wbuf,
+                                   s5r->wbuf_len);
+  if (len <= 0)
+  {
+    /* write error: connection closed, shutdown, etc.; just clean up */
+    cleanup_s5r (s5r); 
+    return;
+  }
+  memmove (s5r->wbuf,
+          &s5r->wbuf[len],
+          s5r->wbuf_len - len);
+  s5r->wbuf_len -= len;
+  if (s5r->wbuf_len > 0)
+  {
+    /* not done writing */
+    s5r->wtask =
+      GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
+                                     s5r->sock,
+                                     &do_write, s5r);
+    return;
+  }
+
+  /* we're done writing, continue with state machine! */
+
+  switch (s5r->state)
+  {
+  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;
+  }
+}
+
+
+/**
  * Return a server response message indicating a failure to the client.
  *
  * @param s5r request to return failure code for
@@ -2780,8 +2911,7 @@
   memset (s_resp, 0, sizeof (struct Socks5ServerResponseMessage));
   s_resp->version = SOCKS_VERSION_5;
   s_resp->reply = sc;
-  s5r->cleanup = GNUNET_YES;
-  s5r->cleanup_sock = GNUNET_YES;
+  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,
@@ -2810,9 +2940,7 @@
          0, 
          sizeof (struct in_addr) + sizeof (uint16_t));
   s5r->wbuf_len += sizeof (struct Socks5ServerResponseMessage) +
-    sizeof (struct in_addr) + sizeof (uint16_t);
-  s5r->cleanup = GNUNET_YES;
-  s5r->cleanup_sock = GNUNET_NO;    
+    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,
@@ -2822,19 +2950,140 @@
 
 
 /**
+ * Process GNS results for target domain.
+ *
+ * @param cls the ctask
+ * @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;
+
+  s5r->gns_lookup = NULL;
+  got_ip = GNUNET_NO;
+  for (i=0;i<rd_count;i++)
+  {
+    r = &rd[i];
+    switch (r->record_type)
+    {
+    case GNUNET_DNSPARSER_TYPE_A:
+      {
+       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
+      }
+      break;
+    case GNUNET_DNSPARSER_TYPE_AAAA: 
+      {
+       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);  
+}
+
+
+/**
+ * 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;
+}
+
+
+/**
  * 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)
+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;
-  int ret;
   ssize_t rlen;
   size_t alen;
 
@@ -2914,6 +3163,7 @@
        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))
@@ -2928,110 +3178,13 @@
        s5r->state = SOCKS5_DATA_TRANSFER;
       }
       break;
-    case SOCKS5_AT_DOMAINNAME:
-      {
-       const uint8_t *dom_len;
-       const char *dom_name;
-       const uint16_t *port;
-       char domain[256];
-       struct hostent *phost;
-       uint32_t remote_ip;
-       struct sockaddr_in remote_addr;
-       struct in_addr *r_sin_addr;
-       
-       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];
-
-
-       strncpy (domain, dom_name, *dom_len);
-       domain[*dom_len] = '\0';
-
-       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                   "Requested connection is to %s:%d\n",
-                   domain,
-                   ntohs (*port));
-
-       if (is_tld (domain, GNUNET_GNS_TLD) ||
-           is_tld (domain, GNUNET_GNS_TLD_ZKEY))
-       {
-         /* GNS TLD */
-         ret = MHD_NO;
-         if (ntohs (*port) == HTTPS_PORT)
-         {
-           ret = add_handle_to_ssl_mhd (s5r->sock, domain);
-         }
-         else 
-         {
-           ret = add_handle_to_mhd (s5r->sock, httpd);
-         }
-         if (ret != MHD_YES)
-         {
-           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                       _("Failed to start HTTP server\n"));
-           signal_socks_failure (s5r,
-                                 SOCKS5_STATUS_GENERAL_FAILURE);
-           return;
-         }
-       }       
-       else
-       {
-         /* non-GNS TLD, use DNS to resolve */
-         /* FIXME: make asynchronous! */
-         phost = (struct hostent *) gethostbyname (domain);
-         if (phost == NULL)
-         {
-           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                       "Failed to resolve `%s'!\n",
-                       domain);
-           signal_socks_failure (s5r,
-                                 SOCKS5_STATUS_GENERAL_FAILURE);
-           return;
-         }
-         
-         s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
-                                                          SOCK_STREAM,
-                                                          0);
-         r_sin_addr = (struct in_addr*)(phost->h_addr);
-         remote_ip = r_sin_addr->s_addr;
-         memset(&remote_addr, 0, sizeof(remote_addr));
-         remote_addr.sin_family = AF_INET;
-#if HAVE_SOCKADDR_IN_SIN_LEN
-         remote_addr.sin_len = sizeof (remote_addr);
-#endif
-         remote_addr.sin_addr.s_addr = remote_ip;
-         remote_addr.sin_port = *port;
-         
-         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                     "target server: %s:%u\n", 
-                     inet_ntoa(remote_addr.sin_addr),
-                     ntohs(*port));
-         
-         if ((GNUNET_OK !=
-              GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
-                                              (const struct 
sockaddr*)&remote_addr,
-                                              sizeof (remote_addr)))
-             && (errno != EINPROGRESS))
-         {
-           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
-           signal_socks_failure (s5r,
-                                 SOCKS5_STATUS_NETWORK_UNREACHABLE);
-           return;
-         }
-         s5r->state = SOCKS5_DATA_TRANSFER;
-       }
-       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))
@@ -3046,6 +3199,36 @@
        s5r->state = SOCKS5_DATA_TRANSFER;
       }
       break;
+    case SOCKS5_AT_DOMAINNAME:
+      {
+       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;
+      }
     default:
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  _("Unsupported socks address type %d\n"),
@@ -3059,24 +3242,29 @@
                         alen + sizeof (uint16_t));
     if (0 != s5r->rbuf_len)
     {
-      /* read more bytes than healthy, why did the client send more? */
+      /* read more bytes than healthy, why did the client send more!? */
       GNUNET_break_op (0);
       signal_socks_failure (s5r,
                            SOCKS5_STATUS_GENERAL_FAILURE);
       return;      
     }
-    signal_socks_success (s5r);
-    run_httpds (); // needed here!?
+    if (SOCKS5_DATA_TRANSFER == s5r->state)
+    {
+      /* 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 < s5r->rbuf_len);
-    if (GNUNET_SCHEDULER_NO_TASK == s5r->wtask)
-      GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
-                                     s5r->remote_sock,
-                                     &do_write /* _remote */, s5r);      
+    GNUNET_assert (0);
     return;
   default:
-    GNUNET_break (0);
+    GNUNET_assert (0);
     return;
   }
 }
@@ -3118,6 +3306,9 @@
 }
 
 
+/* ******************* General / main code ********************* */
+
+
 /**
  * Task run on shutdown
  *
@@ -3128,30 +3319,14 @@
 do_shutdown (void *cls,
              const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
-  struct MhdHttpList *hd;
-  struct MhdHttpList *tmp_hd;
   struct ProxyCurlTask *ctask;
   struct ProxyCurlTask *ctask_tmp;
   struct ProxyUploadData *pdata;
   
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Shutting down...\n");
-  for (hd = mhd_httpd_head; NULL != hd; hd = tmp_hd)
-  {
-    tmp_hd = hd->next;
-    if (GNUNET_SCHEDULER_NO_TASK != 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;
-    }
-    GNUNET_free_non_null (hd->proxy_cert);
-    GNUNET_free (hd);
-  }
+  while (NULL != mhd_httpd_head)
+    kill_httpd (mhd_httpd_head);
   for (ctask=ctasks_head; NULL != ctask; ctask=ctask_tmp)
   {
     ctask_tmp = ctask->next;
@@ -3286,9 +3461,8 @@
                                 0,
                                 NULL, NULL,
                                 &create_response, hd,
-                                MHD_OPTION_CONNECTION_LIMIT, 
MHD_MAX_CONNECTIONS,
                                 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 
16,
-                                MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
+                                MHD_OPTION_NOTIFY_COMPLETED, 
&mhd_completed_cb, NULL,
                                 MHD_OPTION_URI_LOG_CALLBACK, 
&mhd_log_callback, NULL,
                                 MHD_OPTION_END);
   if (NULL == hd->daemon)
@@ -3297,11 +3471,8 @@
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
-  httpd = hd->daemon;
+  httpd = hd;
   GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
-
-  /* start loop running all MHD instances */
-  run_httpds ();
 }
 
 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]