summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am8
-rw-r--r--doc/tor-spec.txt7
-rw-r--r--src/common/compat.c2
-rw-r--r--src/or/connection_edge.c15
-rw-r--r--src/or/dns.c81
-rw-r--r--src/or/eventdns_tor.h1
-rw-r--r--src/or/or.h15
7 files changed, 85 insertions, 44 deletions
diff --git a/Makefile.am b/Makefile.am
index e41d6ebb95..6179291fd1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -55,8 +55,8 @@ doxygen:
# Avoid strlcpy.c, strlcat.c, tree.h
check-spaces:
- ./contrib/checkSpace.pl -C \
- src/common/*.h \
- src/common/[^s]*.c \
- src/or/[^t]*.[ch] src/or/t*.c
+ ./contrib/checkSpace.pl -C \
+ src/common/*.h \
+ src/common/[^as]*.c \
+ src/or/[^et]*.[ch] src/or/t*.c src/or/eventdns_tor.h
diff --git a/doc/tor-spec.txt b/doc/tor-spec.txt
index 347397ce64..6a740bce7b 100644
--- a/doc/tor-spec.txt
+++ b/doc/tor-spec.txt
@@ -571,7 +571,12 @@ when do we rotate which keys (tls, link, etc)?
The IPv6 address to which the connection was made [16 octets]
A number of seconds (TTL) for which the address may be cached [4 octets]
[XXXX Versions of Tor before 0.1.1.6 ignore and do not generate the TTL
- field. No version of Tor currently generates the IPv6 format.]
+ field. No version of Tor currently generates the IPv6 format.
+
+ Tor servers before 0.1.2.0 set the TTL field to a fixed value. Later
+ versions set the TTL to the last value seen from a DNS server, and expire
+ their own cached entries after a fixed interval. This prevents certain
+ attacks.]
The OP waits for a RELAY_CONNECTED cell before sending any data.
Once a connection has been established, the OP and exit node
diff --git a/src/common/compat.c b/src/common/compat.c
index c3e42dbc20..7867c0b894 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -131,7 +131,7 @@ tor_mmap_file(const char *filename, size_t *size)
*size += (page_size + (page_size-(*size%page_size)));
string = mmap(0, *size, PROT_READ, MAP_PRIVATE, fd, 0);
- if(string == MAP_FAILED) {
+ if (string == MAP_FAILED) {
log_warn(LD_FS,"Could not mmap file \"%s\": %s", filename,
strerror(errno));
return NULL;
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 090557a8da..78991185b0 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -196,8 +196,7 @@ connection_edge_end(connection_t *conn, char reason, crypt_path_t *cpath_layer)
if (reason == END_STREAM_REASON_EXITPOLICY &&
!connection_edge_is_rendezvous_stream(conn)) {
set_uint32(payload+1, htonl(conn->addr));
- /* XXXX fill with a real TTL! */
- set_uint32(payload+5, htonl(MAX_DNS_ENTRY_AGE));
+ set_uint32(payload+5, htonl(dns_clip_ttl(conn->address_ttl)));
payload_len += 8;
}
@@ -297,7 +296,7 @@ connection_edge_finished_connecting(connection_t *conn)
char connected_payload[8];
set_uint32(connected_payload, htonl(conn->addr));
set_uint32(connected_payload+4,
- htonl(MAX_DNS_ENTRY_AGE)); /* XXXX fill with a real TTL */
+ htonl(dns_clip_ttl(conn->address_ttl)));
if (connection_edge_send_command(conn, circuit_get_by_edge_conn(conn),
RELAY_COMMAND_CONNECTED, connected_payload, 8, conn->cpath_layer) < 0)
return 0; /* circuit is closed, don't continue */
@@ -682,7 +681,7 @@ client_dns_incr_failures(const char *address)
addressmap_entry_t *ent = strmap_get(addressmap, address);
if (!ent) {
ent = tor_malloc_zero(sizeof(addressmap_entry_t));
- ent->expires = time(NULL)+MAX_DNS_ENTRY_AGE;
+ ent->expires = time(NULL) + MAX_DNS_ENTRY_AGE;
strmap_set(addressmap,address,ent);
}
++ent->num_resolve_failures;
@@ -726,10 +725,10 @@ client_dns_set_addressmap(const char *address, uint32_t val,
char extendedval[INET_NTOA_BUF_LEN+MAX_HEX_NICKNAME_LEN+10];
char valbuf[INET_NTOA_BUF_LEN];
- tor_assert(address); tor_assert(val);
+ tor_assert(address);
+ tor_assert(val);
- if (ttl<0 || ttl>MAX_DNS_ENTRY_AGE)
- ttl = MAX_DNS_ENTRY_AGE;
+ ttl = dns_clip_ttl(ttl);
if (tor_inet_aton(address, &in))
return; /* If address was an IP address already, don't add a mapping. */
@@ -1817,7 +1816,7 @@ connection_exit_connect(connection_t *conn)
char connected_payload[8];
set_uint32(connected_payload, htonl(conn->addr));
set_uint32(connected_payload+4,
- htonl(MAX_DNS_ENTRY_AGE)); /* XXXX fill with a real TTL */
+ htonl(dns_clip_ttl(conn->address_ttl)));
connection_edge_send_command(conn, circuit_get_by_edge_conn(conn),
RELAY_COMMAND_CONNECTED,
connected_payload, 8, conn->cpath_layer);
diff --git a/src/or/dns.c b/src/or/dns.c
index 67972867d5..2e6a7398db 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -71,6 +71,7 @@ typedef struct cached_resolve_t {
#define CACHE_STATE_VALID 1
#define CACHE_STATE_FAILED 2
uint32_t expire; /**< Remove items from cache after this time. */
+ uint32_t ttl; /**< What TTL did the nameserver tell us? */
pending_connection_t *pending_connections;
struct cached_resolve_t *next;
} cached_resolve_t;
@@ -79,8 +80,7 @@ static void purge_expired_resolves(uint32_t now);
static void dns_purge_resolve(cached_resolve_t *resolve);
static void dns_found_answer(char *address, uint32_t addr, char outcome,
uint32_t ttl);
-static void send_resolved_cell(connection_t *conn, uint8_t answer_type,
- uint32_t ttl);
+static void send_resolved_cell(connection_t *conn, uint8_t answer_type);
static int assign_to_dnsworker(connection_t *exitconn);
#ifndef USE_EVENTDNS
static int dnsworker_main(void *data);
@@ -130,6 +130,28 @@ dns_init(void)
#endif
}
+uint32_t
+dns_clip_ttl(uint32_t ttl)
+{
+ if (ttl < MIN_DNS_TTL)
+ return MIN_DNS_TTL;
+ else if (ttl > MAX_DNS_TTL)
+ return MAX_DNS_TTL;
+ else
+ return ttl;
+}
+
+static uint32_t
+dns_get_expiry_ttl(uint32_t ttl)
+{
+ if (ttl < MIN_DNS_TTL)
+ return MIN_DNS_TTL;
+ else if (ttl > MAX_DNS_ENTRY_AGE)
+ return MAX_DNS_ENTRY_AGE;
+ else
+ return ttl;
+}
+
/** Helper: free storage held by an entry in the DNS cache. */
static void
_free_cached_resolve(cached_resolve_t *r)
@@ -212,12 +234,14 @@ purge_expired_resolves(uint32_t now)
/** Send a response to the RESOVLE request of a connection. answer_type must
* be one of RESOLVED_TYPE_(IPV4|ERROR|ERROR_TRANSIENT) */
static void
-send_resolved_cell(connection_t *conn, uint8_t answer_type, uint32_t ttl)
+send_resolved_cell(connection_t *conn, uint8_t answer_type)
{
char buf[RELAY_PAYLOAD_SIZE];
size_t buflen;
+ uint32_t ttl;
buf[0] = answer_type;
+ ttl = dns_clip_ttl(conn->address_ttl);
switch (answer_type)
{
@@ -289,8 +313,9 @@ dns_resolve(connection_t *exitconn)
* know the answer. */
if (tor_inet_aton(exitconn->address, &in) != 0) {
exitconn->addr = ntohl(in.s_addr);
+ exitconn->address_ttl = DEFAULT_DNS_TTL;
if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
- send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4, MAX_DNS_ENTRY_AGE);
+ send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4);
return 1;
}
@@ -305,9 +330,6 @@ dns_resolve(connection_t *exitconn)
strlcpy(search.address, exitconn->address, sizeof(search.address));
resolve = HT_FIND(cache_map, &cache_root, &search);
if (resolve && resolve->expire > now) { /* already there */
- // XXXX Security problem: this leaks a time at which somebody asked for
- // XXXX the address. That's a problem.
- unsigned int ttl = (unsigned int) resolve->expire - now;
switch (resolve->state) {
case CACHE_STATE_PENDING:
/* add us to the pending list */
@@ -323,16 +345,17 @@ dns_resolve(connection_t *exitconn)
return 0;
case CACHE_STATE_VALID:
exitconn->addr = resolve->addr;
+ exitconn->address_ttl = resolve->ttl;
log_debug(LD_EXIT,"Connection (fd %d) found cached answer for %s",
exitconn->s, escaped_safe_str(exitconn->address));
if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
- send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4, ttl);
+ send_resolved_cell(exitconn, RESOLVED_TYPE_IPV4);
return 1;
case CACHE_STATE_FAILED:
log_debug(LD_EXIT,"Connection (fd %d) found cached error for %s",
exitconn->s, escaped_safe_str(exitconn->address));
if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
- send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR, ttl);
+ send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR);
circ = circuit_get_by_edge_conn(exitconn);
if (circ)
circuit_detach_stream(circ, exitconn);
@@ -345,7 +368,7 @@ dns_resolve(connection_t *exitconn)
/* not there, need to add it */
resolve = tor_malloc_zero(sizeof(cached_resolve_t));
resolve->state = CACHE_STATE_PENDING;
- resolve->expire = now + MAX_DNS_ENTRY_AGE;
+ resolve->expire = now + DEFAULT_DNS_TTL; /* this will get replaced. */
strlcpy(resolve->address, exitconn->address, sizeof(resolve->address));
/* add us to the pending list */
@@ -557,7 +580,8 @@ dns_found_answer(char *address, uint32_t addr, char outcome, uint32_t ttl)
resolve->state = (outcome == DNS_RESOLVE_SUCCEEDED) ?
CACHE_STATE_VALID : CACHE_STATE_FAILED;
resolve->addr = addr;
- resolve->expire = time(NULL) + ttl;
+ resolve->expire = time(NULL) + dns_get_expiry_ttl(ttl);
+ resolve->ttl = ttl;
insert_resolve(resolve);
return;
}
@@ -577,7 +601,8 @@ dns_found_answer(char *address, uint32_t addr, char outcome, uint32_t ttl)
/* tor_assert(resolve->state == CACHE_STATE_PENDING); */
resolve->addr = addr;
- resolve->expire = time(NULL) + ttl;
+ resolve->expire = time(NULL) + dns_get_expiry_ttl(ttl);
+ resolve->ttl = ttl;
if (outcome == DNS_RESOLVE_SUCCEEDED)
resolve->state = CACHE_STATE_VALID;
else
@@ -587,6 +612,7 @@ dns_found_answer(char *address, uint32_t addr, char outcome, uint32_t ttl)
pend = resolve->pending_connections;
assert_connection_ok(pend->conn,time(NULL));
pend->conn->addr = resolve->addr;
+ pend->conn->address_ttl = resolve->ttl;
pendconn = pend->conn; /* don't pass complex things to the
connection_mark_for_close macro */
@@ -599,7 +625,7 @@ dns_found_answer(char *address, uint32_t addr, char outcome, uint32_t ttl)
/* This detach must happen after we send the end cell. */
circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
} else {
- send_resolved_cell(pendconn, RESOLVED_TYPE_ERROR, ttl);
+ send_resolved_cell(pendconn, RESOLVED_TYPE_ERROR);
/* This detach must happen after we send the resolved cell. */
circuit_detach_stream(circuit_get_by_edge_conn(pendconn), pendconn);
}
@@ -623,7 +649,7 @@ dns_found_answer(char *address, uint32_t addr, char outcome, uint32_t ttl)
/* prevent double-remove. This isn't really an accurate state,
* but it does the right thing. */
pendconn->state = EXIT_CONN_STATE_RESOLVEFAILED;
- send_resolved_cell(pendconn, RESOLVED_TYPE_IPV4, ttl);
+ send_resolved_cell(pendconn, RESOLVED_TYPE_IPV4);
circ = circuit_get_by_edge_conn(pendconn);
tor_assert(circ);
circuit_detach_stream(circ, pendconn);
@@ -663,7 +689,7 @@ assign_to_dnsworker(connection_t *exitconn)
if (!dnsconn) {
log_warn(LD_EXIT,"no idle dns workers. Failing.");
if (exitconn->purpose == EXIT_PURPOSE_RESOLVE)
- send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR_TRANSIENT, 0);
+ send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR_TRANSIENT);
goto err;
}
@@ -1051,28 +1077,30 @@ eventdns_callback(int result, char type, int count, int ttl, void *addresses,
void *arg)
{
char *string_address = arg;
+ int status = DNS_RESOLVE_FAILED_PERMANENT;
+ uint32_t addr = 0;
if (result == DNS_ERR_NONE) {
if (type == DNS_IPv4_A && count) {
uint32_t *addrs = addresses;
- dns_found_answer(string_address, addrs[0], DNS_RESOLVE_SUCCEEDED, ttl);
+ addr = addrs[0];
+ status = DNS_RESOLVE_SUCCEEDED;
} else if (count) {
log_warn(LD_EXIT, "eventdns returned only non-IPv4 answers for %s.",
escaped_safe_str(string_address));
- dns_found_answer(string_address, 0, DNS_RESOLVE_FAILED_PERMANENT, ttl);
} else {
log_warn(LD_BUG, "eventdns returned no addresses or error for %s!",
escaped_safe_str(string_address));
- dns_found_answer(string_address, 0, DNS_RESOLVE_FAILED_PERMANENT, ttl);
}
} else {
- int err = eventdns_err_is_transient(result)
- ? DNS_RESOLVE_FAILED_TRANSIENT : DNS_RESOLVE_FAILED_PERMANENT;
- dns_found_answer(string_address, 0, err, ttl);
+ if (eventdns_err_is_transient(result))
+ status = DNS_RESOLVE_FAILED_TRANSIENT;
}
+ dns_found_answer(string_address, addr, status, ttl);
tor_free(string_address);
}
-static int assign_to_dnsworker(connection_t *exitconn)
+static int
+assign_to_dnsworker(connection_t *exitconn)
{
char *addr = tor_strdup(exitconn->address);
int r = eventdns_resolve(exitconn->address, DNS_QUERY_NO_SEARCH,
@@ -1082,9 +1110,11 @@ static int assign_to_dnsworker(connection_t *exitconn)
escaped_safe_str(addr), r);
if (exitconn->purpose == EXIT_PURPOSE_RESOLVE) {
if (eventdns_err_is_transient(r))
- send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR_TRANSIENT, 0);
- else
- send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR, MAX_DNS_ENTRY_AGE);
+ send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR_TRANSIENT);
+ else {
+ exitconn->address_ttl = DEFAULT_DNS_TTL;
+ send_resolved_cell(exitconn, RESOLVED_TYPE_ERROR);
+ }
}
dns_cancel_pending_resolve(addr);/* also sends end and frees */
tor_free(addr);
@@ -1092,3 +1122,4 @@ static int assign_to_dnsworker(connection_t *exitconn)
return r ? -1 : 0;
}
#endif /* USE_EVENTDNS */
+
diff --git a/src/or/eventdns_tor.h b/src/or/eventdns_tor.h
index 06abe359f7..a18db12d7d 100644
--- a/src/or/eventdns_tor.h
+++ b/src/or/eventdns_tor.h
@@ -3,3 +3,4 @@
#define DNS_USE_OPENSSL_FOR_ID
typedef unsigned int uint;
typedef unsigned char u_char;
+
diff --git a/src/or/or.h b/src/or/or.h
index 9e0200752e..79101a9a91 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -161,12 +161,14 @@
#define MAX_HEADERS_SIZE 50000
#define MAX_BODY_SIZE 500000
-#ifdef TOR_PERF
-/** How long do we keep DNS cache entries before purging them? */
-#define MAX_DNS_ENTRY_AGE (150*60)
-#else
+/** How long do we keep DNS cache entries before purging them (regardless of
+ * their TTL)? */
#define MAX_DNS_ENTRY_AGE (30*60)
-#endif
+#define DEFAULT_DNS_TTL (30*60)
+/** How long can a TTL be before we stop believing it? */
+#define MAX_DNS_TTL (3*60*60)
+/** How small can a TTL be before we stop believing it? */
+#define MIN_DNS_TTL (60)
/** How often do we rotate onion keys? */
#define MIN_ONION_KEY_LIFETIME (7*24*60*60)
@@ -652,6 +654,8 @@ struct connection_t {
char *address; /**< FQDN (or IP) of the guy on the other end.
* strdup into this, because free_connection frees it.
*/
+ uint32_t address_ttl; /**< TTL for address-to-addr mapping on exit
+ * connection. Exit connections only. */
char identity_digest[DIGEST_LEN]; /**< Hash of the public RSA key for
* the other side's signing key. */
char *nickname; /**< Nickname of OR on other side (if any). */
@@ -1927,6 +1931,7 @@ void dirserv_free_all(void);
void dns_init(void);
void dns_free_all(void);
+uint32_t dns_clip_ttl(uint32_t ttl);
int connection_dns_finished_flushing(connection_t *conn);
int connection_dns_reached_eof(connection_t *conn);
int connection_dns_process_inbuf(connection_t *conn);