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