test_microdesc.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /* Copyright (c) 2010-2013, The Tor Project, Inc. */
  2. /* See LICENSE for licensing information */
  3. #include "orconfig.h"
  4. #include "or.h"
  5. #include "config.h"
  6. #include "microdesc.h"
  7. #include "test.h"
  8. #ifdef _WIN32
  9. /* For mkdir() */
  10. #include <direct.h>
  11. #else
  12. #include <dirent.h>
  13. #endif
  14. static const char test_md1[] =
  15. "onion-key\n"
  16. "-----BEGIN RSA PUBLIC KEY-----\n"
  17. "MIGJAoGBAMjlHH/daN43cSVRaHBwgUfnszzAhg98EvivJ9Qxfv51mvQUxPjQ07es\n"
  18. "gV/3n8fyh3Kqr/ehi9jxkdgSRfSnmF7giaHL1SLZ29kA7KtST+pBvmTpDtHa3ykX\n"
  19. "Xorc7hJvIyTZoc1HU+5XSynj3gsBE5IGK1ZRzrNS688LnuZMVp1tAgMBAAE=\n"
  20. "-----END RSA PUBLIC KEY-----\n";
  21. static const char test_md2[] =
  22. "onion-key\n"
  23. "-----BEGIN RSA PUBLIC KEY-----\n"
  24. "MIGJAoGBAMIixIowh2DyPmDNMDwBX2DHcYcqdcH1zdIQJZkyV6c6rQHnvbcaDoSg\n"
  25. "jgFSLJKpnGmh71FVRqep+yVB0zI1JY43kuEnXry2HbZCD9UDo3d3n7t015X5S7ON\n"
  26. "bSSYtQGPwOr6Epf96IF6DoQxy4iDnPUAlejuhAG51s1y6/rZQ3zxAgMBAAE=\n"
  27. "-----END RSA PUBLIC KEY-----\n";
  28. static const char test_md3[] =
  29. "@last-listed 2009-06-22\n"
  30. "onion-key\n"
  31. "-----BEGIN RSA PUBLIC KEY-----\n"
  32. "MIGJAoGBAMH3340d4ENNGrqx7UxT+lB7x6DNUKOdPEOn4teceE11xlMyZ9TPv41c\n"
  33. "qj2fRZzfxlc88G/tmiaHshmdtEpklZ740OFqaaJVj4LjPMKFNE+J7Xc1142BE9Ci\n"
  34. "KgsbjGYe2RY261aADRWLetJ8T9QDMm+JngL4288hc8pq1uB/3TAbAgMBAAE=\n"
  35. "-----END RSA PUBLIC KEY-----\n"
  36. "p accept 1-700,800-1000\n"
  37. "family nodeX nodeY nodeZ\n";
  38. static void
  39. test_md_cache(void *data)
  40. {
  41. or_options_t *options = NULL;
  42. microdesc_cache_t *mc = NULL ;
  43. smartlist_t *added = NULL, *wanted = NULL;
  44. microdesc_t *md1, *md2, *md3;
  45. char d1[DIGEST256_LEN], d2[DIGEST256_LEN], d3[DIGEST256_LEN];
  46. const char *test_md3_noannotation = strchr(test_md3, '\n')+1;
  47. time_t time1, time2, time3;
  48. char *fn = NULL, *s = NULL;
  49. (void)data;
  50. options = get_options_mutable();
  51. tt_assert(options);
  52. time1 = time(NULL);
  53. time2 = time(NULL) - 2*24*60*60;
  54. time3 = time(NULL) - 15*24*60*60;
  55. /* Possibly, turn this into a test setup/cleanup pair */
  56. tor_free(options->DataDirectory);
  57. options->DataDirectory = tor_strdup(get_fname("md_datadir_test"));
  58. #ifdef _WIN32
  59. tt_int_op(0, ==, mkdir(options->DataDirectory));
  60. #else
  61. tt_int_op(0, ==, mkdir(options->DataDirectory, 0700));
  62. #endif
  63. tt_assert(!strcmpstart(test_md3_noannotation, "onion-key"));
  64. crypto_digest256(d1, test_md1, strlen(test_md1), DIGEST_SHA256);
  65. crypto_digest256(d2, test_md2, strlen(test_md1), DIGEST_SHA256);
  66. crypto_digest256(d3, test_md3_noannotation, strlen(test_md3_noannotation),
  67. DIGEST_SHA256);
  68. mc = get_microdesc_cache();
  69. added = microdescs_add_to_cache(mc, test_md1, NULL, SAVED_NOWHERE, 0,
  70. time1, NULL);
  71. tt_int_op(1, ==, smartlist_len(added));
  72. md1 = smartlist_get(added, 0);
  73. smartlist_free(added);
  74. added = NULL;
  75. wanted = smartlist_new();
  76. added = microdescs_add_to_cache(mc, test_md2, NULL, SAVED_NOWHERE, 0,
  77. time2, wanted);
  78. /* Should fail, since we didn't list test_md2's digest in wanted */
  79. tt_int_op(0, ==, smartlist_len(added));
  80. smartlist_free(added);
  81. added = NULL;
  82. smartlist_add(wanted, tor_memdup(d2, DIGEST256_LEN));
  83. smartlist_add(wanted, tor_memdup(d3, DIGEST256_LEN));
  84. added = microdescs_add_to_cache(mc, test_md2, NULL, SAVED_NOWHERE, 0,
  85. time2, wanted);
  86. /* Now it can work. md2 should have been added */
  87. tt_int_op(1, ==, smartlist_len(added));
  88. md2 = smartlist_get(added, 0);
  89. /* And it should have gotten removed from 'wanted' */
  90. tt_int_op(smartlist_len(wanted), ==, 1);
  91. test_mem_op(smartlist_get(wanted, 0), ==, d3, DIGEST256_LEN);
  92. smartlist_free(added);
  93. added = NULL;
  94. added = microdescs_add_to_cache(mc, test_md3, NULL,
  95. SAVED_NOWHERE, 0, -1, NULL);
  96. /* Must fail, since SAVED_NOWHERE precludes annotations */
  97. tt_int_op(0, ==, smartlist_len(added));
  98. smartlist_free(added);
  99. added = NULL;
  100. added = microdescs_add_to_cache(mc, test_md3_noannotation, NULL,
  101. SAVED_NOWHERE, 0, time3, NULL);
  102. /* Now it can work */
  103. tt_int_op(1, ==, smartlist_len(added));
  104. md3 = smartlist_get(added, 0);
  105. smartlist_free(added);
  106. added = NULL;
  107. /* Okay. We added 1...3. Let's poke them to see how they look, and make
  108. * sure they're really in the journal. */
  109. tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1));
  110. tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2));
  111. tt_ptr_op(md3, ==, microdesc_cache_lookup_by_digest256(mc, d3));
  112. tt_int_op(md1->last_listed, ==, time1);
  113. tt_int_op(md2->last_listed, ==, time2);
  114. tt_int_op(md3->last_listed, ==, time3);
  115. tt_int_op(md1->saved_location, ==, SAVED_IN_JOURNAL);
  116. tt_int_op(md2->saved_location, ==, SAVED_IN_JOURNAL);
  117. tt_int_op(md3->saved_location, ==, SAVED_IN_JOURNAL);
  118. tt_int_op(md1->bodylen, ==, strlen(test_md1));
  119. tt_int_op(md2->bodylen, ==, strlen(test_md2));
  120. tt_int_op(md3->bodylen, ==, strlen(test_md3_noannotation));
  121. test_mem_op(md1->body, ==, test_md1, strlen(test_md1));
  122. test_mem_op(md2->body, ==, test_md2, strlen(test_md2));
  123. test_mem_op(md3->body, ==, test_md3_noannotation,
  124. strlen(test_md3_noannotation));
  125. tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs.new",
  126. options->DataDirectory);
  127. s = read_file_to_str(fn, RFTS_BIN, NULL);
  128. tt_assert(s);
  129. test_mem_op(md1->body, ==, s + md1->off, md1->bodylen);
  130. test_mem_op(md2->body, ==, s + md2->off, md2->bodylen);
  131. test_mem_op(md3->body, ==, s + md3->off, md3->bodylen);
  132. tt_ptr_op(md1->family, ==, NULL);
  133. tt_ptr_op(md3->family, !=, NULL);
  134. tt_int_op(smartlist_len(md3->family), ==, 3);
  135. tt_str_op(smartlist_get(md3->family, 0), ==, "nodeX");
  136. /* Now rebuild the cache! */
  137. tt_int_op(microdesc_cache_rebuild(mc, 1), ==, 0);
  138. tt_int_op(md1->saved_location, ==, SAVED_IN_CACHE);
  139. tt_int_op(md2->saved_location, ==, SAVED_IN_CACHE);
  140. tt_int_op(md3->saved_location, ==, SAVED_IN_CACHE);
  141. /* The journal should be empty now */
  142. tor_free(s);
  143. s = read_file_to_str(fn, RFTS_BIN, NULL);
  144. tt_str_op(s, ==, "");
  145. tor_free(s);
  146. tor_free(fn);
  147. /* read the cache. */
  148. tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs",
  149. options->DataDirectory);
  150. s = read_file_to_str(fn, RFTS_BIN, NULL);
  151. test_mem_op(md1->body, ==, s + md1->off, strlen(test_md1));
  152. test_mem_op(md2->body, ==, s + md2->off, strlen(test_md2));
  153. test_mem_op(md3->body, ==, s + md3->off, strlen(test_md3_noannotation));
  154. /* Okay, now we are going to forget about the cache entirely, and reload it
  155. * from the disk. */
  156. microdesc_free_all();
  157. mc = get_microdesc_cache();
  158. md1 = microdesc_cache_lookup_by_digest256(mc, d1);
  159. md2 = microdesc_cache_lookup_by_digest256(mc, d2);
  160. md3 = microdesc_cache_lookup_by_digest256(mc, d3);
  161. test_assert(md1);
  162. test_assert(md2);
  163. test_assert(md3);
  164. test_mem_op(md1->body, ==, s + md1->off, strlen(test_md1));
  165. test_mem_op(md2->body, ==, s + md2->off, strlen(test_md2));
  166. test_mem_op(md3->body, ==, s + md3->off, strlen(test_md3_noannotation));
  167. tt_int_op(md1->last_listed, ==, time1);
  168. tt_int_op(md2->last_listed, ==, time2);
  169. tt_int_op(md3->last_listed, ==, time3);
  170. /* Okay, now we are going to clear out everything older than a week old.
  171. * In practice, that means md3 */
  172. microdesc_cache_clean(mc, time(NULL)-7*24*60*60, 1/*force*/);
  173. tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1));
  174. tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2));
  175. tt_ptr_op(NULL, ==, microdesc_cache_lookup_by_digest256(mc, d3));
  176. md3 = NULL; /* it's history now! */
  177. /* rebuild again, make sure it stays gone. */
  178. tt_int_op(microdesc_cache_rebuild(mc, 1), ==, 0);
  179. tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1));
  180. tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2));
  181. tt_ptr_op(NULL, ==, microdesc_cache_lookup_by_digest256(mc, d3));
  182. /* Re-add md3, and make sure we can rebuild the cache. */
  183. added = microdescs_add_to_cache(mc, test_md3_noannotation, NULL,
  184. SAVED_NOWHERE, 0, time3, NULL);
  185. tt_int_op(1, ==, smartlist_len(added));
  186. md3 = smartlist_get(added, 0);
  187. smartlist_free(added);
  188. added = NULL;
  189. tt_int_op(md1->saved_location, ==, SAVED_IN_CACHE);
  190. tt_int_op(md2->saved_location, ==, SAVED_IN_CACHE);
  191. tt_int_op(md3->saved_location, ==, SAVED_IN_JOURNAL);
  192. tt_int_op(microdesc_cache_rebuild(mc, 1), ==, 0);
  193. tt_int_op(md3->saved_location, ==, SAVED_IN_CACHE);
  194. done:
  195. if (options)
  196. tor_free(options->DataDirectory);
  197. microdesc_free_all();
  198. smartlist_free(added);
  199. if (wanted)
  200. SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp));
  201. smartlist_free(wanted);
  202. tor_free(s);
  203. tor_free(fn);
  204. }
  205. static const char truncated_md[] =
  206. "@last-listed 2013-08-08 19:02:59\n"
  207. "onion-key\n"
  208. "-----BEGIN RSA PUBLIC KEY-----\n"
  209. "MIGJAoGBAM91vLFNaM+gGhnRIdz2Cm/Kl7Xz0cOobIdVzhS3cKUJfk867hCuTipS\n"
  210. "NveLBzNopvgXKruAAzEj3cACxk6Q8lv5UWOGCD1UolkgsWSE62RBjap44g+oc9J1\n"
  211. "RI9968xOTZw0VaBQg9giEILNXl0djoikQ+5tQRUvLDDa67gpa5Q1AgMBAAE=\n"
  212. "-----END RSA PUBLIC KEY-----\n"
  213. "family @\n";
  214. static void
  215. test_md_cache_broken(void *data)
  216. {
  217. or_options_t *options;
  218. char *fn=NULL;
  219. microdesc_cache_t *mc = NULL;
  220. (void)data;
  221. options = get_options_mutable();
  222. tt_assert(options);
  223. tor_free(options->DataDirectory);
  224. options->DataDirectory = tor_strdup(get_fname("md_datadir_test2"));
  225. #ifdef _WIN32
  226. tt_int_op(0, ==, mkdir(options->DataDirectory));
  227. #else
  228. tt_int_op(0, ==, mkdir(options->DataDirectory, 0700));
  229. #endif
  230. tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs",
  231. options->DataDirectory);
  232. write_str_to_file(fn, truncated_md, 1);
  233. mc = get_microdesc_cache();
  234. tt_assert(mc);
  235. done:
  236. if (options)
  237. tor_free(options->DataDirectory);
  238. tor_free(fn);
  239. microdesc_free_all();
  240. }
  241. struct testcase_t microdesc_tests[] = {
  242. { "cache", test_md_cache, TT_FORK, NULL, NULL },
  243. { "broken_cache", test_md_cache_broken, TT_FORK, NULL, NULL },
  244. END_OF_TESTCASES
  245. };