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