-more rsa refactoring
[oweals/gnunet.git] / src / gns / gnunet-gns-proxy.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 #include "platform.h"
21 #include <microhttpd.h>
22 #include <curl/curl.h>
23 #include <gnutls/gnutls.h>
24 #include <gnutls/x509.h>
25 #include <gnutls/abstract.h>
26 #include <gnutls/crypto.h>
27 #include <regex.h>
28 #include "gnunet_util_lib.h"
29 #include "gnunet_gns_service.h"
30 #include "gns_proxy_proto.h"
31 #include "gns.h"
32
33 #define HAVE_MHD_NO_LISTEN_SOCKET (MHD_VERSION >= 0x00091401)
34
35 #define GNUNET_GNS_PROXY_PORT 7777
36 #define MHD_MAX_CONNECTIONS 300
37 #define MAX_HTTP_URI_LENGTH 2048
38 #define POSTBUFFERSIZE 4096
39
40 /* MHD/cURL defines */
41 enum BufferStatus
42   {
43     BUF_WAIT_FOR_CURL,
44     BUF_WAIT_FOR_MHD,
45     BUF_WAIT_FOR_PP 
46   };
47
48 #define HTML_HDR_CONTENT "Content-Type: text/html"
49
50 /* buffer padding for proper RE matching */
51 #define CURL_BUF_PADDING 1000
52
53 /* regexp */
54 //#define RE_DOTPLUS "<a href=\"http://(([A-Za-z]+[.])+)([+])"
55 #define RE_A_HREF  "href=\"https?://(([A-Za-z0-9]+[.])+)([+]|[a-z]+)"
56 #define RE_N_MATCHES 4
57
58 /* The usual suspects */
59 #define HTTP_PORT 80
60 #define HTTPS_PORT 443
61
62
63 /**
64  * A structure for CA cert/key
65  */
66 struct ProxyCA
67 {
68   /* The certificate */
69   gnutls_x509_crt_t cert;
70
71   /* The private key */
72   gnutls_x509_privkey_t key;
73 };
74
75 #define MAX_PEM_SIZE (10 * 1024)
76
77 /**
78  * Structure for GNS certificates
79  */
80 struct ProxyGNSCertificate
81 {
82   /* The certificate as PEM */
83   char cert[MAX_PEM_SIZE];
84
85   /* The private key as PEM */
86   char key[MAX_PEM_SIZE];
87 };
88
89
90 /**
91  * A structure for socks requests
92  */
93 struct Socks5Request
94 {
95   /* The client socket */
96   struct GNUNET_NETWORK_Handle *sock;
97
98   /* The server socket */
99   struct GNUNET_NETWORK_Handle *remote_sock;
100   
101   /* The socks state */
102   int state;
103   
104   /* Client socket read task */
105   GNUNET_SCHEDULER_TaskIdentifier rtask;
106
107   /* Server socket read task */
108   GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
109
110   /* Client socket write task */
111   GNUNET_SCHEDULER_TaskIdentifier wtask;
112
113   /* Server socket write task */
114   GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
115
116   /* Read buffer */
117   char rbuf[2048];
118
119   /* Write buffer */
120   char wbuf[2048];
121
122   /* Length of data in read buffer */
123   unsigned int rbuf_len;
124
125   /* Length of data in write buffer */
126   unsigned int wbuf_len;
127
128   /* This handle is scheduled for cleanup? */
129   int cleanup;
130
131   /* Shall we close the client socket on cleanup? */
132   int cleanup_sock;
133 };
134
135 /**
136  * DLL for Network Handles
137  */
138 struct NetworkHandleList
139 {
140   /*DLL*/
141   struct NetworkHandleList *next;
142
143   /*DLL*/
144   struct NetworkHandleList *prev;
145
146   /* The handle */
147   struct GNUNET_NETWORK_Handle *h;
148 };
149
150 /**
151  * A structure for all running Httpds
152  */
153 struct MhdHttpList
154 {
155   /* DLL for httpds */
156   struct MhdHttpList *prev;
157
158   /* DLL for httpds */
159   struct MhdHttpList *next;
160
161   /* is this an ssl daemon? */
162   int is_ssl;
163
164   /* the domain name to server (only important for SSL) */
165   char domain[256];
166
167   /* The daemon handle */
168   struct MHD_Daemon *daemon;
169
170   /* Optional proxy certificate used */
171   struct ProxyGNSCertificate *proxy_cert;
172
173   /* The task ID */
174   GNUNET_SCHEDULER_TaskIdentifier httpd_task;
175
176   /* Handles associated with this daemon */
177   struct NetworkHandleList *socket_handles_head;
178   
179   /* Handles associated with this daemon */
180   struct NetworkHandleList *socket_handles_tail;
181 };
182
183 /**
184  * A structure for MHD<->cURL streams
185  */
186 struct ProxyCurlTask
187 {
188   /* DLL for tasks */
189   struct ProxyCurlTask *prev;
190
191   /* DLL for tasks */
192   struct ProxyCurlTask *next;
193
194   /* Handle to cURL */
195   CURL *curl;
196
197   /* Optional header replacements for curl (LEHO) */
198   struct curl_slist *headers;
199
200   /* Optional resolver replacements for curl (LEHO) */
201   struct curl_slist *resolver;
202
203   /* curl response code */
204   long curl_response_code;
205
206   /* The URL to fetch */
207   char url[MAX_HTTP_URI_LENGTH];
208
209   /* The cURL write buffer / MHD read buffer */
210   char buffer[CURL_MAX_WRITE_SIZE + CURL_BUF_PADDING];
211
212   /* Read pos of the data in the buffer */
213   char *buffer_read_ptr;
214
215   /* Write pos in the buffer */
216   char *buffer_write_ptr;
217
218   /* connection */
219   struct MHD_Connection *connection;
220
221   /*put*/
222   size_t put_read_offset;
223   size_t put_read_size;
224
225   /*post*/
226   struct MHD_PostProcessor *post_handler;
227
228   /* post data */
229   struct ProxyUploadData *upload_data_head;
230   struct ProxyUploadData *upload_data_tail;
231
232   /* the type of POST encoding */
233   char* post_type;
234
235   struct curl_httppost *httppost;
236
237   struct curl_httppost *httppost_last;
238
239   /* Number of bytes in buffer */
240   unsigned int bytes_in_buffer;
241
242   /* PP task */
243   GNUNET_SCHEDULER_TaskIdentifier pp_task;
244
245   /* PP match list */
246   struct ProxyREMatch *pp_match_head;
247
248   /* PP match list */
249   struct ProxyREMatch *pp_match_tail;
250
251   /* The associated daemon list entry */
252   struct MhdHttpList *mhd;
253
254   /* The associated response */
255   struct MHD_Response *response;
256
257   /* Cookies to set */
258   struct ProxySetCookieHeader *set_cookies_head;
259
260   /* Cookies to set */
261   struct ProxySetCookieHeader *set_cookies_tail;
262
263   /* The authority of the corresponding host (site of origin) */
264   char authority[256];
265
266   /* The hostname (Host header field) */
267   char host[256];
268
269   /* The LEgacy HOstname (can be empty) */
270   char leho[256];
271
272   /* The port */
273   uint16_t port;
274
275   /* The buffer status (BUF_WAIT_FOR_CURL or BUF_WAIT_FOR_MHD) */
276   enum BufferStatus buf_status;
277
278   /* connection status */
279   int ready_to_queue;
280
281   /* is curl running? */
282   int curl_running;
283   
284   /* are we done */
285   int fin;
286
287   /* Already accepted */
288   int accepted;
289
290   /* Indicates wheather the download is in progress */
291   int download_in_progress;
292
293   /* Indicates wheather the download was successful */
294   int download_is_finished;
295
296   /* Indicates wheather the download failed */
297   int download_error;
298
299   /* Indicates wheather we need to parse HTML */
300   int parse_content;
301
302   /* Indicates wheather we are postprocessing the HTML right now */
303   int is_postprocessing;
304
305   /* Indicates wheather postprocessing has finished */
306   int pp_finished;
307
308   int post_done;
309
310   int is_httppost;
311   
312 };
313
314 /**
315  * Struct for RE matches in postprocessing of HTML
316  */
317 struct ProxyREMatch
318 {
319   /* DLL */
320   struct ProxyREMatch *next;
321
322   /* DLL */
323   struct ProxyREMatch *prev;
324
325   /* start of match in buffer */
326   char* start;
327
328   /* end of match in buffer */
329   char* end;
330
331   /* associated proxycurltask */
332   struct ProxyCurlTask *ctask;
333
334   /* hostname found */
335   char hostname[255];
336
337   /* PP result */
338   char result[255];
339
340   /* shorten task */
341   struct GNUNET_GNS_ShortenRequest *shorten_task;
342
343   /* are we done */
344   int done;
345
346   /* is SSL */
347   int is_ssl;
348
349 };
350
351 /**
352  * Struct for set-cookies
353  */
354 struct ProxySetCookieHeader
355 {
356   /* DLL */
357   struct ProxySetCookieHeader *next;
358
359   /* DLL */
360   struct ProxySetCookieHeader *prev;
361
362   /* the cookie */
363   char *cookie;
364 };
365
366 /**
367  * Post data structure
368  */
369 struct ProxyUploadData
370 {
371   /* DLL */
372   struct ProxyUploadData *next;
373
374   /* DLL */
375   struct ProxyUploadData *prev;
376
377   char *key;
378
379   char *filename;
380
381   char *content_type;
382
383   size_t content_length;
384   
385   /* value */
386   char *value;
387
388   /* to copy */
389   size_t bytes_left;
390
391   /* size */
392   size_t total_bytes;
393 };
394
395
396 /* The port the proxy is running on (default 7777) */
397 static unsigned long port = GNUNET_GNS_PROXY_PORT;
398
399 /* The CA file (pem) to use for the proxy CA */
400 static char* cafile_opt;
401
402 /* The listen socket of the proxy */
403 static struct GNUNET_NETWORK_Handle *lsock;
404
405 /* The listen task ID */
406 static GNUNET_SCHEDULER_TaskIdentifier ltask;
407
408 /* The cURL download task */
409 static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
410
411 /* The non SSL httpd daemon handle */
412 static struct MHD_Daemon *httpd;
413
414 /* Number of current mhd connections */
415 static unsigned int total_mhd_connections;
416
417 /* The cURL multi handle */
418 static CURLM *curl_multi;
419
420 /* Handle to the GNS service */
421 static struct GNUNET_GNS_Handle *gns_handle;
422
423 /* DLL for ProxyCurlTasks */
424 static struct ProxyCurlTask *ctasks_head;
425
426 /* DLL for ProxyCurlTasks */
427 static struct ProxyCurlTask *ctasks_tail;
428
429 /* DLL for http daemons */
430 static struct MhdHttpList *mhd_httpd_head;
431
432 /* DLL for http daemons */
433 static struct MhdHttpList *mhd_httpd_tail;
434
435 /* Handle to the regex for dotplus (.+) replacement in HTML */
436 static regex_t re_dotplus;
437
438 /* The users local GNS zone hash */
439 static struct GNUNET_CRYPTO_ShortHashCode *local_gns_zone;
440
441 /* The users local private zone */
442 static struct GNUNET_CRYPTO_ShortHashCode *local_private_zone;
443
444 /* The users local shorten zone */
445 static struct GNUNET_CRYPTO_ShortHashCode *local_shorten_zone;
446
447 /* The CA for SSL certificate generation */
448 static struct ProxyCA proxy_ca;
449
450 /* UNIX domain socket for mhd */
451 #if !HAVE_MHD_NO_LISTEN_SOCKET
452 static struct GNUNET_NETWORK_Handle *mhd_unix_socket;
453 #endif
454
455 /* Shorten zone private key */
456 static struct GNUNET_CRYPTO_RsaPrivateKey *shorten_zonekey;
457
458
459 /**
460  * Checks if name is in tld
461  *
462  * @param name the name to check 
463  * @param tld the TLD to check for (must NOT begin with ".")
464  * @return GNUNET_YES or GNUNET_NO
465  */
466 static int
467 is_tld (const char* name, const char* tld)
468 {
469   size_t name_len = strlen (name);
470   size_t tld_len = strlen (tld);
471
472   GNUNET_break ('.' != tld[0]);
473   return ( (tld_len < name_len) &&
474            ( ('.' == name[name_len - tld_len - 1]) || (name_len == tld_len) ) &&
475            (0 == memcmp (tld,
476                          name + (name_len - tld_len),
477                          tld_len)) );
478 }
479
480
481 /**
482  * convert integer to string representation
483  *
484  * @param i integer
485  * @return the character
486  */
487 static char 
488 i_to_hexchar (unsigned char i)
489 {
490   static char hexmap[] = "0123456789abcdef";
491
492   GNUNET_assert (sizeof (hexmap) > (i & 15));
493   return hexmap[i & 15];
494 }
495
496
497 /**
498 // FIXME: use cURL API
499  * Escape given 0-terminated string
500  *
501  * @param to_esc string to escapse
502  * @return allocated new escaped string (MUST free!)
503  */
504 static char*
505 escape_to_urlenc (const char *to_esc)
506 {
507   char *pos = (char*)to_esc;
508   char *res = GNUNET_malloc (strlen (to_esc) * 3 + 1);
509   char *rpos = res;
510
511   while ('\0' != *pos)
512   {
513     if (isalnum (*pos) ||
514         ('-' == *pos) || ('_' == *pos) ||
515         ('.' == *pos) || ('~' == *pos))
516         *rpos++ = *pos;
517     else if (' ' == *pos)
518       *rpos++ = '+';
519     else
520     {
521       *rpos++ = '%';
522       *rpos++ = i_to_hexchar (*pos >> 4);
523       *rpos++ = i_to_hexchar (*pos >> 15);
524     }
525     pos++;
526   }
527   *rpos = '\0';
528   return res;
529 }
530
531
532 static int
533 con_post_data_iter (void *cls,
534                   enum MHD_ValueKind kind,
535                   const char *key,
536                   const char *filename,
537                   const char *content_type,
538                   const char *transfer_encoding,
539                   const char *data,
540                   uint64_t off,
541                   size_t size)
542 {
543   struct ProxyCurlTask* ctask = cls;
544   struct ProxyUploadData* pdata;
545   char* enc;
546   char* new_value;
547   
548   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
549               "Got POST data (file: %s, content type: %s): '%s=%.*s' at offset %llu size %llu\n",
550               filename, content_type,
551               key, (int) size, data, 
552               (unsigned long long) off, 
553               (unsigned long long) size);
554   GNUNET_assert (NULL != ctask->post_type);
555
556   if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
557                        ctask->post_type))
558   {
559     ctask->is_httppost = GNUNET_YES;
560     /* new part */
561     if (0 == off)
562     {
563       pdata = GNUNET_malloc (sizeof (struct ProxyUploadData));
564       pdata->key = GNUNET_strdup (key);
565
566       if (NULL != filename)
567         pdata->filename = GNUNET_strdup (filename);
568       if (NULL != content_type)
569         pdata->content_type = GNUNET_strdup (content_type);
570       pdata->value = GNUNET_malloc (size);
571       pdata->total_bytes = size;
572       memcpy (pdata->value, data, size);
573       GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
574                                         ctask->upload_data_tail,
575                                         pdata);
576
577       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
578                   "Copied %llu bytes of POST Data\n", 
579                   (unsigned long long) size);
580       return MHD_YES;
581     }
582     
583     pdata = ctask->upload_data_tail;
584     new_value = GNUNET_malloc (size + pdata->total_bytes);
585     memcpy (new_value, pdata->value, pdata->total_bytes);
586     memcpy (new_value+off, data, size);
587     GNUNET_free (pdata->value);
588     pdata->value = new_value;
589     pdata->total_bytes += size;
590
591     return MHD_YES;
592   }
593
594   if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
595                        ctask->post_type))
596   {
597     return MHD_NO;
598   }
599
600   ctask->is_httppost = GNUNET_NO;
601   
602   if (NULL != ctask->curl)
603     curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
604
605   if (0 == off)
606   {
607     /* a key */
608     pdata = GNUNET_malloc (sizeof (struct ProxyUploadData));
609     enc = escape_to_urlenc (key);
610     pdata->value = GNUNET_malloc (strlen (enc) + 3);
611     if (NULL != ctask->upload_data_head)
612     {
613       pdata->value[0] = '&';
614       memcpy (pdata->value+1, enc, strlen (enc));
615     }
616     else
617       memcpy (pdata->value, enc, strlen (enc));
618     pdata->value[strlen (pdata->value)] = '=';
619     pdata->bytes_left = strlen (pdata->value);
620     pdata->total_bytes = pdata->bytes_left;
621     GNUNET_free (enc);
622
623     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
624                 "Escaped POST key: '%s'\n",
625                 pdata->value);
626
627     GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
628                                       ctask->upload_data_tail,
629                                       pdata);
630   }
631
632   /* a value */
633   pdata = GNUNET_malloc (sizeof (struct ProxyUploadData));
634   enc = escape_to_urlenc (data);
635   pdata->value = GNUNET_malloc (strlen (enc) + 1);
636   memcpy (pdata->value, enc, strlen (enc));
637   pdata->bytes_left = strlen (pdata->value);
638   pdata->total_bytes = pdata->bytes_left;
639   GNUNET_free (enc);
640
641   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
642               "Escaped POST value: '%s'\n",
643               pdata->value);
644
645   GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
646                                     ctask->upload_data_tail,
647                                     pdata);
648   return MHD_YES;
649 }
650
651
652 /**
653  * Read HTTP request header field 'Host'
654  *
655  * @param cls buffer to write to
656  * @param kind value kind
657  * @param key field key
658  * @param value field value
659  * @return MHD_NO when Host found
660  */
661 static int
662 con_val_iter (void *cls,
663               enum MHD_ValueKind kind,
664               const char *key,
665               const char *value)
666 {
667   struct ProxyCurlTask *ctask = cls;
668   char* buf = ctask->host;
669   char* port;
670   char* cstr;
671   const char* hdr_val;
672   unsigned int uport;
673
674   if (0 == strcmp ("Host", key))
675   {
676     port = strchr (value, ':');
677     if (NULL != port)
678     {
679       strncpy (buf, value, port-value);
680       port++;
681       if ((1 != sscanf (port, "%u", &uport)) ||
682            (uport > UINT16_MAX) ||
683            (0 == uport))
684         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
685                     "Unable to parse port!\n");
686       else
687         ctask->port = (uint16_t) uport;
688     }
689     else
690       strcpy (buf, value);
691     return MHD_YES;
692   }
693
694   if (0 == strcmp (MHD_HTTP_HEADER_ACCEPT_ENCODING, key))
695     hdr_val = "";
696   else
697     hdr_val = value;
698
699   if (0 == strcasecmp (MHD_HTTP_HEADER_CONTENT_TYPE,
700                    key))
701   {
702     if (0 == strncasecmp (value,
703                      MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
704                      strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
705       ctask->post_type = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
706     else if (0 == strncasecmp (value,
707                           MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
708                           strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
709       ctask->post_type = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
710     else
711       ctask->post_type = NULL;
712
713   }
714
715   cstr = GNUNET_malloc (strlen (key) + strlen (hdr_val) + 3);
716   GNUNET_snprintf (cstr, strlen (key) + strlen (hdr_val) + 3,
717                    "%s: %s", key, hdr_val);
718
719   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
720               "Client Header: %s\n", cstr);
721
722   ctask->headers = curl_slist_append (ctask->headers, cstr);
723   GNUNET_free (cstr);
724
725   return MHD_YES;
726 }
727
728
729 /**
730  * Callback for MHD response
731  *
732  * @param cls closure
733  * @param pos in buffer
734  * @param buf buffer
735  * @param max space in buffer
736  * @return number of bytes written
737  */
738 static ssize_t
739 mhd_content_cb (void *cls,
740                 uint64_t pos,
741                 char* buf,
742                 size_t max);
743
744 /**
745  * Check HTTP response header for mime
746  *
747  * @param buffer curl buffer
748  * @param size curl blocksize
749  * @param nmemb curl blocknumber
750  * @param cls handle
751  * @return size of read bytes
752  */
753 static size_t
754 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
755 {
756   size_t bytes = size * nmemb;
757   struct ProxyCurlTask *ctask = cls;
758   int html_mime_len = strlen (HTML_HDR_CONTENT);
759   int cookie_hdr_len = strlen (MHD_HTTP_HEADER_SET_COOKIE);
760   char hdr_mime[html_mime_len+1];
761   char hdr_generic[bytes+1];
762   char new_cookie_hdr[bytes+strlen (ctask->leho)+1];
763   char new_location[MAX_HTTP_URI_LENGTH+500];
764   char real_host[264];
765   char leho_host[264];
766   char* ndup;
767   char* tok;
768   char* cookie_domain;
769   char* hdr_type;
770   char* hdr_val;
771   int delta_cdomain;
772   size_t offset = 0;
773   char cors_hdr[strlen (ctask->leho) + strlen ("https://")];
774   
775   if (NULL == ctask->response)
776   {
777     /* FIXME: get total size from curl (if available) */
778     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
779                 "Creating response for %s\n", ctask->url);
780     ctask->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
781                                                          sizeof (ctask->buffer),
782                                                          &mhd_content_cb,
783                                                          ctask,
784                                                          NULL);
785
786     /* if we have a leho add a CORS header */
787     if (0 != strcmp ("", ctask->leho))
788     {
789       /* We could also allow ssl and http here */
790       if (ctask->mhd->is_ssl)
791         sprintf (cors_hdr, "https://%s", ctask->leho);
792       else
793         sprintf (cors_hdr, "http://%s", ctask->leho);
794
795       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
796                   "MHD: Adding CORS header field %s\n",
797                   cors_hdr);
798
799       if (GNUNET_NO == MHD_add_response_header (ctask->response,
800                                               "Access-Control-Allow-Origin",
801                                               cors_hdr))
802       {
803         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
804                   "MHD: Error adding CORS header field %s\n",
805                   cors_hdr);
806       }
807     }
808     ctask->ready_to_queue = GNUNET_YES;
809   }
810   
811   if (html_mime_len <= bytes)
812   {
813     memcpy (hdr_mime, buffer, html_mime_len);
814     hdr_mime[html_mime_len] = '\0';
815
816     if (0 == strcmp (hdr_mime, HTML_HDR_CONTENT))
817     {
818       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
819                   "Got HTML HTTP response header\n");
820       ctask->parse_content = GNUNET_YES;
821     }
822   }
823
824   if (cookie_hdr_len > bytes)
825     return bytes;
826
827   memcpy (hdr_generic, buffer, bytes);
828   hdr_generic[bytes] = '\0';
829   /* remove crlf */
830   if ('\n' == hdr_generic[bytes-1])
831     hdr_generic[bytes-1] = '\0';
832
833   if (hdr_generic[bytes-2] == '\r')
834     hdr_generic[bytes-2] = '\0';
835   
836   if (0 == memcmp (hdr_generic,
837                    MHD_HTTP_HEADER_SET_COOKIE,
838                    cookie_hdr_len))
839   {
840     ndup = GNUNET_strdup (hdr_generic+cookie_hdr_len+1);
841     memset (new_cookie_hdr, 0, sizeof (new_cookie_hdr));
842     tok = strtok (ndup, ";");
843
844     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
845                 "Looking for cookie in : %s\n", hdr_generic);
846     
847     for (; tok != NULL; tok = strtok (NULL, ";"))
848     {
849       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
850                   "Got Cookie token: %s\n", tok);
851       //memcpy (new_cookie_hdr+offset, tok, strlen (tok));
852       if (0 == memcmp (tok, " domain", strlen (" domain")))
853       {
854         cookie_domain = tok + strlen (" domain") + 1;
855
856         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
857                     "Got Set-Cookie Domain: %s\n", cookie_domain);
858
859         if (strlen (cookie_domain) < strlen (ctask->leho))
860         {
861           delta_cdomain = strlen (ctask->leho) - strlen (cookie_domain);
862           if (0 == strcmp (cookie_domain, ctask->leho + (delta_cdomain)))
863           {
864             GNUNET_snprintf (new_cookie_hdr+offset,
865                              sizeof (new_cookie_hdr),
866                              " domain=%s", ctask->authority);
867             offset += strlen (" domain=") + strlen (ctask->authority);
868             new_cookie_hdr[offset] = ';';
869             offset++;
870             continue;
871           }
872         }
873         else if (strlen (cookie_domain) == strlen (ctask->leho))
874         {
875           if (0 == strcmp (cookie_domain, ctask->leho))
876           {
877             GNUNET_snprintf (new_cookie_hdr+offset,
878                              sizeof (new_cookie_hdr),
879                              " domain=%s", ctask->host);
880             offset += strlen (" domain=") + strlen (ctask->host);
881             new_cookie_hdr[offset] = ';';
882             offset++;
883             continue;
884           }
885         }
886         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
887                     "Cookie domain invalid\n");
888
889         
890       }
891       memcpy (new_cookie_hdr+offset, tok, strlen (tok));
892       offset += strlen (tok);
893       new_cookie_hdr[offset] = ';';
894       offset++;
895     }
896     
897     GNUNET_free (ndup);
898
899     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
900                 "Got Set-Cookie HTTP header %s\n", new_cookie_hdr);
901
902     if (GNUNET_NO == MHD_add_response_header (ctask->response,
903                                               MHD_HTTP_HEADER_SET_COOKIE,
904                                               new_cookie_hdr))
905     {
906       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
907                   "MHD: Error adding set-cookie header field %s\n",
908                   hdr_generic+cookie_hdr_len+1);
909     }
910     return bytes;
911   }
912
913   ndup = GNUNET_strdup (hdr_generic);
914   hdr_type = strtok (ndup, ":");
915
916   if (NULL == hdr_type)
917   {
918     GNUNET_free (ndup);
919     return bytes;
920   }
921
922   hdr_val = strtok (NULL, "");
923
924   if (NULL == hdr_val)
925   {
926     GNUNET_free (ndup);
927     return bytes;
928   }
929
930   hdr_val++;
931
932   if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
933   {
934     if (ctask->mhd->is_ssl)
935     {
936       sprintf (leho_host, "https://%s", ctask->leho);
937       sprintf (real_host, "https://%s", ctask->host);
938     }
939     else
940     {
941       sprintf (leho_host, "http://%s", ctask->leho);
942       sprintf (real_host, "http://%s", ctask->host);
943     }
944
945     if (0 == memcmp (leho_host, hdr_val, strlen (leho_host)))
946     {
947       sprintf (new_location, "%s%s", real_host, hdr_val+strlen (leho_host));
948       hdr_val = new_location;
949     }
950   }
951
952   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
953               "Trying to set %s: %s\n",
954               hdr_type,
955               hdr_val);
956   if (GNUNET_NO == MHD_add_response_header (ctask->response,
957                                             hdr_type,
958                                             hdr_val))
959   {
960     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
961                 "MHD: Error adding %s header field %s\n",
962                 hdr_type,
963                 hdr_val);
964   }
965   GNUNET_free (ndup);
966   return bytes;
967 }
968
969 /**
970  * schedule mhd
971  *
972  * @param hd a http daemon list entry
973  */
974 static void
975 run_httpd (struct MhdHttpList *hd);
976
977
978 /**
979  * schedule all mhds
980  *
981  */
982 static void
983 run_httpds (void);
984
985 /**
986  * Task run whenever HTTP server operations are pending.
987  *
988  * @param cls unused
989  * @param tc sched context
990  */
991 static void
992 do_httpd (void *cls,
993           const struct GNUNET_SCHEDULER_TaskContext *tc);
994
995 static void
996 run_mhd_now (struct MhdHttpList *hd)
997 {
998   if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
999   {
1000     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1001                 "MHD: killing old task\n");
1002     GNUNET_SCHEDULER_cancel (hd->httpd_task);
1003   }
1004   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1005               "MHD: Scheduling MHD now\n");
1006   hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, hd);
1007 }
1008
1009 /**
1010  * Ask cURL for the select sets and schedule download
1011  */
1012 static void
1013 curl_download_prepare (void);
1014
1015 /**
1016  * Callback to free content
1017  *
1018  * @param cls content to free
1019  * @param tc task context
1020  */
1021 static void
1022 mhd_content_free (void *cls,
1023                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1024 {
1025   struct ProxyCurlTask *ctask = cls;
1026   struct ProxyUploadData *pdata;
1027
1028   GNUNET_assert (NULL == ctask->pp_match_head);
1029   if (NULL != ctask->headers)
1030     curl_slist_free_all (ctask->headers);
1031
1032   if (NULL != ctask->headers)
1033     curl_slist_free_all (ctask->resolver);
1034
1035   if (NULL != ctask->response)
1036     MHD_destroy_response (ctask->response);
1037
1038   if (NULL != ctask->post_handler)
1039     MHD_destroy_post_processor (ctask->post_handler);
1040
1041   if (GNUNET_SCHEDULER_NO_TASK != ctask->pp_task)
1042     GNUNET_SCHEDULER_cancel (ctask->pp_task);
1043
1044   for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
1045   {
1046     GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1047                                  ctask->upload_data_tail,
1048                                  pdata);
1049     GNUNET_free_non_null (pdata->filename);
1050     GNUNET_free_non_null (pdata->content_type);
1051     GNUNET_free_non_null (pdata->key);
1052     GNUNET_free_non_null (pdata->value);
1053     GNUNET_free (pdata);
1054   }
1055   GNUNET_free (ctask);
1056 }
1057
1058
1059 /**
1060  * Callback for MHD response
1061  *
1062  * @param cls closure
1063  * @param pos in buffer
1064  * @param buf buffer
1065  * @param max space in buffer
1066  * @return number of bytes written
1067  */
1068 static ssize_t
1069 mhd_content_cb (void *cls,
1070                 uint64_t pos,
1071                 char* buf,
1072                 size_t max)
1073 {
1074   struct ProxyCurlTask *ctask = cls;
1075   struct ProxyREMatch *re_match;
1076   ssize_t copied = 0;
1077   size_t bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1078
1079   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1080               "MHD: content cb for %s. To copy: %u\n",
1081               ctask->url, (unsigned int) bytes_to_copy);
1082   if ((GNUNET_YES == ctask->download_is_finished) &&
1083       (GNUNET_NO == ctask->download_error) &&
1084       (0 == bytes_to_copy))
1085   {
1086     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1087                 "MHD: sending response for %s\n", ctask->url);
1088     ctask->download_in_progress = GNUNET_NO;
1089     run_mhd_now (ctask->mhd);
1090     GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1091     total_mhd_connections--;
1092     return MHD_CONTENT_READER_END_OF_STREAM;
1093   }
1094   
1095   if ((GNUNET_YES == ctask->download_error) &&
1096       (GNUNET_YES == ctask->download_is_finished) &&
1097       (0 == bytes_to_copy))
1098   {
1099     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1100                 "MHD: sending error response\n");
1101     ctask->download_in_progress = GNUNET_NO;
1102     run_mhd_now (ctask->mhd);
1103     GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1104     total_mhd_connections--;
1105     return MHD_CONTENT_READER_END_WITH_ERROR;
1106   }
1107
1108   if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
1109     return 0;
1110   
1111   copied = 0;
1112   for (re_match = ctask->pp_match_head; NULL != re_match; re_match = ctask->pp_match_head)
1113   {
1114     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1115                 "MHD: Processing PP %s\n",
1116                 re_match->hostname);
1117     bytes_to_copy = re_match->start - ctask->buffer_read_ptr;
1118     if (bytes_to_copy+copied > max)
1119     {
1120       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1121                   "MHD: buffer in response too small for %u. Using available space (%d). (%s)\n",
1122                   (unsigned int) bytes_to_copy,
1123                   max,
1124                   ctask->url);
1125       memcpy (buf+copied, ctask->buffer_read_ptr, max-copied);
1126       ctask->buffer_read_ptr += max-copied;
1127       copied = max;
1128       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1129                   "MHD: copied %d bytes\n", (int) copied);
1130       return copied;
1131     }
1132
1133     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1134                 "MHD: copying %u bytes to mhd response at offset %d\n",
1135                 (unsigned int) bytes_to_copy, ctask->buffer_read_ptr);
1136     memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
1137     copied += bytes_to_copy;
1138
1139     if (GNUNET_NO == re_match->done)
1140     {
1141       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1142                   "MHD: Waiting for PP of %s\n", re_match->hostname);
1143       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1144                   "MHD: copied %d bytes\n", (int) copied);
1145       ctask->buffer_read_ptr += bytes_to_copy;
1146       return copied;
1147     }
1148     
1149     if (strlen (re_match->result) > (max - copied))
1150     {
1151       //FIXME partially copy domain here
1152       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1153                   "MHD: buffer in response too small for %s! (%s)\n",
1154                   re_match->result,
1155                   ctask->url);
1156       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1157                   "MHD: copied %d bytes\n", (int) copied);
1158       ctask->buffer_read_ptr += bytes_to_copy;
1159       return copied;
1160     }
1161     
1162     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1163                 "MHD: Adding PP result %s to buffer\n",
1164                 re_match->result);
1165     memcpy (buf + copied, re_match->result, strlen (re_match->result));
1166     copied += strlen (re_match->result);
1167     ctask->buffer_read_ptr = re_match->end;
1168     GNUNET_CONTAINER_DLL_remove (ctask->pp_match_head,
1169                                  ctask->pp_match_tail,
1170                                  re_match);
1171     GNUNET_free (re_match);
1172   }
1173
1174   bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1175
1176   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1177               "MHD: copied: %d left: %u, space left in buf: %d\n",
1178               copied,
1179               (unsigned int) bytes_to_copy, (int) (max - copied));
1180   
1181   if (GNUNET_NO == ctask->download_is_finished)
1182   {
1183     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1184                 "MHD: Purging buffer\n");
1185     memmove (ctask->buffer, ctask->buffer_read_ptr, bytes_to_copy);
1186     ctask->buffer_read_ptr = ctask->buffer;
1187     ctask->buffer_write_ptr = ctask->buffer + bytes_to_copy;
1188     ctask->buffer[bytes_to_copy] = '\0';
1189   }
1190   
1191   if (bytes_to_copy + copied > max)
1192     bytes_to_copy = max - copied;
1193   memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
1194   ctask->buffer_read_ptr += bytes_to_copy;
1195   copied += bytes_to_copy;
1196   ctask->buf_status = BUF_WAIT_FOR_CURL;
1197   
1198   if (NULL != ctask->curl)
1199     curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
1200
1201   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1202               "MHD: copied %d bytes\n", (int) copied);
1203   run_mhd_now (ctask->mhd);
1204   return copied;
1205 }
1206
1207
1208 /**
1209  * Shorten result callback
1210  *
1211  * @param cls the proxycurltask
1212  * @param short_name the shortened name (NULL on error)
1213  */
1214 static void
1215 process_shorten (void* cls, const char* short_name)
1216 {
1217   struct ProxyREMatch *re_match = cls;
1218   char result[sizeof (re_match->result)];
1219   
1220   if (NULL == short_name)
1221   {
1222     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1223                 "PP: Unable to shorten %s\n",
1224                 re_match->hostname);
1225     GNUNET_CONTAINER_DLL_remove (re_match->ctask->pp_match_head,
1226                                  re_match->ctask->pp_match_tail,
1227                                  re_match);
1228     GNUNET_free (re_match);
1229     return;
1230   }
1231
1232   if (0 == strcmp (short_name, re_match->ctask->leho))
1233     strcpy (result, re_match->ctask->host);
1234   else
1235     strcpy (result, short_name);
1236
1237   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1238               "PP: Shorten %s -> %s\n",
1239               re_match->hostname,
1240               result);
1241   
1242   if (re_match->is_ssl)
1243     sprintf (re_match->result, "href=\"https://%s", result);
1244   else
1245     sprintf (re_match->result, "href=\"http://%s", result);
1246
1247   re_match->done = GNUNET_YES;
1248   run_mhd_now (re_match->ctask->mhd);
1249 }
1250
1251
1252 /**
1253  * Postprocess data in buffer. From read ptr to write ptr
1254  *
1255  * @param cls the curlproxytask
1256  * @param tc task context
1257  */
1258 static void
1259 postprocess_buffer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1260 {
1261   struct ProxyCurlTask *ctask = cls;
1262   struct ProxyREMatch *re_match;
1263   char* re_ptr = ctask->buffer_read_ptr;
1264   char re_hostname[255];
1265   regmatch_t m[RE_N_MATCHES];
1266
1267   ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
1268
1269   if (GNUNET_YES != ctask->parse_content)
1270   {
1271     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1272                 "PP: Not parsing content\n");
1273     ctask->buf_status = BUF_WAIT_FOR_MHD;
1274     run_mhd_now (ctask->mhd);
1275     return;
1276   }
1277
1278   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1279               "PP: We need to parse the HTML\n");
1280
1281   /* 0 means match found */
1282   while (0 == regexec (&re_dotplus, re_ptr, RE_N_MATCHES, m, 0))
1283   {
1284     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1285                 "PP: regex match\n");
1286
1287     GNUNET_assert (m[1].rm_so != -1);
1288
1289     memset (re_hostname, 0, sizeof (re_hostname));
1290     memcpy (re_hostname, re_ptr+m[1].rm_so, (m[3].rm_eo-m[1].rm_so));
1291     
1292
1293     re_match = GNUNET_malloc (sizeof (struct ProxyREMatch));
1294     re_match->start = re_ptr + m[0].rm_so;
1295     re_match->end = re_ptr + m[3].rm_eo;
1296     re_match->done = GNUNET_NO;
1297     re_match->ctask = ctask;
1298     
1299     if ('s' == *(re_ptr+m[1].rm_so-strlen("://")-1)) //FIXME strcmp
1300       re_match->is_ssl = GNUNET_YES;
1301     else
1302       re_match->is_ssl = GNUNET_NO;
1303       
1304     strcpy (re_match->hostname, re_hostname);
1305     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1306                 "PP: Got hostname %s\n", re_hostname);
1307     re_ptr += m[3].rm_eo;
1308
1309     if (GNUNET_YES == is_tld (re_match->hostname, GNUNET_GNS_TLD_PLUS))
1310     {
1311       re_match->hostname[strlen(re_match->hostname)-1] = '\0';
1312       strcpy (re_match->hostname+strlen(re_match->hostname),
1313               ctask->authority);
1314     }
1315
1316     re_match->shorten_task = GNUNET_GNS_shorten_zone (gns_handle,
1317                              re_match->hostname,
1318                              local_private_zone,
1319                              local_shorten_zone,
1320                              local_gns_zone,
1321                              &process_shorten,
1322                              re_match); //FIXME cancel appropriately
1323
1324     GNUNET_CONTAINER_DLL_insert_tail (ctask->pp_match_head,
1325                                       ctask->pp_match_tail,
1326                                       re_match);
1327   }
1328   
1329   ctask->buf_status = BUF_WAIT_FOR_MHD;
1330   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1331               "PP: No more matches\n");
1332   run_mhd_now (ctask->mhd);
1333 }
1334
1335 /**
1336  * Handle data from cURL
1337  *
1338  * @param ptr pointer to the data
1339  * @param size number of blocks of data
1340  * @param nmemb blocksize
1341  * @param ctx the curlproxytask
1342  * @return number of bytes handled
1343  */
1344 static size_t
1345 curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx)
1346 {
1347   const char *cbuf = ptr;
1348   size_t total = size * nmemb;
1349   struct ProxyCurlTask *ctask = ctx;
1350   size_t buf_space = sizeof (ctask->buffer) -
1351     (ctask->buffer_write_ptr-ctask->buffer);
1352
1353   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1354               "CURL: Got %d. %d free in buffer\n",
1355               total, buf_space);
1356
1357   if (BUF_WAIT_FOR_CURL != ctask->buf_status)
1358     return CURL_WRITEFUNC_PAUSE;
1359
1360   if (total > (buf_space - CURL_BUF_PADDING))
1361   {
1362     if (ctask->buf_status == BUF_WAIT_FOR_CURL)
1363     {
1364       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1365                   "CURL: Buffer full starting postprocessing\n");
1366       ctask->buf_status = BUF_WAIT_FOR_PP;
1367       ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer,
1368                                                  ctask);
1369       return CURL_WRITEFUNC_PAUSE;
1370     }
1371
1372     /* we should not get called in that case */
1373     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1374                 "CURL: called out of context and no space in buffer!\n");
1375     return CURL_WRITEFUNC_PAUSE;
1376   }
1377
1378   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1379               "CURL: Copying %d bytes to buffer (%s)\n", total, ctask->url);
1380   memcpy (ctask->buffer_write_ptr, cbuf, total);
1381   ctask->bytes_in_buffer += total;
1382   ctask->buffer_write_ptr += total;
1383   ctask->buffer_write_ptr[0] = '\0';
1384
1385   return total;
1386 }
1387
1388
1389 /**
1390  * cURL callback for put data
1391  */
1392 static size_t
1393 put_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1394 {
1395   struct ProxyCurlTask *ctask = cls;
1396   struct ProxyUploadData *pdata = ctask->upload_data_head;
1397   size_t len = size * nmemb;
1398   size_t to_copy;
1399   char* pos;
1400
1401   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1402               "CURL: put read callback\n");
1403
1404   if (NULL == pdata)
1405     return CURL_READFUNC_PAUSE;
1406   
1407   //fin
1408   if (NULL == pdata->value)
1409   {
1410     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1411                 "CURL: Terminating PUT\n");
1412
1413     GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1414                                  ctask->upload_data_tail,
1415                                  pdata);
1416     GNUNET_free (pdata);
1417     return 0;
1418   }
1419  
1420   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1421               "CURL: read callback value %s\n", pdata->value); 
1422   
1423   to_copy = pdata->bytes_left;
1424   if (to_copy > len)
1425     to_copy = len;
1426   
1427   pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1428   memcpy (buf, pos, to_copy);
1429   pdata->bytes_left -= to_copy;
1430   if (pdata->bytes_left <= 0)
1431   {
1432     GNUNET_free (pdata->value);
1433     GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1434                                  ctask->upload_data_tail,
1435                                  pdata);
1436     GNUNET_free (pdata);
1437   }
1438   return to_copy;
1439 }
1440
1441
1442 /**
1443  * cURL callback for post data
1444  */
1445 static size_t
1446 post_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1447 {
1448   struct ProxyCurlTask *ctask = cls;
1449   struct ProxyUploadData *pdata = ctask->upload_data_head;
1450   size_t len = size * nmemb;
1451   size_t to_copy;
1452   char* pos;
1453
1454   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1455               "CURL: read callback\n");
1456
1457   if (NULL == pdata)
1458     return CURL_READFUNC_PAUSE;
1459   
1460   //fin
1461   if (NULL == pdata->value)
1462   {
1463     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1464                 "CURL: Terminating POST data\n");
1465
1466     GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1467                                  ctask->upload_data_tail,
1468                                  pdata);
1469     GNUNET_free (pdata);
1470     return 0;
1471   }
1472  
1473   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1474               "CURL: read callback value %s\n", pdata->value); 
1475   
1476   to_copy = pdata->bytes_left;
1477   if (to_copy > len)
1478     to_copy = len;
1479   
1480   pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1481   memcpy (buf, pos, to_copy);
1482   pdata->bytes_left -= to_copy;
1483   if (pdata->bytes_left <= 0)
1484   {
1485     GNUNET_free (pdata->value);
1486     GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1487                                  ctask->upload_data_tail,
1488                                  pdata);
1489     GNUNET_free (pdata);
1490   }
1491   return to_copy;
1492 }
1493
1494 /**
1495  * Task that is run when we are ready to receive more data
1496  * from curl
1497  *
1498  * @param cls closure
1499  * @param tc task context
1500  */
1501 static void
1502 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1503
1504 /**
1505  * Ask cURL for the select sets and schedule download
1506  */
1507 static void
1508 curl_download_prepare ()
1509 {
1510   CURLMcode mret;
1511   fd_set rs;
1512   fd_set ws;
1513   fd_set es;
1514   int max;
1515   struct GNUNET_NETWORK_FDSet *grs;
1516   struct GNUNET_NETWORK_FDSet *gws;
1517   long to;
1518   struct GNUNET_TIME_Relative rtime;
1519
1520   max = -1;
1521   FD_ZERO (&rs);
1522   FD_ZERO (&ws);
1523   FD_ZERO (&es);
1524   mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
1525
1526   if (mret != CURLM_OK)
1527   {
1528     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1529                 "%s failed at %s:%d: `%s'\n",
1530                 "curl_multi_fdset", __FILE__, __LINE__,
1531                 curl_multi_strerror (mret));
1532     //TODO cleanup here?
1533     return;
1534   }
1535
1536   mret = curl_multi_timeout (curl_multi, &to);
1537   rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
1538
1539   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1540               "cURL multi fds: max=%d timeout=%llu\n", max, to);
1541
1542   grs = GNUNET_NETWORK_fdset_create ();
1543   gws = GNUNET_NETWORK_fdset_create ();
1544   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1545   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1546   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1547               "Scheduling task cURL\n");
1548
1549   if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
1550     GNUNET_SCHEDULER_cancel (curl_download_task);
1551   
1552   if (-1 != max)
1553   {
1554     curl_download_task =
1555       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1556                                    rtime,
1557                                    grs, gws,
1558                                    &curl_task_download, curl_multi);
1559   }
1560   else if (NULL != ctasks_head)
1561   {
1562     /* as specified in curl docs */
1563     curl_download_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
1564                                                        &curl_task_download,
1565                                                        curl_multi);
1566   }
1567   GNUNET_NETWORK_fdset_destroy (gws);
1568   GNUNET_NETWORK_fdset_destroy (grs);
1569 }
1570
1571
1572 /**
1573  * Task that is run when we are ready to receive more data
1574  * from curl
1575  *
1576  * @param cls closure
1577  * @param tc task context
1578  */
1579 static void
1580 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1581 {
1582   int running;
1583   int msgnum;
1584   struct CURLMsg *msg;
1585   CURLMcode mret;
1586   struct ProxyCurlTask *ctask;
1587   int num_ctasks;
1588   long resp_code;
1589
1590   struct ProxyCurlTask *clean_head = NULL;
1591   struct ProxyCurlTask *clean_tail = NULL;
1592
1593   curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1594
1595   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1596   {
1597     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1598                 "Shutdown requested while trying to download\n");
1599   //TODO cleanup
1600   return;
1601   }
1602   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1603               "Ready to dl\n");
1604
1605   do
1606   {
1607     running = 0;
1608     num_ctasks = 0;
1609     
1610     mret = curl_multi_perform (curl_multi, &running);
1611
1612     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1613                 "Running curl tasks: %d\n", running);
1614
1615     for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1616     {
1617       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1618                   "CTask: %s\n", ctask->url);
1619       num_ctasks++;
1620     }
1621
1622     if (num_ctasks != running)
1623     {
1624       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1625                   "%d ctasks, %d curl running\n", num_ctasks, running);
1626     }
1627     
1628     do
1629     {
1630       
1631       msg = curl_multi_info_read (curl_multi, &msgnum);
1632       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1633                   "Messages left: %d\n", msgnum);
1634       
1635       if (msg == NULL)
1636         break;
1637       switch (msg->msg)
1638       {
1639        case CURLMSG_DONE:
1640          if ((msg->data.result != CURLE_OK) &&
1641              (msg->data.result != CURLE_GOT_NOTHING))
1642          {
1643            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1644                        "Download curl failed");
1645             
1646            for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1647            {
1648              if (NULL == ctask->curl)
1649                continue;
1650
1651              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
1652                continue;
1653              
1654              GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1655                          "CURL: Download failed for task %s: %s.\n",
1656                          ctask->url,
1657                          curl_easy_strerror (msg->data.result));
1658              ctask->download_is_finished = GNUNET_YES;
1659              ctask->download_error = GNUNET_YES;
1660              if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1661                                                 CURLINFO_RESPONSE_CODE,
1662                                                 &resp_code))
1663                ctask->curl_response_code = resp_code;
1664              ctask->ready_to_queue = MHD_YES;
1665              ctask->buf_status = BUF_WAIT_FOR_MHD;
1666              run_mhd_now (ctask->mhd);
1667              
1668              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1669                                           ctask);
1670              GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1671              break;
1672            }
1673            GNUNET_assert (ctask != NULL);
1674          }
1675          else
1676          {
1677            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1678                        "CURL: download completed.\n");
1679
1680            for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1681            {
1682              if (NULL == ctask->curl)
1683                continue;
1684
1685              if (0 != memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)))
1686                continue;
1687              
1688              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1689                          "CURL: completed task %s found.\n", ctask->url);
1690              if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1691                                                 CURLINFO_RESPONSE_CODE,
1692                                                 &resp_code))
1693                ctask->curl_response_code = resp_code;
1694
1695
1696              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1697                          "CURL: Completed ctask!\n");
1698              if (GNUNET_SCHEDULER_NO_TASK == ctask->pp_task)
1699              {
1700               ctask->buf_status = BUF_WAIT_FOR_PP;
1701               ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer,
1702                                                           ctask);
1703              }
1704
1705              ctask->ready_to_queue = MHD_YES;
1706              ctask->download_is_finished = GNUNET_YES;
1707
1708              /* We MUST not modify the multi handle else we loose messages */
1709              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1710                                           ctask);
1711              GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1712
1713              break;
1714            }
1715            GNUNET_assert (ctask != NULL);
1716          }
1717          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1718                      "CURL: %s\n", curl_easy_strerror(msg->data.result));
1719          break;
1720        default:
1721          GNUNET_assert (0);
1722          break;
1723       }
1724     } while (msgnum > 0);
1725
1726     for (ctask=clean_head; NULL != ctask; ctask = ctask->next)
1727     {
1728       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1729                   "CURL: Removing task %s.\n", ctask->url);
1730       curl_multi_remove_handle (curl_multi, ctask->curl);
1731       curl_easy_cleanup (ctask->curl);
1732       ctask->curl = NULL;
1733     }
1734     
1735     num_ctasks=0;
1736     for (ctask=ctasks_head; NULL != ctask; ctask = ctask->next)    
1737       num_ctasks++; 
1738     
1739     if (num_ctasks != running)
1740     {
1741       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1742                   "CURL: %d tasks, %d running\n", num_ctasks, running);
1743     }
1744
1745     GNUNET_assert ( num_ctasks == running );
1746
1747   } while (mret == CURLM_CALL_MULTI_PERFORM);
1748   
1749   if (mret != CURLM_OK)
1750   {
1751     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CURL: %s failed at %s:%d: `%s'\n",
1752                 "curl_multi_perform", __FILE__, __LINE__,
1753                 curl_multi_strerror (mret));
1754   }
1755   curl_download_prepare();
1756 }
1757
1758
1759 /**
1760  * Process LEHO lookup
1761  *
1762  * @param cls the ctask
1763  * @param rd_count number of records returned
1764  * @param rd record data
1765  */
1766 static void
1767 process_leho_lookup (void *cls,
1768                      uint32_t rd_count,
1769                      const struct GNUNET_NAMESTORE_RecordData *rd)
1770 {
1771   struct ProxyCurlTask *ctask = cls;
1772   char hosthdr[262]; //256 + "Host: "
1773   int i;
1774   CURLcode ret;
1775   CURLMcode mret;
1776   struct hostent *phost;
1777   char *ssl_ip;
1778   char resolvename[512];
1779   char curlurl[512];
1780
1781   strcpy (ctask->leho, "");
1782
1783   if (rd_count == 0)
1784     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1785                 "No LEHO present!\n");
1786
1787   for (i=0; i<rd_count; i++)
1788   {
1789     if (rd[i].record_type != GNUNET_GNS_RECORD_LEHO)
1790       continue;
1791
1792     memcpy (ctask->leho, rd[i].data, rd[i].data_size);
1793
1794     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1795                 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
1796   }
1797
1798   if (0 != strcmp (ctask->leho, ""))
1799   {
1800     sprintf (hosthdr, "%s%s:%d", "Host: ", ctask->leho, ctask->port);
1801     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1802                 "New HTTP header value: %s\n", hosthdr);
1803     ctask->headers = curl_slist_append (ctask->headers, hosthdr);
1804     GNUNET_assert (NULL != ctask->headers);
1805     ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers);
1806     if (CURLE_OK != ret)
1807     {
1808       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s failed at %s:%d: `%s'\n",
1809                            "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret));
1810     }
1811
1812   }
1813
1814   if (ctask->mhd->is_ssl)
1815   {
1816     phost = (struct hostent*)gethostbyname (ctask->host);
1817
1818     if (phost!=NULL)
1819     {
1820       ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
1821       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1822                   "SSL target server: %s\n", ssl_ip);
1823       sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
1824       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1825                   "Curl resolve: %s\n", resolvename);
1826       ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
1827       curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver);
1828       sprintf (curlurl, "https://%s:%d%s", ctask->leho, ctask->port, ctask->url);
1829       curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1830     }
1831     else
1832     {
1833       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1834                   "gethostbyname failed for %s!\n", ctask->host);
1835       ctask->download_is_finished = GNUNET_YES;
1836       ctask->download_error = GNUNET_YES;
1837       return;
1838     }
1839   }
1840
1841   if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
1842   {
1843     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1844                 "%s failed at %s:%d: `%s'\n",
1845                 "curl_multi_add_handle", __FILE__, __LINE__,
1846                 curl_multi_strerror (mret));
1847     ctask->download_is_finished = GNUNET_YES;
1848     ctask->download_error = GNUNET_YES;
1849     return;
1850   }
1851   GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1852
1853   curl_download_prepare ();
1854
1855 }
1856
1857 /**
1858  * Initialize download and trigger curl
1859  *
1860  * @param cls the proxycurltask
1861  * @param auth_name the name of the authority (site of origin) of ctask->host
1862  *
1863  */
1864 static void
1865 process_get_authority (void *cls,
1866                        const char* auth_name)
1867 {
1868   struct ProxyCurlTask *ctask = cls;
1869
1870   if (NULL == auth_name)
1871   {
1872     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1873                 "Get authority failed!\n");
1874     strcpy (ctask->authority, "");
1875   }
1876   else
1877   {
1878     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1879                 "Get authority yielded %s\n", auth_name);
1880     strcpy (ctask->authority, auth_name);
1881   }
1882
1883   GNUNET_GNS_lookup_zone (gns_handle,
1884                           ctask->host,
1885                           local_gns_zone,
1886                           GNUNET_GNS_RECORD_LEHO,
1887                           GNUNET_YES, //Only cached for performance
1888                           shorten_zonekey,
1889                           &process_leho_lookup,
1890                           ctask);
1891 }
1892
1893 static void*
1894 mhd_log_callback (void* cls, const char* url)
1895 {
1896   struct ProxyCurlTask *ctask;
1897
1898   ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
1899   strcpy (ctask->url, url);
1900   return ctask;
1901 }
1902
1903
1904 /**
1905  * Main MHD callback for handling requests.
1906  *
1907  * @param cls unused
1908  * @param con MHD connection handle
1909  * @param url the url in the request
1910  * @param meth the HTTP method used ("GET", "PUT", etc.)
1911  * @param ver the HTTP version string (i.e. "HTTP/1.1")
1912  * @param upload_data the data being uploaded (excluding HEADERS,
1913  *        for a POST that fits into memory and that is encoded
1914  *        with a supported encoding, the POST data will NOT be
1915  *        given in upload_data and is instead available as
1916  *        part of MHD_get_connection_values; very large POST
1917  *        data *will* be made available incrementally in
1918  *        upload_data)
1919  * @param upload_data_size set initially to the size of the
1920  *        upload_data provided; the method must update this
1921  *        value to the number of bytes NOT processed;
1922  * @param con_cls pointer to location where we store the 'struct Request'
1923  * @return MHD_YES if the connection was handled successfully,
1924  *         MHD_NO if the socket must be closed due to a serious
1925  *         error while handling the request
1926  */
1927 static int
1928 create_response (void *cls,
1929                  struct MHD_Connection *con,
1930                  const char *url,
1931                  const char *meth,
1932                  const char *ver,
1933                  const char *upload_data,
1934                  size_t *upload_data_size,
1935                  void **con_cls)
1936 {
1937   struct MhdHttpList* hd = cls;
1938   const char* page = "<html><head><title>gnunet-gns-proxy</title>"
1939                       "</head><body>cURL fail</body></html>";
1940   
1941   char curlurl[MAX_HTTP_URI_LENGTH]; // buffer overflow!
1942   int ret = MHD_YES;
1943   int i;
1944   struct ProxyCurlTask *ctask = *con_cls;
1945   struct ProxyUploadData *fin_post;
1946   struct curl_forms forms[5];
1947   struct ProxyUploadData *upload_data_iter;
1948   
1949   //FIXME handle
1950   if ((0 != strcasecmp (meth, MHD_HTTP_METHOD_GET)) &&
1951       (0 != strcasecmp (meth, MHD_HTTP_METHOD_PUT)) &&
1952       (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST)) &&
1953       (0 != strcasecmp (meth, MHD_HTTP_METHOD_HEAD)))
1954   {
1955     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1956                 "MHD: %s NOT IMPLEMENTED!\n", meth);
1957     return MHD_NO;
1958   }
1959
1960
1961   if (GNUNET_NO == ctask->accepted)
1962   {
1963
1964     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1965                 "Got %s request for %s\n", meth, url);
1966     ctask->mhd = hd;
1967     ctask->curl = curl_easy_init();
1968     ctask->curl_running = GNUNET_NO;
1969     if (NULL == ctask->curl)
1970     {
1971       ctask->response = MHD_create_response_from_buffer (strlen (page),
1972                                                 (void*)page,
1973                                                 MHD_RESPMEM_PERSISTENT);
1974       ret = MHD_queue_response (con,
1975                                 MHD_HTTP_OK,
1976                                 ctask->response);
1977       MHD_destroy_response (ctask->response);
1978       GNUNET_free (ctask);
1979       return ret;
1980     }
1981     
1982     if (ctask->mhd->is_ssl)
1983       ctask->port = HTTPS_PORT;
1984     else
1985       ctask->port = HTTP_PORT;
1986
1987     MHD_get_connection_values (con,
1988                                MHD_HEADER_KIND,
1989                                &con_val_iter, ctask);
1990     
1991     curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1992     curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
1993     curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1994     curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
1995     curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 0);
1996     curl_easy_setopt (ctask->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1997
1998     if (GNUNET_NO == ctask->mhd->is_ssl)
1999     {
2000       sprintf (curlurl, "http://%s:%d%s", ctask->host, ctask->port, ctask->url);
2001       curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
2002     }
2003     
2004
2005     curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
2006     curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
2007     curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
2008     
2009     /* Add GNS header */
2010     ctask->headers = curl_slist_append (ctask->headers,
2011                                           "GNS: YES");
2012     ctask->accepted = GNUNET_YES;
2013     ctask->download_in_progress = GNUNET_YES;
2014     ctask->buf_status = BUF_WAIT_FOR_CURL;
2015     ctask->connection = con;
2016     ctask->curl_response_code = MHD_HTTP_OK;
2017     ctask->buffer_read_ptr = ctask->buffer;
2018     ctask->buffer_write_ptr = ctask->buffer;
2019     ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
2020     
2021
2022     if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
2023     {
2024       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2025                   "Setting up PUT\n");
2026       
2027       curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1);
2028       curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
2029       curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &put_read_callback);
2030       ctask->headers = curl_slist_append (ctask->headers,
2031                                           "Transfer-Encoding: chunked");
2032     }
2033
2034     if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2035     {
2036       //FIXME handle multipart
2037       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2038                   "Setting up POST processor\n");
2039       ctask->post_handler = MHD_create_post_processor (con,
2040                                  POSTBUFFERSIZE,
2041                                  &con_post_data_iter,
2042                                  ctask);
2043       ctask->headers = curl_slist_append (ctask->headers,
2044                                          "Transfer-Encoding: chunked");
2045       return MHD_YES;
2046     }
2047
2048     if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
2049     {
2050       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2051                   "Setting NOBODY\n");
2052       curl_easy_setopt (ctask->curl, CURLOPT_NOBODY, 1);
2053     }
2054
2055     
2056     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2057                   "MHD: Adding new curl task for %s\n", ctask->host);
2058
2059     GNUNET_GNS_get_authority (gns_handle,
2060                               ctask->host,
2061                               &process_get_authority,
2062                               ctask);
2063     ctask->ready_to_queue = GNUNET_NO;
2064     ctask->fin = GNUNET_NO;
2065     ctask->curl_running = GNUNET_YES;
2066     return MHD_YES;
2067   }
2068
2069   ctask = (struct ProxyCurlTask *) *con_cls;
2070   if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2071   {
2072     if (0 != *upload_data_size)
2073     {
2074       
2075       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2076                   "Invoking POST processor\n");
2077       MHD_post_process (ctask->post_handler,
2078                         upload_data, *upload_data_size);
2079       *upload_data_size = 0;
2080       if ((GNUNET_NO == ctask->is_httppost) &&
2081           (GNUNET_NO == ctask->curl_running))
2082       {
2083         curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
2084         curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION,
2085                           &post_read_callback);
2086         curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
2087         
2088         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2089                   "MHD: Adding new curl task for %s\n", ctask->host);
2090
2091         GNUNET_GNS_get_authority (gns_handle,
2092                                   ctask->host,
2093                                   &process_get_authority,
2094                                   ctask);
2095         ctask->ready_to_queue = GNUNET_NO;
2096         ctask->fin = GNUNET_NO;
2097         ctask->curl_running = GNUNET_YES;
2098       }
2099       return MHD_YES;
2100     }
2101     else if (GNUNET_NO == ctask->post_done)
2102     {
2103       if (GNUNET_YES == ctask->is_httppost)
2104       {
2105         for (upload_data_iter = ctask->upload_data_head;
2106              NULL != upload_data_iter;
2107              upload_data_iter = upload_data_iter->next)
2108         {
2109           i = 0;
2110           if (NULL != upload_data_iter->filename)
2111           {
2112             forms[i].option = CURLFORM_FILENAME;
2113             forms[i].value = upload_data_iter->filename;
2114             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2115                         "Adding filename %s\n",
2116                         forms[i].value);
2117             i++;
2118           }
2119           if (NULL != upload_data_iter->content_type)
2120           {
2121             forms[i].option = CURLFORM_CONTENTTYPE;
2122             forms[i].value = upload_data_iter->content_type;
2123             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2124                         "Adding content type %s\n",
2125                         forms[i].value);
2126             i++;
2127           }
2128           forms[i].option = CURLFORM_PTRCONTENTS;
2129           forms[i].value = upload_data_iter->value;
2130           forms[i+1].option = CURLFORM_END;
2131
2132           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2133                       "Adding formdata for %s (len=%lld)\n",
2134                       upload_data_iter->key,
2135                       upload_data_iter->total_bytes);
2136
2137           curl_formadd(&ctask->httppost, &ctask->httppost_last,
2138                        CURLFORM_COPYNAME, upload_data_iter->key,
2139                        CURLFORM_CONTENTSLENGTH, upload_data_iter->total_bytes,
2140                        CURLFORM_ARRAY, forms,
2141                        CURLFORM_END);
2142         }
2143         curl_easy_setopt (ctask->curl, CURLOPT_HTTPPOST,
2144                           ctask->httppost);
2145
2146         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2147                 "MHD: Adding new curl task for %s\n", ctask->host);
2148
2149         GNUNET_GNS_get_authority (gns_handle,
2150                                   ctask->host,
2151                                   &process_get_authority,
2152                                   ctask);
2153         ctask->ready_to_queue = GNUNET_YES;
2154         ctask->fin = GNUNET_NO;
2155         ctask->curl_running = GNUNET_YES;
2156         ctask->post_done = GNUNET_YES;
2157         return MHD_YES;
2158       }
2159
2160       fin_post = GNUNET_malloc (sizeof (struct ProxyUploadData));
2161       GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
2162                                         ctask->upload_data_tail,
2163                                         fin_post);
2164       ctask->post_done = GNUNET_YES;
2165       return MHD_YES;
2166     }
2167   }
2168   
2169   if (GNUNET_YES != ctask->ready_to_queue)
2170     return MHD_YES; /* wait longer */
2171   
2172   if (GNUNET_YES == ctask->fin)
2173     return MHD_YES;
2174
2175   ctask->fin = GNUNET_YES;
2176   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2177               "MHD: Queueing response for %s\n", ctask->url);
2178   ret = MHD_queue_response (con, ctask->curl_response_code, ctask->response);
2179   run_mhd_now (ctask->mhd);
2180   return ret;
2181 }
2182
2183
2184 /**
2185  * run all httpd
2186  */
2187 static void
2188 run_httpds ()
2189 {
2190   struct MhdHttpList *hd;
2191
2192   for (hd=mhd_httpd_head; NULL != hd; hd = hd->next)
2193     run_httpd (hd);
2194
2195 }
2196
2197
2198 #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
2199
2200
2201 /**
2202  * schedule mhd
2203  *
2204  * @param hd the daemon to run
2205  */
2206 static void
2207 run_httpd (struct MhdHttpList *hd)
2208 {
2209   fd_set rs;
2210   fd_set ws;
2211   fd_set es;
2212   struct GNUNET_NETWORK_FDSet *wrs;
2213   struct GNUNET_NETWORK_FDSet *wws;
2214   struct GNUNET_NETWORK_FDSet *wes;
2215   int max;
2216   int haveto;
2217   UNSIGNED_MHD_LONG_LONG timeout;
2218   struct GNUNET_TIME_Relative tv;
2219
2220   FD_ZERO (&rs);
2221   FD_ZERO (&ws);
2222   FD_ZERO (&es);
2223   wrs = GNUNET_NETWORK_fdset_create ();
2224   wes = GNUNET_NETWORK_fdset_create ();
2225   wws = GNUNET_NETWORK_fdset_create ();
2226   max = -1;
2227   GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
2228   
2229   
2230   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2231               "MHD fds: max=%d\n", max);
2232   
2233   haveto = MHD_get_timeout (hd->daemon, &timeout);
2234
2235   if (MHD_YES == haveto)
2236     tv.rel_value = (uint64_t) timeout;
2237   else
2238     tv = GNUNET_TIME_UNIT_FOREVER_REL;
2239   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
2240   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
2241   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
2242   
2243   if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2244     GNUNET_SCHEDULER_cancel (hd->httpd_task);
2245   hd->httpd_task =
2246     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
2247                                  tv, wrs, wws,
2248                                  &do_httpd, hd);
2249   GNUNET_NETWORK_fdset_destroy (wrs);
2250   GNUNET_NETWORK_fdset_destroy (wws);
2251   GNUNET_NETWORK_fdset_destroy (wes);
2252 }
2253
2254
2255 /**
2256  * Task run whenever HTTP server operations are pending.
2257  *
2258  * @param cls unused
2259  * @param tc sched context
2260  */
2261 static void
2262 do_httpd (void *cls,
2263           const struct GNUNET_SCHEDULER_TaskContext *tc)
2264 {
2265   struct MhdHttpList *hd = cls;
2266   
2267   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2268               "MHD: Main loop\n");
2269   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK; 
2270   MHD_run (hd->daemon);
2271   run_httpd (hd);
2272 }
2273
2274
2275
2276 /**
2277  * Read data from socket
2278  *
2279  * @param cls the closure
2280  * @param tc scheduler context
2281  */
2282 static void
2283 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2284
2285 /**
2286  * Read from remote end
2287  *
2288  * @param cls closure
2289  * @param tc scheduler context
2290  */
2291 static void
2292 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2293
2294 /**
2295  * Write data to remote socket
2296  *
2297  * @param cls the closure
2298  * @param tc scheduler context
2299  */
2300 static void
2301 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2302 {
2303   struct Socks5Request *s5r = cls;
2304   unsigned int len;
2305
2306   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2307
2308   if ((NULL != tc->read_ready) &&
2309       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
2310       ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
2311                                          s5r->rbuf_len)>0)))
2312   {
2313     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2314                 "Successfully sent %d bytes to remote socket\n",
2315                 len);
2316   }
2317   else
2318   {
2319     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
2320     if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
2321       GNUNET_SCHEDULER_cancel (s5r->rtask);
2322     if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
2323       GNUNET_SCHEDULER_cancel (s5r->wtask);
2324     if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
2325       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2326     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2327     GNUNET_NETWORK_socket_close (s5r->sock);
2328     GNUNET_free(s5r);
2329     return;
2330   }
2331
2332   s5r->rtask =
2333     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2334                                    s5r->sock,
2335                                    &do_read, s5r);
2336 }
2337
2338
2339 /**
2340  * Clean up s5r handles
2341  *
2342  * @param s5r the handle to destroy
2343  */
2344 static void
2345 cleanup_s5r (struct Socks5Request *s5r)
2346 {
2347   if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
2348     GNUNET_SCHEDULER_cancel (s5r->rtask);
2349   if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdwtask)
2350     GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2351   if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
2352     GNUNET_SCHEDULER_cancel (s5r->fwdrtask);  
2353   if (NULL != s5r->remote_sock)
2354     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2355   if ((NULL != s5r->sock) && (s5r->cleanup_sock == GNUNET_YES))
2356     GNUNET_NETWORK_socket_close (s5r->sock);
2357   
2358   GNUNET_free(s5r);
2359 }
2360
2361 /**
2362  * Write data to socket
2363  *
2364  * @param cls the closure
2365  * @param tc scheduler context
2366  */
2367 static void
2368 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2369 {
2370   struct Socks5Request *s5r = cls;
2371   unsigned int len;
2372
2373   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2374
2375   if ((NULL != tc->read_ready) &&
2376       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
2377       ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
2378                                          s5r->wbuf_len)>0)))
2379   {
2380     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2381                 "Successfully sent %d bytes to socket\n",
2382                 len);
2383   }
2384   else
2385   {
2386     
2387     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
2388     s5r->cleanup = GNUNET_YES;
2389     s5r->cleanup_sock = GNUNET_YES;
2390     cleanup_s5r (s5r);
2391     
2392     return;
2393   }
2394
2395   if (GNUNET_YES == s5r->cleanup)
2396   {
2397     cleanup_s5r (s5r);
2398     return;
2399   }
2400
2401   if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
2402       (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
2403     s5r->fwdrtask =
2404       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2405                                      s5r->remote_sock,
2406                                      &do_read_remote, s5r);
2407 }
2408
2409 /**
2410  * Read from remote end
2411  *
2412  * @param cls closure
2413  * @param tc scheduler context
2414  */
2415 static void
2416 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2417 {
2418   struct Socks5Request *s5r = cls;
2419   
2420   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2421
2422
2423   if ((NULL != tc->write_ready) &&
2424       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
2425       (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
2426                                          sizeof (s5r->wbuf))))
2427   {
2428     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2429                 "Successfully read %d bytes from remote socket\n",
2430                 s5r->wbuf_len);
2431   }
2432   else
2433   {
2434     if (s5r->wbuf_len == 0)
2435       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2436                   "0 bytes received from remote... graceful shutdown!\n");
2437     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2438       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2439     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
2440       GNUNET_SCHEDULER_cancel (s5r->rtask);
2441     
2442     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2443     s5r->remote_sock = NULL;
2444     GNUNET_NETWORK_socket_close (s5r->sock);
2445     GNUNET_free(s5r);
2446
2447     return;
2448   }
2449   
2450   s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2451                                                s5r->sock,
2452                                                &do_write, s5r);
2453   
2454 }
2455
2456
2457 /**
2458  * Adds a socket to MHD
2459  *
2460  * @param h the handle to the socket to add
2461  * @param daemon the daemon to add the fd to
2462  * @return whatever MHD_add_connection returns
2463  */
2464 static int
2465 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon)
2466 {
2467   int fd;
2468   struct sockaddr *addr;
2469   socklen_t len;
2470
2471   fd = dup (GNUNET_NETWORK_get_fd (h));
2472   addr = GNUNET_NETWORK_get_addr (h);
2473   len = GNUNET_NETWORK_get_addrlen (h);
2474
2475   return MHD_add_connection (daemon, fd, addr, len);
2476 }
2477
2478 /**
2479  * Read file in filename
2480  *
2481  * @param filename file to read
2482  * @param size pointer where filesize is stored
2483  * @return NULL on error
2484  */
2485 static void*
2486 load_file (const char* filename, 
2487            unsigned int* size)
2488 {
2489   void *buffer;
2490   uint64_t fsize;
2491
2492   if (GNUNET_OK !=
2493       GNUNET_DISK_file_size (filename, &fsize,
2494                              GNUNET_YES, GNUNET_YES))
2495     return NULL;
2496   if (fsize > MAX_PEM_SIZE)
2497     return NULL;
2498   *size = (unsigned int) fsize;
2499   buffer = GNUNET_malloc (*size);
2500   if (fsize != GNUNET_DISK_fn_read (filename, buffer, (size_t) fsize))
2501   {
2502     GNUNET_free (buffer);
2503     return NULL;
2504   }
2505   return buffer;
2506 }
2507
2508
2509 /**
2510  * Load PEM key from file
2511  *
2512  * @param key where to store the data
2513  * @param keyfile path to the PEM file
2514  * @return GNUNET_OK on success
2515  */
2516 static int
2517 load_key_from_file (gnutls_x509_privkey_t key, const char* keyfile)
2518 {
2519   gnutls_datum_t key_data;
2520   int ret;
2521
2522   key_data.data = load_file (keyfile, &key_data.size);
2523   ret = gnutls_x509_privkey_import (key, &key_data,
2524                                     GNUTLS_X509_FMT_PEM);
2525   if (GNUTLS_E_SUCCESS != ret)
2526   {
2527     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2528                 _("Unable to import private key from file `%s'\n"),
2529                 keyfile);
2530     GNUNET_break (0);
2531   }
2532   GNUNET_free (key_data.data);
2533   return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2534 }
2535
2536
2537 /**
2538  * Load cert from file
2539  *
2540  * @param crt struct to store data in
2541  * @param certfile path to pem file
2542  * @return GNUNET_OK on success
2543  */
2544 static int
2545 load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
2546 {
2547   gnutls_datum_t cert_data;
2548   int ret;
2549
2550   cert_data.data = load_file (certfile, &cert_data.size);
2551   ret = gnutls_x509_crt_import (crt, &cert_data,
2552                                 GNUTLS_X509_FMT_PEM);
2553   if (GNUTLS_E_SUCCESS != ret)
2554   {
2555     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2556                _("Unable to import certificate %s\n"), certfile);
2557     GNUNET_break (0);
2558   }
2559   GNUNET_free (cert_data.data);
2560   return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2561 }
2562
2563
2564 /**
2565  * Generate new certificate for specific name
2566  *
2567  * @param name the subject name to generate a cert for
2568  * @return a struct holding the PEM data
2569  */
2570 static struct ProxyGNSCertificate *
2571 generate_gns_certificate (const char *name)
2572 {
2573   int ret;
2574   unsigned int serial;
2575   size_t key_buf_size;
2576   size_t cert_buf_size;
2577   gnutls_x509_crt_t request;
2578   time_t etime;
2579   struct tm *tm_data;
2580
2581   ret = gnutls_x509_crt_init (&request);
2582
2583   if (GNUTLS_E_SUCCESS != ret)
2584   {
2585     GNUNET_break (0);
2586   }
2587
2588   GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
2589
2590   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
2591
2592   struct ProxyGNSCertificate *pgc =
2593     GNUNET_malloc (sizeof (struct ProxyGNSCertificate));
2594
2595   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\n");
2596   
2597   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
2598                                  0, "DE", 2);
2599   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
2600                                  0, "GADS", 4);
2601   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
2602                                  0, name, strlen (name));
2603   GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_version (request, 3));
2604
2605   ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
2606
2607   etime = time (NULL);
2608   tm_data = localtime (&etime);
2609   
2610
2611   ret = gnutls_x509_crt_set_serial (request,
2612                                     &serial,
2613                                     sizeof (serial));
2614
2615   ret = gnutls_x509_crt_set_activation_time (request,
2616                                              etime);
2617   tm_data->tm_year++;
2618   etime = mktime (tm_data);
2619
2620   if (-1 == etime)
2621   {
2622     GNUNET_break (0);
2623   }
2624
2625   ret = gnutls_x509_crt_set_expiration_time (request,
2626                                              etime);
2627   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
2628
2629   ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
2630
2631   key_buf_size = sizeof (pgc->key);
2632   cert_buf_size = sizeof (pgc->cert);
2633
2634   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Exporting certificate...\n");
2635   
2636   gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
2637                           pgc->cert, &cert_buf_size);
2638
2639   gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
2640                           pgc->key, &key_buf_size);
2641
2642
2643   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
2644   gnutls_x509_crt_deinit (request);
2645
2646   return pgc;
2647
2648 }
2649
2650
2651 /*
2652  * Accept policy for mhdaemons
2653  *
2654  * @param cls NULL
2655  * @param addr the sockaddr
2656  * @param addrlen the sockaddr length
2657  * @return MHD_NO if sockaddr is wrong or #conns too high
2658  */
2659 static int
2660 accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen)
2661 {
2662   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2663               "In MHD accept policy cb\n");
2664
2665   if (addr != NULL)
2666   {
2667     if (addr->sa_family == AF_UNIX)
2668       return MHD_NO;
2669   }
2670
2671   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2672               "Connection accepted\n");
2673
2674   return MHD_YES;
2675 }
2676
2677
2678 /**
2679  * Adds a socket to an SSL MHD instance
2680  * It is important the the domain name is
2681  * correct. In most cases we need to start a new daemon
2682  *
2683  * @param h the handle to add to a daemon
2684  * @param domain the domain the ssl daemon has to serve
2685  * @return MHD_YES on success
2686  */
2687 static int
2688 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, const char* domain)
2689 {
2690   struct MhdHttpList *hd = NULL;
2691   struct ProxyGNSCertificate *pgc;
2692   struct NetworkHandleList *nh;
2693
2694   for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
2695     if (0 == strcmp (hd->domain, domain))
2696       break;
2697
2698   if (NULL == hd)
2699   {    
2700     pgc = generate_gns_certificate (domain);
2701     
2702     hd = GNUNET_malloc (sizeof (struct MhdHttpList));
2703     hd->is_ssl = GNUNET_YES;
2704     strcpy (hd->domain, domain);
2705     hd->proxy_cert = pgc;
2706
2707     /* Start new MHD */
2708     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2709                 "No previous SSL instance found... starting new one for %s\n",
2710                 domain);
2711 #if HAVE_MHD_NO_LISTEN_SOCKET
2712     hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_NO_LISTEN_SOCKET,
2713                                    0,
2714                                    &accept_cb, NULL,
2715                                    &create_response, hd,
2716                                    MHD_OPTION_CONNECTION_LIMIT,
2717                                    MHD_MAX_CONNECTIONS,
2718                                    MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2719                                    MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2720                                    MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2721                                    MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2722                                    MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2723                                    NULL,
2724                                    MHD_OPTION_END);
2725 #else
2726     hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL,
2727                                    4444 /* dummy port */,
2728                                    &accept_cb, NULL,
2729                                    &create_response, hd,
2730                                    MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
2731                                    MHD_OPTION_CONNECTION_LIMIT,
2732                                    MHD_MAX_CONNECTIONS,
2733                                    MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2734                                    MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2735                                    MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2736                                    MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2737                                    MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2738                                    NULL,
2739                                    MHD_OPTION_END);
2740 #endif
2741     GNUNET_assert (hd->daemon != NULL);
2742     hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2743     
2744     GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2745   }
2746
2747   nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
2748   nh->h = h;
2749
2750   GNUNET_CONTAINER_DLL_insert (hd->socket_handles_head,
2751                                hd->socket_handles_tail,
2752                                nh);
2753   
2754   return add_handle_to_mhd (h, hd->daemon);
2755 }
2756
2757
2758
2759 /**
2760  * Read data from incoming connection
2761  *
2762  * @param cls the closure
2763  * @param tc the scheduler context
2764  */
2765 static void
2766 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2767 {
2768   struct Socks5Request *s5r = cls;
2769   struct socks5_client_hello *c_hello;
2770   struct socks5_server_hello *s_hello;
2771   struct socks5_client_request *c_req;
2772   struct socks5_server_response *s_resp;
2773
2774   int ret;
2775   char domain[256];
2776   uint8_t dom_len;
2777   uint16_t req_port;
2778   struct hostent *phost;
2779   uint32_t remote_ip;
2780   struct sockaddr_in remote_addr;
2781   struct in_addr *r_sin_addr;
2782
2783   struct NetworkHandleList *nh;
2784
2785   s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
2786
2787   if ((NULL != tc->write_ready) &&
2788       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
2789       (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
2790                                          sizeof (s5r->rbuf))))
2791   {
2792     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2793                 "Successfully read %d bytes from socket\n",
2794                 s5r->rbuf_len);
2795   }
2796   else
2797   {
2798     if (s5r->rbuf_len != 0)
2799       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
2800     else
2801       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
2802
2803     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2804       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2805     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
2806       GNUNET_SCHEDULER_cancel (s5r->wtask);
2807     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2808       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2809     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2810     GNUNET_NETWORK_socket_close (s5r->sock);
2811     GNUNET_free(s5r);
2812     return;
2813   }
2814
2815   if (s5r->state == SOCKS5_INIT)
2816   {
2817     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2818                 "SOCKS5 init\n");
2819     c_hello = (struct socks5_client_hello*)&s5r->rbuf;
2820
2821     GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
2822
2823     s_hello = (struct socks5_server_hello*)&s5r->wbuf;
2824     s5r->wbuf_len = sizeof( struct socks5_server_hello );
2825
2826     s_hello->version = c_hello->version;
2827     s_hello->auth_method = SOCKS_AUTH_NONE;
2828
2829     /* Write response to client */
2830     s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2831                                                 s5r->sock,
2832                                                 &do_write, s5r);
2833
2834     s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2835                                                 s5r->sock,
2836                                                 &do_read, s5r);
2837
2838     s5r->state = SOCKS5_REQUEST;
2839     return;
2840   }
2841
2842   if (s5r->state == SOCKS5_REQUEST)
2843   {
2844     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2845                 "Processing SOCKS5 request\n");
2846     c_req = (struct socks5_client_request*)&s5r->rbuf;
2847     s_resp = (struct socks5_server_response*)&s5r->wbuf;
2848     //Only 10byte for ipv4 response!
2849     s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
2850
2851     GNUNET_assert (c_req->addr_type == 3);
2852
2853     dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
2854     memset(domain, 0, sizeof(domain));
2855     strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
2856     req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
2857
2858     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2859                 "Requested connection is %s:%d\n",
2860                 domain,
2861                 ntohs(req_port));
2862
2863     if (is_tld (domain, GNUNET_GNS_TLD) ||
2864         is_tld (domain, GNUNET_GNS_TLD_ZKEY))
2865     {
2866       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2867                   "Requested connection is gnunet tld\n",
2868                   domain);
2869       
2870       ret = MHD_NO;
2871       if (ntohs(req_port) == HTTPS_PORT)
2872       {
2873         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2874                     "Requested connection is HTTPS\n");
2875         ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
2876       }
2877       else if (NULL != httpd)
2878       {
2879         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2880                     "Requested connection is HTTP\n");
2881         nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
2882         nh->h = s5r->sock;
2883
2884         GNUNET_CONTAINER_DLL_insert (mhd_httpd_head->socket_handles_head,
2885                                mhd_httpd_head->socket_handles_tail,
2886                                nh);
2887
2888         ret = add_handle_to_mhd ( s5r->sock, httpd );
2889       }
2890
2891       if (ret != MHD_YES)
2892       {
2893         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2894                     _("Failed to start HTTP server\n"));
2895         s_resp->version = 0x05;
2896         s_resp->reply = 0x01;
2897         s5r->cleanup = GNUNET_YES;
2898         s5r->cleanup_sock = GNUNET_YES;
2899         s5r->wtask = 
2900           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2901                                         s5r->sock,
2902                                         &do_write, s5r);
2903         return;
2904       }
2905       
2906       /* Signal success */
2907       s_resp->version = 0x05;
2908       s_resp->reply = 0x00;
2909       s_resp->reserved = 0x00;
2910       s_resp->addr_type = 0x01;
2911       
2912       s5r->cleanup = GNUNET_YES;
2913       s5r->cleanup_sock = GNUNET_NO;
2914       s5r->wtask =
2915         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2916                                         s5r->sock,
2917                                         &do_write, s5r);
2918       run_httpds ();
2919       return;
2920     }
2921     else
2922     {
2923       phost = (struct hostent*)gethostbyname (domain);
2924       if (phost == NULL)
2925       {
2926         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2927                     "Resolve %s error!\n", domain );
2928         s_resp->version = 0x05;
2929         s_resp->reply = 0x01;
2930         s5r->cleanup = GNUNET_YES;
2931         s5r->cleanup_sock = GNUNET_YES;
2932         s5r->wtask = 
2933           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2934                                           s5r->sock,
2935                                           &do_write, s5r);
2936         return;
2937       }
2938
2939       s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
2940                                                        SOCK_STREAM,
2941                                                        0);
2942       r_sin_addr = (struct in_addr*)(phost->h_addr);
2943       remote_ip = r_sin_addr->s_addr;
2944       memset(&remote_addr, 0, sizeof(remote_addr));
2945       remote_addr.sin_family = AF_INET;
2946 #if HAVE_SOCKADDR_IN_SIN_LEN
2947       remote_addr.sin_len = sizeof (remote_addr);
2948 #endif
2949       remote_addr.sin_addr.s_addr = remote_ip;
2950       remote_addr.sin_port = req_port;
2951       
2952       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2953                   "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
2954                   ntohs(req_port));
2955
2956       if ((GNUNET_OK !=
2957           GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
2958                                           (const struct sockaddr*)&remote_addr,
2959                                           sizeof (remote_addr)))
2960           && (errno != EINPROGRESS))
2961       {
2962         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
2963         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2964                     "socket request error...\n");
2965         s_resp->version = 0x05;
2966         s_resp->reply = 0x01;
2967         s5r->wtask =
2968           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2969                                           s5r->sock,
2970                                           &do_write, s5r);
2971         //TODO see above
2972         return;
2973       }
2974
2975       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2976                   "new remote connection\n");
2977
2978       s_resp->version = 0x05;
2979       s_resp->reply = 0x00;
2980       s_resp->reserved = 0x00;
2981       s_resp->addr_type = 0x01;
2982
2983       s5r->state = SOCKS5_DATA_TRANSFER;
2984
2985       s5r->wtask =
2986         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2987                                         s5r->sock,
2988                                         &do_write, s5r);
2989       s5r->rtask =
2990         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2991                                        s5r->sock,
2992                                        &do_read, s5r);
2993
2994     }
2995     return;
2996   }
2997
2998   if (s5r->state == SOCKS5_DATA_TRANSFER)
2999   {
3000     if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
3001     {
3002       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3003                   "Closing connection to client\n");
3004       if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
3005         GNUNET_SCHEDULER_cancel (s5r->rtask);
3006       if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
3007         GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
3008       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
3009         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
3010       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
3011         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
3012       
3013       if (s5r->remote_sock != NULL)
3014         GNUNET_NETWORK_socket_close (s5r->remote_sock);
3015       GNUNET_NETWORK_socket_close (s5r->sock);
3016       GNUNET_free(s5r);
3017       return;
3018     }
3019
3020     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3021                 "forwarding %d bytes from client\n", s5r->rbuf_len);
3022
3023     s5r->fwdwtask =
3024       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
3025                                       s5r->remote_sock,
3026                                       &do_write_remote, s5r);
3027
3028     if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
3029     {
3030       s5r->fwdrtask =
3031         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3032                                        s5r->remote_sock,
3033                                        &do_read_remote, s5r);
3034     }
3035
3036
3037   }
3038
3039 }
3040
3041
3042 /**
3043  * Accept new incoming connections
3044  *
3045  * @param cls the closure
3046  * @param tc the scheduler context
3047  */
3048 static void
3049 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3050 {
3051   struct GNUNET_NETWORK_Handle *s;
3052   struct Socks5Request *s5r;
3053
3054   ltask = GNUNET_SCHEDULER_NO_TASK;
3055   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
3056     return;
3057
3058   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3059                                          lsock,
3060                                          &do_accept, NULL);
3061
3062   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
3063
3064   if (NULL == s)
3065   {
3066     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
3067     return;
3068   }
3069
3070   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3071               "Got an inbound connection, waiting for data\n");
3072
3073   s5r = GNUNET_malloc (sizeof (struct Socks5Request));
3074   s5r->sock = s;
3075   s5r->state = SOCKS5_INIT;
3076   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
3077   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
3078   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
3079   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3080                                               s5r->sock,
3081                                               &do_read, s5r);
3082 }
3083
3084
3085 /**
3086  * Task run on shutdown
3087  *
3088  * @param cls closure
3089  * @param tc task context
3090  */
3091 static void
3092 do_shutdown (void *cls,
3093              const struct GNUNET_SCHEDULER_TaskContext *tc)
3094 {
3095
3096   struct MhdHttpList *hd;
3097   struct MhdHttpList *tmp_hd;
3098   struct NetworkHandleList *nh;
3099   struct NetworkHandleList *tmp_nh;
3100   struct ProxyCurlTask *ctask;
3101   struct ProxyCurlTask *ctask_tmp;
3102   struct ProxyUploadData *pdata;
3103   
3104   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3105               "Shutting down...\n");
3106
3107   gnutls_global_deinit ();
3108  
3109   if (NULL != local_gns_zone)
3110     GNUNET_free (local_gns_zone); 
3111   if (NULL != local_private_zone)
3112     GNUNET_free (local_private_zone);
3113   if (NULL != local_shorten_zone)
3114     GNUNET_free (local_shorten_zone);
3115
3116   if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
3117   {
3118     GNUNET_SCHEDULER_cancel (curl_download_task);
3119     curl_download_task = GNUNET_SCHEDULER_NO_TASK;
3120   }
3121
3122   for (hd = mhd_httpd_head; hd != NULL; hd = tmp_hd)
3123   {
3124     tmp_hd = hd->next;
3125
3126     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3127                 "Stopping daemon\n");
3128
3129     if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
3130     {
3131       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3132                   "Stopping select task %d\n",
3133                   hd->httpd_task);
3134       GNUNET_SCHEDULER_cancel (hd->httpd_task);
3135       hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
3136     }
3137
3138     if (NULL != hd->daemon)
3139     {
3140       MHD_stop_daemon (hd->daemon);
3141       hd->daemon = NULL;
3142     }
3143
3144     for (nh = hd->socket_handles_head; nh != NULL; nh = tmp_nh)
3145     {
3146       tmp_nh = nh->next;
3147
3148       GNUNET_NETWORK_socket_close (nh->h);
3149
3150       GNUNET_free (nh);
3151     }
3152
3153     if (NULL != hd->proxy_cert)
3154     {
3155       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3156                   "Free certificate\n");
3157       GNUNET_free (hd->proxy_cert);
3158     }
3159
3160     GNUNET_free (hd);
3161   }
3162
3163   for (ctask=ctasks_head; ctask != NULL; ctask=ctask_tmp)
3164   {
3165     ctask_tmp = ctask->next;
3166
3167     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3168                 "Cleaning up cURL task\n");
3169
3170     if (ctask->curl != NULL)
3171       curl_easy_cleanup (ctask->curl);
3172     ctask->curl = NULL;
3173     if (NULL != ctask->headers)
3174       curl_slist_free_all (ctask->headers);
3175     if (NULL != ctask->resolver)
3176       curl_slist_free_all (ctask->resolver);
3177
3178     if (NULL != ctask->response)
3179       MHD_destroy_response (ctask->response);
3180
3181     pdata = ctask->upload_data_head;
3182
3183     //FIXME free pdata here
3184     for (; pdata != NULL; pdata = ctask->upload_data_head)
3185     {
3186       GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
3187                                    ctask->upload_data_tail,
3188                                    pdata);
3189       GNUNET_free_non_null (pdata->filename);
3190       GNUNET_free_non_null (pdata->content_type);
3191       GNUNET_free_non_null (pdata->key);
3192       GNUNET_free_non_null (pdata->value);
3193       GNUNET_free (pdata);
3194     }
3195
3196     GNUNET_free (ctask);
3197   }
3198   curl_multi_cleanup (curl_multi);
3199
3200   GNUNET_GNS_disconnect (gns_handle);
3201 }
3202
3203
3204 /**
3205  * Compiles a regex for us
3206  *
3207  * @param re ptr to re struct
3208  * @param rt the expression to compile
3209  * @return 0 on success
3210  */
3211 static int
3212 compile_regex (regex_t *re, const char* rt)
3213 {
3214   int status;
3215   char err[1024];
3216
3217   status = regcomp (re, rt, REG_EXTENDED|REG_NEWLINE);
3218   if (status)
3219   {
3220     regerror (status, re, err, 1024);
3221     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3222                 "Regex error compiling '%s': %s\n", rt, err);
3223     return 1;
3224   }
3225   return 0;
3226 }
3227
3228
3229 /**
3230  * Loads the users local zone key
3231  *
3232  * @return GNUNET_YES on success
3233  */
3234 static int
3235 load_local_zone_key (const struct GNUNET_CONFIGURATION_Handle *cfg)
3236 {
3237   char *keyfile;
3238   struct GNUNET_CRYPTO_RsaPrivateKey *key = NULL;
3239   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
3240   struct GNUNET_CRYPTO_ShortHashCode *zone = NULL;
3241   struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename;
3242
3243   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3244                                                             "ZONEKEY", &keyfile))
3245   {
3246     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3247                 "Unable to load zone key config value!\n");
3248     return GNUNET_NO;
3249   }
3250
3251   if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
3252   {
3253     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3254                 "Unable to load zone key %s!\n", keyfile);
3255     GNUNET_free(keyfile);
3256     return GNUNET_NO;
3257   }
3258
3259   key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
3260   GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
3261   local_gns_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3262   GNUNET_CRYPTO_short_hash(&pkey,
3263                            sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
3264                            local_gns_zone);
3265   zone = local_gns_zone;
3266   GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3267   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3268               "Using zone: %s!\n", &zonename);
3269   GNUNET_CRYPTO_rsa_key_free(key);
3270   GNUNET_free(keyfile);
3271   keyfile = NULL;
3272
3273   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3274                                                    "PRIVATE_ZONEKEY", &keyfile))
3275   {
3276     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3277                 "Unable to load private zone key config value!\n");
3278   }
3279
3280   if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile)))
3281   {
3282     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3283                 "Unable to load private zone key %s!\n", keyfile);
3284     GNUNET_free(keyfile);
3285   }
3286   else
3287   {
3288     key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
3289     GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
3290     local_private_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3291     GNUNET_CRYPTO_short_hash(&pkey,
3292                              sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
3293                              local_private_zone);
3294     GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3295     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3296                 "Using private zone: %s!\n", &zonename);
3297     GNUNET_CRYPTO_rsa_key_free(key);
3298     GNUNET_free(keyfile);
3299   }
3300   keyfile = NULL;
3301
3302   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3303                                                    "SHORTEN_ZONEKEY", &keyfile))
3304   {
3305     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3306                 "Unable to load shorten zone key config value!\n");
3307   }
3308
3309   if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile)))
3310   {
3311     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3312                 "Unable to load shorten zone key %s!\n", keyfile);
3313     GNUNET_free(keyfile);
3314   }
3315   else
3316   {
3317     key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
3318     GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
3319     local_shorten_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3320     GNUNET_CRYPTO_short_hash(&pkey,
3321                              sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
3322                              local_shorten_zone);
3323     GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3324     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3325                 "Using shorten zone: %s!\n", &zonename);
3326     GNUNET_CRYPTO_rsa_key_free(key);
3327     GNUNET_free(keyfile);
3328   }
3329
3330   return GNUNET_YES;
3331 }
3332
3333 /**
3334  * Main function that will be run
3335  *
3336  * @param cls closure
3337  * @param args remaining command-line arguments
3338  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3339  * @param cfg configuration
3340  */
3341 static void
3342 run (void *cls, char *const *args, const char *cfgfile,
3343      const struct GNUNET_CONFIGURATION_Handle *cfg)
3344 {
3345   struct sockaddr_in sa;
3346   struct MhdHttpList *hd;
3347   char* cafile_cfg = NULL;
3348   char* cafile;
3349 #if !HAVE_MHD_NO_LISTEN_SOCKET
3350   size_t len;
3351   char* proxy_sockfile;
3352   struct sockaddr_un mhd_unix_sock_addr;
3353 #endif
3354
3355   curl_multi = curl_multi_init ();
3356
3357   if (NULL == curl_multi)
3358   {
3359     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3360                 "Failed to create cURL multo handle!\n");
3361     return;
3362   }
3363   
3364   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3365               "Loading CA\n");
3366   cafile = cafile_opt;
3367   if (NULL == cafile)
3368   {
3369     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3370                                                           "PROXY_CACERT",
3371                                                           &cafile_cfg))
3372     {
3373       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3374                   "Unable to load proxy CA config value!\n");
3375       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3376                   "No proxy CA provided!\n");
3377       return;
3378     }
3379     cafile = cafile_cfg;
3380   }
3381   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3382               "Using %s as CA\n", cafile);
3383   
3384   gnutls_global_init ();
3385   gnutls_x509_crt_init (&proxy_ca.cert);
3386   gnutls_x509_privkey_init (&proxy_ca.key);
3387   
3388   if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
3389        (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
3390   {
3391     // FIXME: release resources...
3392     return;
3393   }
3394
3395   GNUNET_free_non_null (cafile_cfg);
3396   
3397   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3398               "Loading Template\n");
3399   
3400   compile_regex (&re_dotplus, (char*) RE_A_HREF);
3401
3402   gns_handle = GNUNET_GNS_connect (cfg);
3403
3404   if (GNUNET_NO == load_local_zone_key (cfg))
3405   {
3406     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3407                 "Unable to load zone!\n");
3408     return;
3409   }
3410
3411   if (NULL == gns_handle)
3412   {
3413     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3414                 "Unable to connect to GNS!\n");
3415     return;
3416   }
3417
3418   memset (&sa, 0, sizeof (sa));
3419   sa.sin_family = AF_INET;
3420   sa.sin_port = htons (port);
3421 #if HAVE_SOCKADDR_IN_SIN_LEN
3422   sa.sin_len = sizeof (sa);
3423 #endif
3424
3425   lsock = GNUNET_NETWORK_socket_create (AF_INET,
3426                                         SOCK_STREAM,
3427                                         0);
3428
3429   if ((NULL == lsock) ||
3430       (GNUNET_OK !=
3431        GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
3432                                    sizeof (sa))))
3433   {
3434     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3435                 "Failed to create listen socket bound to `%s'",
3436                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
3437     if (NULL != lsock)
3438       GNUNET_NETWORK_socket_close (lsock);
3439     return;
3440   }
3441
3442   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
3443   {
3444     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3445                 "Failed to listen on socket bound to `%s'",
3446                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
3447     return;
3448   }
3449
3450   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3451                                          lsock, &do_accept, NULL);
3452
3453   ctasks_head = NULL;
3454   ctasks_tail = NULL;
3455
3456   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
3457   {
3458     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3459                 "cURL global init failed!\n");
3460     return;
3461   }
3462
3463   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3464               "Proxy listens on port %u\n",
3465               port);
3466
3467   mhd_httpd_head = NULL;
3468   mhd_httpd_tail = NULL;
3469   total_mhd_connections = 0;
3470 #if ! HAVE_MHD_NO_LISTEN_SOCKET
3471   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3472                                                             "PROXY_UNIXPATH",
3473                                                             &proxy_sockfile))
3474   {
3475     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3476                 "Specify PROXY_UNIXPATH in gns-proxy config section!\n");
3477     return;
3478   }
3479
3480   mhd_unix_socket = GNUNET_NETWORK_socket_create (AF_UNIX,
3481                                                 SOCK_STREAM,
3482                                                 0);
3483
3484   if (NULL == mhd_unix_socket)
3485   {
3486     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3487                 "Unable to create unix domain socket!\n");
3488     return;
3489   }
3490
3491   mhd_unix_sock_addr.sun_family = AF_UNIX;
3492   strcpy (mhd_unix_sock_addr.sun_path, proxy_sockfile);
3493
3494 #if LINUX
3495   mhd_unix_sock_addr.sun_path[0] = '\0';
3496 #endif
3497 #if HAVE_SOCKADDR_IN_SIN_LEN
3498   mhd_unix_sock_addr.sun_len = (u_char) sizeof (struct sockaddr_un);
3499 #endif
3500
3501   len = strlen (proxy_sockfile) + sizeof(AF_UNIX);
3502
3503   GNUNET_free (proxy_sockfile);
3504
3505   if (GNUNET_OK != GNUNET_NETWORK_socket_bind (mhd_unix_socket,
3506                                (struct sockaddr*)&mhd_unix_sock_addr,
3507                                len))
3508   {
3509     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3510                 "Unable to bind unix domain socket!\n");
3511     return;
3512   }
3513
3514   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (mhd_unix_socket,
3515                                                  1))
3516   {
3517     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3518                 "Unable to listen on unix domain socket!\n");
3519     return;
3520   }
3521 #endif
3522
3523   hd = GNUNET_malloc (sizeof (struct MhdHttpList));
3524   hd->is_ssl = GNUNET_NO;
3525   strcpy (hd->domain, "");
3526
3527 #if HAVE_MHD_NO_LISTEN_SOCKET
3528   httpd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
3529                             0,
3530                             &accept_cb, NULL,
3531                             &create_response, hd,
3532                             MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
3533                             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3534                             MHD_OPTION_NOTIFY_COMPLETED,
3535                             NULL, NULL,
3536                             MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3537                             MHD_OPTION_END);
3538 #else
3539   httpd = MHD_start_daemon (MHD_USE_DEBUG,
3540                             4444 /* Dummy port */,
3541                             &accept_cb, NULL,
3542                             &create_response, hd,
3543                             MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
3544                             MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
3545                             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3546                             MHD_OPTION_NOTIFY_COMPLETED,
3547                             NULL, NULL,
3548                             MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3549                             MHD_OPTION_END);
3550 #endif
3551
3552   GNUNET_assert (httpd != NULL);
3553   hd->daemon = httpd;
3554   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
3555
3556   GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
3557
3558   run_httpds ();
3559
3560   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
3561                                 &do_shutdown, NULL);
3562
3563 }
3564
3565
3566 /**
3567  * The main function for gnunet-gns-proxy.
3568  *
3569  * @param argc number of arguments from the command line
3570  * @param argv command line arguments
3571  * @return 0 ok, 1 on error
3572  */
3573 int
3574 main (int argc, char *const *argv)
3575 {
3576   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
3577     {'p', "port", NULL,
3578      gettext_noop ("listen on specified port (default: 7777)"), 1,
3579      &GNUNET_GETOPT_set_ulong, &port},
3580     {'a', "authority", NULL,
3581       gettext_noop ("pem file to use as CA"), 1,
3582       &GNUNET_GETOPT_set_string, &cafile_opt},
3583     GNUNET_GETOPT_OPTION_END
3584   };
3585
3586   int ret;
3587
3588   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
3589     return 2;
3590
3591
3592   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
3593   ret =
3594       (GNUNET_OK ==
3595        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
3596                            _("GNUnet GNS proxy"),
3597                            options,
3598                            &run, NULL)) ? 0 : 1;
3599   GNUNET_free_non_null ((char*)argv);
3600
3601   return ret;
3602 }
3603
3604
3605
3606