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