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