Merge branch 'bug5040_4773_rebase_3'

This commit is contained in:
Nick Mathewson 2013-08-15 12:04:56 -04:00
commit 74262f1571
34 changed files with 2828 additions and 781 deletions

4
changes/bug5040 Normal file
View File

@ -0,0 +1,4 @@
o Minor features:
- Bridges now track the usage of their pluggable transports and
report statistics in their extra-info descriptors. Resolves
ticket 5040.

View File

@ -2404,8 +2404,8 @@ crypto_seed_rng(int startup)
/** Write <b>n</b> bytes of strong random data to <b>to</b>. Return 0 on
* success, -1 on failure.
*/
int
crypto_rand(char *to, size_t n)
MOCK_IMPL(int,
crypto_rand, (char *to, size_t n))
{
int r;
tor_assert(n < INT_MAX);

View File

@ -248,7 +248,7 @@ int crypto_expand_key_material_rfc5869_sha256(
/* random numbers */
int crypto_seed_rng(int startup);
int crypto_rand(char *to, size_t n);
MOCK_DECL(int,crypto_rand,(char *to, size_t n));
int crypto_strongest_rand(uint8_t *out, size_t out_len);
int crypto_rand_int(unsigned int max);
uint64_t crypto_rand_uint64(uint64_t max);

View File

@ -2216,9 +2216,9 @@ write_bytes_to_file_impl(const char *fname, const char *str, size_t len,
/** As write_str_to_file, but does not assume a NUL-terminated
* string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */
int
write_bytes_to_file(const char *fname, const char *str, size_t len,
int bin)
MOCK_IMPL(int,
write_bytes_to_file,(const char *fname, const char *str, size_t len,
int bin))
{
return write_bytes_to_file_impl(fname, str, len,
OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT));

View File

@ -355,8 +355,9 @@ FILE *fdopen_file(open_file_t *file_data);
int finish_writing_to_file(open_file_t *file_data);
int abort_writing_to_file(open_file_t *file_data);
int write_str_to_file(const char *fname, const char *str, int bin);
int write_bytes_to_file(const char *fname, const char *str, size_t len,
int bin);
MOCK_DECL(int,
write_bytes_to_file,(const char *fname, const char *str, size_t len,
int bin));
/** An ad-hoc type to hold a string of characters and a count; used by
* write_chunks_to_file. */
typedef struct sized_chunk_t {

View File

@ -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

View File

@ -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);

View File

@ -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 */
}

View File

@ -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
/*

View File

@ -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;
@ -405,6 +408,30 @@ channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out)
return 1;
}
/**
* 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
*

View File

@ -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);
}
@ -5688,6 +5722,14 @@ parse_ports(or_options_t *options, int validate_only,
*msg = tor_strdup("Invalid ORPort/ORListenAddress configuration");
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,
@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -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,

View File

@ -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);
}

View File

@ -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 */

View File

@ -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. */

648
src/or/ext_orport.c Normal file
View File

@ -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);
}

42
src/or/ext_orport.h Normal file
View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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 \

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -18,6 +18,7 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
src_test_test_SOURCES = \
src/test/test.c \
src/test/test_addr.c \
src/test/test_buffers.c \
src/test/test_cell_formats.c \
src/test/test_circuitlist.c \
src/test/test_circuitmux.c \
@ -26,11 +27,13 @@ src_test_test_SOURCES = \
src/test/test_cell_queue.c \
src/test/test_data.c \
src/test/test_dir.c \
src/test/test_extorport.c \
src/test/test_introduce.c \
src/test/test_microdesc.c \
src/test/test_options.c \
src/test/test_pt.c \
src/test/test_replay.c \
src/test/test_socks.c \
src/test/test_util.c \
src/test/test_config.c \
src/ext/tinytest.c

View File

@ -28,7 +28,6 @@ const char tor_git_revision[] = "";
/* These macros pull in declarations for some functions and structures that
* are typically file-private. */
#define BUFFERS_PRIVATE
#define GEOIP_PRIVATE
#define ROUTER_PRIVATE
#define CIRCUITSTATS_PRIVATE
@ -42,7 +41,6 @@ long int lround(double x);
double fabs(double x);
#include "or.h"
#include "buffers.h"
#include "circuitstats.h"
#include "config.h"
#include "connection_edge.h"
@ -215,622 +213,6 @@ free_pregenerated_keys(void)
}
}
typedef struct socks_test_data_t {
socks_request_t *req;
buf_t *buf;
} socks_test_data_t;
static void *
socks_test_setup(const struct testcase_t *testcase)
{
socks_test_data_t *data = tor_malloc(sizeof(socks_test_data_t));
(void)testcase;
data->buf = buf_new_with_capacity(256);
data->req = socks_request_new();
config_register_addressmaps(get_options());
return data;
}
static int
socks_test_cleanup(const struct testcase_t *testcase, void *ptr)
{
socks_test_data_t *data = ptr;
(void)testcase;
buf_free(data->buf);
socks_request_free(data->req);
tor_free(data);
return 1;
}
const struct testcase_setup_t socks_setup = {
socks_test_setup, socks_test_cleanup
};
#define SOCKS_TEST_INIT() \
socks_test_data_t *testdata = ptr; \
buf_t *buf = testdata->buf; \
socks_request_t *socks = testdata->req;
#define ADD_DATA(buf, s) \
write_to_buf(s, sizeof(s)-1, buf)
static void
socks_request_clear(socks_request_t *socks)
{
tor_free(socks->username);
tor_free(socks->password);
memset(socks, 0, sizeof(socks_request_t));
}
/** Perform unsupported SOCKS 4 commands */
static void
test_socks_4_unsupported_commands(void *ptr)
{
SOCKS_TEST_INIT();
/* SOCKS 4 Send BIND [02] to IP address 2.2.2.2:4369 */
ADD_DATA(buf, "\x04\x02\x11\x11\x02\x02\x02\x02\x00");
test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks) == -1);
test_eq(4, socks->socks_version);
test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
done:
;
}
/** Perform supported SOCKS 4 commands */
static void
test_socks_4_supported_commands(void *ptr)
{
SOCKS_TEST_INIT();
test_eq(0, buf_datalen(buf));
/* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4370 */
ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x03\x00");
test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks) == 1);
test_eq(4, socks->socks_version);
test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
test_eq(SOCKS_COMMAND_CONNECT, socks->command);
test_streq("2.2.2.3", socks->address);
test_eq(4370, socks->port);
test_assert(socks->got_auth == 0);
test_assert(! socks->username);
test_eq(0, buf_datalen(buf));
socks_request_clear(socks);
/* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4369 with userid*/
ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x04me\x00");
test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks) == 1);
test_eq(4, socks->socks_version);
test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
test_eq(SOCKS_COMMAND_CONNECT, socks->command);
test_streq("2.2.2.4", socks->address);
test_eq(4370, socks->port);
test_assert(socks->got_auth == 1);
test_assert(socks->username);
test_eq(2, socks->usernamelen);
test_memeq("me", socks->username, 2);
test_eq(0, buf_datalen(buf));
socks_request_clear(socks);
/* SOCKS 4a Send RESOLVE [F0] request for torproject.org */
ADD_DATA(buf, "\x04\xF0\x01\x01\x00\x00\x00\x02me\x00torproject.org\x00");
test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks) == 1);
test_eq(4, socks->socks_version);
test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
test_streq("torproject.org", socks->address);
test_eq(0, buf_datalen(buf));
done:
;
}
/** Perform unsupported SOCKS 5 commands */
static void
test_socks_5_unsupported_commands(void *ptr)
{
SOCKS_TEST_INIT();
/* SOCKS 5 Send unsupported BIND [02] command */
ADD_DATA(buf, "\x05\x02\x00\x01");
test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks), 0);
test_eq(0, buf_datalen(buf));
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(0, socks->reply[1]);
ADD_DATA(buf, "\x05\x02\x00\x01\x02\x02\x02\x01\x01\x01");
test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks), -1);
/* XXX: shouldn't tor reply 'command not supported' [07]? */
buf_clear(buf);
socks_request_clear(socks);
/* SOCKS 5 Send unsupported UDP_ASSOCIATE [03] command */
ADD_DATA(buf, "\x05\x03\x00\x01\x02");
test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks), 0);
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(2, socks->reply[1]);
ADD_DATA(buf, "\x05\x03\x00\x01\x02\x02\x02\x01\x01\x01");
test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks), -1);
/* XXX: shouldn't tor reply 'command not supported' [07]? */
done:
;
}
/** Perform supported SOCKS 5 commands */
static void
test_socks_5_supported_commands(void *ptr)
{
SOCKS_TEST_INIT();
/* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
ADD_DATA(buf, "\x05\x01\x00");
test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks), 0);
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(0, socks->reply[1]);
ADD_DATA(buf, "\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks), 1);
test_streq("2.2.2.2", socks->address);
test_eq(4369, socks->port);
test_eq(0, buf_datalen(buf));
socks_request_clear(socks);
/* SOCKS 5 Send CONNECT [01] to FQDN torproject.org:4369 */
ADD_DATA(buf, "\x05\x01\x00");
ADD_DATA(buf, "\x05\x01\x00\x03\x0Etorproject.org\x11\x11");
test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks), 1);
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(0, socks->reply[1]);
test_streq("torproject.org", socks->address);
test_eq(4369, socks->port);
test_eq(0, buf_datalen(buf));
socks_request_clear(socks);
/* SOCKS 5 Send RESOLVE [F0] request for torproject.org:4369 */
ADD_DATA(buf, "\x05\x01\x00");
ADD_DATA(buf, "\x05\xF0\x00\x03\x0Etorproject.org\x01\x02");
test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks) == 1);
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(0, socks->reply[1]);
test_streq("torproject.org", socks->address);
test_eq(0, buf_datalen(buf));
socks_request_clear(socks);
/* SOCKS 5 Send RESOLVE_PTR [F1] for IP address 2.2.2.5 */
ADD_DATA(buf, "\x05\x01\x00");
ADD_DATA(buf, "\x05\xF1\x00\x01\x02\x02\x02\x05\x01\x03");
test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks) == 1);
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(0, socks->reply[1]);
test_streq("2.2.2.5", socks->address);
test_eq(0, buf_datalen(buf));
done:
;
}
/** Perform SOCKS 5 authentication */
static void
test_socks_5_no_authenticate(void *ptr)
{
SOCKS_TEST_INIT();
/*SOCKS 5 No Authentication */
ADD_DATA(buf,"\x05\x01\x00");
test_assert(!fetch_from_buf_socks(buf, socks,
get_options()->TestSocks,
get_options()->SafeSocks));
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(SOCKS_NO_AUTH, socks->reply[1]);
test_eq(0, buf_datalen(buf));
/*SOCKS 5 Send username/password anyway - pretend to be broken */
ADD_DATA(buf,"\x01\x02\x01\x01\x02\x01\x01");
test_assert(!fetch_from_buf_socks(buf, socks,
get_options()->TestSocks,
get_options()->SafeSocks));
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(1, socks->reply[0]);
test_eq(0, socks->reply[1]);
test_eq(2, socks->usernamelen);
test_eq(2, socks->passwordlen);
test_memeq("\x01\x01", socks->username, 2);
test_memeq("\x01\x01", socks->password, 2);
done:
;
}
/** Perform SOCKS 5 authentication */
static void
test_socks_5_authenticate(void *ptr)
{
SOCKS_TEST_INIT();
/* SOCKS 5 Negotiate username/password authentication */
ADD_DATA(buf, "\x05\x01\x02");
test_assert(!fetch_from_buf_socks(buf, socks,
get_options()->TestSocks,
get_options()->SafeSocks));
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(SOCKS_USER_PASS, socks->reply[1]);
test_eq(5, socks->socks_version);
test_eq(0, buf_datalen(buf));
/* SOCKS 5 Send username/password */
ADD_DATA(buf, "\x01\x02me\x08mypasswd");
test_assert(!fetch_from_buf_socks(buf, socks,
get_options()->TestSocks,
get_options()->SafeSocks));
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(1, socks->reply[0]);
test_eq(0, socks->reply[1]);
test_eq(2, socks->usernamelen);
test_eq(8, socks->passwordlen);
test_memeq("me", socks->username, 2);
test_memeq("mypasswd", socks->password, 8);
done:
;
}
/** Perform SOCKS 5 authentication and send data all in one go */
static void
test_socks_5_authenticate_with_data(void *ptr)
{
SOCKS_TEST_INIT();
/* SOCKS 5 Negotiate username/password authentication */
ADD_DATA(buf, "\x05\x01\x02");
test_assert(!fetch_from_buf_socks(buf, socks,
get_options()->TestSocks,
get_options()->SafeSocks));
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(SOCKS_USER_PASS, socks->reply[1]);
test_eq(5, socks->socks_version);
test_eq(0, buf_datalen(buf));
/* SOCKS 5 Send username/password */
/* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
ADD_DATA(buf, "\x01\x02me\x03you\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
test_assert(fetch_from_buf_socks(buf, socks,
get_options()->TestSocks,
get_options()->SafeSocks) == 1);
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(1, socks->reply[0]);
test_eq(0, socks->reply[1]);
test_streq("2.2.2.2", socks->address);
test_eq(4369, socks->port);
test_eq(2, socks->usernamelen);
test_eq(3, socks->passwordlen);
test_memeq("me", socks->username, 2);
test_memeq("you", socks->password, 3);
done:
;
}
/** Perform SOCKS 5 authentication before method negotiated */
static void
test_socks_5_auth_before_negotiation(void *ptr)
{
SOCKS_TEST_INIT();
/* SOCKS 5 Send username/password */
ADD_DATA(buf, "\x01\x02me\x02me");
test_assert(fetch_from_buf_socks(buf, socks,
get_options()->TestSocks,
get_options()->SafeSocks) == -1);
test_eq(0, socks->socks_version);
test_eq(0, socks->replylen);
test_eq(0, socks->reply[0]);
test_eq(0, socks->reply[1]);
done:
;
}
static void
test_buffer_copy(void *arg)
{
generic_buffer_t *buf=NULL, *buf2=NULL;
const char *s;
size_t len;
char b[256];
int i;
(void)arg;
buf = generic_buffer_new();
tt_assert(buf);
/* Copy an empty buffer. */
tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
tt_assert(buf2);
tt_int_op(0, ==, generic_buffer_len(buf2));
/* Now try with a short buffer. */
s = "And now comes an act of enormous enormance!";
len = strlen(s);
generic_buffer_add(buf, s, len);
tt_int_op(len, ==, generic_buffer_len(buf));
/* Add junk to buf2 so we can test replacing.*/
generic_buffer_add(buf2, "BLARG", 5);
tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
tt_int_op(len, ==, generic_buffer_len(buf2));
generic_buffer_get(buf2, b, len);
test_mem_op(b, ==, s, len);
/* Now free buf2 and retry so we can test allocating */
generic_buffer_free(buf2);
buf2 = NULL;
tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
tt_int_op(len, ==, generic_buffer_len(buf2));
generic_buffer_get(buf2, b, len);
test_mem_op(b, ==, s, len);
/* Clear buf for next test */
generic_buffer_get(buf, b, len);
tt_int_op(generic_buffer_len(buf),==,0);
/* Okay, now let's try a bigger buffer. */
s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit "
"esse quam nihil molestiae consequatur, vel illum qui dolorem eum "
"fugiat quo voluptas nulla pariatur?";
len = strlen(s);
for (i = 0; i < 256; ++i) {
b[0]=i;
generic_buffer_add(buf, b, 1);
generic_buffer_add(buf, s, len);
}
tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
tt_int_op(generic_buffer_len(buf2), ==, generic_buffer_len(buf));
for (i = 0; i < 256; ++i) {
generic_buffer_get(buf2, b, len+1);
tt_int_op((unsigned char)b[0],==,i);
test_mem_op(b+1, ==, s, len);
}
done:
if (buf)
generic_buffer_free(buf);
if (buf2)
generic_buffer_free(buf2);
}
/** Run unit tests for buffers.c */
static void
test_buffers(void)
{
char str[256];
char str2[256];
buf_t *buf = NULL, *buf2 = NULL;
const char *cp;
int j;
size_t r;
/****
* buf_new
****/
if (!(buf = buf_new()))
test_fail();
//test_eq(buf_capacity(buf), 4096);
test_eq(buf_datalen(buf), 0);
/****
* General pointer frobbing
*/
for (j=0;j<256;++j) {
str[j] = (char)j;
}
write_to_buf(str, 256, buf);
write_to_buf(str, 256, buf);
test_eq(buf_datalen(buf), 512);
fetch_from_buf(str2, 200, buf);
test_memeq(str, str2, 200);
test_eq(buf_datalen(buf), 312);
memset(str2, 0, sizeof(str2));
fetch_from_buf(str2, 256, buf);
test_memeq(str+200, str2, 56);
test_memeq(str, str2+56, 200);
test_eq(buf_datalen(buf), 56);
memset(str2, 0, sizeof(str2));
/* Okay, now we should be 512 bytes into the 4096-byte buffer. If we add
* another 3584 bytes, we hit the end. */
for (j=0;j<15;++j) {
write_to_buf(str, 256, buf);
}
assert_buf_ok(buf);
test_eq(buf_datalen(buf), 3896);
fetch_from_buf(str2, 56, buf);
test_eq(buf_datalen(buf), 3840);
test_memeq(str+200, str2, 56);
for (j=0;j<15;++j) {
memset(str2, 0, sizeof(str2));
fetch_from_buf(str2, 256, buf);
test_memeq(str, str2, 256);
}
test_eq(buf_datalen(buf), 0);
buf_free(buf);
buf = NULL;
/* Okay, now make sure growing can work. */
buf = buf_new_with_capacity(16);
//test_eq(buf_capacity(buf), 16);
write_to_buf(str+1, 255, buf);
//test_eq(buf_capacity(buf), 256);
fetch_from_buf(str2, 254, buf);
test_memeq(str+1, str2, 254);
//test_eq(buf_capacity(buf), 256);
assert_buf_ok(buf);
write_to_buf(str, 32, buf);
//test_eq(buf_capacity(buf), 256);
assert_buf_ok(buf);
write_to_buf(str, 256, buf);
assert_buf_ok(buf);
//test_eq(buf_capacity(buf), 512);
test_eq(buf_datalen(buf), 33+256);
fetch_from_buf(str2, 33, buf);
test_eq(*str2, str[255]);
test_memeq(str2+1, str, 32);
//test_eq(buf_capacity(buf), 512);
test_eq(buf_datalen(buf), 256);
fetch_from_buf(str2, 256, buf);
test_memeq(str, str2, 256);
/* now try shrinking: case 1. */
buf_free(buf);
buf = buf_new_with_capacity(33668);
for (j=0;j<67;++j) {
write_to_buf(str,255, buf);
}
//test_eq(buf_capacity(buf), 33668);
test_eq(buf_datalen(buf), 17085);
for (j=0; j < 40; ++j) {
fetch_from_buf(str2, 255,buf);
test_memeq(str2, str, 255);
}
/* now try shrinking: case 2. */
buf_free(buf);
buf = buf_new_with_capacity(33668);
for (j=0;j<67;++j) {
write_to_buf(str,255, buf);
}
for (j=0; j < 20; ++j) {
fetch_from_buf(str2, 255,buf);
test_memeq(str2, str, 255);
}
for (j=0;j<80;++j) {
write_to_buf(str,255, buf);
}
//test_eq(buf_capacity(buf),33668);
for (j=0; j < 120; ++j) {
fetch_from_buf(str2, 255,buf);
test_memeq(str2, str, 255);
}
/* Move from buf to buf. */
buf_free(buf);
buf = buf_new_with_capacity(4096);
buf2 = buf_new_with_capacity(4096);
for (j=0;j<100;++j)
write_to_buf(str, 255, buf);
test_eq(buf_datalen(buf), 25500);
for (j=0;j<100;++j) {
r = 10;
move_buf_to_buf(buf2, buf, &r);
test_eq(r, 0);
}
test_eq(buf_datalen(buf), 24500);
test_eq(buf_datalen(buf2), 1000);
for (j=0;j<3;++j) {
fetch_from_buf(str2, 255, buf2);
test_memeq(str2, str, 255);
}
r = 8192; /*big move*/
move_buf_to_buf(buf2, buf, &r);
test_eq(r, 0);
r = 30000; /* incomplete move */
move_buf_to_buf(buf2, buf, &r);
test_eq(r, 13692);
for (j=0;j<97;++j) {
fetch_from_buf(str2, 255, buf2);
test_memeq(str2, str, 255);
}
buf_free(buf);
buf_free(buf2);
buf = buf2 = NULL;
buf = buf_new_with_capacity(5);
cp = "Testing. This is a moderately long Testing string.";
for (j = 0; cp[j]; j++)
write_to_buf(cp+j, 1, buf);
test_eq(0, buf_find_string_offset(buf, "Testing", 7));
test_eq(1, buf_find_string_offset(buf, "esting", 6));
test_eq(1, buf_find_string_offset(buf, "est", 3));
test_eq(39, buf_find_string_offset(buf, "ing str", 7));
test_eq(35, buf_find_string_offset(buf, "Testing str", 11));
test_eq(32, buf_find_string_offset(buf, "ng ", 3));
test_eq(43, buf_find_string_offset(buf, "string.", 7));
test_eq(-1, buf_find_string_offset(buf, "shrdlu", 6));
test_eq(-1, buf_find_string_offset(buf, "Testing thing", 13));
test_eq(-1, buf_find_string_offset(buf, "ngx", 3));
buf_free(buf);
buf = NULL;
/* Try adding a string too long for any freelist. */
{
char *cp = tor_malloc_zero(65536);
buf = buf_new();
write_to_buf(cp, 65536, buf);
tor_free(cp);
tt_int_op(buf_datalen(buf), ==, 65536);
buf_free(buf);
buf = NULL;
}
done:
if (buf)
buf_free(buf);
if (buf2)
buf_free(buf2);
}
/** Run unit tests for the onion handshake code. */
static void
test_onion_handshake(void)
@ -1620,6 +1002,34 @@ test_rend_fns(void)
tor_free(intro_points_encrypted);
}
/* Record odd numbered fake-IPs using ipv6, even numbered fake-IPs
* using ipv4. Since our fake geoip database is the same between
* ipv4 and ipv6, we should get the same result no matter which
* address family we pick for each IP. */
#define SET_TEST_ADDRESS(i) do { \
if ((i) & 1) { \
SET_TEST_IPV6(i); \
tor_addr_from_in6(&addr, &in6); \
} else { \
tor_addr_from_ipv4h(&addr, (uint32_t) i); \
} \
} while (0)
/* Make sure that country ID actually works. */
#define SET_TEST_IPV6(i) \
do { \
set_uint32(in6.s6_addr + 12, htonl((uint32_t) (i))); \
} while (0)
#define CHECK_COUNTRY(country, val) do { \
/* test ipv4 country lookup */ \
test_streq(country, \
geoip_get_country_name(geoip_get_country_by_ipv4(val))); \
/* test ipv6 country lookup */ \
SET_TEST_IPV6(val); \
test_streq(country, \
geoip_get_country_name(geoip_get_country_by_ipv6(&in6))); \
} while (0)
/** Run unit tests for GeoIP code. */
static void
test_geoip(void)
@ -1630,7 +1040,8 @@ test_geoip(void)
const char *bridge_stats_1 =
"bridge-stats-end 2010-08-12 13:27:30 (86400 s)\n"
"bridge-ips zz=24,xy=8\n"
"bridge-ip-versions v4=16,v6=16\n",
"bridge-ip-versions v4=16,v6=16\n"
"bridge-ip-transports <OR>=24\n",
*dirreq_stats_1 =
"dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
"dirreq-v3-ips ab=8\n"
@ -1694,21 +1105,6 @@ test_geoip(void)
test_eq(4, geoip_get_n_countries());
memset(&in6, 0, sizeof(in6));
/* Make sure that country ID actually works. */
#define SET_TEST_IPV6(i) \
do { \
set_uint32(in6.s6_addr + 12, htonl((uint32_t) (i))); \
} while (0)
#define CHECK_COUNTRY(country, val) do { \
/* test ipv4 country lookup */ \
test_streq(country, \
geoip_get_country_name(geoip_get_country_by_ipv4(val))); \
/* test ipv6 country lookup */ \
SET_TEST_IPV6(val); \
test_streq(country, \
geoip_get_country_name(geoip_get_country_by_ipv6(&in6))); \
} while (0)
CHECK_COUNTRY("??", 3);
CHECK_COUNTRY("ab", 32);
CHECK_COUNTRY("??", 5);
@ -1721,40 +1117,25 @@ test_geoip(void)
SET_TEST_IPV6(3);
test_eq(0, geoip_get_country_by_ipv6(&in6));
#undef CHECK_COUNTRY
/* Record odd numbered fake-IPs using ipv6, even numbered fake-IPs
* using ipv4. Since our fake geoip database is the same between
* ipv4 and ipv6, we should get the same result no matter which
* address family we pick for each IP. */
#define SET_TEST_ADDRESS(i) do { \
if ((i) & 1) { \
SET_TEST_IPV6(i); \
tor_addr_from_in6(&addr, &in6); \
} else { \
tor_addr_from_ipv4h(&addr, (uint32_t) i); \
} \
} while (0)
get_options_mutable()->BridgeRelay = 1;
get_options_mutable()->BridgeRecordUsageByCountry = 1;
/* Put 9 observations in AB... */
for (i=32; i < 40; ++i) {
SET_TEST_ADDRESS(i);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-7200);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-7200);
}
SET_TEST_ADDRESS(225);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-7200);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-7200);
/* and 3 observations in XY, several times. */
for (j=0; j < 10; ++j)
for (i=52; i < 55; ++i) {
SET_TEST_ADDRESS(i);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-3600);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-3600);
}
/* and 17 observations in ZZ... */
for (i=110; i < 127; ++i) {
SET_TEST_ADDRESS(i);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
}
geoip_get_client_history(GEOIP_CLIENT_CONNECT, &s, &v);
test_assert(s);
@ -1803,7 +1184,7 @@ test_geoip(void)
/* Start testing dirreq statistics by making sure that we don't collect
* dirreq stats without initializing them. */
SET_TEST_ADDRESS(100);
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now);
s = geoip_format_dirreq_stats(now + 86400);
test_assert(!s);
@ -1811,7 +1192,7 @@ test_geoip(void)
* dirreq-stats history string. */
geoip_dirreq_stats_init(now);
SET_TEST_ADDRESS(100);
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now);
s = geoip_format_dirreq_stats(now + 86400);
test_streq(dirreq_stats_1, s);
tor_free(s);
@ -1820,7 +1201,7 @@ test_geoip(void)
* don't generate a history string. */
geoip_dirreq_stats_term();
SET_TEST_ADDRESS(101);
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now);
s = geoip_format_dirreq_stats(now + 86400);
test_assert(!s);
@ -1828,7 +1209,7 @@ test_geoip(void)
* that we get an all empty history string. */
geoip_dirreq_stats_init(now);
SET_TEST_ADDRESS(100);
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now);
geoip_reset_dirreq_stats(now);
s = geoip_format_dirreq_stats(now + 86400);
test_streq(dirreq_stats_2, s);
@ -1855,7 +1236,7 @@ test_geoip(void)
/* Start testing entry statistics by making sure that we don't collect
* anything without initializing entry stats. */
SET_TEST_ADDRESS(100);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
s = geoip_format_entry_stats(now + 86400);
test_assert(!s);
@ -1863,7 +1244,7 @@ test_geoip(void)
* entry-stats history string. */
geoip_entry_stats_init(now);
SET_TEST_ADDRESS(100);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
s = geoip_format_entry_stats(now + 86400);
test_streq(entry_stats_1, s);
tor_free(s);
@ -1872,7 +1253,7 @@ test_geoip(void)
* don't generate a history string. */
geoip_entry_stats_term();
SET_TEST_ADDRESS(101);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
s = geoip_format_entry_stats(now + 86400);
test_assert(!s);
@ -1880,15 +1261,12 @@ test_geoip(void)
* that we get an all empty history string. */
geoip_entry_stats_init(now);
SET_TEST_ADDRESS(100);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
geoip_reset_entry_stats(now);
s = geoip_format_entry_stats(now + 86400);
test_streq(entry_stats_2, s);
tor_free(s);
#undef SET_TEST_ADDRESS
#undef SET_TEST_IPV6
/* Stop collecting entry statistics. */
geoip_entry_stats_term();
get_options_mutable()->EntryStatistics = 0;
@ -1898,6 +1276,78 @@ test_geoip(void)
tor_free(v);
}
static void
test_geoip_with_pt(void)
{
time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */
char *s = NULL;
int i;
tor_addr_t addr;
struct in6_addr in6;
get_options_mutable()->BridgeRelay = 1;
get_options_mutable()->BridgeRecordUsageByCountry = 1;
/* No clients seen yet. */
s = geoip_get_transport_history();
tor_assert(!s);
/* 4 connections without a pluggable transport */
for (i=0; i < 4; ++i) {
SET_TEST_ADDRESS(i);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-7200);
}
/* 9 connections with "alpha" */
for (i=4; i < 13; ++i) {
SET_TEST_ADDRESS(i);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "alpha", now-7200);
}
/* one connection with "beta" */
SET_TEST_ADDRESS(13);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "beta", now-7200);
/* 14 connections with "charlie" */
for (i=14; i < 28; ++i) {
SET_TEST_ADDRESS(i);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "charlie", now-7200);
}
/* 131 connections with "ddr" */
for (i=28; i < 159; ++i) {
SET_TEST_ADDRESS(i);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "ddr", now-7200);
}
/* 8 connections with "entropy" */
for (i=159; i < 167; ++i) {
SET_TEST_ADDRESS(i);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "entropy", now-7200);
}
/* 2 connections from the same IP with two different transports. */
SET_TEST_ADDRESS(++i);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "fire", now-7200);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "google", now-7200);
/* Test the transport history string. */
s = geoip_get_transport_history();
tor_assert(s);
test_streq(s, "<OR>=8,alpha=16,beta=8,charlie=16,ddr=136,entropy=8,fire=8,google=8");
/* Stop collecting entry statistics. */
geoip_entry_stats_term();
get_options_mutable()->EntryStatistics = 0;
done:
tor_free(s);
}
#undef SET_TEST_ADDRESS
#undef SET_TEST_IPV6
#undef CHECK_COUNTRY
/** Run unit tests for stats code. */
static void
test_stats(void)
@ -2088,8 +1538,6 @@ const struct testcase_setup_t legacy_setup = {
{ #name, legacy_test_helper, TT_FORK, &legacy_setup, test_ ## name }
static struct testcase_t test_array[] = {
ENT(buffers),
{ "buffer_copy", test_buffer_copy, 0, NULL, NULL },
ENT(onion_handshake),
{ "bad_onion_handshake", test_bad_onion_handshake, 0, NULL, NULL },
#ifdef CURVE25519_ENABLED
@ -2099,29 +1547,14 @@ static struct testcase_t test_array[] = {
ENT(policies),
ENT(rend_fns),
ENT(geoip),
FORK(geoip_with_pt),
FORK(stats),
END_OF_TESTCASES
};
#define SOCKSENT(name) \
{ #name, test_socks_##name, TT_FORK, &socks_setup, NULL }
static struct testcase_t socks_tests[] = {
SOCKSENT(4_unsupported_commands),
SOCKSENT(4_supported_commands),
SOCKSENT(5_unsupported_commands),
SOCKSENT(5_supported_commands),
SOCKSENT(5_no_authenticate),
SOCKSENT(5_auth_before_negotiation),
SOCKSENT(5_authenticate),
SOCKSENT(5_authenticate_with_data),
END_OF_TESTCASES
};
extern struct testcase_t addr_tests[];
extern struct testcase_t buffer_tests[];
extern struct testcase_t crypto_tests[];
extern struct testcase_t container_tests[];
extern struct testcase_t util_tests[];
@ -2136,9 +1569,12 @@ extern struct testcase_t circuitlist_tests[];
extern struct testcase_t circuitmux_tests[];
extern struct testcase_t cell_queue_tests[];
extern struct testcase_t options_tests[];
extern struct testcase_t socks_tests[];
extern struct testcase_t extorport_tests[];
static struct testgroup_t testgroups[] = {
{ "", test_array },
{ "buffer/", buffer_tests },
{ "socks/", socks_tests },
{ "addr/", addr_tests },
{ "crypto/", crypto_tests },
@ -2155,6 +1591,7 @@ static struct testgroup_t testgroups[] = {
{ "circuitlist/", circuitlist_tests },
{ "circuitmux/", circuitmux_tests },
{ "options/", options_tests },
{ "extorport/", extorport_tests },
END_OF_GROUPS
};

342
src/test/test_buffers.c Normal file
View File

@ -0,0 +1,342 @@
/* 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 */
#define BUFFERS_PRIVATE
#include "or.h"
#include "buffers.h"
#include "ext_orport.h"
#include "test.h"
/** Run unit tests for buffers.c */
static void
test_buffers_basic(void *arg)
{
char str[256];
char str2[256];
buf_t *buf = NULL, *buf2 = NULL;
const char *cp;
int j;
size_t r;
(void) arg;
/****
* buf_new
****/
if (!(buf = buf_new()))
test_fail();
//test_eq(buf_capacity(buf), 4096);
test_eq(buf_datalen(buf), 0);
/****
* General pointer frobbing
*/
for (j=0;j<256;++j) {
str[j] = (char)j;
}
write_to_buf(str, 256, buf);
write_to_buf(str, 256, buf);
test_eq(buf_datalen(buf), 512);
fetch_from_buf(str2, 200, buf);
test_memeq(str, str2, 200);
test_eq(buf_datalen(buf), 312);
memset(str2, 0, sizeof(str2));
fetch_from_buf(str2, 256, buf);
test_memeq(str+200, str2, 56);
test_memeq(str, str2+56, 200);
test_eq(buf_datalen(buf), 56);
memset(str2, 0, sizeof(str2));
/* Okay, now we should be 512 bytes into the 4096-byte buffer. If we add
* another 3584 bytes, we hit the end. */
for (j=0;j<15;++j) {
write_to_buf(str, 256, buf);
}
assert_buf_ok(buf);
test_eq(buf_datalen(buf), 3896);
fetch_from_buf(str2, 56, buf);
test_eq(buf_datalen(buf), 3840);
test_memeq(str+200, str2, 56);
for (j=0;j<15;++j) {
memset(str2, 0, sizeof(str2));
fetch_from_buf(str2, 256, buf);
test_memeq(str, str2, 256);
}
test_eq(buf_datalen(buf), 0);
buf_free(buf);
buf = NULL;
/* Okay, now make sure growing can work. */
buf = buf_new_with_capacity(16);
//test_eq(buf_capacity(buf), 16);
write_to_buf(str+1, 255, buf);
//test_eq(buf_capacity(buf), 256);
fetch_from_buf(str2, 254, buf);
test_memeq(str+1, str2, 254);
//test_eq(buf_capacity(buf), 256);
assert_buf_ok(buf);
write_to_buf(str, 32, buf);
//test_eq(buf_capacity(buf), 256);
assert_buf_ok(buf);
write_to_buf(str, 256, buf);
assert_buf_ok(buf);
//test_eq(buf_capacity(buf), 512);
test_eq(buf_datalen(buf), 33+256);
fetch_from_buf(str2, 33, buf);
test_eq(*str2, str[255]);
test_memeq(str2+1, str, 32);
//test_eq(buf_capacity(buf), 512);
test_eq(buf_datalen(buf), 256);
fetch_from_buf(str2, 256, buf);
test_memeq(str, str2, 256);
/* now try shrinking: case 1. */
buf_free(buf);
buf = buf_new_with_capacity(33668);
for (j=0;j<67;++j) {
write_to_buf(str,255, buf);
}
//test_eq(buf_capacity(buf), 33668);
test_eq(buf_datalen(buf), 17085);
for (j=0; j < 40; ++j) {
fetch_from_buf(str2, 255,buf);
test_memeq(str2, str, 255);
}
/* now try shrinking: case 2. */
buf_free(buf);
buf = buf_new_with_capacity(33668);
for (j=0;j<67;++j) {
write_to_buf(str,255, buf);
}
for (j=0; j < 20; ++j) {
fetch_from_buf(str2, 255,buf);
test_memeq(str2, str, 255);
}
for (j=0;j<80;++j) {
write_to_buf(str,255, buf);
}
//test_eq(buf_capacity(buf),33668);
for (j=0; j < 120; ++j) {
fetch_from_buf(str2, 255,buf);
test_memeq(str2, str, 255);
}
/* Move from buf to buf. */
buf_free(buf);
buf = buf_new_with_capacity(4096);
buf2 = buf_new_with_capacity(4096);
for (j=0;j<100;++j)
write_to_buf(str, 255, buf);
test_eq(buf_datalen(buf), 25500);
for (j=0;j<100;++j) {
r = 10;
move_buf_to_buf(buf2, buf, &r);
test_eq(r, 0);
}
test_eq(buf_datalen(buf), 24500);
test_eq(buf_datalen(buf2), 1000);
for (j=0;j<3;++j) {
fetch_from_buf(str2, 255, buf2);
test_memeq(str2, str, 255);
}
r = 8192; /*big move*/
move_buf_to_buf(buf2, buf, &r);
test_eq(r, 0);
r = 30000; /* incomplete move */
move_buf_to_buf(buf2, buf, &r);
test_eq(r, 13692);
for (j=0;j<97;++j) {
fetch_from_buf(str2, 255, buf2);
test_memeq(str2, str, 255);
}
buf_free(buf);
buf_free(buf2);
buf = buf2 = NULL;
buf = buf_new_with_capacity(5);
cp = "Testing. This is a moderately long Testing string.";
for (j = 0; cp[j]; j++)
write_to_buf(cp+j, 1, buf);
test_eq(0, buf_find_string_offset(buf, "Testing", 7));
test_eq(1, buf_find_string_offset(buf, "esting", 6));
test_eq(1, buf_find_string_offset(buf, "est", 3));
test_eq(39, buf_find_string_offset(buf, "ing str", 7));
test_eq(35, buf_find_string_offset(buf, "Testing str", 11));
test_eq(32, buf_find_string_offset(buf, "ng ", 3));
test_eq(43, buf_find_string_offset(buf, "string.", 7));
test_eq(-1, buf_find_string_offset(buf, "shrdlu", 6));
test_eq(-1, buf_find_string_offset(buf, "Testing thing", 13));
test_eq(-1, buf_find_string_offset(buf, "ngx", 3));
buf_free(buf);
buf = NULL;
/* Try adding a string too long for any freelist. */
{
char *cp = tor_malloc_zero(65536);
buf = buf_new();
write_to_buf(cp, 65536, buf);
tor_free(cp);
tt_int_op(buf_datalen(buf), ==, 65536);
buf_free(buf);
buf = NULL;
}
done:
if (buf)
buf_free(buf);
if (buf2)
buf_free(buf2);
}
static void
test_buffer_copy(void *arg)
{
generic_buffer_t *buf=NULL, *buf2=NULL;
const char *s;
size_t len;
char b[256];
int i;
(void)arg;
buf = generic_buffer_new();
tt_assert(buf);
/* Copy an empty buffer. */
tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
tt_assert(buf2);
tt_int_op(0, ==, generic_buffer_len(buf2));
/* Now try with a short buffer. */
s = "And now comes an act of enormous enormance!";
len = strlen(s);
generic_buffer_add(buf, s, len);
tt_int_op(len, ==, generic_buffer_len(buf));
/* Add junk to buf2 so we can test replacing.*/
generic_buffer_add(buf2, "BLARG", 5);
tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
tt_int_op(len, ==, generic_buffer_len(buf2));
generic_buffer_get(buf2, b, len);
test_mem_op(b, ==, s, len);
/* Now free buf2 and retry so we can test allocating */
generic_buffer_free(buf2);
buf2 = NULL;
tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
tt_int_op(len, ==, generic_buffer_len(buf2));
generic_buffer_get(buf2, b, len);
test_mem_op(b, ==, s, len);
/* Clear buf for next test */
generic_buffer_get(buf, b, len);
tt_int_op(generic_buffer_len(buf),==,0);
/* Okay, now let's try a bigger buffer. */
s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit "
"esse quam nihil molestiae consequatur, vel illum qui dolorem eum "
"fugiat quo voluptas nulla pariatur?";
len = strlen(s);
for (i = 0; i < 256; ++i) {
b[0]=i;
generic_buffer_add(buf, b, 1);
generic_buffer_add(buf, s, len);
}
tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
tt_int_op(generic_buffer_len(buf2), ==, generic_buffer_len(buf));
for (i = 0; i < 256; ++i) {
generic_buffer_get(buf2, b, len+1);
tt_int_op((unsigned char)b[0],==,i);
test_mem_op(b+1, ==, s, len);
}
done:
if (buf)
generic_buffer_free(buf);
if (buf2)
generic_buffer_free(buf2);
}
static void
test_buffer_ext_or_cmd(void *arg)
{
ext_or_cmd_t *cmd = NULL;
generic_buffer_t *buf = generic_buffer_new();
char *tmp = NULL;
(void) arg;
/* Empty -- should give "not there. */
tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
tt_ptr_op(NULL, ==, cmd);
/* Three bytes: shouldn't work. */
generic_buffer_add(buf, "\x00\x20\x00", 3);
tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
tt_ptr_op(NULL, ==, cmd);
tt_int_op(3, ==, generic_buffer_len(buf));
/* 0020 0000: That's a nil command. It should work. */
generic_buffer_add(buf, "\x00", 1);
tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
tt_ptr_op(NULL, !=, cmd);
tt_int_op(0x20, ==, cmd->cmd);
tt_int_op(0, ==, cmd->len);
tt_int_op(0, ==, generic_buffer_len(buf));
ext_or_cmd_free(cmd);
cmd = NULL;
/* Now try a length-6 command with one byte missing. */
generic_buffer_add(buf, "\x10\x21\x00\x06""abcde", 9);
tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
tt_ptr_op(NULL, ==, cmd);
generic_buffer_add(buf, "f", 1);
tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
tt_ptr_op(NULL, !=, cmd);
tt_int_op(0x1021, ==, cmd->cmd);
tt_int_op(6, ==, cmd->len);
test_mem_op("abcdef", ==, cmd->body, 6);
tt_int_op(0, ==, generic_buffer_len(buf));
ext_or_cmd_free(cmd);
cmd = NULL;
/* Now try a length-10 command with 4 extra bytes. */
generic_buffer_add(buf, "\xff\xff\x00\x0a"
"loremipsum\x10\x00\xff\xff", 18);
tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
tt_ptr_op(NULL, !=, cmd);
tt_int_op(0xffff, ==, cmd->cmd);
tt_int_op(10, ==, cmd->len);
test_mem_op("loremipsum", ==, cmd->body, 10);
tt_int_op(4, ==, generic_buffer_len(buf));
ext_or_cmd_free(cmd);
cmd = NULL;
/* Finally, let's try a maximum-length command. We already have the header
* waiting. */
tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
tmp = tor_malloc_zero(65535);
generic_buffer_add(buf, tmp, 65535);
tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
tt_ptr_op(NULL, !=, cmd);
tt_int_op(0x1000, ==, cmd->cmd);
tt_int_op(0xffff, ==, cmd->len);
test_mem_op(tmp, ==, cmd->body, 65535);
tt_int_op(0, ==, generic_buffer_len(buf));
ext_or_cmd_free(cmd);
cmd = NULL;
done:
ext_or_cmd_free(cmd);
generic_buffer_free(buf);
tor_free(tmp);
}
struct testcase_t buffer_tests[] = {
{ "basic", test_buffers_basic, 0, NULL, NULL },
{ "copy", test_buffer_copy, 0, NULL, NULL },
{ "ext_or_cmd", test_buffer_ext_or_cmd, 0, NULL, NULL },
END_OF_TESTCASES
};

604
src/test/test_extorport.c Normal file
View File

@ -0,0 +1,604 @@
/* Copyright (c) 2013, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#define CONNECTION_PRIVATE
#define EXT_ORPORT_PRIVATE
#define MAIN_PRIVATE
#include "or.h"
#include "buffers.h"
#include "connection.h"
#include "connection_or.h"
#include "config.h"
#include "control.h"
#include "ext_orport.h"
#include "main.h"
#include "test.h"
/* Test connection_or_remove_from_ext_or_id_map and
* connection_or_set_ext_or_identifier */
static void
test_ext_or_id_map(void *arg)
{
or_connection_t *c1 = NULL, *c2 = NULL, *c3 = NULL;
char *idp = NULL, *idp2 = NULL;
(void)arg;
/* pre-initialization */
tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx"));
c1 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
c2 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
c3 = or_connection_new(CONN_TYPE_OR, AF_INET);
tt_ptr_op(c1->ext_or_conn_id, !=, NULL);
tt_ptr_op(c2->ext_or_conn_id, !=, NULL);
tt_ptr_op(c3->ext_or_conn_id, ==, NULL);
tt_ptr_op(c1, ==, connection_or_get_by_ext_or_id(c1->ext_or_conn_id));
tt_ptr_op(c2, ==, connection_or_get_by_ext_or_id(c2->ext_or_conn_id));
tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx"));
idp = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN);
/* Give c2 a new ID. */
connection_or_set_ext_or_identifier(c2);
test_mem_op(idp, !=, c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN);
idp2 = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN);
tt_assert(!tor_digest_is_zero(idp2));
tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp));
tt_ptr_op(c2, ==, connection_or_get_by_ext_or_id(idp2));
/* Now remove it. */
connection_or_remove_from_ext_or_id_map(c2);
tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp));
tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp2));
done:
if (c1)
connection_free_(TO_CONN(c1));
if (c2)
connection_free_(TO_CONN(c2));
if (c3)
connection_free_(TO_CONN(c3));
tor_free(idp);
tor_free(idp2);
connection_or_clear_ext_or_id_map();
}
/* Simple connection_write_to_buf_impl_ replacement that unconditionally
* writes to outbuf. */
static void
connection_write_to_buf_impl_replacement(const char *string, size_t len,
connection_t *conn, int zlib)
{
(void) zlib;
tor_assert(string);
tor_assert(conn);
write_to_buf(string, len, conn->outbuf);
}
static char *
buf_get_contents(buf_t *buf, size_t *sz_out)
{
char *out;
*sz_out = buf_datalen(buf);
if (*sz_out >= ULONG_MAX)
return NULL; /* C'mon, really? */
out = tor_malloc(*sz_out + 1);
if (fetch_from_buf(out, (unsigned long)*sz_out, buf) != 0) {
tor_free(out);
return NULL;
}
out[*sz_out] = '\0'; /* Hopefully gratuitous. */
return out;
}
static void
test_ext_or_write_command(void *arg)
{
or_connection_t *c1;
char *cp = NULL;
char *buf = NULL;
size_t sz;
(void) arg;
MOCK(connection_write_to_buf_impl_,
connection_write_to_buf_impl_replacement);
c1 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
tt_assert(c1);
/* Length too long */
tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 100, "X", 100000),
<, 0);
/* Empty command */
tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0x99, NULL, 0),
==, 0);
cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz);
tt_int_op(sz, ==, 4);
test_mem_op(cp, ==, "\x00\x99\x00\x00", 4);
tor_free(cp);
/* Medium command. */
tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0x99,
"Wai\0Hello", 9), ==, 0);
cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz);
tt_int_op(sz, ==, 13);
test_mem_op(cp, ==, "\x00\x99\x00\x09Wai\x00Hello", 13);
tor_free(cp);
/* Long command */
buf = tor_malloc(65535);
memset(buf, 'x', 65535);
tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0xf00d,
buf, 65535), ==, 0);
cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz);
tt_int_op(sz, ==, 65539);
test_mem_op(cp, ==, "\xf0\x0d\xff\xff", 4);
test_mem_op(cp+4, ==, buf, 65535);
tor_free(cp);
done:
if (c1)
connection_free_(TO_CONN(c1));
tor_free(cp);
tor_free(buf);
UNMOCK(connection_write_to_buf_impl_);
}
static int
write_bytes_to_file_fail(const char *fname, const char *str, size_t len,
int bin)
{
(void) fname;
(void) str;
(void) len;
(void) bin;
return -1;
}
static void
test_ext_or_init_auth(void *arg)
{
or_options_t *options = get_options_mutable();
const char *fn;
char *cp = NULL;
struct stat st;
char cookie0[32];
(void)arg;
/* Check default filename location */
options->DataDirectory = tor_strdup("foo");
cp = get_ext_or_auth_cookie_file_name();
tt_str_op(cp, ==, "foo"PATH_SEPARATOR"extended_orport_auth_cookie");
tor_free(cp);
/* Shouldn't be initialized already, or our tests will be a bit
* meaningless */
ext_or_auth_cookie = tor_malloc_zero(32);
test_assert(tor_mem_is_zero((char*)ext_or_auth_cookie, 32));
/* Now make sure we use a temporary file */
fn = get_fname("ext_cookie_file");
options->ExtORPortCookieAuthFile = tor_strdup(fn);
cp = get_ext_or_auth_cookie_file_name();
tt_str_op(cp, ==, fn);
tor_free(cp);
/* Test the initialization function with a broken
write_bytes_to_file(). See if the problem is handled properly. */
MOCK(write_bytes_to_file, write_bytes_to_file_fail);
tt_int_op(-1, ==, init_ext_or_cookie_authentication(1));
tt_int_op(ext_or_auth_cookie_is_set, ==, 0);
UNMOCK(write_bytes_to_file);
/* Now do the actual initialization. */
tt_int_op(0, ==, init_ext_or_cookie_authentication(1));
tt_int_op(ext_or_auth_cookie_is_set, ==, 1);
cp = read_file_to_str(fn, RFTS_BIN, &st);
tt_ptr_op(cp, !=, NULL);
tt_int_op(st.st_size, ==, 64);
test_memeq(cp, "! Extended ORPort Auth Cookie !\x0a", 32);
test_memeq(cp+32, ext_or_auth_cookie, 32);
memcpy(cookie0, ext_or_auth_cookie, 32);
test_assert(!tor_mem_is_zero((char*)ext_or_auth_cookie, 32));
/* Operation should be idempotent. */
tt_int_op(0, ==, init_ext_or_cookie_authentication(1));
test_memeq(cookie0, ext_or_auth_cookie, 32);
done:
tor_free(cp);
ext_orport_free_all();
}
static void
test_ext_or_cookie_auth(void *arg)
{
char *reply=NULL, *reply2=NULL, *client_hash=NULL, *client_hash2=NULL;
size_t reply_len=0;
char hmac1[32], hmac2[32];
const char client_nonce[32] =
"Who is the third who walks alway";
char server_hash_input[] =
"ExtORPort authentication server-to-client hash"
"Who is the third who walks alway"
"................................";
char client_hash_input[] =
"ExtORPort authentication client-to-server hash"
"Who is the third who walks alway"
"................................";
(void)arg;
tt_int_op(strlen(client_hash_input), ==, 46+32+32);
tt_int_op(strlen(server_hash_input), ==, 46+32+32);
ext_or_auth_cookie = tor_malloc_zero(32);
memcpy(ext_or_auth_cookie, "s beside you? When I count, ther", 32);
ext_or_auth_cookie_is_set = 1;
/* For this authentication, the client sends 32 random bytes (ClientNonce)
* The server replies with 32 byte ServerHash and 32 byte ServerNonce,
* where ServerHash is:
* HMAC-SHA256(CookieString,
* "ExtORPort authentication server-to-client hash" | ClientNonce |
* ServerNonce)"
* The client must reply with 32-byte ClientHash, which we compute as:
* ClientHash is computed as:
* HMAC-SHA256(CookieString,
* "ExtORPort authentication client-to-server hash" | ClientNonce |
* ServerNonce)
*/
/* Wrong length */
tt_int_op(-1, ==,
handle_client_auth_nonce(client_nonce, 33, &client_hash, &reply,
&reply_len));
tt_int_op(-1, ==,
handle_client_auth_nonce(client_nonce, 31, &client_hash, &reply,
&reply_len));
/* Now let's try this for real! */
tt_int_op(0, ==,
handle_client_auth_nonce(client_nonce, 32, &client_hash, &reply,
&reply_len));
tt_int_op(reply_len, ==, 64);
tt_ptr_op(reply, !=, NULL);
tt_ptr_op(client_hash, !=, NULL);
/* Fill in the server nonce into the hash inputs... */
memcpy(server_hash_input+46+32, reply+32, 32);
memcpy(client_hash_input+46+32, reply+32, 32);
/* Check the HMACs are correct... */
crypto_hmac_sha256(hmac1, (char*)ext_or_auth_cookie, 32, server_hash_input,
46+32+32);
crypto_hmac_sha256(hmac2, (char*)ext_or_auth_cookie, 32, client_hash_input,
46+32+32);
test_memeq(hmac1, reply, 32);
test_memeq(hmac2, client_hash, 32);
/* Now do it again and make sure that the results are *different* */
tt_int_op(0, ==,
handle_client_auth_nonce(client_nonce, 32, &client_hash2, &reply2,
&reply_len));
test_memneq(reply2, reply, reply_len);
test_memneq(client_hash2, client_hash, 32);
/* But that this one checks out too. */
memcpy(server_hash_input+46+32, reply2+32, 32);
memcpy(client_hash_input+46+32, reply2+32, 32);
/* Check the HMACs are correct... */
crypto_hmac_sha256(hmac1, (char*)ext_or_auth_cookie, 32, server_hash_input,
46+32+32);
crypto_hmac_sha256(hmac2, (char*)ext_or_auth_cookie, 32, client_hash_input,
46+32+32);
test_memeq(hmac1, reply2, 32);
test_memeq(hmac2, client_hash2, 32);
done:
tor_free(reply);
tor_free(client_hash);
tor_free(reply2);
tor_free(client_hash2);
}
static int
crypto_rand_return_tse_str(char *to, size_t n)
{
if (n != 32) {
TT_FAIL(("Asked for %d bytes, not 32", (int)n));
return -1;
}
memcpy(to, "te road There is always another ", 32);
return 0;
}
static void
test_ext_or_cookie_auth_testvec(void *arg)
{
char *reply=NULL, *client_hash=NULL;
size_t reply_len;
char *mem_op_hex_tmp=NULL;
const char client_nonce[] = "But when I look ahead up the whi";
(void)arg;
ext_or_auth_cookie = tor_malloc_zero(32);
memcpy(ext_or_auth_cookie, "Gliding wrapt in a brown mantle," , 32);
ext_or_auth_cookie_is_set = 1;
MOCK(crypto_rand, crypto_rand_return_tse_str);
tt_int_op(0, ==,
handle_client_auth_nonce(client_nonce, 32, &client_hash, &reply,
&reply_len));
tt_ptr_op(reply, !=, NULL );
tt_ptr_op(reply_len, ==, 64);
test_memeq(reply+32, "te road There is always another ", 32);
/* HMACSHA256("Gliding wrapt in a brown mantle,"
* "ExtORPort authentication server-to-client hash"
* "But when I look ahead up the write road There is always another ");
*/
test_memeq_hex(reply,
"ec80ed6e546d3b36fdfc22fe1315416b"
"029f1ade7610d910878b62eeb7403821");
/* HMACSHA256("Gliding wrapt in a brown mantle,"
* "ExtORPort authentication client-to-server hash"
* "But when I look ahead up the write road There is always another ");
* (Both values computed using Python CLI.)
*/
test_memeq_hex(client_hash,
"ab391732dd2ed968cd40c087d1b1f25b"
"33b3cd77ff79bd80c2074bbf438119a2");
done:
UNMOCK(crypto_rand);
tor_free(reply);
tor_free(client_hash);
tor_free(mem_op_hex_tmp);
}
static void
ignore_bootstrap_problem(const char *warn, int reason)
{
(void)warn;
(void)reason;
}
static int is_reading = 1;
static int handshake_start_called = 0;
static void
note_read_stopped(connection_t *conn)
{
(void)conn;
is_reading=0;
}
static void
note_read_started(connection_t *conn)
{
(void)conn;
is_reading=1;
}
static int
handshake_start(or_connection_t *conn, int receiving)
{
if (!conn || !receiving)
TT_FAIL(("Bad arguments to handshake_start"));
handshake_start_called = 1;
return 0;
}
#define WRITE(s,n) \
do { \
write_to_buf((s), (n), TO_CONN(conn)->inbuf); \
} while (0)
#define CONTAINS(s,n) \
do { \
tt_int_op((n), <=, sizeof(b)); \
tt_int_op(buf_datalen(TO_CONN(conn)->outbuf), ==, (n)); \
if ((n)) { \
fetch_from_buf(b, (n), TO_CONN(conn)->outbuf); \
test_memeq(b, (s), (n)); \
} \
} while (0)
/* Helper: Do a successful Extended ORPort authentication handshake. */
static void
do_ext_or_handshake(or_connection_t *conn)
{
char b[256];
tt_int_op(0, ==, connection_ext_or_start_auth(conn));
CONTAINS("\x01\x00", 2);
WRITE("\x01", 1);
WRITE("But when I look ahead up the whi", 32);
MOCK(crypto_rand, crypto_rand_return_tse_str);
tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
UNMOCK(crypto_rand);
tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH);
CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b"
"\x02\x9f\x1a\xde\x76\x10\xd9\x10\x87\x8b\x62\xee\xb7\x40\x38\x21"
"te road There is always another ", 64);
/* Send the right response this time. */
WRITE("\xab\x39\x17\x32\xdd\x2e\xd9\x68\xcd\x40\xc0\x87\xd1\xb1\xf2\x5b"
"\x33\xb3\xcd\x77\xff\x79\xbd\x80\xc2\x07\x4b\xbf\x43\x81\x19\xa2",
32);
tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
CONTAINS("\x01", 1);
tt_assert(! TO_CONN(conn)->marked_for_close);
tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_OPEN);
done: ;
}
static void
test_ext_or_handshake(void *arg)
{
or_connection_t *conn=NULL;
char b[256];
(void) arg;
MOCK(connection_write_to_buf_impl_,
connection_write_to_buf_impl_replacement);
/* Use same authenticators as for test_ext_or_cookie_auth_testvec */
ext_or_auth_cookie = tor_malloc_zero(32);
memcpy(ext_or_auth_cookie, "Gliding wrapt in a brown mantle," , 32);
ext_or_auth_cookie_is_set = 1;
init_connection_lists();
conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
tt_int_op(0, ==, connection_ext_or_start_auth(conn));
/* The server starts by telling us about the one supported authtype. */
CONTAINS("\x01\x00", 2);
/* Say the client hasn't responded yet. */
tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
/* Let's say the client replies badly. */
WRITE("\x99", 1);
tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
CONTAINS("", 0);
tt_assert(TO_CONN(conn)->marked_for_close);
close_closeable_connections();
conn = NULL;
/* Okay, try again. */
conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
tt_int_op(0, ==, connection_ext_or_start_auth(conn));
CONTAINS("\x01\x00", 2);
/* Let's say the client replies sensibly this time. "Yes, AUTHTYPE_COOKIE
* sounds delicious. Let's have some of that!" */
WRITE("\x01", 1);
/* Let's say that the client also sends part of a nonce. */
WRITE("But when I look ", 16);
tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
CONTAINS("", 0);
tt_int_op(TO_CONN(conn)->state, ==,
EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE);
/* Pump it again. Nothing should happen. */
tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
/* send the rest of the nonce. */
WRITE("ahead up the whi", 16);
MOCK(crypto_rand, crypto_rand_return_tse_str);
tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
UNMOCK(crypto_rand);
/* We should get the right reply from the server. */
CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b"
"\x02\x9f\x1a\xde\x76\x10\xd9\x10\x87\x8b\x62\xee\xb7\x40\x38\x21"
"te road There is always another ", 64);
/* Send the wrong response. */
WRITE("not with a bang but a whimper...", 32);
MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
CONTAINS("\x00", 1);
tt_assert(TO_CONN(conn)->marked_for_close);
/* XXXX Hold-open-until-flushed. */
close_closeable_connections();
conn = NULL;
UNMOCK(control_event_bootstrap_problem);
MOCK(connection_start_reading, note_read_started);
MOCK(connection_stop_reading, note_read_stopped);
MOCK(connection_tls_start_handshake, handshake_start);
/* Okay, this time let's succeed. */
conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
do_ext_or_handshake(conn);
/* Now let's run through some messages. */
/* First let's send some junk and make sure it's ignored. */
WRITE("\xff\xf0\x00\x03""ABC", 7);
tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
CONTAINS("", 0);
/* Now let's send a USERADDR command. */
WRITE("\x00\x01\x00\x0c""1.2.3.4:5678", 16);
tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
tt_int_op(TO_CONN(conn)->port, ==, 5678);
tt_int_op(tor_addr_to_ipv4h(&TO_CONN(conn)->addr), ==, 0x01020304);
/* Now let's send a TRANSPORT command. */
WRITE("\x00\x02\x00\x07""rfc1149", 11);
tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
tt_ptr_op(NULL, !=, conn->ext_or_transport);
tt_str_op("rfc1149", ==, conn->ext_or_transport);
tt_int_op(is_reading,==,1);
tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_OPEN);
/* DONE */
WRITE("\x00\x00\x00\x00", 4);
tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_FLUSHING);
tt_int_op(is_reading,==,0);
CONTAINS("\x10\x00\x00\x00", 4);
tt_int_op(handshake_start_called,==,0);
tt_int_op(0, ==, connection_ext_or_finished_flushing(conn));
tt_int_op(is_reading,==,1);
tt_int_op(handshake_start_called,==,1);
tt_int_op(TO_CONN(conn)->type, ==, CONN_TYPE_OR);
tt_int_op(TO_CONN(conn)->state, ==, 0);
close_closeable_connections();
conn = NULL;
/* Okay, this time let's succeed the handshake but fail the USERADDR
command. */
conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
do_ext_or_handshake(conn);
/* USERADDR command with an extra NUL byte */
WRITE("\x00\x01\x00\x0d""1.2.3.4:5678\x00", 17);
MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
CONTAINS("", 0);
tt_assert(TO_CONN(conn)->marked_for_close);
close_closeable_connections();
conn = NULL;
UNMOCK(control_event_bootstrap_problem);
/* Now fail the TRANSPORT command. */
conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
do_ext_or_handshake(conn);
/* TRANSPORT command with an extra NUL byte */
WRITE("\x00\x02\x00\x08""rfc1149\x00", 12);
MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
CONTAINS("", 0);
tt_assert(TO_CONN(conn)->marked_for_close);
close_closeable_connections();
conn = NULL;
UNMOCK(control_event_bootstrap_problem);
/* Now fail the TRANSPORT command. */
conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
do_ext_or_handshake(conn);
/* TRANSPORT command with transport name with symbols (not a
C-identifier) */
WRITE("\x00\x02\x00\x07""rf*1149", 11);
MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
CONTAINS("", 0);
tt_assert(TO_CONN(conn)->marked_for_close);
close_closeable_connections();
conn = NULL;
UNMOCK(control_event_bootstrap_problem);
done:
UNMOCK(connection_write_to_buf_impl_);
UNMOCK(crypto_rand);
if (conn)
connection_free_(TO_CONN(conn));
#undef CONTAINS
#undef WRITE
}
struct testcase_t extorport_tests[] = {
{ "id_map", test_ext_or_id_map, TT_FORK, NULL, NULL },
{ "write_command", test_ext_or_write_command, TT_FORK, NULL, NULL },
{ "init_auth", test_ext_or_init_auth, TT_FORK, NULL, NULL },
{ "cookie_auth", test_ext_or_cookie_auth, TT_FORK, NULL, NULL },
{ "cookie_auth_testvec", test_ext_or_cookie_auth_testvec, TT_FORK,
NULL, NULL },
{ "handshake", test_ext_or_handshake, TT_FORK, NULL, NULL },
END_OF_TESTCASES
};

View File

@ -148,6 +148,8 @@ test_options_validate(void *arg)
(void)arg;
setup_log_callback();
WANT_ERR("ExtORPort 500000", "Invalid ExtORPort");
WANT_ERR_LOG("ServerTransportOptions trebuchet",
"ServerTransportOptions did not parse",
LOG_WARN, "Too few arguments");
@ -157,7 +159,6 @@ test_options_validate(void *arg)
"ServerTransportOptions did not parse",
LOG_WARN, "\"slingsnappy\" is not a k=v");
// done:
clear_log_messages();
return;
}

393
src/test/test_socks.c Normal file
View File

@ -0,0 +1,393 @@
/* 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 */
#include "or.h"
#include "buffers.h"
#include "config.h"
#include "test.h"
typedef struct socks_test_data_t {
socks_request_t *req;
buf_t *buf;
} socks_test_data_t;
static void *
socks_test_setup(const struct testcase_t *testcase)
{
socks_test_data_t *data = tor_malloc(sizeof(socks_test_data_t));
(void)testcase;
data->buf = buf_new_with_capacity(256);
data->req = socks_request_new();
config_register_addressmaps(get_options());
return data;
}
static int
socks_test_cleanup(const struct testcase_t *testcase, void *ptr)
{
socks_test_data_t *data = ptr;
(void)testcase;
buf_free(data->buf);
socks_request_free(data->req);
tor_free(data);
return 1;
}
const struct testcase_setup_t socks_setup = {
socks_test_setup, socks_test_cleanup
};
#define SOCKS_TEST_INIT() \
socks_test_data_t *testdata = ptr; \
buf_t *buf = testdata->buf; \
socks_request_t *socks = testdata->req;
#define ADD_DATA(buf, s) \
write_to_buf(s, sizeof(s)-1, buf)
static void
socks_request_clear(socks_request_t *socks)
{
tor_free(socks->username);
tor_free(socks->password);
memset(socks, 0, sizeof(socks_request_t));
}
/** Perform unsupported SOCKS 4 commands */
static void
test_socks_4_unsupported_commands(void *ptr)
{
SOCKS_TEST_INIT();
/* SOCKS 4 Send BIND [02] to IP address 2.2.2.2:4369 */
ADD_DATA(buf, "\x04\x02\x11\x11\x02\x02\x02\x02\x00");
test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks) == -1);
test_eq(4, socks->socks_version);
test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
done:
;
}
/** Perform supported SOCKS 4 commands */
static void
test_socks_4_supported_commands(void *ptr)
{
SOCKS_TEST_INIT();
test_eq(0, buf_datalen(buf));
/* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4370 */
ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x03\x00");
test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks) == 1);
test_eq(4, socks->socks_version);
test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
test_eq(SOCKS_COMMAND_CONNECT, socks->command);
test_streq("2.2.2.3", socks->address);
test_eq(4370, socks->port);
test_assert(socks->got_auth == 0);
test_assert(! socks->username);
test_eq(0, buf_datalen(buf));
socks_request_clear(socks);
/* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4369 with userid*/
ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x04me\x00");
test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks) == 1);
test_eq(4, socks->socks_version);
test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
test_eq(SOCKS_COMMAND_CONNECT, socks->command);
test_streq("2.2.2.4", socks->address);
test_eq(4370, socks->port);
test_assert(socks->got_auth == 1);
test_assert(socks->username);
test_eq(2, socks->usernamelen);
test_memeq("me", socks->username, 2);
test_eq(0, buf_datalen(buf));
socks_request_clear(socks);
/* SOCKS 4a Send RESOLVE [F0] request for torproject.org */
ADD_DATA(buf, "\x04\xF0\x01\x01\x00\x00\x00\x02me\x00torproject.org\x00");
test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks) == 1);
test_eq(4, socks->socks_version);
test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
test_streq("torproject.org", socks->address);
test_eq(0, buf_datalen(buf));
done:
;
}
/** Perform unsupported SOCKS 5 commands */
static void
test_socks_5_unsupported_commands(void *ptr)
{
SOCKS_TEST_INIT();
/* SOCKS 5 Send unsupported BIND [02] command */
ADD_DATA(buf, "\x05\x02\x00\x01");
test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks), 0);
test_eq(0, buf_datalen(buf));
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(0, socks->reply[1]);
ADD_DATA(buf, "\x05\x02\x00\x01\x02\x02\x02\x01\x01\x01");
test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks), -1);
/* XXX: shouldn't tor reply 'command not supported' [07]? */
buf_clear(buf);
socks_request_clear(socks);
/* SOCKS 5 Send unsupported UDP_ASSOCIATE [03] command */
ADD_DATA(buf, "\x05\x03\x00\x01\x02");
test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks), 0);
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(2, socks->reply[1]);
ADD_DATA(buf, "\x05\x03\x00\x01\x02\x02\x02\x01\x01\x01");
test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks), -1);
/* XXX: shouldn't tor reply 'command not supported' [07]? */
done:
;
}
/** Perform supported SOCKS 5 commands */
static void
test_socks_5_supported_commands(void *ptr)
{
SOCKS_TEST_INIT();
/* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
ADD_DATA(buf, "\x05\x01\x00");
test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks), 0);
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(0, socks->reply[1]);
ADD_DATA(buf, "\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks), 1);
test_streq("2.2.2.2", socks->address);
test_eq(4369, socks->port);
test_eq(0, buf_datalen(buf));
socks_request_clear(socks);
/* SOCKS 5 Send CONNECT [01] to FQDN torproject.org:4369 */
ADD_DATA(buf, "\x05\x01\x00");
ADD_DATA(buf, "\x05\x01\x00\x03\x0Etorproject.org\x11\x11");
test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks), 1);
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(0, socks->reply[1]);
test_streq("torproject.org", socks->address);
test_eq(4369, socks->port);
test_eq(0, buf_datalen(buf));
socks_request_clear(socks);
/* SOCKS 5 Send RESOLVE [F0] request for torproject.org:4369 */
ADD_DATA(buf, "\x05\x01\x00");
ADD_DATA(buf, "\x05\xF0\x00\x03\x0Etorproject.org\x01\x02");
test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks) == 1);
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(0, socks->reply[1]);
test_streq("torproject.org", socks->address);
test_eq(0, buf_datalen(buf));
socks_request_clear(socks);
/* SOCKS 5 Send RESOLVE_PTR [F1] for IP address 2.2.2.5 */
ADD_DATA(buf, "\x05\x01\x00");
ADD_DATA(buf, "\x05\xF1\x00\x01\x02\x02\x02\x05\x01\x03");
test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
get_options()->SafeSocks) == 1);
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(0, socks->reply[1]);
test_streq("2.2.2.5", socks->address);
test_eq(0, buf_datalen(buf));
done:
;
}
/** Perform SOCKS 5 authentication */
static void
test_socks_5_no_authenticate(void *ptr)
{
SOCKS_TEST_INIT();
/*SOCKS 5 No Authentication */
ADD_DATA(buf,"\x05\x01\x00");
test_assert(!fetch_from_buf_socks(buf, socks,
get_options()->TestSocks,
get_options()->SafeSocks));
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(SOCKS_NO_AUTH, socks->reply[1]);
test_eq(0, buf_datalen(buf));
/*SOCKS 5 Send username/password anyway - pretend to be broken */
ADD_DATA(buf,"\x01\x02\x01\x01\x02\x01\x01");
test_assert(!fetch_from_buf_socks(buf, socks,
get_options()->TestSocks,
get_options()->SafeSocks));
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(1, socks->reply[0]);
test_eq(0, socks->reply[1]);
test_eq(2, socks->usernamelen);
test_eq(2, socks->passwordlen);
test_memeq("\x01\x01", socks->username, 2);
test_memeq("\x01\x01", socks->password, 2);
done:
;
}
/** Perform SOCKS 5 authentication */
static void
test_socks_5_authenticate(void *ptr)
{
SOCKS_TEST_INIT();
/* SOCKS 5 Negotiate username/password authentication */
ADD_DATA(buf, "\x05\x01\x02");
test_assert(!fetch_from_buf_socks(buf, socks,
get_options()->TestSocks,
get_options()->SafeSocks));
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(SOCKS_USER_PASS, socks->reply[1]);
test_eq(5, socks->socks_version);
test_eq(0, buf_datalen(buf));
/* SOCKS 5 Send username/password */
ADD_DATA(buf, "\x01\x02me\x08mypasswd");
test_assert(!fetch_from_buf_socks(buf, socks,
get_options()->TestSocks,
get_options()->SafeSocks));
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(1, socks->reply[0]);
test_eq(0, socks->reply[1]);
test_eq(2, socks->usernamelen);
test_eq(8, socks->passwordlen);
test_memeq("me", socks->username, 2);
test_memeq("mypasswd", socks->password, 8);
done:
;
}
/** Perform SOCKS 5 authentication and send data all in one go */
static void
test_socks_5_authenticate_with_data(void *ptr)
{
SOCKS_TEST_INIT();
/* SOCKS 5 Negotiate username/password authentication */
ADD_DATA(buf, "\x05\x01\x02");
test_assert(!fetch_from_buf_socks(buf, socks,
get_options()->TestSocks,
get_options()->SafeSocks));
test_eq(2, socks->replylen);
test_eq(5, socks->reply[0]);
test_eq(SOCKS_USER_PASS, socks->reply[1]);
test_eq(5, socks->socks_version);
test_eq(0, buf_datalen(buf));
/* SOCKS 5 Send username/password */
/* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
ADD_DATA(buf, "\x01\x02me\x03you\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
test_assert(fetch_from_buf_socks(buf, socks,
get_options()->TestSocks,
get_options()->SafeSocks) == 1);
test_eq(5, socks->socks_version);
test_eq(2, socks->replylen);
test_eq(1, socks->reply[0]);
test_eq(0, socks->reply[1]);
test_streq("2.2.2.2", socks->address);
test_eq(4369, socks->port);
test_eq(2, socks->usernamelen);
test_eq(3, socks->passwordlen);
test_memeq("me", socks->username, 2);
test_memeq("you", socks->password, 3);
done:
;
}
/** Perform SOCKS 5 authentication before method negotiated */
static void
test_socks_5_auth_before_negotiation(void *ptr)
{
SOCKS_TEST_INIT();
/* SOCKS 5 Send username/password */
ADD_DATA(buf, "\x01\x02me\x02me");
test_assert(fetch_from_buf_socks(buf, socks,
get_options()->TestSocks,
get_options()->SafeSocks) == -1);
test_eq(0, socks->socks_version);
test_eq(0, socks->replylen);
test_eq(0, socks->reply[0]);
test_eq(0, socks->reply[1]);
done:
;
}
#define SOCKSENT(name) \
{ #name, test_socks_##name, TT_FORK, &socks_setup, NULL }
struct testcase_t socks_tests[] = {
SOCKSENT(4_unsupported_commands),
SOCKSENT(4_supported_commands),
SOCKSENT(5_unsupported_commands),
SOCKSENT(5_supported_commands),
SOCKSENT(5_no_authenticate),
SOCKSENT(5_auth_before_negotiation),
SOCKSENT(5_authenticate),
SOCKSENT(5_authenticate_with_data),
END_OF_TESTCASES
};