r8872@Kushana: nickm | 2006-09-21 14:00:20 -0400

Implement server-side reverse DNS using eventdns.  Add an option to routerdescs so we can tell which servers have eventdns enabled.


svn:r8437
This commit is contained in:
Nick Mathewson 2006-09-21 21:48:06 +00:00
parent b2cc52fa02
commit a942441615
7 changed files with 239 additions and 34 deletions

View File

@ -1,4 +1,12 @@
Changes in version 0.1.2.2-alpha - 2006-??-??
o Major features:
- Add server-side support for "reverse" DNS lookups (using PTR
records so clients can determine the canonical hostname for a given
IPv4 address). This has been specified for a long time, but was
previously never implemented. This is only supported by eventdns;
servers now announce in their descriptors whether they support
eventdns.
o Security Fixes, minor
- If a client asked for a server by name, and we didn't have a

View File

@ -100,13 +100,15 @@ d - Autodetect whether DNS is broken in this way.
d - Add option to use /etc/hosts?
d - Special-case localhost?
- Verify that it works on windows
N - Make reverse DNS work.
- Specify
. Make reverse DNS work.
o Specify
X Implement with dnsworkers
(There's no point doing this, since we will throw away dnsworkers once
eventdns is confirmed to work everywhere.)
o Implement in eventdns
- Connect to resolve cells, server-side.
o Connect to resolve cells, server-side.
o Add element to routerinfo to note routers that aren't using eventdns,
so we can avoid sending them reverse DNS etc.
- Add client-side interface
- Performance improvements

View File

@ -254,6 +254,21 @@ $Id$
[We didn't start parsing these lines until Tor 0.1.0.6-rc; they should
be marked with "opt" until earlier versions of Tor are obsolete.]
"eventdns" bool NL
Declare whether this version of Tor is using the newer enhanced
dns logic. Versions of Tor without eventdns SHOULD not be used for
reverse hostname lookups.
[All versions of Tor before 0.1.2.2-alpha should be assumed to have
this option set to 0 if it is not present. All Tor versions at
0.1.2.2-alpha or later should be assumed to have this option set to
1 if it is not present. Until 0.1.2.1-alpha-dev, this option was
not generated, even when eventdns was in use. Versions of Tor
before 0.1.2.1-alpha-dev did not parse this option, so it should be
marked "opt". With some future version, the old 'dnsworker' logic
will be removed, rendering this option of historical interest only.]
2.1. Nonterminals in router descriptors
nickname ::= between 1 and 19 alphanumeric characters, case-insensitive.
@ -270,6 +285,8 @@ $Id$
ip6 ::= an IPv6 address, surrounded by square brackets.
num_ip6_bits ::= an integer between 0 and 128
bool ::= "0" | "1"
Ports are required; if they are not included in the router
line, they must appear in the "ports" lines.

View File

@ -97,8 +97,12 @@ typedef struct cached_resolve_t {
HT_ENTRY(cached_resolve_t) node;
uint32_t magic;
char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */
union {
uint32_t addr; /**< IPv4 addr for <b>address</b>. */
char *hostname; /**< Hostname for <b>address</b> (if a reverse lookup) */
} result;
uint8_t state; /**< Is this cached entry pending/done/valid/failed? */
uint8_t is_reverse; /**< Is this a reverse (addr-to-hostname) lookup? */
time_t expire; /**< Remove items from cache after this time. */
uint32_t ttl; /**< What TTL did the nameserver tell us? */
/** Connections that want to know when we get an answer for this resolve. */
@ -106,7 +110,8 @@ typedef struct cached_resolve_t {
} cached_resolve_t;
static void purge_expired_resolves(time_t now);
static void dns_found_answer(const char *address, uint32_t addr, char outcome,
static void dns_found_answer(const char *address, int is_reverse,
uint32_t addr, const char *hostname, char outcome,
uint32_t ttl);
static void send_resolved_cell(edge_connection_t *conn, uint8_t answer_type);
static int launch_resolve(edge_connection_t *exitconn);
@ -245,6 +250,8 @@ _free_cached_resolve(cached_resolve_t *r)
r->pending_connections = victim->next;
tor_free(victim);
}
if (r->is_reverse)
tor_free(r->result.hostname);
r->magic = 0xFF00FF00;
tor_free(r);
}
@ -362,6 +369,8 @@ purge_expired_resolves(time_t now)
removed ? removed->address : "NULL", (void*)remove);
}
tor_assert(removed == resolve);
if (resolve->is_reverse)
tor_free(resolve->result.hostname);
resolve->magic = 0xF0BBF0BB;
tor_free(resolve);
} else {
@ -415,6 +424,74 @@ send_resolved_cell(edge_connection_t *conn, uint8_t answer_type)
conn->cpath_layer);
}
/** Send a response to the RESOLVE request of a connection for an in-addr.arpa
* address on connection <b>conn</b> which yielded the result <b>hostname</b>.
* The answer type will be RESOLVED_HOSTNAME.
*/
static void
send_resolved_hostname_cell(edge_connection_t *conn, const char *hostname)
{
char buf[RELAY_PAYLOAD_SIZE];
size_t buflen;
uint32_t ttl;
size_t namelen = strlen(hostname);
tor_assert(namelen < 256);
ttl = dns_clip_ttl(conn->address_ttl);
buf[0] = RESOLVED_TYPE_HOSTNAME;
buf[1] = (uint8_t)namelen;
memcpy(buf+2, hostname, namelen);
set_uint32(buf+2+namelen, htonl(ttl));
buflen = 2+namelen+4;
connection_edge_send_command(conn, circuit_get_by_edge_conn(conn),
RELAY_COMMAND_RESOLVED, buf, buflen,
conn->cpath_layer);
}
/** Given a lower-case <b>address</b>, check to see whether it's a
* 1.2.3.4.in-addr.arpa address used for reverse lookups. If so,
* parse it and place the address in <b>in</b> if present. Return 1 on success;
* 0 if the address is not in in-addr.arpa format, and -1 if the address is
* malformed. */
static int
parse_inaddr_arpa_address(const char *address, struct in_addr *in)
{
char buf[INET_NTOA_BUF_LEN];
char *cp;
size_t len;
struct in_addr inaddr;
cp = strstr(address, ".in-addr.arpa");
if (!cp || *(cp+strlen(".in-addr.arpa")))
return 0; /* not an .in-addr.arpa address */
len = cp - address;
if (len >= INET_NTOA_BUF_LEN)
return -1; /* Too long. */
memcpy(buf, cp, len);
buf[len] = '\0';
if (tor_inet_aton(buf, &inaddr) == 0)
return -1; /* malformed. */
if (in) {
uint32_t a;
/* reverse the bytes */
a = ( ((inaddr.s_addr & 0x000000fful) << 24)
|((inaddr.s_addr & 0x0000ff00ul) << 8)
|((inaddr.s_addr & 0x00ff0000ul) >> 8)
|((inaddr.s_addr & 0xff000000ul) >> 24));
inaddr.s_addr = a;
memcpy(in, &inaddr, sizeof(inaddr));
}
return 1;
}
/** See if we have a cache entry for <b>exitconn</b>-\>address. if so,
* if resolve valid, put it into <b>exitconn</b>-\>addr and return 1.
* If resolve failed, unlink exitconn if needed, free it, and return -1.
@ -431,20 +508,23 @@ dns_resolve(edge_connection_t *exitconn)
cached_resolve_t *resolve;
cached_resolve_t search;
pending_connection_t *pending_connection;
struct in_addr in;
circuit_t *circ;
struct in_addr in;
time_t now = time(NULL);
int is_reverse = 0, is_resolve, r;
assert_connection_ok(TO_CONN(exitconn), 0);
tor_assert(exitconn->_base.s == -1);
assert_cache_ok();
is_resolve = exitconn->_base.purpose = EXIT_PURPOSE_RESOLVE;
/* first check if exitconn->_base.address is an IP. If so, we already
* know the answer. */
if (tor_inet_aton(exitconn->_base.address, &in) != 0) {
exitconn->_base.addr = ntohl(in.s_addr);
exitconn->address_ttl = DEFAULT_DNS_TTL;
if (exitconn->_base.purpose == EXIT_PURPOSE_RESOLVE)
if (is_resolve)
send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4);
return 1;
}
@ -456,6 +536,42 @@ dns_resolve(edge_connection_t *exitconn)
/* lower-case exitconn->_base.address, so it's in canonical form */
tor_strlower(exitconn->_base.address);
/* Check whether this is a reverse lookup. If it's malformed, or it's a
* .in-addr.arpa address but this isn't a resolve request, kill the
* connecction.
*/
if ((r = parse_inaddr_arpa_address(exitconn->_base.address, NULL)) != 0) {
if (r == 1)
is_reverse = 1;
#ifdef USE_EVENTDNS
if (!is_reverse || !is_resolve) {
if (!is_reverse)
log_info(LD_EXIT, "Bad .in-addr.arpa address \"%s\"; sending error.",
escaped_safe_str(exitconn->_base.address));
else if (!is_resolve)
log_info(LD_EXIT,
"Attempt to connect to a .in-addr.arpa address \"%s\"; "
"sending error.",
escaped_safe_str(exitconn->_base.address));
#else
if (1) {
log_info(LD_PROTOCOL, "Dnsworker code does not support in-addr.arpa "
"domain, but received a request for \"%s\"; sending error.",
escaped_safe_str(exitconn->_base.address));
#endif
if (exitconn->_base.purpose == EXIT_PURPOSE_RESOLVE)
send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR);
circ = circuit_get_by_edge_conn(exitconn);
if (circ)
circuit_detach_stream(circ, exitconn);
if (!exitconn->_base.marked_for_close)
connection_free(TO_CONN(exitconn));
return -1;
}
}
/* now check the hash table to see if 'address' is already there. */
strlcpy(search.address, exitconn->_base.address, sizeof(search.address));
resolve = HT_FIND(cache_map, &cache_root, &search);
@ -474,19 +590,24 @@ dns_resolve(edge_connection_t *exitconn)
exitconn->_base.state = EXIT_CONN_STATE_RESOLVING;
return 0;
case CACHE_STATE_CACHED_VALID:
exitconn->_base.addr = resolve->addr;
exitconn->address_ttl = resolve->ttl;
log_debug(LD_EXIT,"Connection (fd %d) found cached answer for %s",
exitconn->_base.s,
escaped_safe_str(exitconn->_base.address));
if (exitconn->_base.purpose == EXIT_PURPOSE_RESOLVE)
escaped_safe_str(resolve->address));
exitconn->address_ttl = resolve->ttl;
if (resolve->is_reverse) {
tor_assert(is_resolve);
send_resolved_hostname_cell(exitconn, resolve->result.hostname);
} else {
exitconn->_base.addr = resolve->result.addr;
if (is_resolve)
send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4);
}
return 1;
case CACHE_STATE_CACHED_FAILED:
log_debug(LD_EXIT,"Connection (fd %d) found cached error for %s",
exitconn->_base.s,
escaped_safe_str(exitconn->_base.address));
if (exitconn->_base.purpose == EXIT_PURPOSE_RESOLVE)
if (is_resolve)
send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR);
circ = circuit_get_by_edge_conn(exitconn);
if (circ)
@ -504,6 +625,7 @@ dns_resolve(edge_connection_t *exitconn)
resolve = tor_malloc_zero(sizeof(cached_resolve_t));
resolve->magic = CACHED_RESOLVE_MAGIC;
resolve->state = CACHE_STATE_PENDING;
resolve->is_reverse = is_reverse;
strlcpy(resolve->address, exitconn->_base.address, sizeof(resolve->address));
/* add this connection to the pending list */
@ -612,7 +734,7 @@ connection_dns_remove(edge_connection_t *conn)
* <b>address</b> from the cache.
*/
void
dns_cancel_pending_resolve(char *address)
dns_cancel_pending_resolve(const char *address)
{
pending_connection_t *pend;
cached_resolve_t search;
@ -675,10 +797,13 @@ dns_cancel_pending_resolve(char *address)
/** Helper: adds an entry to the DNS cache mapping <b>address</b> to the ipv4
* address <b>addr</b>. <b>ttl</b> is a cache ttl; <b>outcome</b> is one of
* DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}. */
* DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}.
*
* DOCDOC args
**/
static void
add_answer_to_cache(const char *address, uint32_t addr, char outcome,
uint32_t ttl)
add_answer_to_cache(const char *address, int is_reverse, uint32_t addr,
const char *hostname, char outcome, uint32_t ttl)
{
cached_resolve_t *resolve;
if (outcome == DNS_RESOLVE_FAILED_TRANSIENT)
@ -689,7 +814,13 @@ add_answer_to_cache(const char *address, uint32_t addr, char outcome,
resolve->state = (outcome == DNS_RESOLVE_SUCCEEDED) ?
CACHE_STATE_CACHED_VALID : CACHE_STATE_CACHED_FAILED;
strlcpy(resolve->address, address, sizeof(resolve->address));
resolve->addr = addr;
if (is_reverse) {
tor_assert(hostname);
resolve->result.hostname = tor_strdup(hostname);
} else {
tor_assert(!hostname);
resolve->result.addr = addr;
}
resolve->ttl = ttl;
assert_resolve_ok(resolve);
HT_INSERT(cache_map, &cache_root, resolve);
@ -704,8 +835,8 @@ add_answer_to_cache(const char *address, uint32_t addr, char outcome,
* DNS_RESOLVE_{FAILED_TRANSIENT|FAILED_PERMANENT|SUCCEEDED}.
*/
static void
dns_found_answer(const char *address, uint32_t addr, char outcome,
uint32_t ttl)
dns_found_answer(const char *address, int is_reverse, uint32_t addr,
const char *hostname, char outcome, uint32_t ttl)
{
pending_connection_t *pend;
cached_resolve_t search;
@ -721,7 +852,7 @@ dns_found_answer(const char *address, uint32_t addr, char outcome,
if (!resolve) {
log_info(LD_EXIT,"Resolved unasked address %s; caching anyway.",
escaped_safe_str(address));
add_answer_to_cache(address, addr, outcome, ttl);
add_answer_to_cache(address, is_reverse, addr, hostname, outcome, ttl);
return;
}
assert_resolve_ok(resolve);
@ -764,6 +895,7 @@ dns_found_answer(const char *address, uint32_t addr, char outcome,
connection_free(TO_CONN(pendconn));
} else {
if (pendconn->_base.purpose == EXIT_PURPOSE_CONNECT) {
tor_assert(!is_reverse);
/* prevent double-remove. */
pend->conn->_base.state = EXIT_CONN_STATE_CONNECTING;
@ -782,6 +914,9 @@ dns_found_answer(const char *address, uint32_t addr, char outcome,
/* prevent double-remove. This isn't really an accurate state,
* but it does the right thing. */
pendconn->_base.state = EXIT_CONN_STATE_RESOLVEFAILED;
if (is_reverse)
send_resolved_hostname_cell(pendconn, hostname);
else
send_resolved_cell(pendconn, RESOLVED_TYPE_IPV4);
circ = circuit_get_by_edge_conn(pendconn);
tor_assert(circ);
@ -804,7 +939,7 @@ dns_found_answer(const char *address, uint32_t addr, char outcome,
assert_resolve_ok(resolve);
assert_cache_ok();
add_answer_to_cache(address, addr, outcome, ttl);
add_answer_to_cache(address, is_reverse, addr, hostname, outcome, ttl);
assert_cache_ok();
}
@ -942,7 +1077,7 @@ connection_dns_process_inbuf(connection_t *conn)
tor_assert(success <= DNS_RESOLVE_SUCCEEDED);
ttl = (success == DNS_RESOLVE_FAILED_TRANSIENT) ? 0 : MAX_DNS_ENTRY_AGE;
dns_found_answer(conn->address, ntohl(addr), success, ttl);
dns_found_answer(conn->address, 0, ntohl(addr), NULL, success, ttl);
tor_free(conn->address);
conn->address = tor_strdup("<idle>");
@ -1320,8 +1455,11 @@ eventdns_callback(int result, char type, int count, int ttl, void *addresses,
void *arg)
{
char *string_address = arg;
int is_reverse = 0;
int status = DNS_RESOLVE_FAILED_PERMANENT;
uint32_t addr = 0;
const char *hostname = NULL;
if (result == DNS_ERR_NONE) {
if (type == DNS_IPv4_A && count) {
char answer_buf[INET_NTOA_BUF_LEN+1];
@ -1333,7 +1471,13 @@ eventdns_callback(int result, char type, int count, int ttl, void *addresses,
tor_inet_ntoa(&in, answer_buf, sizeof(answer_buf));
log_debug(LD_EXIT, "eventdns said that %s resolves to %s",
escaped_safe_str(string_address),
escaped_safe_str(answer_buf));
escaped_safe_str(answer_buf)); // XXXX not ok.
} else if (type == DNS_PTR && count) {
is_reverse = 1;
hostname = ((char**)addresses)[0];
log_debug(LD_EXIT, "eventdns said that %s resolves to %s",
escaped_safe_str(string_address),
escaped_safe_str(hostname)); // XXXX not ok.
} else if (count) {
log_warn(LD_EXIT, "eventdns returned only non-IPv4 answers for %s.",
escaped_safe_str(string_address));
@ -1345,7 +1489,7 @@ eventdns_callback(int result, char type, int count, int ttl, void *addresses,
if (eventdns_err_is_transient(result))
status = DNS_RESOLVE_FAILED_TRANSIENT;
}
dns_found_answer(string_address, addr, status, ttl);
dns_found_answer(string_address, is_reverse, addr, hostname, status, ttl);
tor_free(string_address);
}
@ -1355,6 +1499,7 @@ static int
launch_resolve(edge_connection_t *exitconn)
{
char *addr = tor_strdup(exitconn->_base.address);
struct in_addr in;
int r;
int options = get_options()->SearchDomains ? 0 : DNS_QUERY_NO_SEARCH;
/* What? Nameservers not configured? Sounds like a bug. */
@ -1364,10 +1509,22 @@ launch_resolve(edge_connection_t *exitconn)
if (configure_nameservers(1) < 0)
return -1;
}
r = parse_inaddr_arpa_address(exitconn->_base.address, &in);
if (r == 0) {
log_info(LD_EXIT, "Launching eventdns request for %s",
escaped_safe_str(exitconn->_base.address));
r = eventdns_resolve_ipv4(exitconn->_base.address, options,
eventdns_callback, addr);
} else if (r == 1) {
log_info(LD_EXIT, "Launching eventdns reverse request for %s",
escaped_safe_str(exitconn->_base.address));
r = eventdns_resolve_reverse(&in, DNS_QUERY_NO_SEARCH,
eventdns_callback, addr);
} else if (r == -1) {
log_warn(LD_BUG, "Somehow a malformed in-addr.arpa address reached here.");
}
if (r) {
log_warn(LD_EXIT, "eventdns rejected address %s: error %d.",
escaped_safe_str(addr), r);
@ -1400,7 +1557,10 @@ assert_resolve_ok(cached_resolve_t *resolve)
if (resolve->state == CACHE_STATE_PENDING ||
resolve->state == CACHE_STATE_DONE) {
tor_assert(!resolve->ttl);
tor_assert(!resolve->addr);
if (resolve->is_reverse)
tor_assert(!resolve->result.hostname);
else
tor_assert(!resolve->result.addr);
}
}

View File

@ -900,6 +900,8 @@ typedef struct {
char *contact_info; /**< Declared contact info for this router. */
unsigned int is_hibernating:1; /**< Whether the router claims to be
* hibernating */
unsigned int has_old_dnsworkers:1; /**< Whether the router is using
* dnsworker code. */
/* local info */
unsigned int is_running:1; /**< As far as we know, is this OR currently
@ -2154,7 +2156,7 @@ void dns_reset(void);
void connection_dns_remove(edge_connection_t *conn);
void assert_connection_edge_not_dns_pending(edge_connection_t *conn);
void assert_all_pending_dns_resolves_ok(void);
void dns_cancel_pending_resolve(char *question);
void dns_cancel_pending_resolve(const char *question);
int dns_resolve(edge_connection_t *exitconn);
/********************************* hibernate.c **********************/

View File

@ -1149,7 +1149,13 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
"uptime %ld\n"
"bandwidth %d %d %d\n"
"onion-key\n%s"
"signing-key\n%s%s%s%s",
"signing-key\n"
#ifdef USE_EVENTDNS
"opt eventdns 1\n"
#else
"opt eventdns 0\n"
#endif
"%s%s%s%s",
router->nickname,
router->address,
router->or_port,
@ -1228,6 +1234,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
written += result;
}
} /* end for */
if (written+256 > maxlen) /* Not enough room for signature. */
return -1;

View File

@ -54,6 +54,7 @@ typedef enum {
K_SERVER_VERSIONS,
K_R,
K_S,
K_EVENTDNS,
_UNRECOGNIZED,
_ERR,
_EOF,
@ -145,6 +146,7 @@ static struct {
{ "dir-options", K_DIR_OPTIONS, ARGS, NO_OBJ, NETSTATUS },
{ "client-versions", K_CLIENT_VERSIONS, ARGS, NO_OBJ, NETSTATUS },
{ "server-versions", K_SERVER_VERSIONS, ARGS, NO_OBJ, NETSTATUS },
{ "eventdns", K_EVENTDNS, ARGS, NO_OBJ, RTR },
{ NULL, -1, NO_ARGS, NO_OBJ, ANY }
};
@ -876,6 +878,13 @@ router_parse_entry_from_string(const char *s, const char *end,
router->contact_info = tor_strdup(tok->args[0]);
}
if ((tok = find_first_by_keyword(tokens, K_EVENTDNS))) {
router->has_old_dnsworkers = tok->n_args && !strcmp(tok->args[0], "0");
} else if (router->platform) {
if (! tor_version_as_new_as(router->platform, "0.1.2.2-alpha"))
router->has_old_dnsworkers = 1;
}
exit_policy_tokens = find_all_exitpolicy(tokens);
SMARTLIST_FOREACH(exit_policy_tokens, directory_token_t *, t,
if (router_add_exit_policy(router,t)<0) {