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