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