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