/*
This file is part of GNUnet.
- (C) 2012-2013 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2012-2014 GNUnet e.V.
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
You should have received a copy of the GNU General Public License
along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
*/
/**
* @author Martin Schanzenbach
*
* TODO:
* - double-check queueing logic
- * - handle cookie rewriting
- * - improve IPv6 support (#3037)
- * - actually check SSL certificates (#3038)
*/
#include "platform.h"
#include <microhttpd.h>
+#if HAVE_CURL_CURL_H
#include <curl/curl.h>
+#elif HAVE_GNURL_CURL_H
+#include <gnurl/curl.h>
+#endif
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#include <gnutls/abstract.h>
#include <gnutls/crypto.h>
+#if HAVE_GNUTLS_DANE
+#include <gnutls/dane.h>
+#endif
#include <regex.h>
#include "gnunet_util_lib.h"
#include "gnunet_gns_service.h"
/**
* Default Socks5 listen port.
- */
+ */
#define GNUNET_GNS_PROXY_PORT 7777
/**
/**
* Commands in Socks5.
- */
+ */
enum Socks5Commands
{
/**
/**
* Address types in Socks5.
- */
+ */
enum Socks5AddressType
{
/**
*/
uint8_t addr_type;
- /*
+ /*
* Followed by either an ip4/ipv6 address or a domain name with a
* length field (uint8_t) in front (depending on @e addr_type).
* followed by port number in network byte order (uint16_t).
*/
uint8_t addr_type;
- /*
+ /*
* Followed by either an ip4/ipv6 address or a domain name with a
* length field (uint8_t) in front (depending on @e addr_type).
* followed by port number in network byte order (uint16_t).
struct ProxyCA
{
/**
- * The certificate
+ * The certificate
*/
gnutls_x509_crt_t cert;
/**
- * The private key
+ * The private key
*/
gnutls_x509_privkey_t key;
};
struct ProxyGNSCertificate
{
/**
- * The certificate as PEM
+ * The certificate as PEM
*/
char cert[MAX_PEM_SIZE];
/**
- * The private key as PEM
+ * The private key as PEM
*/
char key[MAX_PEM_SIZE];
};
struct MhdHttpList
{
/**
- * DLL for httpds
+ * DLL for httpds
*/
struct MhdHttpList *prev;
/**
- * DLL for httpds
+ * DLL for httpds
*/
struct MhdHttpList *next;
/**
- * the domain name to server (only important for SSL)
+ * the domain name to server (only important for SSL)
*/
char *domain;
/**
- * The daemon handle
+ * The daemon handle
*/
struct MHD_Daemon *daemon;
struct ProxyGNSCertificate *proxy_cert;
/**
- * The task ID
+ * The task ID
*/
- GNUNET_SCHEDULER_TaskIdentifier httpd_task;
+ struct GNUNET_SCHEDULER_Task *httpd_task;
/**
- * is this an ssl daemon?
+ * is this an ssl daemon?
*/
int is_ssl;
/**
- * The socks phases.
+ * The socks phases.
*/
enum SocksPhase
{
struct Socks5Request *prev;
/**
- * The client socket
+ * The client socket
*/
struct GNUNET_NETWORK_Handle *sock;
struct GNUNET_GNS_LookupRequest *gns_lookup;
/**
- * Client socket read task
+ * Client socket read task
*/
- GNUNET_SCHEDULER_TaskIdentifier rtask;
+ struct GNUNET_SCHEDULER_Task * rtask;
/**
- * Client socket write task
+ * Client socket write task
*/
- GNUNET_SCHEDULER_TaskIdentifier wtask;
+ struct GNUNET_SCHEDULER_Task * wtask;
/**
- * Timeout task
+ * Timeout task
*/
- GNUNET_SCHEDULER_TaskIdentifier timeout_task;
+ struct GNUNET_SCHEDULER_Task * timeout_task;
/**
- * Read buffer
+ * Read buffer
*/
char rbuf[SOCKS_BUFFERSIZE];
/**
- * Write buffer
+ * Write buffer
*/
char wbuf[SOCKS_BUFFERSIZE];
struct MHD_Response *response;
/**
- * the domain name to server (only important for SSL)
+ * the domain name to server (only important for SSL)
*/
char *domain;
char *leho;
/**
- * The URL to fetch
+ * Payload of the (last) DANE record encountered.
+ */
+ char *dane_data;
+
+ /**
+ * The URL to fetch
*/
char *url;
/**
- * Handle to cURL
+ * Handle to cURL
*/
CURL *curl;
* HTTP request headers for the curl request.
*/
struct curl_slist *headers;
-
+
+ /**
+ * DNS->IP mappings resolved through GNS
+ */
+ struct curl_slist *hosts;
+
/**
* HTTP response code to give to MHD for the response.
*/
unsigned int response_code;
/**
- * Number of bytes already in read buffer
+ * Number of bytes in @e dane_data.
+ */
+ size_t dane_data_len;
+
+ /**
+ * Number of bytes already in read buffer
*/
size_t rbuf_len;
/**
- * Number of bytes already in write buffer
+ * Number of bytes already in write buffer
*/
size_t wbuf_len;
-
+
/**
* Number of bytes already in the IO buffer.
*/
struct sockaddr_storage destination_address;
/**
- * The socks state
+ * The socks state
*/
enum SocksPhase state;
/**
- * The port the proxy is running on (default 7777)
+ * The port the proxy is running on (default 7777)
*/
static unsigned long port = GNUNET_GNS_PROXY_PORT;
/**
- * The CA file (pem) to use for the proxy CA
+ * The CA file (pem) to use for the proxy CA
*/
static char *cafile_opt;
/**
- * The listen socket of the proxy
+ * The listen socket of the proxy for IPv4
*/
-static struct GNUNET_NETWORK_Handle *lsock;
+static struct GNUNET_NETWORK_Handle *lsock4;
/**
- * The listen task ID
+ * The listen socket of the proxy for IPv6
*/
-static GNUNET_SCHEDULER_TaskIdentifier ltask;
+static struct GNUNET_NETWORK_Handle *lsock6;
+
+/**
+ * The listen task ID for IPv4
+ */
+static struct GNUNET_SCHEDULER_Task * ltask4;
+
+/**
+ * The listen task ID for IPv6
+ */
+static struct GNUNET_SCHEDULER_Task * ltask6;
/**
* The cURL download task (curl multi API).
*/
-static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
+static struct GNUNET_SCHEDULER_Task * curl_download_task;
/**
- * The cURL multi handle
+ * The cURL multi handle
*/
static CURLM *curl_multi;
/**
- * Handle to the GNS service
+ * Handle to the GNS service
*/
static struct GNUNET_GNS_Handle *gns_handle;
/**
- * DLL for http/https daemons
+ * DLL for http/https daemons
*/
static struct MhdHttpList *mhd_httpd_head;
/**
- * DLL for http/https daemons
+ * DLL for http/https daemons
*/
static struct MhdHttpList *mhd_httpd_tail;
static struct Socks5Request *s5r_tail;
/**
- * The users local GNS master zone
+ * The users local GNS master zone
*/
-static struct GNUNET_CRYPTO_EccPublicSignKey local_gns_zone;
+static struct GNUNET_CRYPTO_EcdsaPublicKey local_gns_zone;
/**
- * The users local shorten zone
+ * The users local shorten zone
*/
-static struct GNUNET_CRYPTO_EccPrivateKey local_shorten_zone;
+static struct GNUNET_CRYPTO_EcdsaPrivateKey local_shorten_zone;
/**
* Is shortening enabled?
static int do_shorten;
/**
- * The CA for SSL certificate generation
+ * The CA for SSL certificate generation
*/
static struct ProxyCA proxy_ca;
/* ************************* Global helpers ********************* */
+/**
+ * Run MHD now, we have extra data ready for the callback.
+ *
+ * @param hd the daemon to run now.
+ */
+static void
+run_mhd_now (struct MhdHttpList *hd);
+
+
/**
* Clean up s5r handles.
*
static void
cleanup_s5r (struct Socks5Request *s5r)
{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Cleaning up socks request\n");
if (NULL != s5r->curl)
- {
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Cleaning up cURL handle\n");
curl_multi_remove_handle (curl_multi, s5r->curl);
curl_easy_cleanup (s5r->curl);
s5r->curl = NULL;
}
curl_slist_free_all (s5r->headers);
- if (NULL != s5r->response)
+ if (NULL != s5r->hosts)
+ {
+ curl_slist_free_all (s5r->hosts);
+ }
+ if ( (NULL != s5r->response) &&
+ (curl_failure_response != s5r->response) )
MHD_destroy_response (s5r->response);
- if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
+ if (NULL != s5r->rtask)
GNUNET_SCHEDULER_cancel (s5r->rtask);
- if (GNUNET_SCHEDULER_NO_TASK != s5r->timeout_task)
+ if (NULL != s5r->timeout_task)
GNUNET_SCHEDULER_cancel (s5r->timeout_task);
- if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
+ if (NULL != s5r->wtask)
GNUNET_SCHEDULER_cancel (s5r->wtask);
if (NULL != s5r->gns_lookup)
GNUNET_GNS_lookup_cancel (s5r->gns_lookup);
- if (NULL != s5r->sock)
+ if (NULL != s5r->sock)
{
- if (SOCKS5_SOCKET_WITH_MHD == s5r->state)
+ if (SOCKS5_SOCKET_WITH_MHD <= s5r->state)
GNUNET_NETWORK_socket_free_memory_only_ (s5r->sock);
else
GNUNET_NETWORK_socket_close (s5r->sock);
GNUNET_free_non_null (s5r->domain);
GNUNET_free_non_null (s5r->leho);
GNUNET_free_non_null (s5r->url);
+ GNUNET_free_non_null (s5r->dane_data);
GNUNET_free (s5r);
}
-/**
- * Run MHD now, we have extra data ready for the callback.
- *
- * @param hd the daemon to run now.
- */
-static void
-run_mhd_now (struct MhdHttpList *hd);
-
-
/* ************************* HTTP handling with cURL *********************** */
/* we're still not done with the upload, do not yet
start the download, the IO buffer is still full
with upload data. */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Pausing MHD download, not yet ready for download\n");
return 0; /* not yet ready for data download */
}
bytes_to_copy = GNUNET_MIN (max,
s5r->io_len);
if ( (0 == bytes_to_copy) &&
(SOCKS5_SOCKET_DOWNLOAD_DONE != s5r->state) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Pausing MHD download, no data available\n");
return 0; /* more data later */
+ }
if ( (0 == bytes_to_copy) &&
(SOCKS5_SOCKET_DOWNLOAD_DONE == s5r->state) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Completed MHD download\n");
return MHD_CONTENT_READER_END_OF_STREAM;
+ }
memcpy (buf, s5r->io_buf, bytes_to_copy);
memmove (s5r->io_buf,
&s5r->io_buf[bytes_to_copy],
s5r->io_len - bytes_to_copy);
s5r->io_len -= bytes_to_copy;
- curl_easy_pause (s5r->curl, CURLPAUSE_CONT);
+ if (NULL != s5r->curl)
+ curl_easy_pause (s5r->curl, CURLPAUSE_CONT);
return bytes_to_copy;
}
static int
check_ssl_certificate (struct Socks5Request *s5r)
{
- union {
- struct curl_slist *to_info;
- struct curl_certinfo *to_certinfo;
- } ptr;
- int i;
- struct curl_slist *slist;
-
- ptr.to_info = NULL;
- if (CURLE_OK !=
- curl_easy_getinfo (s5r->curl,
- CURLINFO_CERTINFO,
- &ptr.to_info))
+ unsigned int cert_list_size;
+ const gnutls_datum_t *chainp;
+ const struct curl_tlssessioninfo *tlsinfo;
+ char certdn[GNUNET_DNSPARSER_MAX_NAME_LENGTH + 3];
+ size_t size;
+ gnutls_x509_crt_t x509_cert;
+ int rc;
+ const char *name;
+
+ if (CURLE_OK !=
+ curl_easy_getinfo (s5r->curl,
+ CURLINFO_TLS_SESSION,
+ (struct curl_slist **) &tlsinfo))
return GNUNET_SYSERR;
- /* FIXME: for now, we just output the certs to stderr, we should
- check them against LEHO / TLSA record information here! (#3038) */
- if(NULL != ptr.to_info)
- {
+ if (CURLSSLBACKEND_GNUTLS != tlsinfo->backend)
+ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Got %d certs!\n",
- ptr.to_certinfo->num_of_certs);
- for (i = 0; i < ptr.to_certinfo->num_of_certs; i++)
- {
- for (slist = ptr.to_certinfo->certinfo[i]; NULL != slist; slist = slist->next)
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Certificate #%d: %s\n",
- i,
- slist->data);
+ _("Unsupported CURL SSL backend %d\n"),
+ tlsinfo->backend);
+ return GNUNET_SYSERR;
+ }
+ chainp = gnutls_certificate_get_peers (tlsinfo->internals, &cert_list_size);
+ if ( (! chainp) || (0 == cert_list_size) )
+ return GNUNET_SYSERR;
+
+ size = sizeof (certdn);
+ /* initialize an X.509 certificate structure. */
+ gnutls_x509_crt_init (&x509_cert);
+ gnutls_x509_crt_import (x509_cert,
+ chainp,
+ GNUTLS_X509_FMT_DER);
+
+ if (0 != (rc = gnutls_x509_crt_get_dn_by_oid (x509_cert,
+ GNUTLS_OID_X520_COMMON_NAME,
+ 0, /* the first and only one */
+ 0 /* no DER encoding */,
+ certdn,
+ &size)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to fetch CN from cert: %s\n"),
+ gnutls_strerror(rc));
+ gnutls_x509_crt_deinit (x509_cert);
+ return GNUNET_SYSERR;
+ }
+ /* check for TLSA/DANE records */
+#if HAVE_GNUTLS_DANE
+ if (NULL != s5r->dane_data)
+ {
+ char *dd[] = { s5r->dane_data, NULL };
+ int dlen[] = { s5r->dane_data_len, 0};
+ dane_state_t dane_state;
+ dane_query_t dane_query;
+ unsigned int verify;
+
+ /* FIXME: add flags to gnutls to NOT read UNBOUND_ROOT_KEY_FILE here! */
+ if (0 != (rc = dane_state_init (&dane_state,
+#ifdef DANE_F_IGNORE_DNSSEC
+ DANE_F_IGNORE_DNSSEC |
+#endif
+ DANE_F_IGNORE_LOCAL_RESOLVER)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to initialize DANE: %s\n"),
+ dane_strerror(rc));
+ gnutls_x509_crt_deinit (x509_cert);
+ return GNUNET_SYSERR;
+ }
+ if (0 != (rc = dane_raw_tlsa (dane_state,
+ &dane_query,
+ dd,
+ dlen,
+ GNUNET_YES,
+ GNUNET_NO)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to parse DANE record: %s\n"),
+ dane_strerror(rc));
+ dane_state_deinit (dane_state);
+ gnutls_x509_crt_deinit (x509_cert);
+ return GNUNET_SYSERR;
+ }
+ if (0 != (rc = dane_verify_crt_raw (dane_state,
+ chainp,
+ cert_list_size,
+ gnutls_certificate_type_get (tlsinfo->internals),
+ dane_query,
+ 0, 0,
+ &verify)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to verify TLS connection using DANE: %s\n"),
+ dane_strerror(rc));
+ dane_query_deinit (dane_query);
+ dane_state_deinit (dane_state);
+ gnutls_x509_crt_deinit (x509_cert);
+ return GNUNET_SYSERR;
+ }
+ if (0 != verify)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed DANE verification failed with GnuTLS verify status code: %u\n"),
+ verify);
+ dane_query_deinit (dane_query);
+ dane_state_deinit (dane_state);
+ gnutls_x509_crt_deinit (x509_cert);
+ return GNUNET_SYSERR;
}
+ dane_query_deinit (dane_query);
+ dane_state_deinit (dane_state);
+ /* success! */
}
+ else
+#endif
+ {
+ /* try LEHO or ordinary domain name X509 verification */
+ name = s5r->domain;
+ if (NULL != s5r->leho)
+ name = s5r->leho;
+ if (NULL != name)
+ {
+ if (0 == (rc = gnutls_x509_crt_check_hostname (x509_cert,
+ name)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("SSL certificate subject name (%s) does not match `%s'\n"),
+ certdn,
+ name);
+ gnutls_x509_crt_deinit (x509_cert);
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ {
+ /* we did not even have the domain name!? */
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ }
+ gnutls_x509_crt_deinit (x509_cert);
return GNUNET_OK;
}
-
/**
* We're getting an HTTP response header from cURL. Convert it to the
* MHD response headers. Mostly copies the headers, but makes special
size_t bytes = size * nmemb;
char *ndup;
const char *hdr_type;
- const char *hdr_val;
+ const char *cookie_domain;
+ char *hdr_val;
long resp_code;
char *new_cookie_hdr;
char *new_location;
+ size_t offset;
+ size_t delta_cdomain;
+ int domain_matched;
+ char *tok;
if (NULL == s5r->response)
{
(GNUNET_OK != check_ssl_certificate (s5r)) )
return 0;
- GNUNET_break (CURLE_OK ==
+ GNUNET_break (CURLE_OK ==
curl_easy_getinfo (s5r->curl,
CURLINFO_RESPONSE_CODE,
&resp_code));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Creating MHD response with code %d\n",
+ (int) resp_code);
s5r->response_code = resp_code;
s5r->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
IO_BUFFERSIZE,
if (NULL != s5r->leho)
{
char *cors_hdr;
-
- GNUNET_asprintf (&cors_hdr,
+
+ GNUNET_asprintf (&cors_hdr,
(HTTPS_PORT == s5r->port)
? "https://%s"
: "http://%s",
s5r->leho);
-
- GNUNET_break (MHD_YES ==
+
+ GNUNET_break (MHD_YES ==
MHD_add_response_header (s5r->response,
MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
cors_hdr));
GNUNET_free (cors_hdr);
}
- /* force connection to be closed after each request, as we
- do not support HTTP pipelining */
- GNUNET_break (MHD_YES ==
+ /* force connection to be closed after each request, as we
+ do not support HTTP pipelining (yet, FIXME!) */
+ GNUNET_break (MHD_YES ==
MHD_add_response_header (s5r->response,
MHD_HTTP_HEADER_CONNECTION,
"close"));
}
-
+
ndup = GNUNET_strndup (buffer, bytes);
hdr_type = strtok (ndup, ":");
if (NULL == hdr_type)
hdr_val++;
/* custom logic for certain header types */
- if (0 == strcasecmp (hdr_type,
- MHD_HTTP_HEADER_SET_COOKIE))
+ new_cookie_hdr = NULL;
+ if ( (NULL != s5r->leho) &&
+ (0 == strcasecmp (hdr_type,
+ MHD_HTTP_HEADER_SET_COOKIE)) )
+
{
-#if 0
- // FIXME: adjust code...
+ new_cookie_hdr = GNUNET_malloc (strlen (hdr_val) +
+ strlen (s5r->domain) + 1);
+ offset = 0;
+ domain_matched = GNUNET_NO; /* make sure we match domain at most once */
for (tok = strtok (hdr_val, ";"); NULL != tok; tok = strtok (NULL, ";"))
{
- if (0 == memcmp (tok, " domain", strlen (" domain")))
+ if ( (0 == strncasecmp (tok, " domain", strlen (" domain"))) &&
+ (GNUNET_NO == domain_matched) )
{
+ domain_matched = GNUNET_YES;
cookie_domain = tok + strlen (" domain") + 1;
if (strlen (cookie_domain) < strlen (s5r->leho))
{
- delta_cdomain = strlen (ctask->leho) - strlen (cookie_domain);
- if (0 == strcmp (cookie_domain, ctask->leho + (delta_cdomain)))
- {
- GNUNET_snprintf (new_cookie_hdr+offset,
- sizeof (new_cookie_hdr),
- " domain=%s", ctask->authority);
- offset += strlen (" domain=") + strlen (ctask->authority);
- new_cookie_hdr[offset] = ';';
- offset++;
+ delta_cdomain = strlen (s5r->leho) - strlen (cookie_domain);
+ if (0 == strcasecmp (cookie_domain, s5r->leho + delta_cdomain))
+ {
+ offset += sprintf (new_cookie_hdr + offset,
+ " domain=%s;",
+ s5r->domain);
continue;
}
}
- else if (strlen (cookie_domain) == strlen (ctask->leho))
+ else if (0 == strcmp (cookie_domain, s5r->leho))
{
- if (0 == strcmp (cookie_domain, ctask->leho))
- {
- GNUNET_snprintf (new_cookie_hdr+offset,
- sizeof (new_cookie_hdr),
- " domain=%s", ctask->host);
- offset += strlen (" domain=") + strlen (ctask->host);
- new_cookie_hdr[offset] = ';';
- offset++;
- continue;
- }
+ offset += sprintf (new_cookie_hdr + offset,
+ " domain=%s;",
+ s5r->domain);
+ continue;
}
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Cookie domain invalid\n");
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Cookie domain `%s' supplied by server is invalid\n"),
+ tok);
}
memcpy (new_cookie_hdr + offset, tok, strlen (tok));
offset += strlen (tok);
- new_cookie_hdr[offset] = ';';
- offset++;
+ new_cookie_hdr[offset++] = ';';
}
hdr_val = new_cookie_hdr;
-
-#else
- new_cookie_hdr = NULL;
-#endif
- }
- else
- {
- new_cookie_hdr = NULL;
}
new_location = NULL;
if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
{
char *leho_host;
-
+
GNUNET_asprintf (&leho_host,
(HTTPS_PORT != s5r->port)
? "http://%s"
: "https://%s",
s5r->leho);
- if (0 == strncmp (leho_host,
- hdr_val,
+ if (0 == strncmp (leho_host,
+ hdr_val,
strlen (leho_host)))
{
GNUNET_asprintf (&new_location,
}
GNUNET_free (leho_host);
}
- GNUNET_break (MHD_YES ==
- MHD_add_response_header (s5r->response,
- hdr_type,
- hdr_val));
+ /* MHD does not allow certain characters in values, remove those */
+ if (NULL != (tok = strchr (hdr_val, '\n')))
+ *tok = '\0';
+ if (NULL != (tok = strchr (hdr_val, '\r')))
+ *tok = '\0';
+ if (NULL != (tok = strchr (hdr_val, '\t')))
+ *tok = '\0';
+ if (0 != strlen (hdr_val))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding header %s: %s to MHD response\n",
+ hdr_type,
+ hdr_val);
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (s5r->response,
+ hdr_type,
+ hdr_val));
+ }
GNUNET_free (ndup);
GNUNET_free_non_null (new_cookie_hdr);
GNUNET_free_non_null (new_location);
/* we're still not done with the upload, do not yet
start the download, the IO buffer is still full
with upload data. */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Pausing CURL download, waiting for UPLOAD to finish\n");
return CURL_WRITEFUNC_PAUSE; /* not yet ready for data download */
}
if (sizeof (s5r->io_buf) - s5r->io_len < total)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Pausing CURL download, not enough space\n");
return CURL_WRITEFUNC_PAUSE; /* not enough space */
- memcpy (&s5r->io_buf[s5r->io_len],
+ }
+ memcpy (&s5r->io_buf[s5r->io_len],
ptr,
total);
s5r->io_len += total;
if (s5r->io_len == total)
- run_mhd_now (s5r->hd);
+ run_mhd_now (s5r->hd);
return total;
}
if ( (0 == s5r->io_len) &&
(SOCKS5_SOCKET_UPLOAD_DONE != s5r->state) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Pausing CURL UPLOAD, need more data\n");
return CURL_READFUNC_PAUSE;
+ }
if ( (0 == s5r->io_len) &&
(SOCKS5_SOCKET_UPLOAD_DONE == s5r->state) )
{
s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Completed CURL UPLOAD\n");
return 0; /* upload finished, can now download */
}
if ( (SOCKS5_SOCKET_UPLOAD_STARTED != s5r->state) ||
* from curl
*
* @param cls closure
- * @param tc task context
*/
static void
-curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+curl_task_download (void *cls);
/**
long to;
struct GNUNET_TIME_Relative rtime;
- if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
+ if (NULL != curl_download_task)
{
GNUNET_SCHEDULER_cancel (curl_download_task);
- curl_download_task = GNUNET_SCHEDULER_NO_TASK;
+ curl_download_task = NULL;
}
max = -1;
FD_ZERO (&rs);
GNUNET_NETWORK_fdset_destroy (gws);
GNUNET_NETWORK_fdset_destroy (grs);
}
- else
+ else
{
curl_download_task = GNUNET_SCHEDULER_add_delayed (rtime,
&curl_task_download,
* Task that is run when we are ready to receive more data from curl.
*
* @param cls closure, NULL
- * @param tc task context
*/
static void
-curl_task_download (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+curl_task_download (void *cls)
{
int running;
int msgnum;
CURLMcode mret;
struct Socks5Request *s5r;
- curl_download_task = GNUNET_SCHEDULER_NO_TASK;
+ curl_download_task = NULL;
do
{
- running = 0;
+ running = 0;
mret = curl_multi_perform (curl_multi, &running);
while (NULL != (msg = curl_multi_info_read (curl_multi, &msgnum)))
{
GNUNET_break (CURLE_OK ==
curl_easy_getinfo (msg->easy_handle,
CURLINFO_PRIVATE,
- &s5r));
+ (char **) &s5r ));
if (NULL == s5r)
{
GNUNET_break (0);
case CURLE_GOT_NOTHING:
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"CURL download completed.\n");
- s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE;
+ s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE;
run_mhd_now (s5r->hd);
break;
default:
curl_easy_strerror (msg->data.result));
/* FIXME: indicate error somehow? close MHD connection badly as well? */
s5r->state = SOCKS5_SOCKET_DOWNLOAD_DONE;
- run_mhd_now (s5r->hd);
+ run_mhd_now (s5r->hd);
break;
}
- GNUNET_break (NULL != s5r->response);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Cleaning up cURL handle\n");
curl_multi_remove_handle (curl_multi, s5r->curl);
curl_easy_cleanup (s5r->curl);
s5r->curl = NULL;
+ if (NULL == s5r->response)
+ s5r->response = curl_failure_response;
break;
case CURLMSG_LAST:
/* documentation says this is not used */
break;
}
};
- } while (mret == CURLM_CALL_MULTI_PERFORM);
+ } while (mret == CURLM_CALL_MULTI_PERFORM);
if (CURLM_OK != mret)
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"%s failed at %s:%d: `%s'\n",
"curl_multi_perform", __FILE__, __LINE__,
- curl_multi_strerror (mret));
+ curl_multi_strerror (mret));
if (0 == running)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Suspending cURL multi loop, no more events pending\n");
return; /* nothing more in progress */
}
- curl_download_prepare();
+ curl_download_prepare ();
}
/**
* Read HTTP request header field from the request. Copies the fields
* over to the 'headers' that will be given to curl. However, 'Host'
- * is substituted with the LEHO if present.
+ * is substituted with the LEHO if present. We also change the
+ * 'Connection' header value to "close" as the proxy does not support
+ * pipelining.
*
* @param cls our `struct Socks5Request`
* @param kind value kind
* @param key field key
* @param value field value
- * @return #MHD_YES to continue to iterate
+ * @return MHD_YES to continue to iterate
*/
static int
con_val_iter (void *cls,
if ( (0 == strcasecmp (MHD_HTTP_HEADER_HOST, key)) &&
(NULL != s5r->leho) )
value = s5r->leho;
+ if (0 == strcasecmp (MHD_HTTP_HEADER_CONNECTION, key))
+ value = "Close";
GNUNET_asprintf (&hdr,
"%s: %s",
key,
value);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding HEADER `%s' to HTTP request\n",
+ hdr);
s5r->headers = curl_slist_append (s5r->headers,
hdr);
GNUNET_free (hdr);
* @a upload_data provided; the method must update this
* value to the number of bytes NOT processed;
* @param con_cls pointer to location where we store the 'struct Request'
- * @return #MHD_YES if the connection was handled successfully,
- * #MHD_NO if the socket must be closed due to a serious
+ * @return MHD_YES if the connection was handled successfully,
+ * MHD_NO if the socket must be closed due to a serious
* error while handling the request
*/
static int
size_t *upload_data_size,
void **con_cls)
{
- /* struct MhdHttpList* hd = cls; */
struct Socks5Request *s5r = *con_cls;
char *curlurl;
+ char *curl_hosts;
char ipstring[INET6_ADDRSTRLEN];
char ipaddr[INET6_ADDRSTRLEN + 2];
const struct sockaddr *sa;
GNUNET_break (0);
return MHD_NO;
}
- if (NULL == s5r->curl)
+ if ( (NULL == s5r->curl) &&
+ (SOCKS5_SOCKET_WITH_MHD == s5r->state) )
{
/* first time here, initialize curl handle */
sa = (const struct sockaddr *) &s5r->destination_address;
if (NULL == s5r->curl)
return MHD_queue_response (con,
MHD_HTTP_INTERNAL_SERVER_ERROR,
- curl_failure_response);
+ curl_failure_response);
curl_easy_setopt (s5r->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
curl_easy_setopt (s5r->curl, CURLOPT_HEADERDATA, s5r);
curl_easy_setopt (s5r->curl, CURLOPT_FOLLOWLOCATION, 0);
curl_easy_setopt (s5r->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
- curl_easy_setopt (s5r->curl, CURLOPT_FAILONERROR, 1); /* not sure we want this */
curl_easy_setopt (s5r->curl, CURLOPT_CONNECTTIMEOUT, 600L);
curl_easy_setopt (s5r->curl, CURLOPT_TIMEOUT, 600L);
curl_easy_setopt (s5r->curl, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt (s5r->curl, CURLOPT_HTTP_TRANSFER_DECODING, 0);
curl_easy_setopt (s5r->curl, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt (s5r->curl, CURLOPT_PRIVATE, s5r);
- curl_easy_setopt (s5r->curl, CURLOPT_VERBOSE, 1); // FIXME: remove later
+ curl_easy_setopt (s5r->curl, CURLOPT_VERBOSE, 0);
+ /**
+ * Pre-populate cache to resolve Hostname.
+ * This is necessary as the DNS name in the CURLOPT_URL is used
+ * for SNI http://de.wikipedia.org/wiki/Server_Name_Indication
+ */
+ if (NULL != s5r->leho)
+ {
+ GNUNET_asprintf (&curl_hosts,
+ "%s:%d:%s",
+ s5r->leho,
+ port,
+ ipaddr);
+ s5r->hosts = curl_slist_append(NULL, curl_hosts);
+ curl_easy_setopt(s5r->curl, CURLOPT_RESOLVE, s5r->hosts);
+ GNUNET_free (curl_hosts);
+ }
GNUNET_asprintf (&curlurl,
(HTTPS_PORT != s5r->port)
? "http://%s:%d%s"
: "https://%s:%d%s",
- ipaddr,
- port,
- s5r->url);
+ (NULL != s5r->leho)
+ ? s5r->leho
+ : ipaddr,
+ port,
+ s5r->url);
curl_easy_setopt (s5r->curl,
- CURLOPT_URL,
- curlurl);
+ CURLOPT_URL,
+ curlurl);
GNUNET_free (curlurl);
if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
curl_easy_setopt (s5r->curl, CURLOPT_WRITEDATA, s5r);
curl_easy_setopt (s5r->curl, CURLOPT_READFUNCTION, &curl_upload_cb);
curl_easy_setopt (s5r->curl, CURLOPT_READDATA, s5r);
- }
+ }
else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
{
- s5r->state = SOCKS5_SOCKET_UPLOAD_STARTED;
+ s5r->state = SOCKS5_SOCKET_UPLOAD_STARTED;
curl_easy_setopt (s5r->curl, CURLOPT_POST, 1);
curl_easy_setopt (s5r->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
curl_easy_setopt (s5r->curl, CURLOPT_WRITEDATA, s5r);
}
else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
{
- s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
+ s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
curl_easy_setopt (s5r->curl, CURLOPT_NOBODY, 1);
}
else if (0 == strcasecmp (meth, MHD_HTTP_METHOD_GET))
{
- s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
+ s5r->state = SOCKS5_SOCKET_DOWNLOAD_STARTED;
curl_easy_setopt (s5r->curl, CURLOPT_HTTPGET, 1);
curl_easy_setopt (s5r->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
curl_easy_setopt (s5r->curl, CURLOPT_WRITEDATA, s5r);
_("Unsupported HTTP method `%s'\n"),
meth);
curl_easy_cleanup (s5r->curl);
- s5r->curl = NULL;
+ s5r->curl = NULL;
return MHD_NO;
}
-
+
if (0 == strcasecmp (ver, MHD_HTTP_VERSION_1_0))
{
curl_easy_setopt (s5r->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
{
curl_easy_setopt (s5r->curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_NONE);
}
-
+
if (HTTPS_PORT == s5r->port)
{
curl_easy_setopt (s5r->curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
curl_easy_setopt (s5r->curl, CURLOPT_SSL_VERIFYPEER, 1L);
/* Disable cURL checking the hostname, as we will check ourselves
as only we have the domain name or the LEHO or the DANE record */
- curl_easy_setopt (s5r->curl, CURLOPT_SSL_VERIFYHOST, 0L);
+ curl_easy_setopt (s5r->curl, CURLOPT_SSL_VERIFYHOST, 0L);
}
else
{
- curl_easy_setopt (s5r->curl, CURLOPT_USE_SSL, CURLUSESSL_NONE);
+ curl_easy_setopt (s5r->curl, CURLOPT_USE_SSL, CURLUSESSL_NONE);
}
if (CURLM_OK != curl_multi_add_handle (curl_multi, s5r->curl))
{
GNUNET_break (0);
curl_easy_cleanup (s5r->curl);
- s5r->curl = NULL;
- return MHD_NO;
+ s5r->curl = NULL;
+ return MHD_NO;
}
MHD_get_connection_values (con,
MHD_HEADER_KIND,
curl_easy_setopt (s5r->curl, CURLOPT_HTTPHEADER, s5r->headers);
curl_download_prepare ();
return MHD_YES;
- }
+ }
/* continuing to process request */
if (0 != *upload_data_size)
{
left = GNUNET_MIN (*upload_data_size,
sizeof (s5r->io_buf) - s5r->io_len);
- memcpy (&s5r->io_buf[s5r->io_len],
+ memcpy (&s5r->io_buf[s5r->io_len],
upload_data,
left);
s5r->io_len += left;
*upload_data_size -= left;
- if (s5r->io_len == left)
- curl_easy_pause (s5r->curl, CURLPAUSE_CONT);
+ GNUNET_assert (NULL != s5r->curl);
+ curl_easy_pause (s5r->curl, CURLPAUSE_CONT);
curl_download_prepare ();
return MHD_YES;
}
- s5r->state = SOCKS5_SOCKET_UPLOAD_DONE;
- if (NULL == s5r->response)
+ if (SOCKS5_SOCKET_UPLOAD_STARTED == s5r->state)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Finished processing UPLOAD\n");
+ s5r->state = SOCKS5_SOCKET_UPLOAD_DONE;
+ }
+ if (NULL == s5r->response)
return MHD_YES; /* too early to queue response, did not yet get headers from cURL */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Queueing response with MHD\n");
return MHD_queue_response (con,
- s5r->response_code,
+ s5r->response_code,
s5r->response);
}
* @param cls NULL
* @param connection connection handle
* @param con_cls value as set by the last call to
- * the #MHD_AccessHandlerCallback, should be our `struct Socks5Request`
+ * the MHD_AccessHandlerCallback, should be our `struct Socks5Request *`
* @param toe reason for request termination (ignored)
*/
static void
if (NULL == s5r)
return;
+ if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "MHD encountered error handling request: %d\n",
+ toe);
cleanup_s5r (s5r);
- *con_cls = NULL;
+ curl_download_prepare();
+ *con_cls = NULL;
}
* Function called when MHD first processes an incoming connection.
* Gives us the respective URI information.
*
- * We use this to associate the `struct MHD_Connection` with our
+ * We use this to associate the `struct MHD_Connection` with our
* internal `struct Socks5Request` data structure (by checking
* for matching sockets).
*
* @return the `struct Socks5Request` that this @a connection is for
*/
static void *
-mhd_log_callback (void *cls,
+mhd_log_callback (void *cls,
const char *url,
struct MHD_Connection *connection)
{
ci = MHD_get_connection_info (connection,
MHD_CONNECTION_INFO_CONNECTION_FD);
- if (NULL == ci)
+ if (NULL == ci)
{
GNUNET_break (0);
return NULL;
return NULL;
}
s5r->url = GNUNET_strdup (url);
+ GNUNET_SCHEDULER_cancel (s5r->timeout_task);
+ s5r->timeout_task = NULL;
return s5r;
}
}
+ GNUNET_break (0);
return NULL;
}
hd);
GNUNET_free_non_null (hd->domain);
MHD_stop_daemon (hd->daemon);
- if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
+ if (NULL != hd->httpd_task)
+ {
GNUNET_SCHEDULER_cancel (hd->httpd_task);
+ hd->httpd_task = NULL;
+ }
GNUNET_free_non_null (hd->proxy_cert);
if (hd == httpd)
httpd = NULL;
* Task run whenever HTTP server is idle for too long. Kill it.
*
* @param cls the `struct MhdHttpList *`
- * @param tc sched context
*/
static void
-kill_httpd_task (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+kill_httpd_task (void *cls)
{
struct MhdHttpList *hd = cls;
-
+
+ hd->httpd_task = NULL;
kill_httpd (hd);
}
* Task run whenever HTTP server operations are pending.
*
* @param cls the `struct MhdHttpList *` of the daemon that is being run
- * @param tc sched context
*/
static void
-do_httpd (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc);
+do_httpd (void *cls);
/**
wrs = NULL;
wws = NULL;
}
- if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
+ if (NULL != hd->httpd_task)
GNUNET_SCHEDULER_cancel (hd->httpd_task);
if ( (MHD_YES != haveto) &&
(-1 == max) &&
* Task run whenever HTTP server operations are pending.
*
* @param cls the `struct MhdHttpList` of the daemon that is being run
- * @param tc scheduler context
*/
static void
-do_httpd (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_httpd (void *cls)
{
struct MhdHttpList *hd = cls;
-
- hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
+
+ hd->httpd_task = NULL;
MHD_run (hd->daemon);
schedule_httpd (hd);
}
static void
run_mhd_now (struct MhdHttpList *hd)
{
- if (GNUNET_SCHEDULER_NO_TASK !=
+ if (NULL !=
hd->httpd_task)
GNUNET_SCHEDULER_cancel (hd->httpd_task);
- hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd,
+ hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd,
hd);
}
* @return NULL on error
*/
static void*
-load_file (const char* filename,
+load_file (const char* filename,
unsigned int* size)
{
void *buffer;
* @return #GNUNET_OK on success
*/
static int
-load_key_from_file (gnutls_x509_privkey_t key,
+load_key_from_file (gnutls_x509_privkey_t key,
const char* keyfile)
{
gnutls_datum_t key_data;
int ret;
key_data.data = load_file (keyfile, &key_data.size);
+ if (NULL == key_data.data)
+ return GNUNET_SYSERR;
ret = gnutls_x509_privkey_import (key, &key_data,
GNUTLS_X509_FMT_PEM);
if (GNUTLS_E_SUCCESS != ret)
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Unable to import private key from file `%s'\n"),
keyfile);
- GNUNET_break (0);
}
- GNUNET_free (key_data.data);
+ GNUNET_free_non_null (key_data.data);
return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
}
* @return #GNUNET_OK on success
*/
static int
-load_cert_from_file (gnutls_x509_crt_t crt,
+load_cert_from_file (gnutls_x509_crt_t crt,
const char* certfile)
{
gnutls_datum_t cert_data;
int ret;
cert_data.data = load_file (certfile, &cert_data.size);
+ if (NULL == cert_data.data)
+ return GNUNET_SYSERR;
ret = gnutls_x509_crt_import (crt, &cert_data,
GNUTLS_X509_FMT_PEM);
if (GNUTLS_E_SUCCESS != ret)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Unable to import certificate %s\n"), certfile);
- GNUNET_break (0);
}
- GNUNET_free (cert_data.data);
+ GNUNET_free_non_null (cert_data.data);
return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
}
struct tm *tm_data;
struct ProxyGNSCertificate *pgc;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Generating TLS/SSL certificate for `%s'\n",
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Generating TLS/SSL certificate for `%s'\n",
name);
GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_init (&request));
GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
pgc = GNUNET_new (struct ProxyGNSCertificate);
gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
- 0, "TNR", 2);
+ 0, "ZZ", 2);
gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
0, "GNU Name System", 4);
gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
&serial,
sizeof (serial));
etime = time (NULL);
- tm_data = localtime (&etime);
+ tm_data = localtime (&etime);
gnutls_x509_crt_set_activation_time (request,
etime);
tm_data->tm_year++;
etime = mktime (tm_data);
gnutls_x509_crt_set_expiration_time (request,
etime);
- gnutls_x509_crt_sign (request,
- proxy_ca.cert,
+ gnutls_x509_crt_sign (request,
+ proxy_ca.cert,
proxy_ca.key);
key_buf_size = sizeof (pgc->key);
cert_buf_size = sizeof (pgc->cert);
}
+/**
+ * Function called by MHD with errors, suppresses them all.
+ *
+ * @param cls closure
+ * @param fm format string (`printf()`-style)
+ * @param ap arguments to @a fm
+ */
+static void
+mhd_error_log_callback (void *cls,
+ const char *fm,
+ va_list ap)
+{
+ /* do nothing */
+}
+
+
/**
* Lookup (or create) an SSL MHD instance for a particular domain.
*
* @param domain the domain the SSL daemon has to serve
- * @return NULL on errro
+ * @return NULL on error
*/
static struct MhdHttpList *
lookup_ssl_httpd (const char* domain)
struct MhdHttpList *hd;
struct ProxyGNSCertificate *pgc;
+ if (NULL == domain)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
- if (0 == strcmp (hd->domain, domain))
+ if ( (NULL != hd->domain) &&
+ (0 == strcmp (hd->domain, domain)) )
return hd;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Starting fresh MHD HTTPS instance for domain `%s'\n",
domain);
- pgc = generate_gns_certificate (domain);
+ pgc = generate_gns_certificate (domain);
hd = GNUNET_new (struct MhdHttpList);
hd->is_ssl = GNUNET_YES;
- hd->domain = GNUNET_strdup (domain);
+ hd->domain = GNUNET_strdup (domain);
hd->proxy_cert = pgc;
hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_NO_LISTEN_SOCKET,
0,
MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL,
MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
+ MHD_OPTION_EXTERNAL_LOGGER, &mhd_error_log_callback, NULL,
MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
MHD_OPTION_END);
GNUNET_free (hd);
return NULL;
}
- GNUNET_CONTAINER_DLL_insert (mhd_httpd_head,
- mhd_httpd_tail,
+ GNUNET_CONTAINER_DLL_insert (mhd_httpd_head,
+ mhd_httpd_tail,
hd);
return hd;
}
* the SOCKS5 handshake). Clean up.
*
* @param cls the `struct Socks5Request *`
- * @param tc sched context
*/
static void
-timeout_s5r_handshake (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+timeout_s5r_handshake (void *cls)
{
struct Socks5Request *s5r = cls;
- s5r->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ s5r->timeout_task = NULL;
cleanup_s5r (s5r);
}
/**
* We're done with the Socks5 protocol, now we need to pass the
- * connection data through to the final destination, either
+ * connection data through to the final destination, either
* direct (if the protocol might not be HTTP), or via MHD
* (if the port looks like it should be HTTP).
*
break;
case HTTP_PORT:
default:
- GNUNET_assert (NULL == httpd);
+ GNUNET_assert (NULL != httpd);
hd = httpd;
break;
}
* Write data from buffer to socks5 client, then continue with state machine.
*
* @param cls the closure with the `struct Socks5Request`
- * @param tc scheduler context
*/
static void
-do_write (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_write (void *cls)
{
struct Socks5Request *s5r = cls;
ssize_t len;
- s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
+ s5r->wtask = NULL;
len = GNUNET_NETWORK_socket_send (s5r->sock,
s5r->wbuf,
s5r->wbuf_len);
if (len <= 0)
{
/* write error: connection closed, shutdown, etc.; just clean up */
- cleanup_s5r (s5r);
+ cleanup_s5r (s5r);
return;
}
memmove (s5r->wbuf,
switch (s5r->state)
{
- case SOCKS5_INIT:
+ case SOCKS5_INIT:
GNUNET_assert (0);
break;
- case SOCKS5_REQUEST:
- GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s5r->rtask);
+ case SOCKS5_REQUEST:
+ GNUNET_assert (NULL != s5r->rtask);
break;
case SOCKS5_DATA_TRANSFER:
setup_data_transfer (s5r);
s_resp->version = SOCKS_VERSION_5;
s_resp->reply = sc;
s5r->state = SOCKS5_WRITE_THEN_CLEANUP;
- if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
- s5r->wtask =
+ if (NULL != s5r->wtask)
+ s5r->wtask =
GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
s5r->sock,
&do_write, s5r);
s_resp->reserved = 0;
s_resp->addr_type = SOCKS5_AT_IPV4;
/* zero out IPv4 address and port */
- memset (&s_resp[1],
- 0,
+ memset (&s_resp[1],
+ 0,
sizeof (struct in_addr) + sizeof (uint16_t));
s5r->wbuf_len += sizeof (struct Socks5ServerResponseMessage) +
- sizeof (struct in_addr) + sizeof (uint16_t);
- if (GNUNET_SCHEDULER_NO_TASK == s5r->wtask)
+ sizeof (struct in_addr) + sizeof (uint16_t);
+ if (NULL == s5r->wtask)
s5r->wtask =
GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
s5r->sock,
- &do_write, s5r);
+ &do_write, s5r);
}
/**
* Process GNS results for target domain.
*
- * @param cls the ctask
+ * @param cls the `struct Socks5Request *`
* @param rd_count number of records returned
* @param rd record data
*/
static void
handle_gns_result (void *cls,
uint32_t rd_count,
- const struct GNUNET_NAMESTORE_RecordData *rd)
+ const struct GNUNET_GNSRECORD_Data *rd)
{
struct Socks5Request *s5r = cls;
uint32_t i;
- const struct GNUNET_NAMESTORE_RecordData *r;
+ const struct GNUNET_GNSRECORD_Data *r;
int got_ip;
s5r->gns_lookup = NULL;
}
if (GNUNET_YES == got_ip)
break;
- if (GNUNET_OK !=
+ if (GNUNET_OK !=
GNUNET_NETWORK_test_pf (PF_INET))
break;
got_ip = GNUNET_YES;
#endif
}
break;
- case GNUNET_DNSPARSER_TYPE_AAAA:
+ case GNUNET_DNSPARSER_TYPE_AAAA:
{
struct sockaddr_in6 *in;
break;
}
if (GNUNET_YES == got_ip)
- break;
- if (GNUNET_OK !=
+ break;
+ if (GNUNET_OK !=
GNUNET_NETWORK_test_pf (PF_INET))
break;
/* FIXME: allow user to disable IPv6 per configuration option... */
in->sin6_len = sizeof (*in);
#endif
}
- break;
- case GNUNET_NAMESTORE_TYPE_VPN:
+ break;
+ case GNUNET_GNSRECORD_TYPE_VPN:
GNUNET_break (0); /* should have been translated within GNS */
break;
- case GNUNET_NAMESTORE_TYPE_LEHO:
+ case GNUNET_GNSRECORD_TYPE_LEHO:
GNUNET_free_non_null (s5r->leho);
s5r->leho = GNUNET_strndup (r->data,
r->data_size);
break;
+ case GNUNET_GNSRECORD_TYPE_BOX:
+ {
+ const struct GNUNET_GNSRECORD_BoxRecord *box;
+
+ if (r->data_size < sizeof (struct GNUNET_GNSRECORD_BoxRecord))
+ {
+ GNUNET_break_op (0);
+ break;
+ }
+ box = r->data;
+ if ( (ntohl (box->record_type) != GNUNET_DNSPARSER_TYPE_TLSA) ||
+ (ntohs (box->protocol) != IPPROTO_TCP) ||
+ (ntohs (box->service) != s5r->port) )
+ break; /* BOX record does not apply */
+ GNUNET_free_non_null (s5r->dane_data);
+ s5r->dane_data_len = r->data_size - sizeof (struct GNUNET_GNSRECORD_BoxRecord);
+ s5r->dane_data = GNUNET_malloc (s5r->dane_data_len);
+ memcpy (s5r->dane_data,
+ &box[1],
+ s5r->dane_data_len);
+ break;
+ }
default:
/* don't care */
break;
}
if (GNUNET_YES != got_ip)
{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Name resolution failed to yield useful IP address.\n");
signal_socks_failure (s5r,
SOCKS5_STATUS_GENERAL_FAILURE);
return;
}
s5r->state = SOCKS5_DATA_TRANSFER;
- signal_socks_success (s5r);
+ signal_socks_success (s5r);
}
* Read data from incoming Socks5 connection
*
* @param cls the closure with the `struct Socks5Request`
- * @param tc the scheduler context
*/
static void
-do_s5r_read (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_s5r_read (void *cls)
{
struct Socks5Request *s5r = cls;
const struct Socks5ClientHelloMessage *c_hello;
const struct Socks5ClientRequestMessage *c_req;
ssize_t rlen;
size_t alen;
+ const struct GNUNET_SCHEDULER_TaskContext *tc;
- s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
+ s5r->rtask = NULL;
+ tc = GNUNET_SCHEDULER_get_task_context ();
if ( (NULL != tc->read_ready) &&
(GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) )
{
- rlen = GNUNET_NETWORK_socket_recv (s5r->sock,
+ rlen = GNUNET_NETWORK_socket_recv (s5r->sock,
&s5r->rbuf[s5r->rbuf_len],
sizeof (s5r->rbuf) - s5r->rbuf_len);
if (rlen <= 0)
{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"socks5 client disconnected.\n");
cleanup_s5r (s5r);
return;
s5r->sock,
&do_s5r_read, s5r);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Processing %u bytes of socks data in state %d\n",
+ "Processing %zu bytes of socks data in state %d\n",
s5r->rbuf_len,
s5r->state);
switch (s5r->state)
s5r->wbuf_len = sizeof (struct Socks5ServerHelloMessage);
s_hello->version = SOCKS_VERSION_5;
s_hello->auth_method = SOCKS_AUTH_NONE;
- GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == s5r->wtask);
+ GNUNET_assert (NULL == s5r->wtask);
s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
s5r->sock,
&do_write, s5r);
struct sockaddr_in *in;
s5r->port = ntohs (*port);
+ if (HTTPS_PORT == s5r->port)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("SSL connection to plain IPv4 address requested\n"));
+ signal_socks_failure (s5r,
+ SOCKS5_STATUS_CONNECTION_NOT_ALLOWED_BY_RULE);
+ return;
+ }
alen = sizeof (struct in_addr);
if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
alen + sizeof (uint16_t))
struct sockaddr_in6 *in;
s5r->port = ntohs (*port);
+ if (HTTPS_PORT == s5r->port)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("SSL connection to plain IPv4 address requested\n"));
+ signal_socks_failure (s5r,
+ SOCKS5_STATUS_CONNECTION_NOT_ALLOWED_BY_RULE);
+ return;
+ }
alen = sizeof (struct in6_addr);
if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
alen + sizeof (uint16_t))
{
const uint8_t *dom_len;
const char *dom_name;
- const uint16_t *port;
-
+ const uint16_t *port;
+
dom_len = (const uint8_t *) &c_req[1];
alen = *dom_len + 1;
if (s5r->rbuf_len < sizeof (struct Socks5ClientRequestMessage) +
GNUNET_NO /* only cached */,
(GNUNET_YES == do_shorten) ? &local_shorten_zone : NULL,
&handle_gns_result,
- s5r);
+ s5r);
break;
}
default:
GNUNET_break_op (0);
signal_socks_failure (s5r,
SOCKS5_STATUS_GENERAL_FAILURE);
- return;
+ return;
}
if (SOCKS5_DATA_TRANSFER == s5r->state)
{
}
/* We are done reading right now */
GNUNET_SCHEDULER_cancel (s5r->rtask);
- s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
+ s5r->rtask = NULL;
return;
case SOCKS5_RESOLVING:
GNUNET_assert (0);
/**
* Accept new incoming connections
*
- * @param cls the closure
+ * @param cls the closure with the lsock4 or lsock6
* @param tc the scheduler context
*/
static void
-do_accept (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_accept (void *cls)
{
+ struct GNUNET_NETWORK_Handle *lsock = cls;
struct GNUNET_NETWORK_Handle *s;
struct Socks5Request *s5r;
- ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- lsock,
- &do_accept, NULL);
+ if (lsock == lsock4)
+ ltask4 = NULL;
+ else
+ ltask6 = NULL;
+ if (lsock == lsock4)
+ ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ lsock,
+ &do_accept, lsock);
+ else
+ ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ lsock,
+ &do_accept, lsock);
s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
if (NULL == s)
{
* Task run on shutdown
*
* @param cls closure
- * @param tc task context
*/
static void
-do_shutdown (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_shutdown (void *cls)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Shutting down...\n");
while (NULL != mhd_httpd_head)
kill_httpd (mhd_httpd_head);
- if (NULL != lsock)
+ while (NULL != s5r_head)
+ cleanup_s5r (s5r_head);
+ if (NULL != lsock4)
{
- GNUNET_NETWORK_socket_close (lsock);
- lsock = NULL;
+ GNUNET_NETWORK_socket_close (lsock4);
+ lsock4 = NULL;
+ }
+ if (NULL != lsock6)
+ {
+ GNUNET_NETWORK_socket_close (lsock6);
+ lsock6 = NULL;
}
if (NULL != id_op)
{
GNUNET_GNS_disconnect (gns_handle);
gns_handle = NULL;
}
- if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
+ if (NULL != curl_download_task)
{
GNUNET_SCHEDULER_cancel (curl_download_task);
- curl_download_task = GNUNET_SCHEDULER_NO_TASK;
+ curl_download_task = NULL;
+ }
+ if (NULL != ltask4)
+ {
+ GNUNET_SCHEDULER_cancel (ltask4);
+ ltask4 = NULL;
}
- if (GNUNET_SCHEDULER_NO_TASK != ltask)
+ if (NULL != ltask6)
{
- GNUNET_SCHEDULER_cancel (ltask);
- ltask = GNUNET_SCHEDULER_NO_TASK;
+ GNUNET_SCHEDULER_cancel (ltask6);
+ ltask6 = NULL;
}
gnutls_x509_crt_deinit (proxy_ca.cert);
gnutls_x509_privkey_deinit (proxy_ca.key);
/**
- * Continue initialization after we have our zone information.
+ * Create an IPv4 listen socket bound to our port.
+ *
+ * @return NULL on error
*/
-static void
-run_cont ()
+static struct GNUNET_NETWORK_Handle *
+bind_v4 ()
{
- struct MhdHttpList *hd;
- struct sockaddr_in sa;
+ struct GNUNET_NETWORK_Handle *ls;
+ struct sockaddr_in sa4;
+ int eno;
- /* Open listen socket for socks proxy */
- /* FIXME: support IPv6! */
- memset (&sa, 0, sizeof (sa));
- sa.sin_family = AF_INET;
- sa.sin_port = htons (port);
+ memset (&sa4, 0, sizeof (sa4));
+ sa4.sin_family = AF_INET;
+ sa4.sin_port = htons (port);
#if HAVE_SOCKADDR_IN_SIN_LEN
- sa.sin_len = sizeof (sa);
+ sa4.sin_len = sizeof (sa4);
#endif
- lsock = GNUNET_NETWORK_socket_create (AF_INET,
- SOCK_STREAM,
- 0);
- if (NULL == lsock)
+ ls = GNUNET_NETWORK_socket_create (AF_INET,
+ SOCK_STREAM,
+ 0);
+ if (NULL == ls)
+ return NULL;
+ if (GNUNET_OK !=
+ GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa4,
+ sizeof (sa4)))
{
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
- GNUNET_SCHEDULER_shutdown ();
- return;
+ eno = errno;
+ GNUNET_NETWORK_socket_close (ls);
+ errno = eno;
+ return NULL;
}
+ return ls;
+}
+
+
+/**
+ * Create an IPv6 listen socket bound to our port.
+ *
+ * @return NULL on error
+ */
+static struct GNUNET_NETWORK_Handle *
+bind_v6 ()
+{
+ struct GNUNET_NETWORK_Handle *ls;
+ struct sockaddr_in6 sa6;
+ int eno;
+
+ memset (&sa6, 0, sizeof (sa6));
+ sa6.sin6_family = AF_INET6;
+ sa6.sin6_port = htons (port);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa6.sin6_len = sizeof (sa6);
+#endif
+ ls = GNUNET_NETWORK_socket_create (AF_INET6,
+ SOCK_STREAM,
+ 0);
+ if (NULL == ls)
+ return NULL;
if (GNUNET_OK !=
- GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
- sizeof (sa), 0))
+ GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa6,
+ sizeof (sa6)))
+ {
+ eno = errno;
+ GNUNET_NETWORK_socket_close (ls);
+ errno = eno;
+ return NULL;
+ }
+ return ls;
+}
+
+
+/**
+ * Continue initialization after we have our zone information.
+ */
+static void
+run_cont ()
+{
+ struct MhdHttpList *hd;
+
+ /* Open listen socket for socks proxy */
+ lsock6 = bind_v6 ();
+ if (NULL == lsock6)
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
+ else
{
+ if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock6, 5))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
+ GNUNET_NETWORK_socket_close (lsock6);
+ lsock6 = NULL;
+ }
+ else
+ {
+ ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ lsock6, &do_accept, lsock6);
+ }
+ }
+ lsock4 = bind_v4 ();
+ if (NULL == lsock4)
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
- GNUNET_SCHEDULER_shutdown ();
- return;
+ else
+ {
+ if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock4, 5))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
+ GNUNET_NETWORK_socket_close (lsock4);
+ lsock4 = NULL;
+ }
+ else
+ {
+ ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
+ lsock4, &do_accept, lsock4);
+ }
}
- if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
+ if ( (NULL == lsock4) &&
+ (NULL == lsock6) )
{
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
+ GNUNET_SCHEDULER_shutdown ();
return;
}
- ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
- lsock, &do_accept, NULL);
-
if (0 != curl_global_init (CURL_GLOBAL_WIN32))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Proxy listens on port %u\n",
+ "Proxy listens on port %lu\n",
port);
/* start MHD daemon for HTTP */
}
-/**
+/**
* Method called to inform about the egos of the shorten zone of this peer.
*
* When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
* @a ego does indicate an error (i.e. name is taken or no default
* value is known). If @a ego is non-NULL and if '*ctx'
* is set in those callbacks, the value WILL be passed to a subsequent
- * call to the identity callback of #GNUNET_IDENTITY_connect (if
+ * call to the identity callback of #GNUNET_IDENTITY_connect (if
* that one was not NULL).
*
* @param cls closure, NULL
}
-/**
+/**
* Method called to inform about the egos of the master zone of this peer.
*
* When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
* @a ego does indicate an error (i.e. name is taken or no default
* value is known). If @a ego is non-NULL and if '*ctx'
* is set in those callbacks, the value WILL be passed to a subsequent
- * call to the identity callback of #GNUNET_IDENTITY_connect (if
+ * call to the identity callback of #GNUNET_IDENTITY_connect (if
* that one was not NULL).
*
* @param cls closure, NULL
if (NULL == ego)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _("No ego configured for `master-zone`\n"));
+ _("No ego configured for `%s`\n"),
+ "gns-proxy");
GNUNET_SCHEDULER_shutdown ();
return;
}
GNUNET_IDENTITY_ego_get_public_key (ego,
&local_gns_zone);
id_op = GNUNET_IDENTITY_get (identity,
- "shorten-zone",
+ "gns-short",
&identity_shorten_cb,
NULL);
}
char* cafile;
cfg = c;
+
if (NULL == (curl_multi = curl_multi_init ()))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to create cURL multi handle!\n");
return;
- }
+ }
cafile = cafile_opt;
if (NULL == cafile)
{
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Using %s as CA\n", cafile);
-
+
gnutls_global_init ();
gnutls_x509_crt_init (&proxy_ca.cert);
gnutls_x509_privkey_init (&proxy_ca.key);
-
+
if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
(GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
{
gnutls_x509_crt_deinit (proxy_ca.cert);
gnutls_x509_privkey_deinit (proxy_ca.key);
gnutls_global_deinit ();
- GNUNET_free_non_null (cafile_cfg);
+ GNUNET_free_non_null (cafile_cfg);
return;
}
GNUNET_free_non_null (cafile_cfg);
return;
}
identity = GNUNET_IDENTITY_connect (cfg,
- NULL, NULL);
+ NULL, NULL);
id_op = GNUNET_IDENTITY_get (identity,
- "master-zone",
+ "gns-proxy",
&identity_master_cb,
- NULL);
- GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
- &do_shutdown, NULL);
+ NULL);
+ GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
}
&GNUNET_GETOPT_set_string, &cafile_opt},
GNUNET_GETOPT_OPTION_END
};
- static const char* page =
+ static const char* page =
"<html><head><title>gnunet-gns-proxy</title>"
"</head><body>cURL fail</body></html>";
int ret;
&run, NULL)) ? 0 : 1;
MHD_destroy_response (curl_failure_response);
GNUNET_free_non_null ((char *) argv);
+ GNUNET_CRYPTO_ecdsa_key_clear (&local_shorten_zone);
return ret;
}