Loading changes/bug5595 0 → 100644 +8 −0 Original line number Diff line number Diff line o Critical bugfixes: - Distinguish downloading an authority certificate by identity digest from downloading one by identity digest/signing key digest pair; formerly we always request them only by identity digest and get the newest one even when we wanted one with a different signing key. Then we would complain about being given a certificate we already had, and never get the one we really wanted. Now we use the "fp-sk/" resource as well as the "fp/" resource to request the one we want. Fixes bug 5595. src/or/Makefile.nmake +1 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ LIBTOR_OBJECTS = \ dirvote.obj \ dns.obj \ dnsserv.obj \ fp_pair.obj \ entrynodes.obj \ geoip.obj \ hibernate.obj \ Loading src/or/directory.c +59 −14 Original line number Diff line number Diff line Loading @@ -856,19 +856,43 @@ connection_dir_bridge_routerdesc_failed(dir_connection_t *conn) static void connection_dir_download_cert_failed(dir_connection_t *conn, int status) { const char *fp_pfx = "fp/"; const char *fpsk_pfx = "fp-sk/"; smartlist_t *failed; tor_assert(conn->base_.purpose == DIR_PURPOSE_FETCH_CERTIFICATE); if (!conn->requested_resource) return; failed = smartlist_new(); dir_split_resource_into_fingerprints(conn->requested_resource+3, /* * We have two cases download by fingerprint (resource starts * with "fp/") or download by fingerprint/signing key pair * (resource starts with "fp-sk/"). */ if (!strcmpstart(conn->requested_resource, fp_pfx)) { /* Download by fingerprint case */ dir_split_resource_into_fingerprints(conn->requested_resource + strlen(fp_pfx), failed, NULL, DSR_HEX); SMARTLIST_FOREACH(failed, char *, cp, { authority_cert_dl_failed(cp, status); SMARTLIST_FOREACH_BEGIN(failed, char *, cp) { /* Null signing key digest indicates download by fp only */ authority_cert_dl_failed(cp, NULL, status); tor_free(cp); }); } SMARTLIST_FOREACH_END(cp); } else if (!strcmpstart(conn->requested_resource, fpsk_pfx)) { /* Download by (fp,sk) pairs */ dir_split_resource_into_fingerprint_pairs(conn->requested_resource + strlen(fpsk_pfx), failed); SMARTLIST_FOREACH_BEGIN(failed, fp_pair_t *, cp) { authority_cert_dl_failed(cp->first, cp->second, status); tor_free(cp); } SMARTLIST_FOREACH_END(cp); } else { log_warn(LD_DIR, "Don't know what to do with failure for cert fetch %s", conn->requested_resource); } smartlist_free(failed); update_certificate_downloads(time(NULL)); Loading Loading @@ -1634,6 +1658,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) conn->base_.purpose == DIR_PURPOSE_FETCH_MICRODESC); int was_compressed=0; time_t now = time(NULL); int src_code; switch (connection_fetch_from_buf_http(TO_CONN(conn), &headers, MAX_HEADERS_SIZE, Loading Loading @@ -1902,7 +1927,20 @@ connection_dir_client_reached_eof(dir_connection_t *conn) } log_info(LD_DIR,"Received authority certificates (size %d) from server " "'%s:%d'", (int)body_len, conn->base_.address, conn->base_.port); if (trusted_dirs_load_certs_from_string(body, 0, 1)<0) { /* * Tell trusted_dirs_load_certs_from_string() whether it was by fp * or fp-sk pair. */ src_code = -1; if (!strcmpstart(conn->requested_resource, "fp/")) { src_code = TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST; } else if (!strcmpstart(conn->requested_resource, "fp-sk/")) { src_code = TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_SK_DIGEST; } if (src_code != -1) { if (trusted_dirs_load_certs_from_string(body, src_code, 1)<0) { log_warn(LD_DIR, "Unable to parse fetched certificates"); /* if we fetched more than one and only some failed, the successful * ones got flushed to disk so it's safe to call this on them */ Loading @@ -1911,6 +1949,13 @@ connection_dir_client_reached_eof(dir_connection_t *conn) directory_info_has_arrived(now, 0); log_info(LD_DIR, "Successfully loaded certificates from fetch."); } } else { log_warn(LD_DIR, "Couldn't figure out what to do with fetched certificates for " "unknown resource %s", conn->requested_resource); connection_dir_download_cert_failed(conn, status_code); } } if (conn->base_.purpose == DIR_PURPOSE_FETCH_STATUS_VOTE) { const char *msg; Loading src/or/dirvote.c +1 −1 Original line number Diff line number Diff line Loading @@ -2963,7 +2963,7 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) /* Hey, it's a new cert! */ trusted_dirs_load_certs_from_string( vote->cert->cache_info.signed_descriptor_body, 0 /* from_store */, 1 /*flush*/); TRUSTED_DIRS_CERTS_SRC_FROM_VOTE, 1 /*flush*/); if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest, vote->cert->signing_key_digest)) { log_warn(LD_BUG, "We added a cert, but still couldn't find it."); Loading src/or/fp_pair.c 0 → 100644 +308 −0 Original line number Diff line number Diff line /* Copyright (c) 2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" #include "fp_pair.h" /* Define fp_pair_map_t structures */ struct fp_pair_map_entry_s { HT_ENTRY(fp_pair_map_entry_s) node; void *val; fp_pair_t key; }; struct fp_pair_map_s { HT_HEAD(fp_pair_map_impl, fp_pair_map_entry_s) head; }; /* * Hash function and equality checker for fp_pair_map_t */ /** Compare fp_pair_entry_t objects by key value. */ static INLINE int fp_pair_map_entries_eq(const fp_pair_map_entry_t *a, const fp_pair_map_entry_t *b) { return tor_memeq(&(a->key), &(b->key), sizeof(fp_pair_t)); } /** Return a hash value for an fp_pair_entry_t. */ static INLINE unsigned int fp_pair_map_entry_hash(const fp_pair_map_entry_t *a) { const uint32_t *p; unsigned int hash; p = (const uint32_t *)(a->key.first); /* Hashes are 20 bytes long, so 5 times uint32_t */ hash = p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4]; /* Now XOR in the second fingerprint */ p = (const uint32_t *)(a->key.second); hash ^= p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4]; return hash; } /* * Hash table functions for fp_pair_map_t */ HT_PROTOTYPE(fp_pair_map_impl, fp_pair_map_entry_s, node, fp_pair_map_entry_hash, fp_pair_map_entries_eq) HT_GENERATE(fp_pair_map_impl, fp_pair_map_entry_s, node, fp_pair_map_entry_hash, fp_pair_map_entries_eq, 0.6, tor_malloc, tor_realloc, tor_free) /** Constructor to create a new empty map from fp_pair_t to void * */ fp_pair_map_t * fp_pair_map_new(void) { fp_pair_map_t *result; result = tor_malloc(sizeof(fp_pair_map_t)); HT_INIT(fp_pair_map_impl, &result->head); return result; } /** Set the current value for key to val; returns the previous * value for key if one was set, or NULL if one was not. */ void * fp_pair_map_set(fp_pair_map_t *map, const fp_pair_t *key, void *val) { fp_pair_map_entry_t *resolve; fp_pair_map_entry_t search; void *oldval; tor_assert(map); tor_assert(key); tor_assert(val); memcpy(&(search.key), key, sizeof(*key)); resolve = HT_FIND(fp_pair_map_impl, &(map->head), &search); if (resolve) { oldval = resolve->val; resolve->val = val; } else { resolve = tor_malloc_zero(sizeof(fp_pair_map_entry_t)); memcpy(&(resolve->key), key, sizeof(*key)); resolve->val = val; HT_INSERT(fp_pair_map_impl, &(map->head), resolve); oldval = NULL; } return oldval; } /** Set the current value for the key (first, second) to val; returns * the previous value for key if one was set, or NULL if one was not. */ void * fp_pair_map_set_by_digests(fp_pair_map_t *map, const char *first, const char *second, void *val) { fp_pair_t k; tor_assert(first); tor_assert(second); memcpy(k.first, first, DIGEST_LEN); memcpy(k.second, second, DIGEST_LEN); return fp_pair_map_set(map, &k, val); } /** Return the current value associated with key, or NULL if no value is set. */ void * fp_pair_map_get(const fp_pair_map_t *map, const fp_pair_t *key) { fp_pair_map_entry_t *resolve; fp_pair_map_entry_t search; void *val = NULL; tor_assert(map); tor_assert(key); memcpy(&(search.key), key, sizeof(*key)); resolve = HT_FIND(fp_pair_map_impl, &(map->head), &search); if (resolve) val = resolve->val; return val; } /** Return the current value associated the key (first, second), or * NULL if no value is set. */ void * fp_pair_map_get_by_digests(const fp_pair_map_t *map, const char *first, const char *second) { fp_pair_t k; tor_assert(first); tor_assert(second); memcpy(k.first, first, DIGEST_LEN); memcpy(k.second, second, DIGEST_LEN); return fp_pair_map_get(map, &k); } /** Remove the value currently associated with key from the map. * Return the value if one was set, or NULL if there was no entry for * key. The caller must free any storage associated with the * returned value. */ void * fp_pair_map_remove(fp_pair_map_t *map, const fp_pair_t *key) { fp_pair_map_entry_t *resolve; fp_pair_map_entry_t search; void *val = NULL; tor_assert(map); tor_assert(key); memcpy(&(search.key), key, sizeof(*key)); resolve = HT_REMOVE(fp_pair_map_impl, &(map->head), &search); if (resolve) { val = resolve->val; tor_free(resolve); } return val; } /** Remove all entries from map, and deallocate storage for those entries. * If free_val is provided, it is invoked on every value in map. */ void fp_pair_map_free(fp_pair_map_t *map, void (*free_val)(void*)) { fp_pair_map_entry_t **ent, **next, *this; if (map) { for (ent = HT_START(fp_pair_map_impl, &(map->head)); ent != NULL; ent = next) { this = *ent; next = HT_NEXT_RMV(fp_pair_map_impl, &(map->head), ent); if (free_val) free_val(this->val); tor_free(this); } tor_assert(HT_EMPTY(&(map->head))); HT_CLEAR(fp_pair_map_impl, &(map->head)); tor_free(map); } } /** Return true iff map has no entries. */ int fp_pair_map_isempty(const fp_pair_map_t *map) { tor_assert(map); return HT_EMPTY(&(map->head)); } /** Return the number of items in map. */ int fp_pair_map_size(const fp_pair_map_t *map) { tor_assert(map); return HT_SIZE(&(map->head)); } /** return an iterator pointing to the start of map. */ fp_pair_map_iter_t * fp_pair_map_iter_init(fp_pair_map_t *map) { tor_assert(map); return HT_START(fp_pair_map_impl, &(map->head)); } /** Advance iter a single step to the next entry of map, and return * its new value. */ fp_pair_map_iter_t * fp_pair_map_iter_next(fp_pair_map_t *map, fp_pair_map_iter_t *iter) { tor_assert(map); tor_assert(iter); return HT_NEXT(fp_pair_map_impl, &(map->head), iter); } /** Advance iter a single step to the next entry of map, removing the current * entry, and return its new value. */ fp_pair_map_iter_t * fp_pair_map_iter_next_rmv(fp_pair_map_t *map, fp_pair_map_iter_t *iter) { fp_pair_map_entry_t *rmv; tor_assert(map); tor_assert(iter); tor_assert(*iter); rmv = *iter; iter = HT_NEXT_RMV(fp_pair_map_impl, &(map->head), iter); tor_free(rmv); return iter; } /** Set *key_out and *val_out to the current entry pointed to by iter. */ void fp_pair_map_iter_get(fp_pair_map_iter_t *iter, fp_pair_t *key_out, void **val_out) { tor_assert(iter); tor_assert(*iter); if (key_out) memcpy(key_out, &((*iter)->key), sizeof(fp_pair_t)); if (val_out) *val_out = (*iter)->val; } /** Return true iff iter has advanced past the last entry of its map. */ int fp_pair_map_iter_done(fp_pair_map_iter_t *iter) { return (iter == NULL); } /** Assert if anything has gone wrong with the internal * representation of map. */ void fp_pair_map_assert_ok(const fp_pair_map_t *map) { tor_assert(!fp_pair_map_impl_HT_REP_IS_BAD_(&(map->head))); } Loading
changes/bug5595 0 → 100644 +8 −0 Original line number Diff line number Diff line o Critical bugfixes: - Distinguish downloading an authority certificate by identity digest from downloading one by identity digest/signing key digest pair; formerly we always request them only by identity digest and get the newest one even when we wanted one with a different signing key. Then we would complain about being given a certificate we already had, and never get the one we really wanted. Now we use the "fp-sk/" resource as well as the "fp/" resource to request the one we want. Fixes bug 5595.
src/or/Makefile.nmake +1 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ LIBTOR_OBJECTS = \ dirvote.obj \ dns.obj \ dnsserv.obj \ fp_pair.obj \ entrynodes.obj \ geoip.obj \ hibernate.obj \ Loading
src/or/directory.c +59 −14 Original line number Diff line number Diff line Loading @@ -856,19 +856,43 @@ connection_dir_bridge_routerdesc_failed(dir_connection_t *conn) static void connection_dir_download_cert_failed(dir_connection_t *conn, int status) { const char *fp_pfx = "fp/"; const char *fpsk_pfx = "fp-sk/"; smartlist_t *failed; tor_assert(conn->base_.purpose == DIR_PURPOSE_FETCH_CERTIFICATE); if (!conn->requested_resource) return; failed = smartlist_new(); dir_split_resource_into_fingerprints(conn->requested_resource+3, /* * We have two cases download by fingerprint (resource starts * with "fp/") or download by fingerprint/signing key pair * (resource starts with "fp-sk/"). */ if (!strcmpstart(conn->requested_resource, fp_pfx)) { /* Download by fingerprint case */ dir_split_resource_into_fingerprints(conn->requested_resource + strlen(fp_pfx), failed, NULL, DSR_HEX); SMARTLIST_FOREACH(failed, char *, cp, { authority_cert_dl_failed(cp, status); SMARTLIST_FOREACH_BEGIN(failed, char *, cp) { /* Null signing key digest indicates download by fp only */ authority_cert_dl_failed(cp, NULL, status); tor_free(cp); }); } SMARTLIST_FOREACH_END(cp); } else if (!strcmpstart(conn->requested_resource, fpsk_pfx)) { /* Download by (fp,sk) pairs */ dir_split_resource_into_fingerprint_pairs(conn->requested_resource + strlen(fpsk_pfx), failed); SMARTLIST_FOREACH_BEGIN(failed, fp_pair_t *, cp) { authority_cert_dl_failed(cp->first, cp->second, status); tor_free(cp); } SMARTLIST_FOREACH_END(cp); } else { log_warn(LD_DIR, "Don't know what to do with failure for cert fetch %s", conn->requested_resource); } smartlist_free(failed); update_certificate_downloads(time(NULL)); Loading Loading @@ -1634,6 +1658,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) conn->base_.purpose == DIR_PURPOSE_FETCH_MICRODESC); int was_compressed=0; time_t now = time(NULL); int src_code; switch (connection_fetch_from_buf_http(TO_CONN(conn), &headers, MAX_HEADERS_SIZE, Loading Loading @@ -1902,7 +1927,20 @@ connection_dir_client_reached_eof(dir_connection_t *conn) } log_info(LD_DIR,"Received authority certificates (size %d) from server " "'%s:%d'", (int)body_len, conn->base_.address, conn->base_.port); if (trusted_dirs_load_certs_from_string(body, 0, 1)<0) { /* * Tell trusted_dirs_load_certs_from_string() whether it was by fp * or fp-sk pair. */ src_code = -1; if (!strcmpstart(conn->requested_resource, "fp/")) { src_code = TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST; } else if (!strcmpstart(conn->requested_resource, "fp-sk/")) { src_code = TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_SK_DIGEST; } if (src_code != -1) { if (trusted_dirs_load_certs_from_string(body, src_code, 1)<0) { log_warn(LD_DIR, "Unable to parse fetched certificates"); /* if we fetched more than one and only some failed, the successful * ones got flushed to disk so it's safe to call this on them */ Loading @@ -1911,6 +1949,13 @@ connection_dir_client_reached_eof(dir_connection_t *conn) directory_info_has_arrived(now, 0); log_info(LD_DIR, "Successfully loaded certificates from fetch."); } } else { log_warn(LD_DIR, "Couldn't figure out what to do with fetched certificates for " "unknown resource %s", conn->requested_resource); connection_dir_download_cert_failed(conn, status_code); } } if (conn->base_.purpose == DIR_PURPOSE_FETCH_STATUS_VOTE) { const char *msg; Loading
src/or/dirvote.c +1 −1 Original line number Diff line number Diff line Loading @@ -2963,7 +2963,7 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out) /* Hey, it's a new cert! */ trusted_dirs_load_certs_from_string( vote->cert->cache_info.signed_descriptor_body, 0 /* from_store */, 1 /*flush*/); TRUSTED_DIRS_CERTS_SRC_FROM_VOTE, 1 /*flush*/); if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest, vote->cert->signing_key_digest)) { log_warn(LD_BUG, "We added a cert, but still couldn't find it."); Loading
src/or/fp_pair.c 0 → 100644 +308 −0 Original line number Diff line number Diff line /* Copyright (c) 2013, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" #include "fp_pair.h" /* Define fp_pair_map_t structures */ struct fp_pair_map_entry_s { HT_ENTRY(fp_pair_map_entry_s) node; void *val; fp_pair_t key; }; struct fp_pair_map_s { HT_HEAD(fp_pair_map_impl, fp_pair_map_entry_s) head; }; /* * Hash function and equality checker for fp_pair_map_t */ /** Compare fp_pair_entry_t objects by key value. */ static INLINE int fp_pair_map_entries_eq(const fp_pair_map_entry_t *a, const fp_pair_map_entry_t *b) { return tor_memeq(&(a->key), &(b->key), sizeof(fp_pair_t)); } /** Return a hash value for an fp_pair_entry_t. */ static INLINE unsigned int fp_pair_map_entry_hash(const fp_pair_map_entry_t *a) { const uint32_t *p; unsigned int hash; p = (const uint32_t *)(a->key.first); /* Hashes are 20 bytes long, so 5 times uint32_t */ hash = p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4]; /* Now XOR in the second fingerprint */ p = (const uint32_t *)(a->key.second); hash ^= p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4]; return hash; } /* * Hash table functions for fp_pair_map_t */ HT_PROTOTYPE(fp_pair_map_impl, fp_pair_map_entry_s, node, fp_pair_map_entry_hash, fp_pair_map_entries_eq) HT_GENERATE(fp_pair_map_impl, fp_pair_map_entry_s, node, fp_pair_map_entry_hash, fp_pair_map_entries_eq, 0.6, tor_malloc, tor_realloc, tor_free) /** Constructor to create a new empty map from fp_pair_t to void * */ fp_pair_map_t * fp_pair_map_new(void) { fp_pair_map_t *result; result = tor_malloc(sizeof(fp_pair_map_t)); HT_INIT(fp_pair_map_impl, &result->head); return result; } /** Set the current value for key to val; returns the previous * value for key if one was set, or NULL if one was not. */ void * fp_pair_map_set(fp_pair_map_t *map, const fp_pair_t *key, void *val) { fp_pair_map_entry_t *resolve; fp_pair_map_entry_t search; void *oldval; tor_assert(map); tor_assert(key); tor_assert(val); memcpy(&(search.key), key, sizeof(*key)); resolve = HT_FIND(fp_pair_map_impl, &(map->head), &search); if (resolve) { oldval = resolve->val; resolve->val = val; } else { resolve = tor_malloc_zero(sizeof(fp_pair_map_entry_t)); memcpy(&(resolve->key), key, sizeof(*key)); resolve->val = val; HT_INSERT(fp_pair_map_impl, &(map->head), resolve); oldval = NULL; } return oldval; } /** Set the current value for the key (first, second) to val; returns * the previous value for key if one was set, or NULL if one was not. */ void * fp_pair_map_set_by_digests(fp_pair_map_t *map, const char *first, const char *second, void *val) { fp_pair_t k; tor_assert(first); tor_assert(second); memcpy(k.first, first, DIGEST_LEN); memcpy(k.second, second, DIGEST_LEN); return fp_pair_map_set(map, &k, val); } /** Return the current value associated with key, or NULL if no value is set. */ void * fp_pair_map_get(const fp_pair_map_t *map, const fp_pair_t *key) { fp_pair_map_entry_t *resolve; fp_pair_map_entry_t search; void *val = NULL; tor_assert(map); tor_assert(key); memcpy(&(search.key), key, sizeof(*key)); resolve = HT_FIND(fp_pair_map_impl, &(map->head), &search); if (resolve) val = resolve->val; return val; } /** Return the current value associated the key (first, second), or * NULL if no value is set. */ void * fp_pair_map_get_by_digests(const fp_pair_map_t *map, const char *first, const char *second) { fp_pair_t k; tor_assert(first); tor_assert(second); memcpy(k.first, first, DIGEST_LEN); memcpy(k.second, second, DIGEST_LEN); return fp_pair_map_get(map, &k); } /** Remove the value currently associated with key from the map. * Return the value if one was set, or NULL if there was no entry for * key. The caller must free any storage associated with the * returned value. */ void * fp_pair_map_remove(fp_pair_map_t *map, const fp_pair_t *key) { fp_pair_map_entry_t *resolve; fp_pair_map_entry_t search; void *val = NULL; tor_assert(map); tor_assert(key); memcpy(&(search.key), key, sizeof(*key)); resolve = HT_REMOVE(fp_pair_map_impl, &(map->head), &search); if (resolve) { val = resolve->val; tor_free(resolve); } return val; } /** Remove all entries from map, and deallocate storage for those entries. * If free_val is provided, it is invoked on every value in map. */ void fp_pair_map_free(fp_pair_map_t *map, void (*free_val)(void*)) { fp_pair_map_entry_t **ent, **next, *this; if (map) { for (ent = HT_START(fp_pair_map_impl, &(map->head)); ent != NULL; ent = next) { this = *ent; next = HT_NEXT_RMV(fp_pair_map_impl, &(map->head), ent); if (free_val) free_val(this->val); tor_free(this); } tor_assert(HT_EMPTY(&(map->head))); HT_CLEAR(fp_pair_map_impl, &(map->head)); tor_free(map); } } /** Return true iff map has no entries. */ int fp_pair_map_isempty(const fp_pair_map_t *map) { tor_assert(map); return HT_EMPTY(&(map->head)); } /** Return the number of items in map. */ int fp_pair_map_size(const fp_pair_map_t *map) { tor_assert(map); return HT_SIZE(&(map->head)); } /** return an iterator pointing to the start of map. */ fp_pair_map_iter_t * fp_pair_map_iter_init(fp_pair_map_t *map) { tor_assert(map); return HT_START(fp_pair_map_impl, &(map->head)); } /** Advance iter a single step to the next entry of map, and return * its new value. */ fp_pair_map_iter_t * fp_pair_map_iter_next(fp_pair_map_t *map, fp_pair_map_iter_t *iter) { tor_assert(map); tor_assert(iter); return HT_NEXT(fp_pair_map_impl, &(map->head), iter); } /** Advance iter a single step to the next entry of map, removing the current * entry, and return its new value. */ fp_pair_map_iter_t * fp_pair_map_iter_next_rmv(fp_pair_map_t *map, fp_pair_map_iter_t *iter) { fp_pair_map_entry_t *rmv; tor_assert(map); tor_assert(iter); tor_assert(*iter); rmv = *iter; iter = HT_NEXT_RMV(fp_pair_map_impl, &(map->head), iter); tor_free(rmv); return iter; } /** Set *key_out and *val_out to the current entry pointed to by iter. */ void fp_pair_map_iter_get(fp_pair_map_iter_t *iter, fp_pair_t *key_out, void **val_out) { tor_assert(iter); tor_assert(*iter); if (key_out) memcpy(key_out, &((*iter)->key), sizeof(fp_pair_t)); if (val_out) *val_out = (*iter)->val; } /** Return true iff iter has advanced past the last entry of its map. */ int fp_pair_map_iter_done(fp_pair_map_iter_t *iter) { return (iter == NULL); } /** Assert if anything has gone wrong with the internal * representation of map. */ void fp_pair_map_assert_ok(const fp_pair_map_t *map) { tor_assert(!fp_pair_map_impl_HT_REP_IS_BAD_(&(map->head))); }