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