mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 21:23:58 +01:00
Merge branch 'bug5040_4773_rebase_3'
This commit is contained in:
commit
74262f1571
4
changes/bug5040
Normal file
4
changes/bug5040
Normal 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.
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
}
|
||||
|
@ -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
|
||||
/*
|
||||
|
@ -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
|
||||
*
|
||||
|
103
src/or/config.c
103
src/or/config.c
@ -45,6 +45,7 @@
|
||||
#include "routerset.h"
|
||||
#include "statefile.h"
|
||||
#include "transports.h"
|
||||
#include "ext_orport.h"
|
||||
#ifdef _WIN32
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
@ -230,6 +231,7 @@ static config_var_t option_vars_[] = {
|
||||
V(ExitPolicyRejectPrivate, BOOL, "1"),
|
||||
V(ExitPortStatistics, BOOL, "0"),
|
||||
V(ExtendAllowPrivateAddresses, BOOL, "0"),
|
||||
VPORT(ExtORPort, LINELIST, NULL),
|
||||
V(ExtraInfoStatistics, BOOL, "1"),
|
||||
V(FallbackDir, LINELIST, NULL),
|
||||
|
||||
@ -1473,8 +1475,14 @@ options_act(const or_options_t *old_options)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (init_cookie_authentication(options->CookieAuthentication) < 0) {
|
||||
log_warn(LD_CONFIG,"Error creating cookie authentication file.");
|
||||
if (init_control_cookie_authentication(options->CookieAuthentication) < 0) {
|
||||
log_warn(LD_CONFIG,"Error creating control cookie authentication file.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If we have an ExtORPort, initialize its auth cookie. */
|
||||
if (init_ext_or_cookie_authentication(!!options->ExtORPort_lines) < 0) {
|
||||
log_warn(LD_CONFIG,"Error creating Extended ORPort cookie file.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -5093,6 +5101,27 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname,
|
||||
} SMARTLIST_FOREACH_END(port);
|
||||
}
|
||||
|
||||
/** Warn for every Extended ORPort port in <b>ports</b> that is on a
|
||||
* publicly routable address. */
|
||||
static void
|
||||
warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname)
|
||||
{
|
||||
SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
|
||||
if (port->type != CONN_TYPE_EXT_OR_LISTENER)
|
||||
continue;
|
||||
if (port->is_unix_addr)
|
||||
continue;
|
||||
/* XXX maybe warn even if address is RFC1918? */
|
||||
if (!tor_addr_is_internal(&port->addr, 1)) {
|
||||
log_warn(LD_CONFIG, "You specified a public address '%s' for %sPort. "
|
||||
"This is not advised; this address is supposed to only be "
|
||||
"exposed on localhost so that your pluggable transport "
|
||||
"proxies can connect to it.",
|
||||
fmt_addrport(&port->addr, port->port), portname);
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(port);
|
||||
}
|
||||
|
||||
/** Given a list of port_cfg_t in <b>ports</b>, warn any controller port there
|
||||
* is listening on any non-loopback address. If <b>forbid</b> is true,
|
||||
* then emit a stronger warning and remove the port from the list.
|
||||
@ -5193,6 +5222,7 @@ parse_port_config(smartlist_t *out,
|
||||
smartlist_t *elts;
|
||||
int retval = -1;
|
||||
const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER);
|
||||
const unsigned is_ext_orport = (listener_type == CONN_TYPE_EXT_OR_LISTENER);
|
||||
const unsigned allow_no_options = flags & CL_PORT_NO_OPTIONS;
|
||||
const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS;
|
||||
const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL;
|
||||
@ -5270,6 +5300,8 @@ parse_port_config(smartlist_t *out,
|
||||
if (warn_nonlocal && out) {
|
||||
if (is_control)
|
||||
warn_nonlocal_controller_ports(out, forbid_nonlocal);
|
||||
else if (is_ext_orport)
|
||||
warn_nonlocal_ext_orports(out, portname);
|
||||
else
|
||||
warn_nonlocal_client_ports(out, portname, listener_type);
|
||||
}
|
||||
@ -5543,6 +5575,8 @@ parse_port_config(smartlist_t *out,
|
||||
if (warn_nonlocal && out) {
|
||||
if (is_control)
|
||||
warn_nonlocal_controller_ports(out, forbid_nonlocal);
|
||||
else if (is_ext_orport)
|
||||
warn_nonlocal_ext_orports(out, portname);
|
||||
else
|
||||
warn_nonlocal_client_ports(out, portname, listener_type);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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
648
src/or/ext_orport.c
Normal 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
42
src/or/ext_orport.h
Normal 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
|
||||
|
168
src/or/geoip.c
168
src/or/geoip.c
@ -461,6 +461,10 @@ geoip_db_digest(sa_family_t family)
|
||||
typedef struct clientmap_entry_t {
|
||||
HT_ENTRY(clientmap_entry_t) node;
|
||||
tor_addr_t addr;
|
||||
/* Name of pluggable transport used by this client. NULL if no
|
||||
pluggable transport was used. */
|
||||
char *transport_name;
|
||||
|
||||
/** Time when we last saw this IP address, in MINUTES since the epoch.
|
||||
*
|
||||
* (This will run out of space around 4011 CE. If Tor is still in use around
|
||||
@ -482,12 +486,18 @@ static HT_HEAD(clientmap, clientmap_entry_t) client_history =
|
||||
static INLINE unsigned
|
||||
clientmap_entry_hash(const clientmap_entry_t *a)
|
||||
{
|
||||
return ht_improve_hash(tor_addr_hash(&a->addr));
|
||||
unsigned h = tor_addr_hash(&a->addr);
|
||||
if (a->transport_name)
|
||||
h += ht_string_hash(a->transport_name);
|
||||
return ht_improve_hash(h);
|
||||
}
|
||||
/** Hashtable helper: compare two clientmap_entry_t values for equality. */
|
||||
static INLINE int
|
||||
clientmap_entries_eq(const clientmap_entry_t *a, const clientmap_entry_t *b)
|
||||
{
|
||||
if (strcmp_opt(a->transport_name, b->transport_name))
|
||||
return 0;
|
||||
|
||||
return !tor_addr_compare(&a->addr, &b->addr, CMP_EXACT) &&
|
||||
a->action == b->action;
|
||||
}
|
||||
@ -497,6 +507,17 @@ HT_PROTOTYPE(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
|
||||
HT_GENERATE(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
|
||||
clientmap_entries_eq, 0.6, malloc, realloc, free);
|
||||
|
||||
/** Free all storage held by <b>ent</b>. */
|
||||
static void
|
||||
clientmap_entry_free(clientmap_entry_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
return;
|
||||
|
||||
tor_free(ent->transport_name);
|
||||
tor_free(ent);
|
||||
}
|
||||
|
||||
/** Clear history of connecting clients used by entry and bridge stats. */
|
||||
static void
|
||||
client_history_clear(void)
|
||||
@ -507,7 +528,7 @@ client_history_clear(void)
|
||||
if ((*ent)->action == GEOIP_CLIENT_CONNECT) {
|
||||
this = *ent;
|
||||
next = HT_NEXT_RMV(clientmap, &client_history, ent);
|
||||
tor_free(this);
|
||||
clientmap_entry_free(this);
|
||||
} else {
|
||||
next = HT_NEXT(clientmap, &client_history, ent);
|
||||
}
|
||||
@ -519,10 +540,14 @@ client_history_clear(void)
|
||||
* configured accordingly. */
|
||||
void
|
||||
geoip_note_client_seen(geoip_client_action_t action,
|
||||
const tor_addr_t *addr, time_t now)
|
||||
const tor_addr_t *addr,
|
||||
const char *transport_name,
|
||||
time_t now)
|
||||
{
|
||||
const or_options_t *options = get_options();
|
||||
clientmap_entry_t lookup, *ent;
|
||||
memset(&lookup, 0, sizeof(clientmap_entry_t));
|
||||
|
||||
if (action == GEOIP_CLIENT_CONNECT) {
|
||||
/* Only remember statistics as entry guard or as bridge. */
|
||||
if (!options->EntryStatistics &&
|
||||
@ -534,12 +559,20 @@ geoip_note_client_seen(geoip_client_action_t action,
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug(LD_GENERAL, "Seen client from '%s' with transport '%s'.",
|
||||
safe_str_client(fmt_addr((addr))),
|
||||
transport_name ? transport_name : "<no transport>");
|
||||
|
||||
tor_addr_copy(&lookup.addr, addr);
|
||||
lookup.action = (int)action;
|
||||
lookup.transport_name = (char*) transport_name;
|
||||
ent = HT_FIND(clientmap, &client_history, &lookup);
|
||||
|
||||
if (! ent) {
|
||||
ent = tor_malloc_zero(sizeof(clientmap_entry_t));
|
||||
tor_addr_copy(&ent->addr, addr);
|
||||
if (transport_name)
|
||||
ent->transport_name = tor_strdup(transport_name);
|
||||
ent->action = (int)action;
|
||||
HT_INSERT(clientmap, &client_history, ent);
|
||||
}
|
||||
@ -566,7 +599,7 @@ remove_old_client_helper_(struct clientmap_entry_t *ent, void *_cutoff)
|
||||
{
|
||||
time_t cutoff = *(time_t*)_cutoff / 60;
|
||||
if (ent->last_seen_in_minutes < cutoff) {
|
||||
tor_free(ent);
|
||||
clientmap_entry_free(ent);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
@ -769,6 +802,106 @@ geoip_change_dirreq_state(uint64_t dirreq_id, dirreq_type_t type,
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the bridge-ip-transports string that should be inserted in
|
||||
* our extra-info descriptor. Return NULL if the bridge-ip-transports
|
||||
* line should be empty. */
|
||||
char *
|
||||
geoip_get_transport_history(void)
|
||||
{
|
||||
unsigned granularity = IP_GRANULARITY;
|
||||
/** String hash table <name of transport> -> <number of users>. */
|
||||
strmap_t *transport_counts = strmap_new();
|
||||
|
||||
/** Smartlist that contains copies of the names of the transports
|
||||
that have been used. */
|
||||
smartlist_t *transports_used = smartlist_new();
|
||||
|
||||
/* Special string to signify that no transport was used for this
|
||||
connection. Pluggable transport names can't have symbols in their
|
||||
names, so this string will never collide with a real transport. */
|
||||
static const char* no_transport_str = "<OR>";
|
||||
|
||||
clientmap_entry_t **ent;
|
||||
const char *transport_name = NULL;
|
||||
smartlist_t *string_chunks = smartlist_new();
|
||||
char *the_string = NULL;
|
||||
|
||||
/* If we haven't seen any clients yet, return NULL. */
|
||||
if (HT_EMPTY(&client_history))
|
||||
goto done;
|
||||
|
||||
/** We do the following steps to form the transport history string:
|
||||
* a) Foreach client that uses a pluggable transport, we increase the
|
||||
* times that transport was used by one. If the client did not use
|
||||
* a transport, we increase the number of times someone connected
|
||||
* without obfuscation.
|
||||
* b) Foreach transport we observed, we write its transport history
|
||||
* string and push it to string_chunks. So, for example, if we've
|
||||
* seen 665 obfs2 clients, we write "obfs2=665".
|
||||
* c) We concatenate string_chunks to form the final string.
|
||||
*/
|
||||
|
||||
log_debug(LD_GENERAL,"Starting iteration for transport history. %d clients.",
|
||||
HT_SIZE(&client_history));
|
||||
|
||||
/* Loop through all clients. */
|
||||
HT_FOREACH(ent, clientmap, &client_history) {
|
||||
uintptr_t val;
|
||||
void *ptr;
|
||||
transport_name = (*ent)->transport_name;
|
||||
if (!transport_name)
|
||||
transport_name = no_transport_str;
|
||||
|
||||
/* Increase the count for this transport name. */
|
||||
ptr = strmap_get(transport_counts, transport_name);
|
||||
val = (uintptr_t)ptr;
|
||||
val++;
|
||||
ptr = (void*)val;
|
||||
strmap_set(transport_counts, transport_name, ptr);
|
||||
|
||||
/* If it's the first time we see this transport, note it. */
|
||||
if (val == 1)
|
||||
smartlist_add(transports_used, tor_strdup(transport_name));
|
||||
|
||||
log_debug(LD_GENERAL, "Client from '%s' with transport '%s'. "
|
||||
"I've now seen %d clients.",
|
||||
safe_str_client(fmt_addr(&(*ent)->addr)),
|
||||
transport_name ? transport_name : "<no transport>",
|
||||
(int)val);
|
||||
}
|
||||
|
||||
/* Sort the transport names (helps with unit testing). */
|
||||
smartlist_sort_strings(transports_used);
|
||||
|
||||
/* Loop through all seen transports. */
|
||||
SMARTLIST_FOREACH_BEGIN(transports_used, const char *, transport_name) {
|
||||
void *transport_count_ptr = strmap_get(transport_counts, transport_name);
|
||||
uintptr_t transport_count = (uintptr_t) transport_count_ptr;
|
||||
|
||||
log_debug(LD_GENERAL, "We got "U64_FORMAT" clients with transport '%s'.",
|
||||
U64_PRINTF_ARG((uint64_t)transport_count), transport_name);
|
||||
|
||||
smartlist_add_asprintf(string_chunks, "%s="U64_FORMAT,
|
||||
transport_name,
|
||||
U64_PRINTF_ARG(round_uint64_to_next_multiple_of(
|
||||
(uint64_t)transport_count,
|
||||
granularity)));
|
||||
} SMARTLIST_FOREACH_END(transport_name);
|
||||
|
||||
the_string = smartlist_join_strings(string_chunks, ",", 0, NULL);
|
||||
|
||||
log_debug(LD_GENERAL, "Final bridge-ip-transports string: '%s'", the_string);
|
||||
|
||||
done:
|
||||
strmap_free(transport_counts, NULL);
|
||||
SMARTLIST_FOREACH(transports_used, char *, s, tor_free(s));
|
||||
smartlist_free(transports_used);
|
||||
SMARTLIST_FOREACH(string_chunks, char *, s, tor_free(s));
|
||||
smartlist_free(string_chunks);
|
||||
|
||||
return the_string;
|
||||
}
|
||||
|
||||
/** Return a newly allocated comma-separated string containing statistics
|
||||
* on network status downloads. The string contains the number of completed
|
||||
* requests, timeouts, and still running requests as well as the download
|
||||
@ -1037,7 +1170,7 @@ geoip_reset_dirreq_stats(time_t now)
|
||||
if ((*ent)->action == GEOIP_CLIENT_NETWORKSTATUS) {
|
||||
this = *ent;
|
||||
next = HT_NEXT_RMV(clientmap, &client_history, ent);
|
||||
tor_free(this);
|
||||
clientmap_entry_free(this);
|
||||
} else {
|
||||
next = HT_NEXT(clientmap, &client_history, ent);
|
||||
}
|
||||
@ -1189,6 +1322,8 @@ validate_bridge_stats(const char *stats_str, time_t now)
|
||||
const char *BRIDGE_STATS_END = "bridge-stats-end ";
|
||||
const char *BRIDGE_IPS = "bridge-ips ";
|
||||
const char *BRIDGE_IPS_EMPTY_LINE = "bridge-ips\n";
|
||||
const char *BRIDGE_TRANSPORTS = "bridge-ip-transports ";
|
||||
const char *BRIDGE_TRANSPORTS_EMPTY_LINE = "bridge-ip-transports\n";
|
||||
const char *tmp;
|
||||
time_t stats_end_time;
|
||||
int seconds;
|
||||
@ -1223,6 +1358,15 @@ validate_bridge_stats(const char *stats_str, time_t now)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse: "bridge-ip-transports PT=N,PT=N,..." */
|
||||
tmp = find_str_at_start_of_line(stats_str, BRIDGE_TRANSPORTS);
|
||||
if (!tmp) {
|
||||
/* Look if there is an empty "bridge-ip-transports" line */
|
||||
tmp = find_str_at_start_of_line(stats_str, BRIDGE_TRANSPORTS_EMPTY_LINE);
|
||||
if (!tmp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1236,7 +1380,8 @@ static char *bridge_stats_extrainfo = NULL;
|
||||
char *
|
||||
geoip_format_bridge_stats(time_t now)
|
||||
{
|
||||
char *out = NULL, *country_data = NULL, *ipver_data = NULL;
|
||||
char *out = NULL;
|
||||
char *country_data = NULL, *ipver_data = NULL, *transport_data = NULL;
|
||||
long duration = now - start_of_bridge_stats_interval;
|
||||
char written[ISO_TIME_LEN+1];
|
||||
|
||||
@ -1247,16 +1392,20 @@ geoip_format_bridge_stats(time_t now)
|
||||
|
||||
format_iso_time(written, now);
|
||||
geoip_get_client_history(GEOIP_CLIENT_CONNECT, &country_data, &ipver_data);
|
||||
transport_data = geoip_get_transport_history();
|
||||
|
||||
tor_asprintf(&out,
|
||||
"bridge-stats-end %s (%ld s)\n"
|
||||
"bridge-ips %s\n"
|
||||
"bridge-ip-versions %s\n",
|
||||
"bridge-ip-versions %s\n"
|
||||
"bridge-ip-transports %s\n",
|
||||
written, duration,
|
||||
country_data ? country_data : "",
|
||||
ipver_data ? ipver_data : "");
|
||||
ipver_data ? ipver_data : "",
|
||||
transport_data ? transport_data : "");
|
||||
tor_free(country_data);
|
||||
tor_free(ipver_data);
|
||||
tor_free(transport_data);
|
||||
|
||||
return out;
|
||||
}
|
||||
@ -1515,7 +1664,7 @@ geoip_free_all(void)
|
||||
for (ent = HT_START(clientmap, &client_history); ent != NULL; ent = next) {
|
||||
this = *ent;
|
||||
next = HT_NEXT_RMV(clientmap, &client_history, ent);
|
||||
tor_free(this);
|
||||
clientmap_entry_free(this);
|
||||
}
|
||||
HT_CLEAR(clientmap, &client_history);
|
||||
}
|
||||
@ -1530,5 +1679,6 @@ geoip_free_all(void)
|
||||
}
|
||||
|
||||
clear_geoip_db();
|
||||
tor_free(bridge_stats_extrainfo);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 \
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
||||
|
63
src/or/or.h
63
src/or/or.h
@ -228,8 +228,14 @@ typedef enum {
|
||||
#define CONN_TYPE_AP_NATD_LISTENER 14
|
||||
/** Type for sockets listening for DNS requests. */
|
||||
#define CONN_TYPE_AP_DNS_LISTENER 15
|
||||
#define CONN_TYPE_MAX_ 15
|
||||
/* !!!! If CONN_TYPE_MAX_ is ever over 15, we must grow the type field in
|
||||
|
||||
/** Type for connections from the Extended ORPort. */
|
||||
#define CONN_TYPE_EXT_OR 16
|
||||
/** Type for sockets listening for Extended ORPort connections. */
|
||||
#define CONN_TYPE_EXT_OR_LISTENER 17
|
||||
|
||||
#define CONN_TYPE_MAX_ 17
|
||||
/* !!!! If _CONN_TYPE_MAX is ever over 31, we must grow the type field in
|
||||
* connection_t. */
|
||||
|
||||
/* Proxy client types */
|
||||
@ -309,6 +315,25 @@ typedef enum {
|
||||
#define OR_CONN_STATE_OPEN 8
|
||||
#define OR_CONN_STATE_MAX_ 8
|
||||
|
||||
/** States of the Extended ORPort protocol. Be careful before changing
|
||||
* the numbers: they matter. */
|
||||
#define EXT_OR_CONN_STATE_MIN_ 1
|
||||
/** Extended ORPort authentication is waiting for the authentication
|
||||
* type selected by the client. */
|
||||
#define EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE 1
|
||||
/** Extended ORPort authentication is waiting for the client nonce. */
|
||||
#define EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE 2
|
||||
/** Extended ORPort authentication is waiting for the client hash. */
|
||||
#define EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH 3
|
||||
#define EXT_OR_CONN_STATE_AUTH_MAX 3
|
||||
/** Authentication finished and the Extended ORPort is now accepting
|
||||
* traffic. */
|
||||
#define EXT_OR_CONN_STATE_OPEN 4
|
||||
/** Extended ORPort is flushing its last messages and preparing to
|
||||
* start accepting OR connections. */
|
||||
#define EXT_OR_CONN_STATE_FLUSHING 5
|
||||
#define EXT_OR_CONN_STATE_MAX_ 5
|
||||
|
||||
#define EXIT_CONN_STATE_MIN_ 1
|
||||
/** State for an exit connection: waiting for response from DNS farm. */
|
||||
#define EXIT_CONN_STATE_RESOLVING 1
|
||||
@ -1082,6 +1107,13 @@ typedef struct var_cell_t {
|
||||
uint8_t payload[FLEXIBLE_ARRAY_MEMBER];
|
||||
} var_cell_t;
|
||||
|
||||
/** A parsed Extended ORPort message. */
|
||||
typedef struct ext_or_cmd_t {
|
||||
uint16_t cmd; /** Command type */
|
||||
uint16_t len; /** Body length */
|
||||
char body[FLEXIBLE_ARRAY_MEMBER]; /** Message body */
|
||||
} ext_or_cmd_t;
|
||||
|
||||
/** A cell as packed for writing to the network. */
|
||||
typedef struct packed_cell_t {
|
||||
/** Next cell queued on this circuit. */
|
||||
@ -1163,7 +1195,7 @@ typedef struct connection_t {
|
||||
* *_CONNECTION_MAGIC. */
|
||||
|
||||
uint8_t state; /**< Current state of this connection. */
|
||||
unsigned int type:4; /**< What kind of connection is this? */
|
||||
unsigned int type:5; /**< What kind of connection is this? */
|
||||
unsigned int purpose:5; /**< Only used for DIR and EXIT types currently. */
|
||||
|
||||
/* The next fields are all one-bit booleans. Some are only applicable to
|
||||
@ -1405,6 +1437,9 @@ typedef struct or_handshake_state_t {
|
||||
/**@}*/
|
||||
} or_handshake_state_t;
|
||||
|
||||
/** Length of Extended ORPort connection identifier. */
|
||||
#define EXT_OR_CONN_ID_LEN DIGEST_LEN /* 20 */
|
||||
|
||||
/** Subtype of connection_t for an "OR connection" -- that is, one that speaks
|
||||
* cells over TLS. */
|
||||
typedef struct or_connection_t {
|
||||
@ -1413,6 +1448,20 @@ typedef struct or_connection_t {
|
||||
/** Hash of the public RSA key for the other side's identity key, or zeroes
|
||||
* if the other side hasn't shown us a valid identity key. */
|
||||
char identity_digest[DIGEST_LEN];
|
||||
|
||||
/** Extended ORPort connection identifier. */
|
||||
char *ext_or_conn_id;
|
||||
/** This is the ClientHash value we expect to receive from the
|
||||
* client during the Extended ORPort authentication protocol. We
|
||||
* compute it upon receiving the ClientNoce from the client, and we
|
||||
* compare it with the acual ClientHash value sent by the
|
||||
* client. */
|
||||
char *ext_or_auth_correct_client_hash;
|
||||
/** String carrying the name of the pluggable transport
|
||||
* (e.g. "obfs2") that is obfuscating this connection. If no
|
||||
* pluggable transports are used, it's NULL. */
|
||||
char *ext_or_transport;
|
||||
|
||||
char *nickname; /**< Nickname of OR on other side (if any). */
|
||||
|
||||
tor_tls_t *tls; /**< TLS connection state. */
|
||||
@ -3428,6 +3477,8 @@ typedef struct {
|
||||
char *User; /**< Name of user to run Tor as. */
|
||||
char *Group; /**< Name of group to run Tor as. */
|
||||
config_line_t *ORPort_lines; /**< Ports to listen on for OR connections. */
|
||||
/** Ports to listen on for extended OR connections. */
|
||||
config_line_t *ExtORPort_lines;
|
||||
/** Ports to listen on for SOCKS connections. */
|
||||
config_line_t *SocksPort_lines;
|
||||
/** Ports to listen on for transparent pf/netfilter connections. */
|
||||
@ -3463,6 +3514,7 @@ typedef struct {
|
||||
unsigned int ControlPort_set : 1;
|
||||
unsigned int DirPort_set : 1;
|
||||
unsigned int DNSPort_set : 1;
|
||||
unsigned int ExtORPort_set : 1;
|
||||
/**@}*/
|
||||
|
||||
int AssumeReachable; /**< Whether to publish our descriptor regardless. */
|
||||
@ -3742,7 +3794,10 @@ typedef struct {
|
||||
|
||||
int CookieAuthentication; /**< Boolean: do we enable cookie-based auth for
|
||||
* the control system? */
|
||||
char *CookieAuthFile; /**< Location of a cookie authentication file. */
|
||||
char *CookieAuthFile; /**< Filesystem location of a ControlPort
|
||||
* authentication cookie. */
|
||||
char *ExtORPortCookieAuthFile; /**< Filesystem location of Extended
|
||||
* ORPort authentication cookie. */
|
||||
int CookieAuthFileGroupReadable; /**< Boolean: Is the CookieAuthFile g+r? */
|
||||
int LeaveStreamsUnattached; /**< Boolean: Does Tor attach new streams to
|
||||
* circuits itself (0), or does it expect a controller
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
803
src/test/test.c
803
src/test/test.c
@ -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
342
src/test/test_buffers.c
Normal 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
604
src/test/test_extorport.c
Normal 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
|
||||
};
|
||||
|
@ -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
393
src/test/test_socks.c
Normal 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
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user