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