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