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