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