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