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