45492935da8584d4a246a5ca04f1077f68132aab
[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 <microhttpd.h>
24 #include <curl/curl.h>
25 #include "gns_proxy_proto.h"
26 #include "gns.h"
27
28 #define GNUNET_GNS_PROXY_PORT 7777
29
30 //TODO maybe make this an api call
31 /**
32  * Checks if name is in tld
33  *
34  * @param name the name to check
35  * @param tld the TLD to check for
36  * @return GNUNET_YES or GNUNET_NO
37  */
38 int
39 is_tld(const char* name, const char* tld)
40 {
41   int offset = 0;
42
43   if (strlen(name) <= strlen(tld))
44   {
45     return GNUNET_NO;
46   }
47
48   offset = strlen(name)-strlen(tld);
49   if (strcmp (name+offset, tld) != 0)
50   {
51     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
52                "%s is not in .%s TLD\n", name, tld);
53     return GNUNET_NO;
54   }
55
56   return GNUNET_YES;
57 }
58
59 struct Socks5Request
60 {
61   struct GNUNET_NETWORK_Handle *sock;
62   struct GNUNET_NETWORK_Handle *remote_sock;
63
64   int state;
65
66   GNUNET_SCHEDULER_TaskIdentifier rtask;
67   GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
68   GNUNET_SCHEDULER_TaskIdentifier wtask;
69   GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
70
71   char rbuf[2048];
72   char wbuf[2048];
73   unsigned int rbuf_len;
74   unsigned int wbuf_len;
75 };
76
77
78 #define BUF_WAIT_FOR_CURL 0
79 #define BUF_WAIT_FOR_MHD 1
80
81 struct ProxyCurlTask
82 {
83   //DLL
84   struct ProxyCurlTask *prev;
85   struct ProxyCurlTask *next;
86
87   CURL *curl;
88   char buffer[CURL_MAX_WRITE_SIZE];
89   int buf_status;
90   unsigned int bytes_downloaded;
91   unsigned int bytes_in_buffer;
92   int download_in_progress;
93   int download_successful;
94   int download_error;
95   struct MHD_Connection *connection;
96   
97 };
98
99 unsigned long port = GNUNET_GNS_PROXY_PORT;
100 static struct GNUNET_NETWORK_Handle *lsock;
101 GNUNET_SCHEDULER_TaskIdentifier ltask;
102 GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
103 static struct MHD_Daemon *httpd;
104 static GNUNET_SCHEDULER_TaskIdentifier httpd_task;
105 CURLM *curl_multi;
106
107 struct ProxyCurlTask *ctasks_head;
108 struct ProxyCurlTask *ctasks_tail;
109
110 static int
111 con_val_iter (void *cls,
112               enum MHD_ValueKind kind,
113               const char *key,
114               const char *value)
115 {
116   char* buf = (char*)cls;
117
118   if (0 == strcmp ("Host", key))
119   {
120     strcpy (buf, value);
121     return MHD_NO;
122   }
123   return MHD_YES;
124 }
125
126 /**
127  * Process cURL download bits
128  *
129  * @param ptr buffer with data
130  * @param size size of a record
131  * @param nmemb number of records downloaded
132  * @param ctx context
133  * @return number of processed bytes
134  */
135 static size_t
136 callback_download (void *ptr, size_t size, size_t nmemb, void *ctx)
137 {
138   const char *cbuf = ptr;
139   size_t total;
140   struct ProxyCurlTask *ctask = ctx;
141
142   total = size*nmemb;
143   ctask->bytes_downloaded += total;
144
145   if (total == 0)
146   {
147     return total;
148   }
149
150   if (total > sizeof (ctask->buffer))
151   {
152     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
153                 "cURL gave us too much data to handle (%d)!\n",
154                 total);
155     return 0;
156   }
157
158   if (ctask->buf_status == BUF_WAIT_FOR_MHD)
159   {
160     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
161                 "Waiting for MHD\n");
162     return CURL_WRITEFUNC_PAUSE;
163   }
164
165   memcpy (ctask->buffer, cbuf, total);
166   ctask->bytes_in_buffer = total;
167
168   ctask->buf_status = BUF_WAIT_FOR_MHD;
169
170   //GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
171   //            "cURL chunk:\n%s\n", (char*)ctask->buffer);
172   MHD_run (httpd);
173   return total;
174 }
175
176 /**
177  * Callback for MHD response
178  *
179  * @param cls closure
180  * @param pos in buffer
181  * @param buf buffer
182  * @param max space in buffer
183  */
184 static ssize_t
185 mhd_content_cb (void *cls,
186                 uint64_t pos,
187                 char* buf,
188                 size_t max)
189 {
190   struct ProxyCurlTask *ctask = cls;
191
192   if (ctask->download_successful &&
193       (ctask->buf_status == BUF_WAIT_FOR_CURL))
194   {
195     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
196                 "MHD: sending response\n");
197     ctask->download_in_progress = GNUNET_NO;
198     return MHD_CONTENT_READER_END_OF_STREAM;
199   }
200   
201   if (ctask->download_error &&
202       (ctask->buf_status == BUF_WAIT_FOR_CURL))
203   {
204     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
205                 "MHD: error sending response\n");
206     ctask->download_in_progress = GNUNET_NO;
207     return MHD_CONTENT_READER_END_WITH_ERROR;
208   }
209
210   if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
211   {
212     return 0;
213   }
214
215   if ( ctask->bytes_in_buffer > max )
216   {
217     GNUNET_log ( GNUNET_ERROR_TYPE_ERROR,
218                  "MHD: buffer in response too small!\n");
219     return MHD_CONTENT_READER_END_WITH_ERROR;
220   }
221
222   if ( 0 != ctask->bytes_in_buffer )
223   {
224     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
225                  "MHD: copying %d bytes to mhd response at offset %d\n",
226                  ctask->bytes_in_buffer, pos);
227     memcpy ( buf, ctask->buffer, ctask->bytes_in_buffer );
228   }
229   
230   ctask->buf_status = BUF_WAIT_FOR_CURL;
231   curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
232
233   return ctask->bytes_in_buffer;
234 }
235
236
237 /**
238  * schedule mhd
239  */
240 static void
241 run_httpd (void);
242
243 /**
244  * Task that is run when we are ready to receive more data
245  * from curl
246  *
247  * @param cls closure
248  * @param tc task context
249  */
250 static void
251 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
252
253 /**
254  * Ask cURL for the select sets and schedule download
255  */
256 static void
257 curl_download_prepare ()
258 {
259   CURLMcode mret;
260   fd_set rs;
261   fd_set ws;
262   fd_set es;
263   int max;
264   struct GNUNET_NETWORK_FDSet *grs;
265   struct GNUNET_NETWORK_FDSet *gws;
266   long to;
267   struct GNUNET_TIME_Relative rtime;
268
269   max = -1;
270   FD_ZERO (&rs);
271   FD_ZERO (&ws);
272   FD_ZERO (&es);
273   mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
274
275   if (mret != CURLM_OK)
276   {
277     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
278                 "%s failed at %s:%d: `%s'\n",
279                 "curl_multi_fdset", __FILE__, __LINE__,
280                 curl_multi_strerror (mret));
281     //TODO cleanup here?
282     return;
283   }
284
285   mret = curl_multi_timeout (curl_multi, &to);
286   rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
287
288   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
289               "cURL multi fds: max=%d\n", max);
290
291   grs = GNUNET_NETWORK_fdset_create ();
292   gws = GNUNET_NETWORK_fdset_create ();
293   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
294   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
295   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
296               "Scheduling task cURL\n");
297
298   if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
299     GNUNET_SCHEDULER_cancel (curl_download_task);
300   
301   curl_download_task =
302     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
303                                  rtime,
304                                  grs, gws,
305                                  &curl_task_download, curl_multi);
306   GNUNET_NETWORK_fdset_destroy (gws);
307   GNUNET_NETWORK_fdset_destroy (grs);
308
309 }
310
311
312 /**
313  * Task that is run when we are ready to receive more data
314  * from curl
315  *
316  * @param cls closure
317  * @param tc task context
318  */
319 static void
320 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
321 {
322   int running;
323   int msgnum;
324   struct CURLMsg *msg;
325   CURLMcode mret;
326   struct ProxyCurlTask *ctask;
327
328   curl_download_task = GNUNET_SCHEDULER_NO_TASK;
329
330   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
331   {
332     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
333                 "Shutdown requested while trying to download\n");
334     //TODO cleanup
335     return;
336   }
337   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
338               "Ready to dl\n");
339
340   do
341   {
342     running = 0;
343     
344     mret = curl_multi_perform (curl_multi, &running);
345
346     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
347                 "Running curl tasks: %d\n", running);
348     do
349     {
350       ctask = ctasks_head;
351       msg = curl_multi_info_read (curl_multi, &msgnum);
352       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
353                   "Messages left: %d\n", msgnum);
354       
355       if (msg == NULL)
356         break;
357       switch (msg->msg)
358       {
359        case CURLMSG_DONE:
360          if ((msg->data.result != CURLE_OK) &&
361              (msg->data.result != CURLE_GOT_NOTHING))
362          {
363            GNUNET_log (GNUNET_ERROR_TYPE_INFO,
364                        "Download curl failed %s\n",
365                       curl_easy_strerror (msg->data.result));
366             
367            for (; ctask != NULL; ctask = ctask->next)
368            {
369              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) == 0)
370              {
371                GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
372                            "cURL task found.\n");
373                ctask->download_successful = GNUNET_NO;
374                ctask->download_error = GNUNET_YES;
375                curl_multi_remove_handle (curl_multi, ctask->curl);
376                curl_easy_cleanup (ctask->curl);
377                GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
378                                             ctask);
379                break;
380              }
381            }
382          }
383          else
384          {
385            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
386                        "cURL download completed.\n");
387
388            for (; ctask != NULL; ctask = ctask->next)
389            {
390              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) == 0)
391              {
392                GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
393                            "cURL task found.\n");
394                ctask->download_successful = GNUNET_YES;
395                curl_multi_remove_handle (curl_multi, ctask->curl);
396                curl_easy_cleanup (ctask->curl);
397                GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
398                                             ctask);
399                break;
400              }
401              else
402                GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
403                            "cURL task skipped.\n");
404            }
405            run_httpd ();
406            //TODO iterate list, find ctask
407          }
408          break;
409        default:
410          break;
411       }
412     } while (msgnum > 0);
413   } while (mret == CURLM_CALL_MULTI_PERFORM);
414
415   if (mret != CURLM_OK)
416   {
417     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s failed at %s:%d: `%s'\n",
418                 "curl_multi_perform", __FILE__, __LINE__,
419                 curl_multi_strerror (mret));
420     //TODO cleanup
421   }
422   curl_download_prepare();
423 }
424
425
426 /**
427  * Main MHD callback for handling requests.
428  *
429  * @param cls unused
430  * @param con MHD connection handle
431  * @param meth the HTTP method used ("GET", "PUT", etc.)
432  * @param ver the HTTP version string (i.e. "HTTP/1.1")
433  * @param upload_data the data being uploaded (excluding HEADERS,
434  *        for a POST that fits into memory and that is encoded
435  *        with a supported encoding, the POST data will NOT be
436  *        given in upload_data and is instead available as
437  *        part of MHD_get_connection_values; very large POST
438  *        data *will* be made available incrementally in
439  *        upload_data)
440  * @param upload_data_size set initially to the size of the
441  *        upload_data provided; the method must update this
442  *        value to the number of bytes NOT processed;
443  * @param con_cls pointer to location where we store the 'struct Request'
444  * @return MHD_YES if the connection was handled successfully,
445  *         MHD_NO if the socket must be closed due to a serious
446  *         error while handling the request
447  */
448 static int
449 create_response (void *cls,
450                  struct MHD_Connection *con,
451                  const char *url,
452                  const char *meth,
453                  const char *ver,
454                  const char *upload_data,
455                  size_t *upload_data_size,
456                  void **con_cls)
457 {
458   static int dummy;
459   const char* page = "<html><head><title>gnoxy</title>"\
460                       "</head><body>cURL fail</body></html>";
461   struct MHD_Response *response;
462   char host[265];
463   char curlurl[512];
464   int ret = MHD_YES;
465
466   CURLMcode mret;
467   struct ProxyCurlTask *ctask;
468   
469   if (0 != strcmp (meth, "GET"))
470     return MHD_NO;
471   if (&dummy != *con_cls)
472   {
473     *con_cls = &dummy;
474     return MHD_YES;
475   }
476
477   if (0 != *upload_data_size)
478     return MHD_NO;
479
480   *con_cls = NULL;
481
482   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
483               "url %s\n", url);
484
485   MHD_get_connection_values (con,
486                              MHD_HEADER_KIND,
487                              &con_val_iter, host);
488
489   
490   /* Do cURL */
491   ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
492   ctask->curl = curl_easy_init();
493
494   if (curl_multi == NULL)
495     curl_multi = curl_multi_init ();
496   
497   if ((ctask->curl == NULL) || (curl_multi == NULL))
498   {
499     response = MHD_create_response_from_buffer (strlen (page),
500                                               (void*)page,
501                                               MHD_RESPMEM_PERSISTENT);
502     ret = MHD_queue_response (con,
503                               MHD_HTTP_OK,
504                               response);
505     MHD_destroy_response (response);
506     GNUNET_free (ctask);
507     return ret;
508   }
509
510   ctask->download_in_progress = GNUNET_YES;
511   ctask->download_successful = GNUNET_NO;
512   ctask->bytes_downloaded = 0;
513   ctask->connection = con;
514   ctask->buf_status = BUF_WAIT_FOR_CURL;
515   ctask->bytes_in_buffer = 0;
516
517   curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &callback_download);
518   curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
519   curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 1);
520   curl_easy_setopt (ctask->curl, CURLOPT_MAXREDIRS, 4);
521   /* no need to abort if the above failed */
522   sprintf (curlurl, "http://%s%s", host, url);
523   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
524               "Adding new curl task for %s\n", curlurl);
525   
526   curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
527   curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
528   curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 60L);
529   curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 60L);
530
531   mret = curl_multi_add_handle (curl_multi, ctask->curl);
532
533   if (mret != CURLM_OK)
534   {
535     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
536                 "%s failed at %s:%d: `%s'\n",
537                 "curl_multi_add_handle", __FILE__, __LINE__,
538                 curl_multi_strerror (mret));
539     curl_easy_cleanup (ctask->curl);
540     GNUNET_free (ctask);
541     //TODO maybe error display here
542     return ret;
543   }
544   
545   GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
546
547   curl_download_prepare ();
548
549   response = MHD_create_response_from_callback (-1, -1,
550                                                 &mhd_content_cb,
551                                                 ctask,
552                                                 NULL); //TODO Destroy resp here
553   
554   ret = MHD_queue_response (con, MHD_HTTP_OK, response);
555
556   return ret;
557 }
558
559 /**
560  * Task run whenever HTTP server operations are pending.
561  *
562  * @param cls unused
563  * @param tc sched context
564  */
565 static void
566 do_httpd (void *cls,
567           const struct GNUNET_SCHEDULER_TaskContext *tc);
568
569
570 /**
571  * schedule mhd
572  */
573 static void
574 run_httpd ()
575 {
576   fd_set rs;
577   fd_set ws;
578   fd_set es;
579   struct GNUNET_NETWORK_FDSet *wrs;
580   struct GNUNET_NETWORK_FDSet *wws;
581   struct GNUNET_NETWORK_FDSet *wes;
582   int max;
583   int haveto;
584   unsigned MHD_LONG_LONG timeout;
585   struct GNUNET_TIME_Relative tv;
586
587   FD_ZERO (&rs);
588   FD_ZERO (&ws);
589   FD_ZERO (&es);
590   wrs = GNUNET_NETWORK_fdset_create ();
591   wes = GNUNET_NETWORK_fdset_create ();
592   wws = GNUNET_NETWORK_fdset_create ();
593   max = -1;
594   GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
595   
596   
597   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
598               "MHD fds: max=%d\n", max);
599   
600   haveto = MHD_get_timeout (httpd, &timeout);
601
602   if (haveto == MHD_YES)
603     tv.rel_value = (uint64_t) timeout;
604   else
605     tv = GNUNET_TIME_UNIT_FOREVER_REL;
606   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
607   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
608   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
609   
610   if (httpd_task != GNUNET_SCHEDULER_NO_TASK)
611     GNUNET_SCHEDULER_cancel (httpd_task);
612   httpd_task =
613     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
614                                  tv, wrs, wws,
615                                  &do_httpd, NULL);
616   GNUNET_NETWORK_fdset_destroy (wrs);
617   GNUNET_NETWORK_fdset_destroy (wws);
618   GNUNET_NETWORK_fdset_destroy (wes);
619 }
620
621 /**
622  * Task run whenever HTTP server operations are pending.
623  *
624  * @param cls unused
625  * @param tc sched context
626  */
627 static void
628 do_httpd (void *cls,
629           const struct GNUNET_SCHEDULER_TaskContext *tc)
630 {
631   httpd_task = GNUNET_SCHEDULER_NO_TASK;
632   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
633               "MHD run \n");
634   MHD_run (httpd);
635   run_httpd ();
636 }
637
638 /**
639  * Read data from socket
640  *
641  * @param cls the closure
642  * @param tc scheduler context
643  */
644 static void
645 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
646
647 /**
648  * Read from remote end
649  *
650  * @param cls closure
651  * @param tc scheduler context
652  */
653 static void
654 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
655
656 /**
657  * Write data to remote socket
658  *
659  * @param cls the closure
660  * @param tc scheduler context
661  */
662 static void
663 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
664 {
665   struct Socks5Request *s5r = cls;
666   unsigned int len;
667
668   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
669
670   if ((NULL != tc->read_ready) &&
671       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
672       (len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
673                                          s5r->rbuf_len)))
674   {
675     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
676                 "Successfully sent %d bytes to remote socket\n",
677                 len);
678   }
679   else
680   {
681     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
682     //Really!?!?!?
683     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
684       GNUNET_SCHEDULER_cancel (s5r->rtask);
685     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
686       GNUNET_SCHEDULER_cancel (s5r->wtask);
687     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
688       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
689     GNUNET_NETWORK_socket_close (s5r->remote_sock);
690     GNUNET_NETWORK_socket_close (s5r->sock);
691     GNUNET_free(s5r);
692     return;
693   }
694
695   s5r->rtask =
696     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
697                                    s5r->sock,
698                                    &do_read, s5r);
699 }
700
701
702 /**
703  * Write data to socket
704  *
705  * @param cls the closure
706  * @param tc scheduler context
707  */
708 static void
709 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
710 {
711   struct Socks5Request *s5r = cls;
712   unsigned int len;
713
714   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
715
716   if ((NULL != tc->read_ready) &&
717       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
718       (len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
719                                          s5r->wbuf_len)))
720   {
721     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
722                 "Successfully sent %d bytes to socket\n",
723                 len);
724   }
725   else
726   {
727     
728     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
729     //Really!?!?!?
730     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
731       GNUNET_SCHEDULER_cancel (s5r->rtask);
732     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
733       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
734     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
735       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
736     GNUNET_NETWORK_socket_close (s5r->remote_sock);
737     GNUNET_NETWORK_socket_close (s5r->sock);
738     GNUNET_free(s5r);
739     return;
740   }
741
742   if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
743       (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
744     s5r->fwdrtask =
745       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
746                                      s5r->remote_sock,
747                                      &do_read_remote, s5r);
748 }
749
750 /**
751  * Read from remote end
752  *
753  * @param cls closure
754  * @param tc scheduler context
755  */
756 static void
757 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
758 {
759   struct Socks5Request *s5r = cls;
760   
761   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
762
763
764   if ((NULL != tc->write_ready) &&
765       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
766       (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
767                                          sizeof (s5r->wbuf))))
768   {
769     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
770                 "Successfully read %d bytes from remote socket\n",
771                 s5r->wbuf_len);
772   }
773   else
774   {
775     if (s5r->wbuf_len == 0)
776       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
777                   "0 bytes received from remote... graceful shutdown!\n");
778     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
779       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
780     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
781       GNUNET_SCHEDULER_cancel (s5r->rtask);
782     
783     GNUNET_NETWORK_socket_close (s5r->remote_sock);
784     s5r->remote_sock = NULL;
785     GNUNET_NETWORK_socket_close (s5r->sock);
786     GNUNET_free(s5r);
787
788     return;
789   }
790   
791   s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
792                                                s5r->sock,
793                                                &do_write, s5r);
794   
795 }
796
797
798 static int
799 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h)
800 {
801   int fd;
802   struct sockaddr *addr;
803   socklen_t len;
804
805   fd = GNUNET_NETWORK_get_fd (h);
806   addr = GNUNET_NETWORK_get_addr (h);
807   len = GNUNET_NETWORK_get_addrlen (h);
808
809   return MHD_add_connection (httpd, fd, addr, len);
810 }
811
812 /**
813  * Read data from incoming connection
814  *
815  * @param cls the closure
816  * @param tc the scheduler context
817  */
818 static void
819 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
820 {
821   struct Socks5Request *s5r = cls;
822   struct socks5_client_hello *c_hello;
823   struct socks5_server_hello *s_hello;
824   struct socks5_client_request *c_req;
825   struct socks5_server_response *s_resp;
826
827   char domain[256];
828   uint8_t dom_len;
829   uint16_t req_port;
830   struct hostent *phost;
831   uint32_t remote_ip;
832   struct sockaddr_in remote_addr;
833   struct in_addr *r_sin_addr;
834
835   s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
836
837   if ((NULL != tc->write_ready) &&
838       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
839       (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
840                                          sizeof (s5r->rbuf))))
841   {
842     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
843                 "Successfully read %d bytes from socket\n",
844                 s5r->rbuf_len);
845   }
846   else
847   {
848     if (s5r->rbuf_len != 0)
849       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
850     else
851       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
852
853     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
854       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
855     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
856       GNUNET_SCHEDULER_cancel (s5r->wtask);
857     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
858       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
859     GNUNET_NETWORK_socket_close (s5r->remote_sock);
860     GNUNET_NETWORK_socket_close (s5r->sock);
861     GNUNET_free(s5r);
862     return;
863   }
864
865   if (s5r->state == SOCKS5_INIT)
866   {
867     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
868                 "SOCKS5 init\n");
869     c_hello = (struct socks5_client_hello*)&s5r->rbuf;
870
871     GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
872
873     s_hello = (struct socks5_server_hello*)&s5r->wbuf;
874     s5r->wbuf_len = sizeof( struct socks5_server_hello );
875
876     s_hello->version = c_hello->version;
877     s_hello->auth_method = SOCKS_AUTH_NONE;
878
879     /* Write response to client */
880     s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
881                                                 s5r->sock,
882                                                 &do_write, s5r);
883
884     s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
885                                                 s5r->sock,
886                                                 &do_read, s5r);
887
888     s5r->state = SOCKS5_REQUEST;
889     return;
890   }
891
892   if (s5r->state == SOCKS5_REQUEST)
893   {
894     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
895                 "Processing SOCKS5 request\n");
896     c_req = (struct socks5_client_request*)&s5r->rbuf;
897     s_resp = (struct socks5_server_response*)&s5r->wbuf;
898     //Only 10byte for ipv4 response!
899     s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
900
901     GNUNET_assert (c_req->addr_type == 3);
902
903     dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
904     memset(domain, 0, sizeof(domain));
905     strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
906     req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
907
908     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
909                 "Requested connection is %s:%d\n",
910                 domain,
911                 ntohs(req_port));
912
913     if (is_tld (domain, GNUNET_GNS_TLD) ||
914         is_tld (domain, GNUNET_GNS_TLD_ZKEY))
915     {
916       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
917                   "Requested connection is gnunet tld\n",
918                   domain);
919
920       if (NULL == httpd)
921       {
922         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
923                     _("Failed to start HTTP server\n"));
924         s_resp->version = 0x05;
925         s_resp->reply = 0x01;
926         s5r->wtask = 
927           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
928                                         s5r->sock,
929                                         &do_write, s5r);
930         //ERROR!
931         //TODO! close socket after the write! schedule task
932         //GNUNET_NETWORK_socket_close (s5r->sock);
933         //GNUNET_free(s5r);
934         return;
935       }
936
937       if (MHD_YES == add_handle_to_mhd ( s5r->sock ))
938         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
939                     "Sucessfully added client to MHD!\n");
940       s_resp->version = 0x05;
941       s_resp->reply = 0x00;
942       s_resp->reserved = 0x00;
943       s_resp->addr_type = 0x01;
944
945       s5r->wtask =
946         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
947                                         s5r->sock,
948                                         &do_write, s5r);
949       run_httpd ();
950       //GNUNET_free ( s5r );
951       //FIXME complete socks resp!
952       return;
953     }
954     else
955     {
956       phost = (struct hostent*)gethostbyname (domain);
957       if (phost == NULL)
958       {
959         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
960                     "Resolve %s error!\n", domain );
961         s_resp->version = 0x05;
962         s_resp->reply = 0x01;
963         s5r->wtask = 
964           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
965                                           s5r->sock,
966                                           &do_write, s5r);
967         //ERROR!
968         //TODO! close socket after the write! schedule task
969         //GNUNET_NETWORK_socket_close (s5r->sock);
970         //GNUNET_free(s5r);
971         return;
972       }
973
974       s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
975                                                        SOCK_STREAM,
976                                                        0);
977       r_sin_addr = (struct in_addr*)(phost->h_addr);
978       remote_ip = r_sin_addr->s_addr;
979       memset(&remote_addr, 0, sizeof(remote_addr));
980       remote_addr.sin_family = AF_INET;
981 #if HAVE_SOCKADDR_IN_SIN_LEN
982       remote_addr.sin_len = sizeof (remote_addr);
983 #endif
984       remote_addr.sin_addr.s_addr = remote_ip;
985       remote_addr.sin_port = req_port;
986       
987       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
988                   "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
989                   ntohs(req_port));
990
991       if ((GNUNET_OK !=
992           GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
993                                           (const struct sockaddr*)&remote_addr,
994                                           sizeof (remote_addr)))
995           && (errno != EINPROGRESS))
996       {
997         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
998         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
999                     "socket request error...\n");
1000         s_resp->version = 0x05;
1001         s_resp->reply = 0x01;
1002         s5r->wtask =
1003           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1004                                           s5r->sock,
1005                                           &do_write, s5r);
1006         //TODO see above
1007         return;
1008       }
1009
1010       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1011                   "new remote connection\n");
1012
1013       s_resp->version = 0x05;
1014       s_resp->reply = 0x00;
1015       s_resp->reserved = 0x00;
1016       s_resp->addr_type = 0x01;
1017
1018       s5r->state = SOCKS5_DATA_TRANSFER;
1019
1020       s5r->wtask =
1021         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1022                                         s5r->sock,
1023                                         &do_write, s5r);
1024       s5r->rtask =
1025         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1026                                        s5r->sock,
1027                                        &do_read, s5r);
1028
1029     }
1030     return;
1031   }
1032
1033   if (s5r->state == SOCKS5_DATA_TRANSFER)
1034   {
1035     if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
1036     {
1037       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1038                   "Closing connection to client\n");
1039       if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1040         GNUNET_SCHEDULER_cancel (s5r->rtask);
1041       if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1042         GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1043       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1044         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1045       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1046         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1047       
1048       if (s5r->remote_sock != NULL)
1049         GNUNET_NETWORK_socket_close (s5r->remote_sock);
1050       GNUNET_NETWORK_socket_close (s5r->sock);
1051       GNUNET_free(s5r);
1052       return;
1053     }
1054
1055     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1056                 "forwarding %d bytes from client\n", s5r->rbuf_len);
1057
1058     s5r->fwdwtask =
1059       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1060                                       s5r->remote_sock,
1061                                       &do_write_remote, s5r);
1062
1063     if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
1064     {
1065       s5r->fwdrtask =
1066         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1067                                        s5r->remote_sock,
1068                                        &do_read_remote, s5r);
1069     }
1070
1071
1072   }
1073
1074   //GNUNET_CONTAINER_DLL_remove (s5conns.head, s5conns.tail, s5r);
1075
1076 }
1077
1078 /**
1079  * Accept new incoming connections
1080  *
1081  * @param cls the closure
1082  * @param tc the scheduler context
1083  */
1084 static void
1085 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1086 {
1087   struct GNUNET_NETWORK_Handle *s;
1088   struct Socks5Request *s5r;
1089
1090   ltask = GNUNET_SCHEDULER_NO_TASK;
1091   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1092     return;
1093
1094   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1095                                          lsock,
1096                                          &do_accept, NULL);
1097
1098   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
1099
1100   if (NULL == s)
1101   {
1102     GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "accept");
1103     return;
1104   }
1105
1106   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1107               "Got an inbound connection, waiting for data\n");
1108
1109   s5r = GNUNET_malloc (sizeof (struct Socks5Request));
1110   s5r->sock = s;
1111   s5r->state = SOCKS5_INIT;
1112   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
1113   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
1114   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
1115   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1116                                               s5r->sock,
1117                                               &do_read, s5r);
1118   //GNUNET_CONTAINER_DLL_insert (s5conns.head, s5conns.tail, s5r);
1119 }
1120
1121 /**
1122  * Task run on shutdown
1123  *
1124  * @param cls closure
1125  * @param tc task context
1126  */
1127 static void
1128 do_shutdown (void *cls,
1129              const struct GNUNET_SCHEDULER_TaskContext *tc)
1130 {
1131   if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
1132   {
1133     GNUNET_SCHEDULER_cancel (httpd_task);
1134     httpd_task = GNUNET_SCHEDULER_NO_TASK;
1135   }
1136   
1137   if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
1138   {
1139     GNUNET_SCHEDULER_cancel (curl_download_task);
1140     curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1141   }
1142
1143   if (NULL != httpd)
1144   {
1145     MHD_stop_daemon (httpd);
1146     httpd = NULL;
1147   }
1148 }
1149
1150 /**
1151  * Main function that will be run
1152  *
1153  * @param cls closure
1154  * @param args remaining command-line arguments
1155  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1156  * @param cfg configuration
1157  */
1158 static void
1159 run (void *cls, char *const *args, const char *cfgfile,
1160      const struct GNUNET_CONFIGURATION_Handle *cfg)
1161 {
1162   struct sockaddr_in sa;
1163
1164   memset (&sa, 0, sizeof (sa));
1165   sa.sin_family = AF_INET;
1166   sa.sin_port = htons (port);
1167 #if HAVE_SOCKADDR_IN_SIN_LEN
1168   sa.sin_len = sizeof (sa);
1169 #endif
1170
1171   lsock = GNUNET_NETWORK_socket_create (AF_INET,
1172                                         SOCK_STREAM,
1173                                         0);
1174
1175   if ((NULL == lsock) ||
1176       (GNUNET_OK !=
1177        GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
1178                                    sizeof (sa))))
1179   {
1180     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1181                 "Failed to create listen socket bound to `%s'",
1182                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
1183     if (NULL != lsock)
1184       GNUNET_NETWORK_socket_close (lsock);
1185     return;
1186   }
1187
1188   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
1189   {
1190     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1191                 "Failed to listen on socket bound to `%s'",
1192                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
1193     return;
1194   }
1195
1196   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1197                                          lsock, &do_accept, NULL);
1198
1199   ctasks_head = NULL;
1200   ctasks_tail = NULL;
1201
1202   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1203   {
1204     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1205                 "cURL global init failed!\n");
1206     return;
1207   }
1208
1209   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1210               "Proxy listens on port %u\n",
1211               port);
1212   
1213   httpd = MHD_start_daemon (MHD_USE_DEBUG, 4444,
1214                                NULL, NULL,
1215                                &create_response, NULL,
1216                                MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
1217                                MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
1218                                MHD_OPTION_NOTIFY_COMPLETED,
1219                                NULL, NULL,
1220                                MHD_OPTION_END);
1221   run_httpd ();
1222
1223   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1224                                 &do_shutdown, NULL);
1225
1226 }
1227
1228 /**
1229  * The main function for gnunet-gns-proxy.
1230  *
1231  * @param argc number of arguments from the command line
1232  * @param argv command line arguments
1233  * @return 0 ok, 1 on error
1234  */
1235 int
1236 main (int argc, char *const *argv)
1237 {
1238   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1239     {'p', "port", NULL,
1240      gettext_noop ("listen on specified port"), 1,
1241      &GNUNET_GETOPT_set_string, &port},
1242     GNUNET_GETOPT_OPTION_END
1243   };
1244
1245   int ret;
1246
1247   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
1248   ret =
1249       (GNUNET_OK ==
1250        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
1251                            _("GNUnet GNS proxy"),
1252                            options,
1253                            &run, NULL)) ? 0 : 1;
1254   return ret;
1255 }