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