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