1 /* Copyright (c) 2015-2018, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 #include "core/or/or.h"
9 #include "feature/relay/dns.h"
10 #include "core/mainloop/connection.h"
11 #include "core/or/connection_edge.h"
12 #include "feature/relay/router.h"
14 #include "core/or/edge_connection_st.h"
15 #include "core/or/or_circuit_st.h"
19 #define NS_SUBMODULE clip_ttl
22 NS(test_main
)(void *arg
)
26 uint32_t ttl_mid
= MIN_DNS_TTL_AT_EXIT
/ 2 + MAX_DNS_TTL_AT_EXIT
/ 2;
28 tt_int_op(dns_clip_ttl(MIN_DNS_TTL_AT_EXIT
- 1),OP_EQ
,MIN_DNS_TTL_AT_EXIT
);
29 tt_int_op(dns_clip_ttl(ttl_mid
),OP_EQ
,MAX_DNS_TTL_AT_EXIT
);
30 tt_int_op(dns_clip_ttl(MAX_DNS_TTL_AT_EXIT
+ 1),OP_EQ
,MAX_DNS_TTL_AT_EXIT
);
38 #define NS_SUBMODULE resolve
40 static int resolve_retval
= 0;
41 static int resolve_made_conn_pending
= 0;
42 static char *resolved_name
= NULL
;
43 static cached_resolve_t
*cache_entry_mock
= NULL
;
45 static int n_fake_impl
= 0;
47 NS_DECL(int, dns_resolve_impl
, (edge_connection_t
*exitconn
, int is_resolve
,
48 or_circuit_t
*oncirc
, char **hostname_out
,
49 int *made_connection_pending_out
,
50 cached_resolve_t
**resolve_out
));
52 /** This will be our configurable substitute for <b>dns_resolve_impl</b> in
53 * dns.c. It will return <b>resolve_retval</b>,
54 * and set <b>resolve_made_conn_pending</b> to
55 * <b>made_connection_pending_out</b>. It will set <b>hostname_out</b>
56 * to a duplicate of <b>resolved_name</b> and it will set <b>resolve_out</b>
57 * to <b>cache_entry</b>. Lastly, it will increment <b>n_fake_impl</b< by
61 NS(dns_resolve_impl
)(edge_connection_t
*exitconn
, int is_resolve
,
62 or_circuit_t
*oncirc
, char **hostname_out
,
63 int *made_connection_pending_out
,
64 cached_resolve_t
**resolve_out
)
70 if (made_connection_pending_out
)
71 *made_connection_pending_out
= resolve_made_conn_pending
;
73 if (hostname_out
&& resolved_name
)
74 *hostname_out
= tor_strdup(resolved_name
);
76 if (resolve_out
&& cache_entry_mock
)
77 *resolve_out
= cache_entry_mock
;
81 return resolve_retval
;
84 static edge_connection_t
*conn_for_resolved_cell
= NULL
;
86 static int n_send_resolved_cell_replacement
= 0;
87 static uint8_t last_answer_type
= 0;
88 static cached_resolve_t
*last_resolved
;
91 NS(send_resolved_cell
)(edge_connection_t
*conn
, uint8_t answer_type
,
92 const cached_resolve_t
*resolved
)
94 conn_for_resolved_cell
= conn
;
96 last_answer_type
= answer_type
;
97 last_resolved
= (cached_resolve_t
*)resolved
;
99 n_send_resolved_cell_replacement
++;
102 static int n_send_resolved_hostname_cell_replacement
= 0;
104 static char *last_resolved_hostname
= NULL
;
107 NS(send_resolved_hostname_cell
)(edge_connection_t
*conn
,
108 const char *hostname
)
110 conn_for_resolved_cell
= conn
;
112 tor_free(last_resolved_hostname
);
113 last_resolved_hostname
= tor_strdup(hostname
);
115 n_send_resolved_hostname_cell_replacement
++;
118 static int n_dns_cancel_pending_resolve_replacement
= 0;
121 NS(dns_cancel_pending_resolve
)(const char *address
)
124 n_dns_cancel_pending_resolve_replacement
++;
127 static int n_connection_free
= 0;
128 static connection_t
*last_freed_conn
= NULL
;
131 NS(connection_free_
)(connection_t
*conn
)
135 last_freed_conn
= conn
;
139 NS(test_main
)(void *arg
)
143 int prev_n_send_resolved_hostname_cell_replacement
;
144 int prev_n_send_resolved_cell_replacement
;
145 int prev_n_connection_free
;
146 cached_resolve_t
*fake_resolved
= tor_malloc(sizeof(cached_resolve_t
));
147 edge_connection_t
*exitconn
= tor_malloc(sizeof(edge_connection_t
));
148 edge_connection_t
*nextconn
= tor_malloc(sizeof(edge_connection_t
));
150 or_circuit_t
*on_circuit
= tor_malloc(sizeof(or_circuit_t
));
151 memset(on_circuit
,0,sizeof(or_circuit_t
));
152 on_circuit
->base_
.magic
= OR_CIRCUIT_MAGIC
;
154 memset(fake_resolved
,0,sizeof(cached_resolve_t
));
155 memset(exitconn
,0,sizeof(edge_connection_t
));
156 memset(nextconn
,0,sizeof(edge_connection_t
));
158 NS_MOCK(dns_resolve_impl
);
159 NS_MOCK(send_resolved_cell
);
160 NS_MOCK(send_resolved_hostname_cell
);
163 * CASE 1: dns_resolve_impl returns 1 and sets a hostname. purpose is
164 * EXIT_PURPOSE_RESOLVE.
166 * We want dns_resolve() to call send_resolved_hostname_cell() for a
167 * given exit connection (represented by edge_connection_t object)
168 * with a hostname it received from _impl.
171 prev_n_send_resolved_hostname_cell_replacement
=
172 n_send_resolved_hostname_cell_replacement
;
174 exitconn
->base_
.purpose
= EXIT_PURPOSE_RESOLVE
;
175 exitconn
->on_circuit
= &(on_circuit
->base_
);
178 resolved_name
= tor_strdup("www.torproject.org");
180 retval
= dns_resolve(exitconn
);
182 tt_int_op(retval
,OP_EQ
,1);
183 tt_str_op(resolved_name
,OP_EQ
,last_resolved_hostname
);
184 tt_assert(conn_for_resolved_cell
== exitconn
);
185 tt_int_op(n_send_resolved_hostname_cell_replacement
,OP_EQ
,
186 prev_n_send_resolved_hostname_cell_replacement
+ 1);
187 tt_assert(exitconn
->on_circuit
== NULL
);
189 tor_free(last_resolved_hostname
);
190 // implies last_resolved_hostname = NULL;
192 /* CASE 2: dns_resolve_impl returns 1, but does not set hostname.
193 * Instead, it yields cached_resolve_t object.
195 * We want dns_resolve to call send_resolved_cell on exitconn with
196 * RESOLVED_TYPE_AUTO and the cached_resolve_t object from _impl.
199 tor_free(resolved_name
);
200 resolved_name
= NULL
;
202 exitconn
->on_circuit
= &(on_circuit
->base_
);
204 cache_entry_mock
= fake_resolved
;
206 prev_n_send_resolved_cell_replacement
=
207 n_send_resolved_cell_replacement
;
209 retval
= dns_resolve(exitconn
);
211 tt_int_op(retval
,OP_EQ
,1);
212 tt_assert(conn_for_resolved_cell
== exitconn
);
213 tt_int_op(n_send_resolved_cell_replacement
,OP_EQ
,
214 prev_n_send_resolved_cell_replacement
+ 1);
215 tt_assert(last_resolved
== fake_resolved
);
216 tt_int_op(last_answer_type
,OP_EQ
,0xff);
217 tt_assert(exitconn
->on_circuit
== NULL
);
219 /* CASE 3: The purpose of exit connection is not EXIT_PURPOSE_RESOLVE
220 * and _impl returns 1.
222 * We want dns_resolve to prepend exitconn to n_streams linked list.
223 * We don't want it to send any cells about hostname being resolved.
226 exitconn
->base_
.purpose
= EXIT_PURPOSE_CONNECT
;
227 exitconn
->on_circuit
= &(on_circuit
->base_
);
229 on_circuit
->n_streams
= nextconn
;
231 prev_n_send_resolved_cell_replacement
=
232 n_send_resolved_cell_replacement
;
234 prev_n_send_resolved_hostname_cell_replacement
=
235 n_send_resolved_hostname_cell_replacement
;
237 retval
= dns_resolve(exitconn
);
239 tt_int_op(retval
,OP_EQ
,1);
240 tt_assert(on_circuit
->n_streams
== exitconn
);
241 tt_assert(exitconn
->next_stream
== nextconn
);
242 tt_int_op(prev_n_send_resolved_cell_replacement
,OP_EQ
,
243 n_send_resolved_cell_replacement
);
244 tt_int_op(prev_n_send_resolved_hostname_cell_replacement
,OP_EQ
,
245 n_send_resolved_hostname_cell_replacement
);
247 /* CASE 4: _impl returns 0.
249 * We want dns_resolve() to set exitconn state to
250 * EXIT_CONN_STATE_RESOLVING and prepend exitconn to resolving_streams
254 exitconn
->on_circuit
= &(on_circuit
->base_
);
258 exitconn
->next_stream
= NULL
;
259 on_circuit
->resolving_streams
= nextconn
;
261 retval
= dns_resolve(exitconn
);
263 tt_int_op(retval
,OP_EQ
,0);
264 tt_int_op(exitconn
->base_
.state
,OP_EQ
,EXIT_CONN_STATE_RESOLVING
);
265 tt_assert(on_circuit
->resolving_streams
== exitconn
);
266 tt_assert(exitconn
->next_stream
== nextconn
);
268 /* CASE 5: _impl returns -1 when purpose of exitconn is
269 * EXIT_PURPOSE_RESOLVE. We want dns_resolve to call send_resolved_cell
270 * on exitconn with type being RESOLVED_TYPE_ERROR.
273 NS_MOCK(dns_cancel_pending_resolve
);
274 NS_MOCK(connection_free_
);
276 exitconn
->on_circuit
= &(on_circuit
->base_
);
277 exitconn
->base_
.purpose
= EXIT_PURPOSE_RESOLVE
;
281 prev_n_send_resolved_cell_replacement
=
282 n_send_resolved_cell_replacement
;
284 prev_n_connection_free
= n_connection_free
;
286 retval
= dns_resolve(exitconn
);
288 tt_int_op(retval
,OP_EQ
,-1);
289 tt_int_op(n_send_resolved_cell_replacement
,OP_EQ
,
290 prev_n_send_resolved_cell_replacement
+ 1);
291 tt_int_op(last_answer_type
,OP_EQ
,RESOLVED_TYPE_ERROR
);
292 tt_int_op(n_dns_cancel_pending_resolve_replacement
,OP_EQ
,1);
293 tt_int_op(n_connection_free
,OP_EQ
,prev_n_connection_free
+ 1);
294 tt_assert(last_freed_conn
== TO_CONN(exitconn
));
297 NS_UNMOCK(dns_resolve_impl
);
298 NS_UNMOCK(send_resolved_cell
);
299 NS_UNMOCK(send_resolved_hostname_cell
);
300 NS_UNMOCK(dns_cancel_pending_resolve
);
301 NS_UNMOCK(connection_free_
);
302 tor_free(on_circuit
);
305 tor_free(resolved_name
);
306 tor_free(fake_resolved
);
307 tor_free(last_resolved_hostname
);
313 /** Create an <b>edge_connection_t</b> instance that is considered a
314 * valid exit connection by asserts in dns_resolve_impl.
316 static edge_connection_t
*
317 create_valid_exitconn(void)
319 edge_connection_t
*exitconn
= tor_malloc_zero(sizeof(edge_connection_t
));
320 TO_CONN(exitconn
)->type
= CONN_TYPE_EXIT
;
321 TO_CONN(exitconn
)->magic
= EDGE_CONNECTION_MAGIC
;
322 TO_CONN(exitconn
)->purpose
= EXIT_PURPOSE_RESOLVE
;
323 TO_CONN(exitconn
)->state
= EXIT_CONN_STATE_RESOLVING
;
324 exitconn
->base_
.s
= TOR_INVALID_SOCKET
;
329 #define NS_SUBMODULE ASPECT(resolve_impl, addr_is_ip_no_need_to_resolve)
332 * Given that <b>exitconn->base_.address</b> is IP address string, we
333 * want dns_resolve_impl() to parse it and store in
334 * <b>exitconn->base_.addr</b>. We expect dns_resolve_impl to return 1.
335 * Lastly, we want it to set the TTL value to default one for DNS queries.
339 NS(test_main
)(void *arg
)
343 const tor_addr_t
*resolved_addr
;
344 tor_addr_t addr_to_compare
;
348 tor_addr_parse(&addr_to_compare
, "8.8.8.8");
350 or_circuit_t
*on_circ
= tor_malloc_zero(sizeof(or_circuit_t
));
352 edge_connection_t
*exitconn
= create_valid_exitconn();
354 TO_CONN(exitconn
)->address
= tor_strdup("8.8.8.8");
356 retval
= dns_resolve_impl(exitconn
, 1, on_circ
, NULL
, &made_pending
,
359 resolved_addr
= &(exitconn
->base_
.addr
);
361 tt_int_op(retval
,OP_EQ
,1);
362 tt_assert(tor_addr_eq(resolved_addr
, (const tor_addr_t
*)&addr_to_compare
));
363 tt_int_op(exitconn
->address_ttl
,OP_EQ
,DEFAULT_DNS_TTL
);
367 tor_free(TO_CONN(exitconn
)->address
);
374 #define NS_SUBMODULE ASPECT(resolve_impl, non_exit)
376 /** Given that Tor instance is not configured as an exit node, we want
377 * dns_resolve_impl() to fail with return value -1.
380 NS(router_my_exit_policy_is_reject_star
)(void)
386 NS(test_main
)(void *arg
)
391 edge_connection_t
*exitconn
= create_valid_exitconn();
392 or_circuit_t
*on_circ
= tor_malloc_zero(sizeof(or_circuit_t
));
396 TO_CONN(exitconn
)->address
= tor_strdup("torproject.org");
398 NS_MOCK(router_my_exit_policy_is_reject_star
);
400 retval
= dns_resolve_impl(exitconn
, 1, on_circ
, NULL
, &made_pending
,
403 tt_int_op(retval
,OP_EQ
,-1);
406 tor_free(TO_CONN(exitconn
)->address
);
409 NS_UNMOCK(router_my_exit_policy_is_reject_star
);
415 #define NS_SUBMODULE ASPECT(resolve_impl, addr_is_invalid_dest)
417 /** Given that address is not a valid destination (as judged by
418 * address_is_invalid_destination() function), we want dns_resolve_impl()
419 * function to fail with return value -1.
423 NS(router_my_exit_policy_is_reject_star
)(void)
429 NS(test_main
)(void *arg
)
434 edge_connection_t
*exitconn
= create_valid_exitconn();
435 or_circuit_t
*on_circ
= tor_malloc_zero(sizeof(or_circuit_t
));
439 NS_MOCK(router_my_exit_policy_is_reject_star
);
441 TO_CONN(exitconn
)->address
= tor_strdup("invalid#@!.org");
443 retval
= dns_resolve_impl(exitconn
, 1, on_circ
, NULL
, &made_pending
,
446 tt_int_op(retval
,OP_EQ
,-1);
449 NS_UNMOCK(router_my_exit_policy_is_reject_star
);
450 tor_free(TO_CONN(exitconn
)->address
);
458 #define NS_SUBMODULE ASPECT(resolve_impl, malformed_ptr)
460 /** Given that address is a malformed PTR name, we want dns_resolve_impl to
465 NS(router_my_exit_policy_is_reject_star
)(void)
471 NS(test_main
)(void *arg
)
476 edge_connection_t
*exitconn
= create_valid_exitconn();
477 or_circuit_t
*on_circ
= tor_malloc_zero(sizeof(or_circuit_t
));
481 TO_CONN(exitconn
)->address
= tor_strdup("1.0.0.127.in-addr.arpa");
483 NS_MOCK(router_my_exit_policy_is_reject_star
);
485 retval
= dns_resolve_impl(exitconn
, 1, on_circ
, NULL
, &made_pending
,
488 tt_int_op(retval
,OP_EQ
,-1);
490 tor_free(TO_CONN(exitconn
)->address
);
492 TO_CONN(exitconn
)->address
=
493 tor_strdup("z01234567890123456789.in-addr.arpa");
495 retval
= dns_resolve_impl(exitconn
, 1, on_circ
, NULL
, &made_pending
,
498 tt_int_op(retval
,OP_EQ
,-1);
501 NS_UNMOCK(router_my_exit_policy_is_reject_star
);
502 tor_free(TO_CONN(exitconn
)->address
);
510 #define NS_SUBMODULE ASPECT(resolve_impl, cache_hit_pending)
512 /* Given that there is already a pending resolve for the given address,
513 * we want dns_resolve_impl to append our exit connection to list
514 * of pending connections for the pending DNS request and return 0.
518 NS(router_my_exit_policy_is_reject_star
)(void)
524 NS(test_main
)(void *arg
)
527 int made_pending
= 0;
529 pending_connection_t
*pending_conn
= NULL
;
531 edge_connection_t
*exitconn
= create_valid_exitconn();
532 or_circuit_t
*on_circ
= tor_malloc_zero(sizeof(or_circuit_t
));
534 cached_resolve_t
*cache_entry
= tor_malloc_zero(sizeof(cached_resolve_t
));
535 cache_entry
->magic
= CACHED_RESOLVE_MAGIC
;
536 cache_entry
->state
= CACHE_STATE_PENDING
;
537 cache_entry
->minheap_idx
= -1;
538 cache_entry
->expire
= time(NULL
) + 60 * 60;
542 TO_CONN(exitconn
)->address
= tor_strdup("torproject.org");
544 strlcpy(cache_entry
->address
, TO_CONN(exitconn
)->address
,
545 sizeof(cache_entry
->address
));
547 NS_MOCK(router_my_exit_policy_is_reject_star
);
551 dns_insert_cache_entry(cache_entry
);
553 retval
= dns_resolve_impl(exitconn
, 1, on_circ
, NULL
, &made_pending
,
556 tt_int_op(retval
,OP_EQ
,0);
557 tt_int_op(made_pending
,OP_EQ
,1);
559 pending_conn
= cache_entry
->pending_connections
;
561 tt_assert(pending_conn
!= NULL
);
562 tt_assert(pending_conn
->conn
== exitconn
);
565 NS_UNMOCK(router_my_exit_policy_is_reject_star
);
567 tor_free(TO_CONN(exitconn
)->address
);
568 tor_free(cache_entry
->pending_connections
);
569 tor_free(cache_entry
);
576 #define NS_SUBMODULE ASPECT(resolve_impl, cache_hit_cached)
578 /* Given that a finished DNS resolve is available in our cache, we want
579 * dns_resolve_impl() return it to called via resolve_out and pass the
580 * handling to set_exitconn_info_from_resolve function.
583 NS(router_my_exit_policy_is_reject_star
)(void)
588 static edge_connection_t
*last_exitconn
= NULL
;
589 static cached_resolve_t
*last_resolve
= NULL
;
592 NS(set_exitconn_info_from_resolve
)(edge_connection_t
*exitconn
,
593 const cached_resolve_t
*resolve
,
596 last_exitconn
= exitconn
;
597 last_resolve
= (cached_resolve_t
*)resolve
;
605 NS(test_main
)(void *arg
)
608 int made_pending
= 0;
610 edge_connection_t
*exitconn
= create_valid_exitconn();
611 or_circuit_t
*on_circ
= tor_malloc_zero(sizeof(or_circuit_t
));
613 cached_resolve_t
*resolve_out
= NULL
;
615 cached_resolve_t
*cache_entry
= tor_malloc_zero(sizeof(cached_resolve_t
));
616 cache_entry
->magic
= CACHED_RESOLVE_MAGIC
;
617 cache_entry
->state
= CACHE_STATE_CACHED
;
618 cache_entry
->minheap_idx
= -1;
619 cache_entry
->expire
= time(NULL
) + 60 * 60;
623 TO_CONN(exitconn
)->address
= tor_strdup("torproject.org");
625 strlcpy(cache_entry
->address
, TO_CONN(exitconn
)->address
,
626 sizeof(cache_entry
->address
));
628 NS_MOCK(router_my_exit_policy_is_reject_star
);
629 NS_MOCK(set_exitconn_info_from_resolve
);
633 dns_insert_cache_entry(cache_entry
);
635 retval
= dns_resolve_impl(exitconn
, 1, on_circ
, NULL
, &made_pending
,
638 tt_int_op(retval
,OP_EQ
,0);
639 tt_int_op(made_pending
,OP_EQ
,0);
640 tt_assert(resolve_out
== cache_entry
);
642 tt_assert(last_exitconn
== exitconn
);
643 tt_assert(last_resolve
== cache_entry
);
646 NS_UNMOCK(router_my_exit_policy_is_reject_star
);
647 NS_UNMOCK(set_exitconn_info_from_resolve
);
649 tor_free(TO_CONN(exitconn
)->address
);
650 tor_free(cache_entry
->pending_connections
);
651 tor_free(cache_entry
);
657 #define NS_SUBMODULE ASPECT(resolve_impl, cache_miss)
659 /* Given that there are neither pending nor pre-cached resolve for a given
660 * address, we want dns_resolve_impl() to create a new cached_resolve_t
661 * object, mark it as pending, insert it into the cache, attach the exit
662 * connection to list of pending connections and call launch_resolve()
663 * with the cached_resolve_t object it created.
666 NS(router_my_exit_policy_is_reject_star
)(void)
671 static cached_resolve_t
*last_launched_resolve
= NULL
;
674 NS(launch_resolve
)(cached_resolve_t
*resolve
)
676 last_launched_resolve
= resolve
;
682 NS(test_main
)(void *arg
)
685 int made_pending
= 0;
687 pending_connection_t
*pending_conn
= NULL
;
689 edge_connection_t
*exitconn
= create_valid_exitconn();
690 or_circuit_t
*on_circ
= tor_malloc_zero(sizeof(or_circuit_t
));
692 cached_resolve_t
*cache_entry
= NULL
;
693 cached_resolve_t query
;
697 TO_CONN(exitconn
)->address
= tor_strdup("torproject.org");
699 strlcpy(query
.address
, TO_CONN(exitconn
)->address
, sizeof(query
.address
));
701 NS_MOCK(router_my_exit_policy_is_reject_star
);
702 NS_MOCK(launch_resolve
);
706 retval
= dns_resolve_impl(exitconn
, 1, on_circ
, NULL
, &made_pending
,
709 tt_int_op(retval
,OP_EQ
,0);
710 tt_int_op(made_pending
,OP_EQ
,1);
712 cache_entry
= dns_get_cache_entry(&query
);
714 tt_assert(cache_entry
);
716 pending_conn
= cache_entry
->pending_connections
;
718 tt_assert(pending_conn
!= NULL
);
719 tt_assert(pending_conn
->conn
== exitconn
);
721 tt_assert(last_launched_resolve
== cache_entry
);
722 tt_str_op(cache_entry
->address
,OP_EQ
,TO_CONN(exitconn
)->address
);
725 NS_UNMOCK(router_my_exit_policy_is_reject_star
);
726 NS_UNMOCK(launch_resolve
);
728 tor_free(TO_CONN(exitconn
)->address
);
730 tor_free(cache_entry
->pending_connections
);
731 tor_free(cache_entry
);
738 struct testcase_t dns_tests
[] = {
741 TEST_CASE_ASPECT(resolve_impl
, addr_is_ip_no_need_to_resolve
),
742 TEST_CASE_ASPECT(resolve_impl
, non_exit
),
743 TEST_CASE_ASPECT(resolve_impl
, addr_is_invalid_dest
),
744 TEST_CASE_ASPECT(resolve_impl
, malformed_ptr
),
745 TEST_CASE_ASPECT(resolve_impl
, cache_hit_pending
),
746 TEST_CASE_ASPECT(resolve_impl
, cache_hit_cached
),
747 TEST_CASE_ASPECT(resolve_impl
, cache_miss
),