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