mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2025-02-23 06:35:28 +01:00
Merge remote-tracking branch 'rl1987/test_dns_resolve_rebased'
This commit is contained in:
commit
e8675dc7fc
9 changed files with 434 additions and 106 deletions
3
changes/ticket16831_part1
Normal file
3
changes/ticket16831_part1
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
o Testing:
|
||||||
|
- Unit test dns_resolve(), dns_clip_ttl() and dns_get_expiry_ttl()
|
||||||
|
functions in dns.c. Implements a portion of ticket 16831.
|
|
@ -654,8 +654,8 @@ connection_free_(connection_t *conn)
|
||||||
|
|
||||||
/** Make sure <b>conn</b> isn't in any of the global conn lists; then free it.
|
/** Make sure <b>conn</b> isn't in any of the global conn lists; then free it.
|
||||||
*/
|
*/
|
||||||
void
|
MOCK_IMPL(void,
|
||||||
connection_free(connection_t *conn)
|
connection_free,(connection_t *conn))
|
||||||
{
|
{
|
||||||
if (!conn)
|
if (!conn)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -28,7 +28,7 @@ listener_connection_t *listener_connection_new(int type, int socket_family);
|
||||||
connection_t *connection_new(int type, int socket_family);
|
connection_t *connection_new(int type, int socket_family);
|
||||||
|
|
||||||
void connection_link_connections(connection_t *conn_a, connection_t *conn_b);
|
void connection_link_connections(connection_t *conn_a, connection_t *conn_b);
|
||||||
void connection_free(connection_t *conn);
|
MOCK_DECL(void,connection_free,(connection_t *conn));
|
||||||
void connection_free_all(void);
|
void connection_free_all(void);
|
||||||
void connection_about_to_close_connection(connection_t *conn);
|
void connection_about_to_close_connection(connection_t *conn);
|
||||||
void connection_close_immediate(connection_t *conn);
|
void connection_close_immediate(connection_t *conn);
|
||||||
|
|
116
src/or/dns.c
116
src/or/dns.c
|
@ -11,6 +11,8 @@
|
||||||
* be nonblocking.)
|
* be nonblocking.)
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
#define DNS_PRIVATE
|
||||||
|
|
||||||
#include "or.h"
|
#include "or.h"
|
||||||
#include "circuitlist.h"
|
#include "circuitlist.h"
|
||||||
#include "circuituse.h"
|
#include "circuituse.h"
|
||||||
|
@ -81,9 +83,6 @@ struct evdns_request;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Longest hostname we're willing to resolve. */
|
|
||||||
#define MAX_ADDRESSLEN 256
|
|
||||||
|
|
||||||
/** How long will we wait for an answer from the resolver before we decide
|
/** How long will we wait for an answer from the resolver before we decide
|
||||||
* that the resolver is wedged? */
|
* that the resolver is wedged? */
|
||||||
#define RESOLVE_MAX_TIMEOUT 300
|
#define RESOLVE_MAX_TIMEOUT 300
|
||||||
|
@ -102,104 +101,16 @@ static char *resolv_conf_fname = NULL;
|
||||||
* the nameservers? Used to check whether we need to reconfigure. */
|
* the nameservers? Used to check whether we need to reconfigure. */
|
||||||
static time_t resolv_conf_mtime = 0;
|
static time_t resolv_conf_mtime = 0;
|
||||||
|
|
||||||
/** Linked list of connections waiting for a DNS answer. */
|
|
||||||
typedef struct pending_connection_t {
|
|
||||||
edge_connection_t *conn;
|
|
||||||
struct pending_connection_t *next;
|
|
||||||
} pending_connection_t;
|
|
||||||
|
|
||||||
/** Value of 'magic' field for cached_resolve_t. Used to try to catch bad
|
|
||||||
* pointers and memory stomping. */
|
|
||||||
#define CACHED_RESOLVE_MAGIC 0x1234F00D
|
|
||||||
|
|
||||||
/* Possible states for a cached resolve_t */
|
|
||||||
/** We are waiting for the resolver system to tell us an answer here.
|
|
||||||
* When we get one, or when we time out, the state of this cached_resolve_t
|
|
||||||
* will become "DONE" and we'll possibly add a CACHED
|
|
||||||
* entry. This cached_resolve_t will be in the hash table so that we will
|
|
||||||
* know not to launch more requests for this addr, but rather to add more
|
|
||||||
* connections to the pending list for the addr. */
|
|
||||||
#define CACHE_STATE_PENDING 0
|
|
||||||
/** This used to be a pending cached_resolve_t, and we got an answer for it.
|
|
||||||
* Now we're waiting for this cached_resolve_t to expire. This should
|
|
||||||
* have no pending connections, and should not appear in the hash table. */
|
|
||||||
#define CACHE_STATE_DONE 1
|
|
||||||
/** We are caching an answer for this address. This should have no pending
|
|
||||||
* connections, and should appear in the hash table. */
|
|
||||||
#define CACHE_STATE_CACHED 2
|
|
||||||
|
|
||||||
/** @name status values for a single DNS request.
|
|
||||||
*
|
|
||||||
* @{ */
|
|
||||||
/** The DNS request is in progress. */
|
|
||||||
#define RES_STATUS_INFLIGHT 1
|
|
||||||
/** The DNS request finished and gave an answer */
|
|
||||||
#define RES_STATUS_DONE_OK 2
|
|
||||||
/** The DNS request finished and gave an error */
|
|
||||||
#define RES_STATUS_DONE_ERR 3
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/** A DNS request: possibly completed, possibly pending; cached_resolve
|
|
||||||
* structs are stored at the OR side in a hash table, and as a linked
|
|
||||||
* list from oldest to newest.
|
|
||||||
*/
|
|
||||||
typedef struct cached_resolve_t {
|
|
||||||
HT_ENTRY(cached_resolve_t) node;
|
|
||||||
uint32_t magic; /**< Must be CACHED_RESOLVE_MAGIC */
|
|
||||||
char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */
|
|
||||||
|
|
||||||
union {
|
|
||||||
uint32_t addr_ipv4; /**< IPv4 addr for <b>address</b>, if successful.
|
|
||||||
* (In host order.) */
|
|
||||||
int err_ipv4; /**< One of DNS_ERR_*, if IPv4 lookup failed. */
|
|
||||||
} result_ipv4; /**< Outcome of IPv4 lookup */
|
|
||||||
union {
|
|
||||||
struct in6_addr addr_ipv6; /**< IPv6 addr for <b>address</b>, if
|
|
||||||
* successful */
|
|
||||||
int err_ipv6; /**< One of DNS_ERR_*, if IPv6 lookup failed. */
|
|
||||||
} result_ipv6; /**< Outcome of IPv6 lookup, if any */
|
|
||||||
union {
|
|
||||||
char *hostname; /** A hostname, if PTR lookup happened successfully*/
|
|
||||||
int err_hostname; /** One of DNS_ERR_*, if PTR lookup failed. */
|
|
||||||
} result_ptr;
|
|
||||||
/** @name Status fields
|
|
||||||
*
|
|
||||||
* These take one of the RES_STATUS_* values, depending on the state
|
|
||||||
* of the corresponding lookup.
|
|
||||||
*
|
|
||||||
* @{ */
|
|
||||||
unsigned int res_status_ipv4 : 2;
|
|
||||||
unsigned int res_status_ipv6 : 2;
|
|
||||||
unsigned int res_status_hostname : 2;
|
|
||||||
/**@}*/
|
|
||||||
uint8_t state; /**< Is this cached entry pending/done/informative? */
|
|
||||||
|
|
||||||
time_t expire; /**< Remove items from cache after this time. */
|
|
||||||
uint32_t ttl_ipv4; /**< What TTL did the nameserver tell us? */
|
|
||||||
uint32_t ttl_ipv6; /**< What TTL did the nameserver tell us? */
|
|
||||||
uint32_t ttl_hostname; /**< What TTL did the nameserver tell us? */
|
|
||||||
/** Connections that want to know when we get an answer for this resolve. */
|
|
||||||
pending_connection_t *pending_connections;
|
|
||||||
/** Position of this element in the heap*/
|
|
||||||
int minheap_idx;
|
|
||||||
} cached_resolve_t;
|
|
||||||
|
|
||||||
static void purge_expired_resolves(time_t now);
|
static void purge_expired_resolves(time_t now);
|
||||||
static void dns_found_answer(const char *address, uint8_t query_type,
|
static void dns_found_answer(const char *address, uint8_t query_type,
|
||||||
int dns_answer,
|
int dns_answer,
|
||||||
const tor_addr_t *addr,
|
const tor_addr_t *addr,
|
||||||
const char *hostname,
|
const char *hostname,
|
||||||
uint32_t ttl);
|
uint32_t ttl);
|
||||||
static void send_resolved_cell(edge_connection_t *conn, uint8_t answer_type,
|
|
||||||
const cached_resolve_t *resolve);
|
|
||||||
static int launch_resolve(cached_resolve_t *resolve);
|
static int launch_resolve(cached_resolve_t *resolve);
|
||||||
static void add_wildcarded_test_address(const char *address);
|
static void add_wildcarded_test_address(const char *address);
|
||||||
static int configure_nameservers(int force);
|
static int configure_nameservers(int force);
|
||||||
static int answer_is_wildcarded(const char *ip);
|
static int answer_is_wildcarded(const char *ip);
|
||||||
static int dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
|
|
||||||
or_circuit_t *oncirc, char **resolved_to_hostname,
|
|
||||||
int *made_connection_pending_out,
|
|
||||||
cached_resolve_t **resolve_out);
|
|
||||||
static int set_exitconn_info_from_resolve(edge_connection_t *exitconn,
|
static int set_exitconn_info_from_resolve(edge_connection_t *exitconn,
|
||||||
const cached_resolve_t *resolve,
|
const cached_resolve_t *resolve,
|
||||||
char **hostname_out);
|
char **hostname_out);
|
||||||
|
@ -367,7 +278,7 @@ dns_clip_ttl(uint32_t ttl)
|
||||||
|
|
||||||
/** Helper: Given a TTL from a DNS response, determine how long to hold it in
|
/** Helper: Given a TTL from a DNS response, determine how long to hold it in
|
||||||
* our cache. */
|
* our cache. */
|
||||||
static uint32_t
|
STATIC uint32_t
|
||||||
dns_get_expiry_ttl(uint32_t ttl)
|
dns_get_expiry_ttl(uint32_t ttl)
|
||||||
{
|
{
|
||||||
if (ttl < MIN_DNS_TTL)
|
if (ttl < MIN_DNS_TTL)
|
||||||
|
@ -605,9 +516,9 @@ purge_expired_resolves(time_t now)
|
||||||
* answer back along circ; otherwise, send the answer back along
|
* answer back along circ; otherwise, send the answer back along
|
||||||
* <b>conn</b>'s attached circuit.
|
* <b>conn</b>'s attached circuit.
|
||||||
*/
|
*/
|
||||||
static void
|
MOCK_IMPL(STATIC void,
|
||||||
send_resolved_cell(edge_connection_t *conn, uint8_t answer_type,
|
send_resolved_cell,(edge_connection_t *conn, uint8_t answer_type,
|
||||||
const cached_resolve_t *resolved)
|
const cached_resolve_t *resolved))
|
||||||
{
|
{
|
||||||
char buf[RELAY_PAYLOAD_SIZE], *cp = buf;
|
char buf[RELAY_PAYLOAD_SIZE], *cp = buf;
|
||||||
size_t buflen = 0;
|
size_t buflen = 0;
|
||||||
|
@ -671,8 +582,9 @@ send_resolved_cell(edge_connection_t *conn, uint8_t answer_type,
|
||||||
* answer back along circ; otherwise, send the answer back along
|
* answer back along circ; otherwise, send the answer back along
|
||||||
* <b>conn</b>'s attached circuit.
|
* <b>conn</b>'s attached circuit.
|
||||||
*/
|
*/
|
||||||
static void
|
MOCK_IMPL(STATIC void,
|
||||||
send_resolved_hostname_cell(edge_connection_t *conn, const char *hostname)
|
send_resolved_hostname_cell,(edge_connection_t *conn,
|
||||||
|
const char *hostname))
|
||||||
{
|
{
|
||||||
char buf[RELAY_PAYLOAD_SIZE];
|
char buf[RELAY_PAYLOAD_SIZE];
|
||||||
size_t buflen;
|
size_t buflen;
|
||||||
|
@ -800,11 +712,11 @@ dns_resolve(edge_connection_t *exitconn)
|
||||||
*
|
*
|
||||||
* Set *<b>resolve_out</b> to a cached resolve, if we found one.
|
* Set *<b>resolve_out</b> to a cached resolve, if we found one.
|
||||||
*/
|
*/
|
||||||
static int
|
MOCK_IMPL(STATIC int,
|
||||||
dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
|
dns_resolve_impl,(edge_connection_t *exitconn, int is_resolve,
|
||||||
or_circuit_t *oncirc, char **hostname_out,
|
or_circuit_t *oncirc, char **hostname_out,
|
||||||
int *made_connection_pending_out,
|
int *made_connection_pending_out,
|
||||||
cached_resolve_t **resolve_out)
|
cached_resolve_t **resolve_out))
|
||||||
{
|
{
|
||||||
cached_resolve_t *resolve;
|
cached_resolve_t *resolve;
|
||||||
cached_resolve_t search;
|
cached_resolve_t search;
|
||||||
|
@ -1145,8 +1057,8 @@ connection_dns_remove(edge_connection_t *conn)
|
||||||
* the resolve for <b>address</b> itself, and remove any cached results for
|
* the resolve for <b>address</b> itself, and remove any cached results for
|
||||||
* <b>address</b> from the cache.
|
* <b>address</b> from the cache.
|
||||||
*/
|
*/
|
||||||
void
|
MOCK_IMPL(void,
|
||||||
dns_cancel_pending_resolve(const char *address)
|
dns_cancel_pending_resolve,(const char *address))
|
||||||
{
|
{
|
||||||
pending_connection_t *pend;
|
pending_connection_t *pend;
|
||||||
cached_resolve_t search;
|
cached_resolve_t search;
|
||||||
|
|
18
src/or/dns.h
18
src/or/dns.h
|
@ -20,7 +20,7 @@ int dns_reset(void);
|
||||||
void connection_dns_remove(edge_connection_t *conn);
|
void connection_dns_remove(edge_connection_t *conn);
|
||||||
void assert_connection_edge_not_dns_pending(edge_connection_t *conn);
|
void assert_connection_edge_not_dns_pending(edge_connection_t *conn);
|
||||||
void assert_all_pending_dns_resolves_ok(void);
|
void assert_all_pending_dns_resolves_ok(void);
|
||||||
void dns_cancel_pending_resolve(const char *question);
|
MOCK_DECL(void,dns_cancel_pending_resolve,(const char *question));
|
||||||
int dns_resolve(edge_connection_t *exitconn);
|
int dns_resolve(edge_connection_t *exitconn);
|
||||||
void dns_launch_correctness_checks(void);
|
void dns_launch_correctness_checks(void);
|
||||||
int dns_seems_to_be_broken(void);
|
int dns_seems_to_be_broken(void);
|
||||||
|
@ -28,5 +28,21 @@ int dns_seems_to_be_broken_for_ipv6(void);
|
||||||
void dns_reset_correctness_checks(void);
|
void dns_reset_correctness_checks(void);
|
||||||
void dump_dns_mem_usage(int severity);
|
void dump_dns_mem_usage(int severity);
|
||||||
|
|
||||||
|
#ifdef DNS_PRIVATE
|
||||||
|
#include "dns_structs.h"
|
||||||
|
|
||||||
|
STATIC uint32_t dns_get_expiry_ttl(uint32_t ttl);
|
||||||
|
|
||||||
|
MOCK_DECL(STATIC int,dns_resolve_impl,(edge_connection_t *exitconn,
|
||||||
|
int is_resolve,or_circuit_t *oncirc, char **hostname_out,
|
||||||
|
int *made_connection_pending_out, cached_resolve_t **resolve_out));
|
||||||
|
|
||||||
|
MOCK_DECL(STATIC void,send_resolved_cell,(edge_connection_t *conn,
|
||||||
|
uint8_t answer_type,const cached_resolve_t *resolved));
|
||||||
|
|
||||||
|
MOCK_DECL(STATIC void,send_resolved_hostname_cell,(edge_connection_t *conn,
|
||||||
|
const char *hostname));
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
90
src/or/dns_structs.h
Normal file
90
src/or/dns_structs.h
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
#ifndef TOR_DNS_STRUCTS_H
|
||||||
|
#define TOR_DNS_STRUCTS_H
|
||||||
|
|
||||||
|
/** Longest hostname we're willing to resolve. */
|
||||||
|
#define MAX_ADDRESSLEN 256
|
||||||
|
|
||||||
|
/** Linked list of connections waiting for a DNS answer. */
|
||||||
|
typedef struct pending_connection_t {
|
||||||
|
edge_connection_t *conn;
|
||||||
|
struct pending_connection_t *next;
|
||||||
|
} pending_connection_t;
|
||||||
|
|
||||||
|
/** Value of 'magic' field for cached_resolve_t. Used to try to catch bad
|
||||||
|
* pointers and memory stomping. */
|
||||||
|
#define CACHED_RESOLVE_MAGIC 0x1234F00D
|
||||||
|
|
||||||
|
/* Possible states for a cached resolve_t */
|
||||||
|
/** We are waiting for the resolver system to tell us an answer here.
|
||||||
|
* When we get one, or when we time out, the state of this cached_resolve_t
|
||||||
|
* will become "DONE" and we'll possibly add a CACHED
|
||||||
|
* entry. This cached_resolve_t will be in the hash table so that we will
|
||||||
|
* know not to launch more requests for this addr, but rather to add more
|
||||||
|
* connections to the pending list for the addr. */
|
||||||
|
#define CACHE_STATE_PENDING 0
|
||||||
|
/** This used to be a pending cached_resolve_t, and we got an answer for it.
|
||||||
|
* Now we're waiting for this cached_resolve_t to expire. This should
|
||||||
|
* have no pending connections, and should not appear in the hash table. */
|
||||||
|
#define CACHE_STATE_DONE 1
|
||||||
|
/** We are caching an answer for this address. This should have no pending
|
||||||
|
* connections, and should appear in the hash table. */
|
||||||
|
#define CACHE_STATE_CACHED 2
|
||||||
|
|
||||||
|
/** @name status values for a single DNS request.
|
||||||
|
*
|
||||||
|
* @{ */
|
||||||
|
/** The DNS request is in progress. */
|
||||||
|
#define RES_STATUS_INFLIGHT 1
|
||||||
|
/** The DNS request finished and gave an answer */
|
||||||
|
#define RES_STATUS_DONE_OK 2
|
||||||
|
/** The DNS request finished and gave an error */
|
||||||
|
#define RES_STATUS_DONE_ERR 3
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** A DNS request: possibly completed, possibly pending; cached_resolve
|
||||||
|
* structs are stored at the OR side in a hash table, and as a linked
|
||||||
|
* list from oldest to newest.
|
||||||
|
*/
|
||||||
|
typedef struct cached_resolve_t {
|
||||||
|
HT_ENTRY(cached_resolve_t) node;
|
||||||
|
uint32_t magic; /**< Must be CACHED_RESOLVE_MAGIC */
|
||||||
|
char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */
|
||||||
|
|
||||||
|
union {
|
||||||
|
uint32_t addr_ipv4; /**< IPv4 addr for <b>address</b>, if successful.
|
||||||
|
* (In host order.) */
|
||||||
|
int err_ipv4; /**< One of DNS_ERR_*, if IPv4 lookup failed. */
|
||||||
|
} result_ipv4; /**< Outcome of IPv4 lookup */
|
||||||
|
union {
|
||||||
|
struct in6_addr addr_ipv6; /**< IPv6 addr for <b>address</b>, if
|
||||||
|
* successful */
|
||||||
|
int err_ipv6; /**< One of DNS_ERR_*, if IPv6 lookup failed. */
|
||||||
|
} result_ipv6; /**< Outcome of IPv6 lookup, if any */
|
||||||
|
union {
|
||||||
|
char *hostname; /** A hostname, if PTR lookup happened successfully*/
|
||||||
|
int err_hostname; /** One of DNS_ERR_*, if PTR lookup failed. */
|
||||||
|
} result_ptr;
|
||||||
|
/** @name Status fields
|
||||||
|
*
|
||||||
|
* These take one of the RES_STATUS_* values, depending on the state
|
||||||
|
* of the corresponding lookup.
|
||||||
|
*
|
||||||
|
* @{ */
|
||||||
|
unsigned int res_status_ipv4 : 2;
|
||||||
|
unsigned int res_status_ipv6 : 2;
|
||||||
|
unsigned int res_status_hostname : 2;
|
||||||
|
/**@}*/
|
||||||
|
uint8_t state; /**< Is this cached entry pending/done/informative? */
|
||||||
|
|
||||||
|
time_t expire; /**< Remove items from cache after this time. */
|
||||||
|
uint32_t ttl_ipv4; /**< What TTL did the nameserver tell us? */
|
||||||
|
uint32_t ttl_ipv6; /**< What TTL did the nameserver tell us? */
|
||||||
|
uint32_t ttl_hostname; /**< What TTL did the nameserver tell us? */
|
||||||
|
/** Connections that want to know when we get an answer for this resolve. */
|
||||||
|
pending_connection_t *pending_connections;
|
||||||
|
/** Position of this element in the heap*/
|
||||||
|
int minheap_idx;
|
||||||
|
} cached_resolve_t;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -90,6 +90,7 @@ src_test_test_SOURCES = \
|
||||||
src/test/test_threads.c \
|
src/test/test_threads.c \
|
||||||
src/test/test_util.c \
|
src/test/test_util.c \
|
||||||
src/test/test_helpers.c \
|
src/test/test_helpers.c \
|
||||||
|
src/test/test_dns.c \
|
||||||
src/test/testing_common.c \
|
src/test/testing_common.c \
|
||||||
src/ext/tinytest.c
|
src/ext/tinytest.c
|
||||||
|
|
||||||
|
|
|
@ -1158,6 +1158,7 @@ extern struct testcase_t socks_tests[];
|
||||||
extern struct testcase_t status_tests[];
|
extern struct testcase_t status_tests[];
|
||||||
extern struct testcase_t thread_tests[];
|
extern struct testcase_t thread_tests[];
|
||||||
extern struct testcase_t util_tests[];
|
extern struct testcase_t util_tests[];
|
||||||
|
extern struct testcase_t dns_tests[];
|
||||||
|
|
||||||
struct testgroup_t testgroups[] = {
|
struct testgroup_t testgroups[] = {
|
||||||
{ "", test_array },
|
{ "", test_array },
|
||||||
|
@ -1204,6 +1205,7 @@ struct testgroup_t testgroups[] = {
|
||||||
{ "util/", util_tests },
|
{ "util/", util_tests },
|
||||||
{ "util/logging/", logging_tests },
|
{ "util/logging/", logging_tests },
|
||||||
{ "util/thread/", thread_tests },
|
{ "util/thread/", thread_tests },
|
||||||
|
{ "dns/", dns_tests },
|
||||||
END_OF_GROUPS
|
END_OF_GROUPS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
304
src/test/test_dns.c
Normal file
304
src/test/test_dns.c
Normal file
|
@ -0,0 +1,304 @@
|
||||||
|
#include "or.h"
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
#define DNS_PRIVATE
|
||||||
|
|
||||||
|
#include "dns.h"
|
||||||
|
#include "connection.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_dns_clip_ttl(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
uint32_t ttl_mid = MIN_DNS_TTL / 2 + MAX_DNS_TTL / 2;
|
||||||
|
|
||||||
|
tt_int_op(dns_clip_ttl(MIN_DNS_TTL - 1),==,MIN_DNS_TTL);
|
||||||
|
tt_int_op(dns_clip_ttl(ttl_mid),==,ttl_mid);
|
||||||
|
tt_int_op(dns_clip_ttl(MAX_DNS_TTL + 1),==,MAX_DNS_TTL);
|
||||||
|
|
||||||
|
done:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_dns_expiry_ttl(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
|
||||||
|
uint32_t ttl_mid = MIN_DNS_TTL / 2 + MAX_DNS_ENTRY_AGE / 2;
|
||||||
|
|
||||||
|
tt_int_op(dns_get_expiry_ttl(MIN_DNS_TTL - 1),==,MIN_DNS_TTL);
|
||||||
|
tt_int_op(dns_get_expiry_ttl(ttl_mid),==,ttl_mid);
|
||||||
|
tt_int_op(dns_get_expiry_ttl(MAX_DNS_ENTRY_AGE + 1),==,MAX_DNS_ENTRY_AGE);
|
||||||
|
|
||||||
|
done:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int resolve_retval = 0;
|
||||||
|
static int resolve_made_conn_pending = 0;
|
||||||
|
static char *resolved_name = NULL;
|
||||||
|
static cached_resolve_t *cache_entry = NULL;
|
||||||
|
|
||||||
|
static int n_fake_impl = 0;
|
||||||
|
|
||||||
|
/** This will be our configurable substitute for <b>dns_resolve_impl</b> in
|
||||||
|
* dns.c. It will return <b>resolve_retval</b>,
|
||||||
|
* and set <b>resolve_made_conn_pending</b> to
|
||||||
|
* <b>made_connection_pending_out</b>. It will set <b>hostname_out</b>
|
||||||
|
* to a duplicate of <b>resolved_name</b> and it will set <b>resolve_out</b>
|
||||||
|
* to <b>cache_entry</b>. Lastly, it will increment <b>n_fake_impl</b< by
|
||||||
|
* 1.
|
||||||
|
*/
|
||||||
|
STATIC int
|
||||||
|
dns_resolve_fake_impl(edge_connection_t *exitconn, int is_resolve,
|
||||||
|
or_circuit_t *oncirc, char **hostname_out,
|
||||||
|
int *made_connection_pending_out,
|
||||||
|
cached_resolve_t **resolve_out)
|
||||||
|
{
|
||||||
|
if (made_connection_pending_out)
|
||||||
|
*made_connection_pending_out = resolve_made_conn_pending;
|
||||||
|
|
||||||
|
if (hostname_out && resolved_name)
|
||||||
|
*hostname_out = tor_strdup(resolved_name);
|
||||||
|
|
||||||
|
if (resolve_out && cache_entry)
|
||||||
|
*resolve_out = cache_entry;
|
||||||
|
|
||||||
|
n_fake_impl++;
|
||||||
|
|
||||||
|
return resolve_retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static edge_connection_t *conn_for_resolved_cell = NULL;
|
||||||
|
|
||||||
|
static int n_send_resolved_cell_replacement = 0;
|
||||||
|
static uint8_t last_answer_type = 0;
|
||||||
|
static cached_resolve_t *last_resolved;
|
||||||
|
|
||||||
|
static void
|
||||||
|
send_resolved_cell_replacement(edge_connection_t *conn, uint8_t answer_type,
|
||||||
|
const cached_resolve_t *resolved)
|
||||||
|
{
|
||||||
|
conn_for_resolved_cell = conn;
|
||||||
|
|
||||||
|
last_answer_type = answer_type;
|
||||||
|
last_resolved = (cached_resolve_t *)resolved;
|
||||||
|
|
||||||
|
n_send_resolved_cell_replacement++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int n_send_resolved_hostname_cell_replacement = 0;
|
||||||
|
|
||||||
|
static char *last_resolved_hostname = NULL;
|
||||||
|
|
||||||
|
static void
|
||||||
|
send_resolved_hostname_cell_replacement(edge_connection_t *conn,
|
||||||
|
const char *hostname)
|
||||||
|
{
|
||||||
|
conn_for_resolved_cell = conn;
|
||||||
|
|
||||||
|
last_resolved_hostname = (char *)hostname;
|
||||||
|
|
||||||
|
n_send_resolved_hostname_cell_replacement++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int n_dns_cancel_pending_resolve_replacement = 0;
|
||||||
|
|
||||||
|
static void
|
||||||
|
dns_cancel_pending_resolve_replacement(const char *address)
|
||||||
|
{
|
||||||
|
n_dns_cancel_pending_resolve_replacement++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int n_connection_free = 0;
|
||||||
|
static connection_t *last_freed_conn = NULL;
|
||||||
|
|
||||||
|
static void
|
||||||
|
connection_free_replacement(connection_t *conn)
|
||||||
|
{
|
||||||
|
n_connection_free++;
|
||||||
|
|
||||||
|
last_freed_conn = conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_dns_resolve_outer(void *arg)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
int prev_n_send_resolved_hostname_cell_replacement;
|
||||||
|
int prev_n_send_resolved_cell_replacement;
|
||||||
|
int prev_n_connection_free;
|
||||||
|
cached_resolve_t *fake_resolved = tor_malloc(sizeof(cached_resolve_t));
|
||||||
|
edge_connection_t *exitconn = tor_malloc(sizeof(edge_connection_t));
|
||||||
|
edge_connection_t *nextconn = tor_malloc(sizeof(edge_connection_t));
|
||||||
|
|
||||||
|
or_circuit_t *on_circuit = tor_malloc(sizeof(or_circuit_t));
|
||||||
|
memset(on_circuit,0,sizeof(or_circuit_t));
|
||||||
|
on_circuit->base_.magic = OR_CIRCUIT_MAGIC;
|
||||||
|
|
||||||
|
memset(fake_resolved,0,sizeof(cached_resolve_t));
|
||||||
|
memset(exitconn,0,sizeof(edge_connection_t));
|
||||||
|
memset(nextconn,0,sizeof(edge_connection_t));
|
||||||
|
|
||||||
|
MOCK(dns_resolve_impl,dns_resolve_fake_impl);
|
||||||
|
MOCK(send_resolved_cell,send_resolved_cell_replacement);
|
||||||
|
MOCK(send_resolved_hostname_cell,send_resolved_hostname_cell_replacement);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CASE 1: dns_resolve_impl returns 1 and sets a hostname. purpose is
|
||||||
|
* EXIT_PURPOSE_RESOLVE.
|
||||||
|
*
|
||||||
|
* We want dns_resolve() to call send_resolved_hostname_cell() for a
|
||||||
|
* given exit connection (represented by edge_connection_t object)
|
||||||
|
* with a hostname it received from _impl.
|
||||||
|
*/
|
||||||
|
|
||||||
|
prev_n_send_resolved_hostname_cell_replacement =
|
||||||
|
n_send_resolved_hostname_cell_replacement;
|
||||||
|
|
||||||
|
exitconn->base_.purpose = EXIT_PURPOSE_RESOLVE;
|
||||||
|
exitconn->on_circuit = &(on_circuit->base_);
|
||||||
|
|
||||||
|
resolve_retval = 1;
|
||||||
|
resolved_name = tor_strdup("www.torproject.org");
|
||||||
|
|
||||||
|
retval = dns_resolve(exitconn);
|
||||||
|
|
||||||
|
tt_int_op(retval,==,1);
|
||||||
|
tt_str_op(resolved_name,==,last_resolved_hostname);
|
||||||
|
tt_assert(conn_for_resolved_cell == exitconn);
|
||||||
|
tt_int_op(n_send_resolved_hostname_cell_replacement,==,
|
||||||
|
prev_n_send_resolved_hostname_cell_replacement + 1);
|
||||||
|
tt_assert(exitconn->on_circuit == NULL);
|
||||||
|
|
||||||
|
last_resolved_hostname = NULL;
|
||||||
|
|
||||||
|
/* CASE 2: dns_resolve_impl returns 1, but does not set hostname.
|
||||||
|
* Instead, it yields cached_resolve_t object.
|
||||||
|
*
|
||||||
|
* We want dns_resolve to call send_resolved_cell on exitconn with
|
||||||
|
* RESOLVED_TYPE_AUTO and the cached_resolve_t object from _impl.
|
||||||
|
*/
|
||||||
|
|
||||||
|
tor_free(resolved_name);
|
||||||
|
resolved_name = NULL;
|
||||||
|
|
||||||
|
exitconn->on_circuit = &(on_circuit->base_);
|
||||||
|
|
||||||
|
cache_entry = fake_resolved;
|
||||||
|
|
||||||
|
prev_n_send_resolved_cell_replacement =
|
||||||
|
n_send_resolved_cell_replacement;
|
||||||
|
|
||||||
|
retval = dns_resolve(exitconn);
|
||||||
|
|
||||||
|
tt_int_op(retval,==,1);
|
||||||
|
tt_assert(conn_for_resolved_cell == exitconn);
|
||||||
|
tt_int_op(n_send_resolved_cell_replacement,==,
|
||||||
|
prev_n_send_resolved_cell_replacement + 1);
|
||||||
|
tt_assert(last_resolved == fake_resolved);
|
||||||
|
tt_int_op(last_answer_type,==,0xff);
|
||||||
|
tt_assert(exitconn->on_circuit == NULL);
|
||||||
|
|
||||||
|
/* CASE 3: The purpose of exit connection is not EXIT_PURPOSE_RESOLVE
|
||||||
|
* and _impl returns 1.
|
||||||
|
*
|
||||||
|
* We want dns_resolve to prepend exitconn to n_streams linked list.
|
||||||
|
* We don't want it to send any cells about hostname being resolved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
exitconn->base_.purpose = EXIT_PURPOSE_CONNECT;
|
||||||
|
exitconn->on_circuit = &(on_circuit->base_);
|
||||||
|
|
||||||
|
on_circuit->n_streams = nextconn;
|
||||||
|
|
||||||
|
prev_n_send_resolved_cell_replacement =
|
||||||
|
n_send_resolved_cell_replacement;
|
||||||
|
|
||||||
|
prev_n_send_resolved_hostname_cell_replacement =
|
||||||
|
n_send_resolved_hostname_cell_replacement;
|
||||||
|
|
||||||
|
retval = dns_resolve(exitconn);
|
||||||
|
|
||||||
|
tt_int_op(retval,==,1);
|
||||||
|
tt_assert(on_circuit->n_streams == exitconn);
|
||||||
|
tt_assert(exitconn->next_stream == nextconn);
|
||||||
|
tt_int_op(prev_n_send_resolved_cell_replacement,==,
|
||||||
|
n_send_resolved_cell_replacement);
|
||||||
|
tt_int_op(prev_n_send_resolved_hostname_cell_replacement,==,
|
||||||
|
n_send_resolved_hostname_cell_replacement);
|
||||||
|
|
||||||
|
/* CASE 4: _impl returns 0.
|
||||||
|
*
|
||||||
|
* We want dns_resolve() to set exitconn state to
|
||||||
|
* EXIT_CONN_STATE_RESOLVING and prepend exitconn to resolving_streams
|
||||||
|
* linked list.
|
||||||
|
*/
|
||||||
|
|
||||||
|
exitconn->on_circuit = &(on_circuit->base_);
|
||||||
|
|
||||||
|
resolve_retval = 0;
|
||||||
|
|
||||||
|
exitconn->next_stream = NULL;
|
||||||
|
on_circuit->resolving_streams = nextconn;
|
||||||
|
|
||||||
|
retval = dns_resolve(exitconn);
|
||||||
|
|
||||||
|
tt_int_op(retval,==,0);
|
||||||
|
tt_int_op(exitconn->base_.state,==,EXIT_CONN_STATE_RESOLVING);
|
||||||
|
tt_assert(on_circuit->resolving_streams == exitconn);
|
||||||
|
tt_assert(exitconn->next_stream == nextconn);
|
||||||
|
|
||||||
|
/* CASE 5: _impl returns -1 when purpose of exitconn is
|
||||||
|
* EXIT_PURPOSE_RESOLVE. We want dns_resolve to call send_resolved_cell
|
||||||
|
* on exitconn with type being RESOLVED_TYPE_ERROR.
|
||||||
|
*/
|
||||||
|
|
||||||
|
MOCK(dns_cancel_pending_resolve,dns_cancel_pending_resolve_replacement);
|
||||||
|
MOCK(connection_free,connection_free_replacement);
|
||||||
|
|
||||||
|
exitconn->on_circuit = &(on_circuit->base_);
|
||||||
|
exitconn->base_.purpose = EXIT_PURPOSE_RESOLVE;
|
||||||
|
|
||||||
|
resolve_retval = -1;
|
||||||
|
|
||||||
|
prev_n_send_resolved_cell_replacement =
|
||||||
|
n_send_resolved_cell_replacement;
|
||||||
|
|
||||||
|
prev_n_connection_free = n_connection_free;
|
||||||
|
|
||||||
|
retval = dns_resolve(exitconn);
|
||||||
|
|
||||||
|
tt_int_op(retval,==,-1);
|
||||||
|
tt_int_op(n_send_resolved_cell_replacement,==,
|
||||||
|
prev_n_send_resolved_cell_replacement + 1);
|
||||||
|
tt_int_op(last_answer_type,==,RESOLVED_TYPE_ERROR);
|
||||||
|
tt_int_op(n_dns_cancel_pending_resolve_replacement,==,1);
|
||||||
|
tt_int_op(n_connection_free,==,prev_n_connection_free + 1);
|
||||||
|
tt_assert(last_freed_conn == TO_CONN(exitconn));
|
||||||
|
|
||||||
|
done:
|
||||||
|
UNMOCK(dns_resolve_impl);
|
||||||
|
UNMOCK(send_resolved_cell);
|
||||||
|
UNMOCK(send_resolved_hostname_cell);
|
||||||
|
UNMOCK(dns_cancel_pending_resolve);
|
||||||
|
UNMOCK(connection_free);
|
||||||
|
tor_free(on_circuit);
|
||||||
|
tor_free(exitconn);
|
||||||
|
tor_free(nextconn);
|
||||||
|
tor_free(resolved_name);
|
||||||
|
tor_free(fake_resolved);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct testcase_t dns_tests[] = {
|
||||||
|
{ "clip_ttl", test_dns_clip_ttl, 0, NULL, NULL },
|
||||||
|
{ "expiry_ttl", test_dns_expiry_ttl, 0, NULL, NULL },
|
||||||
|
{ "resolve_outer", test_dns_resolve_outer, TT_FORK, NULL, NULL },
|
||||||
|
END_OF_TESTCASES
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef DNS_PRIVATE
|
||||||
|
|
Loading…
Add table
Reference in a new issue