-licenses, dce, etc
[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
1194   if (0 > bytes_to_copy)
1195     bytes_to_copy = 0;
1196   
1197   memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
1198   ctask->buffer_read_ptr += bytes_to_copy;
1199   copied += bytes_to_copy;
1200   ctask->buf_status = BUF_WAIT_FOR_CURL;
1201   
1202   if (NULL != ctask->curl)
1203     curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
1204
1205   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1206               "MHD: copied %d bytes\n", (int) copied);
1207   run_mhd_now (ctask->mhd);
1208   return copied;
1209 }
1210
1211
1212 /**
1213  * Shorten result callback
1214  *
1215  * @param cls the proxycurltask
1216  * @param short_name the shortened name (NULL on error)
1217  */
1218 static void
1219 process_shorten (void* cls, const char* short_name)
1220 {
1221   struct ProxyREMatch *re_match = cls;
1222   char result[sizeof (re_match->result)];
1223   
1224   if (NULL == short_name)
1225   {
1226     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1227                 "PP: Unable to shorten %s\n",
1228                 re_match->hostname);
1229     GNUNET_CONTAINER_DLL_remove (re_match->ctask->pp_match_head,
1230                                  re_match->ctask->pp_match_tail,
1231                                  re_match);
1232     GNUNET_free (re_match);
1233     return;
1234   }
1235
1236   if (0 == strcmp (short_name, re_match->ctask->leho))
1237     strcpy (result, re_match->ctask->host);
1238   else
1239     strcpy (result, short_name);
1240
1241   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1242               "PP: Shorten %s -> %s\n",
1243               re_match->hostname,
1244               result);
1245   
1246   if (re_match->is_ssl)
1247     sprintf (re_match->result, "href=\"https://%s", result);
1248   else
1249     sprintf (re_match->result, "href=\"http://%s", result);
1250
1251   re_match->done = GNUNET_YES;
1252   run_mhd_now (re_match->ctask->mhd);
1253 }
1254
1255
1256 /**
1257  * Postprocess data in buffer. From read ptr to write ptr
1258  *
1259  * @param cls the curlproxytask
1260  * @param tc task context
1261  */
1262 static void
1263 postprocess_buffer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1264 {
1265   struct ProxyCurlTask *ctask = cls;
1266   struct ProxyREMatch *re_match;
1267   char* re_ptr = ctask->buffer_read_ptr;
1268   char re_hostname[255];
1269   regmatch_t m[RE_N_MATCHES];
1270
1271   ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
1272
1273   if (GNUNET_YES != ctask->parse_content)
1274   {
1275     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1276                 "PP: Not parsing content\n");
1277     ctask->buf_status = BUF_WAIT_FOR_MHD;
1278     run_mhd_now (ctask->mhd);
1279     return;
1280   }
1281
1282   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1283               "PP: We need to parse the HTML\n");
1284
1285   /* 0 means match found */
1286   while (0 == regexec (&re_dotplus, re_ptr, RE_N_MATCHES, m, 0))
1287   {
1288     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1289                 "PP: regex match\n");
1290
1291     GNUNET_assert (m[1].rm_so != -1);
1292
1293     memset (re_hostname, 0, sizeof (re_hostname));
1294     memcpy (re_hostname, re_ptr+m[1].rm_so, (m[3].rm_eo-m[1].rm_so));
1295     
1296
1297     re_match = GNUNET_malloc (sizeof (struct ProxyREMatch));
1298     re_match->start = re_ptr + m[0].rm_so;
1299     re_match->end = re_ptr + m[3].rm_eo;
1300     re_match->done = GNUNET_NO;
1301     re_match->ctask = ctask;
1302     
1303     if ('s' == *(re_ptr+m[1].rm_so-strlen("://")-1)) //FIXME strcmp
1304       re_match->is_ssl = GNUNET_YES;
1305     else
1306       re_match->is_ssl = GNUNET_NO;
1307       
1308     strcpy (re_match->hostname, re_hostname);
1309     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1310                 "PP: Got hostname %s\n", re_hostname);
1311     re_ptr += m[3].rm_eo;
1312
1313     if (GNUNET_YES == is_tld (re_match->hostname, GNUNET_GNS_TLD_PLUS))
1314     {
1315       re_match->hostname[strlen(re_match->hostname)-1] = '\0';
1316       strcpy (re_match->hostname+strlen(re_match->hostname),
1317               ctask->authority);
1318     }
1319
1320     re_match->shorten_task = GNUNET_GNS_shorten_zone (gns_handle,
1321                              re_match->hostname,
1322                              local_private_zone,
1323                              local_shorten_zone,
1324                              local_gns_zone,
1325                              &process_shorten,
1326                              re_match); //FIXME cancel appropriately
1327
1328     GNUNET_CONTAINER_DLL_insert_tail (ctask->pp_match_head,
1329                                       ctask->pp_match_tail,
1330                                       re_match);
1331   }
1332   
1333   ctask->buf_status = BUF_WAIT_FOR_MHD;
1334   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1335               "PP: No more matches\n");
1336   run_mhd_now (ctask->mhd);
1337 }
1338
1339 /**
1340  * Handle data from cURL
1341  *
1342  * @param ptr pointer to the data
1343  * @param size number of blocks of data
1344  * @param nmemb blocksize
1345  * @param ctx the curlproxytask
1346  * @return number of bytes handled
1347  */
1348 static size_t
1349 curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx)
1350 {
1351   const char *cbuf = ptr;
1352   size_t total = size * nmemb;
1353   struct ProxyCurlTask *ctask = ctx;
1354   size_t buf_space = sizeof (ctask->buffer) -
1355     (ctask->buffer_write_ptr-ctask->buffer);
1356
1357   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1358               "CURL: Got %d. %d free in buffer\n",
1359               total, buf_space);
1360
1361   if (BUF_WAIT_FOR_CURL != ctask->buf_status)
1362     return CURL_WRITEFUNC_PAUSE;
1363
1364   if (total > (buf_space - CURL_BUF_PADDING))
1365   {
1366     if (ctask->buf_status == BUF_WAIT_FOR_CURL)
1367     {
1368       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1369                   "CURL: Buffer full starting postprocessing\n");
1370       ctask->buf_status = BUF_WAIT_FOR_PP;
1371       ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer,
1372                                                  ctask);
1373       return CURL_WRITEFUNC_PAUSE;
1374     }
1375
1376     /* we should not get called in that case */
1377     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1378                 "CURL: called out of context and no space in buffer!\n");
1379     return CURL_WRITEFUNC_PAUSE;
1380   }
1381
1382   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1383               "CURL: Copying %d bytes to buffer (%s)\n", total, ctask->url);
1384   memcpy (ctask->buffer_write_ptr, cbuf, total);
1385   ctask->bytes_in_buffer += total;
1386   ctask->buffer_write_ptr += total;
1387   ctask->buffer_write_ptr[0] = '\0';
1388
1389   return total;
1390 }
1391
1392
1393 /**
1394  * cURL callback for put data
1395  */
1396 static size_t
1397 put_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1398 {
1399   struct ProxyCurlTask *ctask = cls;
1400   struct ProxyUploadData *pdata = ctask->upload_data_head;
1401   size_t len = size * nmemb;
1402   size_t to_copy;
1403   char* pos;
1404
1405   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1406               "CURL: put read callback\n");
1407
1408   if (NULL == pdata)
1409     return CURL_READFUNC_PAUSE;
1410   
1411   //fin
1412   if (NULL == pdata->value)
1413   {
1414     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1415                 "CURL: Terminating PUT\n");
1416
1417     GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1418                                  ctask->upload_data_tail,
1419                                  pdata);
1420     GNUNET_free (pdata);
1421     return 0;
1422   }
1423  
1424   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1425               "CURL: read callback value %s\n", pdata->value); 
1426   
1427   to_copy = pdata->bytes_left;
1428   if (to_copy > len)
1429     to_copy = len;
1430   
1431   pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1432   memcpy (buf, pos, to_copy);
1433   pdata->bytes_left -= to_copy;
1434   if (pdata->bytes_left <= 0)
1435   {
1436     GNUNET_free (pdata->value);
1437     GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1438                                  ctask->upload_data_tail,
1439                                  pdata);
1440     GNUNET_free (pdata);
1441   }
1442   return to_copy;
1443 }
1444
1445
1446 /**
1447  * cURL callback for post data
1448  */
1449 static size_t
1450 post_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1451 {
1452   struct ProxyCurlTask *ctask = cls;
1453   struct ProxyUploadData *pdata = ctask->upload_data_head;
1454   size_t len = size * nmemb;
1455   size_t to_copy;
1456   char* pos;
1457
1458   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1459               "CURL: read callback\n");
1460
1461   if (NULL == pdata)
1462     return CURL_READFUNC_PAUSE;
1463   
1464   //fin
1465   if (NULL == pdata->value)
1466   {
1467     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1468                 "CURL: Terminating POST data\n");
1469
1470     GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1471                                  ctask->upload_data_tail,
1472                                  pdata);
1473     GNUNET_free (pdata);
1474     return 0;
1475   }
1476  
1477   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1478               "CURL: read callback value %s\n", pdata->value); 
1479   
1480   to_copy = pdata->bytes_left;
1481   if (to_copy > len)
1482     to_copy = len;
1483   
1484   pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1485   memcpy (buf, pos, to_copy);
1486   pdata->bytes_left -= to_copy;
1487   if (pdata->bytes_left <= 0)
1488   {
1489     GNUNET_free (pdata->value);
1490     GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1491                                  ctask->upload_data_tail,
1492                                  pdata);
1493     GNUNET_free (pdata);
1494   }
1495   return to_copy;
1496 }
1497
1498 /**
1499  * Task that is run when we are ready to receive more data
1500  * from curl
1501  *
1502  * @param cls closure
1503  * @param tc task context
1504  */
1505 static void
1506 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1507
1508 /**
1509  * Ask cURL for the select sets and schedule download
1510  */
1511 static void
1512 curl_download_prepare ()
1513 {
1514   CURLMcode mret;
1515   fd_set rs;
1516   fd_set ws;
1517   fd_set es;
1518   int max;
1519   struct GNUNET_NETWORK_FDSet *grs;
1520   struct GNUNET_NETWORK_FDSet *gws;
1521   long to;
1522   struct GNUNET_TIME_Relative rtime;
1523
1524   max = -1;
1525   FD_ZERO (&rs);
1526   FD_ZERO (&ws);
1527   FD_ZERO (&es);
1528   mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
1529
1530   if (mret != CURLM_OK)
1531   {
1532     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1533                 "%s failed at %s:%d: `%s'\n",
1534                 "curl_multi_fdset", __FILE__, __LINE__,
1535                 curl_multi_strerror (mret));
1536     //TODO cleanup here?
1537     return;
1538   }
1539
1540   mret = curl_multi_timeout (curl_multi, &to);
1541   rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
1542
1543   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1544               "cURL multi fds: max=%d timeout=%llu\n", max, to);
1545
1546   grs = GNUNET_NETWORK_fdset_create ();
1547   gws = GNUNET_NETWORK_fdset_create ();
1548   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1549   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1550   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1551               "Scheduling task cURL\n");
1552
1553   if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
1554     GNUNET_SCHEDULER_cancel (curl_download_task);
1555   
1556   if (-1 != max)
1557   {
1558     curl_download_task =
1559       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1560                                    rtime,
1561                                    grs, gws,
1562                                    &curl_task_download, curl_multi);
1563   }
1564   else if (NULL != ctasks_head)
1565   {
1566     /* as specified in curl docs */
1567     curl_download_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
1568                                                        &curl_task_download,
1569                                                        curl_multi);
1570   }
1571   GNUNET_NETWORK_fdset_destroy (gws);
1572   GNUNET_NETWORK_fdset_destroy (grs);
1573 }
1574
1575
1576 /**
1577  * Task that is run when we are ready to receive more data
1578  * from curl
1579  *
1580  * @param cls closure
1581  * @param tc task context
1582  */
1583 static void
1584 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1585 {
1586   int running;
1587   int msgnum;
1588   struct CURLMsg *msg;
1589   CURLMcode mret;
1590   struct ProxyCurlTask *ctask;
1591   int num_ctasks;
1592   long resp_code;
1593
1594   struct ProxyCurlTask *clean_head = NULL;
1595   struct ProxyCurlTask *clean_tail = NULL;
1596
1597   curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1598
1599   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1600   {
1601     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1602                 "Shutdown requested while trying to download\n");
1603   //TODO cleanup
1604   return;
1605   }
1606   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1607               "Ready to dl\n");
1608
1609   do
1610   {
1611     running = 0;
1612     num_ctasks = 0;
1613     
1614     mret = curl_multi_perform (curl_multi, &running);
1615
1616     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1617                 "Running curl tasks: %d\n", running);
1618
1619     for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1620     {
1621       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1622                   "CTask: %s\n", ctask->url);
1623       num_ctasks++;
1624     }
1625
1626     if (num_ctasks != running)
1627     {
1628       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1629                   "%d ctasks, %d curl running\n", num_ctasks, running);
1630     }
1631     
1632     do
1633     {
1634       
1635       msg = curl_multi_info_read (curl_multi, &msgnum);
1636       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1637                   "Messages left: %d\n", msgnum);
1638       
1639       if (msg == NULL)
1640         break;
1641       switch (msg->msg)
1642       {
1643        case CURLMSG_DONE:
1644          if ((msg->data.result != CURLE_OK) &&
1645              (msg->data.result != CURLE_GOT_NOTHING))
1646          {
1647            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1648                        "Download curl failed");
1649             
1650            for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1651            {
1652              if (NULL == ctask->curl)
1653                continue;
1654
1655              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
1656                continue;
1657              
1658              GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1659                          "CURL: Download failed for task %s: %s.\n",
1660                          ctask->url,
1661                          curl_easy_strerror (msg->data.result));
1662              ctask->download_is_finished = GNUNET_YES;
1663              ctask->download_error = GNUNET_YES;
1664              if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1665                                                 CURLINFO_RESPONSE_CODE,
1666                                                 &resp_code))
1667                ctask->curl_response_code = resp_code;
1668              ctask->ready_to_queue = MHD_YES;
1669              ctask->buf_status = BUF_WAIT_FOR_MHD;
1670              run_mhd_now (ctask->mhd);
1671              
1672              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1673                                           ctask);
1674              GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1675              break;
1676            }
1677            GNUNET_assert (ctask != NULL);
1678          }
1679          else
1680          {
1681            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1682                        "CURL: download completed.\n");
1683
1684            for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1685            {
1686              if (NULL == ctask->curl)
1687                continue;
1688
1689              if (0 != memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)))
1690                continue;
1691              
1692              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1693                          "CURL: completed task %s found.\n", ctask->url);
1694              if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1695                                                 CURLINFO_RESPONSE_CODE,
1696                                                 &resp_code))
1697                ctask->curl_response_code = resp_code;
1698
1699
1700              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1701                          "CURL: Completed ctask!\n");
1702              if (GNUNET_SCHEDULER_NO_TASK == ctask->pp_task)
1703              {
1704               ctask->buf_status = BUF_WAIT_FOR_PP;
1705               ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer,
1706                                                           ctask);
1707              }
1708
1709              ctask->ready_to_queue = MHD_YES;
1710              ctask->download_is_finished = GNUNET_YES;
1711
1712              /* We MUST not modify the multi handle else we loose messages */
1713              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1714                                           ctask);
1715              GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1716
1717              break;
1718            }
1719            GNUNET_assert (ctask != NULL);
1720          }
1721          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1722                      "CURL: %s\n", curl_easy_strerror(msg->data.result));
1723          break;
1724        default:
1725          GNUNET_assert (0);
1726          break;
1727       }
1728     } while (msgnum > 0);
1729
1730     for (ctask=clean_head; NULL != ctask; ctask = ctask->next)
1731     {
1732       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1733                   "CURL: Removing task %s.\n", ctask->url);
1734       curl_multi_remove_handle (curl_multi, ctask->curl);
1735       curl_easy_cleanup (ctask->curl);
1736       ctask->curl = NULL;
1737     }
1738     
1739     num_ctasks=0;
1740     for (ctask=ctasks_head; NULL != ctask; ctask = ctask->next)    
1741       num_ctasks++; 
1742     
1743     if (num_ctasks != running)
1744     {
1745       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1746                   "CURL: %d tasks, %d running\n", num_ctasks, running);
1747     }
1748
1749     GNUNET_assert ( num_ctasks == running );
1750
1751   } while (mret == CURLM_CALL_MULTI_PERFORM);
1752   
1753   if (mret != CURLM_OK)
1754   {
1755     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CURL: %s failed at %s:%d: `%s'\n",
1756                 "curl_multi_perform", __FILE__, __LINE__,
1757                 curl_multi_strerror (mret));
1758   }
1759   curl_download_prepare();
1760 }
1761
1762
1763 /**
1764  * Process LEHO lookup
1765  *
1766  * @param cls the ctask
1767  * @param rd_count number of records returned
1768  * @param rd record data
1769  */
1770 static void
1771 process_leho_lookup (void *cls,
1772                      uint32_t rd_count,
1773                      const struct GNUNET_NAMESTORE_RecordData *rd)
1774 {
1775   struct ProxyCurlTask *ctask = cls;
1776   char hosthdr[262]; //256 + "Host: "
1777   int i;
1778   CURLcode ret;
1779   CURLMcode mret;
1780   struct hostent *phost;
1781   char *ssl_ip;
1782   char resolvename[512];
1783   char curlurl[512];
1784
1785   strcpy (ctask->leho, "");
1786
1787   if (rd_count == 0)
1788     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1789                 "No LEHO present!\n");
1790
1791   for (i=0; i<rd_count; i++)
1792   {
1793     if (rd[i].record_type != GNUNET_GNS_RECORD_LEHO)
1794       continue;
1795
1796     memcpy (ctask->leho, rd[i].data, rd[i].data_size);
1797
1798     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1799                 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
1800   }
1801
1802   if (0 != strcmp (ctask->leho, ""))
1803   {
1804     sprintf (hosthdr, "%s%s:%d", "Host: ", ctask->leho, ctask->port);
1805     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1806                 "New HTTP header value: %s\n", hosthdr);
1807     ctask->headers = curl_slist_append (ctask->headers, hosthdr);
1808     GNUNET_assert (NULL != ctask->headers);
1809     ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers);
1810     if (CURLE_OK != ret)
1811     {
1812       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s failed at %s:%d: `%s'\n",
1813                            "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret));
1814     }
1815
1816   }
1817
1818   if (ctask->mhd->is_ssl)
1819   {
1820     phost = (struct hostent*)gethostbyname (ctask->host);
1821
1822     if (phost!=NULL)
1823     {
1824       ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
1825       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1826                   "SSL target server: %s\n", ssl_ip);
1827       sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
1828       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1829                   "Curl resolve: %s\n", resolvename);
1830       ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
1831       curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver);
1832       sprintf (curlurl, "https://%s:%d%s", ctask->leho, ctask->port, ctask->url);
1833       curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1834     }
1835     else
1836     {
1837       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1838                   "gethostbyname failed for %s!\n", ctask->host);
1839       ctask->download_is_finished = GNUNET_YES;
1840       ctask->download_error = GNUNET_YES;
1841       return;
1842     }
1843   }
1844
1845   if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
1846   {
1847     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1848                 "%s failed at %s:%d: `%s'\n",
1849                 "curl_multi_add_handle", __FILE__, __LINE__,
1850                 curl_multi_strerror (mret));
1851     ctask->download_is_finished = GNUNET_YES;
1852     ctask->download_error = GNUNET_YES;
1853     return;
1854   }
1855   GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1856
1857   curl_download_prepare ();
1858
1859 }
1860
1861 /**
1862  * Initialize download and trigger curl
1863  *
1864  * @param cls the proxycurltask
1865  * @param auth_name the name of the authority (site of origin) of ctask->host
1866  *
1867  */
1868 static void
1869 process_get_authority (void *cls,
1870                        const char* auth_name)
1871 {
1872   struct ProxyCurlTask *ctask = cls;
1873
1874   if (NULL == auth_name)
1875   {
1876     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1877                 "Get authority failed!\n");
1878     strcpy (ctask->authority, "");
1879   }
1880   else
1881   {
1882     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1883                 "Get authority yielded %s\n", auth_name);
1884     strcpy (ctask->authority, auth_name);
1885   }
1886
1887   GNUNET_GNS_lookup_zone (gns_handle,
1888                           ctask->host,
1889                           local_gns_zone,
1890                           GNUNET_GNS_RECORD_LEHO,
1891                           GNUNET_YES, //Only cached for performance
1892                           shorten_zonekey,
1893                           &process_leho_lookup,
1894                           ctask);
1895 }
1896
1897 static void*
1898 mhd_log_callback (void* cls, const char* url)
1899 {
1900   struct ProxyCurlTask *ctask;
1901
1902   ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
1903   strcpy (ctask->url, url);
1904   return ctask;
1905 }
1906
1907
1908 /**
1909  * Main MHD callback for handling requests.
1910  *
1911  * @param cls unused
1912  * @param con MHD connection handle
1913  * @param url the url in the request
1914  * @param meth the HTTP method used ("GET", "PUT", etc.)
1915  * @param ver the HTTP version string (i.e. "HTTP/1.1")
1916  * @param upload_data the data being uploaded (excluding HEADERS,
1917  *        for a POST that fits into memory and that is encoded
1918  *        with a supported encoding, the POST data will NOT be
1919  *        given in upload_data and is instead available as
1920  *        part of MHD_get_connection_values; very large POST
1921  *        data *will* be made available incrementally in
1922  *        upload_data)
1923  * @param upload_data_size set initially to the size of the
1924  *        upload_data provided; the method must update this
1925  *        value to the number of bytes NOT processed;
1926  * @param con_cls pointer to location where we store the 'struct Request'
1927  * @return MHD_YES if the connection was handled successfully,
1928  *         MHD_NO if the socket must be closed due to a serious
1929  *         error while handling the request
1930  */
1931 static int
1932 create_response (void *cls,
1933                  struct MHD_Connection *con,
1934                  const char *url,
1935                  const char *meth,
1936                  const char *ver,
1937                  const char *upload_data,
1938                  size_t *upload_data_size,
1939                  void **con_cls)
1940 {
1941   struct MhdHttpList* hd = cls;
1942   const char* page = "<html><head><title>gnunet-gns-proxy</title>"
1943                       "</head><body>cURL fail</body></html>";
1944   
1945   char curlurl[MAX_HTTP_URI_LENGTH]; // buffer overflow!
1946   int ret = MHD_YES;
1947   int i;
1948   struct ProxyCurlTask *ctask = *con_cls;
1949   struct ProxyUploadData *fin_post;
1950   struct curl_forms forms[5];
1951   struct ProxyUploadData *upload_data_iter;
1952   
1953   //FIXME handle
1954   if ((0 != strcasecmp (meth, MHD_HTTP_METHOD_GET)) &&
1955       (0 != strcasecmp (meth, MHD_HTTP_METHOD_PUT)) &&
1956       (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST)) &&
1957       (0 != strcasecmp (meth, MHD_HTTP_METHOD_HEAD)))
1958   {
1959     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1960                 "MHD: %s NOT IMPLEMENTED!\n", meth);
1961     return MHD_NO;
1962   }
1963
1964
1965   if (GNUNET_NO == ctask->accepted)
1966   {
1967
1968     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1969                 "Got %s request for %s\n", meth, url);
1970     ctask->mhd = hd;
1971     ctask->curl = curl_easy_init();
1972     ctask->curl_running = GNUNET_NO;
1973     if (NULL == ctask->curl)
1974     {
1975       ctask->response = MHD_create_response_from_buffer (strlen (page),
1976                                                 (void*)page,
1977                                                 MHD_RESPMEM_PERSISTENT);
1978       ret = MHD_queue_response (con,
1979                                 MHD_HTTP_OK,
1980                                 ctask->response);
1981       MHD_destroy_response (ctask->response);
1982       GNUNET_free (ctask);
1983       return ret;
1984     }
1985     
1986     if (ctask->mhd->is_ssl)
1987       ctask->port = HTTPS_PORT;
1988     else
1989       ctask->port = HTTP_PORT;
1990
1991     MHD_get_connection_values (con,
1992                                MHD_HEADER_KIND,
1993                                &con_val_iter, ctask);
1994     
1995     curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1996     curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
1997     curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1998     curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
1999     curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 0);
2000     curl_easy_setopt (ctask->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
2001
2002     if (GNUNET_NO == ctask->mhd->is_ssl)
2003     {
2004       sprintf (curlurl, "http://%s:%d%s", ctask->host, ctask->port, ctask->url);
2005       curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
2006     }
2007     
2008
2009     curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
2010     curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
2011     curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
2012     
2013     /* Add GNS header */
2014     ctask->headers = curl_slist_append (ctask->headers,
2015                                           "GNS: YES");
2016     ctask->accepted = GNUNET_YES;
2017     ctask->download_in_progress = GNUNET_YES;
2018     ctask->buf_status = BUF_WAIT_FOR_CURL;
2019     ctask->connection = con;
2020     ctask->curl_response_code = MHD_HTTP_OK;
2021     ctask->buffer_read_ptr = ctask->buffer;
2022     ctask->buffer_write_ptr = ctask->buffer;
2023     ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
2024     
2025
2026     if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
2027     {
2028       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2029                   "Setting up PUT\n");
2030       
2031       curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1);
2032       curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
2033       curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &put_read_callback);
2034       ctask->headers = curl_slist_append (ctask->headers,
2035                                           "Transfer-Encoding: chunked");
2036     }
2037
2038     if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2039     {
2040       //FIXME handle multipart
2041       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2042                   "Setting up POST processor\n");
2043       ctask->post_handler = MHD_create_post_processor (con,
2044                                  POSTBUFFERSIZE,
2045                                  &con_post_data_iter,
2046                                  ctask);
2047       ctask->headers = curl_slist_append (ctask->headers,
2048                                          "Transfer-Encoding: chunked");
2049       return MHD_YES;
2050     }
2051
2052     if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
2053     {
2054       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2055                   "Setting NOBODY\n");
2056       curl_easy_setopt (ctask->curl, CURLOPT_NOBODY, 1);
2057     }
2058
2059     
2060     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2061                   "MHD: Adding new curl task for %s\n", ctask->host);
2062
2063     GNUNET_GNS_get_authority (gns_handle,
2064                               ctask->host,
2065                               &process_get_authority,
2066                               ctask);
2067     ctask->ready_to_queue = GNUNET_NO;
2068     ctask->fin = GNUNET_NO;
2069     ctask->curl_running = GNUNET_YES;
2070     return MHD_YES;
2071   }
2072
2073   ctask = (struct ProxyCurlTask *) *con_cls;
2074   if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2075   {
2076     if (0 != *upload_data_size)
2077     {
2078       
2079       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2080                   "Invoking POST processor\n");
2081       MHD_post_process (ctask->post_handler,
2082                         upload_data, *upload_data_size);
2083       *upload_data_size = 0;
2084       if ((GNUNET_NO == ctask->is_httppost) &&
2085           (GNUNET_NO == ctask->curl_running))
2086       {
2087         curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
2088         curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION,
2089                           &post_read_callback);
2090         curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
2091         
2092         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2093                   "MHD: Adding new curl task for %s\n", ctask->host);
2094
2095         GNUNET_GNS_get_authority (gns_handle,
2096                                   ctask->host,
2097                                   &process_get_authority,
2098                                   ctask);
2099         ctask->ready_to_queue = GNUNET_NO;
2100         ctask->fin = GNUNET_NO;
2101         ctask->curl_running = GNUNET_YES;
2102       }
2103       return MHD_YES;
2104     }
2105     else if (GNUNET_NO == ctask->post_done)
2106     {
2107       if (GNUNET_YES == ctask->is_httppost)
2108       {
2109         for (upload_data_iter = ctask->upload_data_head;
2110              NULL != upload_data_iter;
2111              upload_data_iter = upload_data_iter->next)
2112         {
2113           i = 0;
2114           if (NULL != upload_data_iter->filename)
2115           {
2116             forms[i].option = CURLFORM_FILENAME;
2117             forms[i].value = upload_data_iter->filename;
2118             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2119                         "Adding filename %s\n",
2120                         forms[i].value);
2121             i++;
2122           }
2123           if (NULL != upload_data_iter->content_type)
2124           {
2125             forms[i].option = CURLFORM_CONTENTTYPE;
2126             forms[i].value = upload_data_iter->content_type;
2127             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2128                         "Adding content type %s\n",
2129                         forms[i].value);
2130             i++;
2131           }
2132           forms[i].option = CURLFORM_PTRCONTENTS;
2133           forms[i].value = upload_data_iter->value;
2134           forms[i+1].option = CURLFORM_END;
2135
2136           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2137                       "Adding formdata for %s (len=%lld)\n",
2138                       upload_data_iter->key,
2139                       upload_data_iter->total_bytes);
2140
2141           curl_formadd(&ctask->httppost, &ctask->httppost_last,
2142                        CURLFORM_COPYNAME, upload_data_iter->key,
2143                        CURLFORM_CONTENTSLENGTH, upload_data_iter->total_bytes,
2144                        CURLFORM_ARRAY, forms,
2145                        CURLFORM_END);
2146         }
2147         curl_easy_setopt (ctask->curl, CURLOPT_HTTPPOST,
2148                           ctask->httppost);
2149
2150         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2151                 "MHD: Adding new curl task for %s\n", ctask->host);
2152
2153         GNUNET_GNS_get_authority (gns_handle,
2154                                   ctask->host,
2155                                   &process_get_authority,
2156                                   ctask);
2157         ctask->ready_to_queue = GNUNET_YES;
2158         ctask->fin = GNUNET_NO;
2159         ctask->curl_running = GNUNET_YES;
2160         ctask->post_done = GNUNET_YES;
2161         return MHD_YES;
2162       }
2163
2164       fin_post = GNUNET_malloc (sizeof (struct ProxyUploadData));
2165       GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
2166                                         ctask->upload_data_tail,
2167                                         fin_post);
2168       ctask->post_done = GNUNET_YES;
2169       return MHD_YES;
2170     }
2171   }
2172   
2173   if (GNUNET_YES != ctask->ready_to_queue)
2174     return MHD_YES; /* wait longer */
2175   
2176   if (GNUNET_YES == ctask->fin)
2177     return MHD_YES;
2178
2179   ctask->fin = GNUNET_YES;
2180   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2181               "MHD: Queueing response for %s\n", ctask->url);
2182   ret = MHD_queue_response (con, ctask->curl_response_code, ctask->response);
2183   run_mhd_now (ctask->mhd);
2184   return ret;
2185 }
2186
2187
2188 /**
2189  * run all httpd
2190  */
2191 static void
2192 run_httpds ()
2193 {
2194   struct MhdHttpList *hd;
2195
2196   for (hd=mhd_httpd_head; NULL != hd; hd = hd->next)
2197     run_httpd (hd);
2198
2199 }
2200
2201
2202 #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
2203
2204
2205 /**
2206  * schedule mhd
2207  *
2208  * @param hd the daemon to run
2209  */
2210 static void
2211 run_httpd (struct MhdHttpList *hd)
2212 {
2213   fd_set rs;
2214   fd_set ws;
2215   fd_set es;
2216   struct GNUNET_NETWORK_FDSet *wrs;
2217   struct GNUNET_NETWORK_FDSet *wws;
2218   struct GNUNET_NETWORK_FDSet *wes;
2219   int max;
2220   int haveto;
2221   UNSIGNED_MHD_LONG_LONG timeout;
2222   struct GNUNET_TIME_Relative tv;
2223
2224   FD_ZERO (&rs);
2225   FD_ZERO (&ws);
2226   FD_ZERO (&es);
2227   wrs = GNUNET_NETWORK_fdset_create ();
2228   wes = GNUNET_NETWORK_fdset_create ();
2229   wws = GNUNET_NETWORK_fdset_create ();
2230   max = -1;
2231   GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
2232   
2233   
2234   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2235               "MHD fds: max=%d\n", max);
2236   
2237   haveto = MHD_get_timeout (hd->daemon, &timeout);
2238
2239   if (MHD_YES == haveto)
2240     tv.rel_value = (uint64_t) timeout;
2241   else
2242     tv = GNUNET_TIME_UNIT_FOREVER_REL;
2243   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
2244   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
2245   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
2246   
2247   if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2248     GNUNET_SCHEDULER_cancel (hd->httpd_task);
2249   hd->httpd_task =
2250     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
2251                                  tv, wrs, wws,
2252                                  &do_httpd, hd);
2253   GNUNET_NETWORK_fdset_destroy (wrs);
2254   GNUNET_NETWORK_fdset_destroy (wws);
2255   GNUNET_NETWORK_fdset_destroy (wes);
2256 }
2257
2258
2259 /**
2260  * Task run whenever HTTP server operations are pending.
2261  *
2262  * @param cls unused
2263  * @param tc sched context
2264  */
2265 static void
2266 do_httpd (void *cls,
2267           const struct GNUNET_SCHEDULER_TaskContext *tc)
2268 {
2269   struct MhdHttpList *hd = cls;
2270   
2271   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2272               "MHD: Main loop\n");
2273   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK; 
2274   MHD_run (hd->daemon);
2275   run_httpd (hd);
2276 }
2277
2278
2279
2280 /**
2281  * Read data from socket
2282  *
2283  * @param cls the closure
2284  * @param tc scheduler context
2285  */
2286 static void
2287 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2288
2289 /**
2290  * Read from remote end
2291  *
2292  * @param cls closure
2293  * @param tc scheduler context
2294  */
2295 static void
2296 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2297
2298 /**
2299  * Write data to remote socket
2300  *
2301  * @param cls the closure
2302  * @param tc scheduler context
2303  */
2304 static void
2305 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2306 {
2307   struct Socks5Request *s5r = cls;
2308   unsigned int len;
2309
2310   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2311
2312   if ((NULL != tc->read_ready) &&
2313       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
2314       ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
2315                                          s5r->rbuf_len)>0)))
2316   {
2317     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2318                 "Successfully sent %d bytes to remote socket\n",
2319                 len);
2320   }
2321   else
2322   {
2323     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
2324     if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
2325       GNUNET_SCHEDULER_cancel (s5r->rtask);
2326     if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
2327       GNUNET_SCHEDULER_cancel (s5r->wtask);
2328     if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
2329       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2330     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2331     GNUNET_NETWORK_socket_close (s5r->sock);
2332     GNUNET_free(s5r);
2333     return;
2334   }
2335
2336   s5r->rtask =
2337     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2338                                    s5r->sock,
2339                                    &do_read, s5r);
2340 }
2341
2342
2343 /**
2344  * Clean up s5r handles
2345  *
2346  * @param s5r the handle to destroy
2347  */
2348 static void
2349 cleanup_s5r (struct Socks5Request *s5r)
2350 {
2351   if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
2352     GNUNET_SCHEDULER_cancel (s5r->rtask);
2353   if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdwtask)
2354     GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2355   if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
2356     GNUNET_SCHEDULER_cancel (s5r->fwdrtask);  
2357   if (NULL != s5r->remote_sock)
2358     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2359   if ((NULL != s5r->sock) && (s5r->cleanup_sock == GNUNET_YES))
2360     GNUNET_NETWORK_socket_close (s5r->sock);
2361   
2362   GNUNET_free(s5r);
2363 }
2364
2365 /**
2366  * Write data to socket
2367  *
2368  * @param cls the closure
2369  * @param tc scheduler context
2370  */
2371 static void
2372 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2373 {
2374   struct Socks5Request *s5r = cls;
2375   unsigned int len;
2376
2377   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2378
2379   if ((NULL != tc->read_ready) &&
2380       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
2381       ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
2382                                          s5r->wbuf_len)>0)))
2383   {
2384     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2385                 "Successfully sent %d bytes to socket\n",
2386                 len);
2387   }
2388   else
2389   {
2390     
2391     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
2392     s5r->cleanup = GNUNET_YES;
2393     s5r->cleanup_sock = GNUNET_YES;
2394     cleanup_s5r (s5r);
2395     
2396     return;
2397   }
2398
2399   if (GNUNET_YES == s5r->cleanup)
2400   {
2401     cleanup_s5r (s5r);
2402     return;
2403   }
2404
2405   if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
2406       (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
2407     s5r->fwdrtask =
2408       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2409                                      s5r->remote_sock,
2410                                      &do_read_remote, s5r);
2411 }
2412
2413 /**
2414  * Read from remote end
2415  *
2416  * @param cls closure
2417  * @param tc scheduler context
2418  */
2419 static void
2420 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2421 {
2422   struct Socks5Request *s5r = cls;
2423   
2424   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2425
2426
2427   if ((NULL != tc->write_ready) &&
2428       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
2429       (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
2430                                          sizeof (s5r->wbuf))))
2431   {
2432     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2433                 "Successfully read %d bytes from remote socket\n",
2434                 s5r->wbuf_len);
2435   }
2436   else
2437   {
2438     if (s5r->wbuf_len == 0)
2439       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2440                   "0 bytes received from remote... graceful shutdown!\n");
2441     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2442       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2443     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
2444       GNUNET_SCHEDULER_cancel (s5r->rtask);
2445     
2446     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2447     s5r->remote_sock = NULL;
2448     GNUNET_NETWORK_socket_close (s5r->sock);
2449     GNUNET_free(s5r);
2450
2451     return;
2452   }
2453   
2454   s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2455                                                s5r->sock,
2456                                                &do_write, s5r);
2457   
2458 }
2459
2460
2461 /**
2462  * Adds a socket to MHD
2463  *
2464  * @param h the handle to the socket to add
2465  * @param daemon the daemon to add the fd to
2466  * @return whatever MHD_add_connection returns
2467  */
2468 static int
2469 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon)
2470 {
2471   int fd;
2472   struct sockaddr *addr;
2473   socklen_t len;
2474
2475   fd = dup (GNUNET_NETWORK_get_fd (h));
2476   addr = GNUNET_NETWORK_get_addr (h);
2477   len = GNUNET_NETWORK_get_addrlen (h);
2478
2479   return MHD_add_connection (daemon, fd, addr, len);
2480 }
2481
2482 /**
2483  * Read file in filename
2484  *
2485  * @param filename file to read
2486  * @param size pointer where filesize is stored
2487  * @return NULL on error
2488  */
2489 static void*
2490 load_file (const char* filename, 
2491            unsigned int* size)
2492 {
2493   void *buffer;
2494   uint64_t fsize;
2495
2496   if (GNUNET_OK !=
2497       GNUNET_DISK_file_size (filename, &fsize,
2498                              GNUNET_YES, GNUNET_YES))
2499     return NULL;
2500   if (fsize > MAX_PEM_SIZE)
2501     return NULL;
2502   *size = (unsigned int) fsize;
2503   buffer = GNUNET_malloc (*size);
2504   if (fsize != GNUNET_DISK_fn_read (filename, buffer, (size_t) fsize))
2505   {
2506     GNUNET_free (buffer);
2507     return NULL;
2508   }
2509   return buffer;
2510 }
2511
2512
2513 /**
2514  * Load PEM key from file
2515  *
2516  * @param key where to store the data
2517  * @param keyfile path to the PEM file
2518  * @return GNUNET_OK on success
2519  */
2520 static int
2521 load_key_from_file (gnutls_x509_privkey_t key, const char* keyfile)
2522 {
2523   gnutls_datum_t key_data;
2524   int ret;
2525
2526   key_data.data = load_file (keyfile, &key_data.size);
2527   ret = gnutls_x509_privkey_import (key, &key_data,
2528                                     GNUTLS_X509_FMT_PEM);
2529   if (GNUTLS_E_SUCCESS != ret)
2530   {
2531     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2532                 _("Unable to import private key from file `%s'\n"),
2533                 keyfile);
2534     GNUNET_break (0);
2535   }
2536   GNUNET_free (key_data.data);
2537   return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2538 }
2539
2540
2541 /**
2542  * Load cert from file
2543  *
2544  * @param crt struct to store data in
2545  * @param certfile path to pem file
2546  * @return GNUNET_OK on success
2547  */
2548 static int
2549 load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
2550 {
2551   gnutls_datum_t cert_data;
2552   int ret;
2553
2554   cert_data.data = load_file (certfile, &cert_data.size);
2555   ret = gnutls_x509_crt_import (crt, &cert_data,
2556                                 GNUTLS_X509_FMT_PEM);
2557   if (GNUTLS_E_SUCCESS != ret)
2558   {
2559     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2560                _("Unable to import certificate %s\n"), certfile);
2561     GNUNET_break (0);
2562   }
2563   GNUNET_free (cert_data.data);
2564   return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2565 }
2566
2567
2568 /**
2569  * Generate new certificate for specific name
2570  *
2571  * @param name the subject name to generate a cert for
2572  * @return a struct holding the PEM data
2573  */
2574 static struct ProxyGNSCertificate *
2575 generate_gns_certificate (const char *name)
2576 {
2577   int ret;
2578   unsigned int serial;
2579   size_t key_buf_size;
2580   size_t cert_buf_size;
2581   gnutls_x509_crt_t request;
2582   time_t etime;
2583   struct tm *tm_data;
2584
2585   ret = gnutls_x509_crt_init (&request);
2586
2587   if (GNUTLS_E_SUCCESS != ret)
2588   {
2589     GNUNET_break (0);
2590   }
2591
2592   GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
2593
2594   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
2595
2596   struct ProxyGNSCertificate *pgc =
2597     GNUNET_malloc (sizeof (struct ProxyGNSCertificate));
2598
2599   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\n");
2600   
2601   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
2602                                  0, "DE", 2);
2603   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
2604                                  0, "GADS", 4);
2605   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
2606                                  0, name, strlen (name));
2607   GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_version (request, 3));
2608
2609   ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
2610
2611   etime = time (NULL);
2612   tm_data = localtime (&etime);
2613   
2614
2615   ret = gnutls_x509_crt_set_serial (request,
2616                                     &serial,
2617                                     sizeof (serial));
2618
2619   ret = gnutls_x509_crt_set_activation_time (request,
2620                                              etime);
2621   tm_data->tm_year++;
2622   etime = mktime (tm_data);
2623
2624   if (-1 == etime)
2625   {
2626     GNUNET_break (0);
2627   }
2628
2629   ret = gnutls_x509_crt_set_expiration_time (request,
2630                                              etime);
2631   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
2632
2633   ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
2634
2635   key_buf_size = sizeof (pgc->key);
2636   cert_buf_size = sizeof (pgc->cert);
2637
2638   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Exporting certificate...\n");
2639   
2640   gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
2641                           pgc->cert, &cert_buf_size);
2642
2643   gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
2644                           pgc->key, &key_buf_size);
2645
2646
2647   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
2648   gnutls_x509_crt_deinit (request);
2649
2650   return pgc;
2651
2652 }
2653
2654
2655 /*
2656  * Accept policy for mhdaemons
2657  *
2658  * @param cls NULL
2659  * @param addr the sockaddr
2660  * @param addrlen the sockaddr length
2661  * @return MHD_NO if sockaddr is wrong or #conns too high
2662  */
2663 static int
2664 accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen)
2665 {
2666   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2667               "In MHD accept policy cb\n");
2668
2669   if (addr != NULL)
2670   {
2671     if (addr->sa_family == AF_UNIX)
2672       return MHD_NO;
2673   }
2674
2675   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2676               "Connection accepted\n");
2677
2678   return MHD_YES;
2679 }
2680
2681
2682 /**
2683  * Adds a socket to an SSL MHD instance
2684  * It is important the the domain name is
2685  * correct. In most cases we need to start a new daemon
2686  *
2687  * @param h the handle to add to a daemon
2688  * @param domain the domain the ssl daemon has to serve
2689  * @return MHD_YES on success
2690  */
2691 static int
2692 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, const char* domain)
2693 {
2694   struct MhdHttpList *hd = NULL;
2695   struct ProxyGNSCertificate *pgc;
2696   struct NetworkHandleList *nh;
2697
2698   for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
2699     if (0 == strcmp (hd->domain, domain))
2700       break;
2701
2702   if (NULL == hd)
2703   {    
2704     pgc = generate_gns_certificate (domain);
2705     
2706     hd = GNUNET_malloc (sizeof (struct MhdHttpList));
2707     hd->is_ssl = GNUNET_YES;
2708     strcpy (hd->domain, domain);
2709     hd->proxy_cert = pgc;
2710
2711     /* Start new MHD */
2712     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2713                 "No previous SSL instance found... starting new one for %s\n",
2714                 domain);
2715 #if HAVE_MHD_NO_LISTEN_SOCKET
2716     hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_NO_LISTEN_SOCKET,
2717                                    0,
2718                                    &accept_cb, NULL,
2719                                    &create_response, hd,
2720                                    MHD_OPTION_CONNECTION_LIMIT,
2721                                    MHD_MAX_CONNECTIONS,
2722                                    MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2723                                    MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2724                                    MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2725                                    MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2726                                    MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2727                                    NULL,
2728                                    MHD_OPTION_END);
2729 #else
2730     hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL,
2731                                    4444 /* dummy port */,
2732                                    &accept_cb, NULL,
2733                                    &create_response, hd,
2734                                    MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
2735                                    MHD_OPTION_CONNECTION_LIMIT,
2736                                    MHD_MAX_CONNECTIONS,
2737                                    MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2738                                    MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2739                                    MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2740                                    MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2741                                    MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2742                                    NULL,
2743                                    MHD_OPTION_END);
2744
2745     GNUNET_assert (hd->daemon != NULL);
2746     hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2747     
2748     GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2749   }
2750
2751   nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
2752   nh->h = h;
2753
2754   GNUNET_CONTAINER_DLL_insert (hd->socket_handles_head,
2755                                hd->socket_handles_tail,
2756                                nh);
2757   
2758   return add_handle_to_mhd (h, hd->daemon);
2759 }
2760
2761
2762
2763 /**
2764  * Read data from incoming connection
2765  *
2766  * @param cls the closure
2767  * @param tc the scheduler context
2768  */
2769 static void
2770 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2771 {
2772   struct Socks5Request *s5r = cls;
2773   struct socks5_client_hello *c_hello;
2774   struct socks5_server_hello *s_hello;
2775   struct socks5_client_request *c_req;
2776   struct socks5_server_response *s_resp;
2777
2778   int ret;
2779   char domain[256];
2780   uint8_t dom_len;
2781   uint16_t req_port;
2782   struct hostent *phost;
2783   uint32_t remote_ip;
2784   struct sockaddr_in remote_addr;
2785   struct in_addr *r_sin_addr;
2786
2787   struct NetworkHandleList *nh;
2788
2789   s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
2790
2791   if ((NULL != tc->write_ready) &&
2792       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
2793       (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
2794                                          sizeof (s5r->rbuf))))
2795   {
2796     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2797                 "Successfully read %d bytes from socket\n",
2798                 s5r->rbuf_len);
2799   }
2800   else
2801   {
2802     if (s5r->rbuf_len != 0)
2803       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
2804     else
2805       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
2806
2807     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2808       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2809     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
2810       GNUNET_SCHEDULER_cancel (s5r->wtask);
2811     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2812       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2813     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2814     GNUNET_NETWORK_socket_close (s5r->sock);
2815     GNUNET_free(s5r);
2816     return;
2817   }
2818
2819   if (s5r->state == SOCKS5_INIT)
2820   {
2821     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2822                 "SOCKS5 init\n");
2823     c_hello = (struct socks5_client_hello*)&s5r->rbuf;
2824
2825     GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
2826
2827     s_hello = (struct socks5_server_hello*)&s5r->wbuf;
2828     s5r->wbuf_len = sizeof( struct socks5_server_hello );
2829
2830     s_hello->version = c_hello->version;
2831     s_hello->auth_method = SOCKS_AUTH_NONE;
2832
2833     /* Write response to client */
2834     s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2835                                                 s5r->sock,
2836                                                 &do_write, s5r);
2837
2838     s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2839                                                 s5r->sock,
2840                                                 &do_read, s5r);
2841
2842     s5r->state = SOCKS5_REQUEST;
2843     return;
2844   }
2845
2846   if (s5r->state == SOCKS5_REQUEST)
2847   {
2848     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2849                 "Processing SOCKS5 request\n");
2850     c_req = (struct socks5_client_request*)&s5r->rbuf;
2851     s_resp = (struct socks5_server_response*)&s5r->wbuf;
2852     //Only 10byte for ipv4 response!
2853     s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
2854
2855     GNUNET_assert (c_req->addr_type == 3);
2856
2857     dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
2858     memset(domain, 0, sizeof(domain));
2859     strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
2860     req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
2861
2862     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2863                 "Requested connection is %s:%d\n",
2864                 domain,
2865                 ntohs(req_port));
2866
2867     if (is_tld (domain, GNUNET_GNS_TLD) ||
2868         is_tld (domain, GNUNET_GNS_TLD_ZKEY))
2869     {
2870       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2871                   "Requested connection is gnunet tld\n",
2872                   domain);
2873       
2874       ret = MHD_NO;
2875       if (ntohs(req_port) == HTTPS_PORT)
2876       {
2877         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2878                     "Requested connection is HTTPS\n");
2879         ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
2880       }
2881       else if (NULL != httpd)
2882       {
2883         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2884                     "Requested connection is HTTP\n");
2885         nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
2886         nh->h = s5r->sock;
2887
2888         GNUNET_CONTAINER_DLL_insert (mhd_httpd_head->socket_handles_head,
2889                                mhd_httpd_head->socket_handles_tail,
2890                                nh);
2891
2892         ret = add_handle_to_mhd ( s5r->sock, httpd );
2893       }
2894
2895       if (ret != MHD_YES)
2896       {
2897         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2898                     _("Failed to start HTTP server\n"));
2899         s_resp->version = 0x05;
2900         s_resp->reply = 0x01;
2901         s5r->cleanup = GNUNET_YES;
2902         s5r->cleanup_sock = GNUNET_YES;
2903         s5r->wtask = 
2904           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2905                                         s5r->sock,
2906                                         &do_write, s5r);
2907         return;
2908       }
2909       
2910       /* Signal success */
2911       s_resp->version = 0x05;
2912       s_resp->reply = 0x00;
2913       s_resp->reserved = 0x00;
2914       s_resp->addr_type = 0x01;
2915       
2916       s5r->cleanup = GNUNET_YES;
2917       s5r->cleanup_sock = GNUNET_NO;
2918       s5r->wtask =
2919         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2920                                         s5r->sock,
2921                                         &do_write, s5r);
2922       run_httpds ();
2923       return;
2924     }
2925     else
2926     {
2927       phost = (struct hostent*)gethostbyname (domain);
2928       if (phost == NULL)
2929       {
2930         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2931                     "Resolve %s error!\n", domain );
2932         s_resp->version = 0x05;
2933         s_resp->reply = 0x01;
2934         s5r->cleanup = GNUNET_YES;
2935         s5r->cleanup_sock = GNUNET_YES;
2936         s5r->wtask = 
2937           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2938                                           s5r->sock,
2939                                           &do_write, s5r);
2940         return;
2941       }
2942
2943       s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
2944                                                        SOCK_STREAM,
2945                                                        0);
2946       r_sin_addr = (struct in_addr*)(phost->h_addr);
2947       remote_ip = r_sin_addr->s_addr;
2948       memset(&remote_addr, 0, sizeof(remote_addr));
2949       remote_addr.sin_family = AF_INET;
2950 #if HAVE_SOCKADDR_IN_SIN_LEN
2951       remote_addr.sin_len = sizeof (remote_addr);
2952 #endif
2953       remote_addr.sin_addr.s_addr = remote_ip;
2954       remote_addr.sin_port = req_port;
2955       
2956       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2957                   "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
2958                   ntohs(req_port));
2959
2960       if ((GNUNET_OK !=
2961           GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
2962                                           (const struct sockaddr*)&remote_addr,
2963                                           sizeof (remote_addr)))
2964           && (errno != EINPROGRESS))
2965       {
2966         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
2967         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2968                     "socket request error...\n");
2969         s_resp->version = 0x05;
2970         s_resp->reply = 0x01;
2971         s5r->wtask =
2972           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2973                                           s5r->sock,
2974                                           &do_write, s5r);
2975         //TODO see above
2976         return;
2977       }
2978
2979       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2980                   "new remote connection\n");
2981
2982       s_resp->version = 0x05;
2983       s_resp->reply = 0x00;
2984       s_resp->reserved = 0x00;
2985       s_resp->addr_type = 0x01;
2986
2987       s5r->state = SOCKS5_DATA_TRANSFER;
2988
2989       s5r->wtask =
2990         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2991                                         s5r->sock,
2992                                         &do_write, s5r);
2993       s5r->rtask =
2994         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2995                                        s5r->sock,
2996                                        &do_read, s5r);
2997
2998     }
2999     return;
3000   }
3001
3002   if (s5r->state == SOCKS5_DATA_TRANSFER)
3003   {
3004     if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
3005     {
3006       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3007                   "Closing connection to client\n");
3008       if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
3009         GNUNET_SCHEDULER_cancel (s5r->rtask);
3010       if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
3011         GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
3012       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
3013         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
3014       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
3015         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
3016       
3017       if (s5r->remote_sock != NULL)
3018         GNUNET_NETWORK_socket_close (s5r->remote_sock);
3019       GNUNET_NETWORK_socket_close (s5r->sock);
3020       GNUNET_free(s5r);
3021       return;
3022     }
3023
3024     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3025                 "forwarding %d bytes from client\n", s5r->rbuf_len);
3026
3027     s5r->fwdwtask =
3028       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
3029                                       s5r->remote_sock,
3030                                       &do_write_remote, s5r);
3031
3032     if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
3033     {
3034       s5r->fwdrtask =
3035         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3036                                        s5r->remote_sock,
3037                                        &do_read_remote, s5r);
3038     }
3039
3040
3041   }
3042
3043 }
3044
3045
3046 /**
3047  * Accept new incoming connections
3048  *
3049  * @param cls the closure
3050  * @param tc the scheduler context
3051  */
3052 static void
3053 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3054 {
3055   struct GNUNET_NETWORK_Handle *s;
3056   struct Socks5Request *s5r;
3057
3058   ltask = GNUNET_SCHEDULER_NO_TASK;
3059   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
3060     return;
3061
3062   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3063                                          lsock,
3064                                          &do_accept, NULL);
3065
3066   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
3067
3068   if (NULL == s)
3069   {
3070     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
3071     return;
3072   }
3073
3074   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3075               "Got an inbound connection, waiting for data\n");
3076
3077   s5r = GNUNET_malloc (sizeof (struct Socks5Request));
3078   s5r->sock = s;
3079   s5r->state = SOCKS5_INIT;
3080   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
3081   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
3082   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
3083   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3084                                               s5r->sock,
3085                                               &do_read, s5r);
3086 }
3087
3088
3089 /**
3090  * Task run on shutdown
3091  *
3092  * @param cls closure
3093  * @param tc task context
3094  */
3095 static void
3096 do_shutdown (void *cls,
3097              const struct GNUNET_SCHEDULER_TaskContext *tc)
3098 {
3099
3100   struct MhdHttpList *hd;
3101   struct MhdHttpList *tmp_hd;
3102   struct NetworkHandleList *nh;
3103   struct NetworkHandleList *tmp_nh;
3104   struct ProxyCurlTask *ctask;
3105   struct ProxyCurlTask *ctask_tmp;
3106   struct ProxyUploadData *pdata;
3107   
3108   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3109               "Shutting down...\n");
3110
3111   gnutls_global_deinit ();
3112  
3113   if (NULL != local_gns_zone)
3114     GNUNET_free (local_gns_zone); 
3115   if (NULL != local_private_zone)
3116     GNUNET_free (local_private_zone);
3117   if (NULL != local_shorten_zone)
3118     GNUNET_free (local_shorten_zone);
3119
3120   if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
3121   {
3122     GNUNET_SCHEDULER_cancel (curl_download_task);
3123     curl_download_task = GNUNET_SCHEDULER_NO_TASK;
3124   }
3125
3126   for (hd = mhd_httpd_head; hd != NULL; hd = tmp_hd)
3127   {
3128     tmp_hd = hd->next;
3129
3130     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3131                 "Stopping daemon\n");
3132
3133     if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
3134     {
3135       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3136                   "Stopping select task %d\n",
3137                   hd->httpd_task);
3138       GNUNET_SCHEDULER_cancel (hd->httpd_task);
3139       hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
3140     }
3141
3142     if (NULL != hd->daemon)
3143     {
3144       MHD_stop_daemon (hd->daemon);
3145       hd->daemon = NULL;
3146     }
3147
3148     for (nh = hd->socket_handles_head; nh != NULL; nh = tmp_nh)
3149     {
3150       tmp_nh = nh->next;
3151
3152       GNUNET_NETWORK_socket_close (nh->h);
3153
3154       GNUNET_free (nh);
3155     }
3156
3157     if (NULL != hd->proxy_cert)
3158     {
3159       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3160                   "Free certificate\n");
3161       GNUNET_free (hd->proxy_cert);
3162     }
3163
3164     GNUNET_free (hd);
3165   }
3166
3167   for (ctask=ctasks_head; ctask != NULL; ctask=ctask_tmp)
3168   {
3169     ctask_tmp = ctask->next;
3170
3171     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3172                 "Cleaning up cURL task\n");
3173
3174     if (ctask->curl != NULL)
3175       curl_easy_cleanup (ctask->curl);
3176     ctask->curl = NULL;
3177     if (NULL != ctask->headers)
3178       curl_slist_free_all (ctask->headers);
3179     if (NULL != ctask->resolver)
3180       curl_slist_free_all (ctask->resolver);
3181
3182     if (NULL != ctask->response)
3183       MHD_destroy_response (ctask->response);
3184
3185     pdata = ctask->upload_data_head;
3186
3187     //FIXME free pdata here
3188     for (; pdata != NULL; pdata = ctask->upload_data_head)
3189     {
3190       GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
3191                                    ctask->upload_data_tail,
3192                                    pdata);
3193       GNUNET_free_non_null (pdata->filename);
3194       GNUNET_free_non_null (pdata->content_type);
3195       GNUNET_free_non_null (pdata->key);
3196       GNUNET_free_non_null (pdata->value);
3197       GNUNET_free (pdata);
3198     }
3199
3200     GNUNET_free (ctask);
3201   }
3202   curl_multi_cleanup (curl_multi);
3203
3204   GNUNET_GNS_disconnect (gns_handle);
3205 }
3206
3207
3208 /**
3209  * Compiles a regex for us
3210  *
3211  * @param re ptr to re struct
3212  * @param rt the expression to compile
3213  * @return 0 on success
3214  */
3215 static int
3216 compile_regex (regex_t *re, const char* rt)
3217 {
3218   int status;
3219   char err[1024];
3220
3221   status = regcomp (re, rt, REG_EXTENDED|REG_NEWLINE);
3222   if (status)
3223   {
3224     regerror (status, re, err, 1024);
3225     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3226                 "Regex error compiling '%s': %s\n", rt, err);
3227     return 1;
3228   }
3229   return 0;
3230 }
3231
3232
3233 /**
3234  * Loads the users local zone key
3235  *
3236  * @return GNUNET_YES on success
3237  */
3238 static int
3239 load_local_zone_key (const struct GNUNET_CONFIGURATION_Handle *cfg)
3240 {
3241   char *keyfile;
3242   struct GNUNET_CRYPTO_RsaPrivateKey *key = NULL;
3243   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
3244   struct GNUNET_CRYPTO_ShortHashCode *zone = NULL;
3245   struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename;
3246
3247   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3248                                                             "ZONEKEY", &keyfile))
3249   {
3250     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3251                 "Unable to load zone key config value!\n");
3252     return GNUNET_NO;
3253   }
3254
3255   if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
3256   {
3257     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3258                 "Unable to load zone key %s!\n", keyfile);
3259     GNUNET_free(keyfile);
3260     return GNUNET_NO;
3261   }
3262
3263   key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
3264   GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
3265   local_gns_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3266   GNUNET_CRYPTO_short_hash(&pkey,
3267                            sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
3268                            local_gns_zone);
3269   zone = local_gns_zone;
3270   GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3271   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3272               "Using zone: %s!\n", &zonename);
3273   GNUNET_CRYPTO_rsa_key_free(key);
3274   GNUNET_free(keyfile);
3275   keyfile = NULL;
3276
3277   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3278                                                    "PRIVATE_ZONEKEY", &keyfile))
3279   {
3280     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3281                 "Unable to load private zone key config value!\n");
3282   }
3283
3284   if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile)))
3285   {
3286     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3287                 "Unable to load private zone key %s!\n", keyfile);
3288     GNUNET_free(keyfile);
3289   }
3290   else
3291   {
3292     key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
3293     GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
3294     local_private_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3295     GNUNET_CRYPTO_short_hash(&pkey,
3296                              sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
3297                              local_private_zone);
3298     GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3299     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3300                 "Using private zone: %s!\n", &zonename);
3301     GNUNET_CRYPTO_rsa_key_free(key);
3302     GNUNET_free(keyfile);
3303   }
3304   keyfile = NULL;
3305
3306   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3307                                                    "SHORTEN_ZONEKEY", &keyfile))
3308   {
3309     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3310                 "Unable to load shorten zone key config value!\n");
3311   }
3312
3313   if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile)))
3314   {
3315     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3316                 "Unable to load shorten zone key %s!\n", keyfile);
3317     GNUNET_free(keyfile);
3318   }
3319   else
3320   {
3321     key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
3322     GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
3323     local_shorten_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3324     GNUNET_CRYPTO_short_hash(&pkey,
3325                              sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
3326                              local_shorten_zone);
3327     GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3328     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3329                 "Using shorten zone: %s!\n", &zonename);
3330     GNUNET_CRYPTO_rsa_key_free(key);
3331     GNUNET_free(keyfile);
3332   }
3333
3334   return GNUNET_YES;
3335 }
3336
3337 /**
3338  * Main function that will be run
3339  *
3340  * @param cls closure
3341  * @param args remaining command-line arguments
3342  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3343  * @param cfg configuration
3344  */
3345 static void
3346 run (void *cls, char *const *args, const char *cfgfile,
3347      const struct GNUNET_CONFIGURATION_Handle *cfg)
3348 {
3349   struct sockaddr_in sa;
3350   struct MhdHttpList *hd;
3351   char* cafile_cfg = NULL;
3352   char* cafile;
3353 #if !HAVE_MHD_NO_LISTEN_SOCKET
3354   size_t len;
3355   char* proxy_sockfile;
3356   struct sockaddr_un mhd_unix_sock_addr;
3357 #endif
3358
3359   curl_multi = curl_multi_init ();
3360
3361   if (NULL == curl_multi)
3362   {
3363     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3364                 "Failed to create cURL multo handle!\n");
3365     return;
3366   }
3367   
3368   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3369               "Loading CA\n");
3370   cafile = cafile_opt;
3371   if (NULL == cafile)
3372   {
3373     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3374                                                           "PROXY_CACERT",
3375                                                           &cafile_cfg))
3376     {
3377       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3378                   "Unable to load proxy CA config value!\n");
3379       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3380                   "No proxy CA provided!\n");
3381       return;
3382     }
3383     cafile = cafile_cfg;
3384   }
3385   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3386               "Using %s as CA\n", cafile);
3387   
3388   gnutls_global_init ();
3389   gnutls_x509_crt_init (&proxy_ca.cert);
3390   gnutls_x509_privkey_init (&proxy_ca.key);
3391   
3392   if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
3393        (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
3394   {
3395     // FIXME: release resources...
3396     return;
3397   }
3398
3399   GNUNET_free_non_null (cafile_cfg);
3400   
3401   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3402               "Loading Template\n");
3403   
3404   compile_regex (&re_dotplus, (char*) RE_A_HREF);
3405
3406   gns_handle = GNUNET_GNS_connect (cfg);
3407
3408   if (GNUNET_NO == load_local_zone_key (cfg))
3409   {
3410     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3411                 "Unable to load zone!\n");
3412     return;
3413   }
3414
3415   if (NULL == gns_handle)
3416   {
3417     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3418                 "Unable to connect to GNS!\n");
3419     return;
3420   }
3421
3422   memset (&sa, 0, sizeof (sa));
3423   sa.sin_family = AF_INET;
3424   sa.sin_port = htons (port);
3425 #if HAVE_SOCKADDR_IN_SIN_LEN
3426   sa.sin_len = sizeof (sa);
3427 #endif
3428
3429   lsock = GNUNET_NETWORK_socket_create (AF_INET,
3430                                         SOCK_STREAM,
3431                                         0);
3432
3433   if ((NULL == lsock) ||
3434       (GNUNET_OK !=
3435        GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
3436                                    sizeof (sa))))
3437   {
3438     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3439                 "Failed to create listen socket bound to `%s'",
3440                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
3441     if (NULL != lsock)
3442       GNUNET_NETWORK_socket_close (lsock);
3443     return;
3444   }
3445
3446   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
3447   {
3448     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3449                 "Failed to listen on socket bound to `%s'",
3450                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
3451     return;
3452   }
3453
3454   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3455                                          lsock, &do_accept, NULL);
3456
3457   ctasks_head = NULL;
3458   ctasks_tail = NULL;
3459
3460   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
3461   {
3462     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3463                 "cURL global init failed!\n");
3464     return;
3465   }
3466
3467   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3468               "Proxy listens on port %u\n",
3469               port);
3470
3471   mhd_httpd_head = NULL;
3472   mhd_httpd_tail = NULL;
3473   total_mhd_connections = 0;
3474 #if ! HAVE_MHD_NO_LISTEN_SOCKET
3475   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3476                                                             "PROXY_UNIXPATH",
3477                                                             &proxy_sockfile))
3478   {
3479     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3480                 "Specify PROXY_UNIXPATH in gns-proxy config section!\n");
3481     return;
3482   }
3483
3484   mhd_unix_socket = GNUNET_NETWORK_socket_create (AF_UNIX,
3485                                                 SOCK_STREAM,
3486                                                 0);
3487
3488   if (NULL == mhd_unix_socket)
3489   {
3490     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3491                 "Unable to create unix domain socket!\n");
3492     return;
3493   }
3494
3495   mhd_unix_sock_addr.sun_family = AF_UNIX;
3496   strcpy (mhd_unix_sock_addr.sun_path, proxy_sockfile);
3497
3498 #if LINUX
3499   mhd_unix_sock_addr.sun_path[0] = '\0';
3500 #endif
3501 #if HAVE_SOCKADDR_IN_SIN_LEN
3502   mhd_unix_sock_addr.sun_len = (u_char) sizeof (struct sockaddr_un);
3503 #endif
3504
3505   len = strlen (proxy_sockfile) + sizeof(AF_UNIX);
3506
3507   GNUNET_free (proxy_sockfile);
3508
3509   if (GNUNET_OK != GNUNET_NETWORK_socket_bind (mhd_unix_socket,
3510                                (struct sockaddr*)&mhd_unix_sock_addr,
3511                                len))
3512   {
3513     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3514                 "Unable to bind unix domain socket!\n");
3515     return;
3516   }
3517
3518   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (mhd_unix_socket,
3519                                                  1))
3520   {
3521     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3522                 "Unable to listen on unix domain socket!\n");
3523     return;
3524   }
3525 #endif
3526
3527   hd = GNUNET_malloc (sizeof (struct MhdHttpList));
3528   hd->is_ssl = GNUNET_NO;
3529   strcpy (hd->domain, "");
3530
3531 #if HAVE_MHD_NO_LISTEN_SOCKET
3532   httpd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
3533                             0,
3534                             &accept_cb, NULL,
3535                             &create_response, hd,
3536                             MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
3537                             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3538                             MHD_OPTION_NOTIFY_COMPLETED,
3539                             NULL, NULL,
3540                             MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3541                             MHD_OPTION_END);
3542 #else
3543   httpd = MHD_start_daemon (MHD_USE_DEBUG,
3544                             4444 /* Dummy port */,
3545                             &accept_cb, NULL,
3546                             &create_response, hd,
3547                             MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
3548                             MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
3549                             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3550                             MHD_OPTION_NOTIFY_COMPLETED,
3551                             NULL, NULL,
3552                             MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3553                             MHD_OPTION_END);
3554 #endif
3555
3556   GNUNET_assert (httpd != NULL);
3557   hd->daemon = httpd;
3558   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
3559
3560   GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
3561
3562   run_httpds ();
3563
3564   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
3565                                 &do_shutdown, NULL);
3566
3567 }
3568
3569
3570 /**
3571  * The main function for gnunet-gns-proxy.
3572  *
3573  * @param argc number of arguments from the command line
3574  * @param argv command line arguments
3575  * @return 0 ok, 1 on error
3576  */
3577 int
3578 main (int argc, char *const *argv)
3579 {
3580   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
3581     {'p', "port", NULL,
3582      gettext_noop ("listen on specified port (default: 7777)"), 1,
3583      &GNUNET_GETOPT_set_ulong, &port},
3584     {'a', "authority", NULL,
3585       gettext_noop ("pem file to use as CA"), 1,
3586       &GNUNET_GETOPT_set_string, &cafile_opt},
3587     GNUNET_GETOPT_OPTION_END
3588   };
3589
3590   int ret;
3591
3592   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
3593     return 2;
3594
3595
3596   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
3597   ret =
3598       (GNUNET_OK ==
3599        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
3600                            _("GNUnet GNS proxy"),
3601                            options,
3602                            &run, NULL)) ? 0 : 1;
3603   GNUNET_free_non_null ((char*)argv);
3604
3605   return ret;
3606 }
3607
3608
3609
3610