/*
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
*/
#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>
/**
* The task ID
*/
- GNUNET_SCHEDULER_TaskIdentifier httpd_task;
+ struct GNUNET_SCHEDULER_Task *httpd_task;
/**
* is this an ssl daemon?
/**
* Client socket read task
*/
- GNUNET_SCHEDULER_TaskIdentifier rtask;
+ struct GNUNET_SCHEDULER_Task * rtask;
/**
* Client socket write task
*/
- GNUNET_SCHEDULER_TaskIdentifier wtask;
+ struct GNUNET_SCHEDULER_Task * wtask;
/**
* Timeout task
*/
- GNUNET_SCHEDULER_TaskIdentifier timeout_task;
+ struct GNUNET_SCHEDULER_Task * timeout_task;
/**
* Read buffer
*/
struct curl_slist *headers;
+ /**
+ * DNS->IP mappings resolved through GNS
+ */
+ struct curl_slist *hosts;
+
/**
* HTTP response code to give to MHD for the response.
*/
/**
* The listen task ID for IPv4
*/
-static GNUNET_SCHEDULER_TaskIdentifier ltask4;
+static struct GNUNET_SCHEDULER_Task * ltask4;
/**
* The listen task ID for IPv6
*/
-static GNUNET_SCHEDULER_TaskIdentifier ltask6;
+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
s5r->curl = NULL;
}
curl_slist_free_all (s5r->headers);
+ 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);
static int
check_ssl_certificate (struct Socks5Request *s5r)
{
- struct curl_tlsinfo tlsinfo;
unsigned int cert_list_size;
const gnutls_datum_t *chainp;
- union {
- struct curl_tlsinfo *tlsinfo;
- struct curl_slist *to_slist;
- } gptr;
+ 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;
- memset (&tlsinfo, 0, sizeof (tlsinfo));
- gptr.tlsinfo = &tlsinfo;
if (CURLE_OK !=
curl_easy_getinfo (s5r->curl,
CURLINFO_TLS_SESSION,
- &gptr))
+ (struct curl_slist **) &tlsinfo))
return GNUNET_SYSERR;
- if (CURLSSLBACKEND_GNUTLS != tlsinfo.ssl_backend)
+ if (CURLSSLBACKEND_GNUTLS != tlsinfo->backend)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Unsupported CURL SSL backend %d\n"),
- tlsinfo.ssl_backend);
+ tlsinfo->backend);
return GNUNET_SYSERR;
}
- chainp = gnutls_certificate_get_peers (tlsinfo.internals, &cert_list_size);
+ chainp = gnutls_certificate_get_peers (tlsinfo->internals, &cert_list_size);
if ( (! chainp) || (0 == cert_list_size) )
return GNUNET_SYSERR;
if (0 != (rc = dane_verify_crt_raw (dane_state,
chainp,
cert_list_size,
- gnutls_certificate_type_get (tlsinfo.internals),
+ gnutls_certificate_type_get (tlsinfo->internals),
dane_query,
0, 0,
&verify)))
GNUNET_free (cors_hdr);
}
/* force connection to be closed after each request, as we
- do not support HTTP pipelining */
+ do not support HTTP pipelining (yet, FIXME!) */
GNUNET_break (MHD_YES ==
MHD_add_response_header (s5r->response,
MHD_HTTP_HEADER_CONNECTION,
* 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);
* 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;
GNUNET_break (CURLE_OK ==
curl_easy_getinfo (msg->easy_handle,
CURLINFO_PRIVATE,
- &s5r));
+ (char **) &s5r ));
if (NULL == s5r)
{
GNUNET_break (0);
{
struct Socks5Request *s5r = *con_cls;
char *curlurl;
+ char *curl_hosts;
char ipstring[INET6_ADDRSTRLEN];
char ipaddr[INET6_ADDRSTRLEN + 2];
const struct sockaddr *sa;
curl_easy_setopt (s5r->curl, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt (s5r->curl, CURLOPT_PRIVATE, s5r);
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,
+ (NULL != s5r->leho)
+ ? s5r->leho
+ : ipaddr,
port,
s5r->url);
curl_easy_setopt (s5r->curl,
* @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
"MHD encountered error handling request: %d\n",
toe);
cleanup_s5r (s5r);
+ curl_download_prepare();
*con_cls = NULL;
}
}
s5r->url = GNUNET_strdup (url);
GNUNET_SCHEDULER_cancel (s5r->timeout_task);
- s5r->timeout_task = GNUNET_SCHEDULER_NO_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 = GNUNET_SCHEDULER_NO_TASK;
+ hd->httpd_task = NULL;
}
GNUNET_free_non_null (hd->proxy_cert);
if (hd == httpd)
* 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 = GNUNET_SCHEDULER_NO_TASK;
+ 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,
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,
* 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 ( (NULL != hd->domain) &&
(0 == strcmp (hd->domain, domain)) )
* 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);
}
* 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);
GNUNET_assert (0);
break;
case SOCKS5_REQUEST:
- GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s5r->rtask);
+ 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)
+ if (NULL != s5r->wtask)
s5r->wtask =
GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
s5r->sock,
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)
+ if (NULL == s5r->wtask)
s5r->wtask =
GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
s5r->sock,
/**
* Process GNS results for target domain.
*
- * @param cls the `struct Socks5Request`
+ * @param cls the `struct Socks5Request *`
* @param rd_count number of records returned
* @param rd record data
*/
s5r->leho = GNUNET_strndup (r->data,
r->data_size);
break;
- case GNUNET_DNSPARSER_TYPE_TLSA:
- GNUNET_free_non_null (s5r->dane_data);
- s5r->dane_data_len = r->data_size;
- s5r->dane_data = GNUNET_malloc (r->data_size);
- memcpy (s5r->dane_data,
- 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;
* 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)) )
{
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))
}
/* 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);
* @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;
if (lsock == lsock4)
- ltask4 = GNUNET_SCHEDULER_NO_TASK;
+ ltask4 = NULL;
else
- ltask6 = GNUNET_SCHEDULER_NO_TASK;
- if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
- return;
+ ltask6 = NULL;
if (lsock == lsock4)
ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
lsock,
* 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");
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 (GNUNET_SCHEDULER_NO_TASK != ltask4)
+ if (NULL != ltask4)
{
GNUNET_SCHEDULER_cancel (ltask4);
- ltask4 = GNUNET_SCHEDULER_NO_TASK;
+ ltask4 = NULL;
}
- if (GNUNET_SCHEDULER_NO_TASK != ltask6)
+ if (NULL != ltask6)
{
GNUNET_SCHEDULER_cancel (ltask6);
- ltask6 = GNUNET_SCHEDULER_NO_TASK;
+ ltask6 = NULL;
}
gnutls_x509_crt_deinit (proxy_ca.cert);
gnutls_x509_privkey_deinit (proxy_ca.key);
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 */
"gns-proxy",
&identity_master_cb,
NULL);
- GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
- &do_shutdown, NULL);
+ GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
}