aboutsummaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2013-08-15 12:04:56 -0400
committerNick Mathewson <nickm@torproject.org>2013-08-15 12:04:56 -0400
commit74262f157178071ad1bce8e70aae79dc01f3ba5d (patch)
treeb1101d4cf3fed95575c9cfc6e3af4452c6d2a8fe /src/or
parent4ab1012b0f5c328f9b70146867e9a2628d06c072 (diff)
parentc5269a59b011c8e961c7e88185b84e78af33d904 (diff)
downloadtor-74262f157178071ad1bce8e70aae79dc01f3ba5d.tar
tor-74262f157178071ad1bce8e70aae79dc01f3ba5d.tar.gz
Merge branch 'bug5040_4773_rebase_3'
Diffstat (limited to 'src/or')
-rw-r--r--src/or/buffers.c59
-rw-r--r--src/or/buffers.h8
-rw-r--r--src/or/channel.c8
-rw-r--r--src/or/channel.h2
-rw-r--r--src/or/channeltls.c27
-rw-r--r--src/or/config.c103
-rw-r--r--src/or/config.h4
-rw-r--r--src/or/connection.c65
-rw-r--r--src/or/connection.h10
-rw-r--r--src/or/connection_or.c82
-rw-r--r--src/or/connection_or.h3
-rw-r--r--src/or/control.c55
-rw-r--r--src/or/control.h6
-rw-r--r--src/or/directory.c4
-rw-r--r--src/or/ext_orport.c648
-rw-r--r--src/or/ext_orport.h42
-rw-r--r--src/or/geoip.c168
-rw-r--r--src/or/geoip.h4
-rw-r--r--src/or/include.am2
-rw-r--r--src/or/main.c42
-rw-r--r--src/or/main.h13
-rw-r--r--src/or/or.h63
-rw-r--r--src/or/transports.c22
23 files changed, 1351 insertions, 89 deletions
diff --git a/src/or/buffers.c b/src/or/buffers.c
index cc5890416..50016d3a8 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -19,6 +19,7 @@
#include "connection_or.h"
#include "control.h"
#include "reasons.h"
+#include "ext_orport.h"
#include "../common/util.h"
#include "../common/torlog.h"
#ifdef HAVE_UNISTD_H
@@ -1702,6 +1703,64 @@ fetch_from_evbuffer_socks(struct evbuffer *buf, socks_request_t *req,
}
#endif
+/** The size of the header of an Extended ORPort message: 2 bytes for
+ * COMMAND, 2 bytes for BODYLEN */
+#define EXT_OR_CMD_HEADER_SIZE 4
+
+/** Read <b>buf</b>, which should contain an Extended ORPort message
+ * from a transport proxy. If well-formed, create and populate
+ * <b>out</b> with the Extended ORport message. Return 0 if the
+ * buffer was incomplete, 1 if it was well-formed and -1 if we
+ * encountered an error while parsing it. */
+int
+fetch_ext_or_command_from_buf(buf_t *buf, ext_or_cmd_t **out)
+{
+ char hdr[EXT_OR_CMD_HEADER_SIZE];
+ uint16_t len;
+
+ check();
+ if (buf->datalen < EXT_OR_CMD_HEADER_SIZE)
+ return 0;
+ peek_from_buf(hdr, sizeof(hdr), buf);
+ len = ntohs(get_uint16(hdr+2));
+ if (buf->datalen < (unsigned)len + EXT_OR_CMD_HEADER_SIZE)
+ return 0;
+ *out = ext_or_cmd_new(len);
+ (*out)->cmd = ntohs(get_uint16(hdr));
+ (*out)->len = len;
+ buf_remove_from_front(buf, EXT_OR_CMD_HEADER_SIZE);
+ fetch_from_buf((*out)->body, len, buf);
+ return 1;
+}
+
+#ifdef USE_BUFFEREVENTS
+/** Read <b>buf</b>, which should contain an Extended ORPort message
+ * from a transport proxy. If well-formed, create and populate
+ * <b>out</b> with the Extended ORport message. Return 0 if the
+ * buffer was incomplete, 1 if it was well-formed and -1 if we
+ * encountered an error while parsing it. */
+int
+fetch_ext_or_command_from_evbuffer(struct evbuffer *buf, ext_or_cmd_t **out)
+{
+ char hdr[EXT_OR_CMD_HEADER_SIZE];
+ uint16_t len;
+ size_t buf_len = evbuffer_get_length(buf);
+
+ if (buf_len < EXT_OR_CMD_HEADER_SIZE)
+ return 0;
+ evbuffer_copyout(buf, hdr, EXT_OR_CMD_HEADER_SIZE);
+ len = ntohs(get_uint16(hdr+2));
+ if (buf_len < (unsigned)len + EXT_OR_CMD_HEADER_SIZE)
+ return 0;
+ *out = ext_or_cmd_new(len);
+ (*out)->cmd = ntohs(get_uint16(hdr));
+ (*out)->len = len;
+ evbuffer_drain(buf, EXT_OR_CMD_HEADER_SIZE);
+ evbuffer_remove(buf, (*out)->body, len);
+ return 1;
+}
+#endif
+
/** Implementation helper to implement fetch_from_*_socks. Instead of looking
* at a buffer's contents, we look at the <b>datalen</b> bytes of data in
* <b>data</b>. Instead of removing data from the buffer, we set
diff --git a/src/or/buffers.h b/src/or/buffers.h
index 910494a87..48b118520 100644
--- a/src/or/buffers.h
+++ b/src/or/buffers.h
@@ -53,6 +53,8 @@ int fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len);
int peek_buf_has_control0_command(buf_t *buf);
+int fetch_ext_or_command_from_buf(buf_t *buf, ext_or_cmd_t **out);
+
#ifdef USE_BUFFEREVENTS
int fetch_var_cell_from_evbuffer(struct evbuffer *buf, var_cell_t **out,
int linkproto);
@@ -68,6 +70,8 @@ int peek_evbuffer_has_control0_command(struct evbuffer *buf);
int write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
const char *data, size_t data_len,
int done);
+int fetch_ext_or_command_from_evbuffer(struct evbuffer *buf,
+ ext_or_cmd_t **out);
#endif
#ifdef USE_BUFFEREVENTS
@@ -77,6 +81,8 @@ int write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
#define generic_buffer_get(b,buf,buflen) evbuffer_remove((b),(buf),(buflen))
#define generic_buffer_clear(b) evbuffer_drain((b), evbuffer_get_length((b)))
#define generic_buffer_free(b) evbuffer_free((b))
+#define generic_buffer_fetch_ext_or_cmd(b, out) \
+ fetch_ext_or_command_from_evbuffer((b), (out))
#else
#define generic_buffer_new() buf_new()
#define generic_buffer_len(b) buf_datalen((b))
@@ -84,6 +90,8 @@ int write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
#define generic_buffer_get(b,buf,buflen) fetch_from_buf((buf),(buflen),(b))
#define generic_buffer_clear(b) buf_clear((b))
#define generic_buffer_free(b) buf_free((b))
+#define generic_buffer_fetch_ext_or_cmd(b, out) \
+ fetch_ext_or_command_from_buf((b), (out))
#endif
int generic_buffer_set_to_copy(generic_buffer_t **output,
const generic_buffer_t *input);
diff --git a/src/or/channel.c b/src/or/channel.c
index 05f269b8f..48bbf7902 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -2379,8 +2379,14 @@ channel_do_open_actions(channel_t *chan)
/* only report it to the geoip module if it's not a known router */
if (!router_get_by_id_digest(chan->identity_digest)) {
if (channel_get_addr_if_possible(chan, &remote_addr)) {
- geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &remote_addr,
+ char *transport_name = NULL;
+ if (chan->get_transport_name(chan, &transport_name) < 0)
+ transport_name = NULL;
+
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT,
+ &remote_addr, transport_name,
now);
+ tor_free(transport_name);
}
/* Otherwise the underlying transport can't tell us this, so skip it */
}
diff --git a/src/or/channel.h b/src/or/channel.h
index 83d7e900f..bd99ebc93 100644
--- a/src/or/channel.h
+++ b/src/or/channel.h
@@ -84,6 +84,8 @@ struct channel_s {
* available.
*/
int (*get_remote_addr)(channel_t *, tor_addr_t *);
+ int (*get_transport_name)(channel_t *chan, char **transport_out);
+
#define GRD_FLAG_ORIGINAL 1
#define GRD_FLAG_ADDR_ONLY 2
/*
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index d758d22d8..7303c5a72 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -55,6 +55,8 @@ static void channel_tls_close_method(channel_t *chan);
static const char * channel_tls_describe_transport_method(channel_t *chan);
static int
channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out);
+static int
+channel_tls_get_transport_name_method(channel_t *chan, char **transport_out);
static const char *
channel_tls_get_remote_descr_method(channel_t *chan, int flags);
static int channel_tls_has_queued_writes_method(channel_t *chan);
@@ -114,6 +116,7 @@ channel_tls_common_init(channel_tls_t *tlschan)
chan->describe_transport = channel_tls_describe_transport_method;
chan->get_remote_addr = channel_tls_get_remote_addr_method;
chan->get_remote_descr = channel_tls_get_remote_descr_method;
+ chan->get_transport_name = channel_tls_get_transport_name_method;
chan->has_queued_writes = channel_tls_has_queued_writes_method;
chan->is_canonical = channel_tls_is_canonical_method;
chan->matches_extend_info = channel_tls_matches_extend_info_method;
@@ -406,6 +409,30 @@ channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out)
}
/**
+ * Get the name of the pluggable transport used by a channel_tls_t.
+ *
+ * This implements the get_transport_name for channel_tls_t. If the
+ * channel uses a pluggable transport, copy its name to
+ * <b>transport_out</b> and return 0. If the channel did not use a
+ * pluggable transport, return -1. */
+
+static int
+channel_tls_get_transport_name_method(channel_t *chan, char **transport_out)
+{
+ channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan);
+
+ tor_assert(tlschan);
+ tor_assert(transport_out);
+ tor_assert(tlschan->conn);
+
+ if (!tlschan->conn->ext_or_transport)
+ return -1;
+
+ *transport_out = tor_strdup(tlschan->conn->ext_or_transport);
+ return 0;
+}
+
+/**
* Get endpoint description of a channel_tls_t
*
* This implements the get_remote_descr method for channel_tls_t; it returns
diff --git a/src/or/config.c b/src/or/config.c
index 5e2de0792..e746da220 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -45,6 +45,7 @@
#include "routerset.h"
#include "statefile.h"
#include "transports.h"
+#include "ext_orport.h"
#ifdef _WIN32
#include <shlobj.h>
#endif
@@ -230,6 +231,7 @@ static config_var_t option_vars_[] = {
V(ExitPolicyRejectPrivate, BOOL, "1"),
V(ExitPortStatistics, BOOL, "0"),
V(ExtendAllowPrivateAddresses, BOOL, "0"),
+ VPORT(ExtORPort, LINELIST, NULL),
V(ExtraInfoStatistics, BOOL, "1"),
V(FallbackDir, LINELIST, NULL),
@@ -1473,8 +1475,14 @@ options_act(const or_options_t *old_options)
return -1;
}
- if (init_cookie_authentication(options->CookieAuthentication) < 0) {
- log_warn(LD_CONFIG,"Error creating cookie authentication file.");
+ if (init_control_cookie_authentication(options->CookieAuthentication) < 0) {
+ log_warn(LD_CONFIG,"Error creating control cookie authentication file.");
+ return -1;
+ }
+
+ /* If we have an ExtORPort, initialize its auth cookie. */
+ if (init_ext_or_cookie_authentication(!!options->ExtORPort_lines) < 0) {
+ log_warn(LD_CONFIG,"Error creating Extended ORPort cookie file.");
return -1;
}
@@ -5093,6 +5101,27 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname,
} SMARTLIST_FOREACH_END(port);
}
+/** Warn for every Extended ORPort port in <b>ports</b> that is on a
+ * publicly routable address. */
+static void
+warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname)
+{
+ SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
+ if (port->type != CONN_TYPE_EXT_OR_LISTENER)
+ continue;
+ if (port->is_unix_addr)
+ continue;
+ /* XXX maybe warn even if address is RFC1918? */
+ if (!tor_addr_is_internal(&port->addr, 1)) {
+ log_warn(LD_CONFIG, "You specified a public address '%s' for %sPort. "
+ "This is not advised; this address is supposed to only be "
+ "exposed on localhost so that your pluggable transport "
+ "proxies can connect to it.",
+ fmt_addrport(&port->addr, port->port), portname);
+ }
+ } SMARTLIST_FOREACH_END(port);
+}
+
/** Given a list of port_cfg_t in <b>ports</b>, warn any controller port there
* is listening on any non-loopback address. If <b>forbid</b> is true,
* then emit a stronger warning and remove the port from the list.
@@ -5193,6 +5222,7 @@ parse_port_config(smartlist_t *out,
smartlist_t *elts;
int retval = -1;
const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER);
+ const unsigned is_ext_orport = (listener_type == CONN_TYPE_EXT_OR_LISTENER);
const unsigned allow_no_options = flags & CL_PORT_NO_OPTIONS;
const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS;
const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL;
@@ -5270,6 +5300,8 @@ parse_port_config(smartlist_t *out,
if (warn_nonlocal && out) {
if (is_control)
warn_nonlocal_controller_ports(out, forbid_nonlocal);
+ else if (is_ext_orport)
+ warn_nonlocal_ext_orports(out, portname);
else
warn_nonlocal_client_ports(out, portname, listener_type);
}
@@ -5543,6 +5575,8 @@ parse_port_config(smartlist_t *out,
if (warn_nonlocal && out) {
if (is_control)
warn_nonlocal_controller_ports(out, forbid_nonlocal);
+ else if (is_ext_orport)
+ warn_nonlocal_ext_orports(out, portname);
else
warn_nonlocal_client_ports(out, portname, listener_type);
}
@@ -5689,6 +5723,14 @@ parse_ports(or_options_t *options, int validate_only,
goto err;
}
if (parse_port_config(ports,
+ options->ExtORPort_lines, NULL,
+ "ExtOR", CONN_TYPE_EXT_OR_LISTENER,
+ "127.0.0.1", 0,
+ CL_PORT_SERVER_OPTIONS|CL_PORT_WARN_NONLOCAL) < 0) {
+ *msg = tor_strdup("Invalid ExtORPort configuration");
+ goto err;
+ }
+ if (parse_port_config(ports,
options->DirPort_lines, options->DirListenAddress,
"Dir", CONN_TYPE_DIR_LISTENER,
"0.0.0.0", 0,
@@ -5723,6 +5765,8 @@ parse_ports(or_options_t *options, int validate_only,
!! count_real_listeners(ports, CONN_TYPE_DIR_LISTENER);
options->DNSPort_set =
!! count_real_listeners(ports, CONN_TYPE_AP_DNS_LISTENER);
+ options->ExtORPort_set =
+ !! count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER);
if (!validate_only) {
if (configured_ports) {
@@ -6421,3 +6465,58 @@ config_maybe_load_geoip_files_(const or_options_t *options,
config_load_geoip_file_(AF_INET6, options->GeoIPv6File, "geoip6");
}
+/** Initialize cookie authentication (used so far by the ControlPort
+ * and Extended ORPort).
+ *
+ * Allocate memory and create a cookie (of length <b>cookie_len</b>)
+ * in <b>cookie_out</b>.
+ * Then write it down to <b>fname</b> and prepend it with <b>header</b>.
+ *
+ * If the whole procedure was successful, set
+ * <b>cookie_is_set_out</b> to True. */
+int
+init_cookie_authentication(const char *fname, const char *header,
+ int cookie_len,
+ uint8_t **cookie_out, int *cookie_is_set_out)
+{
+ char cookie_file_str_len = strlen(header) + cookie_len;
+ char *cookie_file_str = tor_malloc(cookie_file_str_len);
+ int retval = -1;
+
+ /* We don't want to generate a new cookie every time we call
+ * options_act(). One should be enough. */
+ if (*cookie_is_set_out) {
+ retval = 0; /* we are all set */
+ goto done;
+ }
+
+ /* If we've already set the cookie, free it before re-setting
+ it. This can happen if we previously generated a cookie, but
+ couldn't write it to a disk. */
+ if (*cookie_out)
+ tor_free(*cookie_out);
+
+ /* Generate the cookie */
+ *cookie_out = tor_malloc(cookie_len);
+ if (crypto_rand((char *)*cookie_out, cookie_len) < 0)
+ goto done;
+
+ /* Create the string that should be written on the file. */
+ memcpy(cookie_file_str, header, strlen(header));
+ memcpy(cookie_file_str+strlen(header), *cookie_out, cookie_len);
+ if (write_bytes_to_file(fname, cookie_file_str, cookie_file_str_len, 1)) {
+ log_warn(LD_FS,"Error writing auth cookie to %s.", escaped(fname));
+ goto done;
+ }
+
+ /* Success! */
+ log_info(LD_GENERAL, "Generated auth cookie file in '%s'.", escaped(fname));
+ *cookie_is_set_out = 1;
+ retval = 0;
+
+ done:
+ memwipe(cookie_file_str, 0, cookie_file_str_len);
+ tor_free(cookie_file_str);
+ return retval;
+}
+
diff --git a/src/or/config.h b/src/or/config.h
index 16a8a350d..eb16e7461 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -90,6 +90,10 @@ uint32_t get_effective_bwburst(const or_options_t *options);
char *get_transport_bindaddr_from_config(const char *transport);
+int init_cookie_authentication(const char *fname, const char *header,
+ int cookie_len,
+ uint8_t **cookie_out, int *cookie_is_set_out);
+
or_options_t *options_new(void);
void config_register_addressmaps(const or_options_t *options);
diff --git a/src/or/connection.c b/src/or/connection.c
index 28de35b3a..8c66a170e 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -10,6 +10,7 @@
* on connections.
**/
+#define CONNECTION_PRIVATE
#include "or.h"
#include "buffers.h"
/*
@@ -33,6 +34,7 @@
#include "dns.h"
#include "dnsserv.h"
#include "entrynodes.h"
+#include "ext_orport.h"
#include "geoip.h"
#include "main.h"
#include "policies.h"
@@ -98,6 +100,7 @@ static smartlist_t *outgoing_addrs = NULL;
#define CASE_ANY_LISTENER_TYPE \
case CONN_TYPE_OR_LISTENER: \
+ case CONN_TYPE_EXT_OR_LISTENER: \
case CONN_TYPE_AP_LISTENER: \
case CONN_TYPE_DIR_LISTENER: \
case CONN_TYPE_CONTROL_LISTENER: \
@@ -129,6 +132,8 @@ conn_type_to_string(int type)
case CONN_TYPE_CPUWORKER: return "CPU worker";
case CONN_TYPE_CONTROL_LISTENER: return "Control listener";
case CONN_TYPE_CONTROL: return "Control";
+ case CONN_TYPE_EXT_OR: return "Extended OR";
+ case CONN_TYPE_EXT_OR_LISTENER: return "Extended OR listener";
default:
log_warn(LD_BUG, "unknown connection type %d", type);
tor_snprintf(buf, sizeof(buf), "unknown [%d]", type);
@@ -165,6 +170,18 @@ conn_state_to_string(int type, int state)
case OR_CONN_STATE_OPEN: return "open";
}
break;
+ case CONN_TYPE_EXT_OR:
+ switch (state) {
+ case EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE:
+ return "waiting for authentication type";
+ case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE:
+ return "waiting for client nonce";
+ case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH:
+ return "waiting for client hash";
+ case EXT_OR_CONN_STATE_OPEN: return "open";
+ case EXT_OR_CONN_STATE_FLUSHING: return "flushing final OKAY";
+ }
+ break;
case CONN_TYPE_EXIT:
switch (state) {
case EXIT_CONN_STATE_RESOLVING: return "waiting for dest info";
@@ -229,6 +246,7 @@ connection_type_uses_bufferevent(connection_t *conn)
case CONN_TYPE_DIR:
case CONN_TYPE_CONTROL:
case CONN_TYPE_OR:
+ case CONN_TYPE_EXT_OR:
case CONN_TYPE_CPUWORKER:
return 1;
default:
@@ -259,14 +277,18 @@ dir_connection_new(int socket_family)
* Set active_circuit_pqueue_last_recalibrated to current cell_ewma tick.
*/
or_connection_t *
-or_connection_new(int socket_family)
+or_connection_new(int type, int socket_family)
{
or_connection_t *or_conn = tor_malloc_zero(sizeof(or_connection_t));
time_t now = time(NULL);
- connection_init(now, TO_CONN(or_conn), CONN_TYPE_OR, socket_family);
+ tor_assert(type == CONN_TYPE_OR || type == CONN_TYPE_EXT_OR);
+ connection_init(now, TO_CONN(or_conn), type, socket_family);
or_conn->timestamp_last_added_nonpadding = time(NULL);
+ if (type == CONN_TYPE_EXT_OR)
+ connection_or_set_ext_or_identifier(or_conn);
+
return or_conn;
}
@@ -335,7 +357,8 @@ connection_new(int type, int socket_family)
{
switch (type) {
case CONN_TYPE_OR:
- return TO_CONN(or_connection_new(socket_family));
+ case CONN_TYPE_EXT_OR:
+ return TO_CONN(or_connection_new(type, socket_family));
case CONN_TYPE_EXIT:
return TO_CONN(edge_connection_new(type, socket_family));
@@ -377,6 +400,7 @@ connection_init(time_t now, connection_t *conn, int type, int socket_family)
switch (type) {
case CONN_TYPE_OR:
+ case CONN_TYPE_EXT_OR:
conn->magic = OR_CONNECTION_MAGIC;
break;
case CONN_TYPE_EXIT:
@@ -435,7 +459,7 @@ connection_link_connections(connection_t *conn_a, connection_t *conn_b)
* necessary, close its socket if necessary, and mark the directory as dirty
* if <b>conn</b> is an OR or OP connection.
*/
-static void
+STATIC void
connection_free_(connection_t *conn)
{
void *mem;
@@ -445,6 +469,7 @@ connection_free_(connection_t *conn)
switch (conn->type) {
case CONN_TYPE_OR:
+ case CONN_TYPE_EXT_OR:
tor_assert(conn->magic == OR_CONNECTION_MAGIC);
mem = TO_OR_CONN(conn);
memlen = sizeof(or_connection_t);
@@ -575,6 +600,13 @@ connection_free_(connection_t *conn)
log_warn(LD_BUG, "called on OR conn with non-zeroed identity_digest");
connection_or_remove_from_identity_map(TO_OR_CONN(conn));
}
+ if (conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR) {
+ connection_or_remove_from_ext_or_id_map(TO_OR_CONN(conn));
+ tor_free(TO_OR_CONN(conn)->ext_or_conn_id);
+ tor_free(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash);
+ tor_free(TO_OR_CONN(conn)->ext_or_transport);
+ }
+
#ifdef USE_BUFFEREVENTS
if (conn->type == CONN_TYPE_OR && TO_OR_CONN(conn)->bucket_cfg) {
ev_token_bucket_cfg_free(TO_OR_CONN(conn)->bucket_cfg);
@@ -638,6 +670,7 @@ connection_about_to_close_connection(connection_t *conn)
connection_dir_about_to_close(TO_DIR_CONN(conn));
break;
case CONN_TYPE_OR:
+ case CONN_TYPE_EXT_OR:
connection_or_about_to_close(TO_OR_CONN(conn));
break;
case CONN_TYPE_AP:
@@ -1367,6 +1400,9 @@ connection_init_accepted_conn(connection_t *conn,
connection_start_reading(conn);
switch (conn->type) {
+ case CONN_TYPE_EXT_OR:
+ /* Initiate Extended ORPort authentication. */
+ return connection_ext_or_start_auth(TO_OR_CONN(conn));
case CONN_TYPE_OR:
control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0);
rv = connection_tls_start_handshake(TO_OR_CONN(conn), 1);
@@ -2873,6 +2909,8 @@ connection_handle_read_impl(connection_t *conn)
switch (conn->type) {
case CONN_TYPE_OR_LISTENER:
return connection_handle_listener_read(conn, CONN_TYPE_OR);
+ case CONN_TYPE_EXT_OR_LISTENER:
+ return connection_handle_listener_read(conn, CONN_TYPE_EXT_OR);
case CONN_TYPE_AP_LISTENER:
case CONN_TYPE_AP_TRANS_LISTENER:
case CONN_TYPE_AP_NATD_LISTENER:
@@ -3663,9 +3701,9 @@ connection_flush(connection_t *conn)
* it all, so we don't end up with many megabytes of controller info queued at
* once.
*/
-void
-connection_write_to_buf_impl_(const char *string, size_t len,
- connection_t *conn, int zlib)
+MOCK_IMPL(void,
+connection_write_to_buf_impl_,(const char *string, size_t len,
+ connection_t *conn, int zlib))
{
/* XXXX This function really needs to return -1 on failure. */
int r;
@@ -3905,6 +3943,7 @@ int
connection_is_listener(connection_t *conn)
{
if (conn->type == CONN_TYPE_OR_LISTENER ||
+ conn->type == CONN_TYPE_EXT_OR_LISTENER ||
conn->type == CONN_TYPE_AP_LISTENER ||
conn->type == CONN_TYPE_AP_TRANS_LISTENER ||
conn->type == CONN_TYPE_AP_DNS_LISTENER ||
@@ -3927,6 +3966,7 @@ connection_state_is_open(connection_t *conn)
return 0;
if ((conn->type == CONN_TYPE_OR && conn->state == OR_CONN_STATE_OPEN) ||
+ (conn->type == CONN_TYPE_EXT_OR) ||
(conn->type == CONN_TYPE_AP && conn->state == AP_CONN_STATE_OPEN) ||
(conn->type == CONN_TYPE_EXIT && conn->state == EXIT_CONN_STATE_OPEN) ||
(conn->type == CONN_TYPE_CONTROL &&
@@ -4096,6 +4136,8 @@ connection_process_inbuf(connection_t *conn, int package_partial)
switch (conn->type) {
case CONN_TYPE_OR:
return connection_or_process_inbuf(TO_OR_CONN(conn));
+ case CONN_TYPE_EXT_OR:
+ return connection_ext_or_process_inbuf(TO_OR_CONN(conn));
case CONN_TYPE_EXIT:
case CONN_TYPE_AP:
return connection_edge_process_inbuf(TO_EDGE_CONN(conn),
@@ -4156,6 +4198,8 @@ connection_finished_flushing(connection_t *conn)
switch (conn->type) {
case CONN_TYPE_OR:
return connection_or_finished_flushing(TO_OR_CONN(conn));
+ case CONN_TYPE_EXT_OR:
+ return connection_ext_or_finished_flushing(TO_OR_CONN(conn));
case CONN_TYPE_AP:
case CONN_TYPE_EXIT:
return connection_edge_finished_flushing(TO_EDGE_CONN(conn));
@@ -4211,6 +4255,7 @@ connection_reached_eof(connection_t *conn)
{
switch (conn->type) {
case CONN_TYPE_OR:
+ case CONN_TYPE_EXT_OR:
return connection_or_reached_eof(TO_OR_CONN(conn));
case CONN_TYPE_AP:
case CONN_TYPE_EXIT:
@@ -4297,6 +4342,7 @@ assert_connection_ok(connection_t *conn, time_t now)
switch (conn->type) {
case CONN_TYPE_OR:
+ case CONN_TYPE_EXT_OR:
tor_assert(conn->magic == OR_CONNECTION_MAGIC);
break;
case CONN_TYPE_AP:
@@ -4402,6 +4448,10 @@ assert_connection_ok(connection_t *conn, time_t now)
tor_assert(conn->state >= OR_CONN_STATE_MIN_);
tor_assert(conn->state <= OR_CONN_STATE_MAX_);
break;
+ case CONN_TYPE_EXT_OR:
+ tor_assert(conn->state >= EXT_OR_CONN_STATE_MIN_);
+ tor_assert(conn->state <= EXT_OR_CONN_STATE_MAX_);
+ break;
case CONN_TYPE_EXIT:
tor_assert(conn->state >= EXIT_CONN_STATE_MIN_);
tor_assert(conn->state <= EXIT_CONN_STATE_MAX_);
@@ -4534,6 +4584,7 @@ connection_free_all(void)
/* Unlink everything from the identity map. */
connection_or_clear_identity_map();
+ connection_or_clear_ext_or_id_map();
/* Clear out our list of broken connections */
clear_broken_connection_map(0);
diff --git a/src/or/connection.h b/src/or/connection.h
index 3e656ec06..0454ac2f3 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -19,7 +19,7 @@ const char *conn_type_to_string(int type);
const char *conn_state_to_string(int type, int state);
dir_connection_t *dir_connection_new(int socket_family);
-or_connection_t *or_connection_new(int socket_family);
+or_connection_t *or_connection_new(int type, int socket_family);
edge_connection_t *edge_connection_new(int type, int socket_family);
entry_connection_t *entry_connection_new(int type, int socket_family);
control_connection_t *control_connection_new(int socket_family);
@@ -130,8 +130,8 @@ int connection_outbuf_too_full(connection_t *conn);
int connection_handle_write(connection_t *conn, int force);
int connection_flush(connection_t *conn);
-void connection_write_to_buf_impl_(const char *string, size_t len,
- connection_t *conn, int zlib);
+MOCK_DECL(void, connection_write_to_buf_impl_,
+ (const char *string, size_t len, connection_t *conn, int zlib));
/* DOCDOC connection_write_to_buf */
static void connection_write_to_buf(const char *string, size_t len,
connection_t *conn);
@@ -214,5 +214,9 @@ void connection_enable_rate_limiting(connection_t *conn);
#define connection_type_uses_bufferevent(c) (0)
#endif
+#ifdef CONNECTION_PRIVATE
+STATIC void connection_free_(connection_t *conn);
+#endif
+
#endif
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 361636350..31fd6d673 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -37,7 +37,7 @@
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
-
+#include "ext_orport.h"
#ifdef USE_BUFFEREVENTS
#include <event2/bufferevent_ssl.h>
#endif
@@ -75,6 +75,10 @@ static void connection_or_handle_event_cb(struct bufferevent *bufev,
* they form a linked list, with next_with_same_id as the next pointer. */
static digestmap_t *orconn_identity_map = NULL;
+/** Global map between Extended ORPort identifiers and OR
+ * connections. */
+static digestmap_t *orconn_ext_or_id_map = NULL;
+
/** If conn is listed in orconn_identity_map, remove it, and clear
* conn->identity_digest. Otherwise do nothing. */
void
@@ -174,6 +178,71 @@ connection_or_set_identity_digest(or_connection_t *conn, const char *digest)
#endif
}
+/** Remove the Extended ORPort identifier of <b>conn</b> from the
+ * global identifier list. Also, clear the identifier from the
+ * connection itself. */
+void
+connection_or_remove_from_ext_or_id_map(or_connection_t *conn)
+{
+ or_connection_t *tmp;
+ if (!orconn_ext_or_id_map)
+ return;
+ if (!conn->ext_or_conn_id)
+ return;
+
+ tmp = digestmap_remove(orconn_ext_or_id_map, conn->ext_or_conn_id);
+ if (!tor_digest_is_zero(conn->ext_or_conn_id))
+ tor_assert(tmp == conn);
+
+ memset(conn->ext_or_conn_id, 0, EXT_OR_CONN_ID_LEN);
+}
+
+/** Return the connection whose ext_or_id is <b>id</b>. Return NULL if no such
+ * connection is found. */
+or_connection_t *
+connection_or_get_by_ext_or_id(const char *id)
+{
+ if (!orconn_ext_or_id_map)
+ return NULL;
+ return digestmap_get(orconn_ext_or_id_map, id);
+}
+
+/** Deallocate the global Extended ORPort identifier list */
+void
+connection_or_clear_ext_or_id_map(void)
+{
+ digestmap_free(orconn_ext_or_id_map, NULL);
+ orconn_ext_or_id_map = NULL;
+}
+
+/** Creates an Extended ORPort identifier for <b>conn<b/> and deposits
+ * it into the global list of identifiers. */
+void
+connection_or_set_ext_or_identifier(or_connection_t *conn)
+{
+ char random_id[EXT_OR_CONN_ID_LEN];
+ or_connection_t *tmp;
+
+ if (!orconn_ext_or_id_map)
+ orconn_ext_or_id_map = digestmap_new();
+
+ /* Remove any previous identifiers: */
+ if (conn->ext_or_conn_id && !tor_digest_is_zero(conn->ext_or_conn_id))
+ connection_or_remove_from_ext_or_id_map(conn);
+
+ do {
+ crypto_rand(random_id, sizeof(random_id));
+ } while (digestmap_get(orconn_ext_or_id_map, random_id));
+
+ if (!conn->ext_or_conn_id)
+ conn->ext_or_conn_id = tor_malloc_zero(EXT_OR_CONN_ID_LEN);
+
+ memcpy(conn->ext_or_conn_id, random_id, EXT_OR_CONN_ID_LEN);
+
+ tmp = digestmap_set(orconn_ext_or_id_map, random_id, conn);
+ tor_assert(!tmp);
+}
+
/**************************************************************/
/** Map from a string describing what a non-open OR connection was doing when
@@ -228,7 +297,7 @@ connection_or_get_state_description(or_connection_t *orconn,
const char *conn_state;
char tls_state[256];
- tor_assert(conn->type == CONN_TYPE_OR);
+ tor_assert(conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR);
conn_state = conn_state_to_string(conn->type, conn->state);
tor_tls_get_state_description(orconn->tls, tls_state, sizeof(tls_state));
@@ -1077,7 +1146,7 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
return NULL;
}
- conn = or_connection_new(tor_addr_family(&addr));
+ conn = or_connection_new(CONN_TYPE_OR, tor_addr_family(&addr));
/*
* Set up conn so it's got all the data we need to remember for channels
@@ -1212,8 +1281,8 @@ connection_or_close_for_error(or_connection_t *orconn, int flush)
*
* Return -1 if <b>conn</b> is broken, else return 0.
*/
-int
-connection_tls_start_handshake(or_connection_t *conn, int receiving)
+MOCK_IMPL(int,
+connection_tls_start_handshake,(or_connection_t *conn, int receiving))
{
channel_listener_t *chan_listener;
channel_t *chan;
@@ -1470,7 +1539,8 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event,
int
connection_or_nonopen_was_started_here(or_connection_t *conn)
{
- tor_assert(conn->base_.type == CONN_TYPE_OR);
+ tor_assert(conn->base_.type == CONN_TYPE_OR ||
+ conn->base_.type == CONN_TYPE_EXT_OR);
if (!conn->tls)
return 1; /* it's still in proxy states or something */
if (conn->handshake_state)
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index 85e68f1a3..8d9302893 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -45,7 +45,8 @@ void connection_or_close_for_error(or_connection_t *orconn, int flush);
void connection_or_report_broken_states(int severity, int domain);
-int connection_tls_start_handshake(or_connection_t *conn, int receiving);
+MOCK_DECL(int,connection_tls_start_handshake,(or_connection_t *conn,
+ int receiving));
int connection_tls_continue_handshake(or_connection_t *conn);
int connection_init_or_handshake_state(or_connection_t *conn,
diff --git a/src/or/control.c b/src/or/control.c
index 3f8d47c55..03b42af53 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -115,7 +115,7 @@ static int authentication_cookie_is_set = 0;
/** If authentication_cookie_is_set, a secret cookie that we've stored to disk
* and which we're using to authenticate controllers. (If the controller can
* read it off disk, it has permission to connect.) */
-static char authentication_cookie[AUTHENTICATION_COOKIE_LEN];
+static uint8_t *authentication_cookie = NULL;
#define SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT \
"Tor safe cookie authentication server-to-controller hash"
@@ -4446,44 +4446,27 @@ get_cookie_file(void)
}
}
-/** Choose a random authentication cookie and write it to disk.
- * Anybody who can read the cookie from disk will be considered
- * authorized to use the control connection. Return -1 if we can't
- * write the file, or 0 on success. */
+/* Initialize the cookie-based authentication system of the
+ * ControlPort. If <b>enabled</b> is 0, then disable the cookie
+ * authentication system. */
int
-init_cookie_authentication(int enabled)
+init_control_cookie_authentication(int enabled)
{
- char *fname;
+ char *fname = NULL;
+ int retval;
+
if (!enabled) {
authentication_cookie_is_set = 0;
return 0;
}
- /* We don't want to generate a new cookie every time we call
- * options_act(). One should be enough. */
- if (authentication_cookie_is_set)
- return 0; /* all set */
-
fname = get_cookie_file();
- crypto_rand(authentication_cookie, AUTHENTICATION_COOKIE_LEN);
- authentication_cookie_is_set = 1;
- if (write_bytes_to_file(fname, authentication_cookie,
- AUTHENTICATION_COOKIE_LEN, 1)) {
- log_warn(LD_FS,"Error writing authentication cookie to %s.",
- escaped(fname));
- tor_free(fname);
- return -1;
- }
-#ifndef _WIN32
- if (get_options()->CookieAuthFileGroupReadable) {
- if (chmod(fname, 0640)) {
- log_warn(LD_FS,"Unable to make %s group-readable.", escaped(fname));
- }
- }
-#endif
-
+ retval = init_cookie_authentication(fname, "", /* no header */
+ AUTHENTICATION_COOKIE_LEN,
+ &authentication_cookie,
+ &authentication_cookie_is_set);
tor_free(fname);
- return 0;
+ return retval;
}
/** A copy of the process specifier of Tor's owning controller, or
@@ -4699,8 +4682,8 @@ control_event_bootstrap(bootstrap_status_t status, int progress)
* that indicates a problem. <b>warn</b> gives a hint as to why, and
* <b>reason</b> provides an "or_conn_end_reason" tag.
*/
-void
-control_event_bootstrap_problem(const char *warn, int reason)
+MOCK_IMPL(void,
+control_event_bootstrap_problem, (const char *warn, int reason))
{
int status = bootstrap_percent;
const char *tag, *summary;
@@ -4767,3 +4750,11 @@ control_event_clients_seen(const char *controller_str)
"650 CLIENTS_SEEN %s\r\n", controller_str);
}
+/** Free any leftover allocated memory of the control.c subsystem. */
+void
+control_free_all(void)
+{
+ if (authentication_cookie) /* Free the auth cookie */
+ tor_free(authentication_cookie);
+}
+
diff --git a/src/or/control.h b/src/or/control.h
index d0f682067..be9476ea3 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -77,7 +77,7 @@ int control_event_buildtimeout_set(const circuit_build_times_t *cbt,
buildtimeout_set_event_t type);
int control_event_signal(uintptr_t signal);
-int init_cookie_authentication(int enabled);
+int init_control_cookie_authentication(int enabled);
smartlist_t *decode_hashed_passwords(config_line_t *passwords);
void disable_control_logging(void);
void enable_control_logging(void);
@@ -85,9 +85,11 @@ void enable_control_logging(void);
void monitor_owning_controller_process(const char *process_spec);
void control_event_bootstrap(bootstrap_status_t status, int progress);
-void control_event_bootstrap_problem(const char *warn, int reason);
+MOCK_DECL(void, control_event_bootstrap_problem,(const char *warn,
+ int reason));
void control_event_clients_seen(const char *controller_str);
+void control_free_all(void);
#ifdef CONTROL_PRIVATE
/* Used only by control.c and test.c */
diff --git a/src/or/directory.c b/src/or/directory.c
index 88d671779..97305ae2a 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -2966,7 +2966,9 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
tor_addr_t addr;
if (tor_inet_aton((TO_CONN(conn))->address, &in)) {
tor_addr_from_ipv4h(&addr, ntohl(in.s_addr));
- geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, time(NULL));
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS,
+ &addr, NULL,
+ time(NULL));
geoip_note_ns_response(GEOIP_SUCCESS);
/* Note that a request for a network status has started, so that we
* can measure the download time later on. */
diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c
new file mode 100644
index 000000000..d5a0fa1ee
--- /dev/null
+++ b/src/or/ext_orport.c
@@ -0,0 +1,648 @@
+/* Copyright (c) 2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file ext_orport.c
+ * \brief Code implementing the Extended ORPort.
+*/
+
+#define EXT_ORPORT_PRIVATE
+#include "or.h"
+#include "connection.h"
+#include "connection_or.h"
+#include "ext_orport.h"
+#include "control.h"
+#include "config.h"
+#include "util.h"
+#include "main.h"
+
+/** Allocate and return a structure capable of holding an Extended
+ * ORPort message of body length <b>len</b>. */
+ext_or_cmd_t *
+ext_or_cmd_new(uint16_t len)
+{
+ size_t size = STRUCT_OFFSET(ext_or_cmd_t, body) + len;
+ ext_or_cmd_t *cmd = tor_malloc(size);
+ cmd->len = len;
+ return cmd;
+}
+
+/** Deallocate the Extended ORPort message in <b>cmd</b>. */
+void
+ext_or_cmd_free(ext_or_cmd_t *cmd)
+{
+ tor_free(cmd);
+}
+
+/** Get an Extended ORPort message from <b>conn</b>, and place it in
+ * <b>out</b>. Return -1 on fail, 0 if we need more data, and 1 if we
+ * successfully extracted an Extended ORPort command from the
+ * buffer. */
+static int
+connection_fetch_ext_or_cmd_from_buf(connection_t *conn, ext_or_cmd_t **out)
+{
+ IF_HAS_BUFFEREVENT(conn, {
+ struct evbuffer *input = bufferevent_get_input(conn->bufev);
+ return fetch_ext_or_command_from_evbuffer(input, out);
+ }) ELSE_IF_NO_BUFFEREVENT {
+ return fetch_ext_or_command_from_buf(conn->inbuf, out);
+ }
+}
+
+/** Write an Extended ORPort message to <b>conn</b>. Use
+ * <b>command</b> as the command type, <b>bodylen</b> as the body
+ * length, and <b>body</b>, if it's present, as the body of the
+ * message. */
+STATIC int
+connection_write_ext_or_command(connection_t *conn,
+ uint16_t command,
+ const char *body,
+ size_t bodylen)
+{
+ char header[4];
+ if (bodylen > UINT16_MAX)
+ return -1;
+ set_uint16(header, htons(command));
+ set_uint16(header+2, htons(bodylen));
+ connection_write_to_buf(header, 4, conn);
+ if (bodylen) {
+ tor_assert(body);
+ connection_write_to_buf(body, bodylen, conn);
+ }
+ return 0;
+}
+
+/** Transition from an Extended ORPort which accepts Extended ORPort
+ * messages, to an Extended ORport which accepts OR traffic. */
+static void
+connection_ext_or_transition(or_connection_t *conn)
+{
+ tor_assert(conn->base_.type == CONN_TYPE_EXT_OR);
+
+ conn->base_.type = CONN_TYPE_OR;
+ TO_CONN(conn)->state = 0; // set the state to a neutral value
+ control_event_or_conn_status(conn, OR_CONN_EVENT_NEW, 0);
+ connection_tls_start_handshake(conn, 1);
+}
+
+/** Length of authentication cookie. */
+#define EXT_OR_PORT_AUTH_COOKIE_LEN 32
+/** Length of the header of the cookie file. */
+#define EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN 32
+/** Static cookie file header. */
+#define EXT_OR_PORT_AUTH_COOKIE_HEADER "! Extended ORPort Auth Cookie !\x0a"
+/** Length of safe-cookie protocol hashes. */
+#define EXT_OR_PORT_AUTH_HASH_LEN DIGEST256_LEN
+/** Length of safe-cookie protocol nonces. */
+#define EXT_OR_PORT_AUTH_NONCE_LEN 32
+/** Safe-cookie protocol constants. */
+#define EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST \
+ "ExtORPort authentication server-to-client hash"
+#define EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST \
+ "ExtORPort authentication client-to-server hash"
+
+/* Code to indicate cookie authentication */
+#define EXT_OR_AUTHTYPE_SAFECOOKIE 0x01
+
+/** If true, we've set ext_or_auth_cookie to a secret code and stored
+ * it to disk. */
+STATIC int ext_or_auth_cookie_is_set = 0;
+/** If ext_or_auth_cookie_is_set, a secret cookie that we've stored to disk
+ * and which we're using to authenticate controllers. (If the controller can
+ * read it off disk, it has permission to connect.) */
+STATIC uint8_t *ext_or_auth_cookie = NULL;
+
+/** Helper: Return a newly allocated string containing a path to the
+ * file where we store our authentication cookie. */
+char *
+get_ext_or_auth_cookie_file_name(void)
+{
+ const or_options_t *options = get_options();
+ if (options->ExtORPortCookieAuthFile &&
+ strlen(options->ExtORPortCookieAuthFile)) {
+ return tor_strdup(options->ExtORPortCookieAuthFile);
+ } else {
+ return get_datadir_fname("extended_orport_auth_cookie");
+ }
+}
+
+/* Initialize the cookie-based authentication system of the
+ * Extended ORPort. If <b>is_enabled</b> is 0, then disable the cookie
+ * authentication system. */
+int
+init_ext_or_cookie_authentication(int is_enabled)
+{
+ char *fname = NULL;
+ int retval;
+
+ if (!is_enabled) {
+ ext_or_auth_cookie_is_set = 0;
+ return 0;
+ }
+
+ fname = get_ext_or_auth_cookie_file_name();
+ retval = init_cookie_authentication(fname, EXT_OR_PORT_AUTH_COOKIE_HEADER,
+ EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN,
+ &ext_or_auth_cookie,
+ &ext_or_auth_cookie_is_set);
+ tor_free(fname);
+ return retval;
+}
+
+/** Read data from <b>conn</b> and see if the client sent us the
+ * authentication type that she prefers to use in this session.
+ *
+ * Return -1 if we received corrupted data or if we don't support the
+ * authentication type. Return 0 if we need more data in
+ * <b>conn</b>. Return 1 if the authentication type negotiation was
+ * successful. */
+static int
+connection_ext_or_auth_neg_auth_type(connection_t *conn)
+{
+ char authtype[1] = {0};
+
+ if (connection_get_inbuf_len(conn) < 1)
+ return 0;
+
+ if (connection_fetch_from_buf(authtype, 1, conn) < 0)
+ return -1;
+
+ log_debug(LD_GENERAL, "Client wants us to use %d auth type", authtype[0]);
+ if (authtype[0] != EXT_OR_AUTHTYPE_SAFECOOKIE) {
+ /* '1' is the only auth type supported atm */
+ return -1;
+ }
+
+ conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE;
+ return 1;
+}
+
+/** DOCDOC */
+STATIC int
+handle_client_auth_nonce(const char *client_nonce, size_t client_nonce_len,
+ char **client_hash_out,
+ char **reply_out, size_t *reply_len_out)
+{
+ char server_hash[EXT_OR_PORT_AUTH_HASH_LEN] = {0};
+ char server_nonce[EXT_OR_PORT_AUTH_NONCE_LEN] = {0};
+ char *reply;
+ size_t reply_len;
+
+ if (client_nonce_len != EXT_OR_PORT_AUTH_NONCE_LEN)
+ return -1;
+
+ /* Get our nonce */
+ if (crypto_rand(server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN) < 0)
+ return -1;
+
+ { /* set up macs */
+ size_t hmac_s_msg_len = strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) +
+ 2*EXT_OR_PORT_AUTH_NONCE_LEN;
+ size_t hmac_c_msg_len = strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST) +
+ 2*EXT_OR_PORT_AUTH_NONCE_LEN;
+
+ char *hmac_s_msg = tor_malloc_zero(hmac_s_msg_len);
+ char *hmac_c_msg = tor_malloc_zero(hmac_c_msg_len);
+ char *correct_client_hash = tor_malloc_zero(EXT_OR_PORT_AUTH_HASH_LEN);
+
+ memcpy(hmac_s_msg,
+ EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST,
+ strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST));
+ memcpy(hmac_s_msg + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST),
+ client_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
+ memcpy(hmac_s_msg + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) +
+ EXT_OR_PORT_AUTH_NONCE_LEN,
+ server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
+
+ memcpy(hmac_c_msg,
+ EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST,
+ strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST));
+ memcpy(hmac_c_msg + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST),
+ client_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
+ memcpy(hmac_c_msg + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST) +
+ EXT_OR_PORT_AUTH_NONCE_LEN,
+ server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
+
+ crypto_hmac_sha256(server_hash,
+ (char*)ext_or_auth_cookie,
+ EXT_OR_PORT_AUTH_COOKIE_LEN,
+ hmac_s_msg,
+ hmac_s_msg_len);
+
+ crypto_hmac_sha256(correct_client_hash,
+ (char*)ext_or_auth_cookie,
+ EXT_OR_PORT_AUTH_COOKIE_LEN,
+ hmac_c_msg,
+ hmac_c_msg_len);
+
+ /* Store the client hash we generated. We will need to compare it
+ with the hash sent by the client. */
+ *client_hash_out = correct_client_hash;
+
+ memwipe(hmac_s_msg, 0, hmac_s_msg_len);
+ memwipe(hmac_c_msg, 0, hmac_c_msg_len);
+
+ tor_free(hmac_s_msg);
+ tor_free(hmac_c_msg);
+ }
+
+ { /* debug logging */ /* XXX disable this codepath if not logging on debug?*/
+ char server_hash_encoded[(2*EXT_OR_PORT_AUTH_HASH_LEN) + 1];
+ char server_nonce_encoded[(2*EXT_OR_PORT_AUTH_NONCE_LEN) + 1];
+ char client_nonce_encoded[(2*EXT_OR_PORT_AUTH_NONCE_LEN) + 1];
+
+ base16_encode(server_hash_encoded, sizeof(server_hash_encoded),
+ server_hash, sizeof(server_hash));
+ base16_encode(server_nonce_encoded, sizeof(server_nonce_encoded),
+ server_nonce, sizeof(server_nonce));
+ base16_encode(client_nonce_encoded, sizeof(client_nonce_encoded),
+ client_nonce, sizeof(client_nonce));
+
+ log_debug(LD_GENERAL,
+ "server_hash: '%s'\nserver_nonce: '%s'\nclient_nonce: '%s'",
+ server_hash_encoded, server_nonce_encoded, client_nonce_encoded);
+
+ memwipe(server_hash_encoded, 0, sizeof(server_hash_encoded));
+ memwipe(server_nonce_encoded, 0, sizeof(server_nonce_encoded));
+ memwipe(client_nonce_encoded, 0, sizeof(client_nonce_encoded));
+ }
+
+ { /* write reply: (server_hash, server_nonce) */
+
+ reply_len = EXT_OR_PORT_AUTH_COOKIE_LEN+EXT_OR_PORT_AUTH_NONCE_LEN;
+ reply = tor_malloc_zero(reply_len);
+ memcpy(reply, server_hash, EXT_OR_PORT_AUTH_HASH_LEN);
+ memcpy(reply + EXT_OR_PORT_AUTH_HASH_LEN, server_nonce,
+ EXT_OR_PORT_AUTH_NONCE_LEN);
+ }
+
+ *reply_out = reply;
+ *reply_len_out = reply_len;
+
+ return 0;
+}
+
+/** Read the client's nonce out of <b>conn</b>, setup the safe-cookie
+ * crypto, and then send our own hash and nonce to the client
+ *
+ * Return -1 if there was an error; return 0 if we need more data in
+ * <b>conn</b>, and return 1 if we successfully retrieved the
+ * client's nonce and sent our own. */
+static int
+connection_ext_or_auth_handle_client_nonce(connection_t *conn)
+{
+ char client_nonce[EXT_OR_PORT_AUTH_NONCE_LEN];
+ char *reply=NULL;
+ size_t reply_len=0;
+
+ if (!ext_or_auth_cookie_is_set) { /* this should not happen */
+ log_warn(LD_BUG, "Extended ORPort authentication cookie was not set. "
+ "That's weird since we should have done that on startup. "
+ "This might be a Tor bug, please file a bug report. ");
+ return -1;
+ }
+
+ if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_NONCE_LEN)
+ return 0;
+
+ if (connection_fetch_from_buf(client_nonce,
+ EXT_OR_PORT_AUTH_NONCE_LEN, conn) < 0)
+ return -1;
+
+ /* We extract the ClientNonce from the received data, and use it to
+ calculate ServerHash and ServerNonce according to proposal 217.
+
+ We also calculate our own ClientHash value and save it in the
+ connection state. We validate it later against the ClientHash
+ sent by the client. */
+ if (handle_client_auth_nonce(client_nonce, sizeof(client_nonce),
+ &TO_OR_CONN(conn)->ext_or_auth_correct_client_hash,
+ &reply, &reply_len) < 0)
+ return -1;
+
+ connection_write_to_buf(reply, reply_len, conn);
+
+ memwipe(reply, 0, reply_len);
+ tor_free(reply);
+
+ log_debug(LD_GENERAL, "Got client nonce, and sent our own nonce and hash.");
+
+ conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH;
+ return 1;
+}
+
+#define connection_ext_or_auth_send_result_success(c) \
+ connection_ext_or_auth_send_result(c, 1)
+#define connection_ext_or_auth_send_result_fail(c) \
+ connection_ext_or_auth_send_result(c, 0)
+
+/** Send authentication results to <b>conn</b>. Successful results if
+ * <b>success</b> is set; failure results otherwise. */
+static void
+connection_ext_or_auth_send_result(connection_t *conn, int success)
+{
+ if (success)
+ connection_write_to_buf("\x01", 1, conn);
+ else
+ connection_write_to_buf("\x00", 1, conn);
+}
+
+/** Receive the client's hash from <b>conn</b>, validate that it's
+ * correct, and then send the authentication results to the client.
+ *
+ * Return -1 if there was an error during validation; return 0 if we
+ * need more data in <b>conn</b>, and return 1 if we successfully
+ * validated the client's hash and sent a happy authentication
+ * result. */
+static int
+connection_ext_or_auth_handle_client_hash(connection_t *conn)
+{
+ char provided_client_hash[EXT_OR_PORT_AUTH_HASH_LEN] = {0};
+
+ if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_HASH_LEN)
+ return 0;
+
+ if (connection_fetch_from_buf(provided_client_hash,
+ EXT_OR_PORT_AUTH_HASH_LEN, conn) < 0)
+ return -1;
+
+ if (tor_memneq(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash,
+ provided_client_hash, EXT_OR_PORT_AUTH_HASH_LEN)) {
+ log_warn(LD_GENERAL, "Incorrect client hash. Authentication failed.");
+ connection_ext_or_auth_send_result_fail(conn);
+ return -1;
+ }
+
+ log_debug(LD_GENERAL, "Got client's hash and it was legit.");
+
+ /* send positive auth result */
+ connection_ext_or_auth_send_result_success(conn);
+ conn->state = EXT_OR_CONN_STATE_OPEN;
+ return 1;
+}
+
+/** Handle data from <b>or_conn</b> received on Extended ORPort.
+ * Return -1 on error. 0 on unsufficient data. 1 on correct. */
+static int
+connection_ext_or_auth_process_inbuf(or_connection_t *or_conn)
+{
+ connection_t *conn = TO_CONN(or_conn);
+
+ /* State transitions of the Extended ORPort authentication protocol:
+
+ EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE (start state) ->
+ EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE ->
+ EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH ->
+ EXT_OR_CONN_STATE_OPEN
+
+ During EXT_OR_CONN_STATE_OPEN, data is handled by
+ connection_ext_or_process_inbuf().
+ */
+
+ switch (conn->state) { /* Functionify */
+ case EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE:
+ return connection_ext_or_auth_neg_auth_type(conn);
+
+ case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE:
+ return connection_ext_or_auth_handle_client_nonce(conn);
+
+ case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH:
+ return connection_ext_or_auth_handle_client_hash(conn);
+
+ default:
+ log_warn(LD_BUG, "Encountered unexpected connection state %d while trying "
+ "to process Extended ORPort authentication data.", conn->state);
+ return -1;
+ }
+}
+
+/** Extended ORPort commands (Transport-to-Bridge) */
+#define EXT_OR_CMD_TB_DONE 0x0000
+#define EXT_OR_CMD_TB_USERADDR 0x0001
+#define EXT_OR_CMD_TB_TRANSPORT 0x0002
+
+/** Extended ORPort commands (Bridge-to-Transport) */
+#define EXT_OR_CMD_BT_OKAY 0x1000
+#define EXT_OR_CMD_BT_DENY 0x1001
+#define EXT_OR_CMD_BT_CONTROL 0x1002
+
+/** Process a USERADDR command from the Extended
+ * ORPort. <b>payload</b> is a payload of size <b>len</b>.
+ *
+ * If the USERADDR command was well formed, change the address of
+ * <b>conn</b> to the address on the USERADDR command.
+ *
+ * Return 0 on success and -1 on error. */
+static int
+connection_ext_or_handle_cmd_useraddr(connection_t *conn,
+ const char *payload, uint16_t len)
+{
+ /* Copy address string. */
+ tor_addr_t addr;
+ uint16_t port;
+ char *addr_str;
+ char *address_part=NULL;
+ int res;
+ if (memchr(payload, '\0', len)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unexpected NUL in ExtORPort UserAddr");
+ return -1;
+ }
+
+ addr_str = tor_memdup_nulterm(payload, len);
+
+ res = tor_addr_port_split(LOG_INFO, addr_str, &address_part, &port);
+ tor_free(addr_str);
+ if (res<0)
+ return -1;
+
+ res = tor_addr_parse(&addr, address_part);
+ tor_free(address_part);
+ if (res<0)
+ return -1;
+
+ { /* do some logging */
+ char *old_address = tor_dup_addr(&conn->addr);
+ char *new_address = tor_dup_addr(&addr);
+
+ log_debug(LD_NET, "Received USERADDR."
+ "We rewrite our address from '%s:%u' to '%s:%u'.",
+ safe_str(old_address), conn->port, safe_str(new_address), port);
+
+ tor_free(old_address);
+ tor_free(new_address);
+ }
+
+ /* record the address */
+ tor_addr_copy(&conn->addr, &addr);
+ conn->port = port;
+ if (conn->address) {
+ tor_free(conn->address);
+ }
+ conn->address = tor_dup_addr(&addr);
+
+ return 0;
+}
+
+/** Process a TRANSPORT command from the Extended
+ * ORPort. <b>payload</b> is a payload of size <b>len</b>.
+ *
+ * If the TRANSPORT command was well formed, register the name of the
+ * transport on <b>conn</b>.
+ *
+ * Return 0 on success and -1 on error. */
+static int
+connection_ext_or_handle_cmd_transport(or_connection_t *conn,
+ const char *payload, uint16_t len)
+{
+ char *transport_str;
+ if (memchr(payload, '\0', len)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unexpected NUL in ExtORPort Transport");
+ return -1;
+ }
+
+ transport_str = tor_memdup_nulterm(payload, len);
+
+ /* Transport names MUST be C-identifiers. */
+ if (!string_is_C_identifier(transport_str)) {
+ tor_free(transport_str);
+ return -1;
+ }
+
+ /* If ext_or_transport is already occupied (because the PT sent two
+ * TRANSPORT commands), deallocate the old name and keep the new
+ * one */
+ if (conn->ext_or_transport)
+ tor_free(conn->ext_or_transport);
+
+ conn->ext_or_transport = transport_str;
+ return 0;
+}
+
+#define EXT_OR_CONN_STATE_IS_AUTHENTICATING(st) \
+ ((st) <= EXT_OR_CONN_STATE_AUTH_MAX)
+
+/** Process Extended ORPort messages from <b>or_conn</b>. */
+int
+connection_ext_or_process_inbuf(or_connection_t *or_conn)
+{
+ connection_t *conn = TO_CONN(or_conn);
+ ext_or_cmd_t *command;
+ int r;
+
+ /* DOCDOC Document the state machine and transitions in this function */
+
+ /* If we are still in the authentication stage, process traffic as
+ authentication data: */
+ while (EXT_OR_CONN_STATE_IS_AUTHENTICATING(conn->state)) {
+ log_debug(LD_GENERAL, "Got Extended ORPort authentication data (%u).",
+ (unsigned int) connection_get_inbuf_len(conn));
+ r = connection_ext_or_auth_process_inbuf(or_conn);
+ if (r < 0) {
+ connection_mark_for_close(conn);
+ return -1;
+ } else if (r == 0) {
+ return 0;
+ }
+ /* if r > 0, loop and process more data (if any). */
+ }
+
+ while (1) {
+ log_debug(LD_GENERAL, "Got Extended ORPort data.");
+ command = NULL;
+ r = connection_fetch_ext_or_cmd_from_buf(conn, &command);
+ if (r < 0)
+ goto err;
+ else if (r == 0)
+ return 0; /* need to wait for more data */
+
+ /* Got a command! */
+ tor_assert(command);
+
+ if (command->cmd == EXT_OR_CMD_TB_DONE) {
+ if (connection_get_inbuf_len(conn)) {
+ /* The inbuf isn't empty; the client is misbehaving. */
+ goto err;
+ }
+
+ log_debug(LD_NET, "Received DONE.");
+
+ /* If the transport proxy did not use the TRANSPORT command to
+ * specify the transport name, mark this as unknown transport. */
+ if (!or_conn->ext_or_transport) {
+ /* We write this string this way to avoid ??>, which is a C
+ * trigraph. */
+ or_conn->ext_or_transport = tor_strdup("<?" "?>");
+ }
+
+ connection_write_ext_or_command(conn, EXT_OR_CMD_BT_OKAY, NULL, 0);
+
+ /* can't transition immediately; need to flush first. */
+ conn->state = EXT_OR_CONN_STATE_FLUSHING;
+ connection_stop_reading(conn);
+ } else if (command->cmd == EXT_OR_CMD_TB_USERADDR) {
+ if (connection_ext_or_handle_cmd_useraddr(conn,
+ command->body, command->len) < 0)
+ goto err;
+ } else if (command->cmd == EXT_OR_CMD_TB_TRANSPORT) {
+ if (connection_ext_or_handle_cmd_transport(or_conn,
+ command->body, command->len) < 0)
+ goto err;
+ } else {
+ log_notice(LD_NET,"Got Extended ORPort command we don't regognize (%u).",
+ command->cmd);
+ }
+
+ ext_or_cmd_free(command);
+ }
+
+ return 0;
+
+ err:
+ ext_or_cmd_free(command);
+ connection_mark_for_close(conn);
+ return -1;
+}
+
+/** <b>conn</b> finished flushing Extended ORPort messages to the
+ * network, and is now ready to accept OR traffic. This function
+ * does the transition. */
+int
+connection_ext_or_finished_flushing(or_connection_t *conn)
+{
+ if (conn->base_.state == EXT_OR_CONN_STATE_FLUSHING) {
+ connection_start_reading(TO_CONN(conn));
+ connection_ext_or_transition(conn);
+ }
+ return 0;
+}
+
+/** Initiate Extended ORPort authentication, by sending the list of
+ * supported authentication types to the client. */
+int
+connection_ext_or_start_auth(or_connection_t *or_conn)
+{
+ connection_t *conn = TO_CONN(or_conn);
+ const uint8_t authtypes[] = {
+ /* We only support authtype '1' for now. */
+ EXT_OR_AUTHTYPE_SAFECOOKIE,
+ /* Marks the end of the list. */
+ 0
+ };
+
+ log_debug(LD_GENERAL,
+ "ExtORPort authentication: Sending supported authentication types");
+
+ connection_write_to_buf((const char *)authtypes, sizeof(authtypes), conn);
+ conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE;
+
+ return 0;
+}
+
+/** Free any leftover allocated memory of the ext_orport.c subsystem. */
+void
+ext_orport_free_all(void)
+{
+ if (ext_or_auth_cookie) /* Free the auth cookie */
+ tor_free(ext_or_auth_cookie);
+}
+
diff --git a/src/or/ext_orport.h b/src/or/ext_orport.h
new file mode 100644
index 000000000..ce45e5f41
--- /dev/null
+++ b/src/or/ext_orport.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef EXT_ORPORT_H
+#define EXT_ORPORT_H
+
+int connection_ext_or_start_auth(or_connection_t *or_conn);
+
+ext_or_cmd_t *ext_or_cmd_new(uint16_t len);
+void ext_or_cmd_free(ext_or_cmd_t *cmd);
+void connection_or_set_ext_or_identifier(or_connection_t *conn);
+void connection_or_remove_from_ext_or_id_map(or_connection_t *conn);
+void connection_or_clear_ext_or_id_map(void);
+or_connection_t *connection_or_get_by_ext_or_id(const char *id);
+
+int connection_ext_or_finished_flushing(or_connection_t *conn);
+int connection_ext_or_process_inbuf(or_connection_t *or_conn);
+
+int init_ext_or_cookie_authentication(int is_enabled);
+char *get_ext_or_auth_cookie_file_name(void);
+void ext_orport_free_all(void);
+
+#ifdef EXT_ORPORT_PRIVATE
+STATIC int connection_write_ext_or_command(connection_t *conn,
+ uint16_t command,
+ const char *body,
+ size_t bodylen);
+STATIC int handle_client_auth_nonce(const char *client_nonce,
+ size_t client_nonce_len,
+ char **client_hash_out,
+ char **reply_out, size_t *reply_len_out);
+#ifdef TOR_UNIT_TESTS
+extern uint8_t *ext_or_auth_cookie;
+extern int ext_or_auth_cookie_is_set;
+#endif
+#endif
+
+#endif
+
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 020075f80..dc4730c81 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -461,6 +461,10 @@ geoip_db_digest(sa_family_t family)
typedef struct clientmap_entry_t {
HT_ENTRY(clientmap_entry_t) node;
tor_addr_t addr;
+ /* Name of pluggable transport used by this client. NULL if no
+ pluggable transport was used. */
+ char *transport_name;
+
/** Time when we last saw this IP address, in MINUTES since the epoch.
*
* (This will run out of space around 4011 CE. If Tor is still in use around
@@ -482,12 +486,18 @@ static HT_HEAD(clientmap, clientmap_entry_t) client_history =
static INLINE unsigned
clientmap_entry_hash(const clientmap_entry_t *a)
{
- return ht_improve_hash(tor_addr_hash(&a->addr));
+ unsigned h = tor_addr_hash(&a->addr);
+ if (a->transport_name)
+ h += ht_string_hash(a->transport_name);
+ return ht_improve_hash(h);
}
/** Hashtable helper: compare two clientmap_entry_t values for equality. */
static INLINE int
clientmap_entries_eq(const clientmap_entry_t *a, const clientmap_entry_t *b)
{
+ if (strcmp_opt(a->transport_name, b->transport_name))
+ return 0;
+
return !tor_addr_compare(&a->addr, &b->addr, CMP_EXACT) &&
a->action == b->action;
}
@@ -497,6 +507,17 @@ HT_PROTOTYPE(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
HT_GENERATE(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
clientmap_entries_eq, 0.6, malloc, realloc, free);
+/** Free all storage held by <b>ent</b>. */
+static void
+clientmap_entry_free(clientmap_entry_t *ent)
+{
+ if (!ent)
+ return;
+
+ tor_free(ent->transport_name);
+ tor_free(ent);
+}
+
/** Clear history of connecting clients used by entry and bridge stats. */
static void
client_history_clear(void)
@@ -507,7 +528,7 @@ client_history_clear(void)
if ((*ent)->action == GEOIP_CLIENT_CONNECT) {
this = *ent;
next = HT_NEXT_RMV(clientmap, &client_history, ent);
- tor_free(this);
+ clientmap_entry_free(this);
} else {
next = HT_NEXT(clientmap, &client_history, ent);
}
@@ -519,10 +540,14 @@ client_history_clear(void)
* configured accordingly. */
void
geoip_note_client_seen(geoip_client_action_t action,
- const tor_addr_t *addr, time_t now)
+ const tor_addr_t *addr,
+ const char *transport_name,
+ time_t now)
{
const or_options_t *options = get_options();
clientmap_entry_t lookup, *ent;
+ memset(&lookup, 0, sizeof(clientmap_entry_t));
+
if (action == GEOIP_CLIENT_CONNECT) {
/* Only remember statistics as entry guard or as bridge. */
if (!options->EntryStatistics &&
@@ -534,12 +559,20 @@ geoip_note_client_seen(geoip_client_action_t action,
return;
}
+ log_debug(LD_GENERAL, "Seen client from '%s' with transport '%s'.",
+ safe_str_client(fmt_addr((addr))),
+ transport_name ? transport_name : "<no transport>");
+
tor_addr_copy(&lookup.addr, addr);
lookup.action = (int)action;
+ lookup.transport_name = (char*) transport_name;
ent = HT_FIND(clientmap, &client_history, &lookup);
+
if (! ent) {
ent = tor_malloc_zero(sizeof(clientmap_entry_t));
tor_addr_copy(&ent->addr, addr);
+ if (transport_name)
+ ent->transport_name = tor_strdup(transport_name);
ent->action = (int)action;
HT_INSERT(clientmap, &client_history, ent);
}
@@ -566,7 +599,7 @@ remove_old_client_helper_(struct clientmap_entry_t *ent, void *_cutoff)
{
time_t cutoff = *(time_t*)_cutoff / 60;
if (ent->last_seen_in_minutes < cutoff) {
- tor_free(ent);
+ clientmap_entry_free(ent);
return 1;
} else {
return 0;
@@ -769,6 +802,106 @@ geoip_change_dirreq_state(uint64_t dirreq_id, dirreq_type_t type,
}
}
+/** Return the bridge-ip-transports string that should be inserted in
+ * our extra-info descriptor. Return NULL if the bridge-ip-transports
+ * line should be empty. */
+char *
+geoip_get_transport_history(void)
+{
+ unsigned granularity = IP_GRANULARITY;
+ /** String hash table <name of transport> -> <number of users>. */
+ strmap_t *transport_counts = strmap_new();
+
+ /** Smartlist that contains copies of the names of the transports
+ that have been used. */
+ smartlist_t *transports_used = smartlist_new();
+
+ /* Special string to signify that no transport was used for this
+ connection. Pluggable transport names can't have symbols in their
+ names, so this string will never collide with a real transport. */
+ static const char* no_transport_str = "<OR>";
+
+ clientmap_entry_t **ent;
+ const char *transport_name = NULL;
+ smartlist_t *string_chunks = smartlist_new();
+ char *the_string = NULL;
+
+ /* If we haven't seen any clients yet, return NULL. */
+ if (HT_EMPTY(&client_history))
+ goto done;
+
+ /** We do the following steps to form the transport history string:
+ * a) Foreach client that uses a pluggable transport, we increase the
+ * times that transport was used by one. If the client did not use
+ * a transport, we increase the number of times someone connected
+ * without obfuscation.
+ * b) Foreach transport we observed, we write its transport history
+ * string and push it to string_chunks. So, for example, if we've
+ * seen 665 obfs2 clients, we write "obfs2=665".
+ * c) We concatenate string_chunks to form the final string.
+ */
+
+ log_debug(LD_GENERAL,"Starting iteration for transport history. %d clients.",
+ HT_SIZE(&client_history));
+
+ /* Loop through all clients. */
+ HT_FOREACH(ent, clientmap, &client_history) {
+ uintptr_t val;
+ void *ptr;
+ transport_name = (*ent)->transport_name;
+ if (!transport_name)
+ transport_name = no_transport_str;
+
+ /* Increase the count for this transport name. */
+ ptr = strmap_get(transport_counts, transport_name);
+ val = (uintptr_t)ptr;
+ val++;
+ ptr = (void*)val;
+ strmap_set(transport_counts, transport_name, ptr);
+
+ /* If it's the first time we see this transport, note it. */
+ if (val == 1)
+ smartlist_add(transports_used, tor_strdup(transport_name));
+
+ log_debug(LD_GENERAL, "Client from '%s' with transport '%s'. "
+ "I've now seen %d clients.",
+ safe_str_client(fmt_addr(&(*ent)->addr)),
+ transport_name ? transport_name : "<no transport>",
+ (int)val);
+ }
+
+ /* Sort the transport names (helps with unit testing). */
+ smartlist_sort_strings(transports_used);
+
+ /* Loop through all seen transports. */
+ SMARTLIST_FOREACH_BEGIN(transports_used, const char *, transport_name) {
+ void *transport_count_ptr = strmap_get(transport_counts, transport_name);
+ uintptr_t transport_count = (uintptr_t) transport_count_ptr;
+
+ log_debug(LD_GENERAL, "We got "U64_FORMAT" clients with transport '%s'.",
+ U64_PRINTF_ARG((uint64_t)transport_count), transport_name);
+
+ smartlist_add_asprintf(string_chunks, "%s="U64_FORMAT,
+ transport_name,
+ U64_PRINTF_ARG(round_uint64_to_next_multiple_of(
+ (uint64_t)transport_count,
+ granularity)));
+ } SMARTLIST_FOREACH_END(transport_name);
+
+ the_string = smartlist_join_strings(string_chunks, ",", 0, NULL);
+
+ log_debug(LD_GENERAL, "Final bridge-ip-transports string: '%s'", the_string);
+
+ done:
+ strmap_free(transport_counts, NULL);
+ SMARTLIST_FOREACH(transports_used, char *, s, tor_free(s));
+ smartlist_free(transports_used);
+ SMARTLIST_FOREACH(string_chunks, char *, s, tor_free(s));
+ smartlist_free(string_chunks);
+
+ return the_string;
+}
+
/** Return a newly allocated comma-separated string containing statistics
* on network status downloads. The string contains the number of completed
* requests, timeouts, and still running requests as well as the download
@@ -1037,7 +1170,7 @@ geoip_reset_dirreq_stats(time_t now)
if ((*ent)->action == GEOIP_CLIENT_NETWORKSTATUS) {
this = *ent;
next = HT_NEXT_RMV(clientmap, &client_history, ent);
- tor_free(this);
+ clientmap_entry_free(this);
} else {
next = HT_NEXT(clientmap, &client_history, ent);
}
@@ -1189,6 +1322,8 @@ validate_bridge_stats(const char *stats_str, time_t now)
const char *BRIDGE_STATS_END = "bridge-stats-end ";
const char *BRIDGE_IPS = "bridge-ips ";
const char *BRIDGE_IPS_EMPTY_LINE = "bridge-ips\n";
+ const char *BRIDGE_TRANSPORTS = "bridge-ip-transports ";
+ const char *BRIDGE_TRANSPORTS_EMPTY_LINE = "bridge-ip-transports\n";
const char *tmp;
time_t stats_end_time;
int seconds;
@@ -1223,6 +1358,15 @@ validate_bridge_stats(const char *stats_str, time_t now)
return 0;
}
+ /* Parse: "bridge-ip-transports PT=N,PT=N,..." */
+ tmp = find_str_at_start_of_line(stats_str, BRIDGE_TRANSPORTS);
+ if (!tmp) {
+ /* Look if there is an empty "bridge-ip-transports" line */
+ tmp = find_str_at_start_of_line(stats_str, BRIDGE_TRANSPORTS_EMPTY_LINE);
+ if (!tmp)
+ return 0;
+ }
+
return 1;
}
@@ -1236,7 +1380,8 @@ static char *bridge_stats_extrainfo = NULL;
char *
geoip_format_bridge_stats(time_t now)
{
- char *out = NULL, *country_data = NULL, *ipver_data = NULL;
+ char *out = NULL;
+ char *country_data = NULL, *ipver_data = NULL, *transport_data = NULL;
long duration = now - start_of_bridge_stats_interval;
char written[ISO_TIME_LEN+1];
@@ -1247,16 +1392,20 @@ geoip_format_bridge_stats(time_t now)
format_iso_time(written, now);
geoip_get_client_history(GEOIP_CLIENT_CONNECT, &country_data, &ipver_data);
+ transport_data = geoip_get_transport_history();
tor_asprintf(&out,
"bridge-stats-end %s (%ld s)\n"
"bridge-ips %s\n"
- "bridge-ip-versions %s\n",
+ "bridge-ip-versions %s\n"
+ "bridge-ip-transports %s\n",
written, duration,
country_data ? country_data : "",
- ipver_data ? ipver_data : "");
+ ipver_data ? ipver_data : "",
+ transport_data ? transport_data : "");
tor_free(country_data);
tor_free(ipver_data);
+ tor_free(transport_data);
return out;
}
@@ -1515,7 +1664,7 @@ geoip_free_all(void)
for (ent = HT_START(clientmap, &client_history); ent != NULL; ent = next) {
this = *ent;
next = HT_NEXT_RMV(clientmap, &client_history, ent);
- tor_free(this);
+ clientmap_entry_free(this);
}
HT_CLEAR(clientmap, &client_history);
}
@@ -1530,5 +1679,6 @@ geoip_free_all(void)
}
clear_geoip_db();
+ tor_free(bridge_stats_extrainfo);
}
diff --git a/src/or/geoip.h b/src/or/geoip.h
index d67007298..b9b53c300 100644
--- a/src/or/geoip.h
+++ b/src/or/geoip.h
@@ -29,10 +29,12 @@ const char *geoip_db_digest(sa_family_t family);
country_t geoip_get_country(const char *countrycode);
void geoip_note_client_seen(geoip_client_action_t action,
- const tor_addr_t *addr, time_t now);
+ const tor_addr_t *addr, const char *transport_name,
+ time_t now);
void geoip_remove_old_clients(time_t cutoff);
void geoip_note_ns_response(geoip_ns_response_t response);
+char *geoip_get_transport_history(void);
int geoip_get_client_history(geoip_client_action_t action,
char **country_str, char **ipver_str);
char *geoip_get_request_history(void);
diff --git a/src/or/include.am b/src/or/include.am
index f5002e698..8922e0397 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -56,6 +56,7 @@ LIBTOR_A_SOURCES = \
src/or/fp_pair.c \
src/or/geoip.c \
src/or/entrynodes.c \
+ src/or/ext_orport.c \
src/or/hibernate.c \
src/or/main.c \
src/or/microdesc.c \
@@ -153,6 +154,7 @@ ORHEADERS = \
src/or/dns.h \
src/or/dnsserv.h \
src/or/eventdns_tor.h \
+ src/or/ext_orport.h \
src/or/fp_pair.h \
src/or/geoip.h \
src/or/entrynodes.h \
diff --git a/src/or/main.c b/src/or/main.c
index d1728250a..33e1c6437 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -10,6 +10,7 @@
* connections, implements main loop, and drives scheduled events.
**/
+#define MAIN_PRIVATE
#include "or.h"
#include "addressmap.h"
#include "buffers.h"
@@ -51,6 +52,7 @@
#include "routerparse.h"
#include "statefile.h"
#include "status.h"
+#include "ext_orport.h"
#ifdef USE_DMALLOC
#include <dmalloc.h>
#include <openssl/crypto.h>
@@ -412,6 +414,19 @@ connection_unlink(connection_t *conn)
connection_free(conn);
}
+/** Initialize the global connection list, closeable connection list,
+ * and active connection list. */
+STATIC void
+init_connection_lists(void)
+{
+ if (!connection_array)
+ connection_array = smartlist_new();
+ if (!closeable_connection_lst)
+ closeable_connection_lst = smartlist_new();
+ if (!active_linked_connection_lst)
+ active_linked_connection_lst = smartlist_new();
+}
+
/** Schedule <b>conn</b> to be closed. **/
void
add_connection_to_closeable_list(connection_t *conn)
@@ -505,8 +520,8 @@ connection_is_reading(connection_t *conn)
}
/** Tell the main loop to stop notifying <b>conn</b> of any read events. */
-void
-connection_stop_reading(connection_t *conn)
+MOCK_IMPL(void,
+connection_stop_reading,(connection_t *conn))
{
tor_assert(conn);
@@ -530,8 +545,8 @@ connection_stop_reading(connection_t *conn)
}
/** Tell the main loop to start notifying <b>conn</b> of any read events. */
-void
-connection_start_reading(connection_t *conn)
+MOCK_IMPL(void,
+connection_start_reading,(connection_t *conn))
{
tor_assert(conn);
@@ -570,8 +585,8 @@ connection_is_writing(connection_t *conn)
}
/** Tell the main loop to stop notifying <b>conn</b> of any write events. */
-void
-connection_stop_writing(connection_t *conn)
+MOCK_IMPL(void,
+connection_stop_writing,(connection_t *conn))
{
tor_assert(conn);
@@ -596,8 +611,8 @@ connection_stop_writing(connection_t *conn)
}
/** Tell the main loop to start notifying <b>conn</b> of any write events. */
-void
-connection_start_writing(connection_t *conn)
+MOCK_IMPL(void,
+connection_start_writing,(connection_t *conn))
{
tor_assert(conn);
@@ -685,7 +700,7 @@ connection_stop_reading_from_linked_conn(connection_t *conn)
}
/** Close all connections that have been scheduled to get closed. */
-static void
+STATIC void
close_closeable_connections(void)
{
int i;
@@ -2307,12 +2322,7 @@ tor_init(int argc, char *argv[])
char buf[256];
int i, quiet = 0;
time_of_process_start = time(NULL);
- if (!connection_array)
- connection_array = smartlist_new();
- if (!closeable_connection_lst)
- closeable_connection_lst = smartlist_new();
- if (!active_linked_connection_lst)
- active_linked_connection_lst = smartlist_new();
+ init_connection_lists();
/* Have the log set up with our application name. */
tor_snprintf(buf, sizeof(buf), "Tor %s", get_version());
log_set_application_name(buf);
@@ -2501,6 +2511,8 @@ tor_free_all(int postfork)
memarea_clear_freelist();
nodelist_free_all();
microdesc_free_all();
+ ext_orport_free_all();
+ control_free_all();
if (!postfork) {
config_free_all();
or_state_free_all();
diff --git a/src/or/main.h b/src/or/main.h
index 85621a32b..df302ffa7 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -36,12 +36,12 @@ typedef enum watchable_events {
} watchable_events_t;
void connection_watch_events(connection_t *conn, watchable_events_t events);
int connection_is_reading(connection_t *conn);
-void connection_stop_reading(connection_t *conn);
-void connection_start_reading(connection_t *conn);
+MOCK_DECL(void,connection_stop_reading,(connection_t *conn));
+MOCK_DECL(void,connection_start_reading,(connection_t *conn));
int connection_is_writing(connection_t *conn);
-void connection_stop_writing(connection_t *conn);
-void connection_start_writing(connection_t *conn);
+MOCK_DECL(void,connection_stop_writing,(connection_t *conn));
+MOCK_DECL(void,connection_start_writing,(connection_t *conn));
void connection_stop_reading_from_linked_conn(connection_t *conn);
@@ -69,5 +69,10 @@ int tor_main(int argc, char *argv[]);
int do_main_loop(void);
int tor_init(int argc, char **argv);
+#ifdef MAIN_PRIVATE
+STATIC void init_connection_lists(void);
+STATIC void close_closeable_connections(void);
+#endif
+
#endif
diff --git a/src/or/or.h b/src/or/or.h
index a6a4bcc1b..d2f6953ab 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -228,8 +228,14 @@ typedef enum {
#define CONN_TYPE_AP_NATD_LISTENER 14
/** Type for sockets listening for DNS requests. */
#define CONN_TYPE_AP_DNS_LISTENER 15
-#define CONN_TYPE_MAX_ 15
-/* !!!! If CONN_TYPE_MAX_ is ever over 15, we must grow the type field in
+
+/** Type for connections from the Extended ORPort. */
+#define CONN_TYPE_EXT_OR 16
+/** Type for sockets listening for Extended ORPort connections. */
+#define CONN_TYPE_EXT_OR_LISTENER 17
+
+#define CONN_TYPE_MAX_ 17
+/* !!!! If _CONN_TYPE_MAX is ever over 31, we must grow the type field in
* connection_t. */
/* Proxy client types */
@@ -309,6 +315,25 @@ typedef enum {
#define OR_CONN_STATE_OPEN 8
#define OR_CONN_STATE_MAX_ 8
+/** States of the Extended ORPort protocol. Be careful before changing
+ * the numbers: they matter. */
+#define EXT_OR_CONN_STATE_MIN_ 1
+/** Extended ORPort authentication is waiting for the authentication
+ * type selected by the client. */
+#define EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE 1
+/** Extended ORPort authentication is waiting for the client nonce. */
+#define EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE 2
+/** Extended ORPort authentication is waiting for the client hash. */
+#define EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH 3
+#define EXT_OR_CONN_STATE_AUTH_MAX 3
+/** Authentication finished and the Extended ORPort is now accepting
+ * traffic. */
+#define EXT_OR_CONN_STATE_OPEN 4
+/** Extended ORPort is flushing its last messages and preparing to
+ * start accepting OR connections. */
+#define EXT_OR_CONN_STATE_FLUSHING 5
+#define EXT_OR_CONN_STATE_MAX_ 5
+
#define EXIT_CONN_STATE_MIN_ 1
/** State for an exit connection: waiting for response from DNS farm. */
#define EXIT_CONN_STATE_RESOLVING 1
@@ -1082,6 +1107,13 @@ typedef struct var_cell_t {
uint8_t payload[FLEXIBLE_ARRAY_MEMBER];
} var_cell_t;
+/** A parsed Extended ORPort message. */
+typedef struct ext_or_cmd_t {
+ uint16_t cmd; /** Command type */
+ uint16_t len; /** Body length */
+ char body[FLEXIBLE_ARRAY_MEMBER]; /** Message body */
+} ext_or_cmd_t;
+
/** A cell as packed for writing to the network. */
typedef struct packed_cell_t {
/** Next cell queued on this circuit. */
@@ -1163,7 +1195,7 @@ typedef struct connection_t {
* *_CONNECTION_MAGIC. */
uint8_t state; /**< Current state of this connection. */
- unsigned int type:4; /**< What kind of connection is this? */
+ unsigned int type:5; /**< What kind of connection is this? */
unsigned int purpose:5; /**< Only used for DIR and EXIT types currently. */
/* The next fields are all one-bit booleans. Some are only applicable to
@@ -1405,6 +1437,9 @@ typedef struct or_handshake_state_t {
/**@}*/
} or_handshake_state_t;
+/** Length of Extended ORPort connection identifier. */
+#define EXT_OR_CONN_ID_LEN DIGEST_LEN /* 20 */
+
/** Subtype of connection_t for an "OR connection" -- that is, one that speaks
* cells over TLS. */
typedef struct or_connection_t {
@@ -1413,6 +1448,20 @@ typedef struct or_connection_t {
/** Hash of the public RSA key for the other side's identity key, or zeroes
* if the other side hasn't shown us a valid identity key. */
char identity_digest[DIGEST_LEN];
+
+ /** Extended ORPort connection identifier. */
+ char *ext_or_conn_id;
+ /** This is the ClientHash value we expect to receive from the
+ * client during the Extended ORPort authentication protocol. We
+ * compute it upon receiving the ClientNoce from the client, and we
+ * compare it with the acual ClientHash value sent by the
+ * client. */
+ char *ext_or_auth_correct_client_hash;
+ /** String carrying the name of the pluggable transport
+ * (e.g. "obfs2") that is obfuscating this connection. If no
+ * pluggable transports are used, it's NULL. */
+ char *ext_or_transport;
+
char *nickname; /**< Nickname of OR on other side (if any). */
tor_tls_t *tls; /**< TLS connection state. */
@@ -3428,6 +3477,8 @@ typedef struct {
char *User; /**< Name of user to run Tor as. */
char *Group; /**< Name of group to run Tor as. */
config_line_t *ORPort_lines; /**< Ports to listen on for OR connections. */
+ /** Ports to listen on for extended OR connections. */
+ config_line_t *ExtORPort_lines;
/** Ports to listen on for SOCKS connections. */
config_line_t *SocksPort_lines;
/** Ports to listen on for transparent pf/netfilter connections. */
@@ -3463,6 +3514,7 @@ typedef struct {
unsigned int ControlPort_set : 1;
unsigned int DirPort_set : 1;
unsigned int DNSPort_set : 1;
+ unsigned int ExtORPort_set : 1;
/**@}*/
int AssumeReachable; /**< Whether to publish our descriptor regardless. */
@@ -3742,7 +3794,10 @@ typedef struct {
int CookieAuthentication; /**< Boolean: do we enable cookie-based auth for
* the control system? */
- char *CookieAuthFile; /**< Location of a cookie authentication file. */
+ char *CookieAuthFile; /**< Filesystem location of a ControlPort
+ * authentication cookie. */
+ char *ExtORPortCookieAuthFile; /**< Filesystem location of Extended
+ * ORPort authentication cookie. */
int CookieAuthFileGroupReadable; /**< Boolean: Is the CookieAuthFile g+r? */
int LeaveStreamsUnattached; /**< Boolean: Does Tor attach new streams to
* circuits itself (0), or does it expect a controller
diff --git a/src/or/transports.c b/src/or/transports.c
index 62cc1a864..f6bbbe81c 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -96,6 +96,8 @@
#include "router.h"
#include "statefile.h"
#include "entrynodes.h"
+#include "connection_or.h"
+#include "ext_orport.h"
static process_environment_t *
create_managed_proxy_environment(const managed_proxy_t *mp);
@@ -1198,6 +1200,8 @@ get_bindaddr_for_server_proxy(const managed_proxy_t *mp)
static process_environment_t *
create_managed_proxy_environment(const managed_proxy_t *mp)
{
+ const or_options_t *options = get_options();
+
/* Environment variables to be added to or set in mp's environment. */
smartlist_t *envs = smartlist_new();
/* XXXX The next time someone touches this code, shorten the name of
@@ -1261,7 +1265,23 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
* (If we remove this line entirely, some joker will stick this
* variable in Tor's environment and crash PTs that try to parse
* it even when not run in server mode.) */
- smartlist_add(envs, tor_strdup("TOR_PT_EXTENDED_SERVER_PORT="));
+
+ if (options->ExtORPort_lines) {
+ char *ext_or_addrport_tmp =
+ get_first_listener_addrport_string(CONN_TYPE_EXT_OR_LISTENER);
+ char *cookie_file_loc = get_ext_or_auth_cookie_file_name();
+
+ smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=%s",
+ ext_or_addrport_tmp);
+ smartlist_add_asprintf(envs, "TOR_PT_AUTH_COOKIE_FILE=%s",
+ cookie_file_loc);
+
+ tor_free(ext_or_addrport_tmp);
+ tor_free(cookie_file_loc);
+
+ } else {
+ smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=");
+ }
}
SMARTLIST_FOREACH_BEGIN(envs, const char *, env_var) {