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