Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
David Goulet
Tor
Commits
77a5ca90
Commit
77a5ca90
authored
Jul 22, 2015
by
rl1987
Browse files
Unit test dns_resolve(), dns_clip_ttl(), dns_get_expiry_ttl().
parent
494dea70
Changes
8
Hide whitespace changes
Inline
Side-by-side
src/or/connection.c
View file @
77a5ca90
...
...
@@ -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.
*/
void
connection_free
(
connection_t
*
conn
)
MOCK_IMPL
(
void
,
connection_free
,
(
connection_t
*
conn
)
)
{
if
(
!
conn
)
return
;
...
...
src/or/connection.h
View file @
77a5ca90
...
...
@@ -28,7 +28,7 @@ listener_connection_t *listener_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_free
(
connection_t
*
conn
);
MOCK_DECL
(
void
,
connection_free
,
(
connection_t
*
conn
)
)
;
void
connection_free_all
(
void
);
void
connection_about_to_close_connection
(
connection_t
*
conn
);
void
connection_close_immediate
(
connection_t
*
conn
);
...
...
src/or/dns.c
View file @
77a5ca90
...
...
@@ -11,6 +11,8 @@
* be nonblocking.)
**/
#define DNS_PRIVATE
#include
"or.h"
#include
"circuitlist.h"
#include
"circuituse.h"
...
...
@@ -81,9 +83,6 @@ struct evdns_request;
#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
* that the resolver is wedged? */
#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. */
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
dns_found_answer
(
const
char
*
address
,
uint8_t
query_type
,
int
dns_answer
,
const
tor_addr_t
*
addr
,
const
char
*
hostname
,
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
void
add_wildcarded_test_address
(
const
char
*
address
);
static
int
configure_nameservers
(
int
force
);
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
,
const
cached_resolve_t
*
resolve
,
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
* our cache. */
static
uint32_t
STATIC
uint32_t
dns_get_expiry_ttl
(
uint32_t
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
* <b>conn</b>'s attached circuit.
*/
static
void
send_resolved_cell
(
edge_connection_t
*
conn
,
uint8_t
answer_type
,
const
cached_resolve_t
*
resolved
)
MOCK_IMPL
(
STATIC
void
,
send_resolved_cell
,
(
edge_connection_t
*
conn
,
uint8_t
answer_type
,
const
cached_resolve_t
*
resolved
)
)
{
char
buf
[
RELAY_PAYLOAD_SIZE
],
*
cp
=
buf
;
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
* <b>conn</b>'s attached circuit.
*/
static
void
send_resolved_hostname_cell
(
edge_connection_t
*
conn
,
const
char
*
hostname
)
MOCK_IMPL
(
STATIC
void
,
send_resolved_hostname_cell
,(
edge_connection_t
*
conn
,
const
char
*
hostname
))
{
char
buf
[
RELAY_PAYLOAD_SIZE
];
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.
*/
static
int
dns_resolve_impl
(
edge_connection_t
*
exitconn
,
int
is_resolve
,
MOCK_IMPL
(
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
)
cached_resolve_t
**
resolve_out
)
)
{
cached_resolve_t
*
resolve
;
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
* <b>address</b> from the cache.
*/
void
dns_cancel_pending_resolve
(
const
char
*
address
)
MOCK_IMPL
(
void
,
dns_cancel_pending_resolve
,
(
const
char
*
address
)
)
{
pending_connection_t
*
pend
;
cached_resolve_t
search
;
...
...
src/or/dns.h
View file @
77a5ca90
...
...
@@ -20,7 +20,7 @@ int 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
(
const
char
*
question
);
MOCK_DECL
(
void
,
dns_cancel_pending_resolve
,
(
const
char
*
question
)
)
;
int
dns_resolve
(
edge_connection_t
*
exitconn
);
void
dns_launch_correctness_checks
(
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
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
src/or/dns_structs.h
0 → 100644
View file @
77a5ca90
#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
src/test/include.am
View file @
77a5ca90
...
...
@@ -90,6 +90,7 @@ src_test_test_SOURCES = \
src/test/test_threads.c \
src/test/test_util.c \
src/test/test_helpers.c \
src/test/test_dns.c \
src/test/testing_common.c \
src/ext/tinytest.c
...
...
src/test/test.c
View file @
77a5ca90
...
...
@@ -1158,6 +1158,7 @@ extern struct testcase_t socks_tests[];
extern
struct
testcase_t
status_tests
[];
extern
struct
testcase_t
thread_tests
[];
extern
struct
testcase_t
util_tests
[];
extern
struct
testcase_t
dns_tests
[];
struct
testgroup_t
testgroups
[]
=
{
{
""
,
test_array
},
...
...
@@ -1204,6 +1205,7 @@ struct testgroup_t testgroups[] = {
{
"util/"
,
util_tests
},
{
"util/logging/"
,
logging_tests
},
{
"util/thread/"
,
thread_tests
},
{
"dns/"
,
dns_tests
},
END_OF_GROUPS
};
src/test/test_dns.c
0 → 100644
View file @
77a5ca90
#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
);