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