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