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