Merge remote-tracking branch 'tor-github/pr/392' into maint-0.2.9
[tor.git] / src / test / test_dns.c
blob6a8e92cb47e9566e40313b4aa7257e5da0448d42
1 #include "or.h"
2 #include "test.h"
4 #define DNS_PRIVATE
6 #include "dns.h"
7 #include "connection.h"
8 #include "router.h"
10 #define NS_MODULE dns
12 #define NS_SUBMODULE clip_ttl
14 static void
15 NS(test_main)(void *arg)
17 (void)arg;
19 uint32_t ttl_mid = MIN_DNS_TTL_AT_EXIT / 2 + MAX_DNS_TTL_AT_EXIT / 2;
21 tt_int_op(dns_clip_ttl(MIN_DNS_TTL_AT_EXIT - 1),==,MIN_DNS_TTL_AT_EXIT);
22 tt_int_op(dns_clip_ttl(ttl_mid),==,MAX_DNS_TTL_AT_EXIT);
23 tt_int_op(dns_clip_ttl(MAX_DNS_TTL_AT_EXIT + 1),==,MAX_DNS_TTL_AT_EXIT);
25 done:
26 return;
29 #undef NS_SUBMODULE
31 #define NS_SUBMODULE resolve
33 static int resolve_retval = 0;
34 static int resolve_made_conn_pending = 0;
35 static char *resolved_name = NULL;
36 static cached_resolve_t *cache_entry_mock = NULL;
38 static int n_fake_impl = 0;
40 NS_DECL(int, dns_resolve_impl, (edge_connection_t *exitconn, int is_resolve,
41 or_circuit_t *oncirc, char **hostname_out,
42 int *made_connection_pending_out,
43 cached_resolve_t **resolve_out));
45 /** This will be our configurable substitute for <b>dns_resolve_impl</b> in
46 * dns.c. It will return <b>resolve_retval</b>,
47 * and set <b>resolve_made_conn_pending</b> to
48 * <b>made_connection_pending_out</b>. It will set <b>hostname_out</b>
49 * to a duplicate of <b>resolved_name</b> and it will set <b>resolve_out</b>
50 * to <b>cache_entry</b>. Lastly, it will increment <b>n_fake_impl</b< by
51 * 1.
53 static int
54 NS(dns_resolve_impl)(edge_connection_t *exitconn, int is_resolve,
55 or_circuit_t *oncirc, char **hostname_out,
56 int *made_connection_pending_out,
57 cached_resolve_t **resolve_out)
59 (void)oncirc;
60 (void)exitconn;
61 (void)is_resolve;
63 if (made_connection_pending_out)
64 *made_connection_pending_out = resolve_made_conn_pending;
66 if (hostname_out && resolved_name)
67 *hostname_out = tor_strdup(resolved_name);
69 if (resolve_out && cache_entry_mock)
70 *resolve_out = cache_entry_mock;
72 n_fake_impl++;
74 return resolve_retval;
77 static edge_connection_t *conn_for_resolved_cell = NULL;
79 static int n_send_resolved_cell_replacement = 0;
80 static uint8_t last_answer_type = 0;
81 static cached_resolve_t *last_resolved;
83 static void
84 NS(send_resolved_cell)(edge_connection_t *conn, uint8_t answer_type,
85 const cached_resolve_t *resolved)
87 conn_for_resolved_cell = conn;
89 last_answer_type = answer_type;
90 last_resolved = (cached_resolve_t *)resolved;
92 n_send_resolved_cell_replacement++;
95 static int n_send_resolved_hostname_cell_replacement = 0;
97 static char *last_resolved_hostname = NULL;
99 static void
100 NS(send_resolved_hostname_cell)(edge_connection_t *conn,
101 const char *hostname)
103 conn_for_resolved_cell = conn;
105 tor_free(last_resolved_hostname);
106 last_resolved_hostname = tor_strdup(hostname);
108 n_send_resolved_hostname_cell_replacement++;
111 static int n_dns_cancel_pending_resolve_replacement = 0;
113 static void
114 NS(dns_cancel_pending_resolve)(const char *address)
116 (void) address;
117 n_dns_cancel_pending_resolve_replacement++;
120 static int n_connection_free = 0;
121 static connection_t *last_freed_conn = NULL;
123 static void
124 NS(connection_free)(connection_t *conn)
126 n_connection_free++;
128 last_freed_conn = conn;
131 static void
132 NS(test_main)(void *arg)
134 (void) arg;
135 int retval;
136 int prev_n_send_resolved_hostname_cell_replacement;
137 int prev_n_send_resolved_cell_replacement;
138 int prev_n_connection_free;
139 cached_resolve_t *fake_resolved = tor_malloc(sizeof(cached_resolve_t));
140 edge_connection_t *exitconn = tor_malloc(sizeof(edge_connection_t));
141 edge_connection_t *nextconn = tor_malloc(sizeof(edge_connection_t));
143 or_circuit_t *on_circuit = tor_malloc(sizeof(or_circuit_t));
144 memset(on_circuit,0,sizeof(or_circuit_t));
145 on_circuit->base_.magic = OR_CIRCUIT_MAGIC;
147 memset(fake_resolved,0,sizeof(cached_resolve_t));
148 memset(exitconn,0,sizeof(edge_connection_t));
149 memset(nextconn,0,sizeof(edge_connection_t));
151 NS_MOCK(dns_resolve_impl);
152 NS_MOCK(send_resolved_cell);
153 NS_MOCK(send_resolved_hostname_cell);
156 * CASE 1: dns_resolve_impl returns 1 and sets a hostname. purpose is
157 * EXIT_PURPOSE_RESOLVE.
159 * We want dns_resolve() to call send_resolved_hostname_cell() for a
160 * given exit connection (represented by edge_connection_t object)
161 * with a hostname it received from _impl.
164 prev_n_send_resolved_hostname_cell_replacement =
165 n_send_resolved_hostname_cell_replacement;
167 exitconn->base_.purpose = EXIT_PURPOSE_RESOLVE;
168 exitconn->on_circuit = &(on_circuit->base_);
170 resolve_retval = 1;
171 resolved_name = tor_strdup("www.torproject.org");
173 retval = dns_resolve(exitconn);
175 tt_int_op(retval,==,1);
176 tt_str_op(resolved_name,==,last_resolved_hostname);
177 tt_assert(conn_for_resolved_cell == exitconn);
178 tt_int_op(n_send_resolved_hostname_cell_replacement,==,
179 prev_n_send_resolved_hostname_cell_replacement + 1);
180 tt_assert(exitconn->on_circuit == NULL);
182 tor_free(last_resolved_hostname);
183 // implies last_resolved_hostname = NULL;
185 /* CASE 2: dns_resolve_impl returns 1, but does not set hostname.
186 * Instead, it yields cached_resolve_t object.
188 * We want dns_resolve to call send_resolved_cell on exitconn with
189 * RESOLVED_TYPE_AUTO and the cached_resolve_t object from _impl.
192 tor_free(resolved_name);
193 resolved_name = NULL;
195 exitconn->on_circuit = &(on_circuit->base_);
197 cache_entry_mock = fake_resolved;
199 prev_n_send_resolved_cell_replacement =
200 n_send_resolved_cell_replacement;
202 retval = dns_resolve(exitconn);
204 tt_int_op(retval,==,1);
205 tt_assert(conn_for_resolved_cell == exitconn);
206 tt_int_op(n_send_resolved_cell_replacement,==,
207 prev_n_send_resolved_cell_replacement + 1);
208 tt_assert(last_resolved == fake_resolved);
209 tt_int_op(last_answer_type,==,0xff);
210 tt_assert(exitconn->on_circuit == NULL);
212 /* CASE 3: The purpose of exit connection is not EXIT_PURPOSE_RESOLVE
213 * and _impl returns 1.
215 * We want dns_resolve to prepend exitconn to n_streams linked list.
216 * We don't want it to send any cells about hostname being resolved.
219 exitconn->base_.purpose = EXIT_PURPOSE_CONNECT;
220 exitconn->on_circuit = &(on_circuit->base_);
222 on_circuit->n_streams = nextconn;
224 prev_n_send_resolved_cell_replacement =
225 n_send_resolved_cell_replacement;
227 prev_n_send_resolved_hostname_cell_replacement =
228 n_send_resolved_hostname_cell_replacement;
230 retval = dns_resolve(exitconn);
232 tt_int_op(retval,==,1);
233 tt_assert(on_circuit->n_streams == exitconn);
234 tt_assert(exitconn->next_stream == nextconn);
235 tt_int_op(prev_n_send_resolved_cell_replacement,==,
236 n_send_resolved_cell_replacement);
237 tt_int_op(prev_n_send_resolved_hostname_cell_replacement,==,
238 n_send_resolved_hostname_cell_replacement);
240 /* CASE 4: _impl returns 0.
242 * We want dns_resolve() to set exitconn state to
243 * EXIT_CONN_STATE_RESOLVING and prepend exitconn to resolving_streams
244 * linked list.
247 exitconn->on_circuit = &(on_circuit->base_);
249 resolve_retval = 0;
251 exitconn->next_stream = NULL;
252 on_circuit->resolving_streams = nextconn;
254 retval = dns_resolve(exitconn);
256 tt_int_op(retval,==,0);
257 tt_int_op(exitconn->base_.state,==,EXIT_CONN_STATE_RESOLVING);
258 tt_assert(on_circuit->resolving_streams == exitconn);
259 tt_assert(exitconn->next_stream == nextconn);
261 /* CASE 5: _impl returns -1 when purpose of exitconn is
262 * EXIT_PURPOSE_RESOLVE. We want dns_resolve to call send_resolved_cell
263 * on exitconn with type being RESOLVED_TYPE_ERROR.
266 NS_MOCK(dns_cancel_pending_resolve);
267 NS_MOCK(connection_free);
269 exitconn->on_circuit = &(on_circuit->base_);
270 exitconn->base_.purpose = EXIT_PURPOSE_RESOLVE;
272 resolve_retval = -1;
274 prev_n_send_resolved_cell_replacement =
275 n_send_resolved_cell_replacement;
277 prev_n_connection_free = n_connection_free;
279 retval = dns_resolve(exitconn);
281 tt_int_op(retval,==,-1);
282 tt_int_op(n_send_resolved_cell_replacement,==,
283 prev_n_send_resolved_cell_replacement + 1);
284 tt_int_op(last_answer_type,==,RESOLVED_TYPE_ERROR);
285 tt_int_op(n_dns_cancel_pending_resolve_replacement,==,1);
286 tt_int_op(n_connection_free,==,prev_n_connection_free + 1);
287 tt_assert(last_freed_conn == TO_CONN(exitconn));
289 done:
290 NS_UNMOCK(dns_resolve_impl);
291 NS_UNMOCK(send_resolved_cell);
292 NS_UNMOCK(send_resolved_hostname_cell);
293 NS_UNMOCK(dns_cancel_pending_resolve);
294 NS_UNMOCK(connection_free);
295 tor_free(on_circuit);
296 tor_free(exitconn);
297 tor_free(nextconn);
298 tor_free(resolved_name);
299 tor_free(fake_resolved);
300 tor_free(last_resolved_hostname);
301 return;
304 #undef NS_SUBMODULE
306 /** Create an <b>edge_connection_t</b> instance that is considered a
307 * valid exit connection by asserts in dns_resolve_impl.
309 static edge_connection_t *
310 create_valid_exitconn(void)
312 edge_connection_t *exitconn = tor_malloc_zero(sizeof(edge_connection_t));
313 TO_CONN(exitconn)->type = CONN_TYPE_EXIT;
314 TO_CONN(exitconn)->magic = EDGE_CONNECTION_MAGIC;
315 TO_CONN(exitconn)->purpose = EXIT_PURPOSE_RESOLVE;
316 TO_CONN(exitconn)->state = EXIT_CONN_STATE_RESOLVING;
317 exitconn->base_.s = TOR_INVALID_SOCKET;
319 return exitconn;
322 #define NS_SUBMODULE ASPECT(resolve_impl, addr_is_ip_no_need_to_resolve)
325 * Given that <b>exitconn->base_.address</b> is IP address string, we
326 * want dns_resolve_impl() to parse it and store in
327 * <b>exitconn->base_.addr</b>. We expect dns_resolve_impl to return 1.
328 * Lastly, we want it to set the TTL value to default one for DNS queries.
331 static void
332 NS(test_main)(void *arg)
334 int retval;
335 int made_pending;
336 const tor_addr_t *resolved_addr;
337 tor_addr_t addr_to_compare;
339 (void)arg;
341 tor_addr_parse(&addr_to_compare, "8.8.8.8");
343 or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
345 edge_connection_t *exitconn = create_valid_exitconn();
347 TO_CONN(exitconn)->address = tor_strdup("8.8.8.8");
349 retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
350 NULL);
352 resolved_addr = &(exitconn->base_.addr);
354 tt_int_op(retval,==,1);
355 tt_assert(tor_addr_eq(resolved_addr, (const tor_addr_t *)&addr_to_compare));
356 tt_int_op(exitconn->address_ttl,==,DEFAULT_DNS_TTL);
358 done:
359 tor_free(on_circ);
360 tor_free(TO_CONN(exitconn)->address);
361 tor_free(exitconn);
362 return;
365 #undef NS_SUBMODULE
367 #define NS_SUBMODULE ASPECT(resolve_impl, non_exit)
369 /** Given that Tor instance is not configured as an exit node, we want
370 * dns_resolve_impl() to fail with return value -1.
372 static int
373 NS(router_my_exit_policy_is_reject_star)(void)
375 return 1;
378 static void
379 NS(test_main)(void *arg)
381 int retval;
382 int made_pending;
384 edge_connection_t *exitconn = create_valid_exitconn();
385 or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
387 (void)arg;
389 TO_CONN(exitconn)->address = tor_strdup("torproject.org");
391 NS_MOCK(router_my_exit_policy_is_reject_star);
393 retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
394 NULL);
396 tt_int_op(retval,==,-1);
398 done:
399 tor_free(TO_CONN(exitconn)->address);
400 tor_free(exitconn);
401 tor_free(on_circ);
402 NS_UNMOCK(router_my_exit_policy_is_reject_star);
403 return;
406 #undef NS_SUBMODULE
408 #define NS_SUBMODULE ASPECT(resolve_impl, addr_is_invalid_dest)
410 /** Given that address is not a valid destination (as judged by
411 * address_is_invalid_destination() function), we want dns_resolve_impl()
412 * function to fail with return value -1.
415 static int
416 NS(router_my_exit_policy_is_reject_star)(void)
418 return 0;
421 static void
422 NS(test_main)(void *arg)
424 int retval;
425 int made_pending;
427 edge_connection_t *exitconn = create_valid_exitconn();
428 or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
430 (void)arg;
432 NS_MOCK(router_my_exit_policy_is_reject_star);
434 TO_CONN(exitconn)->address = tor_strdup("invalid#@!.org");
436 retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
437 NULL);
439 tt_int_op(retval,==,-1);
441 done:
442 NS_UNMOCK(router_my_exit_policy_is_reject_star);
443 tor_free(TO_CONN(exitconn)->address);
444 tor_free(exitconn);
445 tor_free(on_circ);
446 return;
449 #undef NS_SUBMODULE
451 #define NS_SUBMODULE ASPECT(resolve_impl, malformed_ptr)
453 /** Given that address is a malformed PTR name, we want dns_resolve_impl to
454 * fail.
457 static int
458 NS(router_my_exit_policy_is_reject_star)(void)
460 return 0;
463 static void
464 NS(test_main)(void *arg)
466 int retval;
467 int made_pending;
469 edge_connection_t *exitconn = create_valid_exitconn();
470 or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
472 (void)arg;
474 TO_CONN(exitconn)->address = tor_strdup("1.0.0.127.in-addr.arpa");
476 NS_MOCK(router_my_exit_policy_is_reject_star);
478 retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
479 NULL);
481 tt_int_op(retval,==,-1);
483 tor_free(TO_CONN(exitconn)->address);
485 TO_CONN(exitconn)->address =
486 tor_strdup("z01234567890123456789.in-addr.arpa");
488 retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
489 NULL);
491 tt_int_op(retval,==,-1);
493 done:
494 NS_UNMOCK(router_my_exit_policy_is_reject_star);
495 tor_free(TO_CONN(exitconn)->address);
496 tor_free(exitconn);
497 tor_free(on_circ);
498 return;
501 #undef NS_SUBMODULE
503 #define NS_SUBMODULE ASPECT(resolve_impl, cache_hit_pending)
505 /* Given that there is already a pending resolve for the given address,
506 * we want dns_resolve_impl to append our exit connection to list
507 * of pending connections for the pending DNS request and return 0.
510 static int
511 NS(router_my_exit_policy_is_reject_star)(void)
513 return 0;
516 static void
517 NS(test_main)(void *arg)
519 int retval;
520 int made_pending = 0;
522 pending_connection_t *pending_conn = NULL;
524 edge_connection_t *exitconn = create_valid_exitconn();
525 or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
527 cached_resolve_t *cache_entry = tor_malloc_zero(sizeof(cached_resolve_t));
528 cache_entry->magic = CACHED_RESOLVE_MAGIC;
529 cache_entry->state = CACHE_STATE_PENDING;
530 cache_entry->minheap_idx = -1;
531 cache_entry->expire = time(NULL) + 60 * 60;
533 (void)arg;
535 TO_CONN(exitconn)->address = tor_strdup("torproject.org");
537 strlcpy(cache_entry->address, TO_CONN(exitconn)->address,
538 sizeof(cache_entry->address));
540 NS_MOCK(router_my_exit_policy_is_reject_star);
542 dns_init();
544 dns_insert_cache_entry(cache_entry);
546 retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
547 NULL);
549 tt_int_op(retval,==,0);
550 tt_int_op(made_pending,==,1);
552 pending_conn = cache_entry->pending_connections;
554 tt_assert(pending_conn != NULL);
555 tt_assert(pending_conn->conn == exitconn);
557 done:
558 NS_UNMOCK(router_my_exit_policy_is_reject_star);
559 tor_free(on_circ);
560 tor_free(TO_CONN(exitconn)->address);
561 tor_free(cache_entry->pending_connections);
562 tor_free(cache_entry);
563 tor_free(exitconn);
564 return;
567 #undef NS_SUBMODULE
569 #define NS_SUBMODULE ASPECT(resolve_impl, cache_hit_cached)
571 /* Given that a finished DNS resolve is available in our cache, we want
572 * dns_resolve_impl() return it to called via resolve_out and pass the
573 * handling to set_exitconn_info_from_resolve function.
575 static int
576 NS(router_my_exit_policy_is_reject_star)(void)
578 return 0;
581 static edge_connection_t *last_exitconn = NULL;
582 static cached_resolve_t *last_resolve = NULL;
584 static int
585 NS(set_exitconn_info_from_resolve)(edge_connection_t *exitconn,
586 const cached_resolve_t *resolve,
587 char **hostname_out)
589 last_exitconn = exitconn;
590 last_resolve = (cached_resolve_t *)resolve;
592 (void)hostname_out;
594 return 0;
597 static void
598 NS(test_main)(void *arg)
600 int retval;
601 int made_pending = 0;
603 edge_connection_t *exitconn = create_valid_exitconn();
604 or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
606 cached_resolve_t *resolve_out = NULL;
608 cached_resolve_t *cache_entry = tor_malloc_zero(sizeof(cached_resolve_t));
609 cache_entry->magic = CACHED_RESOLVE_MAGIC;
610 cache_entry->state = CACHE_STATE_CACHED;
611 cache_entry->minheap_idx = -1;
612 cache_entry->expire = time(NULL) + 60 * 60;
614 (void)arg;
616 TO_CONN(exitconn)->address = tor_strdup("torproject.org");
618 strlcpy(cache_entry->address, TO_CONN(exitconn)->address,
619 sizeof(cache_entry->address));
621 NS_MOCK(router_my_exit_policy_is_reject_star);
622 NS_MOCK(set_exitconn_info_from_resolve);
624 dns_init();
626 dns_insert_cache_entry(cache_entry);
628 retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
629 &resolve_out);
631 tt_int_op(retval,==,0);
632 tt_int_op(made_pending,==,0);
633 tt_assert(resolve_out == cache_entry);
635 tt_assert(last_exitconn == exitconn);
636 tt_assert(last_resolve == cache_entry);
638 done:
639 NS_UNMOCK(router_my_exit_policy_is_reject_star);
640 NS_UNMOCK(set_exitconn_info_from_resolve);
641 tor_free(on_circ);
642 tor_free(TO_CONN(exitconn)->address);
643 tor_free(cache_entry->pending_connections);
644 tor_free(cache_entry);
645 return;
648 #undef NS_SUBMODULE
650 #define NS_SUBMODULE ASPECT(resolve_impl, cache_miss)
652 /* Given that there are neither pending nor pre-cached resolve for a given
653 * address, we want dns_resolve_impl() to create a new cached_resolve_t
654 * object, mark it as pending, insert it into the cache, attach the exit
655 * connection to list of pending connections and call launch_resolve()
656 * with the cached_resolve_t object it created.
658 static int
659 NS(router_my_exit_policy_is_reject_star)(void)
661 return 0;
664 static cached_resolve_t *last_launched_resolve = NULL;
666 static int
667 NS(launch_resolve)(cached_resolve_t *resolve)
669 last_launched_resolve = resolve;
671 return 0;
674 static void
675 NS(test_main)(void *arg)
677 int retval;
678 int made_pending = 0;
680 pending_connection_t *pending_conn = NULL;
682 edge_connection_t *exitconn = create_valid_exitconn();
683 or_circuit_t *on_circ = tor_malloc_zero(sizeof(or_circuit_t));
685 cached_resolve_t *cache_entry = NULL;
686 cached_resolve_t query;
688 (void)arg;
690 TO_CONN(exitconn)->address = tor_strdup("torproject.org");
692 strlcpy(query.address, TO_CONN(exitconn)->address, sizeof(query.address));
694 NS_MOCK(router_my_exit_policy_is_reject_star);
695 NS_MOCK(launch_resolve);
697 dns_init();
699 retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
700 NULL);
702 tt_int_op(retval,==,0);
703 tt_int_op(made_pending,==,1);
705 cache_entry = dns_get_cache_entry(&query);
707 tt_assert(cache_entry);
709 pending_conn = cache_entry->pending_connections;
711 tt_assert(pending_conn != NULL);
712 tt_assert(pending_conn->conn == exitconn);
714 tt_assert(last_launched_resolve == cache_entry);
715 tt_str_op(cache_entry->address,==,TO_CONN(exitconn)->address);
717 done:
718 NS_UNMOCK(router_my_exit_policy_is_reject_star);
719 NS_UNMOCK(launch_resolve);
720 tor_free(on_circ);
721 tor_free(TO_CONN(exitconn)->address);
722 if (cache_entry)
723 tor_free(cache_entry->pending_connections);
724 tor_free(cache_entry);
725 tor_free(exitconn);
726 return;
729 #undef NS_SUBMODULE
731 struct testcase_t dns_tests[] = {
732 TEST_CASE(clip_ttl),
733 TEST_CASE(resolve),
734 TEST_CASE_ASPECT(resolve_impl, addr_is_ip_no_need_to_resolve),
735 TEST_CASE_ASPECT(resolve_impl, non_exit),
736 TEST_CASE_ASPECT(resolve_impl, addr_is_invalid_dest),
737 TEST_CASE_ASPECT(resolve_impl, malformed_ptr),
738 TEST_CASE_ASPECT(resolve_impl, cache_hit_pending),
739 TEST_CASE_ASPECT(resolve_impl, cache_hit_cached),
740 TEST_CASE_ASPECT(resolve_impl, cache_miss),
741 END_OF_TESTCASES
744 #undef NS_MODULE