-LRN: adding missing file
[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 /** SSL **/
31 #include <gnutls/gnutls.h>
32 #include <gnutls/x509.h>
33 #include <gnutls/abstract.h>
34 #include <gnutls/crypto.h>
35 #include <time.h>
36
37 #define GNUNET_GNS_PROXY_PORT 7777
38 #define MHD_MAX_CONNECTIONS 300
39
40 /* MHD/cURL defines */
41 #define BUF_WAIT_FOR_CURL 0
42 #define BUF_WAIT_FOR_MHD 1
43 #define HTML_HDR_CONTENT "Content-Type: text/html"
44
45 /* regexp */
46 //#define RE_DOTPLUS "<a href=\"http://(([A-Za-z]+[.])+)([+])"
47 #define RE_A_HREF  "<a href=\"https?://(([A-Za-z0-9]+[.])+)([+]|zkey)"
48 #define RE_N_MATCHES 4
49
50 /* The usual suspects */
51 #define HTTP_PORT 80
52 #define HTTPS_PORT 443
53
54
55 /**
56  * A structure for CA cert/key
57  */
58 struct ProxyCA
59 {
60   /* The certificate */
61   gnutls_x509_crt_t cert;
62
63   /* The private key */
64   gnutls_x509_privkey_t key;
65 };
66
67
68 /**
69  * Structure for GNS certificates
70  */
71 struct ProxyGNSCertificate
72 {
73   /* The certificate as PEM */
74   char cert[10 * 1024];
75
76   /* The private key as PEM */
77   char key[10 * 1024];
78 };
79
80
81 /**
82  * A structure for socks requests
83  */
84 struct Socks5Request
85 {
86   /* The client socket */
87   struct GNUNET_NETWORK_Handle *sock;
88
89   /* The server socket */
90   struct GNUNET_NETWORK_Handle *remote_sock;
91   
92   /* The socks state */
93   int state;
94   
95   /* Client socket read task */
96   GNUNET_SCHEDULER_TaskIdentifier rtask;
97
98   /* Server socket read task */
99   GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
100
101   /* Client socket write task */
102   GNUNET_SCHEDULER_TaskIdentifier wtask;
103
104   /* Server socket write task */
105   GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
106
107   /* Read buffer */
108   char rbuf[2048];
109
110   /* Write buffer */
111   char wbuf[2048];
112
113   /* Length of data in read buffer */
114   unsigned int rbuf_len;
115
116   /* Length of data in write buffer */
117   unsigned int wbuf_len;
118
119   /* This handle is scheduled for cleanup? */
120   int cleanup;
121
122   /* Shall we close the client socket on cleanup? */
123   int cleanup_sock;
124 };
125
126 /**
127  * DLL for Network Handles
128  */
129 struct NetworkHandleList
130 {
131   /*DLL*/
132   struct NetworkHandleList *next;
133
134   /*DLL*/
135   struct NetworkHandleList *prev;
136
137   /* The handle */
138   struct GNUNET_NETWORK_Handle *h;
139 };
140
141 /**
142  * A structure for all running Httpds
143  */
144 struct MhdHttpList
145 {
146   /* DLL for httpds */
147   struct MhdHttpList *prev;
148
149   /* DLL for httpds */
150   struct MhdHttpList *next;
151
152   /* is this an ssl daemon? */
153   int is_ssl;
154
155   /* the domain name to server (only important for SSL) */
156   char domain[256];
157
158   /* The daemon handle */
159   struct MHD_Daemon *daemon;
160
161   /* Optional proxy certificate used */
162   struct ProxyGNSCertificate *proxy_cert;
163
164   /* The task ID */
165   GNUNET_SCHEDULER_TaskIdentifier httpd_task;
166
167   /* Handles associated with this daemon */
168   struct NetworkHandleList *socket_handles_head;
169   
170   /* Handles associated with this daemon */
171   struct NetworkHandleList *socket_handles_tail;
172 };
173
174 /**
175  * A structure for MHD<->cURL streams
176  */
177 struct ProxyCurlTask
178 {
179   /* DLL for tasks */
180   struct ProxyCurlTask *prev;
181
182   /* DLL for tasks */
183   struct ProxyCurlTask *next;
184
185   /* Handle to cURL */
186   CURL *curl;
187
188   /* Optional header replacements for curl (LEHO) */
189   struct curl_slist *headers;
190
191   /* Optional resolver replacements for curl (LEHO) */
192   struct curl_slist *resolver;
193
194   /* The URL to fetch */
195   char url[2048];
196
197   /* The cURL write buffer / MHD read buffer */
198   char buffer[CURL_MAX_WRITE_SIZE];
199
200   /* The pointer to the data in the buffer */
201   char *buffer_ptr;
202
203   /* The buffer status (BUF_WAIT_FOR_CURL or BUF_WAIT_FOR_MHD) */
204   int buf_status;
205
206   /* Number of bytes in buffer */
207   unsigned int bytes_in_buffer;
208
209   /* Indicates wheather the download is in progress */
210   int download_in_progress;
211
212   /* Indicates wheather the download was successful */
213   int download_successful;
214
215   /* Indicates wheather the download failed */
216   int download_error;
217
218   /* Indicates wheather we need to parse HTML */
219   int parse_content;
220
221   /* Indicates wheather we are postprocessing the HTML right now */
222   int is_postprocessing;
223
224   /* Indicates wheather postprocessing has finished */
225   int pp_finished;
226
227   /* Task ID of the postprocessing task */
228   GNUNET_SCHEDULER_TaskIdentifier pp_task;
229
230   /* The postprocessing buffer TODO length? */
231   char pp_buf[256];
232
233   /* The authority of the corresponding host (site of origin) */
234   char authority[256];
235
236   /* The hostname (Host header field) */
237   char host[256];
238
239   /* The LEgacy HOstname (can be empty) */
240   char leho[256];
241
242   /* The associated daemon list entry */
243   struct MhdHttpList *mhd;
244
245   /* The associated response */
246   struct MHD_Response *response;
247
248   /* Cookies to set */
249   struct ProxySetCookieHeader *set_cookies_head;
250
251   /* Cookies to set */
252   struct ProxySetCookieHeader *set_cookies_tail;
253   
254 };
255
256 /**
257  * Struct for set-cookies
258  */
259 struct ProxySetCookieHeader
260 {
261   /* DLL */
262   struct ProxySetCookieHeader *next;
263
264   /* DLL */
265   struct ProxySetCookieHeader *prev;
266
267   /* the cookie */
268   char *cookie;
269 };
270
271 /* The port the proxy is running on (default 7777) */
272 static unsigned long port = GNUNET_GNS_PROXY_PORT;
273
274 /* The CA file (pem) to use for the proxy CA */
275 static char* cafile_opt;
276
277 /* The listen socket of the proxy */
278 static struct GNUNET_NETWORK_Handle *lsock;
279
280 /* The listen task ID */
281 GNUNET_SCHEDULER_TaskIdentifier ltask;
282
283 /* The cURL download task */
284 GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
285
286 /* The non SSL httpd daemon handle */
287 static struct MHD_Daemon *httpd;
288
289 /* Number of current mhd connections */
290 static unsigned int total_mhd_connections;
291
292 /* The cURL multi handle */
293 static CURLM *curl_multi;
294
295 /* Handle to the GNS service */
296 static struct GNUNET_GNS_Handle *gns_handle;
297
298 /* DLL for ProxyCurlTasks */
299 static struct ProxyCurlTask *ctasks_head;
300
301 /* DLL for ProxyCurlTasks */
302 static struct ProxyCurlTask *ctasks_tail;
303
304 /* DLL for http daemons */
305 static struct MhdHttpList *mhd_httpd_head;
306
307 /* DLL for http daemons */
308 static struct MhdHttpList *mhd_httpd_tail;
309
310 /* Handle to the regex for dotplus (.+) replacement in HTML */
311 static regex_t re_dotplus;
312
313 /* The users local GNS zone hash */
314 static struct GNUNET_CRYPTO_ShortHashCode local_gns_zone;
315
316 /* The users local private zone */
317 static struct GNUNET_CRYPTO_ShortHashCode local_private_zone;
318
319 /* The users local shorten zone */
320 static struct GNUNET_CRYPTO_ShortHashCode local_shorten_zone;
321
322 /* The CA for SSL certificate generation */
323 static struct ProxyCA proxy_ca;
324
325 /* UNIX domain socket for mhd */
326 struct GNUNET_NETWORK_Handle *mhd_unix_socket;
327
328 /* Shorten zone private key */
329 struct GNUNET_CRYPTO_RsaPrivateKey *shorten_zonekey;
330
331 /**
332  * Checks if name is in tld
333  *
334  * @param name the name to check
335  * @param tld the TLD to check for
336  * @return GNUNET_YES or GNUNET_NO
337  */
338 int
339 is_tld(const char* name, const char* tld)
340 {
341   int offset = 0;
342
343   if (strlen(name) <= strlen(tld))
344   {
345     return GNUNET_NO;
346   }
347
348   offset = strlen(name)-strlen(tld);
349   if (strcmp (name+offset, tld) != 0)
350   {
351     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
352                "%s is not in .%s TLD\n", name, tld);
353     return GNUNET_NO;
354   }
355
356   return GNUNET_YES;
357 }
358
359
360 /**
361  * Read HTTP request header field 'Host'
362  *
363  * @param cls buffer to write to
364  * @param kind value kind
365  * @param key field key
366  * @param value field value
367  * @return MHD_NO when Host found
368  */
369 static int
370 con_val_iter (void *cls,
371               enum MHD_ValueKind kind,
372               const char *key,
373               const char *value)
374 {
375   char* buf = (char*)cls;
376
377   if (0 == strcmp ("Host", key))
378   {
379     strcpy (buf, value);
380     return MHD_NO;
381   }
382   return MHD_YES;
383 }
384
385
386 /**
387  * Check HTTP response header for mime
388  *
389  * @param buffer curl buffer
390  * @param size curl blocksize
391  * @param nmemb curl blocknumber
392  * @param cls handle
393  * @return size of read bytes
394  */
395 static size_t
396 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
397 {
398   size_t bytes = size * nmemb;
399   struct ProxyCurlTask *ctask = cls;
400   int html_mime_len = strlen (HTML_HDR_CONTENT);
401   int cookie_hdr_len = strlen (MHD_HTTP_HEADER_SET_COOKIE);
402   char hdr_mime[html_mime_len+1];
403   char hdr_cookie[size+1];
404   struct ProxySetCookieHeader *pch;
405   size_t len;
406   
407   if (html_mime_len <= size)
408   {
409     memcpy (hdr_mime, buffer, html_mime_len);
410     hdr_mime[html_mime_len] = '\0';
411
412     if (0 == strcmp (hdr_mime, HTML_HDR_CONTENT))
413     {
414       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
415                   "Got HTML HTTP response header\n");
416       ctask->parse_content = GNUNET_YES;
417     }
418   }
419
420   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
421               "Got header %s\n", buffer);
422
423   if (cookie_hdr_len <= size)
424   {
425     memcpy (hdr_cookie, buffer, size);
426     hdr_cookie[size] = '\0';
427
428     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
429                 "Got Set-Cookie HTTP header %s\n", hdr_cookie);
430     GNUNET_assert (0);
431     
432     pch = GNUNET_malloc (sizeof (struct ProxySetCookieHeader));
433     len = strlen (hdr_cookie) - strlen (MHD_HTTP_HEADER_SET_COOKIE);
434     pch->cookie = GNUNET_malloc (len + 1);
435     memset (pch->cookie, 0, len + 1);
436     memcpy (pch->cookie, hdr_cookie+strlen (MHD_HTTP_HEADER_SET_COOKIE), len);
437     GNUNET_CONTAINER_DLL_insert (ctask->set_cookies_head,
438                                  ctask->set_cookies_tail,
439                                  pch);
440   }
441
442   return bytes;
443 }
444
445 /**
446  * schedule mhd
447  *
448  * @param hd a http daemon list entry
449  */
450 static void
451 run_httpd (struct MhdHttpList *hd);
452
453
454 /**
455  * schedule all mhds
456  *
457  */
458 static void
459 run_httpds (void);
460
461
462 /**
463  * Task that simply runs MHD main loop
464  *
465  * @param cls NULL
466  * @param tc task context
467  */
468 static void
469 run_mhd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
470 {
471
472   struct MhdHttpList *hd = cls;
473
474   //for (hd=mhd_httpd_head; hd != NULL; hd = hd->next)
475     MHD_run (hd->daemon);
476 }
477
478
479 /**
480  * Process cURL download bits
481  *
482  * @param ptr buffer with data
483  * @param size size of a record
484  * @param nmemb number of records downloaded
485  * @param ctx context
486  * @return number of processed bytes
487  */
488 static size_t
489 callback_download (void *ptr, size_t size, size_t nmemb, void *ctx)
490 {
491   const char *cbuf = ptr;
492   size_t total;
493   struct ProxyCurlTask *ctask = ctx;
494
495   //MHD_run (httpd);
496
497   total = size*nmemb;
498
499   if (total == 0)
500   {
501     return total;
502   }
503
504   if (total > sizeof (ctask->buffer))
505   {
506     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
507                 "CURL gave us too much data to handle (%d)!\n",
508                 total);
509     return 0;
510   }
511   
512   if (ctask->buf_status == BUF_WAIT_FOR_MHD)
513   {
514     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
515                 "CURL: Waiting for MHD (%s)\n", ctask->url);
516     return CURL_WRITEFUNC_PAUSE;
517   }
518
519
520   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
521               "CURL: Copying to MHD (%s, %d)\n", ctask->url, total);
522   memcpy (ctask->buffer, cbuf, total);
523   ctask->bytes_in_buffer = total;
524   ctask->buffer_ptr = ctask->buffer;
525
526   ctask->buf_status = BUF_WAIT_FOR_MHD;
527
528   //GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
529   //            "cURL chunk:\n%s\n", (char*)ctask->buffer);
530   //run_mhd (NULL, NULL);
531   GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
532   return total;
533 }
534
535 /**
536  * Ask cURL for the select sets and schedule download
537  */
538 static void
539 curl_download_prepare ();
540
541 /**
542  * Callback to free content
543  *
544  * @param cls content to free
545  * @param tc task context
546  */
547 static void
548 mhd_content_free (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
549 {
550   struct ProxyCurlTask *ctask = cls;
551
552   if (NULL != ctask->headers)
553     curl_slist_free_all (ctask->headers);
554
555   if (NULL != ctask->headers)
556     curl_slist_free_all (ctask->resolver);
557
558   if (NULL != ctask->response)
559     MHD_destroy_response (ctask->response);
560
561   GNUNET_free (ctask);
562
563 }
564
565
566 /**
567  * Shorten result callback
568  *
569  * @param cls the proxycurltask
570  * @param short_name the shortened name (NULL on error)
571  */
572 static void
573 process_shorten (void* cls, const char* short_name)
574 {
575   struct ProxyCurlTask *ctask = cls;
576
577   char tmp[strlen(ctask->pp_buf)]; //TODO length
578
579   if (NULL == short_name)
580   {
581     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
582                 "MHD PP: Unable to shorten %s\n",
583                 ctask->pp_buf);
584     return;
585   }
586
587   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
588               "MHD PP: Shorten %s -> %s\n",
589               ctask->pp_buf,
590               short_name);
591
592   sprintf (tmp, "<a href=\"http://%s", short_name);
593   strcpy (ctask->pp_buf, tmp);
594
595   ctask->pp_finished = GNUNET_YES;
596
597   GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
598 }
599
600
601 /**
602  * Callback for MHD response
603  *
604  * @param cls closure
605  * @param pos in buffer
606  * @param buf buffer
607  * @param max space in buffer
608  * @return number of bytes written
609  */
610 static ssize_t
611 mhd_content_cb (void *cls,
612                 uint64_t pos,
613                 char* buf,
614                 size_t max)
615 {
616   struct ProxyCurlTask *ctask = cls;
617   ssize_t copied = 0;
618   size_t bytes_to_copy;
619   int nomatch;
620   char *hostptr;
621   regmatch_t m[RE_N_MATCHES];
622   struct ProxySetCookieHeader *pch;
623
624   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
625               "MHD: content cb %s\n", ctask->url);
626   
627   pch = ctask->set_cookies_head;
628   while (pch != NULL)
629   {
630     if (GNUNET_NO == MHD_add_response_header (ctask->response,
631                                               MHD_HTTP_HEADER_SET_COOKIE,
632                                               pch->cookie))
633     {
634       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
635                   "MHD: Error adding set-cookie header field %s\n",
636                   pch->cookie);
637     }
638     GNUNET_free (pch->cookie);
639     GNUNET_CONTAINER_DLL_remove (ctask->set_cookies_head,
640                                  ctask->set_cookies_tail,
641                                  pch);
642     pch = ctask->set_cookies_head;
643   }
644
645   if (ctask->download_successful &&
646       (ctask->buf_status == BUF_WAIT_FOR_CURL))
647   {
648     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
649                 "MHD: sending response for %s\n", ctask->url);
650     ctask->download_in_progress = GNUNET_NO;
651     GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
652     GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
653     total_mhd_connections--;
654     return MHD_CONTENT_READER_END_OF_STREAM;
655   }
656   
657   if (ctask->download_error &&
658       (ctask->buf_status == BUF_WAIT_FOR_CURL))
659   {
660     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
661                 "MHD: sending error response\n");
662     ctask->download_in_progress = GNUNET_NO;
663     GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
664     GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
665     total_mhd_connections--;
666     return MHD_CONTENT_READER_END_WITH_ERROR;
667   }
668
669   if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
670     return 0;
671
672   bytes_to_copy = ctask->bytes_in_buffer;
673   
674   if (ctask->parse_content == GNUNET_YES)
675   {
676
677     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
678                  "MHD: We need to parse the HTML %s\n", ctask->buffer_ptr);
679
680     nomatch = regexec ( &re_dotplus, ctask->buffer_ptr, RE_N_MATCHES, m, 0);
681
682     if (nomatch)
683     {
684       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
685                   "MHD RE: No match\n");
686     }
687     else
688     {
689       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
690                   "MHD RE: Match\n");
691
692       GNUNET_assert (m[1].rm_so != -1);
693
694       hostptr = ctask->buffer_ptr+m[1].rm_so;
695
696       if (m[0].rm_so > 0)
697       {
698         bytes_to_copy = m[0].rm_so;
699         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
700                     "Copying %d bytes.\n", m[0].rm_so);
701
702
703       }
704       else
705       {
706         if (ctask->is_postprocessing == GNUNET_YES)
707         {
708           
709           /*Done?*/
710           if ( ctask->pp_finished == GNUNET_NO )
711           {
712             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
713                         "MHD PP: Waiting for PP of %s\n", ctask->pp_buf);
714             return 0;
715           }
716           
717           ctask->is_postprocessing = GNUNET_NO;
718
719           ctask->bytes_in_buffer -= m[0].rm_eo;//(m[1].rm_eo-m[1].rm_so);
720           ctask->buffer_ptr += m[0].rm_eo;//(m[1].rm_eo-m[1].rm_so);
721           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
722                       "Skipping next %d bytes in buffer\n", m[0].rm_eo);
723
724           GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
725
726           if ( strlen (ctask->pp_buf) <= max )
727           {
728             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
729                         "Copying postprocessed %s.\n", ctask->pp_buf);
730             memcpy ( buf, ctask->pp_buf, strlen (ctask->pp_buf) );
731             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
732                         "Done %s.\n", buf);
733             ctask->is_postprocessing = GNUNET_NO;
734             return strlen (ctask->pp_buf);
735           }
736           
737           return 0;
738         }
739
740         memset (ctask->pp_buf, 0, sizeof(ctask->pp_buf));
741         
742         /* If .+ extend with authority */
743         if (*(ctask->buffer_ptr+m[1].rm_eo) == '+')
744         {
745           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
746                       "Links is .+\n");
747            memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so));
748            strcpy ( ctask->pp_buf+strlen(ctask->pp_buf),
749                     ctask->authority);
750         }
751         /* If .zkey simply copy the name */
752         else
753         {
754           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
755                       "Link is zkey\n");
756           memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so + strlen (GNUNET_GNS_TLD_ZKEY)));
757         }
758
759         ctask->is_postprocessing = GNUNET_YES;
760         ctask->pp_finished = GNUNET_NO;
761         
762         GNUNET_GNS_shorten_zone (gns_handle,
763                                  ctask->pp_buf,
764                                  &local_private_zone,
765                                  &local_shorten_zone,
766                                  &local_gns_zone,
767                                  &process_shorten,
768                                  ctask);
769
770         return 0;
771       }
772     }
773   }
774
775   if ( bytes_to_copy > max )
776   {
777     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
778                  "MHD: buffer in response too small! (%s)\n",
779                  ctask->url);
780     memcpy ( buf, ctask->buffer_ptr, max);
781     ctask->bytes_in_buffer -= max;
782     ctask->buffer_ptr += max;
783     copied = max;
784   }
785   else
786   {
787     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
788                  "MHD: copying %d bytes to mhd response at offset %d\n",
789                  bytes_to_copy, pos);
790
791     memcpy ( buf, ctask->buffer_ptr, bytes_to_copy );
792     copied = bytes_to_copy;
793     if (bytes_to_copy < ctask->bytes_in_buffer)
794     {
795       ctask->bytes_in_buffer -= bytes_to_copy;
796       ctask->buffer_ptr += bytes_to_copy;
797     }
798     else
799     {
800       ctask->bytes_in_buffer = 0;
801       ctask->buf_status = BUF_WAIT_FOR_CURL;
802       ctask->buffer_ptr = ctask->buffer;
803       if (NULL != ctask->curl)
804         curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
805       GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
806     }
807   }
808
809   GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
810
811   return copied;
812 }
813
814
815
816 /**
817  * Task that is run when we are ready to receive more data
818  * from curl
819  *
820  * @param cls closure
821  * @param tc task context
822  */
823 static void
824 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
825
826 /**
827  * Ask cURL for the select sets and schedule download
828  */
829 static void
830 curl_download_prepare ()
831 {
832   CURLMcode mret;
833   fd_set rs;
834   fd_set ws;
835   fd_set es;
836   int max;
837   struct GNUNET_NETWORK_FDSet *grs;
838   struct GNUNET_NETWORK_FDSet *gws;
839   long to;
840   struct GNUNET_TIME_Relative rtime;
841
842   max = -1;
843   FD_ZERO (&rs);
844   FD_ZERO (&ws);
845   FD_ZERO (&es);
846   mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
847
848   if (mret != CURLM_OK)
849   {
850     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
851                 "%s failed at %s:%d: `%s'\n",
852                 "curl_multi_fdset", __FILE__, __LINE__,
853                 curl_multi_strerror (mret));
854     //TODO cleanup here?
855     return;
856   }
857
858   mret = curl_multi_timeout (curl_multi, &to);
859   rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
860
861   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
862               "cURL multi fds: max=%d timeout=%llu\n", max, to);
863
864   grs = GNUNET_NETWORK_fdset_create ();
865   gws = GNUNET_NETWORK_fdset_create ();
866   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
867   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
868   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
869               "Scheduling task cURL\n");
870
871   if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
872     GNUNET_SCHEDULER_cancel (curl_download_task);
873   
874   curl_download_task =
875     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
876                                  rtime,
877                                  grs, gws,
878                                  &curl_task_download, curl_multi);
879   GNUNET_NETWORK_fdset_destroy (gws);
880   GNUNET_NETWORK_fdset_destroy (grs);
881
882 }
883
884
885 /**
886  * Task that is run when we are ready to receive more data
887  * from curl
888  *
889  * @param cls closure
890  * @param tc task context
891  */
892 static void
893 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
894 {
895   int running;
896   int msgnum;
897   struct CURLMsg *msg;
898   CURLMcode mret;
899   struct ProxyCurlTask *ctask;
900   int num_ctasks;
901
902   struct ProxyCurlTask *clean_head = NULL;
903   struct ProxyCurlTask *clean_tail = NULL;
904
905   curl_download_task = GNUNET_SCHEDULER_NO_TASK;
906
907   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
908   {
909     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
910                 "Shutdown requested while trying to download\n");
911     //TODO cleanup
912     return;
913   }
914   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
915               "Ready to dl\n");
916
917   do
918   {
919     running = 0;
920     num_ctasks = 0;
921     
922     mret = curl_multi_perform (curl_multi, &running);
923
924     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
925                 "Running curl tasks: %d\n", running);
926
927     ctask = ctasks_head;
928     for (; ctask != NULL; ctask = ctask->next)
929     {
930       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
931                   "CTask: %s\n", ctask->url);
932       num_ctasks++;
933     }
934
935     if (num_ctasks != running)
936     {
937       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
938                   "%d ctasks, %d curl running\n", num_ctasks, running);
939     }
940     
941     do
942     {
943       ctask = ctasks_head;
944       msg = curl_multi_info_read (curl_multi, &msgnum);
945       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
946                   "Messages left: %d\n", msgnum);
947       
948       if (msg == NULL)
949         break;
950       switch (msg->msg)
951       {
952        case CURLMSG_DONE:
953          if ((msg->data.result != CURLE_OK) &&
954              (msg->data.result != CURLE_GOT_NOTHING))
955          {
956            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
957                        "Download curl failed");
958             
959            for (; ctask != NULL; ctask = ctask->next)
960            {
961              if (NULL == ctask->curl)
962                continue;
963
964              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
965                continue;
966              
967              GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
968                          "Download curl failed for task %s: %s.\n",
969                          ctask->url,
970                          curl_easy_strerror (msg->data.result));
971              ctask->download_successful = GNUNET_NO;
972              ctask->download_error = GNUNET_YES;
973              //curl_multi_remove_handle (curl_multi, ctask->curl);
974              //curl_easy_cleanup (ctask->curl);
975              //ctask->curl = NULL;
976              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
977                                           ctask);
978              GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
979              break;
980            }
981            GNUNET_assert (ctask != NULL);
982          }
983          else
984          {
985            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
986                        "cURL download completed.\n");
987
988            for (; ctask != NULL; ctask = ctask->next)
989            {
990              if (NULL == ctask->curl)
991                continue;
992
993              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
994                continue;
995              
996              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
997                          "cURL task %s found.\n", ctask->url);
998              ctask->download_successful = GNUNET_YES;
999              //curl_multi_remove_handle (curl_multi, ctask->curl);
1000              //curl_easy_cleanup (ctask->curl);
1001              //ctask->curl = NULL;
1002              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1003                                           ctask);
1004              GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1005
1006              break;
1007            }
1008            GNUNET_assert (ctask != NULL);
1009          }
1010          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1011                      "curl end %s\n", curl_easy_strerror(msg->data.result));
1012          break;
1013        default:
1014          GNUNET_assert (0);
1015          break;
1016       }
1017     } while (msgnum > 0);
1018
1019     for (ctask=clean_head; ctask != NULL; ctask = ctask->next)
1020     {
1021       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1022                   "Removing cURL task %s.\n", ctask->url);
1023       curl_multi_remove_handle (curl_multi, ctask->curl);
1024       curl_easy_cleanup (ctask->curl);
1025       ctask->curl = NULL;
1026     }
1027     
1028     num_ctasks=0;
1029     for (ctask=ctasks_head; ctask != NULL; ctask = ctask->next)
1030     {
1031       num_ctasks++;
1032     }
1033     
1034     if (num_ctasks != running)
1035     {
1036       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1037                   "%d ctasks, %d curl running\n", num_ctasks, running);
1038     }
1039
1040     GNUNET_assert ( num_ctasks == running );
1041
1042     run_httpds ();
1043
1044   } while (mret == CURLM_CALL_MULTI_PERFORM);
1045   
1046   
1047   if (mret != CURLM_OK)
1048   {
1049     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s failed at %s:%d: `%s'\n",
1050                 "curl_multi_perform", __FILE__, __LINE__,
1051                 curl_multi_strerror (mret));
1052   }
1053   curl_download_prepare();
1054 }
1055
1056 /**
1057  * Process LEHO lookup
1058  *
1059  * @param cls the ctask
1060  * @param rd_count number of records returned
1061  * @param rd record data
1062  */
1063 static void
1064 process_leho_lookup (void *cls,
1065                      uint32_t rd_count,
1066                      const struct GNUNET_NAMESTORE_RecordData *rd)
1067 {
1068   struct ProxyCurlTask *ctask = cls;
1069   char hosthdr[262]; //256 + "Host: "
1070   int i;
1071   CURLcode ret;
1072   CURLMcode mret;
1073   struct hostent *phost;
1074   char *ssl_ip;
1075   char resolvename[512];
1076   char curlurl[512];
1077
1078   ctask->headers = NULL;
1079
1080   strcpy (ctask->leho, "");
1081
1082   if (rd_count == 0)
1083     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1084                 "No LEHO present!\n");
1085
1086   for (i=0; i<rd_count; i++)
1087   {
1088     if (rd[i].record_type != GNUNET_GNS_RECORD_LEHO)
1089       continue;
1090
1091     memcpy (ctask->leho, rd[i].data, rd[i].data_size);
1092
1093     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1094                 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
1095   }
1096
1097   if (0 != strcmp (ctask->leho, ""))
1098   {
1099     sprintf (hosthdr, "%s%s", "Host: ", ctask->leho);
1100     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1101                 "New HTTP header value: %s\n", hosthdr);
1102     ctask->headers = curl_slist_append (ctask->headers, hosthdr);
1103     GNUNET_assert (NULL != ctask->headers);
1104     ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers);
1105     if (CURLE_OK != ret)
1106     {
1107       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s failed at %s:%d: `%s'\n",
1108                            "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret));
1109     }
1110
1111   }
1112
1113   if (ctask->mhd->is_ssl)
1114   {
1115     phost = (struct hostent*)gethostbyname (ctask->host);
1116
1117     if (phost!=NULL)
1118     {
1119       ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
1120       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1121                   "SSL target server: %s\n", ssl_ip);
1122       sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
1123       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1124                   "Curl resolve: %s\n", resolvename);
1125       ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
1126       curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver);
1127       sprintf (curlurl, "https://%s%s", ctask->leho, ctask->url);
1128       curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1129     }
1130     else
1131     {
1132       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1133                   "gethostbyname failed for %s!\n", ctask->host);
1134       ctask->download_successful = GNUNET_NO;
1135       ctask->download_error = GNUNET_YES;
1136       return;
1137     }
1138   }
1139
1140   if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
1141   {
1142     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1143                 "%s failed at %s:%d: `%s'\n",
1144                 "curl_multi_add_handle", __FILE__, __LINE__,
1145                 curl_multi_strerror (mret));
1146     ctask->download_successful = GNUNET_NO;
1147     ctask->download_error = GNUNET_YES;
1148     return;
1149   }
1150   GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1151
1152   curl_download_prepare ();
1153
1154 }
1155
1156 /**
1157  * Initialize download and trigger curl
1158  *
1159  * @param cls the proxycurltask
1160  * @param auth_name the name of the authority (site of origin) of ctask->host
1161  *
1162  */
1163 static void
1164 process_get_authority (void *cls,
1165                        const char* auth_name)
1166 {
1167   struct ProxyCurlTask *ctask = cls;
1168
1169   if (NULL == auth_name)
1170   {
1171     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1172                 "Get authority failed!\n");
1173     strcpy (ctask->authority, "");
1174   }
1175   else
1176   {
1177     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1178                 "Get authority yielded %s\n", auth_name);
1179     strcpy (ctask->authority, auth_name);
1180   }
1181
1182   GNUNET_GNS_lookup_zone (gns_handle,
1183                           ctask->host,
1184                           &local_gns_zone,
1185                           GNUNET_GNS_RECORD_LEHO,
1186                           GNUNET_YES, //Only cached for performance
1187                           shorten_zonekey,
1188                           &process_leho_lookup,
1189                           ctask);
1190 }
1191
1192 /**
1193  * Main MHD callback for handling requests.
1194  *
1195  * @param cls unused
1196  * @param con MHD connection handle
1197  * @param url the url in the request
1198  * @param meth the HTTP method used ("GET", "PUT", etc.)
1199  * @param ver the HTTP version string (i.e. "HTTP/1.1")
1200  * @param upload_data the data being uploaded (excluding HEADERS,
1201  *        for a POST that fits into memory and that is encoded
1202  *        with a supported encoding, the POST data will NOT be
1203  *        given in upload_data and is instead available as
1204  *        part of MHD_get_connection_values; very large POST
1205  *        data *will* be made available incrementally in
1206  *        upload_data)
1207  * @param upload_data_size set initially to the size of the
1208  *        upload_data provided; the method must update this
1209  *        value to the number of bytes NOT processed;
1210  * @param con_cls pointer to location where we store the 'struct Request'
1211  * @return MHD_YES if the connection was handled successfully,
1212  *         MHD_NO if the socket must be closed due to a serious
1213  *         error while handling the request
1214  */
1215 static int
1216 create_response (void *cls,
1217                  struct MHD_Connection *con,
1218                  const char *url,
1219                  const char *meth,
1220                  const char *ver,
1221                  const char *upload_data,
1222                  size_t *upload_data_size,
1223                  void **con_cls)
1224 {
1225   static int dummy;
1226   struct MhdHttpList* hd = cls;
1227   const char* page = "<html><head><title>gnoxy</title>"\
1228                       "</head><body>cURL fail</body></html>";
1229   
1230   char host[265];
1231   char curlurl[512];
1232   int ret = MHD_YES;
1233
1234   struct ProxyCurlTask *ctask;
1235   
1236   if (0 != strcmp (meth, "GET"))
1237     return MHD_NO;
1238   if (&dummy != *con_cls)
1239   {
1240     *con_cls = &dummy;
1241     return MHD_YES;
1242   }
1243
1244   if (0 != *upload_data_size)
1245     return MHD_NO;
1246
1247   *con_cls = NULL;
1248
1249   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1250               "url %s\n", url);
1251
1252   MHD_get_connection_values (con,
1253                              MHD_HEADER_KIND,
1254                              &con_val_iter, host);
1255
1256   
1257   /* Do cURL */
1258   ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
1259   ctask->mhd = hd;
1260
1261   if (curl_multi == NULL)
1262     curl_multi = curl_multi_init ();
1263
1264   ctask->curl = curl_easy_init();
1265   
1266   if ((ctask->curl == NULL) || (curl_multi == NULL))
1267   {
1268     ctask->response = MHD_create_response_from_buffer (strlen (page),
1269                                               (void*)page,
1270                                               MHD_RESPMEM_PERSISTENT);
1271     ret = MHD_queue_response (con,
1272                               MHD_HTTP_OK,
1273                               ctask->response);
1274     MHD_destroy_response (ctask->response);
1275     GNUNET_free (ctask);
1276     return ret;
1277   }
1278   
1279   ctask->prev = NULL;
1280   ctask->next = NULL;
1281   ctask->headers = NULL;
1282   ctask->resolver = NULL;
1283   ctask->buffer_ptr = NULL;
1284   ctask->download_in_progress = GNUNET_YES;
1285   ctask->download_successful = GNUNET_NO;
1286   ctask->buf_status = BUF_WAIT_FOR_CURL;
1287   ctask->bytes_in_buffer = 0;
1288   ctask->parse_content = GNUNET_NO;
1289
1290   curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1291   curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
1292   curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &callback_download);
1293   curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
1294   curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 1);
1295   curl_easy_setopt (ctask->curl, CURLOPT_MAXREDIRS, 4);
1296   /* no need to abort if the above failed */
1297   if (GNUNET_NO == ctask->mhd->is_ssl)
1298   {
1299     sprintf (curlurl, "http://%s%s", host, url);
1300     curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1301     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1302                 "Adding new curl task for %s\n", curlurl);
1303   }
1304   strcpy (ctask->host, host);
1305   strcpy (ctask->url, url);
1306   
1307   //curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1308   curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
1309   curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
1310   curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
1311
1312   GNUNET_GNS_get_authority (gns_handle,
1313                             ctask->host,
1314                             &process_get_authority,
1315                             ctask);
1316   //download_prepare (ctask);
1317   //curl_download_prepare ();
1318
1319   ctask->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
1320                                                 20,
1321                                                 &mhd_content_cb,
1322                                                 ctask,
1323                                                 NULL);
1324   
1325   ret = MHD_queue_response (con, MHD_HTTP_OK, ctask->response);
1326   
1327   //MHD_destroy_response (response);
1328
1329   return ret;
1330 }
1331
1332 /**
1333  * Task run whenever HTTP server operations are pending.
1334  *
1335  * @param cls unused
1336  * @param tc sched context
1337  */
1338 static void
1339 do_httpd (void *cls,
1340           const struct GNUNET_SCHEDULER_TaskContext *tc);
1341
1342
1343 /**
1344  * run all httpd
1345  */
1346 static void
1347 run_httpds ()
1348 {
1349   struct MhdHttpList *hd;
1350
1351   for (hd=mhd_httpd_head; hd != NULL; hd = hd->next)
1352     run_httpd (hd);
1353
1354 }
1355
1356 /**
1357  * schedule mhd
1358  *
1359  * @param hd the daemon to run
1360  */
1361 static void
1362 run_httpd (struct MhdHttpList *hd)
1363 {
1364   fd_set rs;
1365   fd_set ws;
1366   fd_set es;
1367   struct GNUNET_NETWORK_FDSet *wrs;
1368   struct GNUNET_NETWORK_FDSet *wws;
1369   struct GNUNET_NETWORK_FDSet *wes;
1370   int max;
1371   int haveto;
1372   unsigned MHD_LONG_LONG timeout;
1373   struct GNUNET_TIME_Relative tv;
1374
1375   FD_ZERO (&rs);
1376   FD_ZERO (&ws);
1377   FD_ZERO (&es);
1378   wrs = GNUNET_NETWORK_fdset_create ();
1379   wes = GNUNET_NETWORK_fdset_create ();
1380   wws = GNUNET_NETWORK_fdset_create ();
1381   max = -1;
1382   GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
1383   
1384   
1385   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1386               "MHD fds: max=%d\n", max);
1387   
1388   haveto = MHD_get_timeout (hd->daemon, &timeout);
1389
1390   if (haveto == MHD_YES)
1391     tv.rel_value = (uint64_t) timeout;
1392   else
1393     tv = GNUNET_TIME_UNIT_FOREVER_REL;
1394   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
1395   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
1396   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
1397   
1398   if (hd->httpd_task != GNUNET_SCHEDULER_NO_TASK)
1399     GNUNET_SCHEDULER_cancel (hd->httpd_task);
1400   hd->httpd_task =
1401     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
1402                                  tv, wrs, wws,
1403                                  &do_httpd, hd);
1404   GNUNET_NETWORK_fdset_destroy (wrs);
1405   GNUNET_NETWORK_fdset_destroy (wws);
1406   GNUNET_NETWORK_fdset_destroy (wes);
1407 }
1408
1409
1410 /**
1411  * Task run whenever HTTP server operations are pending.
1412  *
1413  * @param cls unused
1414  * @param tc sched context
1415  */
1416 static void
1417 do_httpd (void *cls,
1418           const struct GNUNET_SCHEDULER_TaskContext *tc)
1419 {
1420   struct MhdHttpList *hd = cls;
1421   
1422   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
1423   
1424   MHD_run (hd->daemon);
1425   run_httpd (hd);
1426 }
1427
1428
1429 /**
1430  * Read data from socket
1431  *
1432  * @param cls the closure
1433  * @param tc scheduler context
1434  */
1435 static void
1436 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1437
1438 /**
1439  * Read from remote end
1440  *
1441  * @param cls closure
1442  * @param tc scheduler context
1443  */
1444 static void
1445 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1446
1447 /**
1448  * Write data to remote socket
1449  *
1450  * @param cls the closure
1451  * @param tc scheduler context
1452  */
1453 static void
1454 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1455 {
1456   struct Socks5Request *s5r = cls;
1457   unsigned int len;
1458
1459   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
1460
1461   if ((NULL != tc->read_ready) &&
1462       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
1463       ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
1464                                          s5r->rbuf_len)>0)))
1465   {
1466     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1467                 "Successfully sent %d bytes to remote socket\n",
1468                 len);
1469   }
1470   else
1471   {
1472     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
1473     //Really!?!?!?
1474     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1475       GNUNET_SCHEDULER_cancel (s5r->rtask);
1476     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
1477       GNUNET_SCHEDULER_cancel (s5r->wtask);
1478     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1479       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1480     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1481     GNUNET_NETWORK_socket_close (s5r->sock);
1482     GNUNET_free(s5r);
1483     return;
1484   }
1485
1486   s5r->rtask =
1487     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1488                                    s5r->sock,
1489                                    &do_read, s5r);
1490 }
1491
1492
1493 /**
1494  * Clean up s5r handles
1495  *
1496  * @param s5r the handle to destroy
1497  */
1498 static void
1499 cleanup_s5r (struct Socks5Request *s5r)
1500 {
1501   if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1502     GNUNET_SCHEDULER_cancel (s5r->rtask);
1503   if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1504     GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1505   if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1506     GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1507   
1508   if (NULL != s5r->remote_sock)
1509     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1510   if ((NULL != s5r->sock) && (s5r->cleanup_sock == GNUNET_YES))
1511     GNUNET_NETWORK_socket_close (s5r->sock);
1512   
1513   GNUNET_free(s5r);
1514 }
1515
1516 /**
1517  * Write data to socket
1518  *
1519  * @param cls the closure
1520  * @param tc scheduler context
1521  */
1522 static void
1523 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1524 {
1525   struct Socks5Request *s5r = cls;
1526   unsigned int len;
1527
1528   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
1529
1530   if ((NULL != tc->read_ready) &&
1531       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
1532       ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
1533                                          s5r->wbuf_len)>0)))
1534   {
1535     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1536                 "Successfully sent %d bytes to socket\n",
1537                 len);
1538   }
1539   else
1540   {
1541     
1542     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
1543     s5r->cleanup = GNUNET_YES;
1544     s5r->cleanup_sock = GNUNET_YES;
1545     cleanup_s5r (s5r);
1546     
1547     return;
1548   }
1549
1550   if (GNUNET_YES == s5r->cleanup)
1551   {
1552     cleanup_s5r (s5r);
1553     return;
1554   }
1555
1556   if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
1557       (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
1558     s5r->fwdrtask =
1559       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1560                                      s5r->remote_sock,
1561                                      &do_read_remote, s5r);
1562 }
1563
1564 /**
1565  * Read from remote end
1566  *
1567  * @param cls closure
1568  * @param tc scheduler context
1569  */
1570 static void
1571 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1572 {
1573   struct Socks5Request *s5r = cls;
1574   
1575   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
1576
1577
1578   if ((NULL != tc->write_ready) &&
1579       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
1580       (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
1581                                          sizeof (s5r->wbuf))))
1582   {
1583     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1584                 "Successfully read %d bytes from remote socket\n",
1585                 s5r->wbuf_len);
1586   }
1587   else
1588   {
1589     if (s5r->wbuf_len == 0)
1590       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1591                   "0 bytes received from remote... graceful shutdown!\n");
1592     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1593       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1594     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1595       GNUNET_SCHEDULER_cancel (s5r->rtask);
1596     
1597     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1598     s5r->remote_sock = NULL;
1599     GNUNET_NETWORK_socket_close (s5r->sock);
1600     GNUNET_free(s5r);
1601
1602     return;
1603   }
1604   
1605   s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1606                                                s5r->sock,
1607                                                &do_write, s5r);
1608   
1609 }
1610
1611
1612 /**
1613  * Adds a socket to MHD
1614  *
1615  * @param h the handle to the socket to add
1616  * @param daemon the daemon to add the fd to
1617  * @return whatever MHD_add_connection returns
1618  */
1619 static int
1620 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon)
1621 {
1622   int fd;
1623   struct sockaddr *addr;
1624   socklen_t len;
1625
1626   fd = dup (GNUNET_NETWORK_get_fd (h));
1627   addr = GNUNET_NETWORK_get_addr (h);
1628   len = GNUNET_NETWORK_get_addrlen (h);
1629
1630   return MHD_add_connection (daemon, fd, addr, len);
1631 }
1632
1633 /**
1634  * Calculate size of file
1635  *
1636  * @param filename name of file
1637  * @return filesize or 0 on error
1638  */
1639 static long
1640 get_file_size (const char* filename)
1641 {
1642   FILE *fp;
1643
1644   fp = fopen (filename, "rb");
1645   if (fp)
1646   {
1647     long size;
1648
1649     if ((0 != fseek (fp, 0, SEEK_END)) || (-1 == (size = ftell (fp))))
1650       size = 0;
1651
1652     fclose (fp);
1653
1654     return size;
1655   }
1656   
1657   return 0;
1658 }
1659
1660 /**
1661  * Read file in filename
1662  *
1663  * @param filename file to read
1664  * @param size pointer where filesize is stored
1665  * @return data
1666  */
1667 static char*
1668 load_file (const char* filename, unsigned int* size)
1669 {
1670   FILE *fp;
1671   char *buffer;
1672
1673   *size = get_file_size (filename);
1674   if (*size == 0)
1675     return NULL;
1676
1677   fp = fopen (filename, "rb");
1678   if (!fp)
1679     return NULL;
1680
1681   buffer = GNUNET_malloc (*size);
1682   if (!buffer)
1683   {
1684     fclose (fp);
1685     return NULL;
1686   }
1687
1688   if (*size != fread (buffer, 1, *size, fp))
1689   {
1690     GNUNET_free (buffer);
1691     buffer = NULL;
1692   }
1693
1694   fclose (fp);
1695   return buffer;
1696 }
1697
1698 /**
1699  * Load PEM key from file
1700  *
1701  * @param key where to store the data
1702  * @param keyfile path to the PEM file
1703  */
1704 static void
1705 load_key_from_file (gnutls_x509_privkey_t key, char* keyfile)
1706 {
1707   gnutls_datum_t key_data;
1708   key_data.data = NULL;
1709   int ret;
1710
1711   key_data.data = (unsigned char*)load_file (keyfile, &key_data.size);
1712
1713   ret = gnutls_x509_privkey_import (key, &key_data,
1714                                     GNUTLS_X509_FMT_PEM);
1715   
1716   if (GNUTLS_E_SUCCESS != ret)
1717   {
1718     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1719                 "Unable to import private key %s(ret=%d)\n", key_data.data, ret);
1720     GNUNET_break (0);
1721   }
1722
1723   GNUNET_free (key_data.data);
1724 }
1725
1726 /**
1727  * Load cert from file
1728  *
1729  * @param crt struct to store data in
1730  * @param certfile path to pem file
1731  */
1732 static void
1733 load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
1734 {
1735   gnutls_datum_t cert_data;
1736   cert_data.data = NULL;
1737   int ret;
1738
1739   cert_data.data = (unsigned char*)load_file (certfile, &cert_data.size);
1740
1741   ret = gnutls_x509_crt_import (crt, &cert_data,
1742                                  GNUTLS_X509_FMT_PEM);
1743   if (GNUTLS_E_SUCCESS != ret)
1744   {
1745     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1746                 "Unable to import certificate %s(ret=%d)\n", certfile, ret);
1747     GNUNET_break (0);
1748   }
1749
1750   GNUNET_free (cert_data.data);
1751
1752 }
1753
1754
1755 /**
1756  * Generate new certificate for specific name
1757  *
1758  * @param name the subject name to generate a cert for
1759  * @return a struct holding the PEM data
1760  */
1761 static struct ProxyGNSCertificate *
1762 generate_gns_certificate (const char *name)
1763 {
1764
1765   int ret;
1766   unsigned int serial;
1767   size_t key_buf_size;
1768   size_t cert_buf_size;
1769   gnutls_x509_crt_t request;
1770   time_t etime;
1771   struct tm *tm_data;
1772
1773   ret = gnutls_x509_crt_init (&request);
1774
1775   if (GNUTLS_E_SUCCESS != ret)
1776   {
1777     GNUNET_break (0);
1778   }
1779
1780   ret = gnutls_x509_crt_set_key (request, proxy_ca.key);
1781
1782   if (GNUTLS_E_SUCCESS != ret)
1783   {
1784     GNUNET_break (0);
1785   }
1786
1787   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
1788
1789   struct ProxyGNSCertificate *pgc =
1790     GNUNET_malloc (sizeof (struct ProxyGNSCertificate));
1791
1792   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\n");
1793   
1794   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
1795                                  0, "DE", 2);
1796
1797   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
1798                                  0, "GNUnet", 6);
1799
1800   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
1801                                  0, name, strlen (name));
1802
1803   ret = gnutls_x509_crt_set_version (request, 3);
1804
1805   ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
1806
1807   etime = time (NULL);
1808   tm_data = localtime (&etime);
1809   
1810
1811   ret = gnutls_x509_crt_set_serial (request,
1812                                     &serial,
1813                                     sizeof (serial));
1814
1815   ret = gnutls_x509_crt_set_activation_time (request,
1816                                              etime);
1817   tm_data->tm_year++;
1818   etime = mktime (tm_data);
1819
1820   if (-1 == etime)
1821   {
1822     GNUNET_break (0);
1823   }
1824
1825   ret = gnutls_x509_crt_set_expiration_time (request,
1826                                              etime);
1827   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
1828
1829   ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
1830
1831   key_buf_size = sizeof (pgc->key);
1832   cert_buf_size = sizeof (pgc->cert);
1833
1834   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Exporting certificate...\n");
1835   
1836   gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
1837                           pgc->cert, &cert_buf_size);
1838
1839   gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
1840                           pgc->key, &key_buf_size);
1841
1842
1843   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
1844   gnutls_x509_crt_deinit (request);
1845
1846   return pgc;
1847
1848 }
1849
1850
1851 /*
1852  * Accept policy for mhdaemons
1853  *
1854  * @param cls NULL
1855  * @param addr the sockaddr
1856  * @param addrlen the sockaddr length
1857  * @return MHD_NO if sockaddr is wrong or #conns too high
1858  */
1859 static int
1860 accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen)
1861 {
1862   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1863               "In MHD accept policy cb\n");
1864
1865   if (addr != NULL)
1866   {
1867     if (addr->sa_family == AF_UNIX)
1868       return MHD_NO;
1869   }
1870
1871   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1872               "Connection accepted\n");
1873
1874   return MHD_YES;
1875 }
1876
1877
1878 /**
1879  * Adds a socket to an SSL MHD instance
1880  * It is important the the domain name is
1881  * correct. In most cases we need to start a new daemon
1882  *
1883  * @param h the handle to add to a daemon
1884  * @param domain the domain the ssl daemon has to serve
1885  * @return MHD_YES on success
1886  */
1887 static int
1888 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, char* domain)
1889 {
1890   struct MhdHttpList *hd = NULL;
1891   struct ProxyGNSCertificate *pgc;
1892   struct NetworkHandleList *nh;
1893
1894   for (hd = mhd_httpd_head; hd != NULL; hd = hd->next)
1895   {
1896     if (0 == strcmp (hd->domain, domain))
1897       break;
1898   }
1899
1900   if (NULL == hd)
1901   {
1902     
1903     pgc = generate_gns_certificate (domain);
1904     
1905     hd = GNUNET_malloc (sizeof (struct MhdHttpList));
1906     hd->is_ssl = GNUNET_YES;
1907     strcpy (hd->domain, domain);
1908     hd->proxy_cert = pgc;
1909
1910     /* Start new MHD */
1911     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1912                 "No previous SSL instance found... starting new one for %s\n",
1913                 domain);
1914
1915     hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL, 4444,
1916             &accept_cb, NULL,
1917             &create_response, hd,
1918             MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
1919             MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
1920             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
1921             MHD_OPTION_NOTIFY_COMPLETED,
1922             NULL, NULL,
1923             MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
1924             MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
1925             MHD_OPTION_END);
1926
1927     GNUNET_assert (hd->daemon != NULL);
1928     hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
1929     
1930     GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
1931   }
1932
1933   nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
1934   nh->h = h;
1935
1936   GNUNET_CONTAINER_DLL_insert (hd->socket_handles_head,
1937                                hd->socket_handles_tail,
1938                                nh);
1939   
1940   return add_handle_to_mhd (h, hd->daemon);
1941 }
1942
1943
1944
1945 /**
1946  * Read data from incoming connection
1947  *
1948  * @param cls the closure
1949  * @param tc the scheduler context
1950  */
1951 static void
1952 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1953 {
1954   struct Socks5Request *s5r = cls;
1955   struct socks5_client_hello *c_hello;
1956   struct socks5_server_hello *s_hello;
1957   struct socks5_client_request *c_req;
1958   struct socks5_server_response *s_resp;
1959
1960   int ret;
1961   char domain[256];
1962   uint8_t dom_len;
1963   uint16_t req_port;
1964   struct hostent *phost;
1965   uint32_t remote_ip;
1966   struct sockaddr_in remote_addr;
1967   struct in_addr *r_sin_addr;
1968
1969   struct NetworkHandleList *nh;
1970
1971   s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
1972
1973   if ((NULL != tc->write_ready) &&
1974       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
1975       (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
1976                                          sizeof (s5r->rbuf))))
1977   {
1978     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1979                 "Successfully read %d bytes from socket\n",
1980                 s5r->rbuf_len);
1981   }
1982   else
1983   {
1984     if (s5r->rbuf_len != 0)
1985       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
1986     else
1987       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
1988
1989     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1990       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1991     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
1992       GNUNET_SCHEDULER_cancel (s5r->wtask);
1993     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1994       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1995     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1996     GNUNET_NETWORK_socket_close (s5r->sock);
1997     GNUNET_free(s5r);
1998     return;
1999   }
2000
2001   if (s5r->state == SOCKS5_INIT)
2002   {
2003     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2004                 "SOCKS5 init\n");
2005     c_hello = (struct socks5_client_hello*)&s5r->rbuf;
2006
2007     GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
2008
2009     s_hello = (struct socks5_server_hello*)&s5r->wbuf;
2010     s5r->wbuf_len = sizeof( struct socks5_server_hello );
2011
2012     s_hello->version = c_hello->version;
2013     s_hello->auth_method = SOCKS_AUTH_NONE;
2014
2015     /* Write response to client */
2016     s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2017                                                 s5r->sock,
2018                                                 &do_write, s5r);
2019
2020     s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2021                                                 s5r->sock,
2022                                                 &do_read, s5r);
2023
2024     s5r->state = SOCKS5_REQUEST;
2025     return;
2026   }
2027
2028   if (s5r->state == SOCKS5_REQUEST)
2029   {
2030     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2031                 "Processing SOCKS5 request\n");
2032     c_req = (struct socks5_client_request*)&s5r->rbuf;
2033     s_resp = (struct socks5_server_response*)&s5r->wbuf;
2034     //Only 10byte for ipv4 response!
2035     s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
2036
2037     GNUNET_assert (c_req->addr_type == 3);
2038
2039     dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
2040     memset(domain, 0, sizeof(domain));
2041     strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
2042     req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
2043
2044     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2045                 "Requested connection is %s:%d\n",
2046                 domain,
2047                 ntohs(req_port));
2048
2049     if (is_tld (domain, GNUNET_GNS_TLD) ||
2050         is_tld (domain, GNUNET_GNS_TLD_ZKEY))
2051     {
2052       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2053                   "Requested connection is gnunet tld\n",
2054                   domain);
2055       
2056       ret = MHD_NO;
2057       if (ntohs(req_port) == HTTPS_PORT)
2058       {
2059         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2060                     "Requested connection is HTTPS\n");
2061         ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
2062       }
2063       else if (NULL != httpd)
2064       {
2065         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2066                     "Requested connection is HTTP\n");
2067         nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
2068         nh->h = s5r->sock;
2069
2070         GNUNET_CONTAINER_DLL_insert (mhd_httpd_head->socket_handles_head,
2071                                mhd_httpd_head->socket_handles_tail,
2072                                nh);
2073
2074         ret = add_handle_to_mhd ( s5r->sock, httpd );
2075       }
2076
2077       if (ret != MHD_YES)
2078       {
2079         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2080                     _("Failed to start HTTP server\n"));
2081         s_resp->version = 0x05;
2082         s_resp->reply = 0x01;
2083         s5r->cleanup = GNUNET_YES;
2084         s5r->cleanup_sock = GNUNET_YES;
2085         s5r->wtask = 
2086           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2087                                         s5r->sock,
2088                                         &do_write, s5r);
2089         return;
2090       }
2091       
2092       /* Signal success */
2093       s_resp->version = 0x05;
2094       s_resp->reply = 0x00;
2095       s_resp->reserved = 0x00;
2096       s_resp->addr_type = 0x01;
2097       
2098       s5r->cleanup = GNUNET_YES;
2099       s5r->cleanup_sock = GNUNET_NO;
2100       s5r->wtask =
2101         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2102                                         s5r->sock,
2103                                         &do_write, s5r);
2104       run_httpds ();
2105       return;
2106     }
2107     else
2108     {
2109       phost = (struct hostent*)gethostbyname (domain);
2110       if (phost == NULL)
2111       {
2112         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2113                     "Resolve %s error!\n", domain );
2114         s_resp->version = 0x05;
2115         s_resp->reply = 0x01;
2116         s5r->cleanup = GNUNET_YES;
2117         s5r->cleanup_sock = GNUNET_YES;
2118         s5r->wtask = 
2119           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2120                                           s5r->sock,
2121                                           &do_write, s5r);
2122         return;
2123       }
2124
2125       s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
2126                                                        SOCK_STREAM,
2127                                                        0);
2128       r_sin_addr = (struct in_addr*)(phost->h_addr);
2129       remote_ip = r_sin_addr->s_addr;
2130       memset(&remote_addr, 0, sizeof(remote_addr));
2131       remote_addr.sin_family = AF_INET;
2132 #if HAVE_SOCKADDR_IN_SIN_LEN
2133       remote_addr.sin_len = sizeof (remote_addr);
2134 #endif
2135       remote_addr.sin_addr.s_addr = remote_ip;
2136       remote_addr.sin_port = req_port;
2137       
2138       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2139                   "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
2140                   ntohs(req_port));
2141
2142       if ((GNUNET_OK !=
2143           GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
2144                                           (const struct sockaddr*)&remote_addr,
2145                                           sizeof (remote_addr)))
2146           && (errno != EINPROGRESS))
2147       {
2148         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
2149         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2150                     "socket request error...\n");
2151         s_resp->version = 0x05;
2152         s_resp->reply = 0x01;
2153         s5r->wtask =
2154           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2155                                           s5r->sock,
2156                                           &do_write, s5r);
2157         //TODO see above
2158         return;
2159       }
2160
2161       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2162                   "new remote connection\n");
2163
2164       s_resp->version = 0x05;
2165       s_resp->reply = 0x00;
2166       s_resp->reserved = 0x00;
2167       s_resp->addr_type = 0x01;
2168
2169       s5r->state = SOCKS5_DATA_TRANSFER;
2170
2171       s5r->wtask =
2172         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2173                                         s5r->sock,
2174                                         &do_write, s5r);
2175       s5r->rtask =
2176         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2177                                        s5r->sock,
2178                                        &do_read, s5r);
2179
2180     }
2181     return;
2182   }
2183
2184   if (s5r->state == SOCKS5_DATA_TRANSFER)
2185   {
2186     if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
2187     {
2188       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2189                   "Closing connection to client\n");
2190       if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
2191         GNUNET_SCHEDULER_cancel (s5r->rtask);
2192       if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2193         GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2194       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2195         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2196       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2197         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2198       
2199       if (s5r->remote_sock != NULL)
2200         GNUNET_NETWORK_socket_close (s5r->remote_sock);
2201       GNUNET_NETWORK_socket_close (s5r->sock);
2202       GNUNET_free(s5r);
2203       return;
2204     }
2205
2206     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2207                 "forwarding %d bytes from client\n", s5r->rbuf_len);
2208
2209     s5r->fwdwtask =
2210       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2211                                       s5r->remote_sock,
2212                                       &do_write_remote, s5r);
2213
2214     if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
2215     {
2216       s5r->fwdrtask =
2217         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2218                                        s5r->remote_sock,
2219                                        &do_read_remote, s5r);
2220     }
2221
2222
2223   }
2224
2225   //GNUNET_CONTAINER_DLL_remove (s5conns.head, s5conns.tail, s5r);
2226
2227 }
2228
2229
2230 /**
2231  * Accept new incoming connections
2232  *
2233  * @param cls the closure
2234  * @param tc the scheduler context
2235  */
2236 static void
2237 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2238 {
2239   struct GNUNET_NETWORK_Handle *s;
2240   struct Socks5Request *s5r;
2241
2242   ltask = GNUNET_SCHEDULER_NO_TASK;
2243   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
2244     return;
2245
2246   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2247                                          lsock,
2248                                          &do_accept, NULL);
2249
2250   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
2251
2252   if (NULL == s)
2253   {
2254     GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "accept");
2255     return;
2256   }
2257
2258   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2259               "Got an inbound connection, waiting for data\n");
2260
2261   s5r = GNUNET_malloc (sizeof (struct Socks5Request));
2262   s5r->sock = s;
2263   s5r->state = SOCKS5_INIT;
2264   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2265   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2266   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2267   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2268                                               s5r->sock,
2269                                               &do_read, s5r);
2270   //GNUNET_CONTAINER_DLL_insert (s5conns.head, s5conns.tail, s5r);
2271 }
2272
2273
2274 /**
2275  * Task run on shutdown
2276  *
2277  * @param cls closure
2278  * @param tc task context
2279  */
2280 static void
2281 do_shutdown (void *cls,
2282              const struct GNUNET_SCHEDULER_TaskContext *tc)
2283 {
2284
2285   struct MhdHttpList *hd;
2286   struct MhdHttpList *tmp_hd;
2287   struct NetworkHandleList *nh;
2288   struct NetworkHandleList *tmp_nh;
2289   struct ProxyCurlTask *ctask;
2290   struct ProxyCurlTask *ctask_tmp;
2291   
2292   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2293               "Shutting down...\n");
2294
2295   gnutls_global_deinit ();
2296
2297   if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
2298   {
2299     GNUNET_SCHEDULER_cancel (curl_download_task);
2300     curl_download_task = GNUNET_SCHEDULER_NO_TASK;
2301   }
2302
2303   for (hd = mhd_httpd_head; hd != NULL; hd = tmp_hd)
2304   {
2305     tmp_hd = hd->next;
2306
2307     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2308                 "Stopping daemon\n");
2309
2310     if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2311     {
2312       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2313                   "Stopping select task %d\n",
2314                   hd->httpd_task);
2315       GNUNET_SCHEDULER_cancel (hd->httpd_task);
2316       hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2317     }
2318
2319     if (NULL != hd->daemon)
2320     {
2321       MHD_stop_daemon (hd->daemon);
2322       hd->daemon = NULL;
2323     }
2324
2325     for (nh = hd->socket_handles_head; nh != NULL; nh = tmp_nh)
2326     {
2327       tmp_nh = nh->next;
2328
2329       GNUNET_NETWORK_socket_close (nh->h);
2330
2331       GNUNET_free (nh);
2332     }
2333
2334     if (NULL != hd->proxy_cert)
2335     {
2336       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2337                   "Free certificate\n");
2338       GNUNET_free (hd->proxy_cert);
2339     }
2340
2341     GNUNET_free (hd);
2342   }
2343
2344   for (ctask=ctasks_head; ctask != NULL; ctask=ctask_tmp)
2345   {
2346     ctask_tmp = ctask->next;
2347
2348     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2349                 "Cleaning up cURL task\n");
2350
2351     if (ctask->curl != NULL)
2352       curl_easy_cleanup (ctask->curl);
2353     ctask->curl = NULL;
2354     if (NULL != ctask->headers)
2355       curl_slist_free_all (ctask->headers);
2356     if (NULL != ctask->resolver)
2357       curl_slist_free_all (ctask->resolver);
2358
2359     if (NULL != ctask->response)
2360       MHD_destroy_response (ctask->response);
2361
2362
2363     GNUNET_free (ctask);
2364   }
2365
2366   GNUNET_GNS_disconnect (gns_handle);
2367 }
2368
2369
2370 /**
2371  * Compiles a regex for us
2372  *
2373  * @param re ptr to re struct
2374  * @param rt the expression to compile
2375  * @return 0 on success
2376  */
2377 static int
2378 compile_regex (regex_t *re, const char* rt)
2379 {
2380   int status;
2381   char err[1024];
2382
2383   status = regcomp (re, rt, REG_EXTENDED|REG_NEWLINE);
2384   if (status)
2385   {
2386     regerror (status, re, err, 1024);
2387     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2388                 "Regex error compiling '%s': %s\n", rt, err);
2389     return 1;
2390   }
2391   return 0;
2392 }
2393
2394
2395 /**
2396  * Loads the users local zone key
2397  *
2398  * @return GNUNET_YES on success
2399  */
2400 static int
2401 load_local_zone_key (const struct GNUNET_CONFIGURATION_Handle *cfg)
2402 {
2403   char *keyfile;
2404   struct GNUNET_CRYPTO_RsaPrivateKey *key = NULL;
2405   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
2406   struct GNUNET_CRYPTO_ShortHashCode *zone = NULL;
2407   struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename;
2408
2409   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
2410                                                             "ZONEKEY", &keyfile))
2411   {
2412     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2413                 "Unable to load zone key config value!\n");
2414     return GNUNET_NO;
2415   }
2416
2417   if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
2418   {
2419     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2420                 "Unable to load zone key %s!\n", keyfile);
2421     GNUNET_free(keyfile);
2422     return GNUNET_NO;
2423   }
2424
2425   key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
2426   GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
2427   GNUNET_CRYPTO_short_hash(&pkey,
2428                            sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
2429                            &local_gns_zone);
2430   zone = &local_gns_zone;
2431   GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
2432   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2433               "Using zone: %s!\n", &zonename);
2434   GNUNET_CRYPTO_rsa_key_free(key);
2435   GNUNET_free(keyfile);
2436   
2437   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
2438                                                    "PRIVATE_ZONEKEY", &keyfile))
2439   {
2440     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2441                 "Unable to load private zone key config value!\n");
2442     return GNUNET_NO;
2443   }
2444
2445   if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
2446   {
2447     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2448                 "Unable to load private zone key %s!\n", keyfile);
2449     GNUNET_free(keyfile);
2450     return GNUNET_NO;
2451   }
2452
2453   key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
2454   GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
2455   GNUNET_CRYPTO_short_hash(&pkey,
2456                            sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
2457                            &local_private_zone);
2458   GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
2459   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2460               "Using private zone: %s!\n", &zonename);
2461   GNUNET_CRYPTO_rsa_key_free(key);
2462   GNUNET_free(keyfile);
2463
2464   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
2465                                                    "SHORTEN_ZONEKEY", &keyfile))
2466   {
2467     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2468                 "Unable to load shorten zone key config value!\n");
2469     return GNUNET_NO;
2470   }
2471
2472   if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
2473   {
2474     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2475                 "Unable to load shorten zone key %s!\n", keyfile);
2476     GNUNET_free(keyfile);
2477     return GNUNET_NO;
2478   }
2479
2480   key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
2481   GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
2482   GNUNET_CRYPTO_short_hash(&pkey,
2483                            sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
2484                            &local_shorten_zone);
2485   GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
2486   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2487               "Using shorten zone: %s!\n", &zonename);
2488   GNUNET_CRYPTO_rsa_key_free(key);
2489   GNUNET_free(keyfile);
2490
2491   return GNUNET_YES;
2492 }
2493
2494 /**
2495  * Main function that will be run
2496  *
2497  * @param cls closure
2498  * @param args remaining command-line arguments
2499  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
2500  * @param cfg configuration
2501  */
2502 static void
2503 run (void *cls, char *const *args, const char *cfgfile,
2504      const struct GNUNET_CONFIGURATION_Handle *cfg)
2505 {
2506   struct sockaddr_in sa;
2507   struct MhdHttpList *hd;
2508   struct sockaddr_un mhd_unix_sock_addr;
2509   size_t len;
2510   char* proxy_sockfile;
2511   char* cafile_cfg = NULL;
2512   char* cafile;
2513
2514   curl_multi = NULL;
2515
2516   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2517               "Loading CA\n");
2518   
2519   cafile = cafile_opt;
2520
2521   if (NULL == cafile)
2522   {
2523     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
2524                                                           "PROXY_CACERT",
2525                                                           &cafile_cfg))
2526     {
2527       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2528                   "Unable to load proxy CA config value!\n");
2529       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2530                   "No proxy CA provided!\n");
2531       return;
2532     }
2533     cafile = cafile_cfg;
2534   }
2535   
2536   gnutls_global_init ();
2537
2538   gnutls_x509_crt_init (&proxy_ca.cert);
2539   gnutls_x509_privkey_init (&proxy_ca.key);
2540   
2541   load_cert_from_file (proxy_ca.cert, cafile);
2542   load_key_from_file (proxy_ca.key, cafile);
2543
2544   GNUNET_free_non_null (cafile_cfg);
2545   
2546   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2547               "Loading Template\n");
2548   
2549   compile_regex (&re_dotplus, (char*) RE_A_HREF);
2550
2551   gns_handle = GNUNET_GNS_connect (cfg);
2552
2553   if (GNUNET_NO == load_local_zone_key (cfg))
2554   {
2555     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2556                 "Unable to load zone!\n");
2557     return;
2558   }
2559
2560   if (NULL == gns_handle)
2561   {
2562     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2563                 "Unable to connect to GNS!\n");
2564     return;
2565   }
2566
2567   memset (&sa, 0, sizeof (sa));
2568   sa.sin_family = AF_INET;
2569   sa.sin_port = htons (port);
2570 #if HAVE_SOCKADDR_IN_SIN_LEN
2571   sa.sin_len = sizeof (sa);
2572 #endif
2573
2574   lsock = GNUNET_NETWORK_socket_create (AF_INET,
2575                                         SOCK_STREAM,
2576                                         0);
2577
2578   if ((NULL == lsock) ||
2579       (GNUNET_OK !=
2580        GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
2581                                    sizeof (sa))))
2582   {
2583     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2584                 "Failed to create listen socket bound to `%s'",
2585                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
2586     if (NULL != lsock)
2587       GNUNET_NETWORK_socket_close (lsock);
2588     return;
2589   }
2590
2591   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
2592   {
2593     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2594                 "Failed to listen on socket bound to `%s'",
2595                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
2596     return;
2597   }
2598
2599   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2600                                          lsock, &do_accept, NULL);
2601
2602   ctasks_head = NULL;
2603   ctasks_tail = NULL;
2604
2605   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
2606   {
2607     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2608                 "cURL global init failed!\n");
2609     return;
2610   }
2611
2612   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2613               "Proxy listens on port %u\n",
2614               port);
2615
2616   mhd_httpd_head = NULL;
2617   mhd_httpd_tail = NULL;
2618   total_mhd_connections = 0;
2619
2620   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
2621                                                             "PROXY_UNIXPATH",
2622                                                             &proxy_sockfile))
2623   {
2624     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2625                 "Specify PROXY_UNIXPATH in gns-proxy config section!\n");
2626     return;
2627   }
2628   
2629   mhd_unix_socket = GNUNET_NETWORK_socket_create (AF_UNIX,
2630                                                 SOCK_STREAM,
2631                                                 0);
2632
2633   if (NULL == mhd_unix_socket)
2634   {
2635     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2636                 "Unable to create unix domain socket!\n");
2637     return;
2638   }
2639
2640   mhd_unix_sock_addr.sun_family = AF_UNIX;
2641   strcpy (mhd_unix_sock_addr.sun_path, proxy_sockfile);
2642
2643 #if LINUX
2644   mhd_unix_sock_addr.sun_path[0] = '\0';
2645 #endif
2646 #if HAVE_SOCKADDR_IN_SIN_LEN
2647   mhd_unix_sock_addr.sun_len = (u_char) sizeof (struct sockaddr_un);
2648 #endif
2649
2650   len = strlen (proxy_sockfile) + sizeof(AF_UNIX);
2651
2652   GNUNET_free (proxy_sockfile);
2653
2654   if (GNUNET_OK != GNUNET_NETWORK_socket_bind (mhd_unix_socket,
2655                                (struct sockaddr*)&mhd_unix_sock_addr,
2656                                len))
2657   {
2658     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2659                 "Unable to bind unix domain socket!\n");
2660     return;
2661   }
2662
2663   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (mhd_unix_socket,
2664                                                  1))
2665   {
2666     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2667                 "Unable to listen on unix domain socket!\n");
2668     return;
2669   }
2670
2671   hd = GNUNET_malloc (sizeof (struct MhdHttpList));
2672   hd->is_ssl = GNUNET_NO;
2673   strcpy (hd->domain, "");
2674   httpd = MHD_start_daemon (MHD_USE_DEBUG, 4444, //Dummy port
2675             &accept_cb, NULL,
2676             &create_response, hd,
2677             MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
2678             MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
2679             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2680             MHD_OPTION_NOTIFY_COMPLETED,
2681             NULL, NULL,
2682             MHD_OPTION_END);
2683
2684   GNUNET_assert (httpd != NULL);
2685   hd->daemon = httpd;
2686   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2687
2688   GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2689
2690   run_httpds ();
2691
2692   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
2693                                 &do_shutdown, NULL);
2694
2695 }
2696
2697
2698 /**
2699  * The main function for gnunet-gns-proxy.
2700  *
2701  * @param argc number of arguments from the command line
2702  * @param argv command line arguments
2703  * @return 0 ok, 1 on error
2704  */
2705 int
2706 main (int argc, char *const *argv)
2707 {
2708   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
2709     {'p', "port", NULL,
2710      gettext_noop ("listen on specified port"), 1,
2711      &GNUNET_GETOPT_set_string, &port},
2712     {'a', "authority", NULL,
2713       gettext_noop ("pem file to use as CA"), 1,
2714       &GNUNET_GETOPT_set_string, &cafile_opt},
2715     GNUNET_GETOPT_OPTION_END
2716   };
2717
2718   int ret;
2719
2720   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
2721     return 2;
2722
2723
2724   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
2725   ret =
2726       (GNUNET_OK ==
2727        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
2728                            _("GNUnet GNS proxy"),
2729                            options,
2730                            &run, NULL)) ? 0 : 1;
2731   GNUNET_free_non_null ((char*)argv);
2732
2733   return ret;
2734 }