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