/*
This file is part of GNUnet.
- (C) 2012 Christian Grothoff (and other contributing authors)
+ (C) 2012-2013 Christian Grothoff (and other contributing authors)
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
+/**
+ * @author Martin Schanzenbach
+ * @file src/gns/gnunet-gns-proxy.c
+ * @brief HTTP(S) proxy that rewrites URIs and fakes certificats to make GNS work
+ * with legacy browsers
+ */
#include "platform.h"
#include <microhttpd.h>
#include <curl/curl.h>
#include "gns_proxy_proto.h"
#include "gns.h"
-#define HAVE_MHD_NO_LISTEN_SOCKET MHD_VERSION >= 0x00091401
+#define HAVE_MHD_NO_LISTEN_SOCKET (MHD_VERSION >= 0x00091401)
#define GNUNET_GNS_PROXY_PORT 7777
#define MHD_MAX_CONNECTIONS 300
#endif
/* Shorten zone private key */
-static struct GNUNET_CRYPTO_RsaPrivateKey *shorten_zonekey;
+static struct GNUNET_CRYPTO_EccPrivateKey *shorten_zonekey;
/**
}
-/**
- * convert integer to string representation
- *
- * @param i integer
- * @return the character
- */
-static char
-i_to_hexchar (unsigned char i)
-{
- static char hexmap[] = "0123456789abcdef";
-
- GNUNET_assert (sizeof (hexmap) > (i & 15));
- return hexmap[i & 15];
-}
-
-
-/**
-// FIXME: use cURL API
- * Escape given 0-terminated string
- *
- * @param to_esc string to escapse
- * @return allocated new escaped string (MUST free!)
- */
-static char*
-escape_to_urlenc (const char *to_esc)
-{
- char *pos = (char*)to_esc;
- char *res = GNUNET_malloc (strlen (to_esc) * 3 + 1);
- char *rpos = res;
-
- while ('\0' != *pos)
- {
- if (isalnum (*pos) ||
- ('-' == *pos) || ('_' == *pos) ||
- ('.' == *pos) || ('~' == *pos))
- *rpos++ = *pos;
- else if (' ' == *pos)
- *rpos++ = '+';
- else
- {
- *rpos++ = '%';
- *rpos++ = i_to_hexchar (*pos >> 4);
- *rpos++ = i_to_hexchar (*pos >> 15);
- }
- pos++;
- }
- *rpos = '\0';
- return res;
-}
-
-
static int
con_post_data_iter (void *cls,
enum MHD_ValueKind kind,
if (0 == off)
{
+ enc = curl_easy_escape (ctask->curl, key, 0);
+ if (NULL == enc)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
/* a key */
pdata = GNUNET_malloc (sizeof (struct ProxyUploadData));
- enc = escape_to_urlenc (key);
pdata->value = GNUNET_malloc (strlen (enc) + 3);
if (NULL != ctask->upload_data_head)
{
pdata->value[strlen (pdata->value)] = '=';
pdata->bytes_left = strlen (pdata->value);
pdata->total_bytes = pdata->bytes_left;
- GNUNET_free (enc);
+ curl_free (enc);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Escaped POST key: '%s'\n",
}
/* a value */
+ enc = curl_easy_escape (ctask->curl, data, 0);
+ if (NULL == enc)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
pdata = GNUNET_malloc (sizeof (struct ProxyUploadData));
- enc = escape_to_urlenc (data);
pdata->value = GNUNET_malloc (strlen (enc) + 1);
memcpy (pdata->value, enc, strlen (enc));
pdata->bytes_left = strlen (pdata->value);
pdata->total_bytes = pdata->bytes_left;
- GNUNET_free (enc);
+ curl_free (enc);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Escaped POST value: '%s'\n",
char* buf,
size_t max);
+
/**
* Check HTTP response header for mime
*
MHD_HTTP_HEADER_SET_COOKIE,
cookie_hdr_len))
{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Looking for cookie in: `%s'\n", hdr_generic);
ndup = GNUNET_strdup (hdr_generic+cookie_hdr_len+1);
memset (new_cookie_hdr, 0, sizeof (new_cookie_hdr));
- tok = strtok (ndup, ";");
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Looking for cookie in : %s\n", hdr_generic);
-
- for (; tok != NULL; tok = strtok (NULL, ";"))
+ for (tok = strtok (ndup, ";"); tok != NULL; tok = strtok (NULL, ";"))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Got Cookie token: %s\n", tok);
return bytes;
}
+
/**
* schedule mhd
*
static void
run_httpds (void);
+
/**
* Task run whenever HTTP server operations are pending.
*
do_httpd (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc);
+
static void
run_mhd_now (struct MhdHttpList *hd)
{
hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, hd);
}
+
/**
* Ask cURL for the select sets and schedule download
*/
static void
curl_download_prepare (void);
+
/**
* Callback to free content
*
}
if (bytes_to_copy + copied > max)
- bytes_to_copy = max-copied;
-
- if (0 > bytes_to_copy)
- bytes_to_copy = 0;
-
+ bytes_to_copy = max - copied;
memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
ctask->buffer_read_ptr += bytes_to_copy;
copied += bytes_to_copy;
run_mhd_now (ctask->mhd);
}
+
/**
* Handle data from cURL
*
return to_copy;
}
+
/**
* Task that is run when we are ready to receive more data
* from curl
static void
curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
/**
* Ask cURL for the select sets and schedule download
*/
FD_ZERO (&rs);
FD_ZERO (&ws);
FD_ZERO (&es);
- mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
-
- if (mret != CURLM_OK)
+ if (CURLM_OK != (mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"%s failed at %s:%d: `%s'\n",
//TODO cleanup here?
return;
}
-
- mret = curl_multi_timeout (curl_multi, &to);
- rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
-
+ to = -1;
+ GNUNET_break (CURLM_OK == curl_multi_timeout (curl_multi, &to));
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "cURL multi fds: max=%d timeout=%llu\n", max, to);
-
+ "cURL multi fds: max=%d timeout=%lld\n", max, (long long) to);
+ if (-1 == to)
+ rtime = GNUNET_TIME_UNIT_FOREVER_REL;
+ else
+ rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
grs = GNUNET_NETWORK_fdset_create ();
gws = GNUNET_NETWORK_fdset_create ();
GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Scheduling task cURL\n");
-
if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
- GNUNET_SCHEDULER_cancel (curl_download_task);
-
+ GNUNET_SCHEDULER_cancel (curl_download_task);
if (-1 != max)
{
curl_download_task =
struct ProxyCurlTask *ctask;
int num_ctasks;
long resp_code;
-
struct ProxyCurlTask *clean_head = NULL;
struct ProxyCurlTask *clean_tail = NULL;
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Shutdown requested while trying to download\n");
- //TODO cleanup
- return;
+ //TODO cleanup
+ return;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Ready to dl\n");
for (i=0; i<rd_count; i++)
{
- if (rd[i].record_type != GNUNET_GNS_RECORD_LEHO)
+ if (rd[i].record_type != GNUNET_NAMESTORE_TYPE_LEHO)
continue;
memcpy (ctask->leho, rd[i].data, rd[i].data_size);
}
+
/**
* Initialize download and trigger curl
*
GNUNET_GNS_lookup_zone (gns_handle,
ctask->host,
local_gns_zone,
- GNUNET_GNS_RECORD_LEHO,
+ GNUNET_NAMESTORE_TYPE_LEHO,
GNUNET_YES, //Only cached for performance
shorten_zonekey,
&process_leho_lookup,
ctask);
}
+
static void*
mhd_log_callback (void* cls, const char* url)
{
haveto = MHD_get_timeout (hd->daemon, &timeout);
if (MHD_YES == haveto)
- tv.rel_value = (uint64_t) timeout;
+ tv.rel_value_us = (uint64_t) timeout * 1000LL;
else
tv = GNUNET_TIME_UNIT_FOREVER_REL;
GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
}
-
/**
* Read data from socket
*
static void
do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
/**
* Read from remote end
*
static void
do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
/**
* Write data to remote socket
*
GNUNET_free(s5r);
}
+
/**
* Write data to socket
*
len);
}
else
- {
-
+ {
GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
s5r->cleanup = GNUNET_YES;
s5r->cleanup_sock = GNUNET_YES;
- cleanup_s5r (s5r);
-
+ cleanup_s5r (s5r);
return;
}
&do_read_remote, s5r);
}
+
/**
* Read from remote end
*
struct Socks5Request *s5r = cls;
s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
-
-
if ((NULL != tc->write_ready) &&
(GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
(s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
}
else
{
- if (s5r->wbuf_len == 0)
+ if (0 == s5r->wbuf_len)
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"0 bytes received from remote... graceful shutdown!\n");
if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
s5r->sock,
- &do_write, s5r);
-
+ &do_write, s5r);
}
return MHD_add_connection (daemon, fd, addr, len);
}
+
/**
* Read file in filename
*
}
-/*
+/**
* Accept policy for mhdaemons
*
* @param cls NULL
* @param addr the sockaddr
* @param addrlen the sockaddr length
- * @return MHD_NO if sockaddr is wrong or #conns too high
+ * @return MHD_NO if sockaddr is wrong or number of connections is too high
*/
static int
accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen)
static int
add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, const char* domain)
{
- struct MhdHttpList *hd = NULL;
+ struct MhdHttpList *hd;
struct ProxyGNSCertificate *pgc;
struct NetworkHandleList *nh;
MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
NULL,
MHD_OPTION_END);
-
+#endif
GNUNET_assert (hd->daemon != NULL);
hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
}
-
/**
* Read data from incoming connection
*
struct socks5_server_hello *s_hello;
struct socks5_client_request *c_req;
struct socks5_server_response *s_resp;
-
int ret;
char domain[256];
uint8_t dom_len;
uint32_t remote_ip;
struct sockaddr_in remote_addr;
struct in_addr *r_sin_addr;
-
struct NetworkHandleList *nh;
s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
s5r->remote_sock,
&do_read_remote, s5r);
}
-
-
}
-
}
do_shutdown (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
-
struct MhdHttpList *hd;
struct MhdHttpList *tmp_hd;
struct NetworkHandleList *nh;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Shutting down...\n");
-
- gnutls_global_deinit ();
-
if (NULL != local_gns_zone)
GNUNET_free (local_gns_zone);
if (NULL != local_private_zone)
GNUNET_SCHEDULER_cancel (hd->httpd_task);
hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
}
-
if (NULL != hd->daemon)
{
MHD_stop_daemon (hd->daemon);
hd->daemon = NULL;
}
-
for (nh = hd->socket_handles_head; nh != NULL; nh = tmp_nh)
{
tmp_nh = nh->next;
GNUNET_free_non_null (pdata->value);
GNUNET_free (pdata);
}
-
GNUNET_free (ctask);
}
curl_multi_cleanup (curl_multi);
-
GNUNET_GNS_disconnect (gns_handle);
+ gnutls_global_deinit ();
}
load_local_zone_key (const struct GNUNET_CONFIGURATION_Handle *cfg)
{
char *keyfile;
- struct GNUNET_CRYPTO_RsaPrivateKey *key = NULL;
- struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
- struct GNUNET_CRYPTO_ShortHashCode *zone = NULL;
+ struct GNUNET_CRYPTO_EccPrivateKey *key;
+ struct GNUNET_CRYPTO_EccPublicKey pkey;
+ struct GNUNET_CRYPTO_ShortHashCode *zone;
struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename;
if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
return GNUNET_NO;
}
- key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
- GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
+ key = GNUNET_CRYPTO_ecc_key_create_from_file (keyfile);
+ GNUNET_CRYPTO_ecc_key_get_public (key, &pkey);
local_gns_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
- GNUNET_CRYPTO_short_hash(&pkey,
- sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
- local_gns_zone);
+ GNUNET_CRYPTO_short_hash (&pkey,
+ sizeof (struct GNUNET_CRYPTO_EccPublicKey),
+ local_gns_zone);
zone = local_gns_zone;
GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Using zone: %s!\n", &zonename);
- GNUNET_CRYPTO_rsa_key_free(key);
+ GNUNET_free(key);
GNUNET_free(keyfile);
keyfile = NULL;
}
else
{
- key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
- GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
+ key = GNUNET_CRYPTO_ecc_key_create_from_file (keyfile);
+ GNUNET_CRYPTO_ecc_key_get_public (key, &pkey);
local_private_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
- GNUNET_CRYPTO_short_hash(&pkey,
- sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
- local_private_zone);
+ GNUNET_CRYPTO_short_hash (&pkey,
+ sizeof (struct GNUNET_CRYPTO_EccPublicKey),
+ local_private_zone);
GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Using private zone: %s!\n", &zonename);
- GNUNET_CRYPTO_rsa_key_free(key);
+ GNUNET_free(key);
GNUNET_free(keyfile);
}
keyfile = NULL;
}
else
{
- key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
- GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
+ key = GNUNET_CRYPTO_ecc_key_create_from_file (keyfile);
+ GNUNET_CRYPTO_ecc_key_get_public (key, &pkey);
local_shorten_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
- GNUNET_CRYPTO_short_hash(&pkey,
- sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
- local_shorten_zone);
+ GNUNET_CRYPTO_short_hash (&pkey,
+ sizeof(struct GNUNET_CRYPTO_EccPublicKey),
+ local_shorten_zone);
GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Using shorten zone: %s!\n", &zonename);
- GNUNET_CRYPTO_rsa_key_free(key);
+ GNUNET_free(key);
GNUNET_free(keyfile);
}
return GNUNET_YES;
}
+
/**
* Main function that will be run
*
struct sockaddr_un mhd_unix_sock_addr;
#endif
- curl_multi = curl_multi_init ();
-
- if (NULL == curl_multi)
+ if (NULL == (curl_multi = curl_multi_init ()))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to create cURL multo handle!\n");
if (NULL == cafile)
{
if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
- "PROXY_CACERT",
- &cafile_cfg))
+ "PROXY_CACERT",
+ &cafile_cfg))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unable to load proxy CA config value!\n");
compile_regex (&re_dotplus, (char*) RE_A_HREF);
- gns_handle = GNUNET_GNS_connect (cfg);
-
- if (GNUNET_NO == load_local_zone_key (cfg))
+ if (NULL == (gns_handle = GNUNET_GNS_connect (cfg)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unable to load zone!\n");
+ "Unable to connect to GNS!\n");
return;
}
-
- if (NULL == gns_handle)
+ if (GNUNET_NO == load_local_zone_key (cfg))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unable to connect to GNS!\n");
+ "Unable to load zone!\n");
return;
}
if ((NULL == lsock) ||
(GNUNET_OK !=
GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
- sizeof (sa))))
+ sizeof (sa), 0)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to create listen socket bound to `%s'",
GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
return;
}
-
ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
lsock, &do_accept, NULL);
- ctasks_head = NULL;
- ctasks_tail = NULL;
-
if (0 != curl_global_init (CURL_GLOBAL_WIN32))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Proxy listens on port %u\n",
port);
-
- mhd_httpd_head = NULL;
- mhd_httpd_tail = NULL;
- total_mhd_connections = 0;
#if ! HAVE_MHD_NO_LISTEN_SOCKET
if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
"PROXY_UNIXPATH",
"Specify PROXY_UNIXPATH in gns-proxy config section!\n");
return;
}
-
- mhd_unix_socket = GNUNET_NETWORK_socket_create (AF_UNIX,
- SOCK_STREAM,
- 0);
-
- if (NULL == mhd_unix_socket)
+ if (NULL == (mhd_unix_socket = GNUNET_NETWORK_socket_create (AF_UNIX,
+ SOCK_STREAM,
+ 0)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unable to create unix domain socket!\n");
#endif
len = strlen (proxy_sockfile) + sizeof(AF_UNIX);
-
GNUNET_free (proxy_sockfile);
if (GNUNET_OK != GNUNET_NETWORK_socket_bind (mhd_unix_socket,
(struct sockaddr*)&mhd_unix_sock_addr,
- len))
+ len, 0))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unable to bind unix domain socket!\n");
MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
MHD_OPTION_END);
#endif
-
- GNUNET_assert (httpd != NULL);
+ GNUNET_break (httpd != NULL);
hd->daemon = httpd;
hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
-
GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
-
run_httpds ();
-
GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
&do_shutdown, NULL);
-
}
&GNUNET_GETOPT_set_string, &cafile_opt},
GNUNET_GETOPT_OPTION_END
};
-
int ret;
if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
return 2;
-
-
GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
ret =
(GNUNET_OK ==
_("GNUnet GNS proxy"),
options,
&run, NULL)) ? 0 : 1;
- GNUNET_free_non_null ((char*)argv);
-
+ GNUNET_free_non_null ((char *) argv);
return ret;
}
-
-
-
+/* end of gnunet-gns-proxy.c */