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