-urlencode post working, performance, replace location hdr val
[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   char tmp[2048];
1256
1257   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1258               "CURL: read callback\n");
1259
1260   if (NULL == pdata)
1261     return 0;
1262   
1263   //fin
1264   if (NULL == pdata->value)
1265   {
1266     return 0;
1267     if (len < 2)
1268       return 0;
1269
1270     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1271                 "CURL: Terminating POST data\n");
1272
1273     memcpy (buf, "\n\r", 2);
1274     GNUNET_CONTAINER_DLL_remove (ctask->post_data_head,
1275                                  ctask->post_data_tail,
1276                                  pdata);
1277     GNUNET_free (pdata);
1278     return 2;
1279   }
1280  
1281   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1282               "CURL: read callback value %s\n", pdata->value); 
1283   
1284   to_copy = pdata->bytes_left;
1285   if (to_copy > len)
1286     to_copy = len;
1287   
1288   pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1289   memcpy (buf, pos, to_copy);
1290   memcpy (tmp, pos, to_copy);
1291   tmp[to_copy] = 'c';
1292   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1293               "CURL: Wrote %s\n", tmp);
1294   pdata->bytes_left -= to_copy;
1295   if (pdata->bytes_left <= 0)
1296   {
1297     GNUNET_free (pdata->value);
1298     GNUNET_CONTAINER_DLL_remove (ctask->post_data_head,
1299                                  ctask->post_data_tail,
1300                                  pdata);
1301     GNUNET_free (pdata);
1302   }
1303   return to_copy;
1304 }
1305
1306 /**
1307  * Task that is run when we are ready to receive more data
1308  * from curl
1309  *
1310  * @param cls closure
1311  * @param tc task context
1312  */
1313 static void
1314 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1315
1316 /**
1317  * Ask cURL for the select sets and schedule download
1318  */
1319 static void
1320 curl_download_prepare ()
1321 {
1322   CURLMcode mret;
1323   fd_set rs;
1324   fd_set ws;
1325   fd_set es;
1326   int max;
1327   struct GNUNET_NETWORK_FDSet *grs;
1328   struct GNUNET_NETWORK_FDSet *gws;
1329   long to;
1330   struct GNUNET_TIME_Relative rtime;
1331
1332   max = -1;
1333   FD_ZERO (&rs);
1334   FD_ZERO (&ws);
1335   FD_ZERO (&es);
1336   mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
1337
1338   if (mret != CURLM_OK)
1339   {
1340     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1341                 "%s failed at %s:%d: `%s'\n",
1342                 "curl_multi_fdset", __FILE__, __LINE__,
1343                 curl_multi_strerror (mret));
1344     //TODO cleanup here?
1345     return;
1346   }
1347
1348   mret = curl_multi_timeout (curl_multi, &to);
1349   rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
1350
1351   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1352               "cURL multi fds: max=%d timeout=%llu\n", max, to);
1353
1354   grs = GNUNET_NETWORK_fdset_create ();
1355   gws = GNUNET_NETWORK_fdset_create ();
1356   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1357   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1358   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1359               "Scheduling task cURL\n");
1360
1361   if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
1362     GNUNET_SCHEDULER_cancel (curl_download_task);
1363   
1364   if (-1 != max)
1365   {
1366     curl_download_task =
1367       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1368                                    rtime,
1369                                    grs, gws,
1370                                    &curl_task_download, curl_multi);
1371   }
1372   else if (NULL != ctasks_head)
1373   {
1374     /* as specified in curl docs */
1375     curl_download_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
1376                                                        &curl_task_download,
1377                                                        curl_multi);
1378   }
1379   GNUNET_NETWORK_fdset_destroy (gws);
1380   GNUNET_NETWORK_fdset_destroy (grs);
1381
1382 }
1383
1384
1385 /**
1386  * Task that is run when we are ready to receive more data
1387  * from curl
1388  *
1389  * @param cls closure
1390  * @param tc task context
1391  */
1392 static void
1393 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1394 {
1395   int running;
1396   int msgnum;
1397   struct CURLMsg *msg;
1398   CURLMcode mret;
1399   struct ProxyCurlTask *ctask;
1400   int num_ctasks;
1401   long resp_code;
1402
1403   struct ProxyCurlTask *clean_head = NULL;
1404   struct ProxyCurlTask *clean_tail = NULL;
1405
1406   curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1407
1408   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1409   {
1410     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1411                 "Shutdown requested while trying to download\n");
1412     //TODO cleanup
1413     return;
1414   }
1415   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1416               "Ready to dl\n");
1417
1418   do
1419   {
1420     running = 0;
1421     num_ctasks = 0;
1422     
1423     mret = curl_multi_perform (curl_multi, &running);
1424
1425     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1426                 "Running curl tasks: %d\n", running);
1427
1428     ctask = ctasks_head;
1429     for (; ctask != NULL; ctask = ctask->next)
1430     {
1431       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1432                   "CTask: %s\n", ctask->url);
1433       num_ctasks++;
1434     }
1435
1436     if (num_ctasks != running)
1437     {
1438       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1439                   "%d ctasks, %d curl running\n", num_ctasks, running);
1440     }
1441     
1442     do
1443     {
1444       ctask = ctasks_head;
1445       msg = curl_multi_info_read (curl_multi, &msgnum);
1446       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1447                   "Messages left: %d\n", msgnum);
1448       
1449       if (msg == NULL)
1450         break;
1451       switch (msg->msg)
1452       {
1453        case CURLMSG_DONE:
1454          if ((msg->data.result != CURLE_OK) &&
1455              (msg->data.result != CURLE_GOT_NOTHING))
1456          {
1457            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1458                        "Download curl failed");
1459             
1460            for (; ctask != NULL; ctask = ctask->next)
1461            {
1462              if (NULL == ctask->curl)
1463                continue;
1464
1465              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
1466                continue;
1467              
1468              GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1469                          "CURL: Download failed for task %s: %s.\n",
1470                          ctask->url,
1471                          curl_easy_strerror (msg->data.result));
1472              ctask->download_is_finished = GNUNET_YES;
1473              ctask->download_error = GNUNET_YES;
1474              if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1475                                                 CURLINFO_RESPONSE_CODE,
1476                                                 &resp_code))
1477                ctask->curl_response_code = resp_code;
1478              ctask->ready_to_queue = MHD_YES;
1479              ctask->buf_status = BUF_WAIT_FOR_MHD;
1480              run_mhd_now (ctask->mhd);
1481              
1482              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1483                                           ctask);
1484              GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1485              break;
1486            }
1487            GNUNET_assert (ctask != NULL);
1488          }
1489          else
1490          {
1491            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1492                        "CURL: download completed.\n");
1493
1494            for (; ctask != NULL; ctask = ctask->next)
1495            {
1496              if (NULL == ctask->curl)
1497                continue;
1498
1499              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
1500                continue;
1501              
1502              if (ctask->buf_status != BUF_WAIT_FOR_CURL)
1503                continue;
1504              
1505              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1506                          "CURL: completed task %s found.\n", ctask->url);
1507              if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1508                                                 CURLINFO_RESPONSE_CODE,
1509                                                 &resp_code))
1510                ctask->curl_response_code = resp_code;
1511
1512
1513              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1514                          "CURL: Completed ctask!\n");
1515              if (GNUNET_SCHEDULER_NO_TASK == ctask->pp_task)
1516              {
1517               ctask->buf_status = BUF_WAIT_FOR_PP;
1518               ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer,
1519                                                           ctask);
1520              }
1521
1522              ctask->ready_to_queue = MHD_YES;
1523              ctask->download_is_finished = GNUNET_YES;
1524
1525              /* We MUST not modify the multi handle else we loose messages */
1526              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1527                                           ctask);
1528              GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1529
1530              break;
1531            }
1532            GNUNET_assert (ctask != NULL);
1533          }
1534          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1535                      "CURL: %s\n", curl_easy_strerror(msg->data.result));
1536          break;
1537        default:
1538          GNUNET_assert (0);
1539          break;
1540       }
1541     } while (msgnum > 0);
1542
1543     for (ctask=clean_head; ctask != NULL; ctask = ctask->next)
1544     {
1545       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1546                   "CURL: Removing task %s.\n", ctask->url);
1547       curl_multi_remove_handle (curl_multi, ctask->curl);
1548       curl_easy_cleanup (ctask->curl);
1549       ctask->curl = NULL;
1550     }
1551     
1552     num_ctasks=0;
1553     for (ctask=ctasks_head; ctask != NULL; ctask = ctask->next)
1554     {
1555       num_ctasks++;
1556     }
1557     
1558     if (num_ctasks != running)
1559     {
1560       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1561                   "CURL: %d tasks, %d running\n", num_ctasks, running);
1562     }
1563
1564     GNUNET_assert ( num_ctasks == running );
1565
1566   } while (mret == CURLM_CALL_MULTI_PERFORM);
1567   
1568   if (mret != CURLM_OK)
1569   {
1570     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CURL: %s failed at %s:%d: `%s'\n",
1571                 "curl_multi_perform", __FILE__, __LINE__,
1572                 curl_multi_strerror (mret));
1573   }
1574   curl_download_prepare();
1575 }
1576
1577 /**
1578  * Process LEHO lookup
1579  *
1580  * @param cls the ctask
1581  * @param rd_count number of records returned
1582  * @param rd record data
1583  */
1584 static void
1585 process_leho_lookup (void *cls,
1586                      uint32_t rd_count,
1587                      const struct GNUNET_NAMESTORE_RecordData *rd)
1588 {
1589   struct ProxyCurlTask *ctask = cls;
1590   char hosthdr[262]; //256 + "Host: "
1591   int i;
1592   CURLcode ret;
1593   CURLMcode mret;
1594   struct hostent *phost;
1595   char *ssl_ip;
1596   char resolvename[512];
1597   char curlurl[512];
1598
1599   strcpy (ctask->leho, "");
1600
1601   if (rd_count == 0)
1602     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1603                 "No LEHO present!\n");
1604
1605   for (i=0; i<rd_count; i++)
1606   {
1607     if (rd[i].record_type != GNUNET_GNS_RECORD_LEHO)
1608       continue;
1609
1610     memcpy (ctask->leho, rd[i].data, rd[i].data_size);
1611
1612     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1613                 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
1614   }
1615
1616   if (0 != strcmp (ctask->leho, ""))
1617   {
1618     sprintf (hosthdr, "%s%s", "Host: ", ctask->leho);
1619     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1620                 "New HTTP header value: %s\n", hosthdr);
1621     ctask->headers = curl_slist_append (ctask->headers, hosthdr);
1622     GNUNET_assert (NULL != ctask->headers);
1623     ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers);
1624     if (CURLE_OK != ret)
1625     {
1626       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s failed at %s:%d: `%s'\n",
1627                            "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret));
1628     }
1629
1630   }
1631
1632   if (ctask->mhd->is_ssl)
1633   {
1634     phost = (struct hostent*)gethostbyname (ctask->host);
1635
1636     if (phost!=NULL)
1637     {
1638       ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
1639       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1640                   "SSL target server: %s\n", ssl_ip);
1641       sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
1642       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1643                   "Curl resolve: %s\n", resolvename);
1644       ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
1645       curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver);
1646       sprintf (curlurl, "https://%s%s", ctask->leho, ctask->url);
1647       curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1648     }
1649     else
1650     {
1651       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1652                   "gethostbyname failed for %s!\n", ctask->host);
1653       ctask->download_is_finished = GNUNET_YES;
1654       ctask->download_error = GNUNET_YES;
1655       return;
1656     }
1657   }
1658
1659   if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
1660   {
1661     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1662                 "%s failed at %s:%d: `%s'\n",
1663                 "curl_multi_add_handle", __FILE__, __LINE__,
1664                 curl_multi_strerror (mret));
1665     ctask->download_is_finished = GNUNET_YES;
1666     ctask->download_error = GNUNET_YES;
1667     return;
1668   }
1669   GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1670
1671   curl_download_prepare ();
1672
1673 }
1674
1675 /**
1676  * Initialize download and trigger curl
1677  *
1678  * @param cls the proxycurltask
1679  * @param auth_name the name of the authority (site of origin) of ctask->host
1680  *
1681  */
1682 static void
1683 process_get_authority (void *cls,
1684                        const char* auth_name)
1685 {
1686   struct ProxyCurlTask *ctask = cls;
1687
1688   if (NULL == auth_name)
1689   {
1690     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1691                 "Get authority failed!\n");
1692     strcpy (ctask->authority, "");
1693   }
1694   else
1695   {
1696     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1697                 "Get authority yielded %s\n", auth_name);
1698     strcpy (ctask->authority, auth_name);
1699   }
1700
1701   GNUNET_GNS_lookup_zone (gns_handle,
1702                           ctask->host,
1703                           &local_gns_zone,
1704                           GNUNET_GNS_RECORD_LEHO,
1705                           GNUNET_YES, //Only cached for performance
1706                           shorten_zonekey,
1707                           &process_leho_lookup,
1708                           ctask);
1709 }
1710
1711
1712 /**
1713  * Main MHD callback for handling requests.
1714  *
1715  * @param cls unused
1716  * @param con MHD connection handle
1717  * @param url the url in the request
1718  * @param meth the HTTP method used ("GET", "PUT", etc.)
1719  * @param ver the HTTP version string (i.e. "HTTP/1.1")
1720  * @param upload_data the data being uploaded (excluding HEADERS,
1721  *        for a POST that fits into memory and that is encoded
1722  *        with a supported encoding, the POST data will NOT be
1723  *        given in upload_data and is instead available as
1724  *        part of MHD_get_connection_values; very large POST
1725  *        data *will* be made available incrementally in
1726  *        upload_data)
1727  * @param upload_data_size set initially to the size of the
1728  *        upload_data provided; the method must update this
1729  *        value to the number of bytes NOT processed;
1730  * @param con_cls pointer to location where we store the 'struct Request'
1731  * @return MHD_YES if the connection was handled successfully,
1732  *         MHD_NO if the socket must be closed due to a serious
1733  *         error while handling the request
1734  */
1735 static int
1736 create_response (void *cls,
1737                  struct MHD_Connection *con,
1738                  const char *url,
1739                  const char *meth,
1740                  const char *ver,
1741                  const char *upload_data,
1742                  size_t *upload_data_size,
1743                  void **con_cls)
1744 {
1745   struct MhdHttpList* hd = cls;
1746   const char* page = "<html><head><title>gnoxy</title>"\
1747                       "</head><body>cURL fail</body></html>";
1748   
1749   char curlurl[MAX_HTTP_URI_LENGTH]; // buffer overflow!
1750   int ret = MHD_YES;
1751
1752   struct ProxyCurlTask *ctask;
1753   struct ProxyPostData *fin_post;
1754   
1755   //FIXME handle
1756   if ((0 != strcasecmp (meth, MHD_HTTP_METHOD_GET)) &&
1757       (0 != strcasecmp (meth, MHD_HTTP_METHOD_PUT)) &&
1758       (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST)))
1759   {
1760     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1761                 "MHD: %s NOT IMPLEMENTED!\n", meth);
1762     return MHD_NO;
1763   }
1764
1765
1766   if (NULL == *con_cls)
1767   {
1768
1769     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1770                 "Got %s request for %s\n", meth, url);
1771     ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
1772     ctask->mhd = hd;
1773     *con_cls = ctask;
1774     
1775     ctask->curl = curl_easy_init();
1776     if (NULL == ctask->curl)
1777     {
1778       ctask->response = MHD_create_response_from_buffer (strlen (page),
1779                                                 (void*)page,
1780                                                 MHD_RESPMEM_PERSISTENT);
1781       ret = MHD_queue_response (con,
1782                                 MHD_HTTP_OK,
1783                                 ctask->response);
1784       MHD_destroy_response (ctask->response);
1785       GNUNET_free (ctask);
1786       return ret;
1787     }
1788   
1789     ctask->download_in_progress = GNUNET_YES;
1790     ctask->buf_status = BUF_WAIT_FOR_CURL;
1791     ctask->connection = con;
1792     ctask->curl_response_code = MHD_HTTP_OK;
1793     ctask->buffer_read_ptr = ctask->buffer;
1794     ctask->buffer_write_ptr = ctask->buffer;
1795     ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
1796
1797     MHD_get_connection_values (con,
1798                                MHD_HEADER_KIND,
1799                                &con_val_iter, ctask);
1800
1801     if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
1802     {
1803       if (0 == *upload_data_size)
1804       {
1805         curl_easy_cleanup (ctask->curl);
1806         GNUNET_free (ctask);
1807         return MHD_NO;
1808       }
1809       ctask->put_read_offset = 0;
1810       ctask->put_read_size = *upload_data_size;
1811       curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1);
1812       curl_easy_setopt (ctask->curl, CURLOPT_READDATA, upload_data);
1813       //curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &curl_read_cb);
1814       
1815       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1816                   "Got PUT data: %s\n", upload_data);
1817       curl_easy_cleanup (ctask->curl);
1818       GNUNET_free (ctask);
1819       return MHD_NO;
1820     }
1821
1822     if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
1823     {
1824       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1825                   "Setting up POST processor\n");
1826       ctask->post_handler = MHD_create_post_processor (con,
1827                                  POSTBUFFERSIZE,
1828                                  &con_post_data_iter,
1829                                  ctask);
1830       curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
1831       curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION,
1832                         &read_callback);
1833       curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
1834       ctask->headers = curl_slist_append (ctask->headers,
1835                                           "Transfer-Encoding: chunked");
1836       /*curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
1837       curl_easy_setopt (ctask->curl, CURLOPT_POSTFIELDSIZE, *upload_data_size);
1838       curl_easy_setopt (ctask->curl, CURLOPT_COPYPOSTFIELDS, upload_data);
1839       
1840       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1841                   "Got POST data: %s\n", upload_data);
1842       curl_easy_cleanup (ctask->curl);
1843       GNUNET_free (ctask);
1844       return MHD_NO;*/
1845     }
1846
1847     curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1848     curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
1849     curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1850     curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
1851     curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 0);
1852     curl_easy_setopt (ctask->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1853     
1854     if (GNUNET_NO == ctask->mhd->is_ssl)
1855     {
1856       sprintf (curlurl, "http://%s%s", ctask->host, url);
1857       MHD_get_connection_values (con,
1858                                  MHD_GET_ARGUMENT_KIND,
1859                                  &get_uri_val_iter, curlurl);
1860       curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1861     }
1862     strcpy (ctask->url, url);
1863     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1864                   "MHD: Adding new curl task for %s%s\n", ctask->host, url);
1865     MHD_get_connection_values (con,
1866                                MHD_GET_ARGUMENT_KIND,
1867                                &get_uri_val_iter, ctask->url);
1868   
1869     curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
1870     curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
1871     curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
1872
1873     GNUNET_GNS_get_authority (gns_handle,
1874                               ctask->host,
1875                               &process_get_authority,
1876                               ctask);
1877     ctask->ready_to_queue = GNUNET_NO;
1878     ctask->fin = GNUNET_NO;
1879     return MHD_YES;
1880   }
1881
1882   ctask = (struct ProxyCurlTask *) *con_cls;
1883   if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
1884   {
1885     if (0 != *upload_data_size)
1886     {
1887       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1888                   "Invoking POST processor\n");
1889       MHD_post_process (ctask->post_handler,
1890                         upload_data, *upload_data_size);
1891       *upload_data_size = 0;
1892       return MHD_YES;
1893     }
1894     else if (GNUNET_NO == ctask->post_done)
1895     {
1896       fin_post = GNUNET_malloc (sizeof (struct ProxyPostData));
1897       GNUNET_CONTAINER_DLL_insert_tail (ctask->post_data_head,
1898                                         ctask->post_data_tail,
1899                                         fin_post);
1900       ctask->post_done = GNUNET_YES;
1901       return MHD_YES;
1902     }
1903   }
1904   
1905   if (GNUNET_YES != ctask->ready_to_queue)
1906     return MHD_YES; /* wait longer */
1907   
1908   if (GNUNET_YES == ctask->fin)
1909     return MHD_YES;
1910
1911   ctask->fin = GNUNET_YES;
1912   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1913               "MHD: Queueing response for %s\n", ctask->url);
1914   ret = MHD_queue_response (con, ctask->curl_response_code, ctask->response);
1915   run_mhd_now (ctask->mhd);
1916   return ret;
1917 }
1918
1919
1920 /**
1921  * run all httpd
1922  */
1923 static void
1924 run_httpds ()
1925 {
1926   struct MhdHttpList *hd;
1927
1928   for (hd=mhd_httpd_head; NULL != hd; hd = hd->next)
1929     run_httpd (hd);
1930
1931 }
1932
1933 /**
1934  * schedule mhd
1935  *
1936  * @param hd the daemon to run
1937  */
1938 static void
1939 run_httpd (struct MhdHttpList *hd)
1940 {
1941   fd_set rs;
1942   fd_set ws;
1943   fd_set es;
1944   struct GNUNET_NETWORK_FDSet *wrs;
1945   struct GNUNET_NETWORK_FDSet *wws;
1946   struct GNUNET_NETWORK_FDSet *wes;
1947   int max;
1948   int haveto;
1949   unsigned MHD_LONG_LONG timeout;
1950   struct GNUNET_TIME_Relative tv;
1951
1952   FD_ZERO (&rs);
1953   FD_ZERO (&ws);
1954   FD_ZERO (&es);
1955   wrs = GNUNET_NETWORK_fdset_create ();
1956   wes = GNUNET_NETWORK_fdset_create ();
1957   wws = GNUNET_NETWORK_fdset_create ();
1958   max = -1;
1959   GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
1960   
1961   
1962   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1963               "MHD fds: max=%d\n", max);
1964   
1965   haveto = MHD_get_timeout (hd->daemon, &timeout);
1966
1967   if (MHD_YES == haveto)
1968     tv.rel_value = (uint64_t) timeout;
1969   else
1970     tv = GNUNET_TIME_UNIT_FOREVER_REL;
1971   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
1972   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
1973   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
1974   
1975   if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
1976     GNUNET_SCHEDULER_cancel (hd->httpd_task);
1977   hd->httpd_task =
1978     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
1979                                  tv, wrs, wws,
1980                                  &do_httpd, hd);
1981   GNUNET_NETWORK_fdset_destroy (wrs);
1982   GNUNET_NETWORK_fdset_destroy (wws);
1983   GNUNET_NETWORK_fdset_destroy (wes);
1984 }
1985
1986
1987 /**
1988  * Task run whenever HTTP server operations are pending.
1989  *
1990  * @param cls unused
1991  * @param tc sched context
1992  */
1993 static void
1994 do_httpd (void *cls,
1995           const struct GNUNET_SCHEDULER_TaskContext *tc)
1996 {
1997   struct MhdHttpList *hd = cls;
1998   
1999   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2000               "MHD: Main loop\n");
2001   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK; 
2002   MHD_run (hd->daemon);
2003   run_httpd (hd);
2004 }
2005
2006
2007
2008 /**
2009  * Read data from socket
2010  *
2011  * @param cls the closure
2012  * @param tc scheduler context
2013  */
2014 static void
2015 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2016
2017 /**
2018  * Read from remote end
2019  *
2020  * @param cls closure
2021  * @param tc scheduler context
2022  */
2023 static void
2024 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2025
2026 /**
2027  * Write data to remote socket
2028  *
2029  * @param cls the closure
2030  * @param tc scheduler context
2031  */
2032 static void
2033 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2034 {
2035   struct Socks5Request *s5r = cls;
2036   unsigned int len;
2037
2038   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2039
2040   if ((NULL != tc->read_ready) &&
2041       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
2042       ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
2043                                          s5r->rbuf_len)>0)))
2044   {
2045     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2046                 "Successfully sent %d bytes to remote socket\n",
2047                 len);
2048   }
2049   else
2050   {
2051     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
2052     if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
2053       GNUNET_SCHEDULER_cancel (s5r->rtask);
2054     if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
2055       GNUNET_SCHEDULER_cancel (s5r->wtask);
2056     if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
2057       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2058     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2059     GNUNET_NETWORK_socket_close (s5r->sock);
2060     GNUNET_free(s5r);
2061     return;
2062   }
2063
2064   s5r->rtask =
2065     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2066                                    s5r->sock,
2067                                    &do_read, s5r);
2068 }
2069
2070
2071 /**
2072  * Clean up s5r handles
2073  *
2074  * @param s5r the handle to destroy
2075  */
2076 static void
2077 cleanup_s5r (struct Socks5Request *s5r)
2078 {
2079   if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
2080     GNUNET_SCHEDULER_cancel (s5r->rtask);
2081   if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdwtask)
2082     GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2083   if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
2084     GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2085   
2086   if (NULL != s5r->remote_sock)
2087     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2088   if ((NULL != s5r->sock) && (s5r->cleanup_sock == GNUNET_YES))
2089     GNUNET_NETWORK_socket_close (s5r->sock);
2090   
2091   GNUNET_free(s5r);
2092 }
2093
2094 /**
2095  * Write data to socket
2096  *
2097  * @param cls the closure
2098  * @param tc scheduler context
2099  */
2100 static void
2101 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2102 {
2103   struct Socks5Request *s5r = cls;
2104   unsigned int len;
2105
2106   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2107
2108   if ((NULL != tc->read_ready) &&
2109       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
2110       ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
2111                                          s5r->wbuf_len)>0)))
2112   {
2113     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2114                 "Successfully sent %d bytes to socket\n",
2115                 len);
2116   }
2117   else
2118   {
2119     
2120     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
2121     s5r->cleanup = GNUNET_YES;
2122     s5r->cleanup_sock = GNUNET_YES;
2123     cleanup_s5r (s5r);
2124     
2125     return;
2126   }
2127
2128   if (GNUNET_YES == s5r->cleanup)
2129   {
2130     cleanup_s5r (s5r);
2131     return;
2132   }
2133
2134   if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
2135       (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
2136     s5r->fwdrtask =
2137       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2138                                      s5r->remote_sock,
2139                                      &do_read_remote, s5r);
2140 }
2141
2142 /**
2143  * Read from remote end
2144  *
2145  * @param cls closure
2146  * @param tc scheduler context
2147  */
2148 static void
2149 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2150 {
2151   struct Socks5Request *s5r = cls;
2152   
2153   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2154
2155
2156   if ((NULL != tc->write_ready) &&
2157       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
2158       (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
2159                                          sizeof (s5r->wbuf))))
2160   {
2161     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2162                 "Successfully read %d bytes from remote socket\n",
2163                 s5r->wbuf_len);
2164   }
2165   else
2166   {
2167     if (s5r->wbuf_len == 0)
2168       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2169                   "0 bytes received from remote... graceful shutdown!\n");
2170     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2171       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2172     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
2173       GNUNET_SCHEDULER_cancel (s5r->rtask);
2174     
2175     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2176     s5r->remote_sock = NULL;
2177     GNUNET_NETWORK_socket_close (s5r->sock);
2178     GNUNET_free(s5r);
2179
2180     return;
2181   }
2182   
2183   s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2184                                                s5r->sock,
2185                                                &do_write, s5r);
2186   
2187 }
2188
2189
2190 /**
2191  * Adds a socket to MHD
2192  *
2193  * @param h the handle to the socket to add
2194  * @param daemon the daemon to add the fd to
2195  * @return whatever MHD_add_connection returns
2196  */
2197 static int
2198 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon)
2199 {
2200   int fd;
2201   struct sockaddr *addr;
2202   socklen_t len;
2203
2204   fd = dup (GNUNET_NETWORK_get_fd (h));
2205   addr = GNUNET_NETWORK_get_addr (h);
2206   len = GNUNET_NETWORK_get_addrlen (h);
2207
2208   return MHD_add_connection (daemon, fd, addr, len);
2209 }
2210
2211 /**
2212  * Read file in filename
2213  *
2214  * @param filename file to read
2215  * @param size pointer where filesize is stored
2216  * @return data
2217  */
2218 static char*
2219 load_file (const char* filename, 
2220            unsigned int* size)
2221 {
2222   char *buffer;
2223   uint64_t fsize;
2224
2225   if (GNUNET_OK !=
2226       GNUNET_DISK_file_size (filename, &fsize,
2227                              GNUNET_YES, GNUNET_YES))
2228     return NULL;
2229   if (fsize > MAX_PEM_SIZE)
2230     return NULL;
2231   *size = (unsigned int) fsize;
2232   buffer = GNUNET_malloc (*size);
2233   if (fsize != GNUNET_DISK_fn_read (filename, buffer, (size_t) fsize))
2234   {
2235     GNUNET_free (buffer);
2236     return NULL;
2237   }
2238   return buffer;
2239 }
2240
2241
2242 /**
2243  * Load PEM key from file
2244  *
2245  * @param key where to store the data
2246  * @param keyfile path to the PEM file
2247  * @return GNUNET_OK on success
2248  */
2249 static int
2250 load_key_from_file (gnutls_x509_privkey_t key, const char* keyfile)
2251 {
2252   gnutls_datum_t key_data;
2253   int ret;
2254
2255   key_data.data = (unsigned char*) load_file (keyfile, &key_data.size);
2256   ret = gnutls_x509_privkey_import (key, &key_data,
2257                                     GNUTLS_X509_FMT_PEM);
2258   if (GNUTLS_E_SUCCESS != ret)
2259   {
2260     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2261                 _("Unable to import private key from file `%s'\n"),
2262                 keyfile);
2263     GNUNET_break (0);
2264   }
2265   GNUNET_free (key_data.data);
2266   return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2267 }
2268
2269
2270 /**
2271  * Load cert from file
2272  *
2273  * @param crt struct to store data in
2274  * @param certfile path to pem file
2275  * @return GNUNET_OK on success
2276  */
2277 static int
2278 load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
2279 {
2280   gnutls_datum_t cert_data;
2281   cert_data.data = NULL;
2282   int ret;
2283
2284   cert_data.data = (unsigned char*) load_file (certfile, &cert_data.size);
2285   ret = gnutls_x509_crt_import (crt, &cert_data,
2286                                 GNUTLS_X509_FMT_PEM);
2287   if (GNUTLS_E_SUCCESS != ret)
2288   {
2289     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2290                _("Unable to import certificate %s\n"), certfile);
2291     GNUNET_break (0);
2292   }
2293   GNUNET_free (cert_data.data);
2294   return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2295 }
2296
2297
2298 /**
2299  * Generate new certificate for specific name
2300  *
2301  * @param name the subject name to generate a cert for
2302  * @return a struct holding the PEM data
2303  */
2304 static struct ProxyGNSCertificate *
2305 generate_gns_certificate (const char *name)
2306 {
2307
2308   int ret;
2309   unsigned int serial;
2310   size_t key_buf_size;
2311   size_t cert_buf_size;
2312   gnutls_x509_crt_t request;
2313   time_t etime;
2314   struct tm *tm_data;
2315
2316   ret = gnutls_x509_crt_init (&request);
2317
2318   if (GNUTLS_E_SUCCESS != ret)
2319   {
2320     GNUNET_break (0);
2321   }
2322
2323   ret = gnutls_x509_crt_set_key (request, proxy_ca.key);
2324
2325   if (GNUTLS_E_SUCCESS != ret)
2326   {
2327     GNUNET_break (0);
2328   }
2329
2330   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
2331
2332   struct ProxyGNSCertificate *pgc =
2333     GNUNET_malloc (sizeof (struct ProxyGNSCertificate));
2334
2335   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\n");
2336   
2337   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
2338                                  0, "DE", 2);
2339
2340   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
2341                                  0, "GNUnet", 6);
2342
2343   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
2344                                  0, name, strlen (name));
2345
2346   ret = gnutls_x509_crt_set_version (request, 3);
2347
2348   ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
2349
2350   etime = time (NULL);
2351   tm_data = localtime (&etime);
2352   
2353
2354   ret = gnutls_x509_crt_set_serial (request,
2355                                     &serial,
2356                                     sizeof (serial));
2357
2358   ret = gnutls_x509_crt_set_activation_time (request,
2359                                              etime);
2360   tm_data->tm_year++;
2361   etime = mktime (tm_data);
2362
2363   if (-1 == etime)
2364   {
2365     GNUNET_break (0);
2366   }
2367
2368   ret = gnutls_x509_crt_set_expiration_time (request,
2369                                              etime);
2370   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
2371
2372   ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
2373
2374   key_buf_size = sizeof (pgc->key);
2375   cert_buf_size = sizeof (pgc->cert);
2376
2377   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Exporting certificate...\n");
2378   
2379   gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
2380                           pgc->cert, &cert_buf_size);
2381
2382   gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
2383                           pgc->key, &key_buf_size);
2384
2385
2386   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
2387   gnutls_x509_crt_deinit (request);
2388
2389   return pgc;
2390
2391 }
2392
2393
2394 /*
2395  * Accept policy for mhdaemons
2396  *
2397  * @param cls NULL
2398  * @param addr the sockaddr
2399  * @param addrlen the sockaddr length
2400  * @return MHD_NO if sockaddr is wrong or #conns too high
2401  */
2402 static int
2403 accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen)
2404 {
2405   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2406               "In MHD accept policy cb\n");
2407
2408   if (addr != NULL)
2409   {
2410     if (addr->sa_family == AF_UNIX)
2411       return MHD_NO;
2412   }
2413
2414   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2415               "Connection accepted\n");
2416
2417   return MHD_YES;
2418 }
2419
2420
2421 /**
2422  * Adds a socket to an SSL MHD instance
2423  * It is important the the domain name is
2424  * correct. In most cases we need to start a new daemon
2425  *
2426  * @param h the handle to add to a daemon
2427  * @param domain the domain the ssl daemon has to serve
2428  * @return MHD_YES on success
2429  */
2430 static int
2431 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, const char* domain)
2432 {
2433   struct MhdHttpList *hd = NULL;
2434   struct ProxyGNSCertificate *pgc;
2435   struct NetworkHandleList *nh;
2436
2437   for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
2438     if (0 == strcmp (hd->domain, domain))
2439       break;
2440
2441   if (NULL == hd)
2442   {    
2443     pgc = generate_gns_certificate (domain);
2444     
2445     hd = GNUNET_malloc (sizeof (struct MhdHttpList));
2446     hd->is_ssl = GNUNET_YES;
2447     strcpy (hd->domain, domain);
2448     hd->proxy_cert = pgc;
2449
2450     /* Start new MHD */
2451     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2452                 "No previous SSL instance found... starting new one for %s\n",
2453                 domain);
2454
2455     hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL, 4444,
2456             &accept_cb, NULL,
2457             &create_response, hd,
2458             MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
2459             MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
2460             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2461             MHD_OPTION_NOTIFY_COMPLETED,
2462             NULL, NULL,
2463             MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2464             MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2465             MHD_OPTION_END);
2466
2467     GNUNET_assert (hd->daemon != NULL);
2468     hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2469     
2470     GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2471   }
2472
2473   nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
2474   nh->h = h;
2475
2476   GNUNET_CONTAINER_DLL_insert (hd->socket_handles_head,
2477                                hd->socket_handles_tail,
2478                                nh);
2479   
2480   return add_handle_to_mhd (h, hd->daemon);
2481 }
2482
2483
2484
2485 /**
2486  * Read data from incoming connection
2487  *
2488  * @param cls the closure
2489  * @param tc the scheduler context
2490  */
2491 static void
2492 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2493 {
2494   struct Socks5Request *s5r = cls;
2495   struct socks5_client_hello *c_hello;
2496   struct socks5_server_hello *s_hello;
2497   struct socks5_client_request *c_req;
2498   struct socks5_server_response *s_resp;
2499
2500   int ret;
2501   char domain[256];
2502   uint8_t dom_len;
2503   uint16_t req_port;
2504   struct hostent *phost;
2505   uint32_t remote_ip;
2506   struct sockaddr_in remote_addr;
2507   struct in_addr *r_sin_addr;
2508
2509   struct NetworkHandleList *nh;
2510
2511   s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
2512
2513   if ((NULL != tc->write_ready) &&
2514       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
2515       (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
2516                                          sizeof (s5r->rbuf))))
2517   {
2518     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2519                 "Successfully read %d bytes from socket\n",
2520                 s5r->rbuf_len);
2521   }
2522   else
2523   {
2524     if (s5r->rbuf_len != 0)
2525       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
2526     else
2527       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
2528
2529     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2530       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2531     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
2532       GNUNET_SCHEDULER_cancel (s5r->wtask);
2533     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2534       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2535     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2536     GNUNET_NETWORK_socket_close (s5r->sock);
2537     GNUNET_free(s5r);
2538     return;
2539   }
2540
2541   if (s5r->state == SOCKS5_INIT)
2542   {
2543     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2544                 "SOCKS5 init\n");
2545     c_hello = (struct socks5_client_hello*)&s5r->rbuf;
2546
2547     GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
2548
2549     s_hello = (struct socks5_server_hello*)&s5r->wbuf;
2550     s5r->wbuf_len = sizeof( struct socks5_server_hello );
2551
2552     s_hello->version = c_hello->version;
2553     s_hello->auth_method = SOCKS_AUTH_NONE;
2554
2555     /* Write response to client */
2556     s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2557                                                 s5r->sock,
2558                                                 &do_write, s5r);
2559
2560     s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2561                                                 s5r->sock,
2562                                                 &do_read, s5r);
2563
2564     s5r->state = SOCKS5_REQUEST;
2565     return;
2566   }
2567
2568   if (s5r->state == SOCKS5_REQUEST)
2569   {
2570     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2571                 "Processing SOCKS5 request\n");
2572     c_req = (struct socks5_client_request*)&s5r->rbuf;
2573     s_resp = (struct socks5_server_response*)&s5r->wbuf;
2574     //Only 10byte for ipv4 response!
2575     s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
2576
2577     GNUNET_assert (c_req->addr_type == 3);
2578
2579     dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
2580     memset(domain, 0, sizeof(domain));
2581     strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
2582     req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
2583
2584     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2585                 "Requested connection is %s:%d\n",
2586                 domain,
2587                 ntohs(req_port));
2588
2589     if (is_tld (domain, GNUNET_GNS_TLD) ||
2590         is_tld (domain, GNUNET_GNS_TLD_ZKEY))
2591     {
2592       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2593                   "Requested connection is gnunet tld\n",
2594                   domain);
2595       
2596       ret = MHD_NO;
2597       if (ntohs(req_port) == HTTPS_PORT)
2598       {
2599         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2600                     "Requested connection is HTTPS\n");
2601         ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
2602       }
2603       else if (NULL != httpd)
2604       {
2605         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2606                     "Requested connection is HTTP\n");
2607         nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
2608         nh->h = s5r->sock;
2609
2610         GNUNET_CONTAINER_DLL_insert (mhd_httpd_head->socket_handles_head,
2611                                mhd_httpd_head->socket_handles_tail,
2612                                nh);
2613
2614         ret = add_handle_to_mhd ( s5r->sock, httpd );
2615       }
2616
2617       if (ret != MHD_YES)
2618       {
2619         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2620                     _("Failed to start HTTP server\n"));
2621         s_resp->version = 0x05;
2622         s_resp->reply = 0x01;
2623         s5r->cleanup = GNUNET_YES;
2624         s5r->cleanup_sock = GNUNET_YES;
2625         s5r->wtask = 
2626           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2627                                         s5r->sock,
2628                                         &do_write, s5r);
2629         return;
2630       }
2631       
2632       /* Signal success */
2633       s_resp->version = 0x05;
2634       s_resp->reply = 0x00;
2635       s_resp->reserved = 0x00;
2636       s_resp->addr_type = 0x01;
2637       
2638       s5r->cleanup = GNUNET_YES;
2639       s5r->cleanup_sock = GNUNET_NO;
2640       s5r->wtask =
2641         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2642                                         s5r->sock,
2643                                         &do_write, s5r);
2644       run_httpds ();
2645       return;
2646     }
2647     else
2648     {
2649       phost = (struct hostent*)gethostbyname (domain);
2650       if (phost == NULL)
2651       {
2652         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2653                     "Resolve %s error!\n", domain );
2654         s_resp->version = 0x05;
2655         s_resp->reply = 0x01;
2656         s5r->cleanup = GNUNET_YES;
2657         s5r->cleanup_sock = GNUNET_YES;
2658         s5r->wtask = 
2659           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2660                                           s5r->sock,
2661                                           &do_write, s5r);
2662         return;
2663       }
2664
2665       s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
2666                                                        SOCK_STREAM,
2667                                                        0);
2668       r_sin_addr = (struct in_addr*)(phost->h_addr);
2669       remote_ip = r_sin_addr->s_addr;
2670       memset(&remote_addr, 0, sizeof(remote_addr));
2671       remote_addr.sin_family = AF_INET;
2672 #if HAVE_SOCKADDR_IN_SIN_LEN
2673       remote_addr.sin_len = sizeof (remote_addr);
2674 #endif
2675       remote_addr.sin_addr.s_addr = remote_ip;
2676       remote_addr.sin_port = req_port;
2677       
2678       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2679                   "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
2680                   ntohs(req_port));
2681
2682       if ((GNUNET_OK !=
2683           GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
2684                                           (const struct sockaddr*)&remote_addr,
2685                                           sizeof (remote_addr)))
2686           && (errno != EINPROGRESS))
2687       {
2688         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
2689         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2690                     "socket request error...\n");
2691         s_resp->version = 0x05;
2692         s_resp->reply = 0x01;
2693         s5r->wtask =
2694           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2695                                           s5r->sock,
2696                                           &do_write, s5r);
2697         //TODO see above
2698         return;
2699       }
2700
2701       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2702                   "new remote connection\n");
2703
2704       s_resp->version = 0x05;
2705       s_resp->reply = 0x00;
2706       s_resp->reserved = 0x00;
2707       s_resp->addr_type = 0x01;
2708
2709       s5r->state = SOCKS5_DATA_TRANSFER;
2710
2711       s5r->wtask =
2712         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2713                                         s5r->sock,
2714                                         &do_write, s5r);
2715       s5r->rtask =
2716         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2717                                        s5r->sock,
2718                                        &do_read, s5r);
2719
2720     }
2721     return;
2722   }
2723
2724   if (s5r->state == SOCKS5_DATA_TRANSFER)
2725   {
2726     if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
2727     {
2728       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2729                   "Closing connection to client\n");
2730       if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
2731         GNUNET_SCHEDULER_cancel (s5r->rtask);
2732       if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2733         GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2734       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2735         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2736       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2737         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2738       
2739       if (s5r->remote_sock != NULL)
2740         GNUNET_NETWORK_socket_close (s5r->remote_sock);
2741       GNUNET_NETWORK_socket_close (s5r->sock);
2742       GNUNET_free(s5r);
2743       return;
2744     }
2745
2746     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2747                 "forwarding %d bytes from client\n", s5r->rbuf_len);
2748
2749     s5r->fwdwtask =
2750       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2751                                       s5r->remote_sock,
2752                                       &do_write_remote, s5r);
2753
2754     if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
2755     {
2756       s5r->fwdrtask =
2757         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2758                                        s5r->remote_sock,
2759                                        &do_read_remote, s5r);
2760     }
2761
2762
2763   }
2764
2765 }
2766
2767
2768 /**
2769  * Accept new incoming connections
2770  *
2771  * @param cls the closure
2772  * @param tc the scheduler context
2773  */
2774 static void
2775 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2776 {
2777   struct GNUNET_NETWORK_Handle *s;
2778   struct Socks5Request *s5r;
2779
2780   ltask = GNUNET_SCHEDULER_NO_TASK;
2781   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
2782     return;
2783
2784   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2785                                          lsock,
2786                                          &do_accept, NULL);
2787
2788   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
2789
2790   if (NULL == s)
2791   {
2792     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
2793     return;
2794   }
2795
2796   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2797               "Got an inbound connection, waiting for data\n");
2798
2799   s5r = GNUNET_malloc (sizeof (struct Socks5Request));
2800   s5r->sock = s;
2801   s5r->state = SOCKS5_INIT;
2802   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2803   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2804   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2805   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2806                                               s5r->sock,
2807                                               &do_read, s5r);
2808 }
2809
2810
2811 /**
2812  * Task run on shutdown
2813  *
2814  * @param cls closure
2815  * @param tc task context
2816  */
2817 static void
2818 do_shutdown (void *cls,
2819              const struct GNUNET_SCHEDULER_TaskContext *tc)
2820 {
2821
2822   struct MhdHttpList *hd;
2823   struct MhdHttpList *tmp_hd;
2824   struct NetworkHandleList *nh;
2825   struct NetworkHandleList *tmp_nh;
2826   struct ProxyCurlTask *ctask;
2827   struct ProxyCurlTask *ctask_tmp;
2828   
2829   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2830               "Shutting down...\n");
2831
2832   gnutls_global_deinit ();
2833
2834   if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
2835   {
2836     GNUNET_SCHEDULER_cancel (curl_download_task);
2837     curl_download_task = GNUNET_SCHEDULER_NO_TASK;
2838   }
2839
2840   for (hd = mhd_httpd_head; hd != NULL; hd = tmp_hd)
2841   {
2842     tmp_hd = hd->next;
2843
2844     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2845                 "Stopping daemon\n");
2846
2847     if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2848     {
2849       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2850                   "Stopping select task %d\n",
2851                   hd->httpd_task);
2852       GNUNET_SCHEDULER_cancel (hd->httpd_task);
2853       hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2854     }
2855
2856     if (NULL != hd->daemon)
2857     {
2858       MHD_stop_daemon (hd->daemon);
2859       hd->daemon = NULL;
2860     }
2861
2862     for (nh = hd->socket_handles_head; nh != NULL; nh = tmp_nh)
2863     {
2864       tmp_nh = nh->next;
2865
2866       GNUNET_NETWORK_socket_close (nh->h);
2867
2868       GNUNET_free (nh);
2869     }
2870
2871     if (NULL != hd->proxy_cert)
2872     {
2873       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2874                   "Free certificate\n");
2875       GNUNET_free (hd->proxy_cert);
2876     }
2877
2878     GNUNET_free (hd);
2879   }
2880
2881   for (ctask=ctasks_head; ctask != NULL; ctask=ctask_tmp)
2882   {
2883     ctask_tmp = ctask->next;
2884
2885     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2886                 "Cleaning up cURL task\n");
2887
2888     if (ctask->curl != NULL)
2889       curl_easy_cleanup (ctask->curl);
2890     ctask->curl = NULL;
2891     if (NULL != ctask->headers)
2892       curl_slist_free_all (ctask->headers);
2893     if (NULL != ctask->resolver)
2894       curl_slist_free_all (ctask->resolver);
2895
2896     if (NULL != ctask->response)
2897       MHD_destroy_response (ctask->response);
2898
2899
2900     GNUNET_free (ctask);
2901   }
2902   curl_multi_cleanup (curl_multi);
2903
2904   GNUNET_GNS_disconnect (gns_handle);
2905 }
2906
2907
2908 /**
2909  * Compiles a regex for us
2910  *
2911  * @param re ptr to re struct
2912  * @param rt the expression to compile
2913  * @return 0 on success
2914  */
2915 static int
2916 compile_regex (regex_t *re, const char* rt)
2917 {
2918   int status;
2919   char err[1024];
2920
2921   status = regcomp (re, rt, REG_EXTENDED|REG_NEWLINE);
2922   if (status)
2923   {
2924     regerror (status, re, err, 1024);
2925     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2926                 "Regex error compiling '%s': %s\n", rt, err);
2927     return 1;
2928   }
2929   return 0;
2930 }
2931
2932
2933 /**
2934  * Loads the users local zone key
2935  *
2936  * @return GNUNET_YES on success
2937  */
2938 static int
2939 load_local_zone_key (const struct GNUNET_CONFIGURATION_Handle *cfg)
2940 {
2941   char *keyfile;
2942   struct GNUNET_CRYPTO_RsaPrivateKey *key = NULL;
2943   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
2944   struct GNUNET_CRYPTO_ShortHashCode *zone = NULL;
2945   struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename;
2946
2947   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
2948                                                             "ZONEKEY", &keyfile))
2949   {
2950     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2951                 "Unable to load zone key config value!\n");
2952     return GNUNET_NO;
2953   }
2954
2955   if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
2956   {
2957     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2958                 "Unable to load zone key %s!\n", keyfile);
2959     GNUNET_free(keyfile);
2960     return GNUNET_NO;
2961   }
2962
2963   key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
2964   GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
2965   GNUNET_CRYPTO_short_hash(&pkey,
2966                            sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
2967                            &local_gns_zone);
2968   zone = &local_gns_zone;
2969   GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
2970   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2971               "Using zone: %s!\n", &zonename);
2972   GNUNET_CRYPTO_rsa_key_free(key);
2973   GNUNET_free(keyfile);
2974   
2975   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
2976                                                    "PRIVATE_ZONEKEY", &keyfile))
2977   {
2978     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2979                 "Unable to load private zone key config value!\n");
2980     return GNUNET_NO;
2981   }
2982
2983   if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
2984   {
2985     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2986                 "Unable to load private zone key %s!\n", keyfile);
2987     GNUNET_free(keyfile);
2988     return GNUNET_NO;
2989   }
2990
2991   key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
2992   GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
2993   GNUNET_CRYPTO_short_hash(&pkey,
2994                            sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
2995                            &local_private_zone);
2996   GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
2997   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2998               "Using private zone: %s!\n", &zonename);
2999   GNUNET_CRYPTO_rsa_key_free(key);
3000   GNUNET_free(keyfile);
3001
3002   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3003                                                    "SHORTEN_ZONEKEY", &keyfile))
3004   {
3005     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3006                 "Unable to load shorten zone key config value!\n");
3007     return GNUNET_NO;
3008   }
3009
3010   if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
3011   {
3012     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3013                 "Unable to load shorten zone key %s!\n", keyfile);
3014     GNUNET_free(keyfile);
3015     return GNUNET_NO;
3016   }
3017
3018   key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
3019   GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
3020   GNUNET_CRYPTO_short_hash(&pkey,
3021                            sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
3022                            &local_shorten_zone);
3023   GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3024   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3025               "Using shorten zone: %s!\n", &zonename);
3026   GNUNET_CRYPTO_rsa_key_free(key);
3027   GNUNET_free(keyfile);
3028
3029   return GNUNET_YES;
3030 }
3031
3032 /**
3033  * Main function that will be run
3034  *
3035  * @param cls closure
3036  * @param args remaining command-line arguments
3037  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3038  * @param cfg configuration
3039  */
3040 static void
3041 run (void *cls, char *const *args, const char *cfgfile,
3042      const struct GNUNET_CONFIGURATION_Handle *cfg)
3043 {
3044   struct sockaddr_in sa;
3045   struct MhdHttpList *hd;
3046   struct sockaddr_un mhd_unix_sock_addr;
3047   size_t len;
3048   char* proxy_sockfile;
3049   char* cafile_cfg = NULL;
3050   char* cafile;
3051
3052   curl_multi = curl_multi_init ();
3053
3054   if (NULL == curl_multi)
3055   {
3056     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3057                 "Failed to create cURL multo handle!\n");
3058     return;
3059   }
3060   
3061   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3062               "Loading CA\n");
3063   cafile = cafile_opt;
3064   if (NULL == cafile)
3065   {
3066     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3067                                                           "PROXY_CACERT",
3068                                                           &cafile_cfg))
3069     {
3070       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3071                   "Unable to load proxy CA config value!\n");
3072       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3073                   "No proxy CA provided!\n");
3074       return;
3075     }
3076     cafile = cafile_cfg;
3077   }
3078   
3079   gnutls_global_init ();
3080   gnutls_x509_crt_init (&proxy_ca.cert);
3081   gnutls_x509_privkey_init (&proxy_ca.key);
3082   
3083   if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
3084        (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
3085   {
3086     // FIXME: release resources...
3087     return;
3088   }
3089
3090   GNUNET_free_non_null (cafile_cfg);
3091   
3092   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3093               "Loading Template\n");
3094   
3095   compile_regex (&re_dotplus, (char*) RE_A_HREF);
3096
3097   gns_handle = GNUNET_GNS_connect (cfg);
3098
3099   if (GNUNET_NO == load_local_zone_key (cfg))
3100   {
3101     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3102                 "Unable to load zone!\n");
3103     return;
3104   }
3105
3106   if (NULL == gns_handle)
3107   {
3108     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3109                 "Unable to connect to GNS!\n");
3110     return;
3111   }
3112
3113   memset (&sa, 0, sizeof (sa));
3114   sa.sin_family = AF_INET;
3115   sa.sin_port = htons (port);
3116 #if HAVE_SOCKADDR_IN_SIN_LEN
3117   sa.sin_len = sizeof (sa);
3118 #endif
3119
3120   lsock = GNUNET_NETWORK_socket_create (AF_INET,
3121                                         SOCK_STREAM,
3122                                         0);
3123
3124   if ((NULL == lsock) ||
3125       (GNUNET_OK !=
3126        GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
3127                                    sizeof (sa))))
3128   {
3129     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3130                 "Failed to create listen socket bound to `%s'",
3131                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
3132     if (NULL != lsock)
3133       GNUNET_NETWORK_socket_close (lsock);
3134     return;
3135   }
3136
3137   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
3138   {
3139     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3140                 "Failed to listen on socket bound to `%s'",
3141                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
3142     return;
3143   }
3144
3145   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3146                                          lsock, &do_accept, NULL);
3147
3148   ctasks_head = NULL;
3149   ctasks_tail = NULL;
3150
3151   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
3152   {
3153     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3154                 "cURL global init failed!\n");
3155     return;
3156   }
3157
3158   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3159               "Proxy listens on port %u\n",
3160               port);
3161
3162   mhd_httpd_head = NULL;
3163   mhd_httpd_tail = NULL;
3164   total_mhd_connections = 0;
3165
3166   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3167                                                             "PROXY_UNIXPATH",
3168                                                             &proxy_sockfile))
3169   {
3170     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3171                 "Specify PROXY_UNIXPATH in gns-proxy config section!\n");
3172     return;
3173   }
3174   
3175   mhd_unix_socket = GNUNET_NETWORK_socket_create (AF_UNIX,
3176                                                 SOCK_STREAM,
3177                                                 0);
3178
3179   if (NULL == mhd_unix_socket)
3180   {
3181     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3182                 "Unable to create unix domain socket!\n");
3183     return;
3184   }
3185
3186   mhd_unix_sock_addr.sun_family = AF_UNIX;
3187   strcpy (mhd_unix_sock_addr.sun_path, proxy_sockfile);
3188
3189 #if LINUX
3190   mhd_unix_sock_addr.sun_path[0] = '\0';
3191 #endif
3192 #if HAVE_SOCKADDR_IN_SIN_LEN
3193   mhd_unix_sock_addr.sun_len = (u_char) sizeof (struct sockaddr_un);
3194 #endif
3195
3196   len = strlen (proxy_sockfile) + sizeof(AF_UNIX);
3197
3198   GNUNET_free (proxy_sockfile);
3199
3200   if (GNUNET_OK != GNUNET_NETWORK_socket_bind (mhd_unix_socket,
3201                                (struct sockaddr*)&mhd_unix_sock_addr,
3202                                len))
3203   {
3204     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3205                 "Unable to bind unix domain socket!\n");
3206     return;
3207   }
3208
3209   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (mhd_unix_socket,
3210                                                  1))
3211   {
3212     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3213                 "Unable to listen on unix domain socket!\n");
3214     return;
3215   }
3216
3217   hd = GNUNET_malloc (sizeof (struct MhdHttpList));
3218   hd->is_ssl = GNUNET_NO;
3219   strcpy (hd->domain, "");
3220   httpd = MHD_start_daemon (MHD_USE_DEBUG, 4444, //Dummy port
3221             &accept_cb, NULL,
3222             &create_response, hd,
3223             MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
3224             MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
3225             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3226             MHD_OPTION_NOTIFY_COMPLETED,
3227             NULL, NULL,
3228             MHD_OPTION_END);
3229
3230   GNUNET_assert (httpd != NULL);
3231   hd->daemon = httpd;
3232   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
3233
3234   GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
3235
3236   run_httpds ();
3237
3238   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
3239                                 &do_shutdown, NULL);
3240
3241 }
3242
3243
3244 /**
3245  * The main function for gnunet-gns-proxy.
3246  *
3247  * @param argc number of arguments from the command line
3248  * @param argv command line arguments
3249  * @return 0 ok, 1 on error
3250  */
3251 int
3252 main (int argc, char *const *argv)
3253 {
3254   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
3255     {'p', "port", NULL,
3256      gettext_noop ("listen on specified port"), 1,
3257      &GNUNET_GETOPT_set_string, &port},
3258     {'a', "authority", NULL,
3259       gettext_noop ("pem file to use as CA"), 1,
3260       &GNUNET_GETOPT_set_string, &cafile_opt},
3261     GNUNET_GETOPT_OPTION_END
3262   };
3263
3264   int ret;
3265
3266   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
3267     return 2;
3268
3269
3270   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
3271   ret =
3272       (GNUNET_OK ==
3273        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
3274                            _("GNUnet GNS proxy"),
3275                            options,
3276                            &run, NULL)) ? 0 : 1;
3277   GNUNET_free_non_null ((char*)argv);
3278
3279   return ret;
3280 }
3281
3282
3283
3284