Merge branch 'bug933_nm_rebased_v2'

Conflicts:
	src/test/test.c
This commit is contained in:
Nick Mathewson 2011-11-30 14:10:22 -05:00
commit 3b88b63826
10 changed files with 361 additions and 32 deletions

4
changes/bug933 Normal file
View File

@ -0,0 +1,4 @@
o Minor features:
- Allow MapAddress directives to specify matches against super-domains,
as in 'MapAddress *.torproject.org *.torproject.org.torserver.exit'.
Implements issue 933.

View File

@ -691,11 +691,44 @@ The following options are useful only for clients (that is, if
5050, 5190, 5222, 5223, 6523, 6667, 6697, 8300) 5050, 5190, 5222, 5223, 6523, 6667, 6697, 8300)
**MapAddress** __address__ __newaddress__:: **MapAddress** __address__ __newaddress__::
When a request for address arrives to Tor, it will rewrite it to newaddress When a request for address arrives to Tor, it will transform to newaddress
before processing it. For example, if you always want connections to before processing it. For example, if you always want connections to
www.indymedia.org to exit via __torserver__ (where __torserver__ is the www.example.com to exit via __torserver__ (where __torserver__ is the
nickname of the server), use "MapAddress www.indymedia.org nickname of the server), use "MapAddress www.example.com
www.indymedia.org.torserver.exit". www.example.com.torserver.exit". If the value is prefixed with a
"\*.", matches an entire domain. For example, if you
always want connections to example.com and any if its subdomains
to exit via
__torserver__ (where __torserver__ is the nickname of the server), use
"MapAddress \*.example.com \*.example.com.torserver.exit". (Note the
leading "*." in each part of the directive.) You can also redirect all
subdomains of a domain to a single address. For example, "MapAddress
*.example.com www.example.com". +
+
NOTES:
1. When evaluating MapAddress expressions Tor stops when it hits the most
recently added expression that matches the requested address. So if you
have the following in your torrc, www.torproject.org will map to 1.1.1.1:
MapAddress www.torproject.org 2.2.2.2
MapAddress www.torproject.org 1.1.1.1
2. Tor evaluates the MapAddress configuration until it finds no matches. So
if you have the following in your torrc, www.torproject.org will map to
2.2.2.2:
MapAddress 1.1.1.1 2.2.2.2
MapAddress www.torproject.org 1.1.1.1
3. The following MapAddress expression is invalid (and will be
ignored) because you cannot map from a specific address to a wildcard
address:
MapAddress www.torproject.org *.torproject.org.torserver.exit
4. Using a wildcard to match only part of a string (as in *ample.com) is
also invalid.
**NewCircuitPeriod** __NUM__:: **NewCircuitPeriod** __NUM__::
Every NUM seconds consider whether to build a new circuit. (Default: 30 Every NUM seconds consider whether to build a new circuit. (Default: 30

View File

@ -1687,7 +1687,7 @@ consider_recording_trackhost(const entry_connection_t *conn,
addressmap_register(conn->socks_request->address, new_address, addressmap_register(conn->socks_request->address, new_address,
time(NULL) + options->TrackHostExitsExpire, time(NULL) + options->TrackHostExitsExpire,
ADDRMAPSRC_TRACKEXIT); ADDRMAPSRC_TRACKEXIT, 0, 0);
} }
/** Attempt to attach the connection <b>conn</b> to <b>circ</b>, and send a /** Attempt to attach the connection <b>conn</b> to <b>circ</b>, and send a

View File

@ -4647,24 +4647,60 @@ config_register_addressmaps(const or_options_t *options)
addressmap_clear_configured(); addressmap_clear_configured();
elts = smartlist_create(); elts = smartlist_create();
for (opt = options->AddressMap; opt; opt = opt->next) { for (opt = options->AddressMap; opt; opt = opt->next) {
int from_wildcard = 0, to_wildcard = 0;
smartlist_split_string(elts, opt->value, NULL, smartlist_split_string(elts, opt->value, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2);
if (smartlist_len(elts) >= 2) { if (smartlist_len(elts) < 2) {
from = smartlist_get(elts,0);
to = smartlist_get(elts,1);
if (address_is_invalid_destination(to, 1)) {
log_warn(LD_CONFIG,
"Skipping invalid argument '%s' to MapAddress", to);
} else {
addressmap_register(from, tor_strdup(to), 0, ADDRMAPSRC_TORRC);
if (smartlist_len(elts)>2) {
log_warn(LD_CONFIG,"Ignoring extra arguments to MapAddress.");
}
}
} else {
log_warn(LD_CONFIG,"MapAddress '%s' has too few arguments. Ignoring.", log_warn(LD_CONFIG,"MapAddress '%s' has too few arguments. Ignoring.",
opt->value); opt->value);
goto cleanup;
} }
from = smartlist_get(elts,0);
to = smartlist_get(elts,1);
if (to[0] == '.' || from[0] == '.') {
log_warn(LD_CONFIG,"MapAddress '%s' is ambiguous - address starts with a"
"'.'. Ignoring.",opt->value);
goto cleanup;
}
if (!strcmp(to, "*") || !strcmp(from, "*")) {
log_warn(LD_CONFIG,"MapAddress '%s' is unsupported - can't remap from "
"or to *. Ignoring.",opt->value);
goto cleanup;
}
/* Detect asterisks in expressions of type: '*.example.com' */
if (!strncmp(from,"*.",2)) {
from += 2;
from_wildcard = 1;
}
if (!strncmp(to,"*.",2)) {
to += 2;
to_wildcard = 1;
}
if (to_wildcard && !from_wildcard) {
log_warn(LD_CONFIG,
"Skipping invalid argument '%s' to MapAddress: "
"can only use wildcard (i.e. '*.') if 'from' address "
"uses wildcard also", opt->value);
goto cleanup;
}
if (address_is_invalid_destination(to, 1)) {
log_warn(LD_CONFIG,
"Skipping invalid argument '%s' to MapAddress", opt->value);
goto cleanup;
}
addressmap_register(from, tor_strdup(to), 0, ADDRMAPSRC_TORRC,
from_wildcard, to_wildcard);
if (smartlist_len(elts) > 2)
log_warn(LD_CONFIG,"Ignoring extra arguments to MapAddress.");
cleanup:
SMARTLIST_FOREACH(elts, char*, cp, tor_free(cp)); SMARTLIST_FOREACH(elts, char*, cp, tor_free(cp));
smartlist_clear(elts); smartlist_clear(elts);
} }

View File

@ -803,11 +803,18 @@ connection_ap_detach_retriable(entry_connection_t *conn,
* the configuration file, "1" for mappings set from the control * the configuration file, "1" for mappings set from the control
* interface, and other values for DNS and TrackHostExit mappings that can * interface, and other values for DNS and TrackHostExit mappings that can
* expire.) * expire.)
*
* A mapping may be 'wildcarded'. If "src_wildcard" is true, then
* any address that ends with a . followed by the key for this entry will
* get remapped by it. If "dst_wildcard" is also true, then only the
* matching suffix of such addresses will get replaced by new_address.
*/ */
typedef struct { typedef struct {
char *new_address; char *new_address;
time_t expires; time_t expires;
addressmap_entry_source_t source:3; addressmap_entry_source_t source:3;
unsigned src_wildcard:1;
unsigned dst_wildcard:1;
short num_resolve_failures; short num_resolve_failures;
} addressmap_entry_t; } addressmap_entry_t;
@ -1054,6 +1061,37 @@ addressmap_free_all(void)
virtaddress_reversemap = NULL; virtaddress_reversemap = NULL;
} }
/** Try to find a match for AddressMap expressions that use
* wildcard notation such as '*.c.d *.e.f' (so 'a.c.d' will map to 'a.e.f') or
* '*.c.d a.b.c' (so 'a.c.d' will map to a.b.c).
* Return the matching entry in AddressMap or NULL if no match is found.
* For expressions such as '*.c.d *.e.f', truncate <b>address</b> 'a.c.d'
* to 'a' before we return the matching AddressMap entry.
*
* This function does not handle the case where a pattern of the form "*.c.d"
* matches the address c.d -- that's done by the main addressmap_rewrite
* function.
*/
static addressmap_entry_t *
addressmap_match_superdomains(char *address)
{
addressmap_entry_t *val;
char *cp;
cp = address;
while ((cp = strchr(cp, '.'))) {
/* cp now points to a suffix of address that begins with a . */
val = strmap_get_lc(addressmap, cp+1);
if (val && val->src_wildcard) {
if (val->dst_wildcard)
*cp = '\0';
return val;
}
++cp;
}
return NULL;
}
/** Look at address, and rewrite it until it doesn't want any /** Look at address, and rewrite it until it doesn't want any
* more rewrites; but don't get into an infinite loop. * more rewrites; but don't get into an infinite loop.
* Don't write more than maxlen chars into address. Return true if the * Don't write more than maxlen chars into address. Return true if the
@ -1066,25 +1104,49 @@ addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out)
{ {
addressmap_entry_t *ent; addressmap_entry_t *ent;
int rewrites; int rewrites;
char *cp;
time_t expires = TIME_MAX; time_t expires = TIME_MAX;
for (rewrites = 0; rewrites < 16; rewrites++) { for (rewrites = 0; rewrites < 16; rewrites++) {
int exact_match = 0;
char *addr_orig = tor_strdup(escaped_safe_str_client(address));
ent = strmap_get(addressmap, address); ent = strmap_get(addressmap, address);
if (!ent || !ent->new_address) { if (!ent || !ent->new_address) {
ent = addressmap_match_superdomains(address);
} else {
if (ent->src_wildcard && !ent->dst_wildcard &&
!strcasecmp(address, ent->new_address)) {
/* This is a rule like *.example.com example.com, and we just got
* "example.com" */
tor_free(addr_orig);
if (expires_out)
*expires_out = expires;
return rewrites > 0;
}
exact_match = 1;
}
if (!ent || !ent->new_address) {
tor_free(addr_orig);
if (expires_out) if (expires_out)
*expires_out = expires; *expires_out = expires;
return (rewrites > 0); /* done, no rewrite needed */ return (rewrites > 0); /* done, no rewrite needed */
} }
cp = tor_strdup(escaped_safe_str_client(ent->new_address)); if (ent->dst_wildcard && !exact_match) {
strlcat(address, ".", maxlen);
strlcat(address, ent->new_address, maxlen);
} else {
strlcpy(address, ent->new_address, maxlen);
}
log_info(LD_APP, "Addressmap: rewriting %s to %s", log_info(LD_APP, "Addressmap: rewriting %s to %s",
escaped_safe_str_client(address), cp); addr_orig, escaped_safe_str_client(address));
if (ent->expires > 1 && ent->expires < expires) if (ent->expires > 1 && ent->expires < expires)
expires = ent->expires; expires = ent->expires;
tor_free(cp); tor_free(addr_orig);
strlcpy(address, ent->new_address, maxlen);
} }
log_warn(LD_CONFIG, log_warn(LD_CONFIG,
"Loop detected: we've rewritten %s 16 times! Using it as-is.", "Loop detected: we've rewritten %s 16 times! Using it as-is.",
@ -1148,17 +1210,34 @@ addressmap_have_mapping(const char *address, int update_expiry)
* <b>new_address</b> should be a newly dup'ed string, which we'll use or * <b>new_address</b> should be a newly dup'ed string, which we'll use or
* free as appropriate. We will leave address alone. * free as appropriate. We will leave address alone.
* *
* If <b>new_address</b> is NULL, or equal to <b>address</b>, remove * If <b>wildcard_addr</b> is true, then the mapping will match any address
* any mappings that exist from <b>address</b>. * equal to <b>address</b>, or any address ending with a period followed by
*/ * <b>address</b>. If <b>wildcard_addr</b> and <b>wildcard_new_addr</b> are
* both true, the mapping will rewrite addresses that end with
* ".<b>address</b>" into ones that end with ".<b>new_address</b>."
*
* If <b>new_address</b> is NULL, or <b>new_address</b> is equal to
* <b>address</b> and <b>wildcard_addr</b> is equal to
* <b>wildcard_new_addr</b>, remove any mappings that exist from
* <b>address</b>.
*
*
* It is an error to set <b>wildcard_new_addr</b> if <b>wildcard_addr</b> is
* not set. */
void void
addressmap_register(const char *address, char *new_address, time_t expires, addressmap_register(const char *address, char *new_address, time_t expires,
addressmap_entry_source_t source) addressmap_entry_source_t source,
const int wildcard_addr,
const int wildcard_new_addr)
{ {
addressmap_entry_t *ent; addressmap_entry_t *ent;
if (wildcard_new_addr)
tor_assert(wildcard_addr);
ent = strmap_get(addressmap, address); ent = strmap_get(addressmap, address);
if (!new_address || !strcasecmp(address,new_address)) { if (!new_address || (!strcasecmp(address,new_address) &&
wildcard_addr == wildcard_new_addr)) {
/* Remove the mapping, if any. */ /* Remove the mapping, if any. */
tor_free(new_address); tor_free(new_address);
if (ent) { if (ent) {
@ -1193,6 +1272,8 @@ addressmap_register(const char *address, char *new_address, time_t expires,
ent->expires = expires==2 ? 1 : expires; ent->expires = expires==2 ? 1 : expires;
ent->num_resolve_failures = 0; ent->num_resolve_failures = 0;
ent->source = source; ent->source = source;
ent->src_wildcard = wildcard_addr ? 1 : 0;
ent->dst_wildcard = wildcard_new_addr ? 1 : 0;
log_info(LD_CONFIG, "Addressmap: (re)mapped '%s' to '%s'", log_info(LD_CONFIG, "Addressmap: (re)mapped '%s' to '%s'",
safe_str_client(address), safe_str_client(address),
@ -1277,7 +1358,7 @@ client_dns_set_addressmap_impl(const char *address, const char *name,
"%s", name); "%s", name);
} }
addressmap_register(extendedaddress, tor_strdup(extendedval), addressmap_register(extendedaddress, tor_strdup(extendedval),
time(NULL) + ttl, ADDRMAPSRC_DNS); time(NULL) + ttl, ADDRMAPSRC_DNS, 0, 0);
} }
/** Record the fact that <b>address</b> resolved to <b>val</b>. /** Record the fact that <b>address</b> resolved to <b>val</b>.
@ -1529,7 +1610,7 @@ addressmap_register_virtual_address(int type, char *new_address)
log_info(LD_APP, "Registering map from %s to %s", *addrp, new_address); log_info(LD_APP, "Registering map from %s to %s", *addrp, new_address);
if (vent_needs_to_be_added) if (vent_needs_to_be_added)
strmap_set(virtaddress_reversemap, new_address, vent); strmap_set(virtaddress_reversemap, new_address, vent);
addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP); addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP, 0, 0);
#if 0 #if 0
{ {

View File

@ -78,7 +78,8 @@ int addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out);
int addressmap_have_mapping(const char *address, int update_timeout); int addressmap_have_mapping(const char *address, int update_timeout);
void addressmap_register(const char *address, char *new_address, void addressmap_register(const char *address, char *new_address,
time_t expires, addressmap_entry_source_t source); time_t expires, addressmap_entry_source_t source,
int address_wildcard, int new_address_wildcard);
int parse_virtual_addr_network(const char *val, int validate_only, int parse_virtual_addr_network(const char *val, int validate_only,
char **msg); char **msg);
int client_dns_incr_failures(const char *address); int client_dns_incr_failures(const char *address);

View File

@ -1331,7 +1331,8 @@ handle_control_mapaddress(control_connection_t *conn, uint32_t len,
smartlist_add(reply, ans); smartlist_add(reply, ans);
} }
} else { } else {
addressmap_register(from, tor_strdup(to), 1, ADDRMAPSRC_CONTROLLER); addressmap_register(from, tor_strdup(to), 1,
ADDRMAPSRC_CONTROLLER, 0, 0);
tor_snprintf(ans, anslen, "250-%s", line); tor_snprintf(ans, anslen, "250-%s", line);
smartlist_add(reply, ans); smartlist_add(reply, ans);
} }

View File

@ -21,6 +21,7 @@ test_SOURCES = \
test_microdesc.c \ test_microdesc.c \
test_pt.c \ test_pt.c \
test_util.c \ test_util.c \
test_config.c \
tinytest.c tinytest.c
bench_SOURCES = \ bench_SOURCES = \

View File

@ -1844,6 +1844,7 @@ extern struct testcase_t util_tests[];
extern struct testcase_t dir_tests[]; extern struct testcase_t dir_tests[];
extern struct testcase_t microdesc_tests[]; extern struct testcase_t microdesc_tests[];
extern struct testcase_t pt_tests[]; extern struct testcase_t pt_tests[];
extern struct testcase_t config_tests[];
static struct testgroup_t testgroups[] = { static struct testgroup_t testgroups[] = {
{ "", test_array }, { "", test_array },
@ -1855,6 +1856,7 @@ static struct testgroup_t testgroups[] = {
{ "dir/", dir_tests }, { "dir/", dir_tests },
{ "dir/md/", microdesc_tests }, { "dir/md/", microdesc_tests },
{ "pt/", pt_tests }, { "pt/", pt_tests },
{ "config/", config_tests },
END_OF_GROUPS END_OF_GROUPS
}; };

170
src/test/test_config.c Normal file
View File

@ -0,0 +1,170 @@
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2010, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "orconfig.h"
#include "or.h"
#include "config.h"
#include "connection_edge.h"
#include "test.h"
static void
test_config_addressmap(void *arg)
{
char buf[1024];
char address[256];
time_t expires = TIME_MAX;
(void)arg;
strlcpy(buf, "MapAddress .invalidwildcard.com *.torserver.exit\n" // invalid
"MapAddress *invalidasterisk.com *.torserver.exit\n" // invalid
"MapAddress *.google.com *.torserver.exit\n"
"MapAddress *.yahoo.com *.google.com.torserver.exit\n"
"MapAddress *.cn.com www.cnn.com\n"
"MapAddress *.cnn.com www.cnn.com\n"
"MapAddress ex.com www.cnn.com\n"
"MapAddress ey.com *.cnn.com\n"
"MapAddress www.torproject.org 1.1.1.1\n"
"MapAddress other.torproject.org "
"this.torproject.org.otherserver.exit\n"
"MapAddress test.torproject.org 2.2.2.2\n"
"MapAddress www.google.com 3.3.3.3\n"
"MapAddress www.example.org 4.4.4.4\n"
"MapAddress 4.4.4.4 7.7.7.7\n"
"MapAddress 4.4.4.4 5.5.5.5\n"
"MapAddress www.infiniteloop.org 6.6.6.6\n"
"MapAddress 6.6.6.6 www.infiniteloop.org\n"
, sizeof(buf));
config_get_lines(buf, &(get_options_mutable()->AddressMap), 0);
config_register_addressmaps(get_options());
/* MapAddress .invalidwildcard.com .torserver.exit - no match */
strlcpy(address, "www.invalidwildcard.com", sizeof(address));
test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
/* MapAddress *invalidasterisk.com .torserver.exit - no match */
strlcpy(address, "www.invalidasterisk.com", sizeof(address));
test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
/* Where no mapping for FQDN match on top-level domain */
/* MapAddress .google.com .torserver.exit */
strlcpy(address, "reader.google.com", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "reader.torserver.exit");
/* MapAddress *.yahoo.com *.google.com.torserver.exit */
strlcpy(address, "reader.yahoo.com", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "reader.google.com.torserver.exit");
/*MapAddress *.cnn.com www.cnn.com */
strlcpy(address, "cnn.com", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "www.cnn.com");
/* MapAddress .cn.com www.cnn.com */
strlcpy(address, "www.cn.com", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "www.cnn.com");
/* MapAddress ex.com www.cnn.com - no match */
strlcpy(address, "www.ex.com", sizeof(address));
test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
/* MapAddress ey.com *.cnn.com - invalid expression */
strlcpy(address, "ey.com", sizeof(address));
test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
/* Where mapping for FQDN match on FQDN */
strlcpy(address, "www.google.com", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "3.3.3.3");
strlcpy(address, "www.torproject.org", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "1.1.1.1");
strlcpy(address, "other.torproject.org", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "this.torproject.org.otherserver.exit");
strlcpy(address, "test.torproject.org", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "2.2.2.2");
/* Test a chain of address mappings and the order in which they were added:
"MapAddress www.example.org 4.4.4.4"
"MapAddress 4.4.4.4 7.7.7.7"
"MapAddress 4.4.4.4 5.5.5.5"
*/
strlcpy(address, "www.example.org", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "5.5.5.5");
/* Test infinite address mapping results in no change */
strlcpy(address, "www.infiniteloop.org", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "www.infiniteloop.org");
/* Test we don't find false positives */
strlcpy(address, "www.example.com", sizeof(address));
test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
/* Test top-level-domain matching a bit harder */
addressmap_clear_configured();
strlcpy(buf, "MapAddress *.com *.torserver.exit\n"
"MapAddress *.torproject.org 1.1.1.1\n"
"MapAddress *.net 2.2.2.2\n"
, sizeof(buf));
config_get_lines(buf, &(get_options_mutable()->AddressMap), 0);
config_register_addressmaps(get_options());
strlcpy(address, "www.abc.com", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "www.abc.torserver.exit");
strlcpy(address, "www.def.com", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "www.def.torserver.exit");
strlcpy(address, "www.torproject.org", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "1.1.1.1");
strlcpy(address, "test.torproject.org", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "1.1.1.1");
strlcpy(address, "torproject.net", sizeof(address));
test_assert(addressmap_rewrite(address, sizeof(address), &expires));
test_streq(address, "2.2.2.2");
/* We don't support '*' as a mapping directive */
addressmap_clear_configured();
strlcpy(buf, "MapAddress * *.torserver.exit\n", sizeof(buf));
config_get_lines(buf, &(get_options_mutable()->AddressMap), 0);
config_register_addressmaps(get_options());
strlcpy(address, "www.abc.com", sizeof(address));
test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
strlcpy(address, "www.def.net", sizeof(address));
test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
strlcpy(address, "www.torproject.org", sizeof(address));
test_assert(!addressmap_rewrite(address, sizeof(address), &expires));
done:
;
}
#define CONFIG_TEST(name, flags) \
{ #name, test_config_ ## name, flags, NULL, NULL }
struct testcase_t config_tests[] = {
CONFIG_TEST(addressmap, 0),
END_OF_TESTCASES
};