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