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