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