hs_circuitmap.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. /* Copyright (c) 2016-2017, The Tor Project, Inc. */
  2. /* See LICENSE for licensing information */
  3. /**
  4. * \file hs_circuitmap.c
  5. *
  6. * \brief Hidden service circuitmap: A hash table that maps binary tokens to
  7. * introduction and rendezvous circuits; it's used:
  8. * (a) by relays acting as intro points and rendezvous points
  9. * (b) by hidden services to find intro and rend circuits and
  10. * (c) by HS clients to find rendezvous circuits.
  11. **/
  12. #define HS_CIRCUITMAP_PRIVATE
  13. #include "or.h"
  14. #include "config.h"
  15. #include "circuitlist.h"
  16. #include "hs_circuitmap.h"
  17. /************************** HS circuitmap code *******************************/
  18. /* This is the hidden service circuitmap. It's a hash table that maps
  19. introduction and rendezvous tokens to specific circuits such that given a
  20. token it's easy to find the corresponding circuit. */
  21. static struct hs_circuitmap_ht *the_hs_circuitmap = NULL;
  22. /* This is a helper function used by the hash table code (HT_). It returns 1 if
  23. * two circuits have the same HS token. */
  24. static int
  25. hs_circuits_have_same_token(const circuit_t *first_circuit,
  26. const circuit_t *second_circuit)
  27. {
  28. const hs_token_t *first_token;
  29. const hs_token_t *second_token;
  30. tor_assert(first_circuit);
  31. tor_assert(second_circuit);
  32. first_token = first_circuit->hs_token;
  33. second_token = second_circuit->hs_token;
  34. /* Both circs must have a token */
  35. if (BUG(!first_token) || BUG(!second_token)) {
  36. return 0;
  37. }
  38. if (first_token->type != second_token->type) {
  39. return 0;
  40. }
  41. if (first_token->token_len != second_token->token_len)
  42. return 0;
  43. return tor_memeq(first_token->token,
  44. second_token->token,
  45. first_token->token_len);
  46. }
  47. /* This is a helper function for the hash table code (HT_). It hashes a circuit
  48. * HS token into an unsigned int for use as a key by the hash table routines.*/
  49. static inline unsigned int
  50. hs_circuit_hash_token(const circuit_t *circuit)
  51. {
  52. tor_assert(circuit->hs_token);
  53. return (unsigned) siphash24g(circuit->hs_token->token,
  54. circuit->hs_token->token_len);
  55. }
  56. /* Register the circuitmap hash table */
  57. HT_PROTOTYPE(hs_circuitmap_ht, // The name of the hashtable struct
  58. circuit_t, // The name of the element struct,
  59. hs_circuitmap_node, // The name of HT_ENTRY member
  60. hs_circuit_hash_token, hs_circuits_have_same_token)
  61. HT_GENERATE2(hs_circuitmap_ht, circuit_t, hs_circuitmap_node,
  62. hs_circuit_hash_token, hs_circuits_have_same_token,
  63. 0.6, tor_reallocarray, tor_free_)
  64. #ifdef TOR_UNIT_TESTS
  65. /* Return the global HS circuitmap. Used by unittests. */
  66. hs_circuitmap_ht *
  67. get_hs_circuitmap(void)
  68. {
  69. return the_hs_circuitmap;
  70. }
  71. #endif /* defined(TOR_UNIT_TESTS) */
  72. /****************** HS circuitmap utility functions **************************/
  73. /** Return a new HS token of type <b>type</b> containing <b>token</b>. */
  74. static hs_token_t *
  75. hs_token_new(hs_token_type_t type, size_t token_len,
  76. const uint8_t *token)
  77. {
  78. tor_assert(token);
  79. hs_token_t *hs_token = tor_malloc_zero(sizeof(hs_token_t));
  80. hs_token->type = type;
  81. hs_token->token_len = token_len;
  82. hs_token->token = tor_memdup(token, token_len);
  83. return hs_token;
  84. }
  85. #define hs_token_free(val) \
  86. FREE_AND_NULL(hs_token_t, hs_token_free_, (val))
  87. /** Free memory allocated by this <b>hs_token</b>. */
  88. static void
  89. hs_token_free_(hs_token_t *hs_token)
  90. {
  91. if (!hs_token) {
  92. return;
  93. }
  94. tor_free(hs_token->token);
  95. tor_free(hs_token);
  96. }
  97. /** Return the circuit from the circuitmap with token <b>search_token</b>. */
  98. static circuit_t *
  99. get_circuit_with_token(hs_token_t *search_token)
  100. {
  101. tor_assert(the_hs_circuitmap);
  102. /* We use a dummy circuit object for the hash table search routine. */
  103. circuit_t search_circ;
  104. search_circ.hs_token = search_token;
  105. return HT_FIND(hs_circuitmap_ht, the_hs_circuitmap, &search_circ);
  106. }
  107. /* Helper function that registers <b>circ</b> with <b>token</b> on the HS
  108. circuitmap. This function steals reference of <b>token</b>. */
  109. static void
  110. hs_circuitmap_register_impl(circuit_t *circ, hs_token_t *token)
  111. {
  112. tor_assert(circ);
  113. tor_assert(token);
  114. tor_assert(the_hs_circuitmap);
  115. /* If this circuit already has a token, clear it. */
  116. if (circ->hs_token) {
  117. hs_circuitmap_remove_circuit(circ);
  118. }
  119. /* Kill old circuits with the same token. We want new intro/rend circuits to
  120. take precedence over old ones, so that HSes and clients and reestablish
  121. killed circuits without changing the HS token. */
  122. {
  123. circuit_t *found_circ;
  124. found_circ = get_circuit_with_token(token);
  125. if (found_circ) {
  126. hs_circuitmap_remove_circuit(found_circ);
  127. if (!found_circ->marked_for_close) {
  128. circuit_mark_for_close(found_circ, END_CIRC_REASON_FINISHED);
  129. }
  130. }
  131. }
  132. /* Register circuit and token to circuitmap. */
  133. circ->hs_token = token;
  134. HT_INSERT(hs_circuitmap_ht, the_hs_circuitmap, circ);
  135. }
  136. /** Helper function: Register <b>circ</b> of <b>type</b> on the HS
  137. * circuitmap. Use the HS <b>token</b> as the key to the hash table. If
  138. * <b>token</b> is not set, clear the circuit of any HS tokens. */
  139. static void
  140. hs_circuitmap_register_circuit(circuit_t *circ,
  141. hs_token_type_t type, size_t token_len,
  142. const uint8_t *token)
  143. {
  144. hs_token_t *hs_token = NULL;
  145. /* Create a new token and register it to the circuitmap */
  146. tor_assert(token);
  147. hs_token = hs_token_new(type, token_len, token);
  148. tor_assert(hs_token);
  149. hs_circuitmap_register_impl(circ, hs_token);
  150. }
  151. /* Helper function for hs_circuitmap_get_origin_circuit() and
  152. * hs_circuitmap_get_or_circuit(). Because only circuit_t are indexed in the
  153. * circuitmap, this function returns object type so the specialized functions
  154. * using this helper can upcast it to the right type.
  155. *
  156. * Return NULL if not such circuit is found. */
  157. static circuit_t *
  158. hs_circuitmap_get_circuit_impl(hs_token_type_t type,
  159. size_t token_len,
  160. const uint8_t *token,
  161. uint8_t wanted_circ_purpose)
  162. {
  163. circuit_t *found_circ = NULL;
  164. tor_assert(the_hs_circuitmap);
  165. /* Check the circuitmap if we have a circuit with this token */
  166. {
  167. hs_token_t *search_hs_token = hs_token_new(type, token_len, token);
  168. tor_assert(search_hs_token);
  169. found_circ = get_circuit_with_token(search_hs_token);
  170. hs_token_free(search_hs_token);
  171. }
  172. /* Check that the circuit is useful to us */
  173. if (!found_circ ||
  174. found_circ->purpose != wanted_circ_purpose ||
  175. found_circ->marked_for_close) {
  176. return NULL;
  177. }
  178. return found_circ;
  179. }
  180. /* Helper function: Query circuitmap for origin circuit with <b>token</b> of
  181. * size <b>token_len</b> and <b>type</b>. Only returns a circuit with purpose
  182. * equal to the <b>wanted_circ_purpose</b> parameter and if it is NOT marked
  183. * for close. Return NULL if no such circuit is found. */
  184. static origin_circuit_t *
  185. hs_circuitmap_get_origin_circuit(hs_token_type_t type,
  186. size_t token_len,
  187. const uint8_t *token,
  188. uint8_t wanted_circ_purpose)
  189. {
  190. circuit_t *circ;
  191. tor_assert(token);
  192. tor_assert(CIRCUIT_PURPOSE_IS_ORIGIN(wanted_circ_purpose));
  193. circ = hs_circuitmap_get_circuit_impl(type, token_len, token,
  194. wanted_circ_purpose);
  195. if (!circ) {
  196. return NULL;
  197. }
  198. tor_assert(CIRCUIT_IS_ORIGIN(circ));
  199. return TO_ORIGIN_CIRCUIT(circ);
  200. }
  201. /* Helper function: Query circuitmap for OR circuit with <b>token</b> of size
  202. * <b>token_len</b> and <b>type</b>. Only returns a circuit with purpose equal
  203. * to the <b>wanted_circ_purpose</b> parameter and if it is NOT marked for
  204. * close. Return NULL if no such circuit is found. */
  205. static or_circuit_t *
  206. hs_circuitmap_get_or_circuit(hs_token_type_t type,
  207. size_t token_len,
  208. const uint8_t *token,
  209. uint8_t wanted_circ_purpose)
  210. {
  211. circuit_t *circ;
  212. tor_assert(token);
  213. tor_assert(!CIRCUIT_PURPOSE_IS_ORIGIN(wanted_circ_purpose));
  214. circ = hs_circuitmap_get_circuit_impl(type, token_len, token,
  215. wanted_circ_purpose);
  216. if (!circ) {
  217. return NULL;
  218. }
  219. tor_assert(CIRCUIT_IS_ORCIRC(circ));
  220. return TO_OR_CIRCUIT(circ);
  221. }
  222. /************** Public circuitmap API ****************************************/
  223. /**** Public relay-side getters: */
  224. /* Public function: Return a v3 introduction circuit to this relay with
  225. * <b>auth_key</b>. Return NULL if no such circuit is found in the
  226. * circuitmap. */
  227. or_circuit_t *
  228. hs_circuitmap_get_intro_circ_v3_relay_side(
  229. const ed25519_public_key_t *auth_key)
  230. {
  231. return hs_circuitmap_get_or_circuit(HS_TOKEN_INTRO_V3_RELAY_SIDE,
  232. ED25519_PUBKEY_LEN, auth_key->pubkey,
  233. CIRCUIT_PURPOSE_INTRO_POINT);
  234. }
  235. /* Public function: Return v2 introduction circuit to this relay with
  236. * <b>digest</b>. Return NULL if no such circuit is found in the circuitmap. */
  237. or_circuit_t *
  238. hs_circuitmap_get_intro_circ_v2_relay_side(const uint8_t *digest)
  239. {
  240. return hs_circuitmap_get_or_circuit(HS_TOKEN_INTRO_V2_RELAY_SIDE,
  241. REND_TOKEN_LEN, digest,
  242. CIRCUIT_PURPOSE_INTRO_POINT);
  243. }
  244. /* Public function: Return rendezvous circuit to this relay with rendezvous
  245. * <b>cookie</b>. Return NULL if no such circuit is found in the circuitmap. */
  246. or_circuit_t *
  247. hs_circuitmap_get_rend_circ_relay_side(const uint8_t *cookie)
  248. {
  249. return hs_circuitmap_get_or_circuit(HS_TOKEN_REND_RELAY_SIDE,
  250. REND_TOKEN_LEN, cookie,
  251. CIRCUIT_PURPOSE_REND_POINT_WAITING);
  252. }
  253. /** Public relay-side setters: */
  254. /* Public function: Register rendezvous circuit with key <b>cookie</b> to the
  255. * circuitmap. */
  256. void
  257. hs_circuitmap_register_rend_circ_relay_side(or_circuit_t *circ,
  258. const uint8_t *cookie)
  259. {
  260. hs_circuitmap_register_circuit(TO_CIRCUIT(circ),
  261. HS_TOKEN_REND_RELAY_SIDE,
  262. REND_TOKEN_LEN, cookie);
  263. }
  264. /* Public function: Register v2 intro circuit with key <b>digest</b> to the
  265. * circuitmap. */
  266. void
  267. hs_circuitmap_register_intro_circ_v2_relay_side(or_circuit_t *circ,
  268. const uint8_t *digest)
  269. {
  270. hs_circuitmap_register_circuit(TO_CIRCUIT(circ),
  271. HS_TOKEN_INTRO_V2_RELAY_SIDE,
  272. REND_TOKEN_LEN, digest);
  273. }
  274. /* Public function: Register v3 intro circuit with key <b>auth_key</b> to the
  275. * circuitmap. */
  276. void
  277. hs_circuitmap_register_intro_circ_v3_relay_side(or_circuit_t *circ,
  278. const ed25519_public_key_t *auth_key)
  279. {
  280. hs_circuitmap_register_circuit(TO_CIRCUIT(circ),
  281. HS_TOKEN_INTRO_V3_RELAY_SIDE,
  282. ED25519_PUBKEY_LEN, auth_key->pubkey);
  283. }
  284. /**** Public servide-side getters: */
  285. /* Public function: Return v3 introduction circuit with <b>auth_key</b>
  286. * originating from this hidden service. Return NULL if no such circuit is
  287. * found in the circuitmap. */
  288. origin_circuit_t *
  289. hs_circuitmap_get_intro_circ_v3_service_side(const
  290. ed25519_public_key_t *auth_key)
  291. {
  292. origin_circuit_t *circ = NULL;
  293. /* Check first for established intro circuits */
  294. circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_INTRO_V3_SERVICE_SIDE,
  295. ED25519_PUBKEY_LEN, auth_key->pubkey,
  296. CIRCUIT_PURPOSE_S_INTRO);
  297. if (circ) {
  298. return circ;
  299. }
  300. /* ...if nothing found, check for pending intro circs */
  301. circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_INTRO_V3_SERVICE_SIDE,
  302. ED25519_PUBKEY_LEN, auth_key->pubkey,
  303. CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
  304. return circ;
  305. }
  306. /* Public function: Return v2 introduction circuit originating from this hidden
  307. * service with <b>digest</b>. Return NULL if no such circuit is found in the
  308. * circuitmap. */
  309. origin_circuit_t *
  310. hs_circuitmap_get_intro_circ_v2_service_side(const uint8_t *digest)
  311. {
  312. origin_circuit_t *circ = NULL;
  313. /* Check first for established intro circuits */
  314. circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_INTRO_V2_SERVICE_SIDE,
  315. REND_TOKEN_LEN, digest,
  316. CIRCUIT_PURPOSE_S_INTRO);
  317. if (circ) {
  318. return circ;
  319. }
  320. /* ...if nothing found, check for pending intro circs */
  321. circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_INTRO_V2_SERVICE_SIDE,
  322. REND_TOKEN_LEN, digest,
  323. CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
  324. return circ;
  325. }
  326. /* Public function: Return rendezvous circuit originating from this hidden
  327. * service with rendezvous <b>cookie</b>. Return NULL if no such circuit is
  328. * found in the circuitmap. */
  329. origin_circuit_t *
  330. hs_circuitmap_get_rend_circ_service_side(const uint8_t *cookie)
  331. {
  332. origin_circuit_t *circ = NULL;
  333. /* Try to check if we have a connecting circuit. */
  334. circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_SERVICE_SIDE,
  335. REND_TOKEN_LEN, cookie,
  336. CIRCUIT_PURPOSE_S_CONNECT_REND);
  337. if (circ) {
  338. return circ;
  339. }
  340. /* Then try for connected circuit. */
  341. circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_SERVICE_SIDE,
  342. REND_TOKEN_LEN, cookie,
  343. CIRCUIT_PURPOSE_S_REND_JOINED);
  344. return circ;
  345. }
  346. /* Public function: Return client-side rendezvous circuit with rendezvous
  347. * <b>cookie</b>. It will look for circuits with the following purposes:
  348. * a) CIRCUIT_PURPOSE_C_REND_READY: Established rend circuit (received
  349. * RENDEZVOUS_ESTABLISHED). Waiting for RENDEZVOUS2 from service, and for
  350. * INTRODUCE_ACK from intro point.
  351. *
  352. * b) CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: Established rend circuit and
  353. * introduce circuit acked. Waiting for RENDEZVOUS2 from service.
  354. *
  355. * c) CIRCUIT_PURPOSE_C_REND_JOINED: Established rend circuit and received
  356. * RENDEZVOUS2 from service.
  357. *
  358. * d) CIRCUIT_PURPOSE_C_ESTABLISH_REND: Rend circuit open but not yet
  359. * established.
  360. *
  361. * Return NULL if no such circuit is found in the circuitmap. */
  362. origin_circuit_t *
  363. hs_circuitmap_get_rend_circ_client_side(const uint8_t *cookie)
  364. {
  365. origin_circuit_t *circ = NULL;
  366. circ = hs_circuitmap_get_established_rend_circ_client_side(cookie);
  367. if (circ) {
  368. return circ;
  369. }
  370. circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
  371. REND_TOKEN_LEN, cookie,
  372. CIRCUIT_PURPOSE_C_ESTABLISH_REND);
  373. return circ;
  374. }
  375. /* Public function: Return client-side established rendezvous circuit with
  376. * rendezvous <b>cookie</b>. It will look for circuits with the following
  377. * purposes:
  378. *
  379. * a) CIRCUIT_PURPOSE_C_REND_READY: Established rend circuit (received
  380. * RENDEZVOUS_ESTABLISHED). Waiting for RENDEZVOUS2 from service, and for
  381. * INTRODUCE_ACK from intro point.
  382. *
  383. * b) CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: Established rend circuit and
  384. * introduce circuit acked. Waiting for RENDEZVOUS2 from service.
  385. *
  386. * c) CIRCUIT_PURPOSE_C_REND_JOINED: Established rend circuit and received
  387. * RENDEZVOUS2 from service.
  388. *
  389. * Return NULL if no such circuit is found in the circuitmap. */
  390. origin_circuit_t *
  391. hs_circuitmap_get_established_rend_circ_client_side(const uint8_t *cookie)
  392. {
  393. origin_circuit_t *circ = NULL;
  394. circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
  395. REND_TOKEN_LEN, cookie,
  396. CIRCUIT_PURPOSE_C_REND_READY);
  397. if (circ) {
  398. return circ;
  399. }
  400. circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
  401. REND_TOKEN_LEN, cookie,
  402. CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED);
  403. if (circ) {
  404. return circ;
  405. }
  406. circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
  407. REND_TOKEN_LEN, cookie,
  408. CIRCUIT_PURPOSE_C_REND_JOINED);
  409. return circ;
  410. }
  411. /**** Public servide-side setters: */
  412. /* Public function: Register v2 intro circuit with key <b>digest</b> to the
  413. * circuitmap. */
  414. void
  415. hs_circuitmap_register_intro_circ_v2_service_side(origin_circuit_t *circ,
  416. const uint8_t *digest)
  417. {
  418. hs_circuitmap_register_circuit(TO_CIRCUIT(circ),
  419. HS_TOKEN_INTRO_V2_SERVICE_SIDE,
  420. REND_TOKEN_LEN, digest);
  421. }
  422. /* Public function: Register v3 intro circuit with key <b>auth_key</b> to the
  423. * circuitmap. */
  424. void
  425. hs_circuitmap_register_intro_circ_v3_service_side(origin_circuit_t *circ,
  426. const ed25519_public_key_t *auth_key)
  427. {
  428. hs_circuitmap_register_circuit(TO_CIRCUIT(circ),
  429. HS_TOKEN_INTRO_V3_SERVICE_SIDE,
  430. ED25519_PUBKEY_LEN, auth_key->pubkey);
  431. }
  432. /* Public function: Register rendezvous circuit with key <b>cookie</b> to the
  433. * circuitmap. */
  434. void
  435. hs_circuitmap_register_rend_circ_service_side(origin_circuit_t *circ,
  436. const uint8_t *cookie)
  437. {
  438. hs_circuitmap_register_circuit(TO_CIRCUIT(circ),
  439. HS_TOKEN_REND_SERVICE_SIDE,
  440. REND_TOKEN_LEN, cookie);
  441. }
  442. /* Public function: Register rendezvous circuit with key <b>cookie</b> to the
  443. * client-side circuitmap. */
  444. void
  445. hs_circuitmap_register_rend_circ_client_side(origin_circuit_t *or_circ,
  446. const uint8_t *cookie)
  447. {
  448. circuit_t *circ = TO_CIRCUIT(or_circ);
  449. { /* Basic circ purpose sanity checking */
  450. tor_assert_nonfatal(circ->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
  451. }
  452. hs_circuitmap_register_circuit(circ, HS_TOKEN_REND_CLIENT_SIDE,
  453. REND_TOKEN_LEN, cookie);
  454. }
  455. /**** Misc public functions: */
  456. /** Public function: Remove this circuit from the HS circuitmap. Clear its HS
  457. * token, and remove it from the hashtable. */
  458. void
  459. hs_circuitmap_remove_circuit(circuit_t *circ)
  460. {
  461. tor_assert(the_hs_circuitmap);
  462. if (!circ || !circ->hs_token) {
  463. return;
  464. }
  465. /* Remove circ from circuitmap */
  466. circuit_t *tmp;
  467. tmp = HT_REMOVE(hs_circuitmap_ht, the_hs_circuitmap, circ);
  468. /* ... and ensure the removal was successful. */
  469. if (tmp) {
  470. tor_assert(tmp == circ);
  471. } else {
  472. log_warn(LD_BUG, "Could not find circuit (%u) in circuitmap.",
  473. circ->n_circ_id);
  474. }
  475. /* Clear token from circ */
  476. hs_token_free(circ->hs_token);
  477. circ->hs_token = NULL;
  478. }
  479. /* Public function: Initialize the global HS circuitmap. */
  480. void
  481. hs_circuitmap_init(void)
  482. {
  483. tor_assert(!the_hs_circuitmap);
  484. the_hs_circuitmap = tor_malloc_zero(sizeof(struct hs_circuitmap_ht));
  485. HT_INIT(hs_circuitmap_ht, the_hs_circuitmap);
  486. }
  487. /* Public function: Free all memory allocated by the global HS circuitmap. */
  488. void
  489. hs_circuitmap_free_all(void)
  490. {
  491. if (the_hs_circuitmap) {
  492. HT_CLEAR(hs_circuitmap_ht, the_hs_circuitmap);
  493. tor_free(the_hs_circuitmap);
  494. }
  495. }