test_microdesc.c 8.02 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/* Copyright (c) 2010, The Tor Project, Inc. */
/* See LICENSE for licensing information */

#include "orconfig.h"
#include "or.h"

#include "config.h"
#include "microdesc.h"

#include "test.h"

#ifdef MS_WINDOWS
/* For mkdir() */
#include <direct.h>
#else
#include <dirent.h>
#endif

static const char test_md1[] =
  "onion-key\n"
  "-----BEGIN RSA PUBLIC KEY-----\n"
  "MIGJAoGBAMjlHH/daN43cSVRaHBwgUfnszzAhg98EvivJ9Qxfv51mvQUxPjQ07es\n"
  "gV/3n8fyh3Kqr/ehi9jxkdgSRfSnmF7giaHL1SLZ29kA7KtST+pBvmTpDtHa3ykX\n"
  "Xorc7hJvIyTZoc1HU+5XSynj3gsBE5IGK1ZRzrNS688LnuZMVp1tAgMBAAE=\n"
  "-----END RSA PUBLIC KEY-----\n";

static const char test_md2[] =
  "onion-key\n"
  "-----BEGIN RSA PUBLIC KEY-----\n"
  "MIGJAoGBAMIixIowh2DyPmDNMDwBX2DHcYcqdcH1zdIQJZkyV6c6rQHnvbcaDoSg\n"
  "jgFSLJKpnGmh71FVRqep+yVB0zI1JY43kuEnXry2HbZCD9UDo3d3n7t015X5S7ON\n"
  "bSSYtQGPwOr6Epf96IF6DoQxy4iDnPUAlejuhAG51s1y6/rZQ3zxAgMBAAE=\n"
  "-----END RSA PUBLIC KEY-----\n";

static const char test_md3[] =
  "@last-listed 2009-06-22\n"
  "onion-key\n"
  "-----BEGIN RSA PUBLIC KEY-----\n"
  "MIGJAoGBAMH3340d4ENNGrqx7UxT+lB7x6DNUKOdPEOn4teceE11xlMyZ9TPv41c\n"
  "qj2fRZzfxlc88G/tmiaHshmdtEpklZ740OFqaaJVj4LjPMKFNE+J7Xc1142BE9Ci\n"
  "KgsbjGYe2RY261aADRWLetJ8T9QDMm+JngL4288hc8pq1uB/3TAbAgMBAAE=\n"
  "-----END RSA PUBLIC KEY-----\n"
  "p accept 1-700,800-1000\n"
  "family nodeX nodeY nodeZ\n";

static void
test_md_cache(void *data)
{
  or_options_t *options = NULL;
  microdesc_cache_t *mc = NULL ;
  smartlist_t *added = NULL, *wanted = NULL;
  microdesc_t *md1, *md2, *md3;
  char d1[DIGEST256_LEN], d2[DIGEST256_LEN], d3[DIGEST256_LEN];
  const char *test_md3_noannotation = strchr(test_md3, '\n')+1;
  time_t time1, time2, time3;
  char *fn = NULL, *s = NULL;
  (void)data;

  options = get_options();
  tt_assert(options);

  time1 = time(NULL);
  time2 = time(NULL) - 2*24*60*60;
  time3 = time(NULL) - 15*24*60*60;

  /* Possibly, turn this into a test setup/cleanup pair */
  tor_free(options->DataDirectory);
  options->DataDirectory = tor_strdup(get_fname("md_datadir_test"));
#ifdef MS_WINDOWS
  tt_int_op(0, ==, mkdir(options->DataDirectory));
#else
  tt_int_op(0, ==, mkdir(options->DataDirectory, 0700));
#endif

  tt_assert(!strcmpstart(test_md3_noannotation, "onion-key"));

  crypto_digest256(d1, test_md1, strlen(test_md1), DIGEST_SHA256);
  crypto_digest256(d2, test_md2, strlen(test_md1), DIGEST_SHA256);
  crypto_digest256(d3, test_md3_noannotation, strlen(test_md3_noannotation),
                   DIGEST_SHA256);

  mc = get_microdesc_cache();

  added = microdescs_add_to_cache(mc, test_md1, NULL, SAVED_NOWHERE, 0,
                                  time1, NULL);
  tt_int_op(1, ==, smartlist_len(added));
  md1 = smartlist_get(added, 0);
  smartlist_free(added);
  added = NULL;

  wanted = smartlist_create();
  added = microdescs_add_to_cache(mc, test_md2, NULL, SAVED_NOWHERE, 0,
                                  time2, wanted);
  /* Should fail, since we didn't list test_md2's digest in wanted */
  tt_int_op(0, ==, smartlist_len(added));
  smartlist_free(added);
  added = NULL;

  smartlist_add(wanted, tor_memdup(d2, DIGEST256_LEN));
  smartlist_add(wanted, tor_memdup(d3, DIGEST256_LEN));
  added = microdescs_add_to_cache(mc, test_md2, NULL, SAVED_NOWHERE, 0,
                                  time2, wanted);
  /* Now it can work. md2 should have been added */
  tt_int_op(1, ==, smartlist_len(added));
  md2 = smartlist_get(added, 0);
  /* And it should have gotten removed from 'wanted' */
  tt_int_op(smartlist_len(wanted), ==, 1);
  test_mem_op(smartlist_get(wanted, 0), ==, d3, DIGEST256_LEN);
  smartlist_free(added);
  added = NULL;

  added = microdescs_add_to_cache(mc, test_md3, NULL,
                                  SAVED_NOWHERE, 0, -1, NULL);
  /* Must fail, since SAVED_NOWHERE precludes annotations */
  tt_int_op(0, ==, smartlist_len(added));
  smartlist_free(added);
  added = NULL;

  added = microdescs_add_to_cache(mc, test_md3_noannotation, NULL,
                                  SAVED_NOWHERE, 0, time3, NULL);
  /* Now it can work */
  tt_int_op(1, ==, smartlist_len(added));
  md3 = smartlist_get(added, 0);
  smartlist_free(added);
  added = NULL;

  /* Okay.  We added 1...3.  Let's poke them to see how they look, and make
   * sure they're really in the journal. */
  tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1));
  tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2));
  tt_ptr_op(md3, ==, microdesc_cache_lookup_by_digest256(mc, d3));

  tt_int_op(md1->last_listed, ==, time1);
  tt_int_op(md2->last_listed, ==, time2);
  tt_int_op(md3->last_listed, ==, time3);

  tt_int_op(md1->saved_location, ==, SAVED_IN_JOURNAL);
  tt_int_op(md2->saved_location, ==, SAVED_IN_JOURNAL);
  tt_int_op(md3->saved_location, ==, SAVED_IN_JOURNAL);

  tt_int_op(md1->bodylen, ==, strlen(test_md1));
  tt_int_op(md2->bodylen, ==, strlen(test_md2));
  tt_int_op(md3->bodylen, ==, strlen(test_md3_noannotation));
  test_mem_op(md1->body, ==, test_md1, strlen(test_md1));
  test_mem_op(md2->body, ==, test_md2, strlen(test_md2));
  test_mem_op(md3->body, ==, test_md3_noannotation,
              strlen(test_md3_noannotation));

  tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs.new",
               options->DataDirectory);
  s = read_file_to_str(fn, RFTS_BIN, NULL);
  tt_assert(s);
  test_mem_op(md1->body, ==, s + md1->off, md1->bodylen);
  test_mem_op(md2->body, ==, s + md2->off, md2->bodylen);
  test_mem_op(md3->body, ==, s + md3->off, md3->bodylen);

  tt_ptr_op(md1->family, ==, NULL);
  tt_ptr_op(md3->family, !=, NULL);
  tt_int_op(smartlist_len(md3->family), ==, 3);
  tt_str_op(smartlist_get(md3->family, 0), ==, "nodeX");

  /* Now rebuild the cache! */
  tt_int_op(microdesc_cache_rebuild(mc), ==, 0);

  tt_int_op(md1->saved_location, ==, SAVED_IN_CACHE);
  tt_int_op(md2->saved_location, ==, SAVED_IN_CACHE);
  tt_int_op(md3->saved_location, ==, SAVED_IN_CACHE);

  /* The journal should be empty now */
  tor_free(s);
  s = read_file_to_str(fn, RFTS_BIN, NULL);
  tt_str_op(s, ==, "");
  tor_free(s);
  tor_free(fn);

  /* read the cache. */
  tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs",
               options->DataDirectory);
  s = read_file_to_str(fn, RFTS_BIN, NULL);
  test_mem_op(md1->body, ==, s + md1->off, strlen(test_md1));
  test_mem_op(md2->body, ==, s + md2->off, strlen(test_md2));
  test_mem_op(md3->body, ==, s + md3->off, strlen(test_md3_noannotation));

  /* Okay, now we are going to forget about the cache entirely, and reload it
   * from the disk. */
  microdesc_free_all();
  mc = get_microdesc_cache();
  md1 = microdesc_cache_lookup_by_digest256(mc, d1);
  md2 = microdesc_cache_lookup_by_digest256(mc, d2);
  md3 = microdesc_cache_lookup_by_digest256(mc, d3);
  test_assert(md1);
  test_assert(md2);
  test_assert(md3);
  test_mem_op(md1->body, ==, s + md1->off, strlen(test_md1));
  test_mem_op(md2->body, ==, s + md2->off, strlen(test_md2));
  test_mem_op(md3->body, ==, s + md3->off, strlen(test_md3_noannotation));

  tt_int_op(md1->last_listed, ==, time1);
  tt_int_op(md2->last_listed, ==, time2);
  tt_int_op(md3->last_listed, ==, time3);

  /* Okay, now we are going to clear out everything older than a week old.
   * In practice, that means md3 */
  microdesc_cache_clean(mc, time(NULL)-7*24*60*60, 1/*force*/);
  tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1));
  tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2));
  tt_ptr_op(NULL, ==, microdesc_cache_lookup_by_digest256(mc, d3));
  md3 = NULL; /* it's history now! */

  /* rebuild again, make sure it stays gone. */
  microdesc_cache_rebuild(mc);
  tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1));
  tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2));
  tt_ptr_op(NULL, ==, microdesc_cache_lookup_by_digest256(mc, d3));

 done:
  if (options)
    tor_free(options->DataDirectory);
  microdesc_free_all();

  smartlist_free(added);
  if (wanted)
    SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp));
  smartlist_free(wanted);
  tor_free(s);
  tor_free(fn);
}

struct testcase_t microdesc_tests[] = {
  { "cache", test_md_cache, TT_FORK, NULL, NULL },
  END_OF_TESTCASES
};