📄 routerparse.c
字号:
if (router_get_runningrouters_hash(str, digest)) {
log_warn(LD_DIR, "Unable to compute digest of running-routers");
goto err;
}
area = memarea_new(8192);
tokens = smartlist_create();
if (tokenize_string(area,str,eos,tokens,dir_token_table,0)) {
log_warn(LD_DIR, "Error tokenizing running-routers"); goto err;
}
tok = smartlist_get(tokens,0);
if (tok->tp != K_NETWORK_STATUS) {
log_warn(LD_DIR, "Network-status starts with wrong token");
goto err;
}
tok = find_first_by_keyword(tokens, K_PUBLISHED);
tor_assert(tok);
tor_assert(tok->n_args == 1);
if (parse_iso_time(tok->args[0], &published_on) < 0) {
goto err;
}
if (!(tok = find_first_by_keyword(tokens, K_DIRECTORY_SIGNATURE))) {
log_warn(LD_DIR, "Missing signature on running-routers");
goto err;
}
declared_key = find_dir_signing_key(str, eos);
note_crypto_pk_op(VERIFY_DIR);
if (check_signature_token(digest, tok, declared_key, 1, "running-routers")
< 0)
goto err;
/* Now that we know the signature is okay, and we have a
* publication time, cache the list. */
if (get_options()->DirPort && !authdir_mode_v1(get_options()))
dirserv_set_cached_directory(str, published_on, 1);
r = 0;
err:
if (declared_key) crypto_free_pk_env(declared_key);
if (tokens) {
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
smartlist_free(tokens);
}
if (area) {
DUMP_AREA(area, "v1 running-routers");
memarea_drop_all(area);
}
return r;
}
/** Given a directory or running-routers string in <b>str</b>, try to
* find the its dir-signing-key token (if any). If this token is
* present, extract and return the key. Return NULL on failure. */
static crypto_pk_env_t *
find_dir_signing_key(const char *str, const char *eos)
{
const char *cp;
directory_token_t *tok;
crypto_pk_env_t *key = NULL;
memarea_t *area = NULL;
tor_assert(str);
tor_assert(eos);
/* Is there a dir-signing-key in the directory? */
cp = tor_memstr(str, eos-str, "\nopt dir-signing-key");
if (!cp)
cp = tor_memstr(str, eos-str, "\ndir-signing-key");
if (!cp)
return NULL;
++cp; /* Now cp points to the start of the token. */
area = memarea_new(1024);
tok = get_next_token(area, &cp, eos, dir_token_table);
if (!tok) {
log_warn(LD_DIR, "Unparseable dir-signing-key token");
goto done;
}
if (tok->tp != K_DIR_SIGNING_KEY) {
log_warn(LD_DIR, "Dir-signing-key token did not parse as expected");
goto done;
}
if (tok->key) {
key = tok->key;
tok->key = NULL; /* steal reference. */
} else {
log_warn(LD_DIR, "Dir-signing-key token contained no key");
}
done:
if (tok) token_free(tok);
if (area) {
DUMP_AREA(area, "dir-signing-key token");
memarea_drop_all(area);
}
return key;
}
/** Return true iff <b>key</b> is allowed to sign directories.
*/
static int
dir_signing_key_is_trusted(crypto_pk_env_t *key)
{
char digest[DIGEST_LEN];
if (!key) return 0;
if (crypto_pk_get_digest(key, digest) < 0) {
log_warn(LD_DIR, "Error computing dir-signing-key digest");
return 0;
}
if (!router_digest_is_trusted_dir(digest)) {
log_warn(LD_DIR, "Listed dir-signing-key is not trusted");
return 0;
}
return 1;
}
/** Check whether the object body of the token in <b>tok</b> has a good
* signature for <b>digest</b> using key <b>pkey</b>. If
* <b>check_authority</b> is set, make sure that <b>pkey</b> is the key of a
* directory authority. Use <b>doctype</b> as the type of the document when
* generating log messages. Return 0 on success, negative on failure.
*/
static int
check_signature_token(const char *digest,
directory_token_t *tok,
crypto_pk_env_t *pkey,
int check_authority,
const char *doctype)
{
char *signed_digest;
tor_assert(pkey);
tor_assert(tok);
tor_assert(digest);
tor_assert(doctype);
if (check_authority && !dir_signing_key_is_trusted(pkey)) {
log_warn(LD_DIR, "Key on %s did not come from an authority; rejecting",
doctype);
return -1;
}
if (strcmp(tok->object_type, "SIGNATURE")) {
log_warn(LD_DIR, "Bad object type on %s signature", doctype);
return -1;
}
signed_digest = tor_malloc(tok->object_size);
if (crypto_pk_public_checksig(pkey, signed_digest, tok->object_body,
tok->object_size)
!= DIGEST_LEN) {
log_warn(LD_DIR, "Error reading %s: invalid signature.", doctype);
tor_free(signed_digest);
return -1;
}
// log_debug(LD_DIR,"Signed %s hash starts %s", doctype,
// hex_str(signed_digest,4));
if (memcmp(digest, signed_digest, DIGEST_LEN)) {
log_warn(LD_DIR, "Error reading %s: signature does not match.", doctype);
tor_free(signed_digest);
return -1;
}
tor_free(signed_digest);
return 0;
}
/** Helper: move *<b>s_ptr</b> ahead to the next router, the next extra-info,
* or to the first of the annotations proceeding the next router or
* extra-info---whichever comes first. Set <b>is_extrainfo_out</b> to true if
* we found an extrainfo, or false if found a router. Do not scan beyond
* <b>eos</b>. Return -1 if we found nothing; 0 if we found something. */
static int
find_start_of_next_router_or_extrainfo(const char **s_ptr,
const char *eos,
int *is_extrainfo_out)
{
const char *annotations = NULL;
const char *s = *s_ptr;
s = eat_whitespace_eos(s, eos);
while (s < eos-32) { /* 32 gives enough room for a the first keyword. */
/* We're at the start of a line. */
tor_assert(*s != '\n');
if (*s == '@' && !annotations) {
annotations = s;
} else if (*s == 'r' && !strcmpstart(s, "router ")) {
*s_ptr = annotations ? annotations : s;
*is_extrainfo_out = 0;
return 0;
} else if (*s == 'e' && !strcmpstart(s, "extra-info ")) {
*s_ptr = annotations ? annotations : s;
*is_extrainfo_out = 1;
return 0;
}
if (!(s = memchr(s+1, '\n', eos-(s+1))))
break;
s = eat_whitespace_eos(s, eos);
}
return -1;
}
/** Given a string *<b>s</b> containing a concatenated sequence of router
* descriptors (or extra-info documents if <b>is_extrainfo</b> is set), parses
* them and stores the result in <b>dest</b>. All routers are marked running
* and valid. Advances *s to a point immediately following the last router
* entry. Ignore any trailing router entries that are not complete.
*
* If <b>saved_location</b> isn't SAVED_IN_CACHE, make a local copy of each
* descriptor in the signed_descriptor_body field of each routerinfo_t. If it
* isn't SAVED_NOWHERE, remember the offset of each descriptor.
*
* Returns 0 on success and -1 on failure.
*/
int
router_parse_list_from_string(const char **s, const char *eos,
smartlist_t *dest,
saved_location_t saved_location,
int want_extrainfo,
int allow_annotations,
const char *prepend_annotations)
{
routerinfo_t *router;
extrainfo_t *extrainfo;
signed_descriptor_t *signed_desc;
void *elt;
const char *end, *start;
int have_extrainfo;
tor_assert(s);
tor_assert(*s);
tor_assert(dest);
start = *s;
if (!eos)
eos = *s + strlen(*s);
tor_assert(eos >= *s);
while (1) {
if (find_start_of_next_router_or_extrainfo(s, eos, &have_extrainfo) < 0)
break;
end = tor_memstr(*s, eos-*s, "\nrouter-signature");
if (end)
end = tor_memstr(end, eos-end, "\n-----END SIGNATURE-----\n");
if (end)
end += strlen("\n-----END SIGNATURE-----\n");
if (!end)
break;
elt = NULL;
if (have_extrainfo && want_extrainfo) {
routerlist_t *rl = router_get_routerlist();
extrainfo = extrainfo_parse_entry_from_string(*s, end,
saved_location != SAVED_IN_CACHE,
rl->identity_map);
if (extrainfo) {
signed_desc = &extrainfo->cache_info;
elt = extrainfo;
}
} else if (!have_extrainfo && !want_extrainfo) {
router = router_parse_entry_from_string(*s, end,
saved_location != SAVED_IN_CACHE,
allow_annotations,
prepend_annotations);
if (router) {
log_debug(LD_DIR, "Read router '%s', purpose '%s'",
router->nickname, router_purpose_to_string(router->purpose));
signed_desc = &router->cache_info;
elt = router;
}
}
if (!elt) {
*s = end;
continue;
}
if (saved_location != SAVED_NOWHERE) {
signed_desc->saved_location = saved_location;
signed_desc->saved_offset = *s - start;
}
*s = end;
smartlist_add(dest, elt);
}
return 0;
}
/* For debugging: define to count every descriptor digest we've seen so we
* know if we need to try harder to avoid duplicate verifies. */
#undef COUNT_DISTINCT_DIGESTS
#ifdef COUNT_DISTINCT_DIGESTS
static digestmap_t *verified_digests = NULL;
#endif
/** Log the total count of the number of distinct router digests we've ever
* verified. When compared to the number of times we've verified routerdesc
* signatures <i>in toto</i>, this will tell us if we're doing too much
* multiple-verification. */
void
dump_distinct_digest_count(int severity)
{
#ifdef COUNT_DISTINCT_DIGESTS
if (!verified_digests)
verified_digests = digestmap_new();
log(severity, LD_GENERAL, "%d *distinct* router digests verified",
digestmap_size(verified_digests));
#else
(void)severity; /* suppress "unused parameter" warning */
#endif
}
/** Helper function: reads a single router entry from *<b>s</b> ...
* *<b>end</b>. Mallocs a new router and returns it if all goes well, else
* returns NULL. If <b>cache_copy</b> is true, duplicate the contents of
* s through end into the signed_descriptor_body of the resulting
* routerinfo_t.
*
* If <b>allow_annotations</b>, it's okay to encounter annotations in <b>s</b>
* before the router; if it's false, reject the router if it's annotated. If
* <b>prepend_annotations</b> is set, it should contain some annotations:
* append them to the front of the router before parsing it, and keep them
* around when caching the router.
*
* Only one of allow_annotations and prepend_annotations may be set.
*/
routerinfo_t *
router_parse_entry_from_string(const char *s, const char *end,
int cache_copy, int allow_annotations,
const char *prepend_annotations)
{
routerinfo_t *router = NULL;
char digest[128];
smartlist_t *tokens = NULL, *exit_policy_tokens = NULL;
directory_token_t *tok;
struct in_addr in;
const char *start_of_annotations, *cp;
size_t prepend_len = prepend_annotations ? strlen(prepend_annotations) : 0;
int ok = 1;
memarea_t *area = NULL;
tor_assert(!allow_annotations || !prepend_annotations);
if (!end) {
end = s + strlen(s);
}
/* point 'end' to a point immediately after the final newline. */
while (end > s+2 && *(end-1) == '\n' && *(end-2) == '\n')
--end;
area = memarea_new(4096);
tokens = smartlist_create();
if (prepend_annotations) {
if (tokenize_string(area,prepend_annotations,NULL,tokens,
routerdesc_token_table,TS_NOCHECK)) {
log_warn(LD_DIR, "Error tokenizing router descriptor (annotations).");
goto err;
}
}
start_of_annotations = s;
cp = tor_memstr(s, end-s, "\nrouter ");
if (!cp) {
if (end-s < 7 || strcmpstart(s, "router ")) {
log_warn(LD_DIR, "No router keyword found.");
goto err;
}
} else {
s = cp+1;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -