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