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