-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  * Callback for MHD response
525  *
526  * @param cls closure
527  * @param pos in buffer
528  * @param buf buffer
529  * @param max space in buffer
530  */
531 static ssize_t
532 mhd_content_cb (void *cls,
533                 uint64_t pos,
534                 char* buf,
535                 size_t max)
536 {
537   struct ProxyCurlTask *ctask = cls;
538   ssize_t copied = 0;
539   size_t bytes_to_copy;
540   int nomatch;
541   char *hostptr;
542   regmatch_t m[RE_N_MATCHES];
543
544   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
545               "MHD: content cb\n");
546
547   if (ctask->download_successful &&
548       (ctask->buf_status == BUF_WAIT_FOR_CURL))
549   {
550     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
551                 "MHD: sending response for %s\n", ctask->url);
552     ctask->download_in_progress = GNUNET_NO;
553     curl_multi_remove_handle (curl_multi, ctask->curl);
554     curl_easy_cleanup (ctask->curl);
555     GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
556     return MHD_CONTENT_READER_END_OF_STREAM;
557   }
558   
559   if (ctask->download_error &&
560       (ctask->buf_status == BUF_WAIT_FOR_CURL))
561   {
562     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
563                 "MHD: sending error response\n");
564     ctask->download_in_progress = GNUNET_NO;
565     curl_multi_remove_handle (curl_multi, ctask->curl);
566     curl_easy_cleanup (ctask->curl);
567     GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
568     return MHD_CONTENT_READER_END_WITH_ERROR;
569   }
570
571   if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
572     return 0;
573
574   bytes_to_copy = ctask->bytes_in_buffer;
575   
576   if (ctask->parse_content == GNUNET_YES)
577   {
578
579     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
580                  "MHD: We need to parse the HTML %s\n", ctask->buffer_ptr);
581
582     nomatch = regexec ( &re_dotplus, ctask->buffer_ptr, RE_N_MATCHES, m, 0);
583
584     if (!nomatch)
585     {
586       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
587                   "MHD RE: Match\n");
588
589       GNUNET_assert (m[1].rm_so != -1);
590
591       hostptr = ctask->buffer_ptr+m[1].rm_so;
592
593       if (m[0].rm_so > 0)
594       {
595         bytes_to_copy = m[0].rm_so;
596         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
597                     "Copying %d bytes.\n", m[0].rm_so);
598
599
600       }
601       else
602       {
603         if (ctask->is_postprocessing == GNUNET_YES)
604         {
605           
606           /*Done?*/
607           if ( ctask->pp_finished == GNUNET_NO )
608           {
609             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
610                         "MHD PP: Waiting for PP of %s\n", ctask->pp_buf);
611             return 0;
612           }
613           
614           ctask->is_postprocessing = GNUNET_NO;
615
616           ctask->bytes_in_buffer -= m[0].rm_eo;//(m[1].rm_eo-m[1].rm_so);
617           ctask->buffer_ptr += m[0].rm_eo;//(m[1].rm_eo-m[1].rm_so);
618           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
619                       "Skipping next %d bytes in buffer\n", m[0].rm_eo);
620
621           GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
622
623           if ( strlen (ctask->pp_buf) <= max )
624           {
625             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
626                         "Copying postprocessed %s.\n", ctask->pp_buf);
627             memcpy ( buf, ctask->pp_buf, strlen (ctask->pp_buf) );
628             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
629                         "Done %s.\n", buf);
630             ctask->is_postprocessing = GNUNET_NO;
631             return strlen (ctask->pp_buf);
632           }
633           
634           return 0;
635         }
636
637         memset (ctask->pp_buf, 0, sizeof(ctask->pp_buf));
638         
639         /* If .+ extend with authority */
640         if (0 == strcmp (ctask->buffer_ptr+m[1].rm_eo, "+"))
641         {
642            memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so));
643            strcpy ( ctask->pp_buf+strlen(ctask->pp_buf),
644                     ctask->authority);
645         }
646         /* If .zkey simply copy the name */
647         else
648         {
649           memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so + strlen (GNUNET_TLD_ZKEY)));
650         }
651
652         ctask->is_postprocessing = GNUNET_YES;
653         ctask->pp_finished = GNUNET_NO;
654         
655         GNUNET_GNS_shorten (gns_handle,
656                            ctask->pp_buf,
657                            &process_shorten,
658                            ctask);
659
660         //postprocess_name(ctask, NULL);
661         //ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_name, ctask);
662
663         return 0;
664       }
665     }
666   }
667
668   if ( bytes_to_copy > max )
669   {
670     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
671                  "MHD: buffer in response too small! (%s)\n",
672                  ctask->url);
673     memcpy ( buf, ctask->buffer_ptr, max);
674     ctask->bytes_in_buffer -= max;
675     ctask->buffer_ptr += max;
676     copied = max;
677   }
678   else
679   {
680     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
681                  "MHD: copying %d bytes to mhd response at offset %d\n",
682                  bytes_to_copy, pos);
683
684     memcpy ( buf, ctask->buffer_ptr, bytes_to_copy );
685     copied = bytes_to_copy;
686     if (bytes_to_copy < ctask->bytes_in_buffer)
687     {
688       ctask->bytes_in_buffer -= bytes_to_copy;
689       ctask->buffer_ptr += bytes_to_copy;
690     }
691     else
692     {
693       ctask->bytes_in_buffer = 0;
694       ctask->buf_status = BUF_WAIT_FOR_CURL;
695       ctask->buffer_ptr = ctask->buffer;
696       curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
697       GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
698     }
699   }
700
701   GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
702
703   return copied;
704 }
705
706
707
708 /**
709  * Task that is run when we are ready to receive more data
710  * from curl
711  *
712  * @param cls closure
713  * @param tc task context
714  */
715 static void
716 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
717
718 /**
719  * Ask cURL for the select sets and schedule download
720  */
721 static void
722 curl_download_prepare ()
723 {
724   CURLMcode mret;
725   fd_set rs;
726   fd_set ws;
727   fd_set es;
728   int max;
729   struct GNUNET_NETWORK_FDSet *grs;
730   struct GNUNET_NETWORK_FDSet *gws;
731   long to;
732   struct GNUNET_TIME_Relative rtime;
733
734   max = -1;
735   FD_ZERO (&rs);
736   FD_ZERO (&ws);
737   FD_ZERO (&es);
738   mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
739
740   if (mret != CURLM_OK)
741   {
742     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
743                 "%s failed at %s:%d: `%s'\n",
744                 "curl_multi_fdset", __FILE__, __LINE__,
745                 curl_multi_strerror (mret));
746     //TODO cleanup here?
747     return;
748   }
749
750   mret = curl_multi_timeout (curl_multi, &to);
751   rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
752
753   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
754               "cURL multi fds: max=%d timeout=%llu\n", max, to);
755
756   grs = GNUNET_NETWORK_fdset_create ();
757   gws = GNUNET_NETWORK_fdset_create ();
758   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
759   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
760   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
761               "Scheduling task cURL\n");
762
763   if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
764     GNUNET_SCHEDULER_cancel (curl_download_task);
765   
766   curl_download_task =
767     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
768                                  rtime,
769                                  grs, gws,
770                                  &curl_task_download, curl_multi);
771   GNUNET_NETWORK_fdset_destroy (gws);
772   GNUNET_NETWORK_fdset_destroy (grs);
773
774 }
775
776
777 /**
778  * Task that is run when we are ready to receive more data
779  * from curl
780  *
781  * @param cls closure
782  * @param tc task context
783  */
784 static void
785 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
786 {
787   int running;
788   int msgnum;
789   struct CURLMsg *msg;
790   CURLMcode mret;
791   struct ProxyCurlTask *ctask;
792   int num_ctasks;
793
794   curl_download_task = GNUNET_SCHEDULER_NO_TASK;
795
796   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
797   {
798     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
799                 "Shutdown requested while trying to download\n");
800     //TODO cleanup
801     return;
802   }
803   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
804               "Ready to dl\n");
805
806   do
807   {
808     running = 0;
809     num_ctasks = 0;
810     
811     mret = curl_multi_perform (curl_multi, &running);
812
813     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
814                 "Running curl tasks: %d\n", running);
815
816     ctask = ctasks_head;
817     for (; ctask != NULL; ctask = ctask->next)
818     {
819       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
820                   "CTask: %s\n", ctask->url);
821       num_ctasks++;
822     }
823
824     if (num_ctasks != running)
825     {
826       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
827                   "%d ctasks, %d curl running\n", num_ctasks, running);
828     }
829     
830     do
831     {
832       ctask = ctasks_head;
833       msg = curl_multi_info_read (curl_multi, &msgnum);
834       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
835                   "Messages left: %d\n", msgnum);
836       
837       if (msg == NULL)
838         break;
839       switch (msg->msg)
840       {
841        case CURLMSG_DONE:
842          if ((msg->data.result != CURLE_OK) &&
843              (msg->data.result != CURLE_GOT_NOTHING))
844          {
845            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
846                        "Download curl failed");
847             
848            for (; ctask != NULL; ctask = ctask->next)
849            {
850              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
851                continue;
852              
853              GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
854                          "Download curl failed for task %s: %s.\n",
855                          ctask->url,
856                          curl_easy_strerror (msg->data.result));
857              ctask->download_successful = GNUNET_NO;
858              ctask->download_error = GNUNET_YES;
859              //curl_multi_remove_handle (curl_multi, ctask->curl);
860              //curl_easy_cleanup (ctask->curl);
861              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
862                                           ctask);
863              break;
864            }
865            GNUNET_assert (ctask != NULL);
866          }
867          else
868          {
869            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
870                        "cURL download completed.\n");
871
872            for (; ctask != NULL; ctask = ctask->next)
873            {
874              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
875                continue;
876              
877              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
878                          "cURL task %s found.\n", ctask->url);
879              ctask->download_successful = GNUNET_YES;
880              //curl_multi_remove_handle (curl_multi, ctask->curl);
881              //curl_easy_cleanup (ctask->curl);
882              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
883                                           ctask);
884              break;
885            }
886            GNUNET_assert (ctask != NULL);
887          }
888          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
889                      "curl end %s\n", curl_easy_strerror(msg->data.result));
890          break;
891        default:
892          GNUNET_assert (0);
893          break;
894       }
895     } while (msgnum > 0);
896     
897     num_ctasks=0;
898     for (ctask=ctasks_head; ctask != NULL; ctask = ctask->next)
899     {
900       num_ctasks++;
901     }
902     
903     if (num_ctasks != running)
904     {
905       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
906                   "%d ctasks, %d curl running\n", num_ctasks, running);
907     }
908
909     GNUNET_assert ( num_ctasks == running );
910
911     run_httpds ();
912
913   } while (mret == CURLM_CALL_MULTI_PERFORM);
914   
915   
916   if (mret != CURLM_OK)
917   {
918     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s failed at %s:%d: `%s'\n",
919                 "curl_multi_perform", __FILE__, __LINE__,
920                 curl_multi_strerror (mret));
921   }
922   curl_download_prepare();
923 }
924
925 /**
926  * Process LEHO lookup
927  *
928  * @param cls the ctask
929  * @param rd_count number of records returned
930  * @param rd record data
931  */
932 static void
933 process_leho_lookup (void *cls,
934                      uint32_t rd_count,
935                      const struct GNUNET_NAMESTORE_RecordData *rd)
936 {
937   struct ProxyCurlTask *ctask = cls;
938   char hosthdr[262]; //256 + "Host: "
939   int i;
940   CURLcode ret;
941   CURLMcode mret;
942   struct hostent *phost;
943   char *ssl_ip;
944   char resolvename[512];
945   char curlurl[512];
946
947   ctask->headers = NULL;
948
949   strcpy (ctask->leho, "");
950
951   for (i=0; i<rd_count; i++)
952   {
953     if (rd[i].record_type != GNUNET_GNS_RECORD_LEHO)
954       continue;
955
956     memcpy (ctask->leho, rd[i].data, rd[i].data_size);
957
958     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
959                 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
960   }
961
962   if (0 != strcmp (ctask->leho, ""))
963   {
964     sprintf (hosthdr, "%s%s", "Host: ", ctask->leho);
965     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
966                 "New HTTP header value: %s\n", hosthdr);
967     ctask->headers = curl_slist_append (ctask->headers, hosthdr);
968     GNUNET_assert (NULL != ctask->headers);
969     ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers);
970     if (CURLE_OK != ret)
971     {
972       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s failed at %s:%d: `%s'\n",
973                            "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret));
974     }
975
976   }
977
978   if (ctask->mhd->is_ssl)
979   {
980     phost = (struct hostent*)gethostbyname (ctask->host);
981     ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
982     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
983                 "SSL target server: %s\n", ssl_ip);
984     sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
985     ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
986     curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver);
987     sprintf (curlurl, "https://%s%s", ctask->leho, ctask->url);
988     curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
989   }
990
991   if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
992   {
993     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
994                 "%s failed at %s:%d: `%s'\n",
995                 "curl_multi_add_handle", __FILE__, __LINE__,
996                 curl_multi_strerror (mret));
997     ctask->download_successful = GNUNET_NO;
998     ctask->download_error = GNUNET_YES;
999     return;
1000   }
1001   GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1002
1003   curl_download_prepare ();
1004
1005 }
1006
1007 /**
1008  * Initialize download and trigger curl
1009  *
1010  * @param cls the proxycurltask
1011  * @param auth_name the name of the authority (site of origin) of ctask->host
1012  *
1013  */
1014 static void
1015 process_get_authority (void *cls,
1016                        const char* auth_name)
1017 {
1018   struct ProxyCurlTask *ctask = cls;
1019
1020   if (NULL == auth_name)
1021   {
1022     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1023                 "Get authority failed!\n");
1024     strcpy (ctask->authority, "");
1025   }
1026
1027   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1028               "Get authority yielded %s\n", auth_name);
1029   strcpy (ctask->authority, auth_name);
1030
1031   GNUNET_GNS_lookup_zone (gns_handle,
1032                           ctask->host,
1033                           &local_gns_zone,
1034                           GNUNET_GNS_RECORD_LEHO,
1035                           &process_leho_lookup,
1036                           ctask);
1037 }
1038
1039 /**
1040  * Main MHD callback for handling requests.
1041  *
1042  * @param cls unused
1043  * @param con MHD connection handle
1044  * @param meth the HTTP method used ("GET", "PUT", etc.)
1045  * @param ver the HTTP version string (i.e. "HTTP/1.1")
1046  * @param upload_data the data being uploaded (excluding HEADERS,
1047  *        for a POST that fits into memory and that is encoded
1048  *        with a supported encoding, the POST data will NOT be
1049  *        given in upload_data and is instead available as
1050  *        part of MHD_get_connection_values; very large POST
1051  *        data *will* be made available incrementally in
1052  *        upload_data)
1053  * @param upload_data_size set initially to the size of the
1054  *        upload_data provided; the method must update this
1055  *        value to the number of bytes NOT processed;
1056  * @param con_cls pointer to location where we store the 'struct Request'
1057  * @return MHD_YES if the connection was handled successfully,
1058  *         MHD_NO if the socket must be closed due to a serious
1059  *         error while handling the request
1060  */
1061 static int
1062 create_response (void *cls,
1063                  struct MHD_Connection *con,
1064                  const char *url,
1065                  const char *meth,
1066                  const char *ver,
1067                  const char *upload_data,
1068                  size_t *upload_data_size,
1069                  void **con_cls)
1070 {
1071   static int dummy;
1072   struct MhdHttpList* hd = cls;
1073   const char* page = "<html><head><title>gnoxy</title>"\
1074                       "</head><body>cURL fail</body></html>";
1075   struct MHD_Response *response;
1076   char host[265];
1077   char curlurl[512];
1078   int ret = MHD_YES;
1079
1080   struct ProxyCurlTask *ctask;
1081   
1082   if (0 != strcmp (meth, "GET"))
1083     return MHD_NO;
1084   if (&dummy != *con_cls)
1085   {
1086     *con_cls = &dummy;
1087     return MHD_YES;
1088   }
1089
1090   if (0 != *upload_data_size)
1091     return MHD_NO;
1092
1093   *con_cls = NULL;
1094
1095   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1096               "url %s\n", url);
1097
1098   MHD_get_connection_values (con,
1099                              MHD_HEADER_KIND,
1100                              &con_val_iter, host);
1101
1102   
1103   /* Do cURL */
1104   ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
1105   ctask->mhd = hd;
1106   ctask->curl = curl_easy_init();
1107
1108   if (curl_multi == NULL)
1109     curl_multi = curl_multi_init ();
1110   
1111   if ((ctask->curl == NULL) || (curl_multi == NULL))
1112   {
1113     response = MHD_create_response_from_buffer (strlen (page),
1114                                               (void*)page,
1115                                               MHD_RESPMEM_PERSISTENT);
1116     ret = MHD_queue_response (con,
1117                               MHD_HTTP_OK,
1118                               response);
1119     MHD_destroy_response (response);
1120     GNUNET_free (ctask);
1121     return ret;
1122   }
1123
1124   ctask->download_in_progress = GNUNET_YES;
1125   ctask->download_successful = GNUNET_NO;
1126   ctask->buf_status = BUF_WAIT_FOR_CURL;
1127   ctask->bytes_in_buffer = 0;
1128   ctask->parse_content = GNUNET_NO;
1129
1130   curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1131   curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
1132   curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &callback_download);
1133   curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
1134   curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 1);
1135   curl_easy_setopt (ctask->curl, CURLOPT_MAXREDIRS, 4);
1136   /* no need to abort if the above failed */
1137   if (GNUNET_NO == ctask->mhd->is_ssl)
1138     sprintf (curlurl, "http://%s%s", host, url);
1139   strcpy (ctask->host, host);
1140   strcpy (ctask->url, url);
1141   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1142               "Adding new curl task for %s\n", curlurl);
1143   
1144   curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1145   curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
1146   curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
1147   curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
1148
1149   GNUNET_GNS_get_authority (gns_handle,
1150                             ctask->host,
1151                             &process_get_authority,
1152                             ctask);
1153   //download_prepare (ctask);
1154   //curl_download_prepare ();
1155
1156   response = MHD_create_response_from_callback (-1, -1,
1157                                                 &mhd_content_cb,
1158                                                 ctask,
1159                                                 &mhd_content_free);
1160   
1161   ret = MHD_queue_response (con, MHD_HTTP_OK, response);
1162   
1163   //MHD_destroy_response (response);
1164
1165   return ret;
1166 }
1167
1168 /**
1169  * Task run whenever HTTP server operations are pending.
1170  *
1171  * @param cls unused
1172  * @param tc sched context
1173  */
1174 static void
1175 do_httpd (void *cls,
1176           const struct GNUNET_SCHEDULER_TaskContext *tc);
1177
1178
1179 /**
1180  * run all httpd
1181  */
1182 static void
1183 run_httpds ()
1184 {
1185   struct MhdHttpList *hd;
1186
1187   for (hd=mhd_httpd_head; hd != NULL; hd = hd->next)
1188     run_httpd (hd);
1189
1190 }
1191
1192 /**
1193  * schedule mhd
1194  */
1195 static void
1196 run_httpd (struct MhdHttpList *hd)
1197 {
1198   fd_set rs;
1199   fd_set ws;
1200   fd_set es;
1201   struct GNUNET_NETWORK_FDSet *wrs;
1202   struct GNUNET_NETWORK_FDSet *wws;
1203   struct GNUNET_NETWORK_FDSet *wes;
1204   int max;
1205   int haveto;
1206   unsigned MHD_LONG_LONG timeout;
1207   struct GNUNET_TIME_Relative tv;
1208
1209   FD_ZERO (&rs);
1210   FD_ZERO (&ws);
1211   FD_ZERO (&es);
1212   wrs = GNUNET_NETWORK_fdset_create ();
1213   wes = GNUNET_NETWORK_fdset_create ();
1214   wws = GNUNET_NETWORK_fdset_create ();
1215   max = -1;
1216   GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
1217   
1218   
1219   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1220               "MHD fds: max=%d\n", max);
1221   
1222   haveto = MHD_get_timeout (hd->daemon, &timeout);
1223
1224   if (haveto == MHD_YES)
1225     tv.rel_value = (uint64_t) timeout;
1226   else
1227     tv = GNUNET_TIME_UNIT_FOREVER_REL;
1228   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
1229   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
1230   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
1231   
1232   if (hd->httpd_task != GNUNET_SCHEDULER_NO_TASK)
1233     GNUNET_SCHEDULER_cancel (hd->httpd_task);
1234   hd->httpd_task =
1235     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
1236                                  tv, wrs, wws,
1237                                  &do_httpd, hd);
1238   GNUNET_NETWORK_fdset_destroy (wrs);
1239   GNUNET_NETWORK_fdset_destroy (wws);
1240   GNUNET_NETWORK_fdset_destroy (wes);
1241 }
1242
1243
1244 /**
1245  * Task run whenever HTTP server operations are pending.
1246  *
1247  * @param cls unused
1248  * @param tc sched context
1249  */
1250 static void
1251 do_httpd (void *cls,
1252           const struct GNUNET_SCHEDULER_TaskContext *tc)
1253 {
1254   struct MhdHttpList *hd = cls;
1255   
1256   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
1257   
1258   MHD_run (hd->daemon);
1259   run_httpd (hd);
1260 }
1261
1262
1263 /**
1264  * Read data from socket
1265  *
1266  * @param cls the closure
1267  * @param tc scheduler context
1268  */
1269 static void
1270 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1271
1272 /**
1273  * Read from remote end
1274  *
1275  * @param cls closure
1276  * @param tc scheduler context
1277  */
1278 static void
1279 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1280
1281 /**
1282  * Write data to remote socket
1283  *
1284  * @param cls the closure
1285  * @param tc scheduler context
1286  */
1287 static void
1288 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1289 {
1290   struct Socks5Request *s5r = cls;
1291   unsigned int len;
1292
1293   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
1294
1295   if ((NULL != tc->read_ready) &&
1296       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
1297       ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
1298                                          s5r->rbuf_len)>0)))
1299   {
1300     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1301                 "Successfully sent %d bytes to remote socket\n",
1302                 len);
1303   }
1304   else
1305   {
1306     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
1307     //Really!?!?!?
1308     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1309       GNUNET_SCHEDULER_cancel (s5r->rtask);
1310     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
1311       GNUNET_SCHEDULER_cancel (s5r->wtask);
1312     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1313       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1314     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1315     GNUNET_NETWORK_socket_close (s5r->sock);
1316     GNUNET_free(s5r);
1317     return;
1318   }
1319
1320   s5r->rtask =
1321     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1322                                    s5r->sock,
1323                                    &do_read, s5r);
1324 }
1325
1326
1327 /**
1328  * Clean up s5r handles
1329  *
1330  * @param s5r the handle to destroy
1331  */
1332 static void
1333 cleanup_s5r (struct Socks5Request *s5r)
1334 {
1335   if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1336     GNUNET_SCHEDULER_cancel (s5r->rtask);
1337   if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1338     GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1339   if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1340     GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1341   
1342   if (NULL != s5r->remote_sock)
1343     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1344   if ((NULL != s5r->sock) && (s5r->cleanup_sock == GNUNET_YES))
1345     GNUNET_NETWORK_socket_close (s5r->sock);
1346   
1347   GNUNET_free(s5r);
1348 }
1349
1350 /**
1351  * Write data to socket
1352  *
1353  * @param cls the closure
1354  * @param tc scheduler context
1355  */
1356 static void
1357 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1358 {
1359   struct Socks5Request *s5r = cls;
1360   unsigned int len;
1361
1362   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
1363
1364   if ((NULL != tc->read_ready) &&
1365       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
1366       ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
1367                                          s5r->wbuf_len)>0)))
1368   {
1369     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1370                 "Successfully sent %d bytes to socket\n",
1371                 len);
1372   }
1373   else
1374   {
1375     
1376     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
1377     s5r->cleanup = GNUNET_YES;
1378     s5r->cleanup_sock = GNUNET_YES;
1379     cleanup_s5r (s5r);
1380     
1381     return;
1382   }
1383
1384   if (GNUNET_YES == s5r->cleanup)
1385   {
1386     cleanup_s5r (s5r);
1387     return;
1388   }
1389
1390   if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
1391       (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
1392     s5r->fwdrtask =
1393       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1394                                      s5r->remote_sock,
1395                                      &do_read_remote, s5r);
1396 }
1397
1398 /**
1399  * Read from remote end
1400  *
1401  * @param cls closure
1402  * @param tc scheduler context
1403  */
1404 static void
1405 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1406 {
1407   struct Socks5Request *s5r = cls;
1408   
1409   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
1410
1411
1412   if ((NULL != tc->write_ready) &&
1413       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
1414       (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
1415                                          sizeof (s5r->wbuf))))
1416   {
1417     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1418                 "Successfully read %d bytes from remote socket\n",
1419                 s5r->wbuf_len);
1420   }
1421   else
1422   {
1423     if (s5r->wbuf_len == 0)
1424       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1425                   "0 bytes received from remote... graceful shutdown!\n");
1426     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1427       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1428     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1429       GNUNET_SCHEDULER_cancel (s5r->rtask);
1430     
1431     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1432     s5r->remote_sock = NULL;
1433     GNUNET_NETWORK_socket_close (s5r->sock);
1434     GNUNET_free(s5r);
1435
1436     return;
1437   }
1438   
1439   s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1440                                                s5r->sock,
1441                                                &do_write, s5r);
1442   
1443 }
1444
1445
1446 /**
1447  * Adds a socket to MHD
1448  *
1449  * @param h the handle to the socket to add
1450  * @return whatever MHD_add_connection returns
1451  */
1452 static int
1453 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon)
1454 {
1455   int fd;
1456   struct sockaddr *addr;
1457   socklen_t len;
1458
1459   fd = GNUNET_NETWORK_get_fd (h);
1460   addr = GNUNET_NETWORK_get_addr (h);
1461   len = GNUNET_NETWORK_get_addrlen (h);
1462
1463   return MHD_add_connection (daemon, fd, addr, len);
1464 }
1465
1466
1467 /*TODO this needs MHD API modification */
1468 static int http_port = 4444;
1469
1470
1471 static long
1472 get_file_size (const char* filename)
1473 {
1474   FILE *fp;
1475
1476   fp = fopen (filename, "rb");
1477   if (fp)
1478   {
1479     long size;
1480
1481     if ((0 != fseek (fp, 0, SEEK_END)) || (-1 == (size = ftell (fp))))
1482       size = 0;
1483
1484     fclose (fp);
1485
1486     return size;
1487   }
1488   
1489   return 0;
1490 }
1491
1492 /**
1493  * Read file in filename
1494  *
1495  * @param filename file to read
1496  * @return data
1497  */
1498 static char*
1499 load_file (const char* filename)
1500 {
1501   FILE *fp;
1502   char *buffer;
1503   long size;
1504
1505   size = get_file_size (filename);
1506   if (size == 0)
1507     return NULL;
1508
1509   fp = fopen (filename, "rb");
1510   if (!fp)
1511     return NULL;
1512
1513   buffer = GNUNET_malloc (size);
1514   if (!buffer)
1515   {
1516     fclose (fp);
1517     return NULL;
1518   }
1519
1520   if (size != fread (buffer, 1, size, fp))
1521   {
1522     GNUNET_free (buffer);
1523     buffer = NULL;
1524   }
1525
1526   fclose (fp);
1527   return buffer;
1528 }
1529
1530 /** SSL stuff **/
1531
1532 /**
1533  * Load PEM key from file
1534  *
1535  * @param key where to store the data
1536  * @param keyfile path to the PEM file
1537  */
1538 static void
1539 load_key_from_file (gnutls_x509_privkey_t key, char* keyfile)
1540 {
1541   gnutls_datum_t key_data;
1542   int ret;
1543
1544   key_data.data = (unsigned char*)load_file (keyfile);
1545   key_data.size = strlen ((char*)key_data.data);
1546
1547   ret = gnutls_x509_privkey_import (key, &key_data,
1548                                     GNUTLS_X509_FMT_PEM);
1549   
1550   if (GNUTLS_E_SUCCESS != ret)
1551   {
1552     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1553                 "Unable to import private key %s\n", keyfile);
1554     GNUNET_break (0);
1555   }
1556 }
1557
1558 /**
1559  * Load cert from file
1560  *
1561  * @param crt struct to store data in
1562  * @param certfile path to pem file
1563  */
1564 static void
1565 load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
1566 {
1567   gnutls_datum_t cert_data;
1568   int ret;
1569
1570   cert_data.data = (unsigned char*)load_file (certfile);
1571   cert_data.size = strlen ((char*)cert_data.data);
1572
1573   ret = gnutls_x509_crt_import (crt, &cert_data,
1574                                  GNUTLS_X509_FMT_PEM);
1575   if (GNUTLS_E_SUCCESS != ret)
1576   {
1577     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1578                 "Unable to import certificate %s\n", certfile);
1579     GNUNET_break (0);
1580   }
1581
1582 }
1583
1584
1585 /**
1586  * Generate new certificate for specific name
1587  *
1588  * @param name the subject name to generate a cert for
1589  * @return a struct holding the PEM data
1590  */
1591 static struct ProxyGNSCertificate *
1592 generate_gns_certificate (const char *name)
1593 {
1594
1595   int ret;
1596   unsigned int serial;
1597   unsigned int bits;
1598   size_t key_buf_size;
1599   size_t cert_buf_size;
1600   gnutls_x509_crt_t request;
1601   gnutls_x509_privkey_t rsa;
1602   time_t etime;
1603   struct tm *tm_data;
1604
1605   ret = gnutls_x509_crt_init (&request);
1606
1607   if (GNUTLS_E_SUCCESS != ret)
1608   {
1609     GNUNET_break (0);
1610   }
1611
1612   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating key\n");
1613   gnutls_x509_privkey_init (&rsa);
1614   bits = gnutls_sec_param_to_pk_bits (GNUTLS_PK_RSA, GNUTLS_SEC_PARAM_NORMAL);
1615   ret = gnutls_x509_privkey_generate (rsa, GNUTLS_PK_RSA, bits, 0);
1616
1617   if (GNUTLS_E_SUCCESS != ret)
1618   {
1619     GNUNET_break (0);
1620   }
1621
1622   ret = gnutls_x509_crt_set_key (request, rsa);
1623
1624   if (GNUTLS_E_SUCCESS != ret)
1625   {
1626     GNUNET_break (0);
1627   }
1628
1629   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
1630
1631   struct ProxyGNSCertificate *pgc =
1632     GNUNET_malloc (sizeof (struct ProxyGNSCertificate));
1633
1634   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\n");
1635   
1636   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
1637                                  0, "DE", 2);
1638
1639   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
1640                                  0, "GNUnet", 6);
1641
1642   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
1643                                  0, name, strlen (name));
1644
1645   ret = gnutls_x509_crt_set_version (request, 3);
1646
1647   ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
1648
1649   etime = time (NULL);
1650   tm_data = localtime (&etime);
1651   
1652
1653   ret = gnutls_x509_crt_set_serial (request,
1654                                     &serial,
1655                                     sizeof (serial));
1656
1657   ret = gnutls_x509_crt_set_activation_time (request,
1658                                              etime);
1659   tm_data->tm_year++;
1660   etime = mktime (tm_data);
1661
1662   if (-1 == etime)
1663   {
1664     GNUNET_break (0);
1665   }
1666
1667   ret = gnutls_x509_crt_set_expiration_time (request,
1668                                              etime);
1669   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
1670
1671   ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
1672
1673   key_buf_size = sizeof (pgc->key);
1674   cert_buf_size = sizeof (pgc->cert);
1675   
1676   gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
1677                           pgc->cert, &cert_buf_size);
1678
1679   gnutls_x509_privkey_export (rsa, GNUTLS_X509_FMT_PEM,
1680                           pgc->key, &key_buf_size);
1681
1682
1683   gnutls_x509_crt_deinit (request);
1684   gnutls_x509_privkey_deinit (rsa);
1685
1686   return pgc;
1687
1688 }
1689
1690
1691 /**
1692  * Adds a socket to an SSL MHD instance
1693  * It is important the the domain name is
1694  * correct. In most cases we need to start a new daemon
1695  */
1696 static int
1697 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, char* domain)
1698 {
1699   struct MhdHttpList *hd = NULL;
1700   struct ProxyGNSCertificate *pgc;
1701
1702   for (hd = mhd_httpd_head; hd != NULL; hd = hd->next)
1703   {
1704     if (0 == strcmp (hd->domain, domain))
1705       break;
1706   }
1707
1708   if (NULL == hd)
1709   {
1710     /* Start new MHD */
1711     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1712                 "No previous SSL instance found... starting new one for %s\n",
1713                 domain);
1714     
1715     pgc = generate_gns_certificate (domain);
1716     
1717     hd = GNUNET_malloc (sizeof (struct MhdHttpList));
1718     hd->is_ssl = GNUNET_YES;
1719     strcpy (hd->domain, domain);
1720     hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL, http_port++,
1721                               NULL, NULL,
1722                               &create_response, hd,
1723                               MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
1724                               MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
1725                               MHD_OPTION_NOTIFY_COMPLETED,
1726                               NULL, NULL,
1727                               MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
1728                               MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
1729                               MHD_OPTION_END);
1730     hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
1731     
1732     GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
1733   }
1734   
1735   return add_handle_to_mhd (h, hd->daemon);
1736 }
1737
1738
1739
1740 /**
1741  * Read data from incoming connection
1742  *
1743  * @param cls the closure
1744  * @param tc the scheduler context
1745  */
1746 static void
1747 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1748 {
1749   struct Socks5Request *s5r = cls;
1750   struct socks5_client_hello *c_hello;
1751   struct socks5_server_hello *s_hello;
1752   struct socks5_client_request *c_req;
1753   struct socks5_server_response *s_resp;
1754
1755   int ret;
1756   char domain[256];
1757   uint8_t dom_len;
1758   uint16_t req_port;
1759   struct hostent *phost;
1760   uint32_t remote_ip;
1761   struct sockaddr_in remote_addr;
1762   struct in_addr *r_sin_addr;
1763
1764   s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
1765
1766   if ((NULL != tc->write_ready) &&
1767       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
1768       (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
1769                                          sizeof (s5r->rbuf))))
1770   {
1771     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1772                 "Successfully read %d bytes from socket\n",
1773                 s5r->rbuf_len);
1774   }
1775   else
1776   {
1777     if (s5r->rbuf_len != 0)
1778       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
1779     else
1780       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
1781
1782     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1783       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1784     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
1785       GNUNET_SCHEDULER_cancel (s5r->wtask);
1786     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1787       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1788     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1789     GNUNET_NETWORK_socket_close (s5r->sock);
1790     GNUNET_free(s5r);
1791     return;
1792   }
1793
1794   if (s5r->state == SOCKS5_INIT)
1795   {
1796     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1797                 "SOCKS5 init\n");
1798     c_hello = (struct socks5_client_hello*)&s5r->rbuf;
1799
1800     GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
1801
1802     s_hello = (struct socks5_server_hello*)&s5r->wbuf;
1803     s5r->wbuf_len = sizeof( struct socks5_server_hello );
1804
1805     s_hello->version = c_hello->version;
1806     s_hello->auth_method = SOCKS_AUTH_NONE;
1807
1808     /* Write response to client */
1809     s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1810                                                 s5r->sock,
1811                                                 &do_write, s5r);
1812
1813     s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1814                                                 s5r->sock,
1815                                                 &do_read, s5r);
1816
1817     s5r->state = SOCKS5_REQUEST;
1818     return;
1819   }
1820
1821   if (s5r->state == SOCKS5_REQUEST)
1822   {
1823     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1824                 "Processing SOCKS5 request\n");
1825     c_req = (struct socks5_client_request*)&s5r->rbuf;
1826     s_resp = (struct socks5_server_response*)&s5r->wbuf;
1827     //Only 10byte for ipv4 response!
1828     s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
1829
1830     GNUNET_assert (c_req->addr_type == 3);
1831
1832     dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
1833     memset(domain, 0, sizeof(domain));
1834     strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
1835     req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
1836
1837     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1838                 "Requested connection is %s:%d\n",
1839                 domain,
1840                 ntohs(req_port));
1841
1842     if (is_tld (domain, GNUNET_GNS_TLD) ||
1843         is_tld (domain, GNUNET_GNS_TLD_ZKEY))
1844     {
1845       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1846                   "Requested connection is gnunet tld\n",
1847                   domain);
1848       
1849       ret = MHD_NO;
1850       if (ntohs(req_port) == HTTPS_PORT)
1851       {
1852         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1853                     "Requested connection is HTTPS\n");
1854         ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
1855       }
1856       else if (NULL != httpd)
1857       {
1858         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1859                     "Requested connection is HTTP\n");
1860         ret = add_handle_to_mhd ( s5r->sock, httpd );
1861       }
1862
1863       if (ret != MHD_YES)
1864       {
1865         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1866                     _("Failed to start HTTP server\n"));
1867         s_resp->version = 0x05;
1868         s_resp->reply = 0x01;
1869         s5r->cleanup = GNUNET_YES;
1870         s5r->cleanup_sock = GNUNET_YES;
1871         s5r->wtask = 
1872           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1873                                         s5r->sock,
1874                                         &do_write, s5r);
1875         return;
1876       }
1877       
1878       /* Signal success */
1879       s_resp->version = 0x05;
1880       s_resp->reply = 0x00;
1881       s_resp->reserved = 0x00;
1882       s_resp->addr_type = 0x01;
1883       
1884       s5r->cleanup = GNUNET_YES;
1885       s5r->cleanup_sock = GNUNET_NO;
1886       s5r->wtask =
1887         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1888                                         s5r->sock,
1889                                         &do_write, s5r);
1890       run_httpds ();
1891       return;
1892     }
1893     else
1894     {
1895       phost = (struct hostent*)gethostbyname (domain);
1896       if (phost == NULL)
1897       {
1898         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1899                     "Resolve %s error!\n", domain );
1900         s_resp->version = 0x05;
1901         s_resp->reply = 0x01;
1902         s5r->cleanup = GNUNET_YES;
1903         s5r->cleanup_sock = GNUNET_YES;
1904         s5r->wtask = 
1905           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1906                                           s5r->sock,
1907                                           &do_write, s5r);
1908         return;
1909       }
1910
1911       s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
1912                                                        SOCK_STREAM,
1913                                                        0);
1914       r_sin_addr = (struct in_addr*)(phost->h_addr);
1915       remote_ip = r_sin_addr->s_addr;
1916       memset(&remote_addr, 0, sizeof(remote_addr));
1917       remote_addr.sin_family = AF_INET;
1918 #if HAVE_SOCKADDR_IN_SIN_LEN
1919       remote_addr.sin_len = sizeof (remote_addr);
1920 #endif
1921       remote_addr.sin_addr.s_addr = remote_ip;
1922       remote_addr.sin_port = req_port;
1923       
1924       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1925                   "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
1926                   ntohs(req_port));
1927
1928       if ((GNUNET_OK !=
1929           GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
1930                                           (const struct sockaddr*)&remote_addr,
1931                                           sizeof (remote_addr)))
1932           && (errno != EINPROGRESS))
1933       {
1934         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
1935         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1936                     "socket request error...\n");
1937         s_resp->version = 0x05;
1938         s_resp->reply = 0x01;
1939         s5r->wtask =
1940           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1941                                           s5r->sock,
1942                                           &do_write, s5r);
1943         //TODO see above
1944         return;
1945       }
1946
1947       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1948                   "new remote connection\n");
1949
1950       s_resp->version = 0x05;
1951       s_resp->reply = 0x00;
1952       s_resp->reserved = 0x00;
1953       s_resp->addr_type = 0x01;
1954
1955       s5r->state = SOCKS5_DATA_TRANSFER;
1956
1957       s5r->wtask =
1958         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1959                                         s5r->sock,
1960                                         &do_write, s5r);
1961       s5r->rtask =
1962         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1963                                        s5r->sock,
1964                                        &do_read, s5r);
1965
1966     }
1967     return;
1968   }
1969
1970   if (s5r->state == SOCKS5_DATA_TRANSFER)
1971   {
1972     if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
1973     {
1974       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1975                   "Closing connection to client\n");
1976       if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1977         GNUNET_SCHEDULER_cancel (s5r->rtask);
1978       if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1979         GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1980       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1981         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1982       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1983         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1984       
1985       if (s5r->remote_sock != NULL)
1986         GNUNET_NETWORK_socket_close (s5r->remote_sock);
1987       GNUNET_NETWORK_socket_close (s5r->sock);
1988       GNUNET_free(s5r);
1989       return;
1990     }
1991
1992     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1993                 "forwarding %d bytes from client\n", s5r->rbuf_len);
1994
1995     s5r->fwdwtask =
1996       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1997                                       s5r->remote_sock,
1998                                       &do_write_remote, s5r);
1999
2000     if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
2001     {
2002       s5r->fwdrtask =
2003         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2004                                        s5r->remote_sock,
2005                                        &do_read_remote, s5r);
2006     }
2007
2008
2009   }
2010
2011   //GNUNET_CONTAINER_DLL_remove (s5conns.head, s5conns.tail, s5r);
2012
2013 }
2014
2015
2016 /**
2017  * Accept new incoming connections
2018  *
2019  * @param cls the closure
2020  * @param tc the scheduler context
2021  */
2022 static void
2023 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2024 {
2025   struct GNUNET_NETWORK_Handle *s;
2026   struct Socks5Request *s5r;
2027
2028   ltask = GNUNET_SCHEDULER_NO_TASK;
2029   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
2030     return;
2031
2032   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2033                                          lsock,
2034                                          &do_accept, NULL);
2035
2036   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
2037
2038   if (NULL == s)
2039   {
2040     GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "accept");
2041     return;
2042   }
2043
2044   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2045               "Got an inbound connection, waiting for data\n");
2046
2047   s5r = GNUNET_malloc (sizeof (struct Socks5Request));
2048   s5r->sock = s;
2049   s5r->state = SOCKS5_INIT;
2050   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2051   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2052   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2053   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2054                                               s5r->sock,
2055                                               &do_read, s5r);
2056   //GNUNET_CONTAINER_DLL_insert (s5conns.head, s5conns.tail, s5r);
2057 }
2058
2059
2060 /**
2061  * Task run on shutdown
2062  *
2063  * @param cls closure
2064  * @param tc task context
2065  */
2066 static void
2067 do_shutdown (void *cls,
2068              const struct GNUNET_SCHEDULER_TaskContext *tc)
2069 {
2070
2071   struct MhdHttpList *hd;
2072   struct MhdHttpList *tmp_hd;
2073   struct ProxyCurlTask *ctask;
2074   struct ProxyCurlTask *ctask_tmp;
2075
2076   if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
2077   {
2078     GNUNET_SCHEDULER_cancel (curl_download_task);
2079     curl_download_task = GNUNET_SCHEDULER_NO_TASK;
2080   }
2081
2082   for (hd = mhd_httpd_head; hd != NULL; hd = tmp_hd)
2083   {
2084     tmp_hd = hd->next;
2085
2086     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2087                 "Stopping daemon\n");
2088
2089     if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2090     {
2091       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2092                   "Stopping select task %d\n",
2093                   hd->httpd_task);
2094       GNUNET_SCHEDULER_cancel (hd->httpd_task);
2095       hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2096     }
2097
2098     if (NULL != hd->daemon)
2099     {
2100       MHD_stop_daemon (hd->daemon);
2101       hd->daemon = NULL;
2102     }
2103
2104     GNUNET_free (hd);
2105   }
2106
2107   for (ctask=ctasks_head; ctask != NULL; ctask=ctask_tmp)
2108   {
2109     ctask_tmp = ctask->next;
2110
2111     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2112                 "Cleaning up cURL task\n");
2113
2114     if (ctask->curl != NULL)
2115       curl_easy_cleanup (ctask->curl);
2116     ctask->curl = NULL;
2117     if (NULL != ctask->headers)
2118       curl_slist_free_all (ctask->headers);
2119
2120     GNUNET_free (ctask);
2121   }
2122
2123   GNUNET_GNS_disconnect (gns_handle);
2124 }
2125
2126
2127 /**
2128  * Compiles a regex for us
2129  *
2130  * @param re ptr to re struct
2131  * @param rt the expression to compile
2132  * @return 0 on success
2133  */
2134 static int
2135 compile_regex (regex_t *re, const char* rt)
2136 {
2137   int status;
2138   char err[1024];
2139
2140   status = regcomp (re, rt, REG_EXTENDED|REG_NEWLINE);
2141   if (status)
2142   {
2143     regerror (status, re, err, 1024);
2144     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2145                 "Regex error compiling '%s': %s\n", rt, err);
2146     return 1;
2147   }
2148   return 0;
2149 }
2150
2151
2152 /**
2153  * Loads the users local zone key
2154  *
2155  * @return GNUNET_YES on success
2156  */
2157 static int
2158 load_local_zone_key (const struct GNUNET_CONFIGURATION_Handle *cfg)
2159 {
2160   char *keyfile;
2161   struct GNUNET_CRYPTO_RsaPrivateKey *key = NULL;
2162   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
2163   struct GNUNET_CRYPTO_ShortHashCode *zone = NULL;
2164   struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename;
2165
2166   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
2167                                                             "ZONEKEY", &keyfile))
2168   {
2169     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2170                 "Unable to load zone key config value!\n");
2171     return GNUNET_NO;
2172   }
2173
2174   if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
2175   {
2176     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2177                 "Unable to load zone key!\n");
2178     GNUNET_free(keyfile);
2179     return GNUNET_NO;
2180   }
2181
2182   key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
2183   GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
2184   GNUNET_CRYPTO_short_hash(&pkey,
2185                            sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
2186                            &local_gns_zone);
2187   zone = &local_gns_zone;
2188   GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
2189   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2190               "Using zone: %s!\n", &zonename);
2191   GNUNET_CRYPTO_rsa_key_free(key);
2192   GNUNET_free(keyfile);
2193
2194   return GNUNET_YES;
2195 }
2196
2197
2198
2199 /**
2200  * Main function that will be run
2201  *
2202  * @param cls closure
2203  * @param args remaining command-line arguments
2204  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
2205  * @param cfg configuration
2206  */
2207 static void
2208 run (void *cls, char *const *args, const char *cfgfile,
2209      const struct GNUNET_CONFIGURATION_Handle *cfg)
2210 {
2211   struct sockaddr_in sa;
2212   struct MhdHttpList *hd;
2213
2214   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2215               "Loading CA\n");
2216
2217   gnutls_global_init ();
2218   
2219   load_cert_from_file (proxy_ca.cert, cafile);
2220   load_key_from_file (proxy_ca.key, cafile);
2221   
2222   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2223               "Loading Template\n");
2224
2225   compile_regex (&re_dotplus, (char*) RE_DOTPLUS);
2226
2227   gns_handle = GNUNET_GNS_connect (cfg);
2228
2229   if (GNUNET_NO == load_local_zone_key (cfg))
2230   {
2231     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2232                 "Unable to load zone!\n");
2233     return;
2234   }
2235
2236   if (NULL == gns_handle)
2237   {
2238     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2239                 "Unable to connect to GNS!\n");
2240     return;
2241   }
2242
2243   memset (&sa, 0, sizeof (sa));
2244   sa.sin_family = AF_INET;
2245   sa.sin_port = htons (port);
2246 #if HAVE_SOCKADDR_IN_SIN_LEN
2247   sa.sin_len = sizeof (sa);
2248 #endif
2249
2250   lsock = GNUNET_NETWORK_socket_create (AF_INET,
2251                                         SOCK_STREAM,
2252                                         0);
2253
2254   if ((NULL == lsock) ||
2255       (GNUNET_OK !=
2256        GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
2257                                    sizeof (sa))))
2258   {
2259     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2260                 "Failed to create listen socket bound to `%s'",
2261                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
2262     if (NULL != lsock)
2263       GNUNET_NETWORK_socket_close (lsock);
2264     return;
2265   }
2266
2267   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
2268   {
2269     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2270                 "Failed to listen on socket bound to `%s'",
2271                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
2272     return;
2273   }
2274
2275   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2276                                          lsock, &do_accept, NULL);
2277
2278   ctasks_head = NULL;
2279   ctasks_tail = NULL;
2280
2281   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
2282   {
2283     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2284                 "cURL global init failed!\n");
2285     return;
2286   }
2287
2288   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2289               "Proxy listens on port %u\n",
2290               port);
2291
2292   mhd_httpd_head = NULL;
2293   mhd_httpd_tail = NULL;
2294   
2295   hd = GNUNET_malloc (sizeof (struct MhdHttpList));
2296   hd->is_ssl = GNUNET_NO;
2297   strcpy (hd->domain, "");
2298   httpd = MHD_start_daemon (MHD_USE_DEBUG, http_port++,
2299                                NULL, NULL,
2300                                &create_response, hd,
2301                                MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
2302                                MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2303                                MHD_OPTION_NOTIFY_COMPLETED,
2304                                NULL, NULL,
2305                                MHD_OPTION_END);
2306   hd->daemon = httpd;
2307   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2308
2309   GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2310
2311   run_httpds ();
2312
2313   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
2314                                 &do_shutdown, NULL);
2315
2316 }
2317
2318
2319 /**
2320  * The main function for gnunet-gns-proxy.
2321  *
2322  * @param argc number of arguments from the command line
2323  * @param argv command line arguments
2324  * @return 0 ok, 1 on error
2325  */
2326 int
2327 main (int argc, char *const *argv)
2328 {
2329   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
2330     {'p', "port", NULL,
2331      gettext_noop ("listen on specified port"), 1,
2332      &GNUNET_GETOPT_set_string, &port},
2333     {'a', "authority", NULL,
2334       gettext_noop ("pem file to use as CA"), 1,
2335       &GNUNET_GETOPT_set_string, &cafile},
2336     GNUNET_GETOPT_OPTION_END
2337   };
2338
2339   int ret;
2340
2341   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
2342     return 2;
2343
2344   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
2345   ret =
2346       (GNUNET_OK ==
2347        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
2348                            _("GNUnet GNS proxy"),
2349                            options,
2350                            &run, NULL)) ? 0 : 1;
2351   return ret;
2352 }