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