-fix
[oweals/gnunet.git] / src / gns / gnunet-gns-proxy.c
index a0157ecbe2aa7f9222c007b84929e4b07e443e97..52f47dad27c7999f0e771f3fdd8d1932283adf72 100644 (file)
 #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>
+
 #define GNUNET_GNS_PROXY_PORT 7777
 
 /* MHD/cURL defines */
@@ -36,6 +43,7 @@
 
 /* regexp */
 #define RE_DOTPLUS "<a href=\"http://(([A-Za-z]+[.])+)([+])"
+#define RE_A_HREF  "<a href=\"https?://(([A-Za-z]+[.])+)([+]|zkey)"
 #define RE_N_MATCHES 4
 
 /* The usual suspects */
 #define HTTPS_PORT 443
 
 
+/**
+ * A structure for CA cert/key
+ */
+struct ProxyCA
+{
+  /* The certificate */
+  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[10 * 1024];
+
+  /* The private key as PEM */
+  char key[10 * 1024];
+};
+
 
 /**
  * A structure for socks requests
@@ -81,6 +114,12 @@ struct Socks5Request
 
   /* Length of data in write buffer */
   unsigned int wbuf_len;
+
+  /* This handle is scheduled for cleanup? */
+  int cleanup;
+
+  /* Shall we close the client socket on cleanup? */
+  int cleanup_sock;
 };
 
 
@@ -122,6 +161,12 @@ struct ProxyCurlTask
   /* Handle to cURL */
   CURL *curl;
 
+  /* Optional header replacements for curl (LEHO) */
+  struct curl_slist *headers;
+
+  /* Optional resolver replacements for curl (LEHO) */
+  struct curl_slist *resolver;
+
   /* The URL to fetch */
   char url[2048];
 
@@ -167,13 +212,19 @@ struct ProxyCurlTask
   /* The hostname (Host header field) */
   char host[256];
 
+  /* The LEgacy HOstname (can be empty) */
+  char leho[256];
+
   /* The associated daemon list entry */
-  struct MhdHttpList *daemon;
+  struct MhdHttpList *mhd;
   
 };
 
 /* The port the proxy is running on (default 7777) */
-unsigned long port = GNUNET_GNS_PROXY_PORT;
+static unsigned long port = GNUNET_GNS_PROXY_PORT;
+
+/* The CA file (pem) to use for the proxy CA */
+static char* cafile;
 
 /* The listen socket of the proxy */
 static struct GNUNET_NETWORK_Handle *lsock;
@@ -187,9 +238,6 @@ GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
 /* The non SSL httpd daemon handle */
 static struct MHD_Daemon *httpd;
 
-/* The http task ID */
-static GNUNET_SCHEDULER_TaskIdentifier httpd_task;
-
 /* The cURL multi handle */
 static CURLM *curl_multi;
 
@@ -211,6 +259,12 @@ static struct MhdHttpList *mhd_httpd_tail;
 /* Handle to the regex for dotplus (.+) replacement in HTML */
 static regex_t re_dotplus;
 
+/* The users local GNS zone hash */
+static struct GNUNET_CRYPTO_ShortHashCode local_gns_zone;
+
+/* The CA for SSL certificate generation */
+static struct ProxyCA proxy_ca;
+
 /**
  * Checks if name is in tld
  *
@@ -322,9 +376,9 @@ static void
 run_mhd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
 
-  struct MhdHttpList *hd;
+  struct MhdHttpList *hd = cls;
 
-  for (hd=mhd_httpd_head; hd != NULL; hd = hd->next)
+  //for (hd=mhd_httpd_head; hd != NULL; hd = hd->next)
     MHD_run (hd->daemon);
 }
 
@@ -380,7 +434,8 @@ callback_download (void *ptr, size_t size, size_t nmemb, void *ctx)
 
   //GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   //            "cURL chunk:\n%s\n", (char*)ctask->buffer);
-  run_mhd (NULL, NULL);
+  //run_mhd (NULL, NULL);
+  GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
   return total;
 }
 
@@ -396,17 +451,19 @@ mhd_content_free (void *cls)
 {
   struct ProxyCurlTask *ctask = cls;
 
-  if (ctask->curl != NULL)
+  if (NULL != ctask->headers)
+    curl_slist_free_all (ctask->headers);
+
+  if (NULL != ctask->curl)
     curl_easy_cleanup (ctask->curl);
 
+  ctask->curl = NULL;
+
   GNUNET_free (ctask);
 
 }
 
 
-
-
-
 /**
  * Shorten result callback
  *
@@ -438,7 +495,7 @@ process_shorten (void* cls, const char* short_name)
 
   ctask->pp_finished = GNUNET_YES;
 
-  GNUNET_SCHEDULER_add_now (&run_mhd, NULL);
+  GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
 }
 
 
@@ -495,7 +552,7 @@ mhd_content_cb (void *cls,
     ctask->download_in_progress = GNUNET_NO;
     curl_multi_remove_handle (curl_multi, ctask->curl);
     curl_easy_cleanup (ctask->curl);
-    GNUNET_SCHEDULER_add_now (&run_mhd, NULL);
+    GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
     return MHD_CONTENT_READER_END_OF_STREAM;
   }
   
@@ -507,7 +564,7 @@ mhd_content_cb (void *cls,
     ctask->download_in_progress = GNUNET_NO;
     curl_multi_remove_handle (curl_multi, ctask->curl);
     curl_easy_cleanup (ctask->curl);
-    GNUNET_SCHEDULER_add_now (&run_mhd, NULL);
+    GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
     return MHD_CONTENT_READER_END_WITH_ERROR;
   }
 
@@ -561,7 +618,7 @@ mhd_content_cb (void *cls,
           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                       "Skipping next %d bytes in buffer\n", m[0].rm_eo);
 
-          GNUNET_SCHEDULER_add_now (&run_mhd, NULL);
+          GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
 
           if ( strlen (ctask->pp_buf) <= max )
           {
@@ -578,13 +635,30 @@ mhd_content_cb (void *cls,
         }
 
         memset (ctask->pp_buf, 0, sizeof(ctask->pp_buf));
-        memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so));
+        
+        /* If .+ extend with authority */
+        if (0 == strcmp (ctask->buffer_ptr+m[1].rm_eo, "+"))
+        {
+           memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so));
+           strcpy ( ctask->pp_buf+strlen(ctask->pp_buf),
+                    ctask->authority);
+        }
+        /* If .zkey simply copy the name */
+        else
+        {
+          memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so + strlen (GNUNET_TLD_ZKEY)));
+        }
 
         ctask->is_postprocessing = GNUNET_YES;
         ctask->pp_finished = GNUNET_NO;
+        
+        GNUNET_GNS_shorten (gns_handle,
+                           ctask->pp_buf,
+                           &process_shorten,
+                           ctask);
 
         //postprocess_name(ctask, NULL);
-        ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_name, ctask);
+        //ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_name, ctask);
 
         return 0;
       }
@@ -620,11 +694,11 @@ mhd_content_cb (void *cls,
       ctask->buf_status = BUF_WAIT_FOR_CURL;
       ctask->buffer_ptr = ctask->buffer;
       curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
-      GNUNET_SCHEDULER_add_now (&run_mhd, NULL);
+      GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
     }
   }
 
-  GNUNET_SCHEDULER_add_now (&run_mhd, NULL);
+  GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
 
   return copied;
 }
@@ -848,6 +922,88 @@ curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   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];
+
+  ctask->headers = NULL;
+
+  strcpy (ctask->leho, "");
+
+  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, ""))
+  {
+    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);
+    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);
+    ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
+    curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver);
+    sprintf (curlurl, "https://%s%s", ctask->leho, ctask->url);
+    curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
+  }
+
+  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_successful = GNUNET_NO;
+    ctask->download_error = GNUNET_YES;
+    return;
+  }
+  GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
+
+  curl_download_prepare ();
+
+}
+
 /**
  * Initialize download and trigger curl
  *
@@ -856,7 +1012,7 @@ curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  *
  */
 static void
-process_get_authority (voidcls,
+process_get_authority (void *cls,
                        const char* auth_name)
 {
   struct ProxyCurlTask *ctask = cls;
@@ -872,7 +1028,12 @@ process_get_authority (void* cls,
               "Get authority yielded %s\n", auth_name);
   strcpy (ctask->authority, auth_name);
 
-  curl_download_prepare ();
+  GNUNET_GNS_lookup_zone (gns_handle,
+                          ctask->host,
+                          &local_gns_zone,
+                          GNUNET_GNS_RECORD_LEHO,
+                          &process_leho_lookup,
+                          ctask);
 }
 
 /**
@@ -908,6 +1069,7 @@ create_response (void *cls,
                  void **con_cls)
 {
   static int dummy;
+  struct MhdHttpList* hd = cls;
   const char* page = "<html><head><title>gnoxy</title>"\
                       "</head><body>cURL fail</body></html>";
   struct MHD_Response *response;
@@ -915,7 +1077,6 @@ create_response (void *cls,
   char curlurl[512];
   int ret = MHD_YES;
 
-  CURLMcode mret;
   struct ProxyCurlTask *ctask;
   
   if (0 != strcmp (meth, "GET"))
@@ -941,6 +1102,7 @@ create_response (void *cls,
   
   /* Do cURL */
   ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
+  ctask->mhd = hd;
   ctask->curl = curl_easy_init();
 
   if (curl_multi == NULL)
@@ -972,9 +1134,10 @@ create_response (void *cls,
   curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 1);
   curl_easy_setopt (ctask->curl, CURLOPT_MAXREDIRS, 4);
   /* no need to abort if the above failed */
-  sprintf (curlurl, "http://%s%s", host, url);
+  if (GNUNET_NO == ctask->mhd->is_ssl)
+    sprintf (curlurl, "http://%s%s", host, url);
   strcpy (ctask->host, host);
-  strcpy (ctask->url, curlurl);
+  strcpy (ctask->url, url);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Adding new curl task for %s\n", curlurl);
   
@@ -983,29 +1146,6 @@ create_response (void *cls,
   curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
   curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
 
-  mret = curl_multi_add_handle (curl_multi, ctask->curl);
-
-  if (mret != CURLM_OK)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "%s failed at %s:%d: `%s'\n",
-                "curl_multi_add_handle", __FILE__, __LINE__,
-                curl_multi_strerror (mret));
-    response = MHD_create_response_from_buffer (strlen (page),
-                                                (void*)page,
-                                                MHD_RESPMEM_PERSISTENT);
-    ret = MHD_queue_response (con,
-                              MHD_HTTP_OK,
-                              response);
-    MHD_destroy_response (response);
-
-    curl_easy_cleanup (ctask->curl);
-    GNUNET_free (ctask);
-    return ret;
-  }
-  
-  GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
-  
   GNUNET_GNS_get_authority (gns_handle,
                             ctask->host,
                             &process_get_authority,
@@ -1089,7 +1229,7 @@ run_httpd (struct MhdHttpList *hd)
   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
   
-  if (httpd_task != GNUNET_SCHEDULER_NO_TASK)
+  if (hd->httpd_task != GNUNET_SCHEDULER_NO_TASK)
     GNUNET_SCHEDULER_cancel (hd->httpd_task);
   hd->httpd_task =
     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
@@ -1184,6 +1324,29 @@ do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
 }
 
 
+/**
+ * Clean up s5r handles
+ *
+ * @param s5r the handle to destroy
+ */
+static void
+cleanup_s5r (struct Socks5Request *s5r)
+{
+  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 (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);
+}
+
 /**
  * Write data to socket
  *
@@ -1211,16 +1374,16 @@ do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   {
     
     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
-    //Really!?!?!?
-    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);
-    GNUNET_NETWORK_socket_close (s5r->remote_sock);
-    GNUNET_NETWORK_socket_close (s5r->sock);
-    GNUNET_free(s5r);
+    s5r->cleanup = GNUNET_YES;
+    s5r->cleanup_sock = GNUNET_YES;
+    cleanup_s5r (s5r);
+    
+    return;
+  }
+
+  if (GNUNET_YES == s5r->cleanup)
+  {
+    cleanup_s5r (s5r);
     return;
   }
 
@@ -1364,6 +1527,167 @@ load_file (const char* filename)
   return buffer;
 }
 
+/** SSL stuff **/
+
+/**
+ * Load PEM key from file
+ *
+ * @param key where to store the data
+ * @param keyfile path to the PEM file
+ */
+static void
+load_key_from_file (gnutls_x509_privkey_t key, char* keyfile)
+{
+  gnutls_datum_t key_data;
+  int ret;
+
+  key_data.data = (unsigned char*)load_file (keyfile);
+  key_data.size = strlen ((char*)key_data.data);
+
+  ret = gnutls_x509_privkey_import (key, &key_data,
+                                    GNUTLS_X509_FMT_PEM);
+  
+  if (GNUTLS_E_SUCCESS != ret)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unable to import private key %s\n", keyfile);
+    GNUNET_break (0);
+  }
+}
+
+/**
+ * Load cert from file
+ *
+ * @param crt struct to store data in
+ * @param certfile path to pem file
+ */
+static void
+load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
+{
+  gnutls_datum_t cert_data;
+  int ret;
+
+  cert_data.data = (unsigned char*)load_file (certfile);
+  cert_data.size = strlen ((char*)cert_data.data);
+
+  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);
+  }
+
+}
+
+
+/**
+ * Generate new certificate for specific name
+ *
+ * @param name the subject name to generate a cert for
+ * @return a struct holding the PEM data
+ */
+static struct ProxyGNSCertificate *
+generate_gns_certificate (const char *name)
+{
+
+  int ret;
+  unsigned int serial;
+  unsigned int bits;
+  size_t key_buf_size;
+  size_t cert_buf_size;
+  gnutls_x509_crt_t request;
+  gnutls_x509_privkey_t rsa;
+  time_t etime;
+  struct tm *tm_data;
+
+  ret = gnutls_x509_crt_init (&request);
+
+  if (GNUTLS_E_SUCCESS != ret)
+  {
+    GNUNET_break (0);
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating key\n");
+  gnutls_x509_privkey_init (&rsa);
+  bits = gnutls_sec_param_to_pk_bits (GNUTLS_PK_RSA, GNUTLS_SEC_PARAM_NORMAL);
+  ret = gnutls_x509_privkey_generate (rsa, GNUTLS_PK_RSA, bits, 0);
+
+  if (GNUTLS_E_SUCCESS != ret)
+  {
+    GNUNET_break (0);
+  }
+
+  ret = gnutls_x509_crt_set_key (request, rsa);
+
+  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");
+  
+  gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
+                                 0, "DE", 2);
+
+  gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
+                                 0, "GNUnet", 6);
+
+  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));
+
+  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);
+  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);
+
+  key_buf_size = sizeof (pgc->key);
+  cert_buf_size = sizeof (pgc->cert);
+  
+  gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
+                          pgc->cert, &cert_buf_size);
+
+  gnutls_x509_privkey_export (rsa, GNUTLS_X509_FMT_PEM,
+                          pgc->key, &key_buf_size);
+
+
+  gnutls_x509_crt_deinit (request);
+  gnutls_x509_privkey_deinit (rsa);
+
+  return pgc;
+
+}
+
+
 /**
  * Adds a socket to an SSL MHD instance
  * It is important the the domain name is
@@ -1373,12 +1697,7 @@ static int
 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, char* domain)
 {
   struct MhdHttpList *hd = NULL;
-
-  static char *key_pem;
-  static char *cert_pem;
-
-  key_pem = load_file ("server.key");
-  cert_pem = load_file ("server.pem");
+  struct ProxyGNSCertificate *pgc;
 
   for (hd = mhd_httpd_head; hd != NULL; hd = hd->next)
   {
@@ -1389,21 +1708,24 @@ add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, char* domain)
   if (NULL == hd)
   {
     /* Start new MHD */
-    /* TODO: create cert, start SSL MHD */
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "No previous SSL instance found... starting new one\n");
+                "No previous SSL instance found... starting new one for %s\n",
+                domain);
+    
+    pgc = generate_gns_certificate (domain);
+    
     hd = GNUNET_malloc (sizeof (struct MhdHttpList));
     hd->is_ssl = GNUNET_YES;
     strcpy (hd->domain, domain);
     hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL, http_port++,
                               NULL, NULL,
-                              &create_response, NULL,
+                              &create_response, hd,
                               MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
                               MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
                               MHD_OPTION_NOTIFY_COMPLETED,
                               NULL, NULL,
-                              MHD_OPTION_HTTPS_MEM_KEY, key_pem,
-                              MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
+                              MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
+                              MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
                               MHD_OPTION_END);
     hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
     
@@ -1544,14 +1866,12 @@ do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
                     _("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);
-        //ERROR!
-        //TODO! close socket after the write! schedule task
-        //GNUNET_NETWORK_socket_close (s5r->sock);
-        //GNUNET_free(s5r);
         return;
       }
       
@@ -1560,14 +1880,14 @@ do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
       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 ();
-      //GNUNET_free ( s5r );
-      //FIXME complete socks resp!
       return;
     }
     else
@@ -1579,14 +1899,12 @@ do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
                     "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);
-        //ERROR!
-        //TODO! close socket after the write! schedule task
-        //GNUNET_NETWORK_socket_close (s5r->sock);
-        //GNUNET_free(s5r);
         return;
       }
 
@@ -1752,6 +2070,8 @@ do_shutdown (void *cls,
 
   struct MhdHttpList *hd;
   struct MhdHttpList *tmp_hd;
+  struct ProxyCurlTask *ctask;
+  struct ProxyCurlTask *ctask_tmp;
 
   if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
   {
@@ -1763,8 +2083,14 @@ do_shutdown (void *cls,
   {
     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;
     }
@@ -1777,6 +2103,24 @@ do_shutdown (void *cls,
 
     GNUNET_free (hd);
   }
+
+  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);
+
+    GNUNET_free (ctask);
+  }
+
+  GNUNET_GNS_disconnect (gns_handle);
 }
 
 
@@ -1805,6 +2149,53 @@ compile_regex (regex_t *re, const char* rt)
 }
 
 
+/**
+ * Loads the users local zone key
+ *
+ * @return GNUNET_YES on success
+ */
+static int
+load_local_zone_key (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  char *keyfile;
+  struct GNUNET_CRYPTO_RsaPrivateKey *key = NULL;
+  struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
+  struct GNUNET_CRYPTO_ShortHashCode *zone = NULL;
+  struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename;
+
+  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
+                                                            "ZONEKEY", &keyfile))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unable to load zone key config value!\n");
+    return GNUNET_NO;
+  }
+
+  if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unable to load zone key!\n");
+    GNUNET_free(keyfile);
+    return GNUNET_NO;
+  }
+
+  key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
+  GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
+  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);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Using zone: %s!\n", &zonename);
+  GNUNET_CRYPTO_rsa_key_free(key);
+  GNUNET_free(keyfile);
+
+  return GNUNET_YES;
+}
+
+
+
 /**
  * Main function that will be run
  *
@@ -1820,10 +2211,28 @@ run (void *cls, char *const *args, const char *cfgfile,
   struct sockaddr_in sa;
   struct MhdHttpList *hd;
 
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Loading CA\n");
+
+  gnutls_global_init ();
+  
+  load_cert_from_file (proxy_ca.cert, cafile);
+  load_key_from_file (proxy_ca.key, cafile);
+  
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Loading Template\n");
+
   compile_regex (&re_dotplus, (char*) RE_DOTPLUS);
 
   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");
+    return;
+  }
+
   if (NULL == gns_handle)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -1888,7 +2297,7 @@ run (void *cls, char *const *args, const char *cfgfile,
   strcpy (hd->domain, "");
   httpd = MHD_start_daemon (MHD_USE_DEBUG, http_port++,
                                NULL, NULL,
-                               &create_response, NULL,
+                               &create_response, hd,
                                MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
                                MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
                                MHD_OPTION_NOTIFY_COMPLETED,
@@ -1921,11 +2330,17 @@ main (int argc, char *const *argv)
     {'p', "port", NULL,
      gettext_noop ("listen on specified port"), 1,
      &GNUNET_GETOPT_set_string, &port},
+    {'a', "authority", NULL,
+      gettext_noop ("pem file to use as CA"), 1,
+      &GNUNET_GETOPT_set_string, &cafile},
     GNUNET_GETOPT_OPTION_END
   };
 
   int ret;
 
+  if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
+    return 2;
+
   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
   ret =
       (GNUNET_OK ==