more work on GNS proxy, adding back to standard build
[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  * - actually check SSL certificates 
29  * - double-check queueing logic
30  * - handle cookie rewriting
31  * - handle location rewriting
32  * - improve IPv6 support
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  * Size of the buffer for the data upload / download.  Must be
61  * enough for curl, thus CURL_MAX_WRITE_SIZE is needed here (16k).
62  */
63 #define IO_BUFFERSIZE CURL_MAX_WRITE_SIZE
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
290 /* *********************** Datastructures for HTTP handling ****************** */
291
292 /**
293  * A structure for CA cert/key
294  */
295 struct ProxyCA
296 {
297   /**
298    * The certificate 
299    */
300   gnutls_x509_crt_t cert;
301
302   /**
303    * The private key 
304    */
305   gnutls_x509_privkey_t key;
306 };
307
308
309 /**
310  * Structure for GNS certificates
311  */
312 struct ProxyGNSCertificate
313 {
314   /**
315    * The certificate as PEM 
316    */
317   char cert[MAX_PEM_SIZE];
318
319   /**
320    * The private key as PEM 
321    */
322   char key[MAX_PEM_SIZE];
323 };
324
325
326
327 /**
328  * A structure for all running Httpds
329  */
330 struct MhdHttpList
331 {
332   /**
333    * DLL for httpds 
334    */
335   struct MhdHttpList *prev;
336
337   /**
338    * DLL for httpds 
339    */
340   struct MhdHttpList *next;
341
342   /**
343    * the domain name to server (only important for SSL) 
344    */
345   char *domain;
346
347   /**
348    * The daemon handle 
349    */
350   struct MHD_Daemon *daemon;
351
352   /**
353    * Optional proxy certificate used
354    */
355   struct ProxyGNSCertificate *proxy_cert;
356
357   /**
358    * The task ID 
359    */
360   GNUNET_SCHEDULER_TaskIdentifier httpd_task;
361
362   /**
363    * is this an ssl daemon? 
364    */
365   int is_ssl;
366
367 };
368
369
370 /* ***************** Datastructures for Socks handling **************** */
371
372
373 /**
374  * The socks phases.  
375  */
376 enum SocksPhase
377 {
378   /**
379    * We're waiting to get the client hello.
380    */
381   SOCKS5_INIT,
382
383   /**
384    * We're waiting to get the initial request.
385    */
386   SOCKS5_REQUEST,
387
388   /**
389    * We are currently resolving the destination.
390    */
391   SOCKS5_RESOLVING,
392
393   /**
394    * We're in transfer mode.
395    */
396   SOCKS5_DATA_TRANSFER,
397
398   /**
399    * Finish writing the write buffer, then clean up.
400    */
401   SOCKS5_WRITE_THEN_CLEANUP,
402
403   /**
404    * Socket has been passed to MHD, do not close it anymore.
405    */
406   SOCKS5_SOCKET_WITH_MHD,
407
408   /**
409    * We've finished receiving upload data from MHD.
410    */
411   SOCKS5_SOCKET_UPLOAD_STARTED,
412
413   /**
414    * We've finished receiving upload data from MHD.
415    */
416   SOCKS5_SOCKET_UPLOAD_DONE,
417
418   /**
419    * We've finished uploading data via CURL and can now download.
420    */
421   SOCKS5_SOCKET_DOWNLOAD_STARTED,
422
423   /**
424    * We've finished receiving download data from cURL.
425    */
426   SOCKS5_SOCKET_DOWNLOAD_DONE
427 };
428
429
430
431 /**
432  * A structure for socks requests
433  */
434 struct Socks5Request
435 {
436
437   /**
438    * DLL.
439    */
440   struct Socks5Request *next;
441
442   /**
443    * DLL.
444    */
445   struct Socks5Request *prev;
446
447   /**
448    * The client socket 
449    */
450   struct GNUNET_NETWORK_Handle *sock;
451
452   /**
453    * Handle to GNS lookup, during #SOCKS5_RESOLVING phase.
454    */
455   struct GNUNET_GNS_LookupRequest *gns_lookup;
456
457   /**
458    * Client socket read task 
459    */
460   GNUNET_SCHEDULER_TaskIdentifier rtask;
461
462   /**
463    * Client socket write task 
464    */
465   GNUNET_SCHEDULER_TaskIdentifier wtask;
466
467   /**
468    * Timeout task 
469    */
470   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
471
472   /**
473    * Read buffer 
474    */
475   char rbuf[SOCKS_BUFFERSIZE];
476
477   /**
478    * Write buffer 
479    */
480   char wbuf[SOCKS_BUFFERSIZE];
481
482   /**
483    * Buffer we use for moving data between MHD and curl (in both directions).
484    */
485   char io_buf[IO_BUFFERSIZE];
486
487   /**
488    * MHD HTTP instance handling this request, NULL for none.
489    */
490   struct MhdHttpList *hd;
491
492   /**
493    * MHD response object for this request.
494    */
495   struct MHD_Response *response;
496
497   /**
498    * the domain name to server (only important for SSL) 
499    */
500   char *domain;
501
502   /**
503    * DNS Legacy Host Name as given by GNS, NULL if not given.
504    */
505   char *leho;
506
507   /**
508    * The URL to fetch 
509    */
510   char *url;
511
512   /**
513    * Handle to cURL 
514    */
515   CURL *curl;
516
517   /**
518    * HTTP request headers for the curl request.
519    */
520   struct curl_slist *headers;
521   
522   /**
523    * HTTP response code to give to MHD for the response.
524    */
525   unsigned int response_code;
526
527   /**
528    * Number of bytes already in read buffer 
529    */
530   size_t rbuf_len;
531
532   /**
533    * Number of bytes already in write buffer 
534    */
535   size_t wbuf_len;
536   
537   /**
538    * Number of bytes already in the IO buffer.
539    */
540   size_t io_len;
541
542   /**
543    * Once known, what's the target address for the connection?
544    */
545   struct sockaddr_storage destination_address;
546
547   /**
548    * The socks state 
549    */
550   enum SocksPhase state;
551
552   /**
553    * Desired destination port.
554    */
555   uint16_t port;
556
557 };
558
559
560
561 /* *********************** Globals **************************** */
562
563
564 /**
565  * The port the proxy is running on (default 7777) 
566  */
567 static unsigned long port = GNUNET_GNS_PROXY_PORT;
568
569 /**
570  * The CA file (pem) to use for the proxy CA 
571  */
572 static char *cafile_opt;
573
574 /**
575  * The listen socket of the proxy 
576  */
577 static struct GNUNET_NETWORK_Handle *lsock;
578
579 /**
580  * The listen task ID 
581  */
582 static GNUNET_SCHEDULER_TaskIdentifier ltask;
583
584 /**
585  * The cURL download task (curl multi API).
586  */
587 static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
588
589 /**
590  * The cURL multi handle 
591  */
592 static CURLM *curl_multi;
593
594 /**
595  * Handle to the GNS service 
596  */
597 static struct GNUNET_GNS_Handle *gns_handle;
598
599 /**
600  * DLL for http/https daemons 
601  */
602 static struct MhdHttpList *mhd_httpd_head;
603
604 /**
605  * DLL for http/https daemons 
606  */
607 static struct MhdHttpList *mhd_httpd_tail;
608
609 /**
610  * Daemon for HTTP (we have one per SSL certificate, and then one for
611  * all HTTP connections; this is the one for HTTP, not HTTPS).
612  */
613 static struct MhdHttpList *httpd;
614
615 /**
616  * DLL of active socks requests.
617  */
618 static struct Socks5Request *s5r_head;
619
620 /**
621  * DLL of active socks requests.
622  */
623 static struct Socks5Request *s5r_tail;
624
625 /**
626  * The users local GNS master zone 
627  */
628 static struct GNUNET_CRYPTO_EccPublicSignKey local_gns_zone;
629
630 /**
631  * The users local shorten zone 
632  */
633 static struct GNUNET_CRYPTO_EccPrivateKey local_shorten_zone;
634
635 /**
636  * Is shortening enabled?
637  */
638 static int do_shorten;
639
640 /**
641  * The CA for SSL certificate generation 
642  */
643 static struct ProxyCA proxy_ca;
644
645 /**
646  * Response we return on cURL failures.
647  */
648 static struct MHD_Response *curl_failure_response;
649
650 /**
651  * Connection to identity service.
652  */
653 static struct GNUNET_IDENTITY_Handle *identity;
654
655 /**
656  * Request for our ego.
657  */
658 static struct GNUNET_IDENTITY_Operation *id_op;
659
660 /**
661  * Our configuration.
662  */
663 static const struct GNUNET_CONFIGURATION_Handle *cfg;
664
665
666 /* ************************* Global helpers ********************* */
667
668
669 /**
670  * Clean up s5r handles.
671  *
672  * @param s5r the handle to destroy
673  */
674 static void
675 cleanup_s5r (struct Socks5Request *s5r)
676 {
677   if (NULL != s5r->curl)
678   {    
679     curl_multi_remove_handle (curl_multi, s5r->curl);
680     curl_easy_cleanup (s5r->curl);
681     s5r->curl = NULL;
682   }
683   curl_slist_free_all (s5r->headers);
684   if (NULL != s5r->response)
685     MHD_destroy_response (s5r->response);
686   if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
687     GNUNET_SCHEDULER_cancel (s5r->rtask);
688   if (GNUNET_SCHEDULER_NO_TASK != s5r->timeout_task)
689     GNUNET_SCHEDULER_cancel (s5r->timeout_task);
690   if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
691     GNUNET_SCHEDULER_cancel (s5r->wtask);
692   if (NULL != s5r->gns_lookup)
693     GNUNET_GNS_lookup_cancel (s5r->gns_lookup);
694   if (NULL != s5r->sock) 
695   {
696     if (SOCKS5_SOCKET_WITH_MHD == s5r->state)
697       GNUNET_NETWORK_socket_free_memory_only_ (s5r->sock);
698     else
699       GNUNET_NETWORK_socket_close (s5r->sock);
700   }
701   GNUNET_CONTAINER_DLL_remove (s5r_head,
702                                s5r_tail,
703                                s5r);
704   GNUNET_free_non_null (s5r->domain);
705   GNUNET_free_non_null (s5r->leho);
706   GNUNET_free_non_null (s5r->url);
707   GNUNET_free (s5r);
708 }
709
710
711 /**
712  * Run MHD now, we have extra data ready for the callback.
713  *
714  * @param hd the daemon to run now.
715  */
716 static void
717 run_mhd_now (struct MhdHttpList *hd);
718
719
720 /* ************************* HTTP handling with cURL *********************** */
721
722
723 /**
724  * Callback for MHD response generation.  This function is called from
725  * MHD whenever MHD expects to get data back.  Copies data from the
726  * io_buf, if available.
727  *
728  * @param cls closure with our `struct Socks5Request`
729  * @param pos in buffer
730  * @param buf where to copy data
731  * @param max available space in @a buf
732  * @return number of bytes written to @a buf
733  */
734 static ssize_t
735 mhd_content_cb (void *cls,
736                 uint64_t pos,
737                 char* buf,
738                 size_t max)
739 {
740   struct Socks5Request *s5r = cls;
741   size_t bytes_to_copy;
742
743   if ( (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) ||
744        (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
745   {
746     /* we're still not done with the upload, do not yet
747        start the download, the IO buffer is still full
748        with upload data. */
749     return 0; /* not yet ready for data download */
750   }
751   bytes_to_copy = GNUNET_MIN (max,
752                               s5r->io_len);
753   if ( (0 == bytes_to_copy) &&
754        (SOCKS5_SOCKET_DOWNLOAD_DONE != s5r->state) )
755     return 0; /* more data later */
756   if ( (0 == bytes_to_copy) &&
757        (SOCKS5_SOCKET_DOWNLOAD_DONE == s5r->state) )
758     return MHD_CONTENT_READER_END_OF_STREAM;
759   memcpy (buf, s5r->io_buf, bytes_to_copy);
760   memmove (s5r->io_buf,
761            &s5r->io_buf[bytes_to_copy],
762            s5r->io_len - bytes_to_copy);
763   s5r->io_len -= bytes_to_copy;
764   curl_easy_pause (s5r->curl, CURLPAUSE_CONT);
765   return bytes_to_copy;
766 }
767
768
769 /**
770  * Check that the website has presented us with a valid SSL certificate.
771  * The certificate must either match the domain name or the LEHO name
772  * (or, if available, the TLSA record).
773  *
774  * @param s5r request to check for.
775  * @return #GNUNET_OK if the certificate is valid
776  */
777 static int
778 check_ssl_certificate (struct Socks5Request *s5r)
779 {
780   union {
781     struct curl_slist    *to_info;
782     struct curl_certinfo *to_certinfo;
783   } ptr;
784   int i;
785   struct curl_slist *slist;
786   
787   ptr.to_info = NULL;  
788   if (CURLE_OK != 
789       curl_easy_getinfo (s5r->curl, 
790                          CURLINFO_CERTINFO, 
791                          &ptr.to_info))
792     return GNUNET_SYSERR;
793   /* FIXME: for now, we just output the certs to stderr */
794   if(NULL != ptr.to_info) 
795   {     
796     fprintf (stderr,
797              "Got %d certs!\n", 
798              ptr.to_certinfo->num_of_certs);      
799     for (i = 0; i < ptr.to_certinfo->num_of_certs; i++) 
800     {    
801       for (slist = ptr.to_certinfo->certinfo[i]; NULL != slist; slist = slist->next)
802         fprintf (stderr,
803                  "%d: %s\n",
804                  i,
805                  slist->data);  
806     }
807   }
808   return GNUNET_OK;
809 }
810
811
812  
813 /**
814  * We're getting an HTTP response header from cURL.  Convert it to the
815  * MHD response headers.  Mostly copies the headers, but makes special
816  * adjustments to "Set-Cookie" and "Location" headers as those may need
817  * to be changed from the LEHO to the domain the browser expects.
818  *
819  * @param buffer curl buffer with a single line of header data; not 0-terminated!
820  * @param size curl blocksize
821  * @param nmemb curl blocknumber
822  * @param cls our `struct Socks5Request *`
823  * @return size of processed bytes
824  */
825 static size_t
826 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
827 {
828   struct Socks5Request *s5r = cls;
829   size_t bytes = size * nmemb;
830   char *ndup;
831   const char *hdr_type;
832   const char *hdr_val;
833   long resp_code;
834   char *new_cookie_hdr;
835
836   if (NULL == s5r->response)
837   {
838     /* first, check SSL certificate */
839     if ( (HTTPS_PORT == s5r->port) &&
840          (GNUNET_OK != check_ssl_certificate (s5r)) )
841       return 0;
842
843     GNUNET_break (CURLE_OK == 
844                   curl_easy_getinfo (s5r->curl,
845                                      CURLINFO_RESPONSE_CODE,
846                                      &resp_code));
847     s5r->response_code = resp_code;
848     s5r->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
849                                                        IO_BUFFERSIZE,
850                                                        &mhd_content_cb,
851                                                        s5r,
852                                                        NULL);
853     if (NULL != s5r->leho)
854     {
855       char *cors_hdr;
856       
857       GNUNET_asprintf (&cors_hdr, 
858                        (HTTPS_PORT == s5r->port)
859                        ? "https://%s"
860                        : "http://%s",
861                        s5r->leho);
862       
863       GNUNET_break (MHD_YES == 
864                     MHD_add_response_header (s5r->response,
865                                              MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
866                                              cors_hdr));
867       GNUNET_free (cors_hdr);
868     }
869     /* force connection to be closed after each request, as we 
870        do not support HTTP pipelining */
871     GNUNET_break (MHD_YES == 
872                   MHD_add_response_header (s5r->response,
873                                            MHD_HTTP_HEADER_CONNECTION,
874                                            "close"));
875   }
876   
877   ndup = GNUNET_strndup (buffer, bytes);
878   hdr_type = strtok (ndup, ":");
879   if (NULL == hdr_type)
880   {
881     GNUNET_free (ndup);
882     return bytes;
883   }
884   hdr_val = strtok (NULL, "");
885   if (NULL == hdr_val)
886   {
887     GNUNET_free (ndup);
888     return bytes;
889   }
890   if (' ' == *hdr_val)
891     hdr_val++;
892
893   /* custom logic for certain header types */
894   if (0 == strcasecmp (hdr_type,
895                        MHD_HTTP_HEADER_SET_COOKIE))
896   {
897 #if 0
898     // FIXME: adjust code...
899     for (tok = strtok (hdr_val, ";"); NULL != tok; tok = strtok (NULL, ";"))
900     {
901       if (0 == memcmp (tok, " domain", strlen (" domain")))
902       {
903         cookie_domain = tok + strlen (" domain") + 1;
904         if (strlen (cookie_domain) < strlen (s5r->leho))
905         {
906           delta_cdomain = strlen (ctask->leho) - strlen (cookie_domain);
907           if (0 == strcmp (cookie_domain, ctask->leho + (delta_cdomain)))
908           {
909             GNUNET_snprintf (new_cookie_hdr+offset,
910                              sizeof (new_cookie_hdr),
911                              " domain=%s", ctask->authority);
912             offset += strlen (" domain=") + strlen (ctask->authority);
913             new_cookie_hdr[offset] = ';';
914             offset++;
915             continue;
916           }
917         }
918         else if (strlen (cookie_domain) == strlen (ctask->leho))
919         {
920           if (0 == strcmp (cookie_domain, ctask->leho))
921           {
922             GNUNET_snprintf (new_cookie_hdr+offset,
923                              sizeof (new_cookie_hdr),
924                              " domain=%s", ctask->host);
925             offset += strlen (" domain=") + strlen (ctask->host);
926             new_cookie_hdr[offset] = ';';
927             offset++;
928             continue;
929           }
930         }
931         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
932                     "Cookie domain invalid\n");        
933       }
934       memcpy (new_cookie_hdr + offset, tok, strlen (tok));
935       offset += strlen (tok);
936       new_cookie_hdr[offset] = ';';
937       offset++;
938     }
939     hdr_val = new_cookie_hdr;
940
941 #else
942     new_cookie_hdr = NULL;
943 #endif
944   }
945   else
946   {
947     new_cookie_hdr = NULL;
948   }
949
950 #if 0
951   /* FIXME: adjust handling */
952   if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
953   {
954     if (ctask->mhd->is_ssl)
955     {
956       sprintf (leho_host, "https://%s", ctask->leho);
957       sprintf (real_host, "https://%s", ctask->host);
958     }
959     else
960     {
961       sprintf (leho_host, "http://%s", ctask->leho);
962       sprintf (real_host, "http://%s", ctask->host);
963     }
964
965     if (0 == memcmp (leho_host, hdr_val, strlen (leho_host)))
966     {
967       sprintf (new_location, "%s%s", real_host, hdr_val+strlen (leho_host));
968       hdr_val = new_location;
969     }
970   }
971 #endif
972   GNUNET_break (MHD_YES ==
973                 MHD_add_response_header (s5r->response,
974                                          hdr_type,
975                                          hdr_val));
976   GNUNET_free (ndup);
977   GNUNET_free_non_null (new_cookie_hdr);
978   return bytes;
979 }
980
981
982 /**
983  * Handle response payload data from cURL.  Copies it into our `io_buf` to make
984  * it available to MHD.
985  *
986  * @param ptr pointer to the data
987  * @param size number of blocks of data
988  * @param nmemb blocksize
989  * @param ctx our `struct Socks5Request *`
990  * @return number of bytes handled
991  */
992 static size_t
993 curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx)
994 {
995   struct Socks5Request *s5r = ctx;
996   size_t total = size * nmemb;
997
998   if ( (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) ||
999        (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
1000   {
1001     /* we're still not done with the upload, do not yet
1002        start the download, the IO buffer is still full
1003        with upload data. */
1004     return CURL_WRITEFUNC_PAUSE; /* not yet ready for data download */
1005   }
1006   if (sizeof (s5r->io_buf) - s5r->io_len < total)
1007     return CURL_WRITEFUNC_PAUSE; /* not enough space */
1008   memcpy (&s5r->io_buf[s5r->io_len], 
1009           ptr,
1010           total);
1011   s5r->io_len += total;
1012   if (s5r->io_len == total)
1013     run_mhd_now (s5r->hd);  
1014   return total;
1015 }
1016
1017
1018 /**
1019  * cURL callback for uploaded (PUT/POST) data.  Copies it into our `io_buf`
1020  * to make it available to MHD.
1021  *
1022  * @param buf where to write the data
1023  * @param size number of bytes per member
1024  * @param nmemb number of members available in @a buf
1025  * @param cls our `struct Socks5Request` that generated the data
1026  * @return number of bytes copied to @a buf
1027  */
1028 static size_t
1029 curl_upload_cb (void *buf, size_t size, size_t nmemb, void *cls)
1030 {
1031   struct Socks5Request *s5r = cls;
1032   size_t len = size * nmemb;
1033   size_t to_copy;
1034
1035   if ( (0 == s5r->io_len) &&
1036        (SOCKS5_SOCKET_UPLOAD_DONE != s5r->state) )
1037     return CURL_READFUNC_PAUSE;
1038   if ( (0 == s5r->io_len) &&
1039        (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
1040   {
1041     s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
1042     return 0; /* upload finished, can now download */
1043   }
1044   if ( (SOCKS5_SOCKET_UPLOAD_STARTED != s5r->state) ||
1045        (SOCKS5_SOCKET_UPLOAD_DONE != s5r->state) )
1046   {
1047     GNUNET_break (0);
1048     return CURL_READFUNC_ABORT;
1049   }
1050   to_copy = GNUNET_MIN (s5r->io_len,
1051                         len);
1052   memcpy (buf, s5r->io_buf, to_copy);
1053   memmove (s5r->io_buf,
1054            &s5r->io_buf[to_copy],
1055            s5r->io_len - to_copy);
1056   s5r->io_len -= to_copy;
1057   if (s5r->io_len + to_copy == sizeof (s5r->io_buf))
1058     run_mhd_now (s5r->hd); /* got more space for upload now */
1059   return to_copy;
1060 }
1061
1062
1063 /* ************************** main loop of cURL interaction ****************** */
1064
1065
1066 /**
1067  * Task that is run when we are ready to receive more data
1068  * from curl
1069  *
1070  * @param cls closure
1071  * @param tc task context
1072  */
1073 static void
1074 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1075
1076
1077 /**
1078  * Ask cURL for the select() sets and schedule cURL operations.
1079  */
1080 static void
1081 curl_download_prepare ()
1082 {
1083   CURLMcode mret;
1084   fd_set rs;
1085   fd_set ws;
1086   fd_set es;
1087   int max;
1088   struct GNUNET_NETWORK_FDSet *grs;
1089   struct GNUNET_NETWORK_FDSet *gws;
1090   long to;
1091   struct GNUNET_TIME_Relative rtime;
1092
1093   if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
1094   {
1095     GNUNET_SCHEDULER_cancel (curl_download_task);
1096     curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1097   }
1098   max = -1;
1099   FD_ZERO (&rs);
1100   FD_ZERO (&ws);
1101   FD_ZERO (&es);
1102   if (CURLM_OK != (mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max)))
1103   {
1104     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1105                 "%s failed at %s:%d: `%s'\n",
1106                 "curl_multi_fdset", __FILE__, __LINE__,
1107                 curl_multi_strerror (mret));
1108     return;
1109   }
1110   to = -1;
1111   GNUNET_break (CURLM_OK == curl_multi_timeout (curl_multi, &to));
1112   if (-1 == to)
1113     rtime = GNUNET_TIME_UNIT_FOREVER_REL;
1114   else
1115     rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
1116   if (-1 != max)
1117   {
1118     grs = GNUNET_NETWORK_fdset_create ();
1119     gws = GNUNET_NETWORK_fdset_create ();
1120     GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1121     GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1122     curl_download_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1123                                                       rtime,
1124                                                       grs, gws,
1125                                                       &curl_task_download, curl_multi);
1126     GNUNET_NETWORK_fdset_destroy (gws);
1127     GNUNET_NETWORK_fdset_destroy (grs);
1128   }
1129   else 
1130   {
1131     curl_download_task = GNUNET_SCHEDULER_add_delayed (rtime,
1132                                                        &curl_task_download,
1133                                                        curl_multi);
1134   }
1135 }
1136
1137
1138 /**
1139  * Task that is run when we are ready to receive more data from curl.
1140  *
1141  * @param cls closure, NULL
1142  * @param tc task context
1143  */
1144 static void
1145 curl_task_download (void *cls, 
1146                     const struct GNUNET_SCHEDULER_TaskContext *tc)
1147 {
1148   int running;
1149   int msgnum;
1150   struct CURLMsg *msg;
1151   CURLMcode mret;
1152   struct Socks5Request *s5r;
1153
1154   curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1155   do
1156   {
1157     running = 0;    
1158     mret = curl_multi_perform (curl_multi, &running);
1159     while (NULL != (msg = curl_multi_info_read (curl_multi, &msgnum)))
1160     {
1161       GNUNET_break (CURLE_OK ==
1162                     curl_easy_getinfo (msg->easy_handle,
1163                                        CURLINFO_PRIVATE,
1164                                        &s5r));
1165       if (NULL == s5r)
1166       {
1167         GNUNET_break (0);
1168         continue;
1169       }
1170       switch (msg->msg)
1171       {
1172       case CURLMSG_NONE:
1173         /* documentation says this is not used */
1174         GNUNET_break (0);
1175         break;
1176       case CURLMSG_DONE:
1177         switch (msg->data.result)
1178         {
1179         case CURLE_OK:
1180         case CURLE_GOT_NOTHING:
1181           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1182                       "CURL download completed.\n");
1183           s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE;       
1184           run_mhd_now (s5r->hd);
1185           break;
1186         default:
1187           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1188                       "Download curl failed: %s\n",
1189                       curl_easy_strerror (msg->data.result));
1190           /* FIXME: indicate error somehow? close MHD connection badly as well? */
1191           s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE;
1192           run_mhd_now (s5r->hd);          
1193           break;
1194         }
1195         GNUNET_break (NULL != s5r->response);
1196         curl_multi_remove_handle (curl_multi, s5r->curl);
1197         curl_easy_cleanup (s5r->curl);
1198         s5r->curl = NULL;
1199         break;
1200       case CURLMSG_LAST:
1201         /* documentation says this is not used */
1202         GNUNET_break (0);
1203         break;
1204       default:
1205         /* unexpected status code */
1206         GNUNET_break (0);
1207         break;
1208       }
1209     };
1210   } while (mret == CURLM_CALL_MULTI_PERFORM);  
1211   if (CURLM_OK != mret)
1212     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1213                 "%s failed at %s:%d: `%s'\n",
1214                 "curl_multi_perform", __FILE__, __LINE__,
1215                 curl_multi_strerror (mret));  
1216   if (0 == running)
1217   {
1218     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1219                 "Suspending cURL multi loop, no more events pending\n");
1220     return; /* nothing more in progress */
1221   }
1222   curl_download_prepare();
1223 }
1224
1225
1226 /* ********************************* MHD response generation ******************* */
1227
1228
1229 /**
1230  * Read HTTP request header field from the request.  Copies the fields
1231  * over to the 'headers' that will be given to curl.  However, 'Host'
1232  * is substituted with the LEHO if present.
1233  *
1234  * @param cls our `struct Socks5Request`
1235  * @param kind value kind
1236  * @param key field key
1237  * @param value field value
1238  * @return #MHD_YES to continue to iterate
1239  */
1240 static int
1241 con_val_iter (void *cls,
1242               enum MHD_ValueKind kind,
1243               const char *key,
1244               const char *value)
1245 {
1246   struct Socks5Request *s5r = cls;
1247   char *hdr;
1248
1249   if ( (0 == strcasecmp (MHD_HTTP_HEADER_HOST, key)) &&
1250        (NULL != s5r->leho) )
1251     value = s5r->leho;
1252   GNUNET_asprintf (&hdr,
1253                    "%s: %s",
1254                    key,
1255                    value);
1256   s5r->headers = curl_slist_append (s5r->headers,
1257                                     hdr);
1258   GNUNET_free (hdr);
1259   return MHD_YES;
1260 }
1261
1262
1263 /**
1264  * Main MHD callback for handling requests.
1265  *
1266  * @param cls unused
1267  * @param con MHD connection handle
1268  * @param url the url in the request
1269  * @param meth the HTTP method used ("GET", "PUT", etc.)
1270  * @param ver the HTTP version string (i.e. "HTTP/1.1")
1271  * @param upload_data the data being uploaded (excluding HEADERS,
1272  *        for a POST that fits into memory and that is encoded
1273  *        with a supported encoding, the POST data will NOT be
1274  *        given in upload_data and is instead available as
1275  *        part of MHD_get_connection_values; very large POST
1276  *        data *will* be made available incrementally in
1277  *        upload_data)
1278  * @param upload_data_size set initially to the size of the
1279  *        @a upload_data provided; the method must update this
1280  *        value to the number of bytes NOT processed;
1281  * @param con_cls pointer to location where we store the 'struct Request'
1282  * @return #MHD_YES if the connection was handled successfully,
1283  *         #MHD_NO if the socket must be closed due to a serious
1284  *         error while handling the request
1285  */
1286 static int
1287 create_response (void *cls,
1288                  struct MHD_Connection *con,
1289                  const char *url,
1290                  const char *meth,
1291                  const char *ver,
1292                  const char *upload_data,
1293                  size_t *upload_data_size,
1294                  void **con_cls)
1295 {
1296   /* struct MhdHttpList* hd = cls;  */
1297   struct Socks5Request *s5r = *con_cls;
1298   char *curlurl;
1299   char ipstring[INET6_ADDRSTRLEN];
1300   char ipaddr[INET6_ADDRSTRLEN + 2];
1301   const struct sockaddr *sa;
1302   const struct sockaddr_in *s4;
1303   const struct sockaddr_in6 *s6;
1304   uint16_t port;
1305   size_t left;
1306
1307   if (NULL == s5r)
1308   {
1309     GNUNET_break (0);
1310     return MHD_NO;
1311   }
1312   if (NULL == s5r->curl)
1313   {
1314     /* first time here, initialize curl handle */
1315     sa = (const struct sockaddr *) &s5r->destination_address;
1316     switch (sa->sa_family)
1317     {
1318     case AF_INET:
1319       s4 = (const struct sockaddr_in *) &s5r->destination_address;
1320       if (NULL == inet_ntop (AF_INET,
1321                              &s4->sin_addr,
1322                              ipstring,
1323                              sizeof (ipstring)))
1324       {
1325         GNUNET_break (0);
1326         return MHD_NO;
1327       }
1328       GNUNET_snprintf (ipaddr,
1329                        sizeof (ipaddr),
1330                        "%s",
1331                        ipstring);
1332       port = ntohs (s4->sin_port);
1333       break;
1334     case AF_INET6:
1335       s6 = (const struct sockaddr_in6 *) &s5r->destination_address;
1336       if (NULL == inet_ntop (AF_INET6,
1337                              &s6->sin6_addr,
1338                              ipstring,
1339                              sizeof (ipstring)))
1340       {
1341         GNUNET_break (0);
1342         return MHD_NO;
1343       }
1344       GNUNET_snprintf (ipaddr,
1345                        sizeof (ipaddr),
1346                        "[%s]",
1347                        ipstring);
1348       port = ntohs (s6->sin6_port);
1349       break;
1350     default:
1351       GNUNET_break (0);
1352       return MHD_NO;
1353     }
1354     s5r->curl = curl_easy_init ();
1355     if (NULL == s5r->curl)
1356       return MHD_queue_response (con,
1357                                  MHD_HTTP_INTERNAL_SERVER_ERROR,
1358                                  curl_failure_response);    
1359     curl_easy_setopt (s5r->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1360     curl_easy_setopt (s5r->curl, CURLOPT_HEADERDATA, s5r);
1361     curl_easy_setopt (s5r->curl, CURLOPT_FOLLOWLOCATION, 0);
1362     curl_easy_setopt (s5r->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1363     curl_easy_setopt (s5r->curl, CURLOPT_FAILONERROR, 1); /* not sure we want this */
1364     curl_easy_setopt (s5r->curl, CURLOPT_CONNECTTIMEOUT, 600L);
1365     curl_easy_setopt (s5r->curl, CURLOPT_TIMEOUT, 600L);
1366     curl_easy_setopt (s5r->curl, CURLOPT_NOSIGNAL, 1L);
1367     curl_easy_setopt (s5r->curl, CURLOPT_HTTP_CONTENT_DECODING, 0);
1368     curl_easy_setopt (s5r->curl, CURLOPT_HTTP_TRANSFER_DECODING, 0);
1369     curl_easy_setopt (s5r->curl, CURLOPT_NOSIGNAL, 1L);
1370     curl_easy_setopt (s5r->curl, CURLOPT_PRIVATE, s5r);
1371     curl_easy_setopt (s5r->curl, CURLOPT_VERBOSE, 1); // FIXME: remove later
1372     GNUNET_asprintf (&curlurl,
1373                      (HTTPS_PORT != s5r->port)
1374                      ? "http://%s:%d%s"
1375                      : "https://%s:%d%s",
1376                      ipaddr,
1377                      port, 
1378                      s5r->url);   
1379     curl_easy_setopt (s5r->curl,
1380                       CURLOPT_URL, 
1381                       curlurl);   
1382     GNUNET_free (curlurl);
1383
1384     if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
1385     {
1386       s5r->state = SOCKS5_SOCKET_UPLOAD_STARTED;
1387       curl_easy_setopt (s5r->curl, CURLOPT_UPLOAD, 1);
1388       curl_easy_setopt (s5r->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1389       curl_easy_setopt (s5r->curl, CURLOPT_WRITEDATA, s5r);
1390       curl_easy_setopt (s5r->curl, CURLOPT_READFUNCTION, &curl_upload_cb);
1391       curl_easy_setopt (s5r->curl, CURLOPT_READDATA, s5r);
1392     } 
1393     else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
1394     {
1395       s5r->state = SOCKS5_SOCKET_UPLOAD_STARTED;    
1396       curl_easy_setopt (s5r->curl, CURLOPT_POST, 1);
1397       curl_easy_setopt (s5r->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1398       curl_easy_setopt (s5r->curl, CURLOPT_WRITEDATA, s5r);
1399       curl_easy_setopt (s5r->curl, CURLOPT_READFUNCTION, &curl_upload_cb);
1400       curl_easy_setopt (s5r->curl, CURLOPT_READDATA, s5r);
1401     }
1402     else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
1403     {
1404       s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;    
1405       curl_easy_setopt (s5r->curl, CURLOPT_NOBODY, 1);
1406     }
1407     else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_GET))
1408     {
1409       s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;    
1410       curl_easy_setopt (s5r->curl, CURLOPT_HTTPGET, 1);
1411       curl_easy_setopt (s5r->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1412       curl_easy_setopt (s5r->curl, CURLOPT_WRITEDATA, s5r);
1413     }
1414     else
1415     {
1416       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1417                   _("Unsupported HTTP method `%s'\n"),
1418                   meth);
1419       curl_easy_cleanup (s5r->curl);
1420       s5r->curl = NULL;      
1421       return MHD_NO;
1422     }
1423     
1424     if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_0))
1425     {
1426       curl_easy_setopt (s5r->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
1427     }
1428     else if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_1))
1429     {
1430       curl_easy_setopt (s5r->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
1431     }
1432     else
1433     {
1434       curl_easy_setopt (s5r->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE);
1435     }
1436     
1437     if (HTTPS_PORT == s5r->port)
1438     {
1439       curl_easy_setopt (s5r->curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
1440       curl_easy_setopt (s5r->curl, CURLOPT_SSL_VERIFYPEER, 1L);
1441       /* Disable cURL checking the hostname, as we will check ourselves
1442          as only we have the domain name or the LEHO or the DANE record */
1443       curl_easy_setopt (s5r->curl, CURLOPT_SSL_VERIFYHOST, 0L);      
1444     }
1445     else
1446     {
1447       curl_easy_setopt (s5r->curl, CURLOPT_USE_SSL, CURLUSESSL_NONE);   
1448     }
1449
1450     if (CURLM_OK != curl_multi_add_handle (curl_multi, s5r->curl))
1451     {
1452       GNUNET_break (0);
1453       curl_easy_cleanup (s5r->curl);
1454       s5r->curl = NULL;      
1455       return MHD_NO;      
1456     }
1457     MHD_get_connection_values (con,
1458                                MHD_HEADER_KIND,
1459                                &con_val_iter, s5r);
1460     curl_easy_setopt (s5r->curl, CURLOPT_HTTPHEADER, s5r->headers);
1461     curl_download_prepare ();
1462     return MHD_YES;
1463   } 
1464
1465   /* continuing to process request */
1466   if (0 != *upload_data_size)
1467   {
1468     left = GNUNET_MIN (*upload_data_size,
1469                        sizeof (s5r->io_buf) - s5r->io_len);
1470     memcpy (&s5r->io_buf[s5r->io_len], 
1471             upload_data,
1472             left);
1473     s5r->io_len += left;
1474     *upload_data_size -= left;
1475     if (s5r->io_len == left)
1476       curl_easy_pause (s5r->curl, CURLPAUSE_CONT);
1477     curl_download_prepare ();
1478     return MHD_YES;
1479   }
1480   s5r->state = SOCKS5_SOCKET_UPLOAD_DONE;
1481   if (NULL == s5r->response) 
1482     return MHD_YES; /* too early to queue response, did not yet get headers from cURL */
1483   return MHD_queue_response (con,
1484                              s5r->response_code, 
1485                              s5r->response);
1486 }
1487
1488
1489 /* ******************** MHD HTTP setup and event loop ******************** */
1490
1491
1492 /**
1493  * Function called when MHD decides that we are done with a connection.
1494  *
1495  * @param cls NULL
1496  * @param connection connection handle
1497  * @param con_cls value as set by the last call to
1498  *        the #MHD_AccessHandlerCallback, should be our `struct Socks5Request`
1499  * @param toe reason for request termination (ignored)
1500  */
1501 static void
1502 mhd_completed_cb (void *cls,
1503                   struct MHD_Connection *connection,
1504                   void **con_cls,
1505                   enum MHD_RequestTerminationCode toe)
1506 {
1507   struct Socks5Request *s5r = *con_cls;
1508
1509   if (NULL == s5r)
1510     return;
1511   cleanup_s5r (s5r);
1512   *con_cls = NULL;  
1513 }
1514
1515
1516 /**
1517  * Function called when MHD first processes an incoming connection.
1518  * Gives us the respective URI information.
1519  *
1520  * We use this to associate the `struct MHD_Connection` with our 
1521  * internal `struct Socks5Request` data structure (by checking
1522  * for matching sockets).
1523  *
1524  * @param cls the HTTP server handle (a `struct MhdHttpList`)
1525  * @param url the URL that is being requested
1526  * @param connection MHD connection object for the request
1527  * @return the `struct Socks5Request` that this @a connection is for
1528  */
1529 static void *
1530 mhd_log_callback (void *cls, 
1531                   const char *url,
1532                   struct MHD_Connection *connection)
1533 {
1534   struct Socks5Request *s5r;
1535   const union MHD_ConnectionInfo *ci;
1536   int sock;
1537
1538   ci = MHD_get_connection_info (connection,
1539                                 MHD_CONNECTION_INFO_CONNECTION_FD);
1540   if (NULL == ci) 
1541   {
1542     GNUNET_break (0);
1543     return NULL;
1544   }
1545   sock = ci->connect_fd;
1546   for (s5r = s5r_head; NULL != s5r; s5r = s5r->next)
1547   {
1548     if (GNUNET_NETWORK_get_fd (s5r->sock) == sock)
1549     {
1550       if (NULL != s5r->url)
1551       {
1552         GNUNET_break (0);
1553         return NULL;
1554       }
1555       s5r->url = GNUNET_strdup (url);
1556       return s5r;
1557     }
1558   }
1559   return NULL;
1560 }
1561
1562
1563 /**
1564  * Kill the given MHD daemon.
1565  *
1566  * @param hd daemon to stop
1567  */
1568 static void
1569 kill_httpd (struct MhdHttpList *hd)
1570 {
1571   GNUNET_CONTAINER_DLL_remove (mhd_httpd_head,
1572                                mhd_httpd_tail,
1573                                hd);
1574   GNUNET_free_non_null (hd->domain);
1575   MHD_stop_daemon (hd->daemon);
1576   if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
1577     GNUNET_SCHEDULER_cancel (hd->httpd_task);
1578   GNUNET_free_non_null (hd->proxy_cert);
1579   if (hd == httpd)
1580     httpd = NULL;
1581   GNUNET_free (hd);
1582 }
1583
1584
1585 /**
1586  * Task run whenever HTTP server is idle for too long. Kill it.
1587  *
1588  * @param cls the `struct MhdHttpList *`
1589  * @param tc sched context
1590  */
1591 static void
1592 kill_httpd_task (void *cls,
1593                  const struct GNUNET_SCHEDULER_TaskContext *tc)
1594 {
1595   struct MhdHttpList *hd = cls;
1596   
1597   kill_httpd (hd);
1598 }
1599
1600
1601 /**
1602  * Task run whenever HTTP server operations are pending.
1603  *
1604  * @param cls the `struct MhdHttpList *` of the daemon that is being run
1605  * @param tc sched context
1606  */
1607 static void
1608 do_httpd (void *cls,
1609           const struct GNUNET_SCHEDULER_TaskContext *tc);
1610
1611
1612 /**
1613  * Schedule MHD.  This function should be called initially when an
1614  * MHD is first getting its client socket, and will then automatically
1615  * always be called later whenever there is work to be done.
1616  *
1617  * @param hd the daemon to schedule
1618  */
1619 static void
1620 schedule_httpd (struct MhdHttpList *hd)
1621 {
1622   fd_set rs;
1623   fd_set ws;
1624   fd_set es;
1625   struct GNUNET_NETWORK_FDSet *wrs;
1626   struct GNUNET_NETWORK_FDSet *wws;
1627   int max;
1628   int haveto;
1629   MHD_UNSIGNED_LONG_LONG timeout;
1630   struct GNUNET_TIME_Relative tv;
1631
1632   FD_ZERO (&rs);
1633   FD_ZERO (&ws);
1634   FD_ZERO (&es);
1635   max = -1;
1636   if (MHD_YES != MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max))
1637   {
1638     kill_httpd (hd);
1639     return;
1640   }
1641   haveto = MHD_get_timeout (hd->daemon, &timeout);
1642   if (MHD_YES == haveto)
1643     tv.rel_value_us = (uint64_t) timeout * 1000LL;
1644   else
1645     tv = GNUNET_TIME_UNIT_FOREVER_REL;
1646   if (-1 != max)
1647   {
1648     wrs = GNUNET_NETWORK_fdset_create ();
1649     wws = GNUNET_NETWORK_fdset_create ();
1650     GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
1651     GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
1652   }
1653   else
1654   {
1655     wrs = NULL;
1656     wws = NULL;
1657   }
1658   if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
1659     GNUNET_SCHEDULER_cancel (hd->httpd_task);
1660   if ( (MHD_YES != haveto) &&
1661        (-1 == max) &&
1662        (hd != httpd) )
1663   {
1664     /* daemon is idle, kill after timeout */
1665     hd->httpd_task = GNUNET_SCHEDULER_add_delayed (MHD_CACHE_TIMEOUT,
1666                                                    &kill_httpd_task,
1667                                                    hd);
1668   }
1669   else
1670   {
1671     hd->httpd_task =
1672       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1673                                    tv, wrs, wws,
1674                                    &do_httpd, hd);
1675   }
1676   if (NULL != wrs)
1677     GNUNET_NETWORK_fdset_destroy (wrs);
1678   if (NULL != wws)
1679     GNUNET_NETWORK_fdset_destroy (wws);
1680 }
1681
1682
1683 /**
1684  * Task run whenever HTTP server operations are pending.
1685  *
1686  * @param cls the `struct MhdHttpList` of the daemon that is being run
1687  * @param tc scheduler context
1688  */
1689 static void
1690 do_httpd (void *cls,
1691           const struct GNUNET_SCHEDULER_TaskContext *tc)
1692 {
1693   struct MhdHttpList *hd = cls;
1694   
1695   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK; 
1696   MHD_run (hd->daemon);
1697   schedule_httpd (hd);
1698 }
1699
1700
1701 /**
1702  * Run MHD now, we have extra data ready for the callback.
1703  *
1704  * @param hd the daemon to run now.
1705  */
1706 static void
1707 run_mhd_now (struct MhdHttpList *hd)
1708 {
1709   if (GNUNET_SCHEDULER_NO_TASK != 
1710       hd->httpd_task)
1711     GNUNET_SCHEDULER_cancel (hd->httpd_task);
1712   hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, 
1713                                              hd);
1714 }
1715
1716
1717 /**
1718  * Read file in filename
1719  *
1720  * @param filename file to read
1721  * @param size pointer where filesize is stored
1722  * @return NULL on error
1723  */
1724 static void*
1725 load_file (const char* filename, 
1726            unsigned int* size)
1727 {
1728   void *buffer;
1729   uint64_t fsize;
1730
1731   if (GNUNET_OK !=
1732       GNUNET_DISK_file_size (filename, &fsize,
1733                              GNUNET_YES, GNUNET_YES))
1734     return NULL;
1735   if (fsize > MAX_PEM_SIZE)
1736     return NULL;
1737   *size = (unsigned int) fsize;
1738   buffer = GNUNET_malloc (*size);
1739   if (fsize != GNUNET_DISK_fn_read (filename, buffer, (size_t) fsize))
1740   {
1741     GNUNET_free (buffer);
1742     return NULL;
1743   }
1744   return buffer;
1745 }
1746
1747
1748 /**
1749  * Load PEM key from file
1750  *
1751  * @param key where to store the data
1752  * @param keyfile path to the PEM file
1753  * @return #GNUNET_OK on success
1754  */
1755 static int
1756 load_key_from_file (gnutls_x509_privkey_t key, 
1757                     const char* keyfile)
1758 {
1759   gnutls_datum_t key_data;
1760   int ret;
1761
1762   key_data.data = load_file (keyfile, &key_data.size);
1763   ret = gnutls_x509_privkey_import (key, &key_data,
1764                                     GNUTLS_X509_FMT_PEM);
1765   if (GNUTLS_E_SUCCESS != ret)
1766   {
1767     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1768                 _("Unable to import private key from file `%s'\n"),
1769                 keyfile);
1770     GNUNET_break (0);
1771   }
1772   GNUNET_free (key_data.data);
1773   return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
1774 }
1775
1776
1777 /**
1778  * Load cert from file
1779  *
1780  * @param crt struct to store data in
1781  * @param certfile path to pem file
1782  * @return #GNUNET_OK on success
1783  */
1784 static int
1785 load_cert_from_file (gnutls_x509_crt_t crt, 
1786                      const char* certfile)
1787 {
1788   gnutls_datum_t cert_data;
1789   int ret;
1790
1791   cert_data.data = load_file (certfile, &cert_data.size);
1792   ret = gnutls_x509_crt_import (crt, &cert_data,
1793                                 GNUTLS_X509_FMT_PEM);
1794   if (GNUTLS_E_SUCCESS != ret)
1795   {
1796     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1797                _("Unable to import certificate %s\n"), certfile);
1798     GNUNET_break (0);
1799   }
1800   GNUNET_free (cert_data.data);
1801   return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
1802 }
1803
1804
1805 /**
1806  * Generate new certificate for specific name
1807  *
1808  * @param name the subject name to generate a cert for
1809  * @return a struct holding the PEM data, NULL on error
1810  */
1811 static struct ProxyGNSCertificate *
1812 generate_gns_certificate (const char *name)
1813 {
1814   unsigned int serial;
1815   size_t key_buf_size;
1816   size_t cert_buf_size;
1817   gnutls_x509_crt_t request;
1818   time_t etime;
1819   struct tm *tm_data;
1820   struct ProxyGNSCertificate *pgc;
1821
1822   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
1823               "Generating TLS/SSL certificate for `%s'\n", 
1824               name);
1825   GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_init (&request));
1826   GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
1827   pgc = GNUNET_new (struct ProxyGNSCertificate);
1828   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
1829                                  0, "TNR", 2);
1830   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
1831                                  0, "GNU Name System", 4);
1832   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
1833                                  0, name, strlen (name));
1834   GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_version (request, 3));
1835   gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
1836   gnutls_x509_crt_set_serial (request,
1837                               &serial,
1838                               sizeof (serial));
1839   etime = time (NULL);
1840   tm_data = localtime (&etime);  
1841   gnutls_x509_crt_set_activation_time (request,
1842                                        etime);
1843   tm_data->tm_year++;
1844   etime = mktime (tm_data);
1845   gnutls_x509_crt_set_expiration_time (request,
1846                                        etime);
1847   gnutls_x509_crt_sign (request, 
1848                         proxy_ca.cert, 
1849                         proxy_ca.key);
1850   key_buf_size = sizeof (pgc->key);
1851   cert_buf_size = sizeof (pgc->cert);
1852   gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
1853                           pgc->cert, &cert_buf_size);
1854   gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
1855                               pgc->key, &key_buf_size);
1856   gnutls_x509_crt_deinit (request);
1857   return pgc;
1858 }
1859
1860
1861 /**
1862  * Lookup (or create) an SSL MHD instance for a particular domain.
1863  *
1864  * @param domain the domain the SSL daemon has to serve
1865  * @return NULL on errro
1866  */
1867 static struct MhdHttpList *
1868 lookup_ssl_httpd (const char* domain)
1869 {
1870   struct MhdHttpList *hd;
1871   struct ProxyGNSCertificate *pgc;
1872
1873   for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
1874     if (0 == strcmp (hd->domain, domain))
1875       return hd;
1876   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1877               "Starting fresh MHD HTTPS instance for domain `%s'\n",
1878               domain);
1879   pgc = generate_gns_certificate (domain);   
1880   hd = GNUNET_new (struct MhdHttpList);
1881   hd->is_ssl = GNUNET_YES;
1882   hd->domain = GNUNET_strdup (domain); 
1883   hd->proxy_cert = pgc;
1884   hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_NO_LISTEN_SOCKET,
1885                                  0,
1886                                  NULL, NULL,
1887                                  &create_response, hd,
1888                                  MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
1889                                  MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL,
1890                                  MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
1891                                  MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
1892                                  MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
1893                                  MHD_OPTION_END);
1894   if (NULL == hd->daemon)
1895   {
1896     GNUNET_free (pgc);
1897     GNUNET_free (hd);
1898     return NULL;
1899   }
1900   GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, 
1901                                mhd_httpd_tail, 
1902                                hd);
1903   return hd;
1904 }
1905
1906
1907 /**
1908  * Task run when a Socks5Request somehow fails to be associated with
1909  * an MHD connection (i.e. because the client never speaks HTTP after
1910  * the SOCKS5 handshake).  Clean up.
1911  *
1912  * @param cls the `struct Socks5Request *`
1913  * @param tc sched context
1914  */
1915 static void
1916 timeout_s5r_handshake (void *cls,
1917                        const struct GNUNET_SCHEDULER_TaskContext *tc)
1918 {
1919   struct Socks5Request *s5r = cls;
1920
1921   s5r->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1922   cleanup_s5r (s5r);
1923 }
1924
1925
1926 /**
1927  * We're done with the Socks5 protocol, now we need to pass the
1928  * connection data through to the final destination, either 
1929  * direct (if the protocol might not be HTTP), or via MHD
1930  * (if the port looks like it should be HTTP).
1931  *
1932  * @param s5r socks request that has reached the final stage
1933  */
1934 static void
1935 setup_data_transfer (struct Socks5Request *s5r)
1936 {
1937   struct MhdHttpList *hd;
1938   int fd;
1939   const struct sockaddr *addr;
1940   socklen_t len;
1941
1942   switch (s5r->port)
1943   {
1944   case HTTPS_PORT:
1945     hd = lookup_ssl_httpd (s5r->domain);
1946     if (NULL == hd)
1947     {
1948       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1949                   _("Failed to start HTTPS server for `%s'\n"),
1950                   s5r->domain);
1951       cleanup_s5r (s5r);
1952       return;
1953     }
1954     break;
1955   case HTTP_PORT:
1956   default:
1957     GNUNET_assert (NULL == httpd);
1958     hd = httpd;
1959     break;
1960   }
1961   fd = GNUNET_NETWORK_get_fd (s5r->sock);
1962   addr = GNUNET_NETWORK_get_addr (s5r->sock);
1963   len = GNUNET_NETWORK_get_addrlen (s5r->sock);
1964   s5r->state = SOCKS5_SOCKET_WITH_MHD;
1965   if (MHD_YES != MHD_add_connection (hd->daemon, fd, addr, len))
1966   {
1967     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1968                 _("Failed to pass client to MHD\n"));
1969     cleanup_s5r (s5r);
1970     return;
1971   }
1972   s5r->hd = hd;
1973   schedule_httpd (hd);
1974   s5r->timeout_task = GNUNET_SCHEDULER_add_delayed (HTTP_HANDSHAKE_TIMEOUT,
1975                                                     &timeout_s5r_handshake,
1976                                                     s5r);
1977 }
1978
1979
1980 /* ********************* SOCKS handling ************************* */
1981
1982
1983 /**
1984  * Write data from buffer to socks5 client, then continue with state machine.
1985  *
1986  * @param cls the closure with the `struct Socks5Request`
1987  * @param tc scheduler context
1988  */
1989 static void
1990 do_write (void *cls,
1991           const struct GNUNET_SCHEDULER_TaskContext *tc)
1992 {
1993   struct Socks5Request *s5r = cls;
1994   ssize_t len;
1995
1996   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
1997   len = GNUNET_NETWORK_socket_send (s5r->sock,
1998                                     s5r->wbuf,
1999                                     s5r->wbuf_len);
2000   if (len <= 0)
2001   {
2002     /* write error: connection closed, shutdown, etc.; just clean up */
2003     cleanup_s5r (s5r); 
2004     return;
2005   }
2006   memmove (s5r->wbuf,
2007            &s5r->wbuf[len],
2008            s5r->wbuf_len - len);
2009   s5r->wbuf_len -= len;
2010   if (s5r->wbuf_len > 0)
2011   {
2012     /* not done writing */
2013     s5r->wtask =
2014       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2015                                       s5r->sock,
2016                                       &do_write, s5r);
2017     return;
2018   }
2019
2020   /* we're done writing, continue with state machine! */
2021
2022   switch (s5r->state)
2023   {
2024   case SOCKS5_INIT:    
2025     GNUNET_assert (0);
2026     break;
2027   case SOCKS5_REQUEST:    
2028     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s5r->rtask);
2029     break;
2030   case SOCKS5_DATA_TRANSFER:
2031     setup_data_transfer (s5r);
2032     return;
2033   case SOCKS5_WRITE_THEN_CLEANUP:
2034     cleanup_s5r (s5r);
2035     return;
2036   default:
2037     GNUNET_break (0);
2038     break;
2039   }
2040 }
2041
2042
2043 /**
2044  * Return a server response message indicating a failure to the client.
2045  *
2046  * @param s5r request to return failure code for
2047  * @param sc status code to return
2048  */
2049 static void
2050 signal_socks_failure (struct Socks5Request *s5r,
2051                       enum Socks5StatusCode sc)
2052 {
2053   struct Socks5ServerResponseMessage *s_resp;
2054
2055   s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf[s5r->wbuf_len];
2056   memset (s_resp, 0, sizeof (struct Socks5ServerResponseMessage));
2057   s_resp->version = SOCKS_VERSION_5;
2058   s_resp->reply = sc;
2059   s5r->state = SOCKS5_WRITE_THEN_CLEANUP;
2060   if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
2061     s5r->wtask = 
2062       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2063                                       s5r->sock,
2064                                       &do_write, s5r);
2065 }
2066
2067
2068 /**
2069  * Return a server response message indicating success.
2070  *
2071  * @param s5r request to return success status message for
2072  */
2073 static void
2074 signal_socks_success (struct Socks5Request *s5r)
2075 {
2076   struct Socks5ServerResponseMessage *s_resp;
2077
2078   s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf[s5r->wbuf_len];
2079   s_resp->version = SOCKS_VERSION_5;
2080   s_resp->reply = SOCKS5_STATUS_REQUEST_GRANTED;
2081   s_resp->reserved = 0;
2082   s_resp->addr_type = SOCKS5_AT_IPV4;
2083   /* zero out IPv4 address and port */
2084   memset (&s_resp[1], 
2085           0, 
2086           sizeof (struct in_addr) + sizeof (uint16_t));
2087   s5r->wbuf_len += sizeof (struct Socks5ServerResponseMessage) +
2088     sizeof (struct in_addr) + sizeof (uint16_t);  
2089   if (GNUNET_SCHEDULER_NO_TASK == s5r->wtask)      
2090     s5r->wtask =
2091       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2092                                       s5r->sock,
2093                                       &do_write, s5r); 
2094 }
2095
2096
2097 /**
2098  * Process GNS results for target domain.
2099  *
2100  * @param cls the ctask
2101  * @param rd_count number of records returned
2102  * @param rd record data
2103  */
2104 static void
2105 handle_gns_result (void *cls,
2106                    uint32_t rd_count,
2107                    const struct GNUNET_NAMESTORE_RecordData *rd)
2108 {
2109   struct Socks5Request *s5r = cls;
2110   uint32_t i;
2111   const struct GNUNET_NAMESTORE_RecordData *r;
2112   int got_ip;
2113
2114   s5r->gns_lookup = NULL;
2115   got_ip = GNUNET_NO;
2116   for (i=0;i<rd_count;i++)
2117   {
2118     r = &rd[i];
2119     switch (r->record_type)
2120     {
2121     case GNUNET_DNSPARSER_TYPE_A:
2122       {
2123         struct sockaddr_in *in;
2124
2125         if (sizeof (struct in_addr) != r->data_size)
2126         {
2127           GNUNET_break_op (0);
2128           break;
2129         }
2130         if (GNUNET_YES == got_ip)
2131           break;
2132         if (GNUNET_OK != 
2133             GNUNET_NETWORK_test_pf (PF_INET))
2134           break;
2135         got_ip = GNUNET_YES;
2136         in = (struct sockaddr_in *) &s5r->destination_address;
2137         in->sin_family = AF_INET;
2138         memcpy (&in->sin_addr,
2139                 r->data,
2140                 r->data_size);
2141         in->sin_port = htons (s5r->port);
2142 #if HAVE_SOCKADDR_IN_SIN_LEN
2143         in->sin_len = sizeof (*in);
2144 #endif
2145       }
2146       break;
2147     case GNUNET_DNSPARSER_TYPE_AAAA: 
2148       {
2149         struct sockaddr_in6 *in;
2150
2151         if (sizeof (struct in6_addr) != r->data_size)
2152         {
2153           GNUNET_break_op (0);
2154           break;
2155         }
2156         if (GNUNET_YES == got_ip)
2157           break; 
2158         if (GNUNET_OK != 
2159             GNUNET_NETWORK_test_pf (PF_INET))
2160           break;
2161         /* FIXME: allow user to disable IPv6 per configuration option... */
2162         got_ip = GNUNET_YES;
2163         in = (struct sockaddr_in6 *) &s5r->destination_address;
2164         in->sin6_family = AF_INET6;
2165         memcpy (&in->sin6_addr,
2166                 r->data,
2167                 r->data_size);
2168         in->sin6_port = htons (s5r->port);
2169 #if HAVE_SOCKADDR_IN_SIN_LEN
2170         in->sin6_len = sizeof (*in);
2171 #endif
2172       }
2173       break;      
2174     case GNUNET_NAMESTORE_TYPE_VPN:
2175       GNUNET_break (0); /* should have been translated within GNS */
2176       break;
2177     case GNUNET_NAMESTORE_TYPE_LEHO:
2178       GNUNET_free_non_null (s5r->leho);
2179       s5r->leho = GNUNET_strndup (r->data,
2180                                   r->data_size);
2181       break;
2182     default:
2183       /* don't care */
2184       break;
2185     }
2186   }
2187   if (GNUNET_YES != got_ip)
2188   {
2189     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
2190                 "Name resolution failed to yield useful IP address.\n");
2191     signal_socks_failure (s5r,
2192                           SOCKS5_STATUS_GENERAL_FAILURE);
2193     return;
2194   }
2195   s5r->state = SOCKS5_DATA_TRANSFER;
2196   signal_socks_success (s5r);  
2197 }
2198
2199
2200 /**
2201  * Remove the first @a len bytes from the beginning of the read buffer.
2202  *
2203  * @param s5r the handle clear the read buffer for
2204  * @param len number of bytes in read buffer to advance
2205  */
2206 static void
2207 clear_from_s5r_rbuf (struct Socks5Request *s5r,
2208                      size_t len)
2209 {
2210   GNUNET_assert (len <= s5r->rbuf_len);
2211   memmove (s5r->rbuf,
2212            &s5r->rbuf[len],
2213            s5r->rbuf_len - len);
2214   s5r->rbuf_len -= len;
2215 }
2216
2217
2218 /**
2219  * Read data from incoming Socks5 connection
2220  *
2221  * @param cls the closure with the `struct Socks5Request`
2222  * @param tc the scheduler context
2223  */
2224 static void
2225 do_s5r_read (void *cls,
2226              const struct GNUNET_SCHEDULER_TaskContext *tc)
2227 {
2228   struct Socks5Request *s5r = cls;
2229   const struct Socks5ClientHelloMessage *c_hello;
2230   struct Socks5ServerHelloMessage *s_hello;
2231   const struct Socks5ClientRequestMessage *c_req;
2232   ssize_t rlen;
2233   size_t alen;
2234
2235   s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
2236   if ( (NULL != tc->read_ready) &&
2237        (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) )
2238   {
2239     rlen = GNUNET_NETWORK_socket_recv (s5r->sock, 
2240                                        &s5r->rbuf[s5r->rbuf_len],
2241                                        sizeof (s5r->rbuf) - s5r->rbuf_len);
2242     if (rlen <= 0)
2243     {
2244       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
2245                   "socks5 client disconnected.\n");
2246       cleanup_s5r (s5r);
2247       return;
2248     }
2249     s5r->rbuf_len += rlen;
2250   }
2251   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2252                                               s5r->sock,
2253                                               &do_s5r_read, s5r);
2254   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2255               "Processing %u bytes of socks data in state %d\n",
2256               s5r->rbuf_len,
2257               s5r->state);
2258   switch (s5r->state)
2259   {
2260   case SOCKS5_INIT:
2261     c_hello = (const struct Socks5ClientHelloMessage*) &s5r->rbuf;
2262     if ( (s5r->rbuf_len < sizeof (struct Socks5ClientHelloMessage)) ||
2263          (s5r->rbuf_len < sizeof (struct Socks5ClientHelloMessage) + c_hello->num_auth_methods) )
2264       return; /* need more data */
2265     if (SOCKS_VERSION_5 != c_hello->version)
2266     {
2267       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2268                   _("Unsupported socks version %d\n"),
2269                   (int) c_hello->version);
2270       cleanup_s5r (s5r);
2271       return;
2272     }
2273     clear_from_s5r_rbuf (s5r,
2274                          sizeof (struct Socks5ClientHelloMessage) + c_hello->num_auth_methods);
2275     GNUNET_assert (0 == s5r->wbuf_len);
2276     s_hello = (struct Socks5ServerHelloMessage *) &s5r->wbuf;
2277     s5r->wbuf_len = sizeof (struct Socks5ServerHelloMessage);
2278     s_hello->version = SOCKS_VERSION_5;
2279     s_hello->auth_method = SOCKS_AUTH_NONE;
2280     GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == s5r->wtask);
2281     s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2282                                                  s5r->sock,
2283                                                  &do_write, s5r);
2284     s5r->state = SOCKS5_REQUEST;
2285     return;
2286   case SOCKS5_REQUEST:
2287     c_req = (const struct Socks5ClientRequestMessage *) &s5r->rbuf;
2288     if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage))
2289       return;
2290     switch (c_req->command)
2291     {
2292     case SOCKS5_CMD_TCP_STREAM:
2293       /* handled below */
2294       break;
2295     default:
2296       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2297                   _("Unsupported socks command %d\n"),
2298                   (int) c_req->command);
2299       signal_socks_failure (s5r,
2300                             SOCKS5_STATUS_COMMAND_NOT_SUPPORTED);
2301       return;
2302     }
2303     switch (c_req->addr_type)
2304     {
2305     case SOCKS5_AT_IPV4:
2306       {
2307         const struct in_addr *v4 = (const struct in_addr *) &c_req[1];
2308         const uint16_t *port = (const uint16_t *) &v4[1];
2309         struct sockaddr_in *in;
2310
2311         s5r->port = ntohs (*port);
2312         alen = sizeof (struct in_addr);
2313         if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
2314             alen + sizeof (uint16_t))
2315           return; /* need more data */
2316         in = (struct sockaddr_in *) &s5r->destination_address;
2317         in->sin_family = AF_INET;
2318         in->sin_addr = *v4;
2319         in->sin_port = *port;
2320 #if HAVE_SOCKADDR_IN_SIN_LEN
2321         in->sin_len = sizeof (*in);
2322 #endif
2323         s5r->state = SOCKS5_DATA_TRANSFER;
2324       }
2325       break;
2326     case SOCKS5_AT_IPV6:
2327       {
2328         const struct in6_addr *v6 = (const struct in6_addr *) &c_req[1];
2329         const uint16_t *port = (const uint16_t *) &v6[1];
2330         struct sockaddr_in6 *in;
2331
2332         s5r->port = ntohs (*port);
2333         alen = sizeof (struct in6_addr);
2334         if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
2335             alen + sizeof (uint16_t))
2336           return; /* need more data */
2337         in = (struct sockaddr_in6 *) &s5r->destination_address;
2338         in->sin6_family = AF_INET6;
2339         in->sin6_addr = *v6;
2340         in->sin6_port = *port;
2341 #if HAVE_SOCKADDR_IN_SIN_LEN
2342         in->sin6_len = sizeof (*in);
2343 #endif
2344         s5r->state = SOCKS5_DATA_TRANSFER;
2345       }
2346       break;
2347     case SOCKS5_AT_DOMAINNAME:
2348       {
2349         const uint8_t *dom_len;
2350         const char *dom_name;
2351         const uint16_t *port;   
2352         
2353         dom_len = (const uint8_t *) &c_req[1];
2354         alen = *dom_len + 1;
2355         if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
2356             alen + sizeof (uint16_t))
2357           return; /* need more data */
2358         dom_name = (const char *) &dom_len[1];
2359         port = (const uint16_t*) &dom_name[*dom_len];
2360         s5r->domain = GNUNET_strndup (dom_name, *dom_len);
2361         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2362                     "Requested connection is to %s:%d\n",
2363                     s5r->domain,
2364                     ntohs (*port));
2365         s5r->state = SOCKS5_RESOLVING;
2366         s5r->port = ntohs (*port);
2367         s5r->gns_lookup = GNUNET_GNS_lookup (gns_handle,
2368                                              s5r->domain,
2369                                              &local_gns_zone,
2370                                              GNUNET_DNSPARSER_TYPE_A,
2371                                              GNUNET_NO /* only cached */,
2372                                              (GNUNET_YES == do_shorten) ? &local_shorten_zone : NULL,
2373                                              &handle_gns_result,
2374                                              s5r);                                           
2375         break;
2376       }
2377     default:
2378       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2379                   _("Unsupported socks address type %d\n"),
2380                   (int) c_req->addr_type);
2381       signal_socks_failure (s5r,
2382                             SOCKS5_STATUS_ADDRESS_TYPE_NOT_SUPPORTED);
2383       return;
2384     }
2385     clear_from_s5r_rbuf (s5r,
2386                          sizeof (struct Socks5ClientRequestMessage) +
2387                          alen + sizeof (uint16_t));
2388     if (0 != s5r->rbuf_len)
2389     {
2390       /* read more bytes than healthy, why did the client send more!? */
2391       GNUNET_break_op (0);
2392       signal_socks_failure (s5r,
2393                             SOCKS5_STATUS_GENERAL_FAILURE);
2394       return;       
2395     }
2396     if (SOCKS5_DATA_TRANSFER == s5r->state)
2397     {
2398       /* if we are not waiting for GNS resolution, signal success */
2399       signal_socks_success (s5r);
2400     }
2401     /* We are done reading right now */
2402     GNUNET_SCHEDULER_cancel (s5r->rtask);
2403     s5r->rtask = GNUNET_SCHEDULER_NO_TASK;    
2404     return;
2405   case SOCKS5_RESOLVING:
2406     GNUNET_assert (0);
2407     return;
2408   case SOCKS5_DATA_TRANSFER:
2409     GNUNET_assert (0);
2410     return;
2411   default:
2412     GNUNET_assert (0);
2413     return;
2414   }
2415 }
2416
2417
2418 /**
2419  * Accept new incoming connections
2420  *
2421  * @param cls the closure
2422  * @param tc the scheduler context
2423  */
2424 static void
2425 do_accept (void *cls, 
2426            const struct GNUNET_SCHEDULER_TaskContext *tc)
2427 {
2428   struct GNUNET_NETWORK_Handle *s;
2429   struct Socks5Request *s5r;
2430
2431   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2432                                          lsock,
2433                                          &do_accept, NULL);
2434   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
2435   if (NULL == s)
2436   {
2437     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
2438     return;
2439   }
2440   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2441               "Got an inbound connection, waiting for data\n");
2442   s5r = GNUNET_new (struct Socks5Request);
2443   GNUNET_CONTAINER_DLL_insert (s5r_head,
2444                                s5r_tail,
2445                                s5r);
2446   s5r->sock = s;
2447   s5r->state = SOCKS5_INIT;
2448   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2449                                               s5r->sock,
2450                                               &do_s5r_read, s5r);
2451 }
2452
2453
2454 /* ******************* General / main code ********************* */
2455
2456
2457 /**
2458  * Task run on shutdown
2459  *
2460  * @param cls closure
2461  * @param tc task context
2462  */
2463 static void
2464 do_shutdown (void *cls,
2465              const struct GNUNET_SCHEDULER_TaskContext *tc)
2466 {
2467   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2468               "Shutting down...\n");
2469   while (NULL != mhd_httpd_head)
2470     kill_httpd (mhd_httpd_head);
2471   if (NULL != lsock)
2472   {
2473     GNUNET_NETWORK_socket_close (lsock);
2474     lsock = NULL;
2475   }
2476   if (NULL != id_op)
2477   {
2478     GNUNET_IDENTITY_cancel (id_op);
2479     id_op = NULL;
2480   }
2481   if (NULL != identity)
2482   {
2483     GNUNET_IDENTITY_disconnect (identity);
2484     identity = NULL;
2485   }
2486   if (NULL != curl_multi)
2487   {
2488     curl_multi_cleanup (curl_multi);
2489     curl_multi = NULL;
2490   }
2491   if (NULL != gns_handle)
2492   {
2493     GNUNET_GNS_disconnect (gns_handle);
2494     gns_handle = NULL;
2495   }
2496   if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
2497   {
2498     GNUNET_SCHEDULER_cancel (curl_download_task);
2499     curl_download_task = GNUNET_SCHEDULER_NO_TASK;
2500   }
2501   if (GNUNET_SCHEDULER_NO_TASK != ltask)
2502   {
2503     GNUNET_SCHEDULER_cancel (ltask);
2504     ltask = GNUNET_SCHEDULER_NO_TASK;
2505   }
2506   gnutls_x509_crt_deinit (proxy_ca.cert);
2507   gnutls_x509_privkey_deinit (proxy_ca.key);
2508   gnutls_global_deinit ();
2509 }
2510
2511
2512 /**
2513  * Continue initialization after we have our zone information.
2514  */
2515 static void 
2516 run_cont () 
2517 {
2518   struct MhdHttpList *hd;
2519   struct sockaddr_in sa;
2520
2521   /* Open listen socket for socks proxy */
2522   /* FIXME: support IPv6! */
2523   memset (&sa, 0, sizeof (sa));
2524   sa.sin_family = AF_INET;
2525   sa.sin_port = htons (port);
2526 #if HAVE_SOCKADDR_IN_SIN_LEN
2527   sa.sin_len = sizeof (sa);
2528 #endif
2529   lsock = GNUNET_NETWORK_socket_create (AF_INET,
2530                                         SOCK_STREAM,
2531                                         0);
2532   if (NULL == lsock) 
2533   {
2534     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
2535     GNUNET_SCHEDULER_shutdown ();
2536     return;
2537   }
2538   if (GNUNET_OK !=
2539       GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
2540                                   sizeof (sa), 0))
2541   {
2542     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
2543     GNUNET_SCHEDULER_shutdown ();
2544     return;
2545   }
2546   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
2547   {
2548     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
2549     return;
2550   }
2551   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2552                                          lsock, &do_accept, NULL);
2553
2554   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
2555   {
2556     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2557                 "cURL global init failed!\n");
2558     GNUNET_SCHEDULER_shutdown ();
2559     return;
2560   }
2561   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2562               "Proxy listens on port %u\n",
2563               port);
2564
2565   /* start MHD daemon for HTTP */
2566   hd = GNUNET_new (struct MhdHttpList);
2567   hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
2568                                  0,
2569                                  NULL, NULL,
2570                                  &create_response, hd,
2571                                  MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2572                                  MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL,
2573                                  MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
2574                                  MHD_OPTION_END);
2575   if (NULL == hd->daemon)
2576   {
2577     GNUNET_free (hd);
2578     GNUNET_SCHEDULER_shutdown ();
2579     return;
2580   }
2581   httpd = hd;
2582   GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2583 }
2584
2585
2586 /** 
2587  * Method called to inform about the egos of the shorten zone of this peer.
2588  *
2589  * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
2590  * this function is only called ONCE, and 'NULL' being passed in
2591  * @a ego does indicate an error (i.e. name is taken or no default
2592  * value is known).  If @a ego is non-NULL and if '*ctx'
2593  * is set in those callbacks, the value WILL be passed to a subsequent
2594  * call to the identity callback of #GNUNET_IDENTITY_connect (if 
2595  * that one was not NULL).
2596  *
2597  * @param cls closure, NULL
2598  * @param ego ego handle
2599  * @param ctx context for application to store data for this ego
2600  *                 (during the lifetime of this process, initially NULL)
2601  * @param name name assigned by the user for this ego,
2602  *                   NULL if the user just deleted the ego and it
2603  *                   must thus no longer be used
2604  */
2605 static void
2606 identity_shorten_cb (void *cls,
2607                      struct GNUNET_IDENTITY_Ego *ego,
2608                      void **ctx,
2609                      const char *name)
2610 {
2611   id_op = NULL;
2612   if (NULL == ego)
2613   {
2614     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2615                 _("No ego configured for `shorten-zone`\n"));
2616   }
2617   else
2618   {
2619     local_shorten_zone = *GNUNET_IDENTITY_ego_get_private_key (ego);
2620     do_shorten = GNUNET_YES;
2621   }
2622   run_cont ();
2623 }
2624
2625
2626 /** 
2627  * Method called to inform about the egos of the master zone of this peer.
2628  *
2629  * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
2630  * this function is only called ONCE, and 'NULL' being passed in
2631  * @a ego does indicate an error (i.e. name is taken or no default
2632  * value is known).  If @a ego is non-NULL and if '*ctx'
2633  * is set in those callbacks, the value WILL be passed to a subsequent
2634  * call to the identity callback of #GNUNET_IDENTITY_connect (if 
2635  * that one was not NULL).
2636  *
2637  * @param cls closure, NULL
2638  * @param ego ego handle
2639  * @param ctx context for application to store data for this ego
2640  *                 (during the lifetime of this process, initially NULL)
2641  * @param name name assigned by the user for this ego,
2642  *                   NULL if the user just deleted the ego and it
2643  *                   must thus no longer be used
2644  */
2645 static void
2646 identity_master_cb (void *cls,
2647                     struct GNUNET_IDENTITY_Ego *ego,
2648                     void **ctx,
2649                     const char *name)
2650 {
2651   id_op = NULL;
2652   if (NULL == ego)
2653   {
2654     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2655                 _("No ego configured for `master-zone`\n"));
2656     GNUNET_SCHEDULER_shutdown ();
2657     return;
2658   }
2659   GNUNET_IDENTITY_ego_get_public_key (ego,
2660                                       &local_gns_zone);
2661   id_op = GNUNET_IDENTITY_get (identity,
2662                                "shorten-zone",
2663                                &identity_shorten_cb,
2664                                NULL);
2665 }
2666
2667
2668 /**
2669  * Main function that will be run
2670  *
2671  * @param cls closure
2672  * @param args remaining command-line arguments
2673  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
2674  * @param c configuration
2675  */
2676 static void
2677 run (void *cls, char *const *args, const char *cfgfile,
2678      const struct GNUNET_CONFIGURATION_Handle *c)
2679 {
2680   char* cafile_cfg = NULL;
2681   char* cafile;
2682
2683   cfg = c;
2684   if (NULL == (curl_multi = curl_multi_init ()))
2685   {
2686     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2687                 "Failed to create cURL multi handle!\n");
2688     return;
2689   } 
2690   cafile = cafile_opt;
2691   if (NULL == cafile)
2692   {
2693     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
2694                                                               "PROXY_CACERT",
2695                                                               &cafile_cfg))
2696     {
2697       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2698                                  "gns-proxy",
2699                                  "PROXY_CACERT");
2700       return;
2701     }
2702     cafile = cafile_cfg;
2703   }
2704   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2705               "Using %s as CA\n", cafile);
2706   
2707   gnutls_global_init ();
2708   gnutls_x509_crt_init (&proxy_ca.cert);
2709   gnutls_x509_privkey_init (&proxy_ca.key);
2710   
2711   if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
2712        (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
2713   {
2714     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2715                 _("Failed to load SSL/TLS key and certificate from `%s'\n"),
2716                 cafile);
2717     gnutls_x509_crt_deinit (proxy_ca.cert);
2718     gnutls_x509_privkey_deinit (proxy_ca.key);
2719     gnutls_global_deinit ();
2720     GNUNET_free_non_null (cafile_cfg);  
2721     return;
2722   }
2723   GNUNET_free_non_null (cafile_cfg);
2724   if (NULL == (gns_handle = GNUNET_GNS_connect (cfg)))
2725   {
2726     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2727                 "Unable to connect to GNS!\n");
2728     gnutls_x509_crt_deinit (proxy_ca.cert);
2729     gnutls_x509_privkey_deinit (proxy_ca.key);
2730     gnutls_global_deinit ();
2731     return;
2732   }
2733   identity = GNUNET_IDENTITY_connect (cfg,
2734                                       NULL, NULL);  
2735   id_op = GNUNET_IDENTITY_get (identity,
2736                                "master-zone",
2737                                &identity_master_cb,
2738                                NULL);  
2739   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
2740                                 &do_shutdown, NULL);
2741 }
2742
2743
2744 /**
2745  * The main function for gnunet-gns-proxy.
2746  *
2747  * @param argc number of arguments from the command line
2748  * @param argv command line arguments
2749  * @return 0 ok, 1 on error
2750  */
2751 int
2752 main (int argc, char *const *argv)
2753 {
2754   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
2755     {'p', "port", NULL,
2756      gettext_noop ("listen on specified port (default: 7777)"), 1,
2757      &GNUNET_GETOPT_set_ulong, &port},
2758     {'a', "authority", NULL,
2759       gettext_noop ("pem file to use as CA"), 1,
2760       &GNUNET_GETOPT_set_string, &cafile_opt},
2761     GNUNET_GETOPT_OPTION_END
2762   };
2763   static const char* page = 
2764     "<html><head><title>gnunet-gns-proxy</title>"
2765     "</head><body>cURL fail</body></html>";
2766   int ret;
2767
2768   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
2769     return 2;
2770   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
2771   curl_failure_response = MHD_create_response_from_buffer (strlen (page),
2772                                                            (void*)page,
2773                                                            MHD_RESPMEM_PERSISTENT);
2774
2775   ret =
2776       (GNUNET_OK ==
2777        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
2778                            _("GNUnet GNS proxy"),
2779                            options,
2780                            &run, NULL)) ? 0 : 1;
2781   MHD_destroy_response (curl_failure_response);
2782   GNUNET_free_non_null ((char *) argv);
2783   return ret;
2784 }
2785
2786 /* end of gnunet-gns-proxy.c */