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