2 This file is part of GNUnet.
3 Copyright (C) 2012-2014 GNUnet e.V.
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 * @author Martin Schanzenbach
22 * @author Christian Grothoff
23 * @file src/gns/gnunet-gns-proxy.c
24 * @brief HTTP(S) proxy that rewrites URIs and fakes certificats to make GNS work
25 * with legacy browsers
28 * - double-check queueing logic
31 #include <microhttpd.h>
33 #include <curl/curl.h>
34 #elif HAVE_GNURL_CURL_H
35 #include <gnurl/curl.h>
37 #include <gnutls/gnutls.h>
38 #include <gnutls/x509.h>
39 #include <gnutls/abstract.h>
40 #include <gnutls/crypto.h>
42 #include <gnutls/dane.h>
45 #include "gnunet_util_lib.h"
46 #include "gnunet_gns_service.h"
47 #include "gnunet_identity_service.h"
52 * Default Socks5 listen port.
54 #define GNUNET_GNS_PROXY_PORT 7777
57 * Maximum supported length for a URI.
58 * Should die. @deprecated
60 #define MAX_HTTP_URI_LENGTH 2048
63 * Size of the buffer for the data upload / download. Must be
64 * enough for curl, thus CURL_MAX_WRITE_SIZE is needed here (16k).
66 #define IO_BUFFERSIZE CURL_MAX_WRITE_SIZE
69 * Size of the read/write buffers for Socks. Uses
70 * 256 bytes for the hostname (at most), plus a few
71 * bytes overhead for the messages.
73 #define SOCKS_BUFFERSIZE (256 + 32)
76 * Port for plaintext HTTP.
83 #define HTTPS_PORT 443
86 * Largest allowed size for a PEM certificate.
88 #define MAX_PEM_SIZE (10 * 1024)
91 * After how long do we clean up unused MHD SSL/TLS instances?
93 #define MHD_CACHE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
96 * After how long do we clean up Socks5 handles that failed to show any activity
97 * with their respective MHD instance?
99 #define HTTP_HANDSHAKE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
105 * @param level log level
106 * @param fun name of curl_easy-function that gave the error
107 * @param rc return code from curl
109 #define LOG_CURL_EASY(level,fun,rc) GNUNET_log(level, _("%s failed at %s:%d: `%s'\n"), fun, __FILE__, __LINE__, curl_easy_strerror (rc))
112 /* *************** Socks protocol definitions (move to TUN?) ****************** */
115 * Which SOCKS version do we speak?
117 #define SOCKS_VERSION_5 0x05
120 * Flag to set for 'no authentication'.
122 #define SOCKS_AUTH_NONE 0
126 * Commands in Socks5.
131 * Establish TCP/IP stream.
133 SOCKS5_CMD_TCP_STREAM = 1,
136 * Establish TCP port binding.
138 SOCKS5_CMD_TCP_PORT = 2,
141 * Establish UDP port binding.
143 SOCKS5_CMD_UDP_PORT = 3
148 * Address types in Socks5.
150 enum Socks5AddressType
160 SOCKS5_AT_DOMAINNAME = 3,
171 * Status codes in Socks5 response.
173 enum Socks5StatusCode
175 SOCKS5_STATUS_REQUEST_GRANTED = 0,
176 SOCKS5_STATUS_GENERAL_FAILURE = 1,
177 SOCKS5_STATUS_CONNECTION_NOT_ALLOWED_BY_RULE = 2,
178 SOCKS5_STATUS_NETWORK_UNREACHABLE = 3,
179 SOCKS5_STATUS_HOST_UNREACHABLE = 4,
180 SOCKS5_STATUS_CONNECTION_REFUSED_BY_HOST = 5,
181 SOCKS5_STATUS_TTL_EXPIRED = 6,
182 SOCKS5_STATUS_COMMAND_NOT_SUPPORTED = 7,
183 SOCKS5_STATUS_ADDRESS_TYPE_NOT_SUPPORTED = 8
188 * Client hello in Socks5 protocol.
190 struct Socks5ClientHelloMessage
193 * Should be #SOCKS_VERSION_5.
198 * How many authentication methods does the client support.
200 uint8_t num_auth_methods;
202 /* followed by supported authentication methods, 1 byte per method */
208 * Server hello in Socks5 protocol.
210 struct Socks5ServerHelloMessage
213 * Should be #SOCKS_VERSION_5.
218 * Chosen authentication method, for us always #SOCKS_AUTH_NONE,
219 * which skips the authentication step.
226 * Client socks request in Socks5 protocol.
228 struct Socks5ClientRequestMessage
231 * Should be #SOCKS_VERSION_5.
236 * Command code, we only uspport #SOCKS5_CMD_TCP_STREAM.
241 * Reserved, always zero.
246 * Address type, an `enum Socks5AddressType`.
251 * Followed by either an ip4/ipv6 address or a domain name with a
252 * length field (uint8_t) in front (depending on @e addr_type).
253 * followed by port number in network byte order (uint16_t).
259 * Server response to client requests in Socks5 protocol.
261 struct Socks5ServerResponseMessage
264 * Should be #SOCKS_VERSION_5.
269 * Status code, an `enum Socks5StatusCode`
279 * Address type, an `enum Socks5AddressType`.
284 * Followed by either an ip4/ipv6 address or a domain name with a
285 * length field (uint8_t) in front (depending on @e addr_type).
286 * followed by port number in network byte order (uint16_t).
293 /* *********************** Datastructures for HTTP handling ****************** */
296 * A structure for CA cert/key
303 gnutls_x509_crt_t cert;
308 gnutls_x509_privkey_t key;
313 * Structure for GNS certificates
315 struct ProxyGNSCertificate
318 * The certificate as PEM
320 char cert[MAX_PEM_SIZE];
323 * The private key as PEM
325 char key[MAX_PEM_SIZE];
331 * A structure for all running Httpds
338 struct MhdHttpList *prev;
343 struct MhdHttpList *next;
346 * the domain name to server (only important for SSL)
353 struct MHD_Daemon *daemon;
356 * Optional proxy certificate used
358 struct ProxyGNSCertificate *proxy_cert;
363 struct GNUNET_SCHEDULER_Task *httpd_task;
366 * is this an ssl daemon?
373 /* ***************** Datastructures for Socks handling **************** */
382 * We're waiting to get the client hello.
387 * We're waiting to get the initial request.
392 * We are currently resolving the destination.
397 * We're in transfer mode.
399 SOCKS5_DATA_TRANSFER,
402 * Finish writing the write buffer, then clean up.
404 SOCKS5_WRITE_THEN_CLEANUP,
407 * Socket has been passed to MHD, do not close it anymore.
409 SOCKS5_SOCKET_WITH_MHD,
412 * We've started receiving upload data from MHD.
414 SOCKS5_SOCKET_UPLOAD_STARTED,
417 * We've finished receiving upload data from MHD.
419 SOCKS5_SOCKET_UPLOAD_DONE,
422 * We've finished uploading data via CURL and can now download.
424 SOCKS5_SOCKET_DOWNLOAD_STARTED,
427 * We've finished receiving download data from cURL.
429 SOCKS5_SOCKET_DOWNLOAD_DONE
436 struct HttpResponseHeader
441 struct HttpResponseHeader *next;
446 struct HttpResponseHeader *prev;
460 * A structure for socks requests
468 struct Socks5Request *next;
473 struct Socks5Request *prev;
478 struct GNUNET_NETWORK_Handle *sock;
481 * Handle to GNS lookup, during #SOCKS5_RESOLVING phase.
483 struct GNUNET_GNS_LookupRequest *gns_lookup;
486 * Client socket read task
488 struct GNUNET_SCHEDULER_Task * rtask;
491 * Client socket write task
493 struct GNUNET_SCHEDULER_Task * wtask;
498 struct GNUNET_SCHEDULER_Task * timeout_task;
503 char rbuf[SOCKS_BUFFERSIZE];
508 char wbuf[SOCKS_BUFFERSIZE];
511 * Buffer we use for moving data between MHD and curl (in both directions).
513 char io_buf[IO_BUFFERSIZE];
516 * MHD HTTP instance handling this request, NULL for none.
518 struct MhdHttpList *hd;
521 * MHD response object for this request.
523 struct MHD_Response *response;
526 * the domain name to server (only important for SSL)
531 * DNS Legacy Host Name as given by GNS, NULL if not given.
536 * Payload of the (last) DANE record encountered.
551 * HTTP request headers for the curl request.
553 struct curl_slist *headers;
556 * DNS->IP mappings resolved through GNS
558 struct curl_slist *hosts;
561 * HTTP response code to give to MHD for the response.
563 unsigned int response_code;
566 * Number of bytes in @e dane_data.
568 size_t dane_data_len;
571 * Number of bytes already in read buffer
576 * Number of bytes already in write buffer
581 * Number of bytes already in the IO buffer.
586 * Once known, what's the target address for the connection?
588 struct sockaddr_storage destination_address;
593 enum SocksPhase state;
596 * Desired destination port.
601 * Headers from response
603 struct HttpResponseHeader *header_head;
606 * Headers from response
608 struct HttpResponseHeader *header_tail;
611 * SSL Certificate status
618 /* *********************** Globals **************************** */
622 * The port the proxy is running on (default 7777)
624 static unsigned long long port = GNUNET_GNS_PROXY_PORT;
627 * The CA file (pem) to use for the proxy CA
629 static char *cafile_opt;
632 * The listen socket of the proxy for IPv4
634 static struct GNUNET_NETWORK_Handle *lsock4;
637 * The listen socket of the proxy for IPv6
639 static struct GNUNET_NETWORK_Handle *lsock6;
642 * The listen task ID for IPv4
644 static struct GNUNET_SCHEDULER_Task * ltask4;
647 * The listen task ID for IPv6
649 static struct GNUNET_SCHEDULER_Task * ltask6;
652 * The cURL download task (curl multi API).
654 static struct GNUNET_SCHEDULER_Task * curl_download_task;
657 * The cURL multi handle
659 static CURLM *curl_multi;
662 * Handle to the GNS service
664 static struct GNUNET_GNS_Handle *gns_handle;
667 * DLL for http/https daemons
669 static struct MhdHttpList *mhd_httpd_head;
672 * DLL for http/https daemons
674 static struct MhdHttpList *mhd_httpd_tail;
677 * Daemon for HTTP (we have one per SSL certificate, and then one for
678 * all HTTP connections; this is the one for HTTP, not HTTPS).
680 static struct MhdHttpList *httpd;
683 * DLL of active socks requests.
685 static struct Socks5Request *s5r_head;
688 * DLL of active socks requests.
690 static struct Socks5Request *s5r_tail;
693 * The users local GNS master zone
695 static struct GNUNET_CRYPTO_EcdsaPublicKey local_gns_zone;
698 * The CA for SSL certificate generation
700 static struct ProxyCA proxy_ca;
703 * Response we return on cURL failures.
705 static struct MHD_Response *curl_failure_response;
708 * Connection to identity service.
710 static struct GNUNET_IDENTITY_Handle *identity;
713 * Request for our ego.
715 static struct GNUNET_IDENTITY_Operation *id_op;
720 static const struct GNUNET_CONFIGURATION_Handle *cfg;
723 /* ************************* Global helpers ********************* */
727 * Run MHD now, we have extra data ready for the callback.
729 * @param hd the daemon to run now.
732 run_mhd_now (struct MhdHttpList *hd);
736 * Clean up s5r handles.
738 * @param s5r the handle to destroy
741 cleanup_s5r (struct Socks5Request *s5r)
743 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
744 "Cleaning up socks request\n");
745 if (NULL != s5r->curl)
747 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
748 "Cleaning up cURL handle\n");
749 curl_multi_remove_handle (curl_multi, s5r->curl);
750 curl_easy_cleanup (s5r->curl);
753 curl_slist_free_all (s5r->headers);
754 if (NULL != s5r->hosts)
756 curl_slist_free_all (s5r->hosts);
758 if ( (NULL != s5r->response) &&
759 (curl_failure_response != s5r->response) )
760 MHD_destroy_response (s5r->response);
761 if (NULL != s5r->rtask)
762 GNUNET_SCHEDULER_cancel (s5r->rtask);
763 if (NULL != s5r->timeout_task)
764 GNUNET_SCHEDULER_cancel (s5r->timeout_task);
765 if (NULL != s5r->wtask)
766 GNUNET_SCHEDULER_cancel (s5r->wtask);
767 if (NULL != s5r->gns_lookup)
768 GNUNET_GNS_lookup_cancel (s5r->gns_lookup);
769 if (NULL != s5r->sock)
771 if (SOCKS5_SOCKET_WITH_MHD <= s5r->state)
772 GNUNET_NETWORK_socket_free_memory_only_ (s5r->sock);
774 GNUNET_NETWORK_socket_close (s5r->sock);
776 GNUNET_CONTAINER_DLL_remove (s5r_head,
779 GNUNET_free_non_null (s5r->domain);
780 GNUNET_free_non_null (s5r->leho);
781 GNUNET_free_non_null (s5r->url);
782 GNUNET_free_non_null (s5r->dane_data);
787 /* ************************* HTTP handling with cURL *********************** */
791 * Callback for MHD response generation. This function is called from
792 * MHD whenever MHD expects to get data back. Copies data from the
793 * io_buf, if available.
795 * @param cls closure with our `struct Socks5Request`
796 * @param pos in buffer
797 * @param buf where to copy data
798 * @param max available space in @a buf
799 * @return number of bytes written to @a buf
802 mhd_content_cb (void *cls,
807 struct Socks5Request *s5r = cls;
808 size_t bytes_to_copy;
810 if ( (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) ||
811 (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
813 /* we're still not done with the upload, do not yet
814 start the download, the IO buffer is still full
816 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
817 "Pausing MHD download, not yet ready for download\n");
818 return 0; /* not yet ready for data download */
820 bytes_to_copy = GNUNET_MIN (max,
822 if ( (0 == bytes_to_copy) &&
823 (SOCKS5_SOCKET_DOWNLOAD_DONE != s5r->state) )
825 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
826 "Pausing MHD download, no data available\n");
827 return 0; /* more data later */
829 if ( (0 == bytes_to_copy) &&
830 (SOCKS5_SOCKET_DOWNLOAD_DONE == s5r->state) )
832 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
833 "Completed MHD download\n");
834 return MHD_CONTENT_READER_END_OF_STREAM;
836 GNUNET_memcpy (buf, s5r->io_buf, bytes_to_copy);
837 memmove (s5r->io_buf,
838 &s5r->io_buf[bytes_to_copy],
839 s5r->io_len - bytes_to_copy);
840 s5r->io_len -= bytes_to_copy;
841 if (NULL != s5r->curl)
842 curl_easy_pause (s5r->curl, CURLPAUSE_CONT);
843 return bytes_to_copy;
848 * Check that the website has presented us with a valid SSL certificate.
849 * The certificate must either match the domain name or the LEHO name
850 * (or, if available, the TLSA record).
852 * @param s5r request to check for.
853 * @return #GNUNET_OK if the certificate is valid
856 check_ssl_certificate (struct Socks5Request *s5r)
858 unsigned int cert_list_size;
859 const gnutls_datum_t *chainp;
860 const struct curl_tlssessioninfo *tlsinfo;
861 char certdn[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 3];
863 gnutls_x509_crt_t x509_cert;
867 s5r->ssl_checked = GNUNET_YES;
868 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
869 "Checking SSL certificate\n");
871 curl_easy_getinfo (s5r->curl,
872 CURLINFO_TLS_SESSION,
873 (struct curl_slist **) &tlsinfo))
874 return GNUNET_SYSERR;
875 if (CURLSSLBACKEND_GNUTLS != tlsinfo->backend)
877 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
878 _("Unsupported CURL SSL backend %d\n"),
880 return GNUNET_SYSERR;
882 chainp = gnutls_certificate_get_peers (tlsinfo->internals, &cert_list_size);
883 if ( (! chainp) || (0 == cert_list_size) )
884 return GNUNET_SYSERR;
886 size = sizeof (certdn);
887 /* initialize an X.509 certificate structure. */
888 gnutls_x509_crt_init (&x509_cert);
889 gnutls_x509_crt_import (x509_cert,
891 GNUTLS_X509_FMT_DER);
893 if (0 != (rc = gnutls_x509_crt_get_dn_by_oid (x509_cert,
894 GNUTLS_OID_X520_COMMON_NAME,
895 0, /* the first and only one */
896 0 /* no DER encoding */,
900 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
901 _("Failed to fetch CN from cert: %s\n"),
902 gnutls_strerror(rc));
903 gnutls_x509_crt_deinit (x509_cert);
904 return GNUNET_SYSERR;
906 /* check for TLSA/DANE records */
908 if (NULL != s5r->dane_data)
910 char *dd[] = { s5r->dane_data, NULL };
911 int dlen[] = { s5r->dane_data_len, 0};
912 dane_state_t dane_state;
913 dane_query_t dane_query;
916 /* FIXME: add flags to gnutls to NOT read UNBOUND_ROOT_KEY_FILE here! */
917 if (0 != (rc = dane_state_init (&dane_state,
918 #ifdef DANE_F_IGNORE_DNSSEC
919 DANE_F_IGNORE_DNSSEC |
921 DANE_F_IGNORE_LOCAL_RESOLVER)))
923 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
924 _("Failed to initialize DANE: %s\n"),
926 gnutls_x509_crt_deinit (x509_cert);
927 return GNUNET_SYSERR;
929 if (0 != (rc = dane_raw_tlsa (dane_state,
936 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
937 _("Failed to parse DANE record: %s\n"),
939 dane_state_deinit (dane_state);
940 gnutls_x509_crt_deinit (x509_cert);
941 return GNUNET_SYSERR;
943 if (0 != (rc = dane_verify_crt_raw (dane_state,
946 gnutls_certificate_type_get (tlsinfo->internals),
951 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
952 _("Failed to verify TLS connection using DANE: %s\n"),
954 dane_query_deinit (dane_query);
955 dane_state_deinit (dane_state);
956 gnutls_x509_crt_deinit (x509_cert);
957 return GNUNET_SYSERR;
961 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
962 _("Failed DANE verification failed with GnuTLS verify status code: %u\n"),
964 dane_query_deinit (dane_query);
965 dane_state_deinit (dane_state);
966 gnutls_x509_crt_deinit (x509_cert);
967 return GNUNET_SYSERR;
969 dane_query_deinit (dane_query);
970 dane_state_deinit (dane_state);
976 /* try LEHO or ordinary domain name X509 verification */
978 if (NULL != s5r->leho)
982 if (0 == (rc = gnutls_x509_crt_check_hostname (x509_cert,
985 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
986 _("SSL certificate subject name (%s) does not match `%s'\n"),
989 gnutls_x509_crt_deinit (x509_cert);
990 return GNUNET_SYSERR;
995 /* we did not even have the domain name!? */
997 return GNUNET_SYSERR;
1000 gnutls_x509_crt_deinit (x509_cert);
1006 * We're getting an HTTP response header from cURL. Convert it to the
1007 * MHD response headers. Mostly copies the headers, but makes special
1008 * adjustments to "Set-Cookie" and "Location" headers as those may need
1009 * to be changed from the LEHO to the domain the browser expects.
1011 * @param buffer curl buffer with a single line of header data; not 0-terminated!
1012 * @param size curl blocksize
1013 * @param nmemb curl blocknumber
1014 * @param cls our `struct Socks5Request *`
1015 * @return size of processed bytes
1018 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
1020 struct Socks5Request *s5r = cls;
1021 struct HttpResponseHeader *header;
1022 size_t bytes = size * nmemb;
1024 const char *hdr_type;
1025 const char *cookie_domain;
1027 char *new_cookie_hdr;
1030 size_t delta_cdomain;
1034 /* first, check SSL certificate */
1035 if ((GNUNET_YES != s5r->ssl_checked) &&
1036 (HTTPS_PORT == s5r->port))
1038 if (GNUNET_OK != check_ssl_certificate (s5r))
1042 ndup = GNUNET_strndup (buffer, bytes);
1043 hdr_type = strtok (ndup, ":");
1044 if (NULL == hdr_type)
1049 hdr_val = strtok (NULL, "");
1050 if (NULL == hdr_val)
1055 if (' ' == *hdr_val)
1058 /* custom logic for certain header types */
1059 new_cookie_hdr = NULL;
1060 if ( (NULL != s5r->leho) &&
1061 (0 == strcasecmp (hdr_type,
1062 MHD_HTTP_HEADER_SET_COOKIE)) )
1065 new_cookie_hdr = GNUNET_malloc (strlen (hdr_val) +
1066 strlen (s5r->domain) + 1);
1068 domain_matched = GNUNET_NO; /* make sure we match domain at most once */
1069 for (tok = strtok (hdr_val, ";"); NULL != tok; tok = strtok (NULL, ";"))
1071 if ( (0 == strncasecmp (tok, " domain", strlen (" domain"))) &&
1072 (GNUNET_NO == domain_matched) )
1074 domain_matched = GNUNET_YES;
1075 cookie_domain = tok + strlen (" domain") + 1;
1076 if (strlen (cookie_domain) < strlen (s5r->leho))
1078 delta_cdomain = strlen (s5r->leho) - strlen (cookie_domain);
1079 if (0 == strcasecmp (cookie_domain, s5r->leho + delta_cdomain))
1081 offset += sprintf (new_cookie_hdr + offset,
1087 else if (0 == strcmp (cookie_domain, s5r->leho))
1089 offset += sprintf (new_cookie_hdr + offset,
1094 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1095 _("Cookie domain `%s' supplied by server is invalid\n"),
1098 GNUNET_memcpy (new_cookie_hdr + offset, tok, strlen (tok));
1099 offset += strlen (tok);
1100 new_cookie_hdr[offset++] = ';';
1102 hdr_val = new_cookie_hdr;
1105 new_location = NULL;
1106 if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
1110 GNUNET_asprintf (&leho_host,
1111 (HTTPS_PORT != s5r->port)
1115 if (0 == strncmp (leho_host,
1117 strlen (leho_host)))
1119 GNUNET_asprintf (&new_location,
1121 (HTTPS_PORT != s5r->port)
1125 hdr_val + strlen (leho_host));
1126 hdr_val = new_location;
1128 GNUNET_free (leho_host);
1130 /* MHD does not allow certain characters in values, remove those */
1131 if (NULL != (tok = strchr (hdr_val, '\n')))
1133 if (NULL != (tok = strchr (hdr_val, '\r')))
1135 if (NULL != (tok = strchr (hdr_val, '\t')))
1137 if (0 != strlen (hdr_val)) /* Rely in MHD to set those */
1139 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1140 "Adding header %s: %s to MHD response\n",
1143 header = GNUNET_new (struct HttpResponseHeader);
1144 header->type = GNUNET_strndup (hdr_type, strlen (hdr_type));
1145 header->value = GNUNET_strndup (hdr_val, strlen (hdr_val));
1146 GNUNET_CONTAINER_DLL_insert (s5r->header_head,
1151 GNUNET_free_non_null (new_cookie_hdr);
1152 GNUNET_free_non_null (new_location);
1157 create_mhd_response_from_s5r (struct Socks5Request *s5r)
1160 double content_length;
1161 struct HttpResponseHeader *header;
1163 if (NULL != s5r->response)
1165 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1166 "Response already set!\n");
1167 return GNUNET_SYSERR;
1170 GNUNET_break (CURLE_OK ==
1171 curl_easy_getinfo (s5r->curl,
1172 CURLINFO_RESPONSE_CODE,
1174 GNUNET_break (CURLE_OK ==
1175 curl_easy_getinfo (s5r->curl,
1176 CURLINFO_CONTENT_LENGTH_DOWNLOAD,
1178 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1179 "Creating MHD response with code %d and size %d\n",
1180 (int) resp_code, (int) content_length);
1181 s5r->response_code = resp_code;
1182 s5r->response = MHD_create_response_from_callback ((-1 == content_length) ? MHD_SIZE_UNKNOWN : content_length,
1187 for (header = s5r->header_head; NULL != header; header = header->next)
1189 GNUNET_break (MHD_YES ==
1190 MHD_add_response_header (s5r->response,
1195 if (NULL != s5r->leho)
1199 GNUNET_asprintf (&cors_hdr,
1200 (HTTPS_PORT == s5r->port)
1205 GNUNET_break (MHD_YES ==
1206 MHD_add_response_header (s5r->response,
1207 MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
1209 GNUNET_free (cors_hdr);
1211 /* force connection to be closed after each request, as we
1212 do not support HTTP pipelining (yet, FIXME!) */
1213 /*GNUNET_break (MHD_YES ==
1214 MHD_add_response_header (s5r->response,
1215 MHD_HTTP_HEADER_CONNECTION,
1221 * Handle response payload data from cURL. Copies it into our `io_buf` to make
1222 * it available to MHD.
1224 * @param ptr pointer to the data
1225 * @param size number of blocks of data
1226 * @param nmemb blocksize
1227 * @param ctx our `struct Socks5Request *`
1228 * @return number of bytes handled
1231 curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx)
1233 struct Socks5Request *s5r = ctx;
1234 size_t total = size * nmemb;
1236 if (NULL == s5r->response)
1237 GNUNET_assert (GNUNET_OK == create_mhd_response_from_s5r (s5r));
1239 if ( (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state) ||
1240 (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
1242 /* we're still not done with the upload, do not yet
1243 start the download, the IO buffer is still full
1244 with upload data. */
1245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1246 "Pausing CURL download, waiting for UPLOAD to finish\n");
1247 return CURL_WRITEFUNC_PAUSE; /* not yet ready for data download */
1249 if (sizeof (s5r->io_buf) - s5r->io_len < total)
1251 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1252 "Pausing CURL download, not enough space\n");
1253 return CURL_WRITEFUNC_PAUSE; /* not enough space */
1255 GNUNET_memcpy (&s5r->io_buf[s5r->io_len],
1258 s5r->io_len += total;
1259 if (s5r->io_len == total)
1260 run_mhd_now (s5r->hd);
1266 * cURL callback for uploaded (PUT/POST) data. Copies it into our `io_buf`
1267 * to make it available to MHD.
1269 * @param buf where to write the data
1270 * @param size number of bytes per member
1271 * @param nmemb number of members available in @a buf
1272 * @param cls our `struct Socks5Request` that generated the data
1273 * @return number of bytes copied to @a buf
1276 curl_upload_cb (void *buf, size_t size, size_t nmemb, void *cls)
1278 struct Socks5Request *s5r = cls;
1279 size_t len = size * nmemb;
1282 if ( (0 == s5r->io_len) &&
1283 (SOCKS5_SOCKET_UPLOAD_DONE != s5r->state) )
1285 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1286 "Pausing CURL UPLOAD, need more data\n");
1287 return CURL_READFUNC_PAUSE;
1289 if ( (0 == s5r->io_len) &&
1290 (SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
1292 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
1293 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1294 "Completed CURL UPLOAD\n");
1295 return 0; /* upload finished, can now download */
1297 if ( (SOCKS5_SOCKET_UPLOAD_STARTED != s5r->state) &&
1298 (SOCKS5_SOCKET_UPLOAD_DONE != s5r->state) )
1301 return CURL_READFUNC_ABORT;
1303 to_copy = GNUNET_MIN (s5r->io_len,
1305 GNUNET_memcpy (buf, s5r->io_buf, to_copy);
1306 memmove (s5r->io_buf,
1307 &s5r->io_buf[to_copy],
1308 s5r->io_len - to_copy);
1309 s5r->io_len -= to_copy;
1310 if (s5r->io_len + to_copy == sizeof (s5r->io_buf))
1311 run_mhd_now (s5r->hd); /* got more space for upload now */
1316 /* ************************** main loop of cURL interaction ****************** */
1320 * Task that is run when we are ready to receive more data
1323 * @param cls closure
1326 curl_task_download (void *cls);
1330 * Ask cURL for the select() sets and schedule cURL operations.
1333 curl_download_prepare ()
1340 struct GNUNET_NETWORK_FDSet *grs;
1341 struct GNUNET_NETWORK_FDSet *gws;
1343 struct GNUNET_TIME_Relative rtime;
1345 if (NULL != curl_download_task)
1347 GNUNET_SCHEDULER_cancel (curl_download_task);
1348 curl_download_task = NULL;
1354 if (CURLM_OK != (mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max)))
1356 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1357 "%s failed at %s:%d: `%s'\n",
1358 "curl_multi_fdset", __FILE__, __LINE__,
1359 curl_multi_strerror (mret));
1363 GNUNET_break (CURLM_OK == curl_multi_timeout (curl_multi, &to));
1365 rtime = GNUNET_TIME_UNIT_FOREVER_REL;
1367 rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
1370 grs = GNUNET_NETWORK_fdset_create ();
1371 gws = GNUNET_NETWORK_fdset_create ();
1372 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1373 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1374 curl_download_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1377 &curl_task_download, curl_multi);
1378 GNUNET_NETWORK_fdset_destroy (gws);
1379 GNUNET_NETWORK_fdset_destroy (grs);
1383 curl_download_task = GNUNET_SCHEDULER_add_delayed (rtime,
1384 &curl_task_download,
1391 * Task that is run when we are ready to receive more data from curl.
1393 * @param cls closure, NULL
1396 curl_task_download (void *cls)
1400 struct CURLMsg *msg;
1402 struct Socks5Request *s5r;
1404 curl_download_task = NULL;
1408 mret = curl_multi_perform (curl_multi, &running);
1409 while (NULL != (msg = curl_multi_info_read (curl_multi, &msgnum)))
1411 GNUNET_break (CURLE_OK ==
1412 curl_easy_getinfo (msg->easy_handle,
1423 /* documentation says this is not used */
1427 switch (msg->data.result)
1430 case CURLE_GOT_NOTHING:
1431 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1432 "CURL download completed.\n");
1433 if (NULL == s5r->response)
1434 GNUNET_assert (GNUNET_OK == create_mhd_response_from_s5r (s5r));
1435 s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE;
1436 run_mhd_now (s5r->hd);
1439 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1440 "Download curl failed: %s\n",
1441 curl_easy_strerror (msg->data.result));
1442 /* FIXME: indicate error somehow? close MHD connection badly as well? */
1443 s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE;
1444 run_mhd_now (s5r->hd);
1447 if (NULL == s5r->response)
1448 s5r->response = curl_failure_response;
1451 /* documentation says this is not used */
1455 /* unexpected status code */
1460 } while (mret == CURLM_CALL_MULTI_PERFORM);
1461 if (CURLM_OK != mret)
1462 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1463 "%s failed at %s:%d: `%s'\n",
1464 "curl_multi_perform", __FILE__, __LINE__,
1465 curl_multi_strerror (mret));
1468 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1469 "Suspending cURL multi loop, no more events pending\n");
1470 return; /* nothing more in progress */
1472 curl_download_prepare ();
1476 /* ********************************* MHD response generation ******************* */
1480 * Read HTTP request header field from the request. Copies the fields
1481 * over to the 'headers' that will be given to curl. However, 'Host'
1482 * is substituted with the LEHO if present. We also change the
1483 * 'Connection' header value to "close" as the proxy does not support
1486 * @param cls our `struct Socks5Request`
1487 * @param kind value kind
1488 * @param key field key
1489 * @param value field value
1490 * @return MHD_YES to continue to iterate
1493 con_val_iter (void *cls,
1494 enum MHD_ValueKind kind,
1498 struct Socks5Request *s5r = cls;
1501 if ( (0 == strcasecmp (MHD_HTTP_HEADER_HOST, key)) &&
1502 (NULL != s5r->leho) )
1504 if (0 == strcasecmp (MHD_HTTP_HEADER_CONTENT_LENGTH, key))
1506 if (0 == strcasecmp (MHD_HTTP_HEADER_ACCEPT_ENCODING, key))
1508 GNUNET_asprintf (&hdr,
1512 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1513 "Adding HEADER `%s' to HTTP request\n",
1515 s5r->headers = curl_slist_append (s5r->headers,
1523 * Main MHD callback for handling requests.
1526 * @param con MHD connection handle
1527 * @param url the url in the request
1528 * @param meth the HTTP method used ("GET", "PUT", etc.)
1529 * @param ver the HTTP version string (i.e. "HTTP/1.1")
1530 * @param upload_data the data being uploaded (excluding HEADERS,
1531 * for a POST that fits into memory and that is encoded
1532 * with a supported encoding, the POST data will NOT be
1533 * given in upload_data and is instead available as
1534 * part of MHD_get_connection_values; very large POST
1535 * data *will* be made available incrementally in
1537 * @param upload_data_size set initially to the size of the
1538 * @a upload_data provided; the method must update this
1539 * value to the number of bytes NOT processed;
1540 * @param con_cls pointer to location where we store the 'struct Request'
1541 * @return MHD_YES if the connection was handled successfully,
1542 * MHD_NO if the socket must be closed due to a serious
1543 * error while handling the request
1546 create_response (void *cls,
1547 struct MHD_Connection *con,
1551 const char *upload_data,
1552 size_t *upload_data_size,
1555 struct Socks5Request *s5r = *con_cls;
1558 char ipstring[INET6_ADDRSTRLEN];
1559 char ipaddr[INET6_ADDRSTRLEN + 2];
1560 const struct sockaddr *sa;
1561 const struct sockaddr_in *s4;
1562 const struct sockaddr_in6 *s6;
1572 if (SOCKS5_SOCKET_WITH_MHD == s5r->state)
1574 /* first time here, initialize curl handle */
1575 sa = (const struct sockaddr *) &s5r->destination_address;
1576 switch (sa->sa_family)
1579 s4 = (const struct sockaddr_in *) &s5r->destination_address;
1580 if (NULL == inet_ntop (AF_INET,
1588 GNUNET_snprintf (ipaddr,
1592 port = ntohs (s4->sin_port);
1595 s6 = (const struct sockaddr_in6 *) &s5r->destination_address;
1596 if (NULL == inet_ntop (AF_INET6,
1604 GNUNET_snprintf (ipaddr,
1608 port = ntohs (s6->sin6_port);
1614 if (NULL == s5r->curl)
1615 s5r->curl = curl_easy_init ();
1616 if (NULL == s5r->curl)
1617 return MHD_queue_response (con,
1618 MHD_HTTP_INTERNAL_SERVER_ERROR,
1619 curl_failure_response);
1620 curl_easy_setopt (s5r->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1621 curl_easy_setopt (s5r->curl, CURLOPT_HEADERDATA, s5r);
1622 curl_easy_setopt (s5r->curl, CURLOPT_FOLLOWLOCATION, 0);
1623 curl_easy_setopt (s5r->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1624 curl_easy_setopt (s5r->curl, CURLOPT_CONNECTTIMEOUT, 600L);
1625 curl_easy_setopt (s5r->curl, CURLOPT_TIMEOUT, 600L);
1626 curl_easy_setopt (s5r->curl, CURLOPT_NOSIGNAL, 1L);
1627 curl_easy_setopt (s5r->curl, CURLOPT_HTTP_CONTENT_DECODING, 0);
1628 curl_easy_setopt (s5r->curl, CURLOPT_HTTP_TRANSFER_DECODING, 0);
1629 curl_easy_setopt (s5r->curl, CURLOPT_NOSIGNAL, 1L);
1630 curl_easy_setopt (s5r->curl, CURLOPT_PRIVATE, s5r);
1631 curl_easy_setopt (s5r->curl, CURLOPT_VERBOSE, 0);
1633 * Pre-populate cache to resolve Hostname.
1634 * This is necessary as the DNS name in the CURLOPT_URL is used
1635 * for SNI http://de.wikipedia.org/wiki/Server_Name_Indication
1637 if (NULL != s5r->leho)
1639 GNUNET_asprintf (&curl_hosts,
1644 s5r->hosts = curl_slist_append(NULL, curl_hosts);
1645 curl_easy_setopt(s5r->curl, CURLOPT_RESOLVE, s5r->hosts);
1646 GNUNET_free (curl_hosts);
1648 GNUNET_asprintf (&curlurl,
1649 (HTTPS_PORT != s5r->port)
1651 : "https://%s:%d%s",
1657 curl_easy_setopt (s5r->curl,
1660 GNUNET_free (curlurl);
1661 if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
1663 s5r->state = SOCKS5_SOCKET_UPLOAD_STARTED;
1664 curl_easy_setopt (s5r->curl, CURLOPT_UPLOAD, 1);
1665 curl_easy_setopt (s5r->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1666 curl_easy_setopt (s5r->curl, CURLOPT_WRITEDATA, s5r);
1667 curl_easy_setopt (s5r->curl, CURLOPT_READFUNCTION, &curl_upload_cb);
1668 curl_easy_setopt (s5r->curl, CURLOPT_READDATA, s5r);
1670 else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
1672 s5r->state = SOCKS5_SOCKET_UPLOAD_STARTED;
1673 curl_easy_setopt (s5r->curl, CURLOPT_POST, 1L);
1674 curl_easy_setopt (s5r->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1675 curl_easy_setopt (s5r->curl, CURLOPT_WRITEDATA, s5r);
1676 curl_easy_setopt (s5r->curl, CURLOPT_READFUNCTION, &curl_upload_cb);
1677 curl_easy_setopt (s5r->curl, CURLOPT_READDATA, s5r);
1679 else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
1681 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
1682 curl_easy_setopt (s5r->curl, CURLOPT_NOBODY, 1);
1684 else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_OPTIONS))
1686 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
1687 curl_easy_setopt (s5r->curl, CURLOPT_CUSTOMREQUEST, "OPTIONS");
1689 else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_GET))
1691 s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
1692 curl_easy_setopt (s5r->curl, CURLOPT_HTTPGET, 1);
1693 curl_easy_setopt (s5r->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1694 curl_easy_setopt (s5r->curl, CURLOPT_WRITEDATA, s5r);
1698 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1699 _("Unsupported HTTP method `%s'\n"),
1701 curl_easy_cleanup (s5r->curl);
1706 if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_0))
1708 curl_easy_setopt (s5r->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
1710 else if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_1))
1712 curl_easy_setopt (s5r->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
1716 curl_easy_setopt (s5r->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE);
1719 if (HTTPS_PORT == s5r->port)
1721 curl_easy_setopt (s5r->curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
1722 if (NULL != s5r->dane_data)
1723 curl_easy_setopt (s5r->curl, CURLOPT_SSL_VERIFYPEER, 0L);
1725 curl_easy_setopt (s5r->curl, CURLOPT_SSL_VERIFYPEER, 1L);
1726 /* Disable cURL checking the hostname, as we will check ourselves
1727 as only we have the domain name or the LEHO or the DANE record */
1728 curl_easy_setopt (s5r->curl, CURLOPT_SSL_VERIFYHOST, 0L);
1732 curl_easy_setopt (s5r->curl, CURLOPT_USE_SSL, CURLUSESSL_NONE);
1735 if (CURLM_OK != curl_multi_add_handle (curl_multi, s5r->curl))
1738 curl_easy_cleanup (s5r->curl);
1742 MHD_get_connection_values (con,
1744 &con_val_iter, s5r);
1745 curl_easy_setopt (s5r->curl, CURLOPT_HTTPHEADER, s5r->headers);
1746 curl_download_prepare ();
1750 /* continuing to process request */
1751 if (0 != *upload_data_size)
1754 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1755 "Processing %u bytes UPLOAD\n",
1756 (unsigned int) *upload_data_size);
1758 /* FIXME: This must be set or a header with Transfer-Encoding: chunked. Else
1759 * upload callback is not called!
1761 curl_easy_setopt (s5r->curl, CURLOPT_POSTFIELDSIZE, *upload_data_size);
1763 left = GNUNET_MIN (*upload_data_size,
1764 sizeof (s5r->io_buf) - s5r->io_len);
1765 GNUNET_memcpy (&s5r->io_buf[s5r->io_len],
1768 s5r->io_len += left;
1769 *upload_data_size -= left;
1770 GNUNET_assert (NULL != s5r->curl);
1771 curl_easy_pause (s5r->curl, CURLPAUSE_CONT);
1774 if (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state)
1776 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1777 "Finished processing UPLOAD\n");
1778 s5r->state = SOCKS5_SOCKET_UPLOAD_DONE;
1780 if (NULL == s5r->response)
1782 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1783 "Queueing response with MHD\n");
1784 run_mhd_now (s5r->hd);
1785 return MHD_queue_response (con,
1791 /* ******************** MHD HTTP setup and event loop ******************** */
1795 * Function called when MHD decides that we are done with a request.
1798 * @param connection connection handle
1799 * @param con_cls value as set by the last call to
1800 * the MHD_AccessHandlerCallback, should be our `struct Socks5Request *`
1801 * @param toe reason for request termination (ignored)
1804 mhd_completed_cb (void *cls,
1805 struct MHD_Connection *connection,
1807 enum MHD_RequestTerminationCode toe)
1809 struct Socks5Request *s5r = *con_cls;
1810 struct HttpResponseHeader *header;
1814 if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
1815 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1816 "MHD encountered error handling request: %d\n",
1818 if (NULL != s5r->curl)
1820 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1821 "Resetting cURL handle\n");
1822 curl_multi_remove_handle (curl_multi, s5r->curl);
1823 curl_slist_free_all (s5r->headers);
1824 s5r->headers = NULL;
1825 curl_easy_reset (s5r->curl);
1830 if ( (NULL != s5r->response) &&
1831 (curl_failure_response != s5r->response) )
1832 MHD_destroy_response (s5r->response);
1833 for (header = s5r->header_head; header != NULL; header = s5r->header_head)
1835 GNUNET_CONTAINER_DLL_remove (s5r->header_head,
1838 GNUNET_free (header->type);
1839 GNUNET_free (header->value);
1840 GNUNET_free (header);
1842 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Finished request for %s\n", s5r->url);
1843 GNUNET_free (s5r->url);
1844 s5r->state = SOCKS5_SOCKET_WITH_MHD;
1846 s5r->response = NULL;
1852 * Function called when MHD connection is opened or closed.
1855 * @param connection connection handle
1856 * @param con_cls value as set by the last call to
1857 * the MHD_AccessHandlerCallback, should be our `struct Socks5Request *`
1858 * @param toe connection notification type
1861 mhd_connection_cb (void *cls,
1862 struct MHD_Connection *connection,
1864 enum MHD_ConnectionNotificationCode cnc)
1866 struct Socks5Request *s5r;
1867 const union MHD_ConnectionInfo *ci;
1872 case MHD_CONNECTION_NOTIFY_STARTED:
1873 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connection started...\n");
1874 ci = MHD_get_connection_info (connection,
1875 MHD_CONNECTION_INFO_CONNECTION_FD);
1881 sock = ci->connect_fd;
1882 for (s5r = s5r_head; NULL != s5r; s5r = s5r->next)
1884 if (GNUNET_NETWORK_get_fd (s5r->sock) == sock)
1886 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1887 "Context set...\n");
1888 s5r->ssl_checked = GNUNET_NO;
1894 case MHD_CONNECTION_NOTIFY_CLOSED:
1895 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1896 "Connection closed... cleaning up\n");
1900 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1901 "Connection stale!\n");
1905 curl_download_prepare ();
1914 * Function called when MHD first processes an incoming connection.
1915 * Gives us the respective URI information.
1917 * We use this to associate the `struct MHD_Connection` with our
1918 * internal `struct Socks5Request` data structure (by checking
1919 * for matching sockets).
1921 * @param cls the HTTP server handle (a `struct MhdHttpList`)
1922 * @param url the URL that is being requested
1923 * @param connection MHD connection object for the request
1924 * @return the `struct Socks5Request` that this @a connection is for
1927 mhd_log_callback (void *cls,
1929 struct MHD_Connection *connection)
1931 struct Socks5Request *s5r;
1932 const union MHD_ConnectionInfo *ci;
1934 ci = MHD_get_connection_info (connection,
1935 MHD_CONNECTION_INFO_SOCKET_CONTEXT);
1936 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Processing %s\n", url);
1942 s5r = ci->socket_context;
1943 if (NULL != s5r->url)
1948 s5r->url = GNUNET_strdup (url);
1949 if (NULL != s5r->timeout_task)
1950 GNUNET_SCHEDULER_cancel (s5r->timeout_task);
1951 s5r->timeout_task = NULL;
1952 GNUNET_assert (s5r->state == SOCKS5_SOCKET_WITH_MHD);
1958 * Kill the given MHD daemon.
1960 * @param hd daemon to stop
1963 kill_httpd (struct MhdHttpList *hd)
1965 GNUNET_CONTAINER_DLL_remove (mhd_httpd_head,
1968 GNUNET_free_non_null (hd->domain);
1969 MHD_stop_daemon (hd->daemon);
1970 if (NULL != hd->httpd_task)
1972 GNUNET_SCHEDULER_cancel (hd->httpd_task);
1973 hd->httpd_task = NULL;
1975 GNUNET_free_non_null (hd->proxy_cert);
1983 * Task run whenever HTTP server is idle for too long. Kill it.
1985 * @param cls the `struct MhdHttpList *`
1988 kill_httpd_task (void *cls)
1990 struct MhdHttpList *hd = cls;
1992 hd->httpd_task = NULL;
1998 * Task run whenever HTTP server operations are pending.
2000 * @param cls the `struct MhdHttpList *` of the daemon that is being run
2003 do_httpd (void *cls);
2007 * Schedule MHD. This function should be called initially when an
2008 * MHD is first getting its client socket, and will then automatically
2009 * always be called later whenever there is work to be done.
2011 * @param hd the daemon to schedule
2014 schedule_httpd (struct MhdHttpList *hd)
2019 struct GNUNET_NETWORK_FDSet *wrs;
2020 struct GNUNET_NETWORK_FDSet *wws;
2023 MHD_UNSIGNED_LONG_LONG timeout;
2024 struct GNUNET_TIME_Relative tv;
2030 if (MHD_YES != MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max))
2035 haveto = MHD_get_timeout (hd->daemon, &timeout);
2036 if (MHD_YES == haveto)
2037 tv.rel_value_us = (uint64_t) timeout * 1000LL;
2039 tv = GNUNET_TIME_UNIT_FOREVER_REL;
2042 wrs = GNUNET_NETWORK_fdset_create ();
2043 wws = GNUNET_NETWORK_fdset_create ();
2044 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
2045 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
2052 if (NULL != hd->httpd_task)
2053 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2054 if ( (MHD_YES != haveto) &&
2058 /* daemon is idle, kill after timeout */
2059 hd->httpd_task = GNUNET_SCHEDULER_add_delayed (MHD_CACHE_TIMEOUT,
2066 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
2071 GNUNET_NETWORK_fdset_destroy (wrs);
2073 GNUNET_NETWORK_fdset_destroy (wws);
2078 * Task run whenever HTTP server operations are pending.
2080 * @param cls the `struct MhdHttpList` of the daemon that is being run
2083 do_httpd (void *cls)
2085 struct MhdHttpList *hd = cls;
2087 hd->httpd_task = NULL;
2088 MHD_run (hd->daemon);
2089 schedule_httpd (hd);
2094 * Run MHD now, we have extra data ready for the callback.
2096 * @param hd the daemon to run now.
2099 run_mhd_now (struct MhdHttpList *hd)
2103 GNUNET_SCHEDULER_cancel (hd->httpd_task);
2104 hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd,
2110 * Read file in filename
2112 * @param filename file to read
2113 * @param size pointer where filesize is stored
2114 * @return NULL on error
2117 load_file (const char* filename,
2124 GNUNET_DISK_file_size (filename, &fsize,
2125 GNUNET_YES, GNUNET_YES))
2127 if (fsize > MAX_PEM_SIZE)
2129 *size = (unsigned int) fsize;
2130 buffer = GNUNET_malloc (*size);
2131 if (fsize != GNUNET_DISK_fn_read (filename, buffer, (size_t) fsize))
2133 GNUNET_free (buffer);
2141 * Load PEM key from file
2143 * @param key where to store the data
2144 * @param keyfile path to the PEM file
2145 * @return #GNUNET_OK on success
2148 load_key_from_file (gnutls_x509_privkey_t key,
2149 const char* keyfile)
2151 gnutls_datum_t key_data;
2154 key_data.data = load_file (keyfile, &key_data.size);
2155 if (NULL == key_data.data)
2156 return GNUNET_SYSERR;
2157 ret = gnutls_x509_privkey_import (key, &key_data,
2158 GNUTLS_X509_FMT_PEM);
2159 if (GNUTLS_E_SUCCESS != ret)
2161 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2162 _("Unable to import private key from file `%s'\n"),
2165 GNUNET_free_non_null (key_data.data);
2166 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2171 * Load cert from file
2173 * @param crt struct to store data in
2174 * @param certfile path to pem file
2175 * @return #GNUNET_OK on success
2178 load_cert_from_file (gnutls_x509_crt_t crt,
2179 const char* certfile)
2181 gnutls_datum_t cert_data;
2184 cert_data.data = load_file (certfile, &cert_data.size);
2185 if (NULL == cert_data.data)
2186 return GNUNET_SYSERR;
2187 ret = gnutls_x509_crt_import (crt, &cert_data,
2188 GNUTLS_X509_FMT_PEM);
2189 if (GNUTLS_E_SUCCESS != ret)
2191 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2192 _("Unable to import certificate %s\n"), certfile);
2194 GNUNET_free_non_null (cert_data.data);
2195 return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2200 * Generate new certificate for specific name
2202 * @param name the subject name to generate a cert for
2203 * @return a struct holding the PEM data, NULL on error
2205 static struct ProxyGNSCertificate *
2206 generate_gns_certificate (const char *name)
2208 unsigned int serial;
2209 size_t key_buf_size;
2210 size_t cert_buf_size;
2211 gnutls_x509_crt_t request;
2214 struct ProxyGNSCertificate *pgc;
2216 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2217 "Generating TLS/SSL certificate for `%s'\n",
2219 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_init (&request));
2220 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
2221 pgc = GNUNET_new (struct ProxyGNSCertificate);
2222 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
2224 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
2225 0, "GNU Name System", 4);
2226 gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
2227 0, name, strlen (name));
2228 GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_version (request, 3));
2229 gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
2230 gnutls_x509_crt_set_serial (request,
2233 etime = time (NULL);
2234 tm_data = localtime (&etime);
2235 gnutls_x509_crt_set_activation_time (request,
2238 etime = mktime (tm_data);
2239 gnutls_x509_crt_set_expiration_time (request,
2241 gnutls_x509_crt_sign (request,
2244 key_buf_size = sizeof (pgc->key);
2245 cert_buf_size = sizeof (pgc->cert);
2246 gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
2247 pgc->cert, &cert_buf_size);
2248 gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
2249 pgc->key, &key_buf_size);
2250 gnutls_x509_crt_deinit (request);
2256 * Function called by MHD with errors, suppresses them all.
2258 * @param cls closure
2259 * @param fm format string (`printf()`-style)
2260 * @param ap arguments to @a fm
2263 mhd_error_log_callback (void *cls,
2272 * Lookup (or create) an SSL MHD instance for a particular domain.
2274 * @param domain the domain the SSL daemon has to serve
2275 * @return NULL on error
2277 static struct MhdHttpList *
2278 lookup_ssl_httpd (const char* domain)
2280 struct MhdHttpList *hd;
2281 struct ProxyGNSCertificate *pgc;
2288 for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
2289 if ( (NULL != hd->domain) &&
2290 (0 == strcmp (hd->domain, domain)) )
2292 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2293 "Starting fresh MHD HTTPS instance for domain `%s'\n",
2295 pgc = generate_gns_certificate (domain);
2296 hd = GNUNET_new (struct MhdHttpList);
2297 hd->is_ssl = GNUNET_YES;
2298 hd->domain = GNUNET_strdup (domain);
2299 hd->proxy_cert = pgc;
2300 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_NO_LISTEN_SOCKET,
2303 &create_response, hd,
2304 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2305 MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL,
2306 MHD_OPTION_NOTIFY_CONNECTION, &mhd_connection_cb, NULL,
2307 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
2308 MHD_OPTION_EXTERNAL_LOGGER, &mhd_error_log_callback, NULL,
2309 MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2310 MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2312 if (NULL == hd->daemon)
2318 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head,
2326 * Task run when a Socks5Request somehow fails to be associated with
2327 * an MHD connection (i.e. because the client never speaks HTTP after
2328 * the SOCKS5 handshake). Clean up.
2330 * @param cls the `struct Socks5Request *`
2333 timeout_s5r_handshake (void *cls)
2335 struct Socks5Request *s5r = cls;
2337 s5r->timeout_task = NULL;
2343 * We're done with the Socks5 protocol, now we need to pass the
2344 * connection data through to the final destination, either
2345 * direct (if the protocol might not be HTTP), or via MHD
2346 * (if the port looks like it should be HTTP).
2348 * @param s5r socks request that has reached the final stage
2351 setup_data_transfer (struct Socks5Request *s5r)
2353 struct MhdHttpList *hd;
2355 const struct sockaddr *addr;
2361 hd = lookup_ssl_httpd (s5r->domain);
2364 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2365 _("Failed to start HTTPS server for `%s'\n"),
2373 GNUNET_assert (NULL != httpd);
2377 fd = GNUNET_NETWORK_get_fd (s5r->sock);
2378 addr = GNUNET_NETWORK_get_addr (s5r->sock);
2379 len = GNUNET_NETWORK_get_addrlen (s5r->sock);
2380 s5r->state = SOCKS5_SOCKET_WITH_MHD;
2381 if (MHD_YES != MHD_add_connection (hd->daemon, fd, addr, len))
2383 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2384 _("Failed to pass client to MHD\n"));
2389 schedule_httpd (hd);
2390 s5r->timeout_task = GNUNET_SCHEDULER_add_delayed (HTTP_HANDSHAKE_TIMEOUT,
2391 &timeout_s5r_handshake,
2396 /* ********************* SOCKS handling ************************* */
2400 * Write data from buffer to socks5 client, then continue with state machine.
2402 * @param cls the closure with the `struct Socks5Request`
2405 do_write (void *cls)
2407 struct Socks5Request *s5r = cls;
2411 len = GNUNET_NETWORK_socket_send (s5r->sock,
2416 /* write error: connection closed, shutdown, etc.; just clean up */
2422 s5r->wbuf_len - len);
2423 s5r->wbuf_len -= len;
2424 if (s5r->wbuf_len > 0)
2426 /* not done writing */
2428 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2434 /* we're done writing, continue with state machine! */
2441 case SOCKS5_REQUEST:
2442 GNUNET_assert (NULL != s5r->rtask);
2444 case SOCKS5_DATA_TRANSFER:
2445 setup_data_transfer (s5r);
2447 case SOCKS5_WRITE_THEN_CLEANUP:
2458 * Return a server response message indicating a failure to the client.
2460 * @param s5r request to return failure code for
2461 * @param sc status code to return
2464 signal_socks_failure (struct Socks5Request *s5r,
2465 enum Socks5StatusCode sc)
2467 struct Socks5ServerResponseMessage *s_resp;
2469 s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf[s5r->wbuf_len];
2470 memset (s_resp, 0, sizeof (struct Socks5ServerResponseMessage));
2471 s_resp->version = SOCKS_VERSION_5;
2473 s5r->state = SOCKS5_WRITE_THEN_CLEANUP;
2474 if (NULL != s5r->wtask)
2476 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2483 * Return a server response message indicating success.
2485 * @param s5r request to return success status message for
2488 signal_socks_success (struct Socks5Request *s5r)
2490 struct Socks5ServerResponseMessage *s_resp;
2492 s_resp = (struct Socks5ServerResponseMessage *) &s5r->wbuf[s5r->wbuf_len];
2493 s_resp->version = SOCKS_VERSION_5;
2494 s_resp->reply = SOCKS5_STATUS_REQUEST_GRANTED;
2495 s_resp->reserved = 0;
2496 s_resp->addr_type = SOCKS5_AT_IPV4;
2497 /* zero out IPv4 address and port */
2500 sizeof (struct in_addr) + sizeof (uint16_t));
2501 s5r->wbuf_len += sizeof (struct Socks5ServerResponseMessage) +
2502 sizeof (struct in_addr) + sizeof (uint16_t);
2503 if (NULL == s5r->wtask)
2505 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2512 * Process GNS results for target domain.
2514 * @param cls the `struct Socks5Request *`
2515 * @param rd_count number of records returned
2516 * @param rd record data
2519 handle_gns_result (void *cls,
2521 const struct GNUNET_GNSRECORD_Data *rd)
2523 struct Socks5Request *s5r = cls;
2525 const struct GNUNET_GNSRECORD_Data *r;
2528 s5r->gns_lookup = NULL;
2530 for (i=0;i<rd_count;i++)
2533 switch (r->record_type)
2535 case GNUNET_DNSPARSER_TYPE_A:
2537 struct sockaddr_in *in;
2539 if (sizeof (struct in_addr) != r->data_size)
2541 GNUNET_break_op (0);
2544 if (GNUNET_YES == got_ip)
2547 GNUNET_NETWORK_test_pf (PF_INET))
2549 got_ip = GNUNET_YES;
2550 in = (struct sockaddr_in *) &s5r->destination_address;
2551 in->sin_family = AF_INET;
2552 GNUNET_memcpy (&in->sin_addr,
2555 in->sin_port = htons (s5r->port);
2556 #if HAVE_SOCKADDR_IN_SIN_LEN
2557 in->sin_len = sizeof (*in);
2561 case GNUNET_DNSPARSER_TYPE_AAAA:
2563 struct sockaddr_in6 *in;
2565 if (sizeof (struct in6_addr) != r->data_size)
2567 GNUNET_break_op (0);
2570 if (GNUNET_YES == got_ip)
2573 GNUNET_NETWORK_test_pf (PF_INET))
2575 /* FIXME: allow user to disable IPv6 per configuration option... */
2576 got_ip = GNUNET_YES;
2577 in = (struct sockaddr_in6 *) &s5r->destination_address;
2578 in->sin6_family = AF_INET6;
2579 GNUNET_memcpy (&in->sin6_addr,
2582 in->sin6_port = htons (s5r->port);
2583 #if HAVE_SOCKADDR_IN_SIN_LEN
2584 in->sin6_len = sizeof (*in);
2588 case GNUNET_GNSRECORD_TYPE_VPN:
2589 GNUNET_break (0); /* should have been translated within GNS */
2591 case GNUNET_GNSRECORD_TYPE_LEHO:
2592 GNUNET_free_non_null (s5r->leho);
2593 s5r->leho = GNUNET_strndup (r->data,
2596 case GNUNET_GNSRECORD_TYPE_BOX:
2598 const struct GNUNET_GNSRECORD_BoxRecord *box;
2600 if (r->data_size < sizeof (struct GNUNET_GNSRECORD_BoxRecord))
2602 GNUNET_break_op (0);
2606 if ( (ntohl (box->record_type) != GNUNET_DNSPARSER_TYPE_TLSA) ||
2607 (ntohs (box->protocol) != IPPROTO_TCP) ||
2608 (ntohs (box->service) != s5r->port) )
2609 break; /* BOX record does not apply */
2610 GNUNET_free_non_null (s5r->dane_data);
2611 s5r->dane_data_len = r->data_size - sizeof (struct GNUNET_GNSRECORD_BoxRecord);
2612 s5r->dane_data = GNUNET_malloc (s5r->dane_data_len);
2613 GNUNET_memcpy (s5r->dane_data,
2615 s5r->dane_data_len);
2623 if (GNUNET_YES != got_ip)
2625 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2626 "Name resolution failed to yield useful IP address.\n");
2627 signal_socks_failure (s5r,
2628 SOCKS5_STATUS_GENERAL_FAILURE);
2631 s5r->state = SOCKS5_DATA_TRANSFER;
2632 signal_socks_success (s5r);
2637 * Remove the first @a len bytes from the beginning of the read buffer.
2639 * @param s5r the handle clear the read buffer for
2640 * @param len number of bytes in read buffer to advance
2643 clear_from_s5r_rbuf (struct Socks5Request *s5r,
2646 GNUNET_assert (len <= s5r->rbuf_len);
2649 s5r->rbuf_len - len);
2650 s5r->rbuf_len -= len;
2655 * Read data from incoming Socks5 connection
2657 * @param cls the closure with the `struct Socks5Request`
2660 do_s5r_read (void *cls)
2662 struct Socks5Request *s5r = cls;
2663 const struct Socks5ClientHelloMessage *c_hello;
2664 struct Socks5ServerHelloMessage *s_hello;
2665 const struct Socks5ClientRequestMessage *c_req;
2668 const struct GNUNET_SCHEDULER_TaskContext *tc;
2671 tc = GNUNET_SCHEDULER_get_task_context ();
2672 if ( (NULL != tc->read_ready) &&
2673 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) )
2675 rlen = GNUNET_NETWORK_socket_recv (s5r->sock,
2676 &s5r->rbuf[s5r->rbuf_len],
2677 sizeof (s5r->rbuf) - s5r->rbuf_len);
2680 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2681 "socks5 client disconnected.\n");
2685 s5r->rbuf_len += rlen;
2687 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2690 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2691 "Processing %zu bytes of socks data in state %d\n",
2697 c_hello = (const struct Socks5ClientHelloMessage*) &s5r->rbuf;
2698 if ( (s5r->rbuf_len < sizeof (struct Socks5ClientHelloMessage)) ||
2699 (s5r->rbuf_len < sizeof (struct Socks5ClientHelloMessage) + c_hello->num_auth_methods) )
2700 return; /* need more data */
2701 if (SOCKS_VERSION_5 != c_hello->version)
2703 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2704 _("Unsupported socks version %d\n"),
2705 (int) c_hello->version);
2709 clear_from_s5r_rbuf (s5r,
2710 sizeof (struct Socks5ClientHelloMessage) + c_hello->num_auth_methods);
2711 GNUNET_assert (0 == s5r->wbuf_len);
2712 s_hello = (struct Socks5ServerHelloMessage *) &s5r->wbuf;
2713 s5r->wbuf_len = sizeof (struct Socks5ServerHelloMessage);
2714 s_hello->version = SOCKS_VERSION_5;
2715 s_hello->auth_method = SOCKS_AUTH_NONE;
2716 GNUNET_assert (NULL == s5r->wtask);
2717 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2720 s5r->state = SOCKS5_REQUEST;
2722 case SOCKS5_REQUEST:
2723 c_req = (const struct Socks5ClientRequestMessage *) &s5r->rbuf;
2724 if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage))
2726 switch (c_req->command)
2728 case SOCKS5_CMD_TCP_STREAM:
2732 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2733 _("Unsupported socks command %d\n"),
2734 (int) c_req->command);
2735 signal_socks_failure (s5r,
2736 SOCKS5_STATUS_COMMAND_NOT_SUPPORTED);
2739 switch (c_req->addr_type)
2741 case SOCKS5_AT_IPV4:
2743 const struct in_addr *v4 = (const struct in_addr *) &c_req[1];
2744 const uint16_t *port = (const uint16_t *) &v4[1];
2745 struct sockaddr_in *in;
2747 s5r->port = ntohs (*port);
2748 if (HTTPS_PORT == s5r->port)
2750 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2751 _("SSL connection to plain IPv4 address requested\n"));
2752 signal_socks_failure (s5r,
2753 SOCKS5_STATUS_CONNECTION_NOT_ALLOWED_BY_RULE);
2756 alen = sizeof (struct in_addr);
2757 if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
2758 alen + sizeof (uint16_t))
2759 return; /* need more data */
2760 in = (struct sockaddr_in *) &s5r->destination_address;
2761 in->sin_family = AF_INET;
2763 in->sin_port = *port;
2764 #if HAVE_SOCKADDR_IN_SIN_LEN
2765 in->sin_len = sizeof (*in);
2767 s5r->state = SOCKS5_DATA_TRANSFER;
2770 case SOCKS5_AT_IPV6:
2772 const struct in6_addr *v6 = (const struct in6_addr *) &c_req[1];
2773 const uint16_t *port = (const uint16_t *) &v6[1];
2774 struct sockaddr_in6 *in;
2776 s5r->port = ntohs (*port);
2777 if (HTTPS_PORT == s5r->port)
2779 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2780 _("SSL connection to plain IPv4 address requested\n"));
2781 signal_socks_failure (s5r,
2782 SOCKS5_STATUS_CONNECTION_NOT_ALLOWED_BY_RULE);
2785 alen = sizeof (struct in6_addr);
2786 if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
2787 alen + sizeof (uint16_t))
2788 return; /* need more data */
2789 in = (struct sockaddr_in6 *) &s5r->destination_address;
2790 in->sin6_family = AF_INET6;
2791 in->sin6_addr = *v6;
2792 in->sin6_port = *port;
2793 #if HAVE_SOCKADDR_IN_SIN_LEN
2794 in->sin6_len = sizeof (*in);
2796 s5r->state = SOCKS5_DATA_TRANSFER;
2799 case SOCKS5_AT_DOMAINNAME:
2801 const uint8_t *dom_len;
2802 const char *dom_name;
2803 const uint16_t *port;
2805 dom_len = (const uint8_t *) &c_req[1];
2806 alen = *dom_len + 1;
2807 if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
2808 alen + sizeof (uint16_t))
2809 return; /* need more data */
2810 dom_name = (const char *) &dom_len[1];
2811 port = (const uint16_t*) &dom_name[*dom_len];
2812 s5r->domain = GNUNET_strndup (dom_name, *dom_len);
2813 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2814 "Requested connection is to %s:%d\n",
2817 s5r->state = SOCKS5_RESOLVING;
2818 s5r->port = ntohs (*port);
2819 s5r->gns_lookup = GNUNET_GNS_lookup (gns_handle,
2822 GNUNET_DNSPARSER_TYPE_A,
2823 GNUNET_NO /* only cached */,
2829 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2830 _("Unsupported socks address type %d\n"),
2831 (int) c_req->addr_type);
2832 signal_socks_failure (s5r,
2833 SOCKS5_STATUS_ADDRESS_TYPE_NOT_SUPPORTED);
2836 clear_from_s5r_rbuf (s5r,
2837 sizeof (struct Socks5ClientRequestMessage) +
2838 alen + sizeof (uint16_t));
2839 if (0 != s5r->rbuf_len)
2841 /* read more bytes than healthy, why did the client send more!? */
2842 GNUNET_break_op (0);
2843 signal_socks_failure (s5r,
2844 SOCKS5_STATUS_GENERAL_FAILURE);
2847 if (SOCKS5_DATA_TRANSFER == s5r->state)
2849 /* if we are not waiting for GNS resolution, signal success */
2850 signal_socks_success (s5r);
2852 /* We are done reading right now */
2853 GNUNET_SCHEDULER_cancel (s5r->rtask);
2856 case SOCKS5_RESOLVING:
2859 case SOCKS5_DATA_TRANSFER:
2870 * Accept new incoming connections
2872 * @param cls the closure with the lsock4 or lsock6
2873 * @param tc the scheduler context
2876 do_accept (void *cls)
2878 struct GNUNET_NETWORK_Handle *lsock = cls;
2879 struct GNUNET_NETWORK_Handle *s;
2880 struct Socks5Request *s5r;
2882 if (lsock == lsock4)
2886 if (lsock == lsock4)
2887 ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2891 ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2894 s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
2897 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
2900 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2901 "Got an inbound connection, waiting for data\n");
2902 s5r = GNUNET_new (struct Socks5Request);
2903 GNUNET_CONTAINER_DLL_insert (s5r_head,
2907 s5r->state = SOCKS5_INIT;
2908 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2914 /* ******************* General / main code ********************* */
2918 * Task run on shutdown
2920 * @param cls closure
2923 do_shutdown (void *cls)
2925 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2926 "Shutting down...\n");
2927 while (NULL != mhd_httpd_head)
2928 kill_httpd (mhd_httpd_head);
2929 while (NULL != s5r_head)
2930 cleanup_s5r (s5r_head);
2933 GNUNET_NETWORK_socket_close (lsock4);
2938 GNUNET_NETWORK_socket_close (lsock6);
2943 GNUNET_IDENTITY_cancel (id_op);
2946 if (NULL != identity)
2948 GNUNET_IDENTITY_disconnect (identity);
2951 if (NULL != curl_multi)
2953 curl_multi_cleanup (curl_multi);
2956 if (NULL != gns_handle)
2958 GNUNET_GNS_disconnect (gns_handle);
2961 if (NULL != curl_download_task)
2963 GNUNET_SCHEDULER_cancel (curl_download_task);
2964 curl_download_task = NULL;
2968 GNUNET_SCHEDULER_cancel (ltask4);
2973 GNUNET_SCHEDULER_cancel (ltask6);
2976 gnutls_x509_crt_deinit (proxy_ca.cert);
2977 gnutls_x509_privkey_deinit (proxy_ca.key);
2978 gnutls_global_deinit ();
2983 * Create an IPv4 listen socket bound to our port.
2985 * @return NULL on error
2987 static struct GNUNET_NETWORK_Handle *
2990 struct GNUNET_NETWORK_Handle *ls;
2991 struct sockaddr_in sa4;
2994 memset (&sa4, 0, sizeof (sa4));
2995 sa4.sin_family = AF_INET;
2996 sa4.sin_port = htons (port);
2997 #if HAVE_SOCKADDR_IN_SIN_LEN
2998 sa4.sin_len = sizeof (sa4);
3000 ls = GNUNET_NETWORK_socket_create (AF_INET,
3006 GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa4,
3010 GNUNET_NETWORK_socket_close (ls);
3019 * Create an IPv6 listen socket bound to our port.
3021 * @return NULL on error
3023 static struct GNUNET_NETWORK_Handle *
3026 struct GNUNET_NETWORK_Handle *ls;
3027 struct sockaddr_in6 sa6;
3030 memset (&sa6, 0, sizeof (sa6));
3031 sa6.sin6_family = AF_INET6;
3032 sa6.sin6_port = htons (port);
3033 #if HAVE_SOCKADDR_IN_SIN_LEN
3034 sa6.sin6_len = sizeof (sa6);
3036 ls = GNUNET_NETWORK_socket_create (AF_INET6,
3042 GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa6,
3046 GNUNET_NETWORK_socket_close (ls);
3055 * Continue initialization after we have our zone information.
3060 struct MhdHttpList *hd;
3062 /* Open listen socket for socks proxy */
3063 lsock6 = bind_v6 ();
3065 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
3068 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock6, 5))
3070 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
3071 GNUNET_NETWORK_socket_close (lsock6);
3076 ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3077 lsock6, &do_accept, lsock6);
3080 lsock4 = bind_v4 ();
3082 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
3085 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock4, 5))
3087 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
3088 GNUNET_NETWORK_socket_close (lsock4);
3093 ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3094 lsock4, &do_accept, lsock4);
3097 if ( (NULL == lsock4) &&
3100 GNUNET_SCHEDULER_shutdown ();
3103 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
3105 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3106 "cURL global init failed!\n");
3107 GNUNET_SCHEDULER_shutdown ();
3110 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3111 "Proxy listens on port %llu\n",
3114 /* start MHD daemon for HTTP */
3115 hd = GNUNET_new (struct MhdHttpList);
3116 hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
3119 &create_response, hd,
3120 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3121 MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL,
3122 MHD_OPTION_NOTIFY_CONNECTION, &mhd_connection_cb, NULL,
3123 MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3125 if (NULL == hd->daemon)
3128 GNUNET_SCHEDULER_shutdown ();
3132 GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
3137 * Method called to inform about the egos of the master zone of this peer.
3139 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
3140 * this function is only called ONCE, and 'NULL' being passed in
3141 * @a ego does indicate an error (i.e. name is taken or no default
3142 * value is known). If @a ego is non-NULL and if '*ctx'
3143 * is set in those callbacks, the value WILL be passed to a subsequent
3144 * call to the identity callback of #GNUNET_IDENTITY_connect (if
3145 * that one was not NULL).
3147 * @param cls closure, NULL
3148 * @param ego ego handle
3149 * @param ctx context for application to store data for this ego
3150 * (during the lifetime of this process, initially NULL)
3151 * @param name name assigned by the user for this ego,
3152 * NULL if the user just deleted the ego and it
3153 * must thus no longer be used
3156 identity_master_cb (void *cls,
3157 struct GNUNET_IDENTITY_Ego *ego,
3164 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3165 _("No ego configured for `%s`\n"),
3167 GNUNET_SCHEDULER_shutdown ();
3170 GNUNET_IDENTITY_ego_get_public_key (ego,
3177 * Main function that will be run
3179 * @param cls closure
3180 * @param args remaining command-line arguments
3181 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3182 * @param c configuration
3187 const char *cfgfile,
3188 const struct GNUNET_CONFIGURATION_Handle *c)
3190 char* cafile_cfg = NULL;
3195 if (NULL == (curl_multi = curl_multi_init ()))
3197 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3198 "Failed to create cURL multi handle!\n");
3201 cafile = cafile_opt;
3204 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3208 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
3213 cafile = cafile_cfg;
3215 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3216 "Using %s as CA\n", cafile);
3218 gnutls_global_init ();
3219 gnutls_x509_crt_init (&proxy_ca.cert);
3220 gnutls_x509_privkey_init (&proxy_ca.key);
3222 if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
3223 (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
3225 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3226 _("Failed to load SSL/TLS key and certificate from `%s'\n"),
3228 gnutls_x509_crt_deinit (proxy_ca.cert);
3229 gnutls_x509_privkey_deinit (proxy_ca.key);
3230 gnutls_global_deinit ();
3231 GNUNET_free_non_null (cafile_cfg);
3234 GNUNET_free_non_null (cafile_cfg);
3235 if (NULL == (gns_handle = GNUNET_GNS_connect (cfg)))
3237 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3238 "Unable to connect to GNS!\n");
3239 gnutls_x509_crt_deinit (proxy_ca.cert);
3240 gnutls_x509_privkey_deinit (proxy_ca.key);
3241 gnutls_global_deinit ();
3244 identity = GNUNET_IDENTITY_connect (cfg,
3246 id_op = GNUNET_IDENTITY_get (identity,
3248 &identity_master_cb,
3250 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
3255 * The main function for gnunet-gns-proxy.
3257 * @param argc number of arguments from the command line
3258 * @param argv command line arguments
3259 * @return 0 ok, 1 on error
3262 main (int argc, char *const *argv)
3264 struct GNUNET_GETOPT_CommandLineOption options[] = {
3266 GNUNET_GETOPT_option_ulong ('p',
3269 gettext_noop ("listen on specified port (default: 7777)"),
3272 GNUNET_GETOPT_option_string ('a',
3275 gettext_noop ("pem file to use as CA"),
3278 GNUNET_GETOPT_OPTION_END
3280 static const char* page =
3281 "<html><head><title>gnunet-gns-proxy</title>"
3282 "</head><body>cURL fail</body></html>";
3285 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv,
3288 GNUNET_log_setup ("gnunet-gns-proxy",
3291 curl_failure_response
3292 = MHD_create_response_from_buffer (strlen (page),
3294 MHD_RESPMEM_PERSISTENT);
3298 GNUNET_PROGRAM_run (argc, argv,
3300 _("GNUnet GNS proxy"),
3302 &run, NULL)) ? 0 : 1;
3303 MHD_destroy_response (curl_failure_response);
3304 GNUNET_free_non_null ((char *) argv);
3308 /* end of gnunet-gns-proxy.c */