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