-fix
[oweals/gnunet.git] / src / gns / gnunet-gns-proxy.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 #include "platform.h"
22 #include <gnunet_util_lib.h>
23 #include <gnunet_gns_service.h>
24 #include <microhttpd.h>
25 #include <curl/curl.h>
26 #include <regex.h>
27 #include "gns_proxy_proto.h"
28 #include "gns.h"
29
30 /** SSL **/
31 #include <gnutls/gnutls.h>
32 #include <gnutls/x509.h>
33 #include <gnutls/abstract.h>
34 #include <gnutls/crypto.h>
35 #include <time.h>
36
37 #define GNUNET_GNS_PROXY_PORT 7777
38
39 /* MHD/cURL defines */
40 #define BUF_WAIT_FOR_CURL 0
41 #define BUF_WAIT_FOR_MHD 1
42 #define HTML_HDR_CONTENT "Content-Type: text/html\r\n"
43
44 /* regexp */
45 #define RE_DOTPLUS "<a href=\"http://(([A-Za-z]+[.])+)([+])"
46 #define RE_A_HREF  "<a href=\"https?://(([A-Za-z]+[.])+)([+]|zkey)"
47 #define RE_N_MATCHES 4
48
49 /* The usual suspects */
50 #define HTTP_PORT 80
51 #define HTTPS_PORT 443
52
53
54 /**
55  * A structure for CA cert/key
56  */
57 struct ProxyCA
58 {
59   /* The certificate */
60   gnutls_x509_crt_t cert;
61
62   /* The private key */
63   gnutls_x509_privkey_t key;
64 };
65
66
67 /**
68  * Structure for GNS certificates
69  */
70 struct ProxyGNSCertificate
71 {
72   /* The certificate as PEM */
73   char cert[10 * 1024];
74
75   /* The private key as PEM */
76   char key[10 * 1024];
77 };
78
79
80 /**
81  * A structure for socks requests
82  */
83 struct Socks5Request
84 {
85   /* The client socket */
86   struct GNUNET_NETWORK_Handle *sock;
87
88   /* The server socket */
89   struct GNUNET_NETWORK_Handle *remote_sock;
90   
91   /* The socks state */
92   int state;
93   
94   /* Client socket read task */
95   GNUNET_SCHEDULER_TaskIdentifier rtask;
96
97   /* Server socket read task */
98   GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
99
100   /* Client socket write task */
101   GNUNET_SCHEDULER_TaskIdentifier wtask;
102
103   /* Server socket write task */
104   GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
105
106   /* Read buffer */
107   char rbuf[2048];
108
109   /* Write buffer */
110   char wbuf[2048];
111
112   /* Length of data in read buffer */
113   unsigned int rbuf_len;
114
115   /* Length of data in write buffer */
116   unsigned int wbuf_len;
117
118   /* This handle is scheduled for cleanup? */
119   int cleanup;
120
121   /* Shall we close the client socket on cleanup? */
122   int cleanup_sock;
123 };
124
125
126 /**
127  * A structure for all running Httpds
128  */
129 struct MhdHttpList
130 {
131   /* DLL for httpds */
132   struct MhdHttpList *prev;
133
134   /* DLL for httpds */
135   struct MhdHttpList *next;
136
137   /* is this an ssl daemon? */
138   int is_ssl;
139
140   /* the domain name to server (only important for SSL) */
141   char domain[256];
142
143   /* The daemon handle */
144   struct MHD_Daemon *daemon;
145
146   /* The task ID */
147   GNUNET_SCHEDULER_TaskIdentifier httpd_task;
148 };
149
150 /**
151  * A structure for MHD<->cURL streams
152  */
153 struct ProxyCurlTask
154 {
155   /* DLL for tasks */
156   struct ProxyCurlTask *prev;
157
158   /* DLL for tasks */
159   struct ProxyCurlTask *next;
160
161   /* Handle to cURL */
162   CURL *curl;
163
164   /* Optional header replacements for curl (LEHO) */
165   struct curl_slist *headers;
166
167   /* Optional resolver replacements for curl (LEHO) */
168   struct curl_slist *resolver;
169
170   /* The URL to fetch */
171   char url[2048];
172
173   /* The cURL write buffer / MHD read buffer */
174   char buffer[CURL_MAX_WRITE_SIZE];
175
176   /* The pointer to the data in the buffer */
177   char *buffer_ptr;
178
179   /* The buffer status (BUF_WAIT_FOR_CURL or BUF_WAIT_FOR_MHD) */
180   int buf_status;
181
182   /* Number of bytes in buffer */
183   unsigned int bytes_in_buffer;
184
185   /* Indicates wheather the download is in progress */
186   int download_in_progress;
187
188   /* Indicates wheather the download was successful */
189   int download_successful;
190
191   /* Indicates wheather the download failed */
192   int download_error;
193
194   /* Indicates wheather we need to parse HTML */
195   int parse_content;
196
197   /* Indicates wheather we are postprocessing the HTML right now */
198   int is_postprocessing;
199
200   /* Indicates wheather postprocessing has finished */
201   int pp_finished;
202
203   /* Task ID of the postprocessing task */
204   GNUNET_SCHEDULER_TaskIdentifier pp_task;
205
206   /* The postprocessing buffer TODO length? */
207   char pp_buf[256];
208
209   /* The authority of the corresponding host (site of origin) */
210   char authority[256];
211
212   /* The hostname (Host header field) */
213   char host[256];
214
215   /* The LEgacy HOstname (can be empty) */
216   char leho[256];
217
218   /* The associated daemon list entry */
219   struct MhdHttpList *mhd;
220   
221 };
222
223 /* The port the proxy is running on (default 7777) */
224 static unsigned long port = GNUNET_GNS_PROXY_PORT;
225
226 /* The CA file (pem) to use for the proxy CA */
227 static char* cafile;
228
229 /* The listen socket of the proxy */
230 static struct GNUNET_NETWORK_Handle *lsock;
231
232 /* The listen task ID */
233 GNUNET_SCHEDULER_TaskIdentifier ltask;
234
235 /* The cURL download task */
236 GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
237
238 /* The non SSL httpd daemon handle */
239 static struct MHD_Daemon *httpd;
240
241 /* The cURL multi handle */
242 static CURLM *curl_multi;
243
244 /* Handle to the GNS service */
245 static struct GNUNET_GNS_Handle *gns_handle;
246
247 /* DLL for ProxyCurlTasks */
248 static struct ProxyCurlTask *ctasks_head;
249
250 /* DLL for ProxyCurlTasks */
251 static struct ProxyCurlTask *ctasks_tail;
252
253 /* DLL for http daemons */
254 static struct MhdHttpList *mhd_httpd_head;
255
256 /* DLL for http daemons */
257 static struct MhdHttpList *mhd_httpd_tail;
258
259 /* Handle to the regex for dotplus (.+) replacement in HTML */
260 static regex_t re_dotplus;
261
262 /* The users local GNS zone hash */
263 static struct GNUNET_CRYPTO_ShortHashCode local_gns_zone;
264
265 /* The CA for SSL certificate generation */
266 static struct ProxyCA proxy_ca;
267
268 /**
269  * Checks if name is in tld
270  *
271  * @param name the name to check
272  * @param tld the TLD to check for
273  * @return GNUNET_YES or GNUNET_NO
274  */
275 int
276 is_tld(const char* name, const char* tld)
277 {
278   int offset = 0;
279
280   if (strlen(name) <= strlen(tld))
281   {
282     return GNUNET_NO;
283   }
284
285   offset = strlen(name)-strlen(tld);
286   if (strcmp (name+offset, tld) != 0)
287   {
288     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
289                "%s is not in .%s TLD\n", name, tld);
290     return GNUNET_NO;
291   }
292
293   return GNUNET_YES;
294 }
295
296
297 /**
298  * Read HTTP request header field 'Host'
299  *
300  * @param cls buffer to write to
301  * @param kind value kind
302  * @param key field key
303  * @param value field value
304  * @return MHD_NO when Host found
305  */
306 static int
307 con_val_iter (void *cls,
308               enum MHD_ValueKind kind,
309               const char *key,
310               const char *value)
311 {
312   char* buf = (char*)cls;
313
314   if (0 == strcmp ("Host", key))
315   {
316     strcpy (buf, value);
317     return MHD_NO;
318   }
319   return MHD_YES;
320 }
321
322
323 /**
324  * Check HTTP response header for mime
325  *
326  * @param buffer curl buffer
327  * @param size curl blocksize
328  * @param nmemb curl blocknumber
329  * @param cls handle
330  * @return size of read bytes
331  */
332 static size_t
333 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
334 {
335   size_t bytes = size * nmemb;
336   struct ProxyCurlTask *ctask = cls;
337   char hdr[bytes+1];
338
339   memcpy (hdr, buffer, bytes);
340   hdr[bytes] = '\0';
341
342   if (0 == strcmp (hdr, HTML_HDR_CONTENT))
343   {
344     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
345                 "Got HTML HTTP response header\n");
346     ctask->parse_content = GNUNET_YES;
347   }
348
349   return bytes;
350 }
351
352 /**
353  * schedule mhd
354  *
355  * @param hd a http daemon list entry
356  */
357 static void
358 run_httpd (struct MhdHttpList *hd);
359
360
361 /**
362  * schedule all mhds
363  *
364  */
365 static void
366 run_httpds (void);
367
368
369 /**
370  * Task that simply runs MHD main loop
371  *
372  * @param cls NULL
373  * @param tc task context
374  */
375 static void
376 run_mhd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
377 {
378
379   struct MhdHttpList *hd = cls;
380
381   //for (hd=mhd_httpd_head; hd != NULL; hd = hd->next)
382     MHD_run (hd->daemon);
383 }
384
385
386 /**
387  * Process cURL download bits
388  *
389  * @param ptr buffer with data
390  * @param size size of a record
391  * @param nmemb number of records downloaded
392  * @param ctx context
393  * @return number of processed bytes
394  */
395 static size_t
396 callback_download (void *ptr, size_t size, size_t nmemb, void *ctx)
397 {
398   const char *cbuf = ptr;
399   size_t total;
400   struct ProxyCurlTask *ctask = ctx;
401
402   //MHD_run (httpd);
403
404   total = size*nmemb;
405
406   if (total == 0)
407   {
408     return total;
409   }
410
411   if (total > sizeof (ctask->buffer))
412   {
413     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
414                 "CURL gave us too much data to handle (%d)!\n",
415                 total);
416     return 0;
417   }
418   
419   if (ctask->buf_status == BUF_WAIT_FOR_MHD)
420   {
421     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
422                 "CURL: Waiting for MHD (%s)\n", ctask->url);
423     return CURL_WRITEFUNC_PAUSE;
424   }
425
426
427   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
428               "CURL: Copying to MHD (%s, %d)\n", ctask->url, total);
429   memcpy (ctask->buffer, cbuf, total);
430   ctask->bytes_in_buffer = total;
431   ctask->buffer_ptr = ctask->buffer;
432
433   ctask->buf_status = BUF_WAIT_FOR_MHD;
434
435   //GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
436   //            "cURL chunk:\n%s\n", (char*)ctask->buffer);
437   //run_mhd (NULL, NULL);
438   GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
439   return total;
440 }
441
442
443
444 /**
445  * Callback to free content
446  *
447  * @param cls content to free
448  */
449 static void
450 mhd_content_free (void *cls)
451 {
452   struct ProxyCurlTask *ctask = cls;
453
454   if (NULL != ctask->headers)
455     curl_slist_free_all (ctask->headers);
456
457   if (NULL != ctask->curl)
458     curl_easy_cleanup (ctask->curl);
459
460   ctask->curl = NULL;
461
462   GNUNET_free (ctask);
463
464 }
465
466
467 /**
468  * Shorten result callback
469  *
470  * @param cls the proxycurltask
471  * @param short_name the shortened name (NULL on error)
472  */
473 static void
474 process_shorten (void* cls, const char* short_name)
475 {
476   struct ProxyCurlTask *ctask = cls;
477
478   char tmp[strlen(ctask->pp_buf)]; //TODO length
479
480   if (NULL == short_name)
481   {
482     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
483                 "MHD PP: Unable to shorten %s\n",
484                 ctask->pp_buf);
485     return;
486   }
487
488   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
489               "MHD PP: Shorten %s -> %s\n",
490               ctask->pp_buf,
491               short_name);
492
493   sprintf (tmp, "<a href=http://%s", short_name);
494   strcpy (ctask->pp_buf, tmp);
495
496   ctask->pp_finished = GNUNET_YES;
497
498   GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
499 }
500
501
502 /**
503  * Postprocessing task that uses GNS to shorten names
504  *
505  * @param cls the proxycurltask
506  * @param tc the task context
507  *
508 static void
509 postprocess_name (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
510 {
511   struct ProxyCurlTask *ctask = cls;
512   char tmp[strlen(ctask->pp_buf)];
513
514   sprintf ( tmp, "%s%s", ctask->pp_buf, ctask->authority);
515
516   GNUNET_GNS_shorten (gns_handle,
517                       tmp,
518                       &process_shorten,
519                       ctask);
520
521 }
522 */
523
524 /**
525  * Callback for MHD response
526  *
527  * @param cls closure
528  * @param pos in buffer
529  * @param buf buffer
530  * @param max space in buffer
531  */
532 static ssize_t
533 mhd_content_cb (void *cls,
534                 uint64_t pos,
535                 char* buf,
536                 size_t max)
537 {
538   struct ProxyCurlTask *ctask = cls;
539   ssize_t copied = 0;
540   size_t bytes_to_copy;
541   int nomatch;
542   char *hostptr;
543   regmatch_t m[RE_N_MATCHES];
544
545   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
546               "MHD: content cb\n");
547
548   if (ctask->download_successful &&
549       (ctask->buf_status == BUF_WAIT_FOR_CURL))
550   {
551     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
552                 "MHD: sending response for %s\n", ctask->url);
553     ctask->download_in_progress = GNUNET_NO;
554     curl_multi_remove_handle (curl_multi, ctask->curl);
555     curl_easy_cleanup (ctask->curl);
556     GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
557     return MHD_CONTENT_READER_END_OF_STREAM;
558   }
559   
560   if (ctask->download_error &&
561       (ctask->buf_status == BUF_WAIT_FOR_CURL))
562   {
563     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
564                 "MHD: sending error response\n");
565     ctask->download_in_progress = GNUNET_NO;
566     curl_multi_remove_handle (curl_multi, ctask->curl);
567     curl_easy_cleanup (ctask->curl);
568     GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
569     return MHD_CONTENT_READER_END_WITH_ERROR;
570   }
571
572   if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
573     return 0;
574
575   bytes_to_copy = ctask->bytes_in_buffer;
576   
577   if (ctask->parse_content == GNUNET_YES)
578   {
579
580     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
581                  "MHD: We need to parse the HTML %s\n", ctask->buffer_ptr);
582
583     nomatch = regexec ( &re_dotplus, ctask->buffer_ptr, RE_N_MATCHES, m, 0);
584
585     if (!nomatch)
586     {
587       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
588                   "MHD RE: Match\n");
589
590       GNUNET_assert (m[1].rm_so != -1);
591
592       hostptr = ctask->buffer_ptr+m[1].rm_so;
593
594       if (m[0].rm_so > 0)
595       {
596         bytes_to_copy = m[0].rm_so;
597         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
598                     "Copying %d bytes.\n", m[0].rm_so);
599
600
601       }
602       else
603       {
604         if (ctask->is_postprocessing == GNUNET_YES)
605         {
606           
607           /*Done?*/
608           if ( ctask->pp_finished == GNUNET_NO )
609           {
610             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
611                         "MHD PP: Waiting for PP of %s\n", ctask->pp_buf);
612             return 0;
613           }
614           
615           ctask->is_postprocessing = GNUNET_NO;
616
617           ctask->bytes_in_buffer -= m[0].rm_eo;//(m[1].rm_eo-m[1].rm_so);
618           ctask->buffer_ptr += m[0].rm_eo;//(m[1].rm_eo-m[1].rm_so);
619           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
620                       "Skipping next %d bytes in buffer\n", m[0].rm_eo);
621
622           GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
623
624           if ( strlen (ctask->pp_buf) <= max )
625           {
626             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
627                         "Copying postprocessed %s.\n", ctask->pp_buf);
628             memcpy ( buf, ctask->pp_buf, strlen (ctask->pp_buf) );
629             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
630                         "Done %s.\n", buf);
631             ctask->is_postprocessing = GNUNET_NO;
632             return strlen (ctask->pp_buf);
633           }
634           
635           return 0;
636         }
637
638         memset (ctask->pp_buf, 0, sizeof(ctask->pp_buf));
639         
640         /* If .+ extend with authority */
641         if (0 == strcmp (ctask->buffer_ptr+m[1].rm_eo, "+"))
642         {
643            memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so));
644            strcpy ( ctask->pp_buf+strlen(ctask->pp_buf),
645                     ctask->authority);
646         }
647         /* If .zkey simply copy the name */
648         else
649         {
650           memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so + strlen (GNUNET_GNS_TLD_ZKEY)));
651         }
652
653         ctask->is_postprocessing = GNUNET_YES;
654         ctask->pp_finished = GNUNET_NO;
655         
656         GNUNET_GNS_shorten (gns_handle,
657                            ctask->pp_buf,
658                            &process_shorten,
659                            ctask);
660
661         //postprocess_name(ctask, NULL);
662         //ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_name, ctask);
663
664         return 0;
665       }
666     }
667   }
668
669   if ( bytes_to_copy > max )
670   {
671     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
672                  "MHD: buffer in response too small! (%s)\n",
673                  ctask->url);
674     memcpy ( buf, ctask->buffer_ptr, max);
675     ctask->bytes_in_buffer -= max;
676     ctask->buffer_ptr += max;
677     copied = max;
678   }
679   else
680   {
681     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
682                  "MHD: copying %d bytes to mhd response at offset %d\n",
683                  bytes_to_copy, pos);
684
685     memcpy ( buf, ctask->buffer_ptr, bytes_to_copy );
686     copied = bytes_to_copy;
687     if (bytes_to_copy < ctask->bytes_in_buffer)
688     {
689       ctask->bytes_in_buffer -= bytes_to_copy;
690       ctask->buffer_ptr += bytes_to_copy;
691     }
692     else
693     {
694       ctask->bytes_in_buffer = 0;
695       ctask->buf_status = BUF_WAIT_FOR_CURL;
696       ctask->buffer_ptr = ctask->buffer;
697       curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
698       GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
699     }
700   }
701
702   GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
703
704   return copied;
705 }
706
707
708
709 /**
710  * Task that is run when we are ready to receive more data
711  * from curl
712  *
713  * @param cls closure
714  * @param tc task context
715  */
716 static void
717 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
718
719 /**
720  * Ask cURL for the select sets and schedule download
721  */
722 static void
723 curl_download_prepare ()
724 {
725   CURLMcode mret;
726   fd_set rs;
727   fd_set ws;
728   fd_set es;
729   int max;
730   struct GNUNET_NETWORK_FDSet *grs;
731   struct GNUNET_NETWORK_FDSet *gws;
732   long to;
733   struct GNUNET_TIME_Relative rtime;
734
735   max = -1;
736   FD_ZERO (&rs);
737   FD_ZERO (&ws);
738   FD_ZERO (&es);
739   mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
740
741   if (mret != CURLM_OK)
742   {
743     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
744                 "%s failed at %s:%d: `%s'\n",
745                 "curl_multi_fdset", __FILE__, __LINE__,
746                 curl_multi_strerror (mret));
747     //TODO cleanup here?
748     return;
749   }
750
751   mret = curl_multi_timeout (curl_multi, &to);
752   rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
753
754   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
755               "cURL multi fds: max=%d timeout=%llu\n", max, to);
756
757   grs = GNUNET_NETWORK_fdset_create ();
758   gws = GNUNET_NETWORK_fdset_create ();
759   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
760   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
761   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
762               "Scheduling task cURL\n");
763
764   if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
765     GNUNET_SCHEDULER_cancel (curl_download_task);
766   
767   curl_download_task =
768     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
769                                  rtime,
770                                  grs, gws,
771                                  &curl_task_download, curl_multi);
772   GNUNET_NETWORK_fdset_destroy (gws);
773   GNUNET_NETWORK_fdset_destroy (grs);
774
775 }
776
777
778 /**
779  * Task that is run when we are ready to receive more data
780  * from curl
781  *
782  * @param cls closure
783  * @param tc task context
784  */
785 static void
786 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
787 {
788   int running;
789   int msgnum;
790   struct CURLMsg *msg;
791   CURLMcode mret;
792   struct ProxyCurlTask *ctask;
793   int num_ctasks;
794
795   curl_download_task = GNUNET_SCHEDULER_NO_TASK;
796
797   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
798   {
799     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
800                 "Shutdown requested while trying to download\n");
801     //TODO cleanup
802     return;
803   }
804   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
805               "Ready to dl\n");
806
807   do
808   {
809     running = 0;
810     num_ctasks = 0;
811     
812     mret = curl_multi_perform (curl_multi, &running);
813
814     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
815                 "Running curl tasks: %d\n", running);
816
817     ctask = ctasks_head;
818     for (; ctask != NULL; ctask = ctask->next)
819     {
820       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
821                   "CTask: %s\n", ctask->url);
822       num_ctasks++;
823     }
824
825     if (num_ctasks != running)
826     {
827       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
828                   "%d ctasks, %d curl running\n", num_ctasks, running);
829     }
830     
831     do
832     {
833       ctask = ctasks_head;
834       msg = curl_multi_info_read (curl_multi, &msgnum);
835       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
836                   "Messages left: %d\n", msgnum);
837       
838       if (msg == NULL)
839         break;
840       switch (msg->msg)
841       {
842        case CURLMSG_DONE:
843          if ((msg->data.result != CURLE_OK) &&
844              (msg->data.result != CURLE_GOT_NOTHING))
845          {
846            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
847                        "Download curl failed");
848             
849            for (; ctask != NULL; ctask = ctask->next)
850            {
851              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
852                continue;
853              
854              GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
855                          "Download curl failed for task %s: %s.\n",
856                          ctask->url,
857                          curl_easy_strerror (msg->data.result));
858              ctask->download_successful = GNUNET_NO;
859              ctask->download_error = GNUNET_YES;
860              //curl_multi_remove_handle (curl_multi, ctask->curl);
861              //curl_easy_cleanup (ctask->curl);
862              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
863                                           ctask);
864              break;
865            }
866            GNUNET_assert (ctask != NULL);
867          }
868          else
869          {
870            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
871                        "cURL download completed.\n");
872
873            for (; ctask != NULL; ctask = ctask->next)
874            {
875              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
876                continue;
877              
878              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
879                          "cURL task %s found.\n", ctask->url);
880              ctask->download_successful = GNUNET_YES;
881              //curl_multi_remove_handle (curl_multi, ctask->curl);
882              //curl_easy_cleanup (ctask->curl);
883              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
884                                           ctask);
885              break;
886            }
887            GNUNET_assert (ctask != NULL);
888          }
889          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
890                      "curl end %s\n", curl_easy_strerror(msg->data.result));
891          break;
892        default:
893          GNUNET_assert (0);
894          break;
895       }
896     } while (msgnum > 0);
897     
898     num_ctasks=0;
899     for (ctask=ctasks_head; ctask != NULL; ctask = ctask->next)
900     {
901       num_ctasks++;
902     }
903     
904     if (num_ctasks != running)
905     {
906       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
907                   "%d ctasks, %d curl running\n", num_ctasks, running);
908     }
909
910     GNUNET_assert ( num_ctasks == running );
911
912     run_httpds ();
913
914   } while (mret == CURLM_CALL_MULTI_PERFORM);
915   
916   
917   if (mret != CURLM_OK)
918   {
919     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s failed at %s:%d: `%s'\n",
920                 "curl_multi_perform", __FILE__, __LINE__,
921                 curl_multi_strerror (mret));
922   }
923   curl_download_prepare();
924 }
925
926 /**
927  * Process LEHO lookup
928  *
929  * @param cls the ctask
930  * @param rd_count number of records returned
931  * @param rd record data
932  */
933 static void
934 process_leho_lookup (void *cls,
935                      uint32_t rd_count,
936                      const struct GNUNET_NAMESTORE_RecordData *rd)
937 {
938   struct ProxyCurlTask *ctask = cls;
939   char hosthdr[262]; //256 + "Host: "
940   int i;
941   CURLcode ret;
942   CURLMcode mret;
943   struct hostent *phost;
944   char *ssl_ip;
945   char resolvename[512];
946   char curlurl[512];
947
948   ctask->headers = NULL;
949
950   strcpy (ctask->leho, "");
951
952   for (i=0; i<rd_count; i++)
953   {
954     if (rd[i].record_type != GNUNET_GNS_RECORD_LEHO)
955       continue;
956
957     memcpy (ctask->leho, rd[i].data, rd[i].data_size);
958
959     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
960                 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
961   }
962
963   if (0 != strcmp (ctask->leho, ""))
964   {
965     sprintf (hosthdr, "%s%s", "Host: ", ctask->leho);
966     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
967                 "New HTTP header value: %s\n", hosthdr);
968     ctask->headers = curl_slist_append (ctask->headers, hosthdr);
969     GNUNET_assert (NULL != ctask->headers);
970     ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers);
971     if (CURLE_OK != ret)
972     {
973       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s failed at %s:%d: `%s'\n",
974                            "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret));
975     }
976
977   }
978
979   if (ctask->mhd->is_ssl)
980   {
981     phost = (struct hostent*)gethostbyname (ctask->host);
982     ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
983     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
984                 "SSL target server: %s\n", ssl_ip);
985     sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
986     ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
987     curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver);
988     sprintf (curlurl, "https://%s%s", ctask->leho, ctask->url);
989     curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
990   }
991
992   if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
993   {
994     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
995                 "%s failed at %s:%d: `%s'\n",
996                 "curl_multi_add_handle", __FILE__, __LINE__,
997                 curl_multi_strerror (mret));
998     ctask->download_successful = GNUNET_NO;
999     ctask->download_error = GNUNET_YES;
1000     return;
1001   }
1002   GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1003
1004   curl_download_prepare ();
1005
1006 }
1007
1008 /**
1009  * Initialize download and trigger curl
1010  *
1011  * @param cls the proxycurltask
1012  * @param auth_name the name of the authority (site of origin) of ctask->host
1013  *
1014  */
1015 static void
1016 process_get_authority (void *cls,
1017                        const char* auth_name)
1018 {
1019   struct ProxyCurlTask *ctask = cls;
1020
1021   if (NULL == auth_name)
1022   {
1023     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1024                 "Get authority failed!\n");
1025     strcpy (ctask->authority, "");
1026   }
1027
1028   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1029               "Get authority yielded %s\n", auth_name);
1030   strcpy (ctask->authority, auth_name);
1031
1032   GNUNET_GNS_lookup_zone (gns_handle,
1033                           ctask->host,
1034                           &local_gns_zone,
1035                           GNUNET_GNS_RECORD_LEHO,
1036                           &process_leho_lookup,
1037                           ctask);
1038 }
1039
1040 /**
1041  * Main MHD callback for handling requests.
1042  *
1043  * @param cls unused
1044  * @param con MHD connection handle
1045  * @param meth the HTTP method used ("GET", "PUT", etc.)
1046  * @param ver the HTTP version string (i.e. "HTTP/1.1")
1047  * @param upload_data the data being uploaded (excluding HEADERS,
1048  *        for a POST that fits into memory and that is encoded
1049  *        with a supported encoding, the POST data will NOT be
1050  *        given in upload_data and is instead available as
1051  *        part of MHD_get_connection_values; very large POST
1052  *        data *will* be made available incrementally in
1053  *        upload_data)
1054  * @param upload_data_size set initially to the size of the
1055  *        upload_data provided; the method must update this
1056  *        value to the number of bytes NOT processed;
1057  * @param con_cls pointer to location where we store the 'struct Request'
1058  * @return MHD_YES if the connection was handled successfully,
1059  *         MHD_NO if the socket must be closed due to a serious
1060  *         error while handling the request
1061  */
1062 static int
1063 create_response (void *cls,
1064                  struct MHD_Connection *con,
1065                  const char *url,
1066                  const char *meth,
1067                  const char *ver,
1068                  const char *upload_data,
1069                  size_t *upload_data_size,
1070                  void **con_cls)
1071 {
1072   static int dummy;
1073   struct MhdHttpList* hd = cls;
1074   const char* page = "<html><head><title>gnoxy</title>"\
1075                       "</head><body>cURL fail</body></html>";
1076   struct MHD_Response *response;
1077   char host[265];
1078   char curlurl[512];
1079   int ret = MHD_YES;
1080
1081   struct ProxyCurlTask *ctask;
1082   
1083   if (0 != strcmp (meth, "GET"))
1084     return MHD_NO;
1085   if (&dummy != *con_cls)
1086   {
1087     *con_cls = &dummy;
1088     return MHD_YES;
1089   }
1090
1091   if (0 != *upload_data_size)
1092     return MHD_NO;
1093
1094   *con_cls = NULL;
1095
1096   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1097               "url %s\n", url);
1098
1099   MHD_get_connection_values (con,
1100                              MHD_HEADER_KIND,
1101                              &con_val_iter, host);
1102
1103   
1104   /* Do cURL */
1105   ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
1106   ctask->mhd = hd;
1107   ctask->curl = curl_easy_init();
1108
1109   if (curl_multi == NULL)
1110     curl_multi = curl_multi_init ();
1111   
1112   if ((ctask->curl == NULL) || (curl_multi == NULL))
1113   {
1114     response = MHD_create_response_from_buffer (strlen (page),
1115                                               (void*)page,
1116                                               MHD_RESPMEM_PERSISTENT);
1117     ret = MHD_queue_response (con,
1118                               MHD_HTTP_OK,
1119                               response);
1120     MHD_destroy_response (response);
1121     GNUNET_free (ctask);
1122     return ret;
1123   }
1124
1125   ctask->download_in_progress = GNUNET_YES;
1126   ctask->download_successful = GNUNET_NO;
1127   ctask->buf_status = BUF_WAIT_FOR_CURL;
1128   ctask->bytes_in_buffer = 0;
1129   ctask->parse_content = GNUNET_NO;
1130
1131   curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1132   curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
1133   curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &callback_download);
1134   curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
1135   curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 1);
1136   curl_easy_setopt (ctask->curl, CURLOPT_MAXREDIRS, 4);
1137   /* no need to abort if the above failed */
1138   if (GNUNET_NO == ctask->mhd->is_ssl)
1139     sprintf (curlurl, "http://%s%s", host, url);
1140   strcpy (ctask->host, host);
1141   strcpy (ctask->url, url);
1142   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1143               "Adding new curl task for %s\n", curlurl);
1144   
1145   curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1146   curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
1147   curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
1148   curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
1149
1150   GNUNET_GNS_get_authority (gns_handle,
1151                             ctask->host,
1152                             &process_get_authority,
1153                             ctask);
1154   //download_prepare (ctask);
1155   //curl_download_prepare ();
1156
1157   response = MHD_create_response_from_callback (-1, -1,
1158                                                 &mhd_content_cb,
1159                                                 ctask,
1160                                                 &mhd_content_free);
1161   
1162   ret = MHD_queue_response (con, MHD_HTTP_OK, response);
1163   
1164   //MHD_destroy_response (response);
1165
1166   return ret;
1167 }
1168
1169 /**
1170  * Task run whenever HTTP server operations are pending.
1171  *
1172  * @param cls unused
1173  * @param tc sched context
1174  */
1175 static void
1176 do_httpd (void *cls,
1177           const struct GNUNET_SCHEDULER_TaskContext *tc);
1178
1179
1180 /**
1181  * run all httpd
1182  */
1183 static void
1184 run_httpds ()
1185 {
1186   struct MhdHttpList *hd;
1187
1188   for (hd=mhd_httpd_head; hd != NULL; hd = hd->next)
1189     run_httpd (hd);
1190
1191 }
1192
1193 /**
1194  * schedule mhd
1195  */
1196 static void
1197 run_httpd (struct MhdHttpList *hd)
1198 {
1199   fd_set rs;
1200   fd_set ws;
1201   fd_set es;
1202   struct GNUNET_NETWORK_FDSet *wrs;
1203   struct GNUNET_NETWORK_FDSet *wws;
1204   struct GNUNET_NETWORK_FDSet *wes;
1205   int max;
1206   int haveto;
1207   unsigned MHD_LONG_LONG timeout;
1208   struct GNUNET_TIME_Relative tv;
1209
1210   FD_ZERO (&rs);
1211   FD_ZERO (&ws);
1212   FD_ZERO (&es);
1213   wrs = GNUNET_NETWORK_fdset_create ();
1214   wes = GNUNET_NETWORK_fdset_create ();
1215   wws = GNUNET_NETWORK_fdset_create ();
1216   max = -1;
1217   GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
1218   
1219   
1220   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1221               "MHD fds: max=%d\n", max);
1222   
1223   haveto = MHD_get_timeout (hd->daemon, &timeout);
1224
1225   if (haveto == MHD_YES)
1226     tv.rel_value = (uint64_t) timeout;
1227   else
1228     tv = GNUNET_TIME_UNIT_FOREVER_REL;
1229   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
1230   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
1231   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
1232   
1233   if (hd->httpd_task != GNUNET_SCHEDULER_NO_TASK)
1234     GNUNET_SCHEDULER_cancel (hd->httpd_task);
1235   hd->httpd_task =
1236     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
1237                                  tv, wrs, wws,
1238                                  &do_httpd, hd);
1239   GNUNET_NETWORK_fdset_destroy (wrs);
1240   GNUNET_NETWORK_fdset_destroy (wws);
1241   GNUNET_NETWORK_fdset_destroy (wes);
1242 }
1243
1244
1245 /**
1246  * Task run whenever HTTP server operations are pending.
1247  *
1248  * @param cls unused
1249  * @param tc sched context
1250  */
1251 static void
1252 do_httpd (void *cls,
1253           const struct GNUNET_SCHEDULER_TaskContext *tc)
1254 {
1255   struct MhdHttpList *hd = cls;
1256   
1257   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
1258   
1259   MHD_run (hd->daemon);
1260   run_httpd (hd);
1261 }
1262
1263
1264 /**
1265  * Read data from socket
1266  *
1267  * @param cls the closure
1268  * @param tc scheduler context
1269  */
1270 static void
1271 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1272
1273 /**
1274  * Read from remote end
1275  *
1276  * @param cls closure
1277  * @param tc scheduler context
1278  */
1279 static void
1280 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1281
1282 /**
1283  * Write data to remote socket
1284  *
1285  * @param cls the closure
1286  * @param tc scheduler context
1287  */
1288 static void
1289 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1290 {
1291   struct Socks5Request *s5r = cls;
1292   unsigned int len;
1293
1294   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
1295
1296   if ((NULL != tc->read_ready) &&
1297       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
1298       ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
1299                                          s5r->rbuf_len)>0)))
1300   {
1301     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1302                 "Successfully sent %d bytes to remote socket\n",
1303                 len);
1304   }
1305   else
1306   {
1307     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
1308     //Really!?!?!?
1309     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1310       GNUNET_SCHEDULER_cancel (s5r->rtask);
1311     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
1312       GNUNET_SCHEDULER_cancel (s5r->wtask);
1313     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1314       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1315     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1316     GNUNET_NETWORK_socket_close (s5r->sock);
1317     GNUNET_free(s5r);
1318     return;
1319   }
1320
1321   s5r->rtask =
1322     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1323                                    s5r->sock,
1324                                    &do_read, s5r);
1325 }
1326
1327
1328 /**
1329  * Clean up s5r handles
1330  *
1331  * @param s5r the handle to destroy
1332  */
1333 static void
1334 cleanup_s5r (struct Socks5Request *s5r)
1335 {
1336   if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1337     GNUNET_SCHEDULER_cancel (s5r->rtask);
1338   if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1339     GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1340   if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1341     GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1342   
1343   if (NULL != s5r->remote_sock)
1344     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1345   if ((NULL != s5r->sock) && (s5r->cleanup_sock == GNUNET_YES))
1346     GNUNET_NETWORK_socket_close (s5r->sock);
1347   
1348   GNUNET_free(s5r);
1349 }
1350
1351 /**
1352  * Write data to socket
1353  *
1354  * @param cls the closure
1355  * @param tc scheduler context
1356  */
1357 static void
1358 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1359 {
1360   struct Socks5Request *s5r = cls;
1361   unsigned int len;
1362
1363   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
1364
1365   if ((NULL != tc->read_ready) &&
1366       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
1367       ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
1368                                          s5r->wbuf_len)>0)))
1369   {
1370     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1371                 "Successfully sent %d bytes to socket\n",
1372                 len);
1373   }
1374   else
1375   {
1376     
1377     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
1378     s5r->cleanup = GNUNET_YES;
1379     s5r->cleanup_sock = GNUNET_YES;
1380     cleanup_s5r (s5r);
1381     
1382     return;
1383   }
1384
1385   if (GNUNET_YES == s5r->cleanup)
1386   {
1387     cleanup_s5r (s5r);
1388     return;
1389   }
1390
1391   if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
1392       (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
1393     s5r->fwdrtask =
1394       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1395                                      s5r->remote_sock,
1396                                      &do_read_remote, s5r);
1397 }
1398
1399 /**
1400  * Read from remote end
1401  *
1402  * @param cls closure
1403  * @param tc scheduler context
1404  */
1405 static void
1406 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1407 {
1408   struct Socks5Request *s5r = cls;
1409   
1410   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
1411
1412
1413   if ((NULL != tc->write_ready) &&
1414       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
1415       (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
1416                                          sizeof (s5r->wbuf))))
1417   {
1418     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1419                 "Successfully read %d bytes from remote socket\n",
1420                 s5r->wbuf_len);
1421   }
1422   else
1423   {
1424     if (s5r->wbuf_len == 0)
1425       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1426                   "0 bytes received from remote... graceful shutdown!\n");
1427     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1428       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1429     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1430       GNUNET_SCHEDULER_cancel (s5r->rtask);
1431     
1432     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1433     s5r->remote_sock = NULL;
1434     GNUNET_NETWORK_socket_close (s5r->sock);
1435     GNUNET_free(s5r);
1436
1437     return;
1438   }
1439   
1440   s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1441                                                s5r->sock,
1442                                                &do_write, s5r);
1443   
1444 }
1445
1446
1447 /**
1448  * Adds a socket to MHD
1449  *
1450  * @param h the handle to the socket to add
1451  * @return whatever MHD_add_connection returns
1452  */
1453 static int
1454 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon)
1455 {
1456   int fd;
1457   struct sockaddr *addr;
1458   socklen_t len;
1459
1460   fd = GNUNET_NETWORK_get_fd (h);
1461   addr = GNUNET_NETWORK_get_addr (h);
1462   len = GNUNET_NETWORK_get_addrlen (h);
1463
1464   return MHD_add_connection (daemon, fd, addr, len);
1465 }
1466
1467
1468 /*TODO this needs MHD API modification */
1469 static int http_port = 4444;
1470
1471
1472 static long
1473 get_file_size (const char* filename)
1474 {
1475   FILE *fp;
1476
1477   fp = fopen (filename, "rb");
1478   if (fp)
1479   {
1480     long size;
1481
1482     if ((0 != fseek (fp, 0, SEEK_END)) || (-1 == (size = ftell (fp))))
1483       size = 0;
1484
1485     fclose (fp);
1486
1487     return size;
1488   }
1489   
1490   return 0;
1491 }
1492
1493 /**
1494  * Read file in filename
1495  *
1496  * @param filename file to read
1497  * @return data
1498  */
1499 static char*
1500 load_file (const char* filename)
1501 {
1502   FILE *fp;
1503   char *buffer;
1504   long size;
1505
1506   size = get_file_size (filename);
1507   if (size == 0)
1508     return NULL;
1509
1510   fp = fopen (filename, "rb");
1511   if (!fp)
1512     return NULL;
1513
1514   buffer = GNUNET_malloc (size);
1515   if (!buffer)
1516   {
1517     fclose (fp);
1518     return NULL;
1519   }
1520
1521   if (size != fread (buffer, 1, size, fp))
1522   {
1523     GNUNET_free (buffer);
1524     buffer = NULL;
1525   }
1526
1527   fclose (fp);
1528   return buffer;
1529 }
1530
1531 /** SSL stuff **/
1532
1533 /**
1534  * Load PEM key from file
1535  *
1536  * @param key where to store the data
1537  * @param keyfile path to the PEM file
1538  */
1539 static void
1540 load_key_from_file (gnutls_x509_privkey_t key, char* keyfile)
1541 {
1542   gnutls_datum_t key_data;
1543   int ret;
1544
1545   key_data.data = (unsigned char*)load_file (keyfile);
1546   key_data.size = strlen ((char*)key_data.data);
1547
1548   ret = gnutls_x509_privkey_import (key, &key_data,
1549                                     GNUTLS_X509_FMT_PEM);
1550   
1551   if (GNUTLS_E_SUCCESS != ret)
1552   {
1553     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1554                 "Unable to import private key %s\n", keyfile);
1555     GNUNET_break (0);
1556   }
1557 }
1558
1559 /**
1560  * Load cert from file
1561  *
1562  * @param crt struct to store data in
1563  * @param certfile path to pem file
1564  */
1565 static void
1566 load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
1567 {
1568   gnutls_datum_t cert_data;
1569   int ret;
1570
1571   cert_data.data = (unsigned char*)load_file (certfile);
1572   cert_data.size = strlen ((char*)cert_data.data);
1573
1574   ret = gnutls_x509_crt_import (crt, &cert_data,
1575                                  GNUTLS_X509_FMT_PEM);
1576   if (GNUTLS_E_SUCCESS != ret)
1577   {
1578     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1579                 "Unable to import certificate %s\n", certfile);
1580     GNUNET_break (0);
1581   }
1582
1583 }
1584
1585
1586 /**
1587  * Generate new certificate for specific name
1588  *
1589  * @param name the subject name to generate a cert for
1590  * @return a struct holding the PEM data
1591  */
1592 static struct ProxyGNSCertificate *
1593 generate_gns_certificate (const char *name)
1594 {
1595
1596   int ret;
1597   unsigned int serial;
1598   unsigned int bits;
1599   size_t key_buf_size;
1600   size_t cert_buf_size;
1601   gnutls_x509_crt_t request;
1602   gnutls_x509_privkey_t rsa;
1603   time_t etime;
1604   struct tm *tm_data;
1605
1606   ret = gnutls_x509_crt_init (&request);
1607
1608   if (GNUTLS_E_SUCCESS != ret)
1609   {
1610     GNUNET_break (0);
1611   }
1612
1613   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating key\n");
1614   gnutls_x509_privkey_init (&rsa);
1615   bits = gnutls_sec_param_to_pk_bits (GNUTLS_PK_RSA, GNUTLS_SEC_PARAM_NORMAL);
1616   ret = gnutls_x509_privkey_generate (rsa, GNUTLS_PK_RSA, bits, 0);
1617
1618   if (GNUTLS_E_SUCCESS != ret)
1619   {
1620     GNUNET_break (0);
1621   }
1622
1623   ret = gnutls_x509_crt_set_key (request, rsa);
1624
1625   if (GNUTLS_E_SUCCESS != ret)
1626   {
1627     GNUNET_break (0);
1628   }
1629
1630   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
1631
1632   struct ProxyGNSCertificate *pgc =
1633     GNUNET_malloc (sizeof (struct ProxyGNSCertificate));
1634
1635   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\n");
1636   
1637   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
1638                                  0, "DE", 2);
1639
1640   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
1641                                  0, "GNUnet", 6);
1642
1643   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
1644                                  0, name, strlen (name));
1645
1646   ret = gnutls_x509_crt_set_version (request, 3);
1647
1648   ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
1649
1650   etime = time (NULL);
1651   tm_data = localtime (&etime);
1652   
1653
1654   ret = gnutls_x509_crt_set_serial (request,
1655                                     &serial,
1656                                     sizeof (serial));
1657
1658   ret = gnutls_x509_crt_set_activation_time (request,
1659                                              etime);
1660   tm_data->tm_year++;
1661   etime = mktime (tm_data);
1662
1663   if (-1 == etime)
1664   {
1665     GNUNET_break (0);
1666   }
1667
1668   ret = gnutls_x509_crt_set_expiration_time (request,
1669                                              etime);
1670   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
1671
1672   ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
1673
1674   key_buf_size = sizeof (pgc->key);
1675   cert_buf_size = sizeof (pgc->cert);
1676   
1677   gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
1678                           pgc->cert, &cert_buf_size);
1679
1680   gnutls_x509_privkey_export (rsa, GNUTLS_X509_FMT_PEM,
1681                           pgc->key, &key_buf_size);
1682
1683
1684   gnutls_x509_crt_deinit (request);
1685   gnutls_x509_privkey_deinit (rsa);
1686
1687   return pgc;
1688
1689 }
1690
1691
1692 /**
1693  * Adds a socket to an SSL MHD instance
1694  * It is important the the domain name is
1695  * correct. In most cases we need to start a new daemon
1696  */
1697 static int
1698 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, char* domain)
1699 {
1700   struct MhdHttpList *hd = NULL;
1701   struct ProxyGNSCertificate *pgc;
1702
1703   for (hd = mhd_httpd_head; hd != NULL; hd = hd->next)
1704   {
1705     if (0 == strcmp (hd->domain, domain))
1706       break;
1707   }
1708
1709   if (NULL == hd)
1710   {
1711     /* Start new MHD */
1712     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1713                 "No previous SSL instance found... starting new one for %s\n",
1714                 domain);
1715     
1716     pgc = generate_gns_certificate (domain);
1717     
1718     hd = GNUNET_malloc (sizeof (struct MhdHttpList));
1719     hd->is_ssl = GNUNET_YES;
1720     strcpy (hd->domain, domain);
1721     hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL, http_port++,
1722                               NULL, NULL,
1723                               &create_response, hd,
1724                               MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
1725                               MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
1726                               MHD_OPTION_NOTIFY_COMPLETED,
1727                               NULL, NULL,
1728                               MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
1729                               MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
1730                               MHD_OPTION_END);
1731     hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
1732     
1733     GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
1734   }
1735   
1736   return add_handle_to_mhd (h, hd->daemon);
1737 }
1738
1739
1740
1741 /**
1742  * Read data from incoming connection
1743  *
1744  * @param cls the closure
1745  * @param tc the scheduler context
1746  */
1747 static void
1748 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1749 {
1750   struct Socks5Request *s5r = cls;
1751   struct socks5_client_hello *c_hello;
1752   struct socks5_server_hello *s_hello;
1753   struct socks5_client_request *c_req;
1754   struct socks5_server_response *s_resp;
1755
1756   int ret;
1757   char domain[256];
1758   uint8_t dom_len;
1759   uint16_t req_port;
1760   struct hostent *phost;
1761   uint32_t remote_ip;
1762   struct sockaddr_in remote_addr;
1763   struct in_addr *r_sin_addr;
1764
1765   s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
1766
1767   if ((NULL != tc->write_ready) &&
1768       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
1769       (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
1770                                          sizeof (s5r->rbuf))))
1771   {
1772     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1773                 "Successfully read %d bytes from socket\n",
1774                 s5r->rbuf_len);
1775   }
1776   else
1777   {
1778     if (s5r->rbuf_len != 0)
1779       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
1780     else
1781       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
1782
1783     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1784       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1785     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
1786       GNUNET_SCHEDULER_cancel (s5r->wtask);
1787     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1788       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1789     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1790     GNUNET_NETWORK_socket_close (s5r->sock);
1791     GNUNET_free(s5r);
1792     return;
1793   }
1794
1795   if (s5r->state == SOCKS5_INIT)
1796   {
1797     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1798                 "SOCKS5 init\n");
1799     c_hello = (struct socks5_client_hello*)&s5r->rbuf;
1800
1801     GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
1802
1803     s_hello = (struct socks5_server_hello*)&s5r->wbuf;
1804     s5r->wbuf_len = sizeof( struct socks5_server_hello );
1805
1806     s_hello->version = c_hello->version;
1807     s_hello->auth_method = SOCKS_AUTH_NONE;
1808
1809     /* Write response to client */
1810     s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1811                                                 s5r->sock,
1812                                                 &do_write, s5r);
1813
1814     s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1815                                                 s5r->sock,
1816                                                 &do_read, s5r);
1817
1818     s5r->state = SOCKS5_REQUEST;
1819     return;
1820   }
1821
1822   if (s5r->state == SOCKS5_REQUEST)
1823   {
1824     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1825                 "Processing SOCKS5 request\n");
1826     c_req = (struct socks5_client_request*)&s5r->rbuf;
1827     s_resp = (struct socks5_server_response*)&s5r->wbuf;
1828     //Only 10byte for ipv4 response!
1829     s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
1830
1831     GNUNET_assert (c_req->addr_type == 3);
1832
1833     dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
1834     memset(domain, 0, sizeof(domain));
1835     strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
1836     req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
1837
1838     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1839                 "Requested connection is %s:%d\n",
1840                 domain,
1841                 ntohs(req_port));
1842
1843     if (is_tld (domain, GNUNET_GNS_TLD) ||
1844         is_tld (domain, GNUNET_GNS_TLD_ZKEY))
1845     {
1846       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1847                   "Requested connection is gnunet tld\n",
1848                   domain);
1849       
1850       ret = MHD_NO;
1851       if (ntohs(req_port) == HTTPS_PORT)
1852       {
1853         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1854                     "Requested connection is HTTPS\n");
1855         ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
1856       }
1857       else if (NULL != httpd)
1858       {
1859         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1860                     "Requested connection is HTTP\n");
1861         ret = add_handle_to_mhd ( s5r->sock, httpd );
1862       }
1863
1864       if (ret != MHD_YES)
1865       {
1866         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1867                     _("Failed to start HTTP server\n"));
1868         s_resp->version = 0x05;
1869         s_resp->reply = 0x01;
1870         s5r->cleanup = GNUNET_YES;
1871         s5r->cleanup_sock = GNUNET_YES;
1872         s5r->wtask = 
1873           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1874                                         s5r->sock,
1875                                         &do_write, s5r);
1876         return;
1877       }
1878       
1879       /* Signal success */
1880       s_resp->version = 0x05;
1881       s_resp->reply = 0x00;
1882       s_resp->reserved = 0x00;
1883       s_resp->addr_type = 0x01;
1884       
1885       s5r->cleanup = GNUNET_YES;
1886       s5r->cleanup_sock = GNUNET_NO;
1887       s5r->wtask =
1888         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1889                                         s5r->sock,
1890                                         &do_write, s5r);
1891       run_httpds ();
1892       return;
1893     }
1894     else
1895     {
1896       phost = (struct hostent*)gethostbyname (domain);
1897       if (phost == NULL)
1898       {
1899         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1900                     "Resolve %s error!\n", domain );
1901         s_resp->version = 0x05;
1902         s_resp->reply = 0x01;
1903         s5r->cleanup = GNUNET_YES;
1904         s5r->cleanup_sock = GNUNET_YES;
1905         s5r->wtask = 
1906           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1907                                           s5r->sock,
1908                                           &do_write, s5r);
1909         return;
1910       }
1911
1912       s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
1913                                                        SOCK_STREAM,
1914                                                        0);
1915       r_sin_addr = (struct in_addr*)(phost->h_addr);
1916       remote_ip = r_sin_addr->s_addr;
1917       memset(&remote_addr, 0, sizeof(remote_addr));
1918       remote_addr.sin_family = AF_INET;
1919 #if HAVE_SOCKADDR_IN_SIN_LEN
1920       remote_addr.sin_len = sizeof (remote_addr);
1921 #endif
1922       remote_addr.sin_addr.s_addr = remote_ip;
1923       remote_addr.sin_port = req_port;
1924       
1925       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1926                   "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
1927                   ntohs(req_port));
1928
1929       if ((GNUNET_OK !=
1930           GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
1931                                           (const struct sockaddr*)&remote_addr,
1932                                           sizeof (remote_addr)))
1933           && (errno != EINPROGRESS))
1934       {
1935         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
1936         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1937                     "socket request error...\n");
1938         s_resp->version = 0x05;
1939         s_resp->reply = 0x01;
1940         s5r->wtask =
1941           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1942                                           s5r->sock,
1943                                           &do_write, s5r);
1944         //TODO see above
1945         return;
1946       }
1947
1948       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1949                   "new remote connection\n");
1950
1951       s_resp->version = 0x05;
1952       s_resp->reply = 0x00;
1953       s_resp->reserved = 0x00;
1954       s_resp->addr_type = 0x01;
1955
1956       s5r->state = SOCKS5_DATA_TRANSFER;
1957
1958       s5r->wtask =
1959         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1960                                         s5r->sock,
1961                                         &do_write, s5r);
1962       s5r->rtask =
1963         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1964                                        s5r->sock,
1965                                        &do_read, s5r);
1966
1967     }
1968     return;
1969   }
1970
1971   if (s5r->state == SOCKS5_DATA_TRANSFER)
1972   {
1973     if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
1974     {
1975       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1976                   "Closing connection to client\n");
1977       if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1978         GNUNET_SCHEDULER_cancel (s5r->rtask);
1979       if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1980         GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1981       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1982         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1983       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1984         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1985       
1986       if (s5r->remote_sock != NULL)
1987         GNUNET_NETWORK_socket_close (s5r->remote_sock);
1988       GNUNET_NETWORK_socket_close (s5r->sock);
1989       GNUNET_free(s5r);
1990       return;
1991     }
1992
1993     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1994                 "forwarding %d bytes from client\n", s5r->rbuf_len);
1995
1996     s5r->fwdwtask =
1997       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1998                                       s5r->remote_sock,
1999                                       &do_write_remote, s5r);
2000
2001     if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
2002     {
2003       s5r->fwdrtask =
2004         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2005                                        s5r->remote_sock,
2006                                        &do_read_remote, s5r);
2007     }
2008
2009
2010   }
2011
2012   //GNUNET_CONTAINER_DLL_remove (s5conns.head, s5conns.tail, s5r);
2013
2014 }
2015
2016
2017 /**
2018  * Accept new incoming connections
2019  *
2020  * @param cls the closure
2021  * @param tc the scheduler context
2022  */
2023 static void
2024 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2025 {
2026   struct GNUNET_NETWORK_Handle *s;
2027   struct Socks5Request *s5r;
2028
2029   ltask = GNUNET_SCHEDULER_NO_TASK;
2030   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
2031     return;
2032
2033   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2034                                          lsock,
2035                                          &do_accept, NULL);
2036
2037   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
2038
2039   if (NULL == s)
2040   {
2041     GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "accept");
2042     return;
2043   }
2044
2045   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2046               "Got an inbound connection, waiting for data\n");
2047
2048   s5r = GNUNET_malloc (sizeof (struct Socks5Request));
2049   s5r->sock = s;
2050   s5r->state = SOCKS5_INIT;
2051   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2052   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2053   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2054   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2055                                               s5r->sock,
2056                                               &do_read, s5r);
2057   //GNUNET_CONTAINER_DLL_insert (s5conns.head, s5conns.tail, s5r);
2058 }
2059
2060
2061 /**
2062  * Task run on shutdown
2063  *
2064  * @param cls closure
2065  * @param tc task context
2066  */
2067 static void
2068 do_shutdown (void *cls,
2069              const struct GNUNET_SCHEDULER_TaskContext *tc)
2070 {
2071
2072   struct MhdHttpList *hd;
2073   struct MhdHttpList *tmp_hd;
2074   struct ProxyCurlTask *ctask;
2075   struct ProxyCurlTask *ctask_tmp;
2076
2077   if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
2078   {
2079     GNUNET_SCHEDULER_cancel (curl_download_task);
2080     curl_download_task = GNUNET_SCHEDULER_NO_TASK;
2081   }
2082
2083   for (hd = mhd_httpd_head; hd != NULL; hd = tmp_hd)
2084   {
2085     tmp_hd = hd->next;
2086
2087     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2088                 "Stopping daemon\n");
2089
2090     if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2091     {
2092       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2093                   "Stopping select task %d\n",
2094                   hd->httpd_task);
2095       GNUNET_SCHEDULER_cancel (hd->httpd_task);
2096       hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2097     }
2098
2099     if (NULL != hd->daemon)
2100     {
2101       MHD_stop_daemon (hd->daemon);
2102       hd->daemon = NULL;
2103     }
2104
2105     GNUNET_free (hd);
2106   }
2107
2108   for (ctask=ctasks_head; ctask != NULL; ctask=ctask_tmp)
2109   {
2110     ctask_tmp = ctask->next;
2111
2112     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2113                 "Cleaning up cURL task\n");
2114
2115     if (ctask->curl != NULL)
2116       curl_easy_cleanup (ctask->curl);
2117     ctask->curl = NULL;
2118     if (NULL != ctask->headers)
2119       curl_slist_free_all (ctask->headers);
2120
2121     GNUNET_free (ctask);
2122   }
2123
2124   GNUNET_GNS_disconnect (gns_handle);
2125 }
2126
2127
2128 /**
2129  * Compiles a regex for us
2130  *
2131  * @param re ptr to re struct
2132  * @param rt the expression to compile
2133  * @return 0 on success
2134  */
2135 static int
2136 compile_regex (regex_t *re, const char* rt)
2137 {
2138   int status;
2139   char err[1024];
2140
2141   status = regcomp (re, rt, REG_EXTENDED|REG_NEWLINE);
2142   if (status)
2143   {
2144     regerror (status, re, err, 1024);
2145     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2146                 "Regex error compiling '%s': %s\n", rt, err);
2147     return 1;
2148   }
2149   return 0;
2150 }
2151
2152
2153 /**
2154  * Loads the users local zone key
2155  *
2156  * @return GNUNET_YES on success
2157  */
2158 static int
2159 load_local_zone_key (const struct GNUNET_CONFIGURATION_Handle *cfg)
2160 {
2161   char *keyfile;
2162   struct GNUNET_CRYPTO_RsaPrivateKey *key = NULL;
2163   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
2164   struct GNUNET_CRYPTO_ShortHashCode *zone = NULL;
2165   struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename;
2166
2167   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
2168                                                             "ZONEKEY", &keyfile))
2169   {
2170     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2171                 "Unable to load zone key config value!\n");
2172     return GNUNET_NO;
2173   }
2174
2175   if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
2176   {
2177     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2178                 "Unable to load zone key!\n");
2179     GNUNET_free(keyfile);
2180     return GNUNET_NO;
2181   }
2182
2183   key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
2184   GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
2185   GNUNET_CRYPTO_short_hash(&pkey,
2186                            sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
2187                            &local_gns_zone);
2188   zone = &local_gns_zone;
2189   GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
2190   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2191               "Using zone: %s!\n", &zonename);
2192   GNUNET_CRYPTO_rsa_key_free(key);
2193   GNUNET_free(keyfile);
2194
2195   return GNUNET_YES;
2196 }
2197
2198
2199
2200 /**
2201  * Main function that will be run
2202  *
2203  * @param cls closure
2204  * @param args remaining command-line arguments
2205  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
2206  * @param cfg configuration
2207  */
2208 static void
2209 run (void *cls, char *const *args, const char *cfgfile,
2210      const struct GNUNET_CONFIGURATION_Handle *cfg)
2211 {
2212   struct sockaddr_in sa;
2213   struct MhdHttpList *hd;
2214
2215   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2216               "Loading CA\n");
2217
2218   gnutls_global_init ();
2219   
2220   load_cert_from_file (proxy_ca.cert, cafile);
2221   load_key_from_file (proxy_ca.key, cafile);
2222   
2223   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2224               "Loading Template\n");
2225
2226   compile_regex (&re_dotplus, (char*) RE_DOTPLUS);
2227
2228   gns_handle = GNUNET_GNS_connect (cfg);
2229
2230   if (GNUNET_NO == load_local_zone_key (cfg))
2231   {
2232     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2233                 "Unable to load zone!\n");
2234     return;
2235   }
2236
2237   if (NULL == gns_handle)
2238   {
2239     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2240                 "Unable to connect to GNS!\n");
2241     return;
2242   }
2243
2244   memset (&sa, 0, sizeof (sa));
2245   sa.sin_family = AF_INET;
2246   sa.sin_port = htons (port);
2247 #if HAVE_SOCKADDR_IN_SIN_LEN
2248   sa.sin_len = sizeof (sa);
2249 #endif
2250
2251   lsock = GNUNET_NETWORK_socket_create (AF_INET,
2252                                         SOCK_STREAM,
2253                                         0);
2254
2255   if ((NULL == lsock) ||
2256       (GNUNET_OK !=
2257        GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
2258                                    sizeof (sa))))
2259   {
2260     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2261                 "Failed to create listen socket bound to `%s'",
2262                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
2263     if (NULL != lsock)
2264       GNUNET_NETWORK_socket_close (lsock);
2265     return;
2266   }
2267
2268   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
2269   {
2270     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2271                 "Failed to listen on socket bound to `%s'",
2272                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
2273     return;
2274   }
2275
2276   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2277                                          lsock, &do_accept, NULL);
2278
2279   ctasks_head = NULL;
2280   ctasks_tail = NULL;
2281
2282   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
2283   {
2284     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2285                 "cURL global init failed!\n");
2286     return;
2287   }
2288
2289   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2290               "Proxy listens on port %u\n",
2291               port);
2292
2293   mhd_httpd_head = NULL;
2294   mhd_httpd_tail = NULL;
2295   
2296   hd = GNUNET_malloc (sizeof (struct MhdHttpList));
2297   hd->is_ssl = GNUNET_NO;
2298   strcpy (hd->domain, "");
2299   httpd = MHD_start_daemon (MHD_USE_DEBUG, http_port++,
2300                                NULL, NULL,
2301                                &create_response, hd,
2302                                MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
2303                                MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2304                                MHD_OPTION_NOTIFY_COMPLETED,
2305                                NULL, NULL,
2306                                MHD_OPTION_END);
2307   hd->daemon = httpd;
2308   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2309
2310   GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2311
2312   run_httpds ();
2313
2314   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
2315                                 &do_shutdown, NULL);
2316
2317 }
2318
2319
2320 /**
2321  * The main function for gnunet-gns-proxy.
2322  *
2323  * @param argc number of arguments from the command line
2324  * @param argv command line arguments
2325  * @return 0 ok, 1 on error
2326  */
2327 int
2328 main (int argc, char *const *argv)
2329 {
2330   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
2331     {'p', "port", NULL,
2332      gettext_noop ("listen on specified port"), 1,
2333      &GNUNET_GETOPT_set_string, &port},
2334     {'a', "authority", NULL,
2335       gettext_noop ("pem file to use as CA"), 1,
2336       &GNUNET_GETOPT_set_string, &cafile},
2337     GNUNET_GETOPT_OPTION_END
2338   };
2339
2340   int ret;
2341
2342   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
2343     return 2;
2344
2345   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
2346   ret =
2347       (GNUNET_OK ==
2348        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
2349                            _("GNUnet GNS proxy"),
2350                            options,
2351                            &run, NULL)) ? 0 : 1;
2352   return ret;
2353 }