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