Merge remote-tracking branch 'asn/bug3594_rebased_and_fixed'

Conflicts:
	src/common/util.c
	src/or/entrynodes.h
This commit is contained in:
Nick Mathewson 2013-03-19 13:25:45 -04:00
commit c101ecc8dc
14 changed files with 685 additions and 89 deletions

3
changes/bug3594 Normal file
View File

@ -0,0 +1,3 @@
o Major bugfixes:
- Add support for passing arguments to managed pluggable transport
proxies. Implements ticket #3594.

View File

@ -865,6 +865,39 @@ tor_digest_is_zero(const char *digest)
return tor_memeq(digest, ZERO_DIGEST, DIGEST_LEN);
}
/** Return true if <b>string</b> is a valid '<key>=[<value>]' string.
* <value> is optional, to indicate the empty string. Log at logging
* <b>severity</b> if something ugly happens. */
int
string_is_key_value(int severity, const char *string)
{
/* position of equal sign in string */
const char *equal_sign_pos = NULL;
tor_assert(string);
if (strlen(string) < 2) { /* "x=" is shortest args string */
tor_log(severity, LD_GENERAL, "'%s' is too short to be a k=v value.",
escaped(string));
return 0;
}
equal_sign_pos = strchr(string, '=');
if (!equal_sign_pos) {
tor_log(severity, LD_GENERAL, "'%s' is not a k=v value.", escaped(string));
return 0;
}
/* validate that the '=' is not in the beginning of the string. */
if (equal_sign_pos == string) {
tor_log(severity, LD_GENERAL, "'%s' is not a valid k=v value.",
escaped(string));
return 0;
}
return 1;
}
/** Return true iff the DIGEST256_LEN bytes in digest are all zero. */
int
tor_digest256_is_zero(const char *digest)
@ -1176,6 +1209,46 @@ escaped(const char *s)
return escaped_val_;
}
/** Escape every ";" or "\" character of <b>string</b>. Use
* <b>escape_char</b> as the character to use for escaping.
* The returned string is allocated on the heap and it's the
* responsibility of the caller to free it. */
char *
tor_escape_str_for_socks_arg(const char *string)
{
char *new_string = NULL;
char *new_cp = NULL;
size_t length, new_length;
static const char *chars_to_escape = ";\\";
tor_assert(string);
length = strlen(string);
if (!length) /* If we were given the empty string, return the same. */
return tor_strdup("");
/* (new_length > SIZE_MAX) => ((length * 2) + 1 > SIZE_MAX) =>
(length*2 > SIZE_MAX - 1) => (length > (SIZE_MAX - 1)/2) */
if (length > (SIZE_MAX - 1)/2) /* check for overflow */
return NULL;
/* this should be enough even if all characters must be escaped */
new_length = (length * 2) + 1;
new_string = new_cp = tor_malloc(new_length);
while (*string) {
if (strchr(chars_to_escape, *string))
*new_cp++ = '\\';
*new_cp++ = *string++;
}
*new_cp = '\0'; /* NUL-terminate the new string */
return new_string;
}
/* =====
* Time
* ===== */

View File

@ -208,12 +208,16 @@ const char *find_whitespace_eos(const char *s, const char *eos);
const char *find_str_at_start_of_line(const char *haystack,
const char *needle);
int string_is_C_identifier(const char *string);
int string_is_key_value(int severity, const char *string);
int tor_mem_is_zero(const char *mem, size_t len);
int tor_digest_is_zero(const char *digest);
int tor_digest256_is_zero(const char *digest);
char *esc_for_log(const char *string) ATTR_MALLOC;
const char *escaped(const char *string);
char *tor_escape_str_for_socks_arg(const char *string);
struct smartlist_t;
int tor_vsscanf(const char *buf, const char *pattern, va_list ap)
#ifdef __GNUC__

View File

@ -484,7 +484,6 @@ static int options_transition_affects_descriptor(
const or_options_t *old_options, const or_options_t *new_options);
static int check_nickname_list(const char *lst, const char *name, char **msg);
static int parse_bridge_line(const char *line, int validate_only);
static int parse_client_transport_line(const char *line, int validate_only);
static int parse_server_transport_line(const char *line, int validate_only);
@ -1299,11 +1298,13 @@ options_act(const or_options_t *old_options)
if (options->Bridges) {
mark_bridge_list();
for (cl = options->Bridges; cl; cl = cl->next) {
if (parse_bridge_line(cl->value, 0)<0) {
bridge_line_t *bridge_line = parse_bridge_line(cl->value);
if (!bridge_line) {
log_warn(LD_BUG,
"Previously validated Bridge line could not be added!");
return -1;
}
bridge_add_from_config(bridge_line);
}
sweep_bridge_list();
}
@ -2946,14 +2947,14 @@ options_validate(or_options_t *old_options, or_options_t *options,
size_t len;
len = strlen(options->Socks5ProxyUsername);
if (len < 1 || len > 255)
if (len < 1 || len > MAX_SOCKS5_AUTH_FIELD_SIZE)
REJECT("Socks5ProxyUsername must be between 1 and 255 characters.");
if (!options->Socks5ProxyPassword)
REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername.");
len = strlen(options->Socks5ProxyPassword);
if (len < 1 || len > 255)
if (len < 1 || len > MAX_SOCKS5_AUTH_FIELD_SIZE)
REJECT("Socks5ProxyPassword must be between 1 and 255 characters.");
} else if (options->Socks5ProxyPassword)
REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername.");
@ -3037,8 +3038,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("If you set UseBridges, you must set TunnelDirConns.");
for (cl = options->Bridges; cl; cl = cl->next) {
if (parse_bridge_line(cl->value, 1)<0)
bridge_line_t *bridge_line = parse_bridge_line(cl->value);
if (!bridge_line)
REJECT("Bridge line did not parse. See logs for details.");
bridge_line_free(bridge_line);
}
for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
@ -4096,21 +4099,72 @@ options_init_logs(or_options_t *options, int validate_only)
return ok?0:-1;
}
/** Given a smartlist of SOCKS arguments to be passed to a transport
* proxy in <b>args</b>, validate them and return -1 if they are
* corrupted. Return 0 if they seem OK. */
static int
validate_transport_socks_arguments(const smartlist_t *args)
{
char *socks_string = NULL;
size_t socks_string_len;
tor_assert(args);
tor_assert(smartlist_len(args) > 0);
SMARTLIST_FOREACH_BEGIN(args, const char *, s) {
if (!string_is_key_value(LOG_WARN, s)) { /* items should be k=v items */
log_warn(LD_CONFIG, "'%s' is not a k=v item.", s);
return -1;
}
} SMARTLIST_FOREACH_END(s);
socks_string = pt_stringify_socks_args(args);
if (!socks_string)
return -1;
socks_string_len = strlen(socks_string);
tor_free(socks_string);
if (socks_string_len > MAX_SOCKS5_AUTH_SIZE_TOTAL) {
log_warn(LD_CONFIG, "SOCKS arguments can't be more than %u bytes (%lu).",
MAX_SOCKS5_AUTH_SIZE_TOTAL,
(unsigned long) socks_string_len);
return -1;
}
return 0;
}
/** Deallocate a bridge_line_t structure. */
/* private */ void
bridge_line_free(bridge_line_t *bridge_line)
{
if (!bridge_line)
return;
if (bridge_line->socks_args) {
SMARTLIST_FOREACH(bridge_line->socks_args, char*, s, tor_free(s));
smartlist_free(bridge_line->socks_args);
}
tor_free(bridge_line->transport_name);
tor_free(bridge_line);
}
/** Read the contents of a Bridge line from <b>line</b>. Return 0
* if the line is well-formed, and -1 if it isn't. If
* <b>validate_only</b> is 0, and the line is well-formed, then add
* the bridge described in the line to our internal bridge list. */
static int
parse_bridge_line(const char *line, int validate_only)
* the bridge described in the line to our internal bridge list.
*
* Bridge line format:
* Bridge [transport] IP:PORT [id-fingerprint] [k=v] [k=v] ...
*/
/* private */ bridge_line_t *
parse_bridge_line(const char *line)
{
smartlist_t *items = NULL;
int r;
char *addrport=NULL, *fingerprint=NULL;
char *transport_name=NULL;
char *field1=NULL;
tor_addr_t addr;
uint16_t port = 0;
char digest[DIGEST_LEN];
char *field=NULL;
bridge_line_t *bridge_line = tor_malloc_zero(sizeof(bridge_line_t));
items = smartlist_new();
smartlist_split_string(items, line, NULL,
@ -4120,68 +4174,102 @@ parse_bridge_line(const char *line, int validate_only)
goto err;
}
/* field1 is either a transport name or addrport */
field1 = smartlist_get(items, 0);
/* first field is either a transport name or addrport */
field = smartlist_get(items, 0);
smartlist_del_keeporder(items, 0);
if (!(strstr(field1, ".") || strstr(field1, ":"))) {
/* new-style bridge line */
transport_name = field1;
if (string_is_C_identifier(field)) {
/* It's a transport name. */
bridge_line->transport_name = field;
if (smartlist_len(items) < 1) {
log_warn(LD_CONFIG, "Too few items to Bridge line.");
goto err;
}
addrport = smartlist_get(items, 0);
addrport = smartlist_get(items, 0); /* Next field is addrport then. */
smartlist_del_keeporder(items, 0);
} else {
addrport = field1;
addrport = field;
}
if (tor_addr_port_lookup(addrport, &addr, &port)<0) {
/* Parse addrport. */
if (tor_addr_port_lookup(addrport,
&bridge_line->addr, &bridge_line->port)<0) {
log_warn(LD_CONFIG, "Error parsing Bridge address '%s'", addrport);
goto err;
}
if (!port) {
if (!bridge_line->port) {
log_info(LD_CONFIG,
"Bridge address '%s' has no port; using default port 443.",
addrport);
port = 443;
bridge_line->port = 443;
}
/* If transports are enabled, next field could be a fingerprint or a
socks argument. If transports are disabled, next field must be
a fingerprint. */
if (smartlist_len(items)) {
if (bridge_line->transport_name) { /* transports enabled: */
field = smartlist_get(items, 0);
smartlist_del_keeporder(items, 0);
/* If it's a key=value pair, then it's a SOCKS argument for the
transport proxy... */
if (string_is_key_value(LOG_DEBUG, field)) {
bridge_line->socks_args = smartlist_new();
smartlist_add(bridge_line->socks_args, field);
} else { /* ...otherwise, it's the bridge fingerprint. */
fingerprint = field;
}
} else { /* transports disabled: */
fingerprint = smartlist_join_strings(items, "", 0, NULL);
}
}
/* Handle fingerprint, if it was provided. */
if (fingerprint) {
if (strlen(fingerprint) != HEX_DIGEST_LEN) {
log_warn(LD_CONFIG, "Key digest for Bridge is wrong length.");
goto err;
}
if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) {
if (base16_decode(bridge_line->digest, DIGEST_LEN,
fingerprint, HEX_DIGEST_LEN)<0) {
log_warn(LD_CONFIG, "Unable to decode Bridge key digest.");
goto err;
}
}
if (!validate_only) {
log_debug(LD_DIR, "Bridge at %s (transport: %s) (%s)",
fmt_addrport(&addr, port),
transport_name ? transport_name : "no transport",
fingerprint ? fingerprint : "no key listed");
bridge_add_from_config(&addr, port,
fingerprint ? digest : NULL, transport_name);
/* If we are using transports, any remaining items in the smartlist
should be k=v values. */
if (bridge_line->transport_name && smartlist_len(items)) {
if (!bridge_line->socks_args)
bridge_line->socks_args = smartlist_new();
/* append remaining items of 'items' to 'socks_args' */
smartlist_add_all(bridge_line->socks_args, items);
smartlist_clear(items);
tor_assert(smartlist_len(bridge_line->socks_args) > 0);
}
if (bridge_line->socks_args) {
if (validate_transport_socks_arguments(bridge_line->socks_args) < 0)
goto err;
}
r = 0;
goto done;
err:
r = -1;
bridge_line_free(bridge_line);
bridge_line = NULL;
done:
SMARTLIST_FOREACH(items, char*, s, tor_free(s));
smartlist_free(items);
tor_free(addrport);
tor_free(transport_name);
tor_free(fingerprint);
return r;
return bridge_line;
}
/** Read the contents of a ClientTransportPlugin line from

View File

@ -98,5 +98,19 @@ int addressmap_register_auto(const char *from, const char *to,
addressmap_entry_source_t addrmap_source,
const char **msg);
/** Represents the information stored in a torrc Bridge line. */
typedef struct bridge_line_t {
tor_addr_t addr; /* The IP address of the bridge. */
uint16_t port; /* The TCP port of the bridge. */
char *transport_name; /* The name of the pluggable transport that
should be used to connect to the bridge. */
char digest[DIGEST_LEN]; /* The bridge's identity key digest. */
smartlist_t *socks_args;; /* SOCKS arguments for the pluggable
transport proxy. */
} bridge_line_t;
void bridge_line_free(bridge_line_t *bridge_line);
bridge_line_t *parse_bridge_line(const char *line);
#endif

View File

@ -44,6 +44,7 @@
#include "router.h"
#include "transports.h"
#include "routerparse.h"
#include "transports.h"
#ifdef USE_BUFFEREVENTS
#include <event2/event.h>
@ -1574,6 +1575,32 @@ connection_proxy_state_to_string(int state)
return states[state];
}
/** Returns the global proxy type used by tor. Use this function for
* logging or high-level purposes, don't use it to fill the
* <b>proxy_type</b> field of or_connection_t; use the actual proxy
* protocol instead.*/
static int
get_proxy_type(void)
{
const or_options_t *options = get_options();
if (options->HTTPSProxy)
return PROXY_CONNECT;
else if (options->Socks4Proxy)
return PROXY_SOCKS4;
else if (options->Socks5Proxy)
return PROXY_SOCKS5;
else if (options->ClientTransportPlugin)
return PROXY_PLUGGABLE;
else
return PROXY_NONE;
}
/* One byte for the version, one for the command, two for the
port, and four for the addr... and, one more for the
username NUL: */
#define SOCKS4_STANDARD_BUFFER_SIZE (1 + 1 + 2 + 4 + 1)
/** Write a proxy request of <b>type</b> (socks4, socks5, https) to conn
* for conn->addr:conn->port, authenticating with the auth details given
* in the configuration (if available). SOCKS 5 and HTTP CONNECT proxies
@ -1628,17 +1655,45 @@ connection_proxy_connect(connection_t *conn, int type)
}
case PROXY_SOCKS4: {
unsigned char buf[9];
unsigned char *buf;
uint16_t portn;
uint32_t ip4addr;
size_t buf_size = 0;
char *socks_args_string = NULL;
/* Send a SOCKS4 connect request with empty user id */
/* Send a SOCKS4 connect request */
if (tor_addr_family(&conn->addr) != AF_INET) {
log_warn(LD_NET, "SOCKS4 client is incompatible with IPv6");
return -1;
}
{ /* If we are here because we are trying to connect to a
pluggable transport proxy, check if we have any SOCKS
arguments to transmit. If we do, compress all arguments to
a single string in 'socks_args_string': */
if (get_proxy_type() == PROXY_PLUGGABLE) {
socks_args_string =
pt_get_socks_args_for_proxy_addrport(&conn->addr, conn->port);
if (socks_args_string)
log_debug(LD_NET, "Sending out '%s' as our SOCKS argument string.",
socks_args_string);
}
}
{ /* Figure out the buffer size we need for the SOCKS message: */
buf_size = SOCKS4_STANDARD_BUFFER_SIZE;
/* If we have a SOCKS argument string, consider its size when
calculating the buffer size: */
if (socks_args_string)
buf_size += strlen(socks_args_string);
}
buf = tor_malloc_zero(buf_size);
ip4addr = tor_addr_to_ipv4n(&conn->addr);
portn = htons(conn->port);
@ -1646,9 +1701,23 @@ connection_proxy_connect(connection_t *conn, int type)
buf[1] = SOCKS_COMMAND_CONNECT; /* command */
memcpy(buf + 2, &portn, 2); /* port */
memcpy(buf + 4, &ip4addr, 4); /* addr */
buf[8] = 0; /* userid (empty) */
connection_write_to_buf((char *)buf, sizeof(buf), conn);
/* Next packet field is the userid. If we have pluggable
transport SOCKS arguments, we have to embed them
there. Otherwise, we use an empty userid. */
if (socks_args_string) { /* place the SOCKS args string: */
tor_assert(strlen(socks_args_string) > 0);
tor_assert(buf_size >=
SOCKS4_STANDARD_BUFFER_SIZE + strlen(socks_args_string));
strlcpy((char *)buf + 8, socks_args_string, buf_size - 8);
tor_free(socks_args_string);
} else {
buf[8] = 0; /* no userid */
}
connection_write_to_buf((char *)buf, buf_size, conn);
tor_free(buf);
conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK;
break;
}
@ -1660,8 +1729,13 @@ connection_proxy_connect(connection_t *conn, int type)
buf[0] = 5; /* version */
/* We have to use SOCKS5 authentication, if we have a
Socks5ProxyUsername or if we want to pass arguments to our
pluggable transport proxy: */
if ((options->Socks5ProxyUsername) ||
(get_proxy_type() == PROXY_PLUGGABLE &&
(get_socks_args_by_bridge_addrport(&conn->addr, conn->port)))) {
/* number of auth methods */
if (options->Socks5ProxyUsername) {
buf[1] = 2;
buf[2] = 0x00; /* no authentication */
buf[3] = 0x02; /* rfc1929 Username/Passwd auth */
@ -1855,15 +1929,49 @@ connection_read_proxy_handshake(connection_t *conn)
unsigned char buf[1024];
size_t reqsize, usize, psize;
const char *user, *pass;
char *socks_args_string = NULL;
if (get_proxy_type() == PROXY_PLUGGABLE) {
socks_args_string =
pt_get_socks_args_for_proxy_addrport(&conn->addr, conn->port);
if (!socks_args_string) {
log_warn(LD_NET, "Could not create SOCKS args string.");
ret = -1;
break;
}
log_debug(LD_NET, "SOCKS5 arguments: %s", socks_args_string);
tor_assert(strlen(socks_args_string) > 0);
tor_assert(strlen(socks_args_string) <= MAX_SOCKS5_AUTH_SIZE_TOTAL);
if (strlen(socks_args_string) > MAX_SOCKS5_AUTH_FIELD_SIZE) {
user = socks_args_string;
usize = MAX_SOCKS5_AUTH_FIELD_SIZE;
pass = socks_args_string + MAX_SOCKS5_AUTH_FIELD_SIZE;
psize = strlen(socks_args_string) - MAX_SOCKS5_AUTH_FIELD_SIZE;
} else {
user = socks_args_string;
usize = strlen(socks_args_string);
pass = "\0";
psize = 1;
}
} else if (get_options()->Socks5ProxyUsername) {
user = get_options()->Socks5ProxyUsername;
pass = get_options()->Socks5ProxyPassword;
tor_assert(user && pass);
/* XXX len of user and pass must be <= 255 !!! */
usize = strlen(user);
psize = strlen(pass);
tor_assert(usize <= 255 && psize <= 255);
} else {
log_err(LD_BUG, "We entered %s for no reason!", __func__);
tor_fragile_assert();
ret = -1;
break;
}
/* Username and password lengths should have been checked
above and during torrc parsing. */
tor_assert(usize <= MAX_SOCKS5_AUTH_FIELD_SIZE &&
psize <= MAX_SOCKS5_AUTH_FIELD_SIZE);
reqsize = 3 + usize + psize;
buf[0] = 1; /* negotiation version */
@ -1872,6 +1980,9 @@ connection_read_proxy_handshake(connection_t *conn)
buf[2 + usize] = psize;
memcpy(buf + 3 + usize, pass, psize);
if (socks_args_string)
tor_free(socks_args_string);
connection_write_to_buf((char *)buf, reqsize, conn);
conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_RFC1929_OK;
@ -4338,7 +4449,7 @@ get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type,
options->Bridges) {
const transport_t *transport = NULL;
int r;
r = find_transport_by_bridge_addrport(&conn->addr, conn->port, &transport);
r = get_transport_by_bridge_addrport(&conn->addr, conn->port, &transport);
if (r<0)
return -1;
if (transport) { /* transport found */
@ -4353,24 +4464,6 @@ get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type,
return 0;
}
/** Returns the global proxy type used by tor. */
static int
get_proxy_type(void)
{
const or_options_t *options = get_options();
if (options->HTTPSProxy)
return PROXY_CONNECT;
else if (options->Socks4Proxy)
return PROXY_SOCKS4;
else if (options->Socks5Proxy)
return PROXY_SOCKS5;
else if (options->ClientTransportPlugin)
return PROXY_PLUGGABLE;
else
return PROXY_NONE;
}
/** Log a failed connection to a proxy server.
* <b>conn</b> is the connection we use the proxy server for. */
void

View File

@ -89,6 +89,14 @@ int connection_connect(connection_t *conn, const char *address,
const tor_addr_t *addr,
uint16_t port, int *socket_error);
/** Maximum size of information that we can fit into SOCKS5 username
or password fields. */
#define MAX_SOCKS5_AUTH_FIELD_SIZE 255
/** Total maximum size of information that we can fit into SOCKS5
username and password fields. */
#define MAX_SOCKS5_AUTH_SIZE_TOTAL 2*MAX_SOCKS5_AUTH_FIELD_SIZE
int connection_proxy_connect(connection_t *conn, int type);
int connection_read_proxy_handshake(connection_t *conn);
void log_failed_proxy_connection(connection_t *conn);

View File

@ -53,6 +53,10 @@ typedef struct {
/** When should we next try to fetch a descriptor for this bridge? */
download_status_t fetch_status;
/** A smartlist of k=v values to be passed to the SOCKS proxy, if
transports are used for this bridge. */
smartlist_t *socks_args;
} bridge_info_t;
/** A list of our chosen entry guards. */
@ -1513,6 +1517,11 @@ bridge_free(bridge_info_t *bridge)
return;
tor_free(bridge->transport_name);
if (bridge->socks_args) {
SMARTLIST_FOREACH(bridge->socks_args, char*, s, tor_free(s));
smartlist_free(bridge->socks_args);
}
tor_free(bridge);
}
@ -1691,30 +1700,52 @@ bridge_resolve_conflicts(const tor_addr_t *addr, uint16_t port,
} SMARTLIST_FOREACH_END(bridge);
}
/** Remember a new bridge at <b>addr</b>:<b>port</b>. If <b>digest</b>
* is set, it tells us the identity key too. If we already had the
* bridge in our list, unmark it, and don't actually add anything new.
* If <b>transport_name</b> is non-NULL - the bridge is associated with a
* pluggable transport - we assign the transport to the bridge. */
/** Register the bridge information in <b>bridge_line</b> to the
* bridge subsystem. Steals reference of <b>bridge_line</b>. */
void
bridge_add_from_config(const tor_addr_t *addr, uint16_t port,
const char *digest, const char *transport_name)
bridge_add_from_config(bridge_line_t *bridge_line)
{
bridge_info_t *b;
bridge_resolve_conflicts(addr, port, digest, transport_name);
{ /* Log the bridge we are about to register: */
log_debug(LD_GENERAL, "Registering bridge at %s (transport: %s) (%s)",
fmt_addrport(&bridge_line->addr, bridge_line->port),
bridge_line->transport_name ?
bridge_line->transport_name : "no transport",
tor_digest_is_zero(bridge_line->digest) ?
"no key listed" : hex_str(bridge_line->digest, DIGEST_LEN));
if (bridge_line->socks_args) { /* print socks arguments */
int i = 0;
tor_assert(smartlist_len(bridge_line->socks_args) > 0);
log_debug(LD_GENERAL, "Bridge uses %d SOCKS arguments:",
smartlist_len(bridge_line->socks_args));
SMARTLIST_FOREACH(bridge_line->socks_args, const char *, arg,
log_debug(LD_CONFIG, "%d: %s", ++i, arg));
}
}
bridge_resolve_conflicts(&bridge_line->addr,
bridge_line->port,
bridge_line->digest,
bridge_line->transport_name);
b = tor_malloc_zero(sizeof(bridge_info_t));
tor_addr_copy(&b->addr, addr);
b->port = port;
if (digest)
memcpy(b->identity, digest, DIGEST_LEN);
if (transport_name)
b->transport_name = tor_strdup(transport_name);
tor_addr_copy(&b->addr, &bridge_line->addr);
b->port = bridge_line->port;
if (bridge_line->digest)
memcpy(b->identity, bridge_line->digest, DIGEST_LEN);
if (bridge_line->transport_name)
b->transport_name = bridge_line->transport_name;
b->fetch_status.schedule = DL_SCHED_BRIDGE;
b->socks_args = bridge_line->socks_args;
if (!bridge_list)
bridge_list = smartlist_new();
tor_free(bridge_line); /* Deallocate bridge_line now. */
smartlist_add(bridge_list, b);
}
@ -1775,7 +1806,7 @@ find_transport_name_by_bridge_addrport(const tor_addr_t *addr, uint16_t port)
* transport, but the transport could not be found.
*/
int
find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
const transport_t **transport)
{
*transport = NULL;
@ -1802,6 +1833,17 @@ find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
return 0;
}
/** Return a smartlist containing all the SOCKS arguments that we
* should pass to the SOCKS proxy. */
const smartlist_t *
get_socks_args_by_bridge_addrport(const tor_addr_t *addr, uint16_t port)
{
bridge_info_t *bridge = get_configured_bridge_by_addr_port_digest(addr,
port,
NULL);
return bridge ? bridge->socks_args : NULL;
}
/** We need to ask <b>bridge</b> for its server descriptor. */
static void
launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge)

View File

@ -97,9 +97,8 @@ int routerinfo_is_a_configured_bridge(const routerinfo_t *ri);
int node_is_a_configured_bridge(const node_t *node);
void learned_router_identity(const tor_addr_t *addr, uint16_t port,
const char *digest);
void bridge_add_from_config(const tor_addr_t *addr, uint16_t port,
const char *digest,
const char *transport_name);
struct bridge_line_t;
void bridge_add_from_config(struct bridge_line_t *bridge_line);
void retry_bridge_descriptor_fetch_directly(const char *digest);
void fetch_bridge_descriptors(const or_options_t *options, time_t now);
void learned_bridge_descriptor(routerinfo_t *ri, int from_cache);
@ -109,13 +108,17 @@ int entries_known_but_down(const or_options_t *options);
void entries_retry_all(const or_options_t *options);
int any_bridge_supports_microdescriptors(void);
const smartlist_t *get_socks_args_by_bridge_addrport(const tor_addr_t *addr,
uint16_t port);
int any_bridges_dont_support_microdescriptors(void);
void entry_guards_free_all(void);
const char *find_transport_name_by_bridge_addrport(const tor_addr_t *addr,
uint16_t port);
struct transport_t;
int find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
int get_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
const struct transport_t **transport);
int validate_pluggable_transports_config(void);

View File

@ -238,7 +238,9 @@ typedef enum {
#define PROXY_SOCKS5 3
/* !!!! If there is ever a PROXY_* type over 2, we must grow the proxy_type
* field in or_connection_t */
/* pluggable transports proxy type */
/* Pluggable transport proxy type. Don't use this in or_connection_t,
* instead use the actual underlying proxy type (see above). */
#define PROXY_PLUGGABLE 4
/* Proxy client handshake states */

View File

@ -95,6 +95,7 @@
#include "util.h"
#include "router.h"
#include "statefile.h"
#include "entrynodes.h"
static process_environment_t *
create_managed_proxy_environment(const managed_proxy_t *mp);
@ -1420,6 +1421,57 @@ pt_get_extra_info_descriptor_string(void)
return the_string;
}
/** Stringify the SOCKS arguments in <b>socks_args</b> according to
* 180_pluggable_transport.txt. The string is allocated on the heap
* and it's the responsibility of the caller to free it after use. */
char *
pt_stringify_socks_args(const smartlist_t *socks_args)
{
/* tmp place to store escaped socks arguments, so that we can
concatenate them up afterwards */
smartlist_t *sl_tmp = NULL;
char *escaped_string = NULL;
char *new_string = NULL;
tor_assert(socks_args);
tor_assert(smartlist_len(socks_args) > 0);
sl_tmp = smartlist_new();
SMARTLIST_FOREACH_BEGIN(socks_args, const char *, s) {
/* Escape ';' and '\'. */
escaped_string = tor_escape_str_for_socks_arg(s);
if (!escaped_string)
goto done;
smartlist_add(sl_tmp, escaped_string);
} SMARTLIST_FOREACH_END(s);
new_string = smartlist_join_strings(sl_tmp, ";", 0, NULL);
done:
SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s));
smartlist_free(sl_tmp);
return new_string;
}
/** Return a string of the SOCKS arguments that we should pass to the
* pluggable transports proxy in <b>addr</b>:<b>port</b> according to
* 180_pluggable_transport.txt. The string is allocated on the heap
* and it's the responsibility of the caller to free it after use. */
char *
pt_get_socks_args_for_proxy_addrport(const tor_addr_t *addr, uint16_t port)
{
const smartlist_t *socks_args = NULL;
socks_args = get_socks_args_by_bridge_addrport(addr, port);
if (!socks_args)
return NULL;
return pt_stringify_socks_args(socks_args);
}
/** The tor config was read.
* Destroy all managed proxies that were marked by a previous call to
* prepare_proxy_list_for_config_read() and are not used by the new

View File

@ -55,6 +55,10 @@ void pt_prepare_proxy_list_for_config_read(void);
void sweep_proxy_list(void);
smartlist_t *get_transport_proxy_ports(void);
char *pt_stringify_socks_args(const smartlist_t *socks_args);
char *pt_get_socks_args_for_proxy_addrport(const tor_addr_t *addr,
uint16_t port);
#ifdef PT_PRIVATE
/** State of the managed proxy configuration protocol. */

View File

@ -10,6 +10,8 @@
#include "confparse.h"
#include "connection_edge.h"
#include "test.h"
#include "util.h"
#include "address.h"
static void
test_config_addressmap(void *arg)
@ -169,11 +171,159 @@ test_config_addressmap(void *arg)
;
}
/* Test helper function: Make sure that a bridge line gets parsed
* properly. Also make sure that the resulting bridge_line_t structure
* has its fields set correctly. */
static void
good_bridge_line_test(const char *string, const char *test_addrport,
const char *test_digest, const char *test_transport,
const smartlist_t *test_socks_args)
{
char *tmp = NULL;
bridge_line_t *bridge_line = parse_bridge_line(string);
test_assert(bridge_line);
/* test addrport */
tmp = tor_strdup(fmt_addrport(&bridge_line->addr, bridge_line->port));
test_streq(test_addrport, tmp);
tor_free(tmp);
/* If we were asked to validate a digest, but we did not get a
digest after parsing, we failed. */
if (test_digest && tor_digest_is_zero(bridge_line->digest))
test_assert(0);
/* If we were not asked to validate a digest, and we got a digest
after parsing, we failed again. */
if (!test_digest && !tor_digest_is_zero(bridge_line->digest))
test_assert(0);
/* If we were asked to validate a digest, and we got a digest after
parsing, make sure it's correct. */
if (test_digest) {
tmp = tor_strdup(hex_str(bridge_line->digest, DIGEST_LEN));
tor_strlower(tmp);
test_streq(test_digest, tmp);
tor_free(tmp);
}
/* If we were asked to validate a transport name, make sure tha it
matches with the transport name that was parsed. */
if (test_transport && !bridge_line->transport_name)
test_assert(0);
if (!test_transport && bridge_line->transport_name)
test_assert(0);
if (test_transport)
test_streq(test_transport, bridge_line->transport_name);
/* Validate the SOCKS argument smartlist. */
if (test_socks_args && !bridge_line->socks_args)
test_assert(0);
if (!test_socks_args && bridge_line->socks_args)
test_assert(0);
if (test_socks_args)
test_assert(smartlist_strings_eq(test_socks_args,
bridge_line->socks_args));
done:
tor_free(tmp);
bridge_line_free(bridge_line);
}
/* Test helper function: Make sure that a bridge line is
* unparseable. */
static void
bad_bridge_line_test(const char *string)
{
bridge_line_t *bridge_line = parse_bridge_line(string);
test_assert(!bridge_line);
done:
bridge_line_free(bridge_line);
}
static void
test_config_parse_bridge_line(void *arg)
{
(void) arg;
good_bridge_line_test("192.0.2.1:4123",
"192.0.2.1:4123", NULL, NULL, NULL);
good_bridge_line_test("192.0.2.1",
"192.0.2.1:443", NULL, NULL, NULL);
good_bridge_line_test("transport [::1]",
"[::1]:443", NULL, "transport", NULL);
good_bridge_line_test("transport 192.0.2.1:12 "
"4352e58420e68f5e40bf7c74faddccd9d1349413",
"192.0.2.1:12",
"4352e58420e68f5e40bf7c74faddccd9d1349413",
"transport", NULL);
{
smartlist_t *sl_tmp = smartlist_new();
smartlist_add_asprintf(sl_tmp, "twoandtwo=five");
good_bridge_line_test("transport 192.0.2.1:12 "
"4352e58420e68f5e40bf7c74faddccd9d1349413 twoandtwo=five",
"192.0.2.1:12", "4352e58420e68f5e40bf7c74faddccd9d1349413",
"transport", sl_tmp);
SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s));
smartlist_free(sl_tmp);
}
{
smartlist_t *sl_tmp = smartlist_new();
smartlist_add_asprintf(sl_tmp, "twoandtwo=five");
smartlist_add_asprintf(sl_tmp, "z=z");
good_bridge_line_test("transport 192.0.2.1:12 twoandtwo=five z=z",
"192.0.2.1:12", NULL, "transport", sl_tmp);
SMARTLIST_FOREACH(sl_tmp, char *, s, tor_free(s));
smartlist_free(sl_tmp);
}
good_bridge_line_test("192.0.2.1:1231 "
"4352e58420e68f5e40bf7c74faddccd9d1349413",
"192.0.2.1:1231",
"4352e58420e68f5e40bf7c74faddccd9d1349413",
NULL, NULL);
/* Empty line */
bad_bridge_line_test("");
/* bad transport name */
bad_bridge_line_test("tr$n_sp0r7 190.20.2.2");
/* weird ip address */
bad_bridge_line_test("a.b.c.d");
/* invalid fpr */
bad_bridge_line_test("2.2.2.2:1231 4352e58420e68f5e40bf7c74faddccd9d1349");
/* no k=v in the end */
bad_bridge_line_test("obfs2 2.2.2.2:1231 "
"4352e58420e68f5e40bf7c74faddccd9d1349413 what");
/* no addrport */
bad_bridge_line_test("asdw");
/* huge k=v value that can't fit in SOCKS fields */
bad_bridge_line_test(
"obfs2 2.2.2.2:1231 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aa=b");
}
#define CONFIG_TEST(name, flags) \
{ #name, test_config_ ## name, flags, NULL, NULL }
struct testcase_t config_tests[] = {
CONFIG_TEST(addressmap, 0),
CONFIG_TEST(parse_bridge_line, 0),
END_OF_TESTCASES
};

View File

@ -796,6 +796,64 @@ test_util_expand_filename(void)
}
#endif
/** Test tor_escape_str_for_socks_arg(). */
static void
test_util_escape_string_socks(void)
{
char *escaped_string = NULL;
/** Simple backslash escape. */
escaped_string = tor_escape_str_for_socks_arg("This is a backslash: \\");
test_assert(escaped_string);
test_streq(escaped_string, "This is a backslash: \\\\");
tor_free(escaped_string);
/** Simple semicolon escape. */
escaped_string = tor_escape_str_for_socks_arg("First rule: Do not use ;");
test_assert(escaped_string);
test_streq(escaped_string, "First rule: Do not use \\;");
tor_free(escaped_string);
/** Empty string. */
escaped_string = tor_escape_str_for_socks_arg("");
test_assert(escaped_string);
test_streq(escaped_string, "");
tor_free(escaped_string);
/** Escape all characters. */
escaped_string = tor_escape_str_for_socks_arg(";\\;\\");
test_assert(escaped_string);
test_streq(escaped_string, "\\;\\\\\\;\\\\");
tor_free(escaped_string);
escaped_string = tor_escape_str_for_socks_arg(";");
test_assert(escaped_string);
test_streq(escaped_string, "\\;");
tor_free(escaped_string);
done:
tor_free(escaped_string);
}
static void
test_util_string_is_key_value(void *ptr)
{
(void)ptr;
test_assert(string_is_key_value(LOG_WARN, "key=value"));
test_assert(string_is_key_value(LOG_WARN, "k=v"));
test_assert(string_is_key_value(LOG_WARN, "key="));
test_assert(string_is_key_value(LOG_WARN, "x="));
test_assert(string_is_key_value(LOG_WARN, "xx="));
test_assert(!string_is_key_value(LOG_WARN, "=value"));
test_assert(!string_is_key_value(LOG_WARN, "=x"));
test_assert(!string_is_key_value(LOG_WARN, "="));
/* ??? */
/* test_assert(!string_is_key_value(LOG_WARN, "===")); */
done:
;
}
/** Test basic string functionality. */
static void
test_util_strmisc(void)
@ -3263,6 +3321,8 @@ struct testcase_t util_tests[] = {
#ifndef _WIN32
UTIL_LEGACY(expand_filename),
#endif
UTIL_LEGACY(escape_string_socks),
UTIL_LEGACY(string_is_key_value),
UTIL_LEGACY(strmisc),
UTIL_LEGACY(pow2),
UTIL_LEGACY(gzip),