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