Try to extract as many descriptors as possible from truncated http responses. (when...
[tor.git] / src / or / routerparse.c
blob721632c1c5e12c399ba9a1a710500a302189fecc
1 /* Copyright 2001 Matej Pfajfar.
2 * Copyright 2001-2004 Roger Dingledine.
3 * Copyright 2004-2005 Roger Dingledine, Nick Mathewson. */
4 /* See LICENSE for licensing information */
5 /* $Id$ */
6 const char routerparse_c_id[] = "$Id$";
8 /**
9 * \file routerparse.c
10 * \brief Code to parse and validate router descriptors and directories.
11 **/
13 #include "or.h"
15 /****************************************************************************/
17 /** Enumeration of possible token types. The ones starting with K_
18 * correspond to directory 'keywords'. _UNRECOGNIZED is for an
19 * unrecognized keyword; _ERR is an error in the tokenizing process,
20 * _EOF is an end-of-file marker, and _NIL is used to encode
21 * not-a-token.
23 typedef enum {
24 K_ACCEPT,
25 K_DIRECTORY_SIGNATURE,
26 K_RECOMMENDED_SOFTWARE,
27 K_REJECT,
28 K_ROUTER,
29 K_SIGNED_DIRECTORY,
30 K_SIGNING_KEY,
31 K_ONION_KEY,
32 K_ROUTER_SIGNATURE,
33 K_PUBLISHED,
34 K_RUNNING_ROUTERS,
35 K_ROUTER_STATUS,
36 K_PLATFORM,
37 K_OPT,
38 K_BANDWIDTH,
39 K_PORTS,
40 K_CONTACT,
41 K_NETWORK_STATUS,
42 K_UPTIME,
43 K_DIR_SIGNING_KEY,
44 K_FAMILY,
45 K_FINGERPRINT,
46 K_HIBERNATING,
47 K_READ_HISTORY,
48 K_WRITE_HISTORY,
49 K_NETWORK_STATUS_VERSION,
50 K_DIR_SOURCE,
51 K_DIR_OPTIONS,
52 K_CLIENT_VERSIONS,
53 K_SERVER_VERSIONS,
54 K_R,
55 K_S,
56 _UNRECOGNIZED,
57 _ERR,
58 _EOF,
59 _NIL
60 } directory_keyword;
62 /** Structure to hold a single directory token.
64 * We parse a directory by breaking it into "tokens", each consisting
65 * of a keyword, a line full of arguments, and a binary object. The
66 * arguments and object are both optional, depending on the keyword
67 * type.
69 typedef struct directory_token_t {
70 directory_keyword tp; /**< Type of the token. */
71 int n_args; /**< Number of elements in args */
72 char **args; /**< Array of arguments from keyword line. */
73 char *object_type; /**< -----BEGIN [object_type]-----*/
74 size_t object_size; /**< Bytes in object_body */
75 char *object_body; /**< Contents of object, base64-decoded. */
76 crypto_pk_env_t *key; /**< For public keys only. */
77 const char *error; /**< For _ERR tokens only. */
78 } directory_token_t;
80 /* ********************************************************************** */
82 /** We use a table of rules to decide how to parse each token type. */
84 /** Rules for how many arguments a keyword can take. */
85 typedef enum {
86 NO_ARGS, /**< No arguments, ever. */
87 ARGS, /**< A list of arguments separated by spaces. */
88 CONCAT_ARGS, /**< The rest of the line, treated as a single argument. */
89 } arg_syntax;
91 /** Rules for whether the keyword needs an object. */
92 typedef enum {
93 NO_OBJ, /**< No object, ever. */
94 NEED_OBJ, /**< Object is required. */
95 NEED_KEY, /**< Object is required, and must be a public key. */
96 OBJ_OK, /**< Object is optional. */
97 } obj_syntax;
99 /** Rules for where a keyword can appear. */
100 typedef enum {
101 DIR = 1, /**< Appears only in directory. */
102 RTR = 2, /**< Appears only in router descriptor or runningrouters. */
103 NETSTATUS = 4, /**< v2 or later ("versioned") network status. */
104 ANYSIGNED = 7, /**< Any "full" document (that is, not a router status.) */
105 RTRSTATUS = 8, /**< Router-status portion of a versioned network status. */
106 ANY = 15, /**< Appears in any document type. */
107 } where_syntax;
109 /** Table mapping keywords to token value and to argument rules. */
110 static struct {
111 const char *t; int v; arg_syntax s; obj_syntax os; int ws;
112 } token_table[] = {
113 { "accept", K_ACCEPT, ARGS, NO_OBJ, RTR },
114 { "directory-signature", K_DIRECTORY_SIGNATURE, ARGS, NEED_OBJ,
115 DIR|NETSTATUS},
116 { "r", K_R, ARGS, NO_OBJ, RTRSTATUS },
117 { "s", K_S, ARGS, NO_OBJ, RTRSTATUS },
118 { "reject", K_REJECT, ARGS, NO_OBJ, RTR },
119 { "router", K_ROUTER, ARGS, NO_OBJ, RTR },
120 { "recommended-software",K_RECOMMENDED_SOFTWARE,ARGS, NO_OBJ, DIR },
121 { "signed-directory", K_SIGNED_DIRECTORY, NO_ARGS, NO_OBJ, DIR },
122 { "signing-key", K_SIGNING_KEY, NO_ARGS, NEED_KEY,RTR },
123 { "onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY,RTR },
124 { "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ,RTR },
125 { "running-routers", K_RUNNING_ROUTERS, ARGS, NO_OBJ, DIR },
126 { "router-status", K_ROUTER_STATUS, ARGS, NO_OBJ, DIR },
127 { "ports", K_PORTS, ARGS, NO_OBJ, RTR },
128 { "bandwidth", K_BANDWIDTH, ARGS, NO_OBJ, RTR },
129 { "platform", K_PLATFORM, CONCAT_ARGS, NO_OBJ, RTR },
130 { "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ, ANYSIGNED },
131 { "opt", K_OPT, CONCAT_ARGS, OBJ_OK, ANY },
132 { "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ, ANYSIGNED },
133 { "network-status", K_NETWORK_STATUS, NO_ARGS, NO_OBJ, DIR },
134 { "uptime", K_UPTIME, ARGS, NO_OBJ, RTR },
135 { "dir-signing-key", K_DIR_SIGNING_KEY, ARGS, OBJ_OK,
136 DIR|NETSTATUS},
137 { "family", K_FAMILY, ARGS, NO_OBJ, RTR },
138 { "fingerprint", K_FINGERPRINT, ARGS, NO_OBJ, ANYSIGNED },
139 { "hibernating", K_HIBERNATING, ARGS, NO_OBJ, RTR },
140 { "read-history", K_READ_HISTORY, ARGS, NO_OBJ, RTR },
141 { "write-history", K_WRITE_HISTORY, ARGS, NO_OBJ, RTR },
142 { "network-status-version", K_NETWORK_STATUS_VERSION,
143 ARGS, NO_OBJ, NETSTATUS },
144 { "dir-source", K_DIR_SOURCE, ARGS, NO_OBJ, NETSTATUS },
145 { "dir-options", K_DIR_OPTIONS, ARGS, NO_OBJ, NETSTATUS },
146 { "client-versions", K_CLIENT_VERSIONS, ARGS, NO_OBJ, NETSTATUS },
147 { "server-versions", K_SERVER_VERSIONS, ARGS, NO_OBJ, NETSTATUS },
148 { NULL, -1, NO_ARGS, NO_OBJ, ANY }
151 /* static function prototypes */
152 static int router_add_exit_policy(routerinfo_t *router,directory_token_t *tok);
153 static addr_policy_t *router_parse_addr_policy(directory_token_t *tok);
154 static int router_get_hash_impl(const char *s, char *digest,
155 const char *start_str, const char *end_str);
156 static void token_free(directory_token_t *tok);
157 static smartlist_t *find_all_exitpolicy(smartlist_t *s);
158 static directory_token_t *find_first_by_keyword(smartlist_t *s,
159 directory_keyword keyword);
160 static int tokenize_string(const char *start, const char *end,
161 smartlist_t *out, where_syntax where);
162 static directory_token_t *get_next_token(const char **s, where_syntax where);
163 static int check_directory_signature(const char *digest,
164 directory_token_t *tok,
165 crypto_pk_env_t *pkey,
166 crypto_pk_env_t *declared_key,
167 int check_authority);
168 static crypto_pk_env_t *find_dir_signing_key(const char *str);
169 static int tor_version_same_series(tor_version_t *a, tor_version_t *b);
171 /** Set <b>digest</b> to the SHA-1 digest of the hash of the directory in
172 * <b>s</b>. Return 0 on success, -1 on failure.
175 router_get_dir_hash(const char *s, char *digest)
177 return router_get_hash_impl(s,digest,
178 "signed-directory","\ndirectory-signature");
181 /** Set <b>digest</b> to the SHA-1 digest of the hash of the first router in
182 * <b>s</b>. Return 0 on success, -1 on failure.
185 router_get_router_hash(const char *s, char *digest)
187 return router_get_hash_impl(s,digest,
188 "router ","\nrouter-signature");
191 /** Set <b>digest</b> to the SHA-1 digest of the hash of the running-routers
192 * string in <b>s</b>. Return 0 on success, -1 on failure.
195 router_get_runningrouters_hash(const char *s, char *digest)
197 return router_get_hash_impl(s,digest,
198 "network-status","\ndirectory-signature");
201 /** Set <b>digest</b> to the SHA-1 digest of the hash of the network-status
202 * string in <b>s</b>. Return 0 on success, -1 on failure. */
204 router_get_networkstatus_v2_hash(const char *s, char *digest)
206 return router_get_hash_impl(s,digest,
207 "network-status-version","\ndirectory-signature");
210 /** Helper: used to generate signatures for routers, directories and
211 * network-status objects. Given a digest in <b>digest</b> and a secret
212 * <b>private_key</b>, generate an PKCS1-padded signature, BASE64-encode it,
213 * surround it with -----BEGIN/END----- pairs, and write it to the
214 * <b>buf_len</b>-byte buffer at <b>buf</b>. Return 0 on success, -1 on
215 * failure.
218 router_append_dirobj_signature(char *buf, size_t buf_len, const char *digest,
219 crypto_pk_env_t *private_key)
221 char signature[PK_BYTES];
222 int i;
224 if (crypto_pk_private_sign(private_key, signature, digest, DIGEST_LEN) < 0) {
226 log_fn(LOG_WARN,"Couldn't sign digest.");
227 return -1;
229 if (strlcat(buf, "-----BEGIN SIGNATURE-----\n", buf_len) >= buf_len)
230 goto truncated;
232 i = strlen(buf);
233 if (base64_encode(buf+i, buf_len-i, signature, 128) < 0) {
234 log_fn(LOG_WARN,"couldn't base64-encode signature");
235 tor_free(buf);
236 return -1;
239 if (strlcat(buf, "-----END SIGNATURE-----\n", buf_len) >= buf_len)
240 goto truncated;
242 return 0;
243 truncated:
244 log_fn(LOG_WARN,"tried to exceed string length.");
245 return -1;
248 /** Return VS_RECOMMENDED if <b>myversion</b> is contained in
249 * <b>versionlist</b>. Else, return VS_OLD if every member of
250 * <b>versionlist</b> is newer than <b>myversion</b>. Else, return
251 * VS_NEW_IN_SERIES if there is at least one member of <b>versionlist</b> in
252 * the same series (major.minor.micro) as <b>myversion</b>, but no such member
253 * is newer than <b>myversion.</b>. Else, return VS_NEW if every memeber of
254 * <b>versionlist</b> is older than <b>myversion</b>. Else, return
255 * VS_UNRECOMMENDED.
257 * (versionlist is a comma-separated list of version strings,
258 * optionally prefixed with "Tor". Versions that can't be parsed are
259 * ignored.)
261 version_status_t
262 tor_version_is_obsolete(const char *myversion, const char *versionlist)
264 const char *vl;
265 tor_version_t mine, other;
266 int found_newer = 0, found_older = 0, found_newer_in_series = 0,
267 found_any_in_series = 0, r, same;
268 version_status_t ret = VS_UNRECOMMENDED;
269 smartlist_t *version_sl;
271 vl = versionlist;
273 log_fn(LOG_DEBUG,"Checking whether version '%s' is in '%s'",
274 myversion, versionlist);
276 if (tor_version_parse(myversion, &mine)) {
277 log_fn(LOG_ERR, "I couldn't parse my own version (%s)", myversion);
278 tor_assert(0);
280 version_sl = smartlist_create();
281 smartlist_split_string(version_sl, versionlist, ",", SPLIT_SKIP_SPACE, 0);
283 SMARTLIST_FOREACH(version_sl, const char *, cp, {
284 if (!strcmpstart(cp, "Tor "))
285 cp += 4;
287 if (tor_version_parse(cp, &other)) {
288 /* Couldn't parse other; it can't be a match. */
289 } else {
290 same = tor_version_same_series(&mine, &other);
291 if (same)
292 found_any_in_series = 1;
293 r = tor_version_compare(&mine, &other);
294 if (r==0) {
295 ret = VS_RECOMMENDED;
296 goto done;
297 } else if (r<0) {
298 found_newer = 1;
299 if (same)
300 found_newer_in_series = 1;
301 } else if (r>0) {
302 found_older = 1;
307 /* We didn't find the listed version. Is it new or old? */
308 if (found_any_in_series && !found_newer_in_series) {
309 ret = VS_NEW_IN_SERIES;
310 } else if (found_newer && !found_older) {
311 ret = VS_OLD;
312 } else if (found_older && !found_newer) {
313 ret = VS_NEW;
314 } else {
315 ret = VS_UNRECOMMENDED;
318 done:
319 SMARTLIST_FOREACH(version_sl, char *, version, tor_free(version));
320 smartlist_free(version_sl);
321 return ret;
324 /** Return the combined status of the current version, given that we know of
325 * one set of networkstatuses that give us status <b>a</b>, and another that
326 * gives us status <b>b</b>.
328 * For example, if one authority thinks that we're NEW, and another thinks
329 * we're OLD, we're simply UNRECOMMENDED.
331 * This function does not handle calculating whether we're RECOMMENDED; that
332 * follows a simple majority rule. This function simply calculates *why*
333 * we're not recommended (if we're not).
335 version_status_t
336 version_status_join(version_status_t a, version_status_t b)
338 if (a == b)
339 return a;
340 else if (a == VS_UNRECOMMENDED || b == VS_UNRECOMMENDED)
341 return VS_UNRECOMMENDED;
342 else if (a == VS_RECOMMENDED)
343 return b;
344 else if (b == VS_RECOMMENDED)
345 return a;
346 /* Okay. Neither is 'recommended' or 'unrecommended', and they differ. */
347 else if (a == VS_OLD || b == VS_OLD)
348 return VS_UNRECOMMENDED;
349 /* One is VS_NEW, the other is VS_NEW_IN_SERIES */
350 else
351 return VS_NEW_IN_SERIES;
354 /** Read a signed directory from <b>str</b>. If it's well-formed, return 0.
355 * Otherwise, return -1. If we're a directory cache, cache it.
358 router_parse_directory(const char *str)
360 directory_token_t *tok;
361 char digest[DIGEST_LEN];
362 time_t published_on;
363 int r;
364 const char *end, *cp;
365 smartlist_t *tokens = NULL;
366 crypto_pk_env_t *declared_key = NULL;
368 /* XXXX This could be simplified a lot, but it will all go away
369 * once pre-0.1.1.8 is obsolete, and for now it's better not to
370 * tuoch it. */
372 if (router_get_dir_hash(str, digest)) {
373 log_fn(LOG_WARN, "Unable to compute digest of directory");
374 goto err;
376 log_fn(LOG_DEBUG,"Received directory hashes to %s",hex_str(digest,4));
378 /* Check signature first, before we try to tokenize. */
379 cp = str;
380 while (cp && (end = strstr(cp+1, "\ndirectory-signature")))
381 cp = end;
382 if (cp == str || !cp) {
383 log_fn(LOG_WARN, "No signature found on directory."); goto err;
385 ++cp;
386 tokens = smartlist_create();
387 if (tokenize_string(cp,strchr(cp,'\0'),tokens,DIR)) {
388 log_fn(LOG_WARN, "Error tokenizing directory signature"); goto err;
390 if (smartlist_len(tokens) != 1) {
391 log_fn(LOG_WARN, "Unexpected number of tokens in signature"); goto err;
393 tok=smartlist_get(tokens,0);
394 if (tok->tp != K_DIRECTORY_SIGNATURE) {
395 log_fn(LOG_WARN,"Expected a single directory signature"); goto err;
397 declared_key = find_dir_signing_key(str);
398 if (check_directory_signature(digest, tok, NULL, declared_key, 1)<0)
399 goto err;
401 SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok));
402 smartlist_free(tokens);
403 tokens = NULL;
405 /* Now try to parse the first part of the directory. */
406 if ((end = strstr(str,"\nrouter "))) {
407 ++end;
408 } else if ((end = strstr(str, "\ndirectory-signature"))) {
409 ++end;
410 } else {
411 end = str + strlen(str);
414 tokens = smartlist_create();
415 if (tokenize_string(str,end,tokens,DIR)) {
416 log_fn(LOG_WARN, "Error tokenizing directory"); goto err;
419 if (!(tok = find_first_by_keyword(tokens, K_PUBLISHED))) {
420 log_fn(LOG_WARN, "Missing published time on directory.");
421 goto err;
423 tor_assert(tok->n_args == 1);
425 if (parse_iso_time(tok->args[0], &published_on) < 0) {
426 goto err;
429 /* Now that we know the signature is okay, and we have a
430 * publication time, cache the directory. */
431 if (get_options()->DirPort && !get_options()->V1AuthoritativeDir)
432 dirserv_set_cached_directory(str, published_on, 0);
434 r = 0;
435 goto done;
436 err:
437 r = -1;
438 done:
439 if (declared_key) crypto_free_pk_env(declared_key);
440 if (tokens) {
441 SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok));
442 smartlist_free(tokens);
444 return r;
447 /** Read a signed router status statement from <b>str</b>. If it's
448 * well-formed, return 0. Otherwise, return -1. If we're a directory cache,
449 * cache it.*/
451 router_parse_runningrouters(const char *str)
453 char digest[DIGEST_LEN];
454 directory_token_t *tok;
455 time_t published_on;
456 int r = -1;
457 crypto_pk_env_t *declared_key = NULL;
458 smartlist_t *tokens = NULL;
460 if (router_get_runningrouters_hash(str, digest)) {
461 log_fn(LOG_WARN, "Unable to compute digest of directory");
462 goto err;
464 tokens = smartlist_create();
465 if (tokenize_string(str,str+strlen(str),tokens,DIR)) {
466 log_fn(LOG_WARN, "Error tokenizing directory"); goto err;
468 if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) {
469 log_fn(LOG_WARN, "Unrecognized keyword '%s'; can't parse running-routers",
470 tok->args[0]);
471 goto err;
473 tok = smartlist_get(tokens,0);
474 if (tok->tp != K_NETWORK_STATUS) {
475 log_fn(LOG_WARN, "Network-status starts with wrong token");
476 goto err;
479 if (!(tok = find_first_by_keyword(tokens, K_PUBLISHED))) {
480 log_fn(LOG_WARN, "Missing published time on directory.");
481 goto err;
483 tor_assert(tok->n_args == 1);
484 if (parse_iso_time(tok->args[0], &published_on) < 0) {
485 goto err;
487 if (!(tok = find_first_by_keyword(tokens, K_DIRECTORY_SIGNATURE))) {
488 log_fn(LOG_WARN, "Missing signature on running-routers");
489 goto err;
491 declared_key = find_dir_signing_key(str);
492 if (check_directory_signature(digest, tok, NULL, declared_key, 1) < 0)
493 goto err;
495 /* Now that we know the signature is okay, and we have a
496 * publication time, cache the list. */
497 if (get_options()->DirPort && !get_options()->V1AuthoritativeDir)
498 dirserv_set_cached_directory(str, published_on, 1);
500 r = 0;
501 err:
502 if (declared_key) crypto_free_pk_env(declared_key);
503 if (tokens) {
504 SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok));
505 smartlist_free(tokens);
507 return r;
510 /** Given a directory or running-routers string in <b>str</b>, try to
511 * find the its dir-signing-key token (if any). If this token is
512 * present, extract and return the key. Return NULL on failure. */
513 static crypto_pk_env_t *
514 find_dir_signing_key(const char *str)
516 const char *cp;
517 directory_token_t *tok;
518 crypto_pk_env_t *key = NULL;
520 /* Is there a dir-signing-key in the directory? */
521 cp = strstr(str, "\nopt dir-signing-key");
522 if (!cp)
523 cp = strstr(str, "\ndir-signing-key");
524 if (!cp)
525 return NULL;
526 ++cp; /* Now cp points to the start of the token. */
528 tok = get_next_token(&cp, DIR);
529 if (!tok) {
530 log_fn(LOG_WARN, "Unparseable dir-signing-key token");
531 return NULL;
533 if (tok->tp != K_DIR_SIGNING_KEY) {
534 log_fn(LOG_WARN, "Dir-signing-key token did not parse as expected");
535 return NULL;
538 if (tok->key) {
539 key = tok->key;
540 tok->key = NULL; /* steal reference. */
541 } else if (tok->n_args >= 1) {
542 /** XXXX Once all the directories are running 0.1.0.6-rc or later, we
543 * can remove this logic. */
544 key = crypto_pk_DER64_decode_public_key(tok->args[0]);
545 if (!key) {
546 log_fn(LOG_WARN, "Unparseable dir-signing-key argument");
547 return NULL;
549 } else {
550 log_fn(LOG_WARN, "Dir-signing-key token contained no key");
551 return NULL;
554 token_free(tok);
555 return key;
558 /** Return true iff <b>key</b> is allowed to sign directories.
560 static int
561 dir_signing_key_is_trusted(crypto_pk_env_t *key)
563 char digest[DIGEST_LEN];
564 if (!key) return 0;
565 if (crypto_pk_get_digest(key, digest) < 0) {
566 log_fn(LOG_WARN, "Error computing dir-signing-key digest");
567 return 0;
569 if (!router_digest_is_trusted_dir(digest)) {
570 log_fn(LOG_WARN, "Listed dir-signing-key is not trusted");
571 return 0;
573 return 1;
576 /** Check whether the K_DIRECTORY_SIGNATURE token in <b>tok</b> has a
577 * good signature for <b>digest</b>.
579 * If <b>declared_key</b> is set, the directory has declared what key
580 * was used to sign it, so we will use that key only if it is an
581 * authoritative directory signing key or if check_authority is 0.
583 * Otherwise, if pkey is provided, try to use it.
585 * (New callers should always use <b>declared_key</b> when possible;
586 * <b>pkey</b> is only for debugging.)
588 static int
589 check_directory_signature(const char *digest,
590 directory_token_t *tok,
591 crypto_pk_env_t *pkey,
592 crypto_pk_env_t *declared_key,
593 int check_authority)
595 char signed_digest[PK_BYTES];
596 crypto_pk_env_t *_pkey = NULL;
598 if (tok->n_args != 1) {
599 log_fn(LOG_WARN, "Too many or too few arguments to directory-signature");
600 return -1;
603 if (declared_key) {
604 if (!check_authority || dir_signing_key_is_trusted(declared_key))
605 _pkey = declared_key;
607 if (!_pkey && pkey) {
608 /* pkey provided for debugging purposes */
609 _pkey = pkey;
611 if (!_pkey) {
612 log_fn(LOG_WARN, "Obsolete directory format (dir signing key not present) or signing key not trusted--rejecting.");
613 return -1;
616 if (strcmp(tok->object_type, "SIGNATURE") || tok->object_size != 128) {
617 log_fn(LOG_WARN, "Bad object type or length on directory signature");
618 return -1;
621 tor_assert(_pkey);
623 if (crypto_pk_public_checksig(_pkey, signed_digest, tok->object_body, 128)
624 != 20) {
625 log_fn(LOG_WARN, "Error reading directory: invalid signature.");
626 return -1;
628 log_fn(LOG_DEBUG,"Signed directory hash starts %s", hex_str(signed_digest,4));
629 if (memcmp(digest, signed_digest, DIGEST_LEN)) {
630 log_fn(LOG_WARN, "Error reading directory: signature does not match.");
631 return -1;
633 return 0;
636 /** Given a string *<b>s</b> containing a concatenated sequence of router
637 * descriptors, parses them and stores the result in <b>dest</b>. All routers
638 * are marked running and verified. Advances *s to a point immediately
639 * following the last router entry. Ignore any trailing router entries that
640 * are not complete. Returns 0 on success and -1 on failure.
643 router_parse_list_from_string(const char **s, smartlist_t *dest)
645 routerinfo_t *router;
646 const char *end, *cp;
648 tor_assert(s);
649 tor_assert(*s);
650 tor_assert(dest);
652 while (1) {
653 *s = eat_whitespace(*s);
654 /* Don't start parsing the rest of *s unless it contains a router. */
655 if (strcmpstart(*s, "router ")!=0)
656 break;
657 if ((end = strstr(*s+1, "\nrouter "))) {
658 cp = end;
659 end++;
660 } else if ((end = strstr(*s+1, "\ndirectory-signature"))) {
661 cp = end;
662 end++;
663 } else {
664 cp = end = *s+strlen(*s);
667 while (cp > *s && (!*cp || TOR_ISSPACE(*cp)))
668 --cp;
669 /* cp now points to the last non-space character in this descriptor. */
671 while (cp > *s && *cp != '\n')
672 --cp;
673 /* cp now points to the first \n before the last non-bank line in this
674 * descriptor */
676 if (strcmpstart(cp, "\n-----END SIGNATURE-----\n")) {
677 log_fn(LOG_INFO, "Ignoring truncated router descriptor.");
678 continue;
681 router = router_parse_entry_from_string(*s, end);
683 *s = end;
684 if (!router) {
685 log_fn(LOG_WARN, "Error reading router; skipping");
686 continue;
688 smartlist_add(dest, router);
691 return 0;
694 /** Helper function: reads a single router entry from *<b>s</b> ...
695 * *<b>end</b>. Mallocs a new router and returns it if all goes well, else
696 * returns NULL.
698 routerinfo_t *
699 router_parse_entry_from_string(const char *s, const char *end)
701 routerinfo_t *router = NULL;
702 char signed_digest[128];
703 char digest[128];
704 smartlist_t *tokens = NULL, *exit_policy_tokens = NULL;
705 directory_token_t *tok;
706 int t;
707 int ports_set, bw_set;
708 struct in_addr in;
710 if (!end) {
711 end = s + strlen(s);
714 /* point 'end' to a point immediately after the final newline. */
715 while (end > s+2 && *(end-1) == '\n' && *(end-2) == '\n')
716 --end;
718 if (router_get_router_hash(s, digest) < 0) {
719 log_fn(LOG_WARN, "Couldn't compute router hash.");
720 return NULL;
722 tokens = smartlist_create();
723 if (tokenize_string(s,end,tokens,RTR)) {
724 log_fn(LOG_WARN, "Error tokeninzing router descriptor.");
725 goto err;
728 if (smartlist_len(tokens) < 2) {
729 log_fn(LOG_WARN, "Impossibly short router descriptor.");
730 goto err;
732 if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) {
733 log_fn(LOG_INFO, "Unrecognized critical keyword '%s'; skipping descriptor. (It may be from another version of Tor.)",
734 tok->args[0]);
735 goto err;
738 tok = smartlist_get(tokens,0);
739 if (tok->tp != K_ROUTER) {
740 log_fn(LOG_WARN,"Entry does not start with \"router\"");
741 goto err;
744 router = tor_malloc_zero(sizeof(routerinfo_t));
745 router->signed_descriptor = tor_strndup(s, end-s);
746 router->signed_descriptor_len = end-s;
747 memcpy(router->signed_descriptor_digest, digest, DIGEST_LEN);
748 ports_set = bw_set = 0;
750 if (tok->n_args == 2 || tok->n_args == 5 || tok->n_args == 6) {
751 router->nickname = tor_strdup(tok->args[0]);
752 if (!is_legal_nickname(router->nickname)) {
753 log_fn(LOG_WARN,"Router nickname is invalid");
754 goto err;
756 router->address = tor_strdup(tok->args[1]);
757 if (!tor_inet_aton(router->address, &in)) {
758 log_fn(LOG_WARN,"Router address is not an IP.");
759 goto err;
761 router->addr = ntohl(in.s_addr);
763 if (tok->n_args >= 5) {
764 router->or_port = (uint16_t) tor_parse_long(tok->args[2],10,0,65535,NULL,NULL);
765 router->dir_port = (uint16_t) tor_parse_long(tok->args[4],10,0,65535,NULL,NULL);
766 ports_set = 1;
768 } else {
769 log_fn(LOG_WARN,"Wrong # of arguments to \"router\" (%d)",tok->n_args);
770 goto err;
773 tok = find_first_by_keyword(tokens, K_PORTS);
774 if (tok && ports_set) {
775 log_fn(LOG_WARN,"Redundant ports line");
776 goto err;
777 } else if (tok) {
778 if (tok->n_args != 3) {
779 log_fn(LOG_WARN,"Wrong # of arguments to \"ports\"");
780 goto err;
782 router->or_port = (uint16_t) tor_parse_long(tok->args[0],10,0,65535,NULL,NULL);
783 router->dir_port = (uint16_t) tor_parse_long(tok->args[2],10,0,65535,NULL,NULL);
784 ports_set = 1;
787 tok = find_first_by_keyword(tokens, K_BANDWIDTH);
788 if (tok && bw_set) {
789 log_fn(LOG_WARN,"Redundant bandwidth line");
790 goto err;
791 } else if (tok) {
792 if (tok->n_args < 3) {
793 /* XXXX Once 0.0.7 is *really* dead, restore this warning to its old form*/
794 log_fn(LOG_WARN,"Not enough arguments to \"bandwidth\": must be an obsolete server. Rejecting one server (nickname '%s').", router->nickname);
795 goto err;
797 router->bandwidthrate = tor_parse_long(tok->args[0],10,0,INT_MAX,NULL,NULL);
798 router->bandwidthburst = tor_parse_long(tok->args[1],10,0,INT_MAX,NULL,NULL);
799 router->bandwidthcapacity = tor_parse_long(tok->args[2],10,0,INT_MAX,NULL,NULL);
800 bw_set = 1;
803 if ((tok = find_first_by_keyword(tokens, K_UPTIME))) {
804 if (tok->n_args != 1) {
805 log_fn(LOG_WARN, "Unrecognized number of args on K_UPTIME; skipping.");
806 } else {
807 router->uptime = tor_parse_long(tok->args[0],10,0,LONG_MAX,NULL,NULL);
811 if ((tok = find_first_by_keyword(tokens, K_HIBERNATING))) {
812 if (tok->n_args < 1) {
813 log_fn(LOG_WARN, "Too few args on 'hibernating' keyword. Skipping.");
814 } else {
815 router->is_hibernating
816 = (tor_parse_long(tok->args[0],10,0,LONG_MAX,NULL,NULL) != 0);
820 if (!(tok = find_first_by_keyword(tokens, K_PUBLISHED))) {
821 log_fn(LOG_WARN, "Missing published time"); goto err;
823 tor_assert(tok->n_args == 1);
824 if (parse_iso_time(tok->args[0], &router->published_on) < 0)
825 goto err;
827 if (!(tok = find_first_by_keyword(tokens, K_ONION_KEY))) {
828 log_fn(LOG_WARN, "Missing onion key"); goto err;
830 if (crypto_pk_keysize(tok->key) != PK_BYTES) {
831 log_fn(LOG_WARN, "Wrong size on onion key: %d bits!",
832 (int)crypto_pk_keysize(tok->key)*8);
833 goto err;
835 router->onion_pkey = tok->key;
836 tok->key = NULL; /* Prevent free */
838 if (!(tok = find_first_by_keyword(tokens, K_SIGNING_KEY))) {
839 log_fn(LOG_WARN, "Missing identity key"); goto err;
841 if (crypto_pk_keysize(tok->key) != PK_BYTES) {
842 log_fn(LOG_WARN, "Wrong size on identity key: %d bits!",
843 (int)crypto_pk_keysize(tok->key)*8);
844 goto err;
846 router->identity_pkey = tok->key;
847 tok->key = NULL; /* Prevent free */
848 if (crypto_pk_get_digest(router->identity_pkey,router->identity_digest)) {
849 log_fn(LOG_WARN, "Couldn't calculate key digest"); goto err;
852 if ((tok = find_first_by_keyword(tokens, K_PLATFORM))) {
853 router->platform = tor_strdup(tok->args[0]);
856 if ((tok = find_first_by_keyword(tokens, K_CONTACT))) {
857 router->contact_info = tor_strdup(tok->args[0]);
860 exit_policy_tokens = find_all_exitpolicy(tokens);
861 SMARTLIST_FOREACH(exit_policy_tokens, directory_token_t *, t,
862 if (router_add_exit_policy(router,t)<0) {
863 log_fn(LOG_WARN,"Error in exit policy");
864 goto err;
867 if ((tok = find_first_by_keyword(tokens, K_FAMILY)) && tok->n_args) {
868 int i;
869 router->declared_family = smartlist_create();
870 for (i=0;i<tok->n_args;++i) {
871 if (!is_legal_nickname_or_hexdigest(tok->args[i])) {
872 log_fn(LOG_WARN, "Illegal nickname '%s' in family line", tok->args[i]);
873 goto err;
875 smartlist_add(router->declared_family, tor_strdup(tok->args[i]));
879 if (!(tok = find_first_by_keyword(tokens, K_ROUTER_SIGNATURE))) {
880 log_fn(LOG_WARN, "Missing router signature");
881 goto err;
883 if (strcmp(tok->object_type, "SIGNATURE") || tok->object_size != 128) {
884 log_fn(LOG_WARN, "Bad object type or length on router signature");
885 goto err;
887 if ((t=crypto_pk_public_checksig(router->identity_pkey, signed_digest,
888 tok->object_body, 128)) != 20) {
889 log_fn(LOG_WARN, "Invalid signature %d",t);
890 goto err;
892 if (memcmp(digest, signed_digest, DIGEST_LEN)) {
893 log_fn(LOG_WARN, "Mismatched signature");
894 goto err;
897 if (!ports_set) {
898 log_fn(LOG_WARN,"No ports declared; failing.");
899 goto err;
901 if (!bw_set) {
902 log_fn(LOG_WARN,"No bandwidth declared; failing.");
903 goto err;
905 if (!router->or_port) {
906 log_fn(LOG_WARN,"or_port unreadable or 0. Failing.");
907 goto err;
909 if (!router->bandwidthrate) {
910 log_fn(LOG_WARN,"bandwidthrate unreadable or 0. Failing.");
911 goto err;
913 if (!router->platform) {
914 router->platform = tor_strdup("<unknown>");
917 goto done;
919 err:
920 routerinfo_free(router);
921 router = NULL;
922 done:
923 if (tokens) {
924 SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok));
925 smartlist_free(tokens);
927 if (exit_policy_tokens) {
928 smartlist_free(exit_policy_tokens);
930 return router;
933 /** Helper: given a string <b>s</b>, return the start of the next router-status
934 * object (starting with "r " at the start of a line). If none is found,
935 * return the start of the next directory signature. If none is found, return
936 * the end of the string. */
937 static INLINE const char *
938 find_start_of_next_routerstatus(const char *s)
940 const char *eos = strstr(s, "\nr ");
941 if (!eos)
942 eos = strstr(s, "\ndirectory-signature");
943 if (eos)
944 return eos+1;
945 else
946 return s + strlen(s);
949 /** Given a string at *<b>s</b>, containing a routerstatus object, and an
950 * empty smartlist at <b>tokens</b>, parse and return the first router status
951 * object in the string, and advance *<b>s</b> to just after the end of the
952 * router status. Return NULL and advance *<b>s</b> on error. */
953 static routerstatus_t *
954 routerstatus_parse_entry_from_string(const char **s, smartlist_t *tokens)
956 const char *eos;
957 routerstatus_t *rs = NULL;
958 directory_token_t *tok;
959 char timebuf[ISO_TIME_LEN+1];
960 struct in_addr in;
962 tor_assert(tokens);
964 eos = find_start_of_next_routerstatus(*s);
966 if (tokenize_string(*s, eos, tokens, RTRSTATUS)) {
967 log_fn(LOG_WARN, "Error tokenizing router status");
968 goto err;
970 if (smartlist_len(tokens) < 1) {
971 log_fn(LOG_WARN, "Impossibly short router status");
972 goto err;
974 if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) {
975 log_fn(LOG_WARN, "Unrecognized keyword \"%s\" in router status; skipping.",
976 tok->args[0]);
977 goto err;
979 if (!(tok = find_first_by_keyword(tokens, K_R))) {
980 log_fn(LOG_WARN, "Missing 'r' keywork in router status; skipping.");
981 goto err;
983 if (tok->n_args < 8) {
984 log_fn(LOG_WARN,
985 "Too few arguments to 'r' keywork in router status; skipping.");
987 rs = tor_malloc_zero(sizeof(routerstatus_t));
989 if (!is_legal_nickname(tok->args[0])) {
990 log_fn(LOG_WARN,
991 "Invalid nickname '%s' in router status; skipping.", tok->args[0]);
992 goto err;
994 strlcpy(rs->nickname, tok->args[0], sizeof(rs->nickname));
996 if (digest_from_base64(rs->identity_digest, tok->args[1])) {
997 log_fn(LOG_WARN, "Error decoding digest '%s'", tok->args[1]);
998 goto err;
1001 if (digest_from_base64(rs->descriptor_digest, tok->args[2])) {
1002 log_fn(LOG_WARN, "Error decoding digest '%s'", tok->args[2]);
1003 goto err;
1006 if (tor_snprintf(timebuf, sizeof(timebuf), "%s %s",
1007 tok->args[3], tok->args[4]) < 0 ||
1008 parse_iso_time(timebuf, &rs->published_on)<0) {
1009 log_fn(LOG_WARN, "Error parsing time '%s %s'", tok->args[3], tok->args[4]);
1010 goto err;
1013 if (tor_inet_aton(tok->args[5], &in) == 0) {
1014 log_fn(LOG_WARN, "Error parsing address '%s'", tok->args[5]);
1015 goto err;
1017 rs->addr = ntohl(in.s_addr);
1019 rs->or_port =(uint16_t) tor_parse_long(tok->args[6],10,0,65535,NULL,NULL);
1020 rs->dir_port = (uint16_t) tor_parse_long(tok->args[7],10,0,65535,NULL,NULL);
1022 if ((tok = find_first_by_keyword(tokens, K_S))) {
1023 int i;
1024 for (i=0; i < tok->n_args; ++i) {
1025 if (!strcmp(tok->args[i], "Exit"))
1026 rs->is_exit = 1;
1027 else if (!strcmp(tok->args[i], "Stable"))
1028 rs->is_stable = 1;
1029 else if (!strcmp(tok->args[i], "Fast"))
1030 rs->is_fast = 1;
1031 else if (!strcmp(tok->args[i], "Running"))
1032 rs->is_running = 1;
1033 else if (!strcmp(tok->args[i], "Named"))
1034 rs->is_named = 1;
1035 else if (!strcmp(tok->args[i], "Valid"))
1036 rs->is_valid = 1;
1040 goto done;
1041 err:
1042 if (rs)
1043 routerstatus_free(rs);
1044 rs = NULL;
1045 done:
1046 SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
1047 smartlist_clear(tokens);
1048 *s = eos;
1050 return rs;
1053 /** Helper to sort a smartlist of pointers to routerstatus_t */
1054 static int
1055 _compare_routerstatus_entries(const void **_a, const void **_b)
1057 const routerstatus_t *a = *_a, *b = *_b;
1058 return memcmp(a->identity_digest, b->identity_digest, DIGEST_LEN);
1061 void
1062 sort_routerstatus_entries(smartlist_t *sl)
1064 smartlist_sort(sl, _compare_routerstatus_entries);
1067 /** Given a versioned (v2 or later) network-status object in <b>s</b>, try to
1068 * parse it and return the result. Return NULL on failure. Check the
1069 * signature of the network status, but do not (yet) check the signing key for
1070 * authority.
1072 networkstatus_t *
1073 networkstatus_parse_from_string(const char *s)
1075 const char *eos;
1076 smartlist_t *tokens = smartlist_create();
1077 networkstatus_t *ns = NULL;
1078 char ns_digest[DIGEST_LEN];
1079 char tmp_digest[DIGEST_LEN];
1080 struct in_addr in;
1081 directory_token_t *tok;
1082 int i;
1084 if (router_get_networkstatus_v2_hash(s, ns_digest)) {
1085 log_fn(LOG_WARN, "Unable to compute digest of network-status");
1086 goto err;
1089 eos = find_start_of_next_routerstatus(s);
1090 if (tokenize_string(s, eos, tokens, NETSTATUS)) {
1091 log_fn(LOG_WARN, "Error tokenizing network-status header.");
1092 goto err;
1094 if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) {
1095 log_fn(LOG_WARN, "Unrecognized keyword '%s'; can't parse network-status",
1096 tok->args[0]);
1097 goto err;
1099 ns = tor_malloc_zero(sizeof(networkstatus_t));
1100 memcpy(ns->networkstatus_digest, ns_digest, DIGEST_LEN);
1102 if (!(tok = find_first_by_keyword(tokens, K_NETWORK_STATUS_VERSION))) {
1103 log_fn(LOG_WARN, "Couldn't find network-status-version keyword");
1104 goto err;
1107 if (!(tok = find_first_by_keyword(tokens, K_DIR_SOURCE))) {
1108 log_fn(LOG_WARN, "Couldn't find dir-source keyword");
1109 goto err;
1111 if (tok->n_args < 3) {
1112 log_fn(LOG_WARN, "Too few arguments to dir-source keyword");
1113 goto err;
1115 ns->source_address = tok->args[0]; tok->args[0] = NULL;
1116 if (tor_inet_aton(tok->args[1], &in) == 0) {
1117 log_fn(LOG_WARN, "Error parsing address '%s'", tok->args[1]);
1118 goto err;
1120 ns->source_addr = ntohl(in.s_addr);
1121 ns->source_dirport =
1122 (uint16_t) tor_parse_long(tok->args[2],10,0,65535,NULL,NULL);
1123 if (ns->source_dirport == 0) {
1124 log_fn(LOG_WARN, "Directory source without dirport; skipping.");
1125 goto err;
1128 if (!(tok = find_first_by_keyword(tokens, K_FINGERPRINT))) {
1129 log_fn(LOG_WARN, "Couldn't find fingerprint keyword");
1130 goto err;
1132 if (tok->n_args < 1) {
1133 log_fn(LOG_WARN, "Too few arguments to fingerprint");
1134 goto err;
1136 if (base16_decode(ns->identity_digest, DIGEST_LEN, tok->args[0],
1137 strlen(tok->args[0]))) {
1138 log_fn(LOG_WARN, "Couldn't decode fingerprint '%s'", tok->args[0]);
1139 goto err;
1142 if ((tok = find_first_by_keyword(tokens, K_CONTACT)) && tok->n_args) {
1143 ns->contact = tok->args[0];
1144 tok->args[0] = NULL;
1147 if (!(tok = find_first_by_keyword(tokens, K_DIR_SIGNING_KEY)) || !tok->key) {
1148 log_fn(LOG_WARN, "Missing dir-signing-key");
1149 goto err;
1151 ns->signing_key = tok->key;
1152 tok->key = NULL;
1154 if (crypto_pk_get_digest(ns->signing_key, tmp_digest)<0) {
1155 log_fn(LOG_WARN, "Couldn't compute signing key digest");
1156 goto err;
1158 if (memcmp(tmp_digest, ns->identity_digest, DIGEST_LEN)) {
1159 log_fn(LOG_WARN, "network-status fingerprint did not match dir-signing-key");
1160 goto err;
1163 if ((tok = find_first_by_keyword(tokens, K_DIR_OPTIONS))) {
1164 for (i=0; i < tok->n_args; ++i) {
1165 if (!strcmp(tok->args[i], "Names"))
1166 ns->binds_names = 1;
1167 if (!strcmp(tok->args[i], "Versions"))
1168 ns->recommends_versions = 1;
1172 if (ns->recommends_versions) {
1173 if (!(tok = find_first_by_keyword(tokens, K_CLIENT_VERSIONS)) ||
1174 tok->n_args<1) {
1175 log_fn(LOG_WARN, "Missing client-versions");
1177 ns->client_versions = tok->args[0];
1178 tok->args[0] = NULL;
1180 if (!(tok = find_first_by_keyword(tokens, K_SERVER_VERSIONS)) ||
1181 tok->n_args<1) {
1182 log_fn(LOG_WARN, "Missing server-versions on versioning directory");
1183 goto err;
1185 ns->server_versions = tok->args[0];
1186 tok->args[0] = NULL;
1189 if (!(tok = find_first_by_keyword(tokens, K_PUBLISHED))) {
1190 log_fn(LOG_WARN, "Missing published time on directory.");
1191 goto err;
1193 tor_assert(tok->n_args == 1);
1194 if (parse_iso_time(tok->args[0], &ns->published_on) < 0) {
1195 goto err;
1198 ns->entries = smartlist_create();
1199 s = eos;
1200 SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
1201 smartlist_clear(tokens);
1202 while (!strcmpstart(s, "r ")) {
1203 routerstatus_t *rs;
1204 if ((rs = routerstatus_parse_entry_from_string(&s, tokens)))
1205 smartlist_add(ns->entries, rs);
1207 smartlist_sort(ns->entries, _compare_routerstatus_entries);
1209 /* Kill duplicate entries. */
1210 for (i=0; i < smartlist_len(ns->entries)-1; ++i) {
1211 routerstatus_t *rs1 = smartlist_get(ns->entries, i);
1212 routerstatus_t *rs2 = smartlist_get(ns->entries, i+1);
1213 if (!memcmp(rs1->identity_digest,
1214 rs2->identity_digest, DIGEST_LEN)) {
1215 log_fn(LOG_WARN, "Network-status has two entries for the same router. Dropping one.");
1216 smartlist_del_keeporder(ns->entries, i--);
1217 routerstatus_free(rs1);
1221 if (tokenize_string(s, NULL, tokens, NETSTATUS)) {
1222 log_fn(LOG_WARN, "Error tokenizing network-status footer.");
1223 goto err;
1225 if (smartlist_len(tokens) < 1) {
1226 log_fn(LOG_WARN, "Too few items in network-status footer.");
1227 goto err;
1229 tok = smartlist_get(tokens, smartlist_len(tokens)-1);
1230 if (tok->tp != K_DIRECTORY_SIGNATURE) {
1231 log_fn(LOG_WARN, "Expected network-status footer to end with a signature.");
1232 goto err;
1235 if (check_directory_signature(ns_digest, tok, NULL, ns->signing_key, 0))
1236 goto err;
1238 goto done;
1239 err:
1240 if (ns)
1241 networkstatus_free(ns);
1242 ns = NULL;
1243 done:
1244 SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_free(t));
1245 smartlist_free(tokens);
1247 return ns;
1250 /** Parse the exit policy in the string <b>s</b> and return it. If
1251 * assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or
1252 * ADDR_POLICY_REJECT) for items that specify no action.
1254 addr_policy_t *
1255 router_parse_addr_policy_from_string(const char *s, int assume_action)
1257 directory_token_t *tok = NULL;
1258 const char *cp;
1259 char *tmp;
1260 addr_policy_t *r;
1261 size_t len, idx;
1263 /* *s might not end with \n, so we need to extend it with one. */
1264 len = strlen(s);
1265 cp = tmp = tor_malloc(len+2);
1266 for (idx = 0; idx < len; ++idx) {
1267 tmp[idx] = tolower(s[idx]);
1269 tmp[len]='\n';
1270 tmp[len+1]='\0';
1271 while (TOR_ISSPACE(*cp))
1272 ++cp;
1273 if ((*cp == '*' || TOR_ISDIGIT(*cp)) && assume_action >= 0) {
1274 char *new_str = tor_malloc(len+10);
1275 tor_snprintf(new_str, len+10, "%s %s\n",
1276 assume_action == ADDR_POLICY_ACCEPT?"accept":"reject", cp);
1277 tor_free(tmp);
1278 cp = tmp = new_str;
1280 tok = get_next_token(&cp, RTR);
1281 if (tok->tp == _ERR) {
1282 log_fn(LOG_WARN, "Error reading exit policy: %s", tok->error);
1283 goto err;
1285 if (tok->tp != K_ACCEPT && tok->tp != K_REJECT) {
1286 log_fn(LOG_WARN, "Expected 'accept' or 'reject'.");
1287 goto err;
1290 /* Now that we've gotten an exit policy, add it to the router. */
1291 r = router_parse_addr_policy(tok);
1292 goto done;
1293 err:
1294 r = NULL;
1295 done:
1296 tor_free(tmp);
1297 token_free(tok);
1298 return r;
1301 /** Given an exit policicy stored in <b>s</b>, parse it and add it to the end
1302 * of the exit policy of <b>router</b>. Return 0 on success, -1 on failure.
1305 router_add_exit_policy_from_string(routerinfo_t *router, const char *s)
1307 addr_policy_t *newe, *tmpe;
1308 newe = router_parse_addr_policy_from_string(s, -1);
1309 if (!newe)
1310 return -1;
1311 for (tmpe = router->exit_policy; tmpe; tmpe=tmpe->next)
1313 tmpe->next = newe;
1315 return 0;
1318 /** Add an exit policy stored in the token <b>tok</b> to the router info in
1319 * <b>router</b>. Return 0 on success, -1 on failure. */
1320 static int
1321 router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
1323 addr_policy_t *newe, **tmpe;
1324 newe = router_parse_addr_policy(tok);
1325 if (!newe)
1326 return -1;
1327 for (tmpe = &router->exit_policy; *tmpe; tmpe=&((*tmpe)->next))
1329 *tmpe = newe;
1331 return 0;
1334 /** Given a K_ACCEPT or K_REJECT token and a router, create and return
1335 * a new exit_policy_t corresponding to the token. */
1336 static addr_policy_t *
1337 router_parse_addr_policy(directory_token_t *tok)
1339 addr_policy_t *newe;
1340 // struct in_addr in;
1341 char *arg;
1342 // char *address;
1343 // char buf[INET_NTOA_BUF_LEN];
1345 tor_assert(tok->tp == K_REJECT || tok->tp == K_ACCEPT);
1347 if (tok->n_args != 1)
1348 return NULL;
1349 arg = tok->args[0];
1351 newe = tor_malloc_zero(sizeof(addr_policy_t));
1353 newe->string = tor_malloc(8+strlen(arg));
1354 /* XXX eventually, use the code from router.c:727 to generate this */
1355 tor_snprintf(newe->string, 8+strlen(arg), "%s %s",
1356 (tok->tp == K_REJECT) ? "reject" : "accept", arg);
1357 newe->policy_type = (tok->tp == K_REJECT) ? ADDR_POLICY_REJECT
1358 : ADDR_POLICY_ACCEPT;
1360 if (parse_addr_and_port_range(arg, &newe->addr, &newe->msk,
1361 &newe->prt_min, &newe->prt_max))
1362 goto policy_read_failed;
1364 // in.s_addr = htonl(newe->addr);
1365 // tor_inet_ntoa(&in, buf, sizeof(buf));
1366 // address = tor_strdup(buf);
1367 // in.s_addr = htonl(newe->msk);
1368 // log_fn(LOG_DEBUG,"%s %s/%s:%d-%d",
1369 // newe->policy_type == ADDR_POLICY_REJECT ? "reject" : "accept",
1370 // address, inet_ntoa(in), newe->prt_min, newe->prt_max);
1371 // tor_free(address);
1373 return newe;
1375 policy_read_failed:
1376 tor_assert(newe->string);
1377 log_fn(LOG_WARN,"Couldn't parse line '%s'. Dropping", newe->string);
1378 tor_free(newe->string);
1379 tor_free(newe);
1380 return NULL;
1383 /** Log and exit if <b>t</b> is malformed */
1384 void
1385 assert_addr_policy_ok(addr_policy_t *t)
1387 addr_policy_t *t2;
1388 while (t) {
1389 tor_assert(t->policy_type == ADDR_POLICY_REJECT ||
1390 t->policy_type == ADDR_POLICY_ACCEPT);
1391 tor_assert(t->prt_min <= t->prt_max);
1392 t2 = router_parse_addr_policy_from_string(t->string, -1);
1393 tor_assert(t2);
1394 tor_assert(t2->policy_type == t->policy_type);
1395 tor_assert(t2->addr == t->addr);
1396 tor_assert(t2->msk == t->msk);
1397 tor_assert(t2->prt_min == t->prt_min);
1398 tor_assert(t2->prt_max == t->prt_max);
1399 tor_assert(!strcmp(t2->string, t->string));
1400 tor_assert(t2->next == NULL);
1401 addr_policy_free(t2);
1403 t = t->next;
1409 * Low-level tokenizer for router descriptors and directories.
1412 /** Free all resources allocated for <b>tok</b> */
1413 static void
1414 token_free(directory_token_t *tok)
1416 int i;
1417 tor_assert(tok);
1418 if (tok->args) {
1419 for (i = 0; i < tok->n_args; ++i) {
1420 tor_free(tok->args[i]);
1422 tor_free(tok->args);
1424 tor_free(tok->object_type);
1425 tor_free(tok->object_body);
1426 if (tok->key)
1427 crypto_free_pk_env(tok->key);
1428 tor_free(tok);
1431 /** Helper function: read the next token from *s, advance *s to the end
1432 * of the token, and return the parsed token. If 'where' is DIR
1433 * or RTR, reject all tokens of the wrong type.
1435 static directory_token_t *
1436 get_next_token(const char **s, where_syntax where)
1438 const char *next, *obstart;
1439 int i, done, allocated, is_opt;
1440 directory_token_t *tok;
1441 arg_syntax a_syn;
1442 obj_syntax o_syn = NO_OBJ;
1444 #define RET_ERR(msg) \
1445 do { if (tok) token_free(tok); \
1446 tok = tor_malloc_zero(sizeof(directory_token_t));\
1447 tok->tp = _ERR; \
1448 tok->error = msg; \
1449 goto done_tokenizing; } while (0)
1451 tok = tor_malloc_zero(sizeof(directory_token_t));
1452 tok->tp = _ERR;
1454 *s = eat_whitespace(*s);
1455 if (!**s) {
1456 tok->tp = _EOF;
1457 return tok;
1459 next = find_whitespace(*s);
1460 if (!next) {
1461 tok->error = "Unexpected EOF"; return tok;
1463 /* It's a keyword... but which one? */
1464 is_opt = !strncmp("opt", *s, next-*s);
1465 if (is_opt) {
1466 *s = eat_whitespace(next);
1467 next = NULL;
1468 if (**s)
1469 next = find_whitespace(*s);
1470 if (!**s || !next) {
1471 RET_ERR("opt without keyword");
1474 for (i = 0; token_table[i].t ; ++i) {
1475 if (!strncmp(token_table[i].t, *s, next-*s)) {
1476 /* We've found the keyword. */
1477 tok->tp = token_table[i].v;
1478 a_syn = token_table[i].s;
1479 o_syn = token_table[i].os;
1480 if (!(token_table[i].ws & where)) {
1481 if (where == DIR) {
1482 RET_ERR("Found an out-of-place token in a directory section");
1483 } else if (where == RTR) {
1484 RET_ERR("Found an out-of-place token in a router descriptor");
1485 } else if (where == NETSTATUS) {
1486 RET_ERR("Found an out-of-place token in a network-status header");
1487 } else {
1488 RET_ERR("Found an out-of-place token in a router status body");
1491 if (a_syn == ARGS) {
1492 /* This keyword takes multiple arguments. */
1493 i = 0;
1494 done = (*next == '\n');
1495 allocated = 32;
1496 tok->args = tor_malloc(sizeof(char*)*32);
1497 *s = eat_whitespace_no_nl(next);
1498 while (**s != '\n' && !done) {
1499 next = find_whitespace(*s);
1500 if (*next == '\n')
1501 done = 1;
1502 if (i == allocated) {
1503 allocated *= 2;
1504 tok->args = tor_realloc(tok->args,sizeof(char*)*allocated);
1506 tok->args[i++] = tor_strndup(*s,next-*s);
1507 *s = eat_whitespace_no_nl(next+1);
1509 tok->n_args = i;
1510 } else if (a_syn == CONCAT_ARGS) {
1511 /* The keyword takes the line as a single argument */
1512 *s = eat_whitespace_no_nl(next);
1513 next = strchr(*s, '\n');
1514 if (!next)
1515 RET_ERR("Unexpected EOF");
1516 tok->args = tor_malloc(sizeof(char*));
1517 tok->args[0] = tor_strndup(*s,next-*s);
1518 tok->n_args = 1;
1519 *s = eat_whitespace_no_nl(next+1);
1520 } else {
1521 /* The keyword takes no arguments. */
1522 tor_assert(a_syn == NO_ARGS);
1523 *s = eat_whitespace_no_nl(next);
1524 if (**s != '\n') {
1525 RET_ERR("Unexpected arguments");
1527 tok->n_args = 0;
1528 *s = eat_whitespace_no_nl(*s+1);
1530 break;
1533 if (tok->tp == _ERR) {
1534 if (is_opt) {
1535 tok->tp = K_OPT;
1536 *s = eat_whitespace_no_nl(next);
1537 next = strchr(*s,'\n');
1538 if (!next)
1539 RET_ERR("Unexpected EOF");
1540 tok->args = tor_malloc(sizeof(char*));
1541 tok->args[0] = tor_strndup(*s,next-*s);
1542 tok->n_args = 1;
1543 *s = eat_whitespace_no_nl(next+1);
1544 o_syn = OBJ_OK;
1545 } else {
1546 tok->tp = _UNRECOGNIZED;
1547 next = strchr(*s, '\n');
1548 if (!next) {
1549 RET_ERR("Unexpected EOF");
1551 tok->args = tor_malloc(sizeof(char*));
1552 tok->args[0] = tor_strndup(*s,next-*s);
1553 tok->n_args = 1;
1554 *s = next+1;
1555 o_syn = OBJ_OK;
1558 *s = eat_whitespace(*s);
1559 if (strcmpstart(*s, "-----BEGIN ")) {
1560 goto done_tokenizing;
1562 obstart = *s;
1563 *s += 11; /* length of "-----BEGIN ". */
1564 next = strchr(*s, '\n');
1565 if (next-*s < 6 || strcmpstart(next-5, "-----\n")) {
1566 RET_ERR("Malformed object: bad begin line");
1568 tok->object_type = tor_strndup(*s, next-*s-5);
1569 *s = next+1;
1570 next = strstr(*s, "-----END ");
1571 if (!next) {
1572 RET_ERR("Malformed object: missing end line");
1574 if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) {
1575 if (strcmpstart(next, "-----END RSA PUBLIC KEY-----\n"))
1576 RET_ERR("Malformed object: mismatched end line");
1577 next = strchr(next,'\n')+1;
1578 tok->key = crypto_new_pk_env();
1579 if (crypto_pk_read_public_key_from_string(tok->key, obstart, next-obstart))
1580 RET_ERR("Couldn't parse public key.");
1581 *s = next;
1582 } else {
1583 tok->object_body = tor_malloc(next-*s); /* really, this is too much RAM. */
1584 i = base64_decode(tok->object_body, 256, *s, next-*s);
1585 if (i<0) {
1586 RET_ERR("Malformed object: bad base64-encoded data");
1588 tok->object_size = i;
1589 *s = next + 9; /* length of "-----END ". */
1590 i = strlen(tok->object_type);
1591 if (strncmp(*s, tok->object_type, i) || strcmpstart(*s+i, "-----\n")) {
1592 RET_ERR("Malformed object: mismatched end tag");
1594 *s += i+6;
1596 switch (o_syn)
1598 case NO_OBJ:
1599 if (tok->object_body)
1600 RET_ERR("Unexpected object for keyword");
1601 if (tok->key)
1602 RET_ERR("Unexpected public key for keyword");
1603 break;
1604 case NEED_OBJ:
1605 if (!tok->object_body)
1606 RET_ERR("Missing object for keyword");
1607 break;
1608 case NEED_KEY:
1609 if (!tok->key)
1610 RET_ERR("Missing public key for keyword");
1611 break;
1612 case OBJ_OK:
1613 break;
1616 done_tokenizing:
1618 return tok;
1619 #undef RET_ERR
1622 /** Read all tokens from a string between <b>start</b> and <b>end</b>, and add
1623 * them to <b>out</b>. If <b>is_dir</b> is true, reject all non-directory
1624 * tokens; else reject all non-routerdescriptor tokens.
1626 static int
1627 tokenize_string(const char *start, const char *end, smartlist_t *out,
1628 where_syntax where)
1630 const char **s;
1631 directory_token_t *tok = NULL;
1632 s = &start;
1633 if (!end)
1634 end = start+strlen(start);
1635 while (*s < end && (!tok || tok->tp != _EOF)) {
1636 tok = get_next_token(s, where);
1637 if (tok->tp == _ERR) {
1638 log_fn(LOG_WARN, "parse error: %s", tok->error);
1639 return -1;
1641 smartlist_add(out, tok);
1642 *s = eat_whitespace(*s);
1645 return 0;
1648 /** Find the first token in <b>s</b> whose keyword is <b>keyword</b>; return
1649 * NULL if no such keyword is found.
1651 static directory_token_t *
1652 find_first_by_keyword(smartlist_t *s, directory_keyword keyword)
1654 SMARTLIST_FOREACH(s, directory_token_t *, t, if (t->tp == keyword) return t);
1655 return NULL;
1658 /** Return a newly allocated smartlist of all accept or reject tokens in
1659 * <b>s</b>.
1661 static smartlist_t *
1662 find_all_exitpolicy(smartlist_t *s)
1664 smartlist_t *out = smartlist_create();
1665 SMARTLIST_FOREACH(s, directory_token_t *, t,
1666 if (t->tp == K_ACCEPT || t->tp == K_REJECT)
1667 smartlist_add(out,t));
1668 return out;
1671 /** Compute the SHA digest of the substring of <b>s</b> taken from the first
1672 * occurrence of <b>start_str</b> through the first newline after the first
1673 * subsequent occurrence of <b>end_str</b>; store the 20-byte result in
1674 * <b>digest</b>; return 0 on success.
1676 * If no such substring exists, return -1.
1678 static int
1679 router_get_hash_impl(const char *s, char *digest,
1680 const char *start_str,
1681 const char *end_str)
1683 char *start, *end;
1684 start = strstr(s, start_str);
1685 if (!start) {
1686 log_fn(LOG_WARN,"couldn't find \"%s\"",start_str);
1687 return -1;
1689 if (start != s && *(start-1) != '\n') {
1690 log_fn(LOG_WARN, "first occurrence of \"%s\" is not at the start of a line",
1691 start_str);
1692 return -1;
1694 end = strstr(start+strlen(start_str), end_str);
1695 if (!end) {
1696 log_fn(LOG_WARN,"couldn't find \"%s\"",end_str);
1697 return -1;
1699 end = strchr(end+strlen(end_str), '\n');
1700 if (!end) {
1701 log_fn(LOG_WARN,"couldn't find EOL");
1702 return -1;
1704 ++end;
1706 if (crypto_digest(digest, start, end-start)) {
1707 log_fn(LOG_WARN,"couldn't compute digest");
1708 return -1;
1711 return 0;
1714 /** Parse the Tor version of the platform string <b>platform</b>,
1715 * and compare it to the version in <b>cutoff</b>. Return 1 if
1716 * the router is at least as new as the cutoff, else return 0.
1719 tor_version_as_new_as(const char *platform, const char *cutoff)
1721 tor_version_t cutoff_version, router_version;
1722 char *s, *start;
1723 char tmp[128];
1725 if (tor_version_parse(cutoff, &cutoff_version)<0) {
1726 log_fn(LOG_WARN,"Bug: cutoff version '%s' unparseable.",cutoff);
1727 return 0;
1729 if (strcmpstart(platform,"Tor ")) /* nonstandard Tor; be safe and say yes */
1730 return 1;
1732 start = (char *)eat_whitespace(platform+3);
1733 if (!*start) return 0;
1734 s = (char *)find_whitespace(start); /* also finds '\0', which is fine */
1735 if ((size_t)(s-start+1) >= sizeof(tmp)) /* too big, no */
1736 return 0;
1737 strlcpy(tmp, start, s-start+1);
1739 if (tor_version_parse(tmp, &router_version)<0) {
1740 log_fn(LOG_INFO,"Router version '%s' unparseable.",tmp);
1741 return 1; /* be safe and say yes */
1744 return tor_version_compare(&router_version, &cutoff_version) >= 0;
1747 /** Parse a tor version from <b>s</b>, and store the result in <b>out</b>.
1748 * Return 0 on success, -1 on failure. */
1750 tor_version_parse(const char *s, tor_version_t *out)
1752 char *eos=NULL, *cp=NULL;
1753 /* Format is:
1754 * "Tor " ? NUM dot NUM dot NUM [ ( pre | rc | dot ) NUM [ -cvs ] ]
1756 tor_assert(s);
1757 tor_assert(out);
1759 memset(out, 0, sizeof(tor_version_t));
1761 if (!strcasecmpstart(s, "Tor "))
1762 cp += 4;
1764 /* Get major. */
1765 out->major = strtol(s,&eos,10);
1766 if (!eos || eos==s || *eos != '.') return -1;
1767 cp = eos+1;
1769 /* Get minor */
1770 out->minor = strtol(cp,&eos,10);
1771 if (!eos || eos==cp || *eos != '.') return -1;
1772 cp = eos+1;
1774 /* Get micro */
1775 out->micro = strtol(cp,&eos,10);
1776 if (!eos || eos==cp) return -1;
1777 if (!*eos) {
1778 out->status = VER_RELEASE;
1779 out->patchlevel = 0;
1780 out->cvs = IS_NOT_CVS;
1781 return 0;
1783 cp = eos;
1785 /* Get status */
1786 if (*cp == '.') {
1787 out->status = VER_RELEASE;
1788 ++cp;
1789 } else if (0==strncmp(cp, "pre", 3)) {
1790 out->status = VER_PRE;
1791 cp += 3;
1792 } else if (0==strncmp(cp, "rc", 2)) {
1793 out->status = VER_RC;
1794 cp += 2;
1795 } else {
1796 return -1;
1799 /* Get patchlevel */
1800 out->patchlevel = strtol(cp,&eos,10);
1801 if (!eos || eos==cp) return -1;
1802 cp = eos;
1804 /* Get cvs status and status tag. */
1805 if (*cp == '-' || *cp == '.')
1806 ++cp;
1807 strlcpy(out->status_tag, cp, sizeof(out->status_tag));
1808 if (0==strcmp(cp, "cvs")) {
1809 out->cvs = IS_CVS;
1810 } else {
1811 out->cvs = IS_NOT_CVS;
1814 return 0;
1817 /** Compare two tor versions; Return <0 if a < b; 0 if a ==b, >0 if a >
1818 * b. */
1820 tor_version_compare(tor_version_t *a, tor_version_t *b)
1822 int i;
1823 tor_assert(a);
1824 tor_assert(b);
1825 if ((i = a->major - b->major))
1826 return i;
1827 else if ((i = a->minor - b->minor))
1828 return i;
1829 else if ((i = a->micro - b->micro))
1830 return i;
1831 else if ((i = a->status - b->status))
1832 return i;
1833 else if ((i = a->patchlevel - b->patchlevel))
1834 return i;
1836 if (a->major > 0 || a->minor > 0) {
1837 return strcmp(a->status_tag, b->status_tag);
1838 } else {
1839 return (a->cvs - b->cvs);
1843 /** Return true iff versions <b>a</b> and <b>b</b> belong to the same series.
1845 static int
1846 tor_version_same_series(tor_version_t *a, tor_version_t *b)
1848 tor_assert(a);
1849 tor_assert(b);
1850 return ((a->major == b->major) &&
1851 (a->minor == b->minor) &&
1852 (a->micro == b->micro));