and another one
[tor.git] / src / or / routerparse.c
blobb58950a68fc613a2d218098b68e5b9c3f2224969
1 /* Copyright 2001-2003 Roger Dingledine, Matej Pfajfar. */
2 /* See LICENSE for licensing information */
3 /* $Id$ */
5 /**
6 * \file routerparse.c
8 * \brief Code to parse and validate router descriptors and directories.
9 **/
11 #include "or.h"
13 /****************************************************************************/
15 extern or_options_t options; /* command-line and config-file options */
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_PLATFORM,
36 K_OPT,
37 K_BANDWIDTH,
38 K_PORTS,
39 K_DIRCACHEPORT,
40 K_CONTACT,
41 K_NETWORK_STATUS,
42 K_UPTIME,
43 K_DIR_SIGNING_KEY,
44 _UNRECOGNIZED,
45 _ERR,
46 _EOF,
47 _NIL
48 } directory_keyword;
50 /** Structure to hold a single directory token.
52 * We parse a directory by breaking it into "tokens", each consisting
53 * of a keyword, a line full of arguments, and a binary object. The
54 * arguments and object are both optional, depending on the keyword
55 * type.
57 typedef struct directory_token_t {
58 directory_keyword tp; /**< Type of the token. */
59 int n_args; /**< Number of elements in args */
60 char **args; /**< Array of arguments from keyword line. */
61 char *object_type; /**< -----BEGIN [object_type]-----*/
62 size_t object_size; /**< Bytes in object_body */
63 char *object_body; /**< Contents of object, base64-decoded. */
64 crypto_pk_env_t *key; /**< For public keys only. */
65 char *error; /**< For _ERR tokens only. */
66 } directory_token_t;
68 /* ********************************************************************** */
70 /** We use a table of rules to decide how to parse each token type. */
72 /** Rules for how many arguments a keyword can take. */
73 typedef enum {
74 NO_ARGS, /**< (1) no arguments, ever */
75 ARGS, /**< (2) a list of arguments separated by spaces */
76 CONCAT_ARGS, /**< or (3) the rest of the line, treated as a single argument. */
77 } arg_syntax;
79 /** Rules for whether the keyword needs an object. */
80 typedef enum {
81 NO_OBJ, /**< (1) no object, ever */
82 NEED_OBJ, /**< (2) object is required */
83 NEED_KEY, /**< (3) object is required, and must be a public key. */
84 OBJ_OK, /**< or (4) object is optional. */
85 } obj_syntax;
87 /** Rules for where a keyword can appear. */
88 typedef enum {
89 ANY = 0, /**< Appears in router descriptor or in directory sections. */
90 DIR_ONLY, /**< Appears only in directory. */
91 RTR_ONLY, /**< Appears only in router descriptor or runningrouters */
92 } where_syntax;
94 /** Table mapping keywords to token value and to argument rules. */
95 static struct {
96 char *t; int v; arg_syntax s; obj_syntax os; where_syntax ws;
97 } token_table[] = {
98 { "accept", K_ACCEPT, ARGS, NO_OBJ, RTR_ONLY },
99 { "directory-signature", K_DIRECTORY_SIGNATURE, ARGS, NEED_OBJ,DIR_ONLY},
100 { "reject", K_REJECT, ARGS, NO_OBJ, RTR_ONLY },
101 { "router", K_ROUTER, ARGS, NO_OBJ, RTR_ONLY },
102 { "recommended-software",K_RECOMMENDED_SOFTWARE,ARGS, NO_OBJ, DIR_ONLY },
103 { "signed-directory", K_SIGNED_DIRECTORY, NO_ARGS, NO_OBJ, DIR_ONLY },
104 { "signing-key", K_SIGNING_KEY, NO_ARGS, NEED_KEY,RTR_ONLY },
105 { "onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY,RTR_ONLY },
106 { "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ,RTR_ONLY },
107 { "running-routers", K_RUNNING_ROUTERS, ARGS, NO_OBJ, DIR_ONLY },
108 { "ports", K_PORTS, ARGS, NO_OBJ, RTR_ONLY },
109 { "bandwidth", K_BANDWIDTH, ARGS, NO_OBJ, RTR_ONLY },
110 { "platform", K_PLATFORM, CONCAT_ARGS, NO_OBJ, RTR_ONLY },
111 { "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ, ANY },
112 { "opt", K_OPT, CONCAT_ARGS, OBJ_OK, ANY },
113 { "dircacheport", K_DIRCACHEPORT, ARGS, NO_OBJ, RTR_ONLY },
114 { "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ, ANY },
115 { "network-status", K_NETWORK_STATUS, NO_ARGS, NO_OBJ, DIR_ONLY },
116 { "uptime", K_UPTIME, ARGS, NO_OBJ, RTR_ONLY },
117 { "dir-signing-key", K_DIR_SIGNING_KEY, ARGS, OBJ_OK, DIR_ONLY },
118 { NULL, -1, NO_ARGS, NO_OBJ, ANY }
121 /* static function prototypes */
122 static int router_add_exit_policy(routerinfo_t *router,directory_token_t *tok);
123 static struct exit_policy_t *router_parse_exit_policy(directory_token_t *tok);
124 static int router_get_hash_impl(const char *s, char *digest,
125 const char *start_str, const char *end_str);
126 static void token_free(directory_token_t *tok);
127 static smartlist_t *find_all_exitpolicy(smartlist_t *s);
128 static directory_token_t *find_first_by_keyword(smartlist_t *s,
129 directory_keyword keyword);
130 static int tokenize_string(const char *start, const char *end,
131 smartlist_t *out, int is_dir);
132 static directory_token_t *get_next_token(const char **s, where_syntax where);
133 static int check_directory_signature(const char *digest,
134 directory_token_t *tok,
135 crypto_pk_env_t *pkey,
136 crypto_pk_env_t *declared_key);
137 static crypto_pk_env_t *find_dir_signing_key(const char *str);
139 /** Set <b>digest</b> to the SHA-1 digest of the hash of the directory in
140 * <b>s</b>. Return 0 on success, nonzero on failure.
142 int router_get_dir_hash(const char *s, char *digest)
144 return router_get_hash_impl(s,digest,
145 "signed-directory","\ndirectory-signature");
148 /** Set <b>digest</b> to the SHA-1 digest of the hash of the first router in
149 * <b>s</b>. Return 0 on success, nonzero on failure.
151 int router_get_router_hash(const char *s, char *digest)
153 return router_get_hash_impl(s,digest,
154 "router ","\nrouter-signature");
157 /** Set <b>digest</b> to the SHA-1 digest of the hash of the running-routers
158 * string in <b>s</b>. Return 0 on success, nonzero on failure.
160 int router_get_runningrouters_hash(const char *s, char *digest)
162 return router_get_hash_impl(s,digest,
163 "network-status","\ndirectory-signature");
167 * Find the first instance of "recommended-software ...\n" at the start of
168 * a line; return a newly allocated string containing the "..." portion.
169 * Return NULL if no such instance was found.
171 static char *
172 get_recommended_software_from_directory(const char *str)
174 #define REC "recommended-software "
175 const char *cp = str, *eol;
176 size_t len = strlen(REC);
177 cp = str;
178 if (strcmpstart(str, REC)==0) {
179 cp += len;
180 } else {
181 cp = strstr(str, "\n"REC);
182 if (!cp)
183 return NULL;
184 cp += len+1;
186 eol = strchr(cp, '\n');
187 if (!eol)
188 return NULL;
189 return tor_strndup(cp, eol-cp);
190 #undef REC
193 /** Return 1 if <b>myversion</b> is not in <b>versionlist</b>, and if at least
194 * one version of Tor on <b>versionlist</b> is newer than <b>myversion</b>.
195 * Otherwise return 0.
196 * (versionlist is a comma-separated list of version strings,
197 * optionally prefixed with "Tor". Versions that can't be parsed are
198 * ignored.) */
199 /* static */ int is_obsolete_version(const char *myversion,
200 const char *versionlist) {
201 const char *vl;
202 tor_version_t mine, other;
203 int found_newer = 0, r, ret;
204 static int warned_too_new=0;
205 smartlist_t *version_sl;
207 vl = versionlist;
209 log_fn(LOG_DEBUG,"checking '%s' in '%s'.", myversion, versionlist);
211 if (tor_version_parse(myversion, &mine)) {
212 log_fn(LOG_ERR, "I couldn't parse my own version (%s)", myversion);
213 tor_assert(0);
215 version_sl = smartlist_create();
216 smartlist_split_string(version_sl, versionlist, ",", SPLIT_SKIP_SPACE, 0);
218 SMARTLIST_FOREACH(version_sl, const char *, cp, {
219 if (!strcmpstart(cp, "Tor "))
220 cp += 4;
222 if (tor_version_parse(cp, &other)) {
223 /* Couldn't parse other; it can't be a match. */
224 } else {
225 r = tor_version_compare(&mine, &other);
226 if (r==0) {
227 ret = 0;
228 goto done;
229 } else if (r<0) {
230 found_newer = 1;
235 if (!found_newer) {
236 if (!warned_too_new) {
237 log_fn(LOG_WARN, "This version of Tor (%s) is newer than any on the recommended list (%s)",
238 myversion, versionlist);
239 warned_too_new=1;
241 ret = 0;
242 } else {
243 ret = 1;
246 done:
247 SMARTLIST_FOREACH(version_sl, char *, version, tor_free(version));
248 smartlist_free(version_sl);
249 return ret;
252 /* Return 0 if myversion is supported; else log a message and return
253 * -1 (or exit if ignoreversions is false) */
254 int check_software_version_against_directory(const char *directory,
255 int ignoreversion)
257 char *v;
258 v = get_recommended_software_from_directory(directory);
259 if (!v) {
260 log_fn(LOG_WARN, "No recommended-versions string found in directory");
261 return -1;
263 if (!is_obsolete_version(VERSION, v)) {
264 tor_free(v);
265 return 0;
267 log(ignoreversion ? LOG_WARN : LOG_ERR,
268 "You are running Tor version %s, which will not work with this network.\n"
269 "Please use %s%s.",
270 VERSION, strchr(v,',') ? "one of " : "", v);
271 tor_free(v);
273 if(ignoreversion) {
274 log(LOG_WARN, "IgnoreVersion is set. If it breaks, we told you so.");
275 return -1;
276 } else {
277 fflush(0);
278 tor_cleanup();
279 exit(0);
280 return -1; /* never reached */
284 /** Parse a directory from <b>str</b> and, when done, store the
285 * resulting routerlist in *<b>dest</b>, freeing the old value if necessary.
286 * If <b>pkey</b> is provided, we check the directory signature with pkey.
288 int /* Should be static; exposed for unit tests */
289 router_parse_routerlist_from_directory(const char *str,
290 routerlist_t **dest,
291 crypto_pk_env_t *pkey,
292 int check_version)
294 directory_token_t *tok;
295 char digest[DIGEST_LEN];
296 routerlist_t *new_dir = NULL;
297 char *versions = NULL;
298 smartlist_t *good_nickname_list = NULL;
299 time_t published_on;
300 int i, r;
301 const char *end, *cp;
302 smartlist_t *tokens = NULL;
303 char dirnickname[MAX_NICKNAME_LEN+1];
304 crypto_pk_env_t *declared_key = NULL;
306 if (router_get_dir_hash(str, digest)) {
307 log_fn(LOG_WARN, "Unable to compute digest of directory");
308 goto err;
310 log_fn(LOG_DEBUG,"Received directory hashes to %s",hex_str(digest,4));
312 /* Check signature first, before we try to tokenize. */
313 cp = str;
314 while (cp && (end = strstr(cp+1, "\ndirectory-signature")))
315 cp = end;
316 if (cp == str || !cp) {
317 log_fn(LOG_WARN, "No signature found on directory."); goto err;
319 ++cp;
320 tokens = smartlist_create();
321 if (tokenize_string(cp,strchr(cp,'\0'),tokens,1)) {
322 log_fn(LOG_WARN, "Error tokenizing directory signature"); goto err;
324 if (smartlist_len(tokens) != 1) {
325 log_fn(LOG_WARN, "Unexpected number of tokens in signature"); goto err;
327 tok=smartlist_get(tokens,0);
328 if(tok->tp != K_DIRECTORY_SIGNATURE) {
329 log_fn(LOG_WARN,"Expected a single directory signature"); goto err;
331 declared_key = find_dir_signing_key(str);
332 if (check_directory_signature(digest, tok, pkey, declared_key)<0)
333 goto err;
335 /* now we know tok->n_args == 1, so it's safe to access tok->args[0] */
336 strlcpy(dirnickname, tok->args[0], sizeof(dirnickname));
338 SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok));
339 smartlist_free(tokens);
340 tokens = NULL;
342 /* Now that we know the signature is okay, check the version. */
343 if (check_version)
344 check_software_version_against_directory(str, options.IgnoreVersion);
346 /* Now try to parse the first part of the directory. */
347 if ((end = strstr(str,"\nrouter "))) {
348 ++end;
349 } else if ((end = strstr(str, "\ndirectory-signature"))) {
350 ++end;
351 } else {
352 end = str + strlen(str);
355 tokens = smartlist_create();
356 if (tokenize_string(str,end,tokens,1)) {
357 log_fn(LOG_WARN, "Error tokenizing directory"); goto err;
359 if (smartlist_len(tokens) < 1) {
360 log_fn(LOG_WARN, "Impossibly short directory header"); goto err;
362 if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) {
363 log_fn(LOG_WARN, "Unrecognized keyword \"%s\" in directory header; can't parse directory.",
364 tok->args[0]);
365 goto err;
368 tok = smartlist_get(tokens,0);
369 if (tok->tp != K_SIGNED_DIRECTORY) {
370 log_fn(LOG_WARN, "Directory doesn't start with signed-directory.");
371 goto err;
374 if (!(tok = find_first_by_keyword(tokens, K_PUBLISHED))) {
375 log_fn(LOG_WARN, "Missing published time on directory.");
376 goto err;
378 tor_assert(tok->n_args == 1);
380 if (parse_iso_time(tok->args[0], &published_on) < 0) {
381 goto err;
384 if (!(tok = find_first_by_keyword(tokens, K_RECOMMENDED_SOFTWARE))) {
385 log_fn(LOG_WARN, "Missing recommended-software line from directory.");
386 goto err;
388 if (tok->n_args != 1) {
389 log_fn(LOG_WARN, "Invalid recommended-software line"); goto err;
391 versions = tor_strdup(tok->args[0]);
393 if (!(tok = find_first_by_keyword(tokens, K_RUNNING_ROUTERS))) {
394 log_fn(LOG_WARN, "Missing running-routers line from directory.");
395 goto err;
398 good_nickname_list = smartlist_create();
399 for (i=0; i<tok->n_args; ++i) {
400 smartlist_add(good_nickname_list, tok->args[i]);
402 tok->n_args = 0; /* Don't free the strings in good_nickname_list yet. */
404 /* Read the router list from s, advancing s up past the end of the last
405 * router. */
406 str = end;
407 if (router_parse_list_from_string(&str, &new_dir,
408 good_nickname_list, published_on)) {
409 log_fn(LOG_WARN, "Error reading routers from directory");
410 goto err;
413 new_dir->software_versions = versions; versions = NULL;
414 new_dir->published_on = published_on;
416 SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok));
417 smartlist_free(tokens);
418 tokens = NULL;
420 /* Determine if my routerinfo is considered verified. */
422 static int have_warned_about_unverified_status = 0;
423 routerinfo_t *me = router_get_my_routerinfo();
424 if(me) {
425 if(router_update_status_from_smartlist(me, published_on,
426 good_nickname_list)==1 &&
427 me->is_verified == 0 && !have_warned_about_unverified_status) {
428 log_fn(LOG_WARN,"Dirserver %s lists your server as unverified. Please consider sending your identity fingerprint to the tor-ops.", dirnickname);
429 have_warned_about_unverified_status = 1;
434 if (*dest)
435 routerlist_free(*dest);
436 *dest = new_dir;
438 r = 0;
439 goto done;
440 err:
441 r = -1;
442 if (new_dir)
443 routerlist_free(new_dir);
444 tor_free(versions);
445 done:
446 if (declared_key) crypto_free_pk_env(declared_key);
447 if (tokens) {
448 SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok));
449 smartlist_free(tokens);
451 if (good_nickname_list) {
452 SMARTLIST_FOREACH(good_nickname_list, char *, n, tor_free(n));
453 smartlist_free(good_nickname_list);
455 return r;
458 running_routers_t *
459 router_parse_runningrouters(const char *str)
461 char digest[DIGEST_LEN];
462 running_routers_t *new_list = NULL;
463 directory_token_t *tok;
464 time_t published_on;
465 int i;
466 crypto_pk_env_t *declared_key = NULL;
467 smartlist_t *tokens = NULL;
469 if (router_get_runningrouters_hash(str, digest)) {
470 log_fn(LOG_WARN, "Unable to compute digest of directory");
471 goto err;
473 tokens = smartlist_create();
474 if (tokenize_string(str,str+strlen(str),tokens,1)) {
475 log_fn(LOG_WARN, "Error tokenizing directory"); goto err;
477 if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) {
478 log_fn(LOG_WARN, "Unrecognized keyword \"%s\"; can't parse running-routers",
479 tok->args[0]);
480 goto err;
482 tok = smartlist_get(tokens,0);
483 if (tok->tp != K_NETWORK_STATUS) {
484 log_fn(LOG_WARN, "Network-status starts with wrong token");
485 goto err;
488 if (!(tok = find_first_by_keyword(tokens, K_PUBLISHED))) {
489 log_fn(LOG_WARN, "Missing published time on directory.");
490 goto err;
492 tor_assert(tok->n_args == 1);
493 if (parse_iso_time(tok->args[0], &published_on) < 0) {
494 goto err;
497 if (!(tok = find_first_by_keyword(tokens, K_RUNNING_ROUTERS))) {
498 log_fn(LOG_WARN, "Missing running-routers line from directory.");
499 goto err;
502 new_list = tor_malloc_zero(sizeof(running_routers_t));
503 new_list->published_on = published_on;
504 new_list->running_routers = smartlist_create();
505 for (i=0;i<tok->n_args;++i) {
506 smartlist_add(new_list->running_routers, tok->args[i]);
509 if (!(tok = find_first_by_keyword(tokens, K_DIRECTORY_SIGNATURE))) {
510 log_fn(LOG_WARN, "Missing signature on directory");
511 goto err;
513 declared_key = find_dir_signing_key(str);
514 if (check_directory_signature(digest, tok, NULL, declared_key) < 0)
515 goto err;
517 goto done;
518 err:
519 running_routers_free(new_list);
520 new_list = NULL;
521 done:
522 if (declared_key) crypto_free_pk_env(declared_key);
523 if (tokens) {
524 SMARTLIST_FOREACH(tokens, directory_token_t *, tok, token_free(tok));
525 smartlist_free(tokens);
527 return new_list;
530 /** Given a directory or running-routers string in <b>str</b>, try to
531 * find the its dir-signing-key token (if any). If this token is
532 * present, extract and return the key. Return NULL on failure. */
533 static crypto_pk_env_t *find_dir_signing_key(const char *str)
535 const char *cp;
536 directory_token_t *tok;
537 crypto_pk_env_t *key = NULL;
539 /* Is there a dir-signing-key in the directory? */
540 cp = strstr(str, "\nopt dir-signing-key");
541 if (!cp)
542 cp = strstr(str, "\ndir-signing-key");
543 if (!cp)
544 return NULL;
545 ++cp; /* Now cp points to the start of the token. */
547 tok = get_next_token(&cp, DIR_ONLY);
548 if (!tok) {
549 log_fn(LOG_WARN, "Unparseable dir-signing-key token");
550 return NULL;
552 if (tok->tp != K_DIR_SIGNING_KEY) {
553 log_fn(LOG_WARN, "Dir-signing-key token did not parse as expected");
554 return NULL;
557 if (tok->key) {
558 key = tok->key;
559 tok->key = NULL; /* steal reference. */
560 } else if (tok->n_args >= 1) {
561 key = crypto_pk_DER64_decode_public_key(tok->args[0]);
562 if (!key) {
563 log_fn(LOG_WARN, "Unparseable dir-signing-key argument");
564 return NULL;
566 } else {
567 log_fn(LOG_WARN, "Dir-signing-key token contained no key");
568 return NULL;
571 token_free(tok);
572 return key;
575 /** Return true iff <b>key</b> is allowed to sign directories.
577 static int dir_signing_key_is_trusted(crypto_pk_env_t *key)
579 char digest[DIGEST_LEN];
580 if (!key) return 0;
581 if (crypto_pk_get_digest(key, digest) < 0) {
582 log_fn(LOG_WARN, "Error computing dir-signing-key digest");
583 return 0;
585 if(!router_digest_is_trusted_dir(digest)) {
586 log_fn(LOG_WARN, "Listed dir-signing-key is not trusted");
587 return 0;
589 return 1;
592 /** Check whether the K_DIRECTORY_SIGNATURE token in <b>tok</b> has a
593 * good signature for <b>digest</b>.
595 * If <b>declared_key</b> is set, the directory has declared what key
596 * was used to sign it, so we will use that key only if it is an
597 * authoritative directory signing key.
599 * Otherwise, try to look up the router whose nickname is given in the
600 * directory-signature token. If this fails, or the named router is
601 * not authoritative, try to use pkey.
603 * (New callers should always use <b>declared_key</b> when possible;
604 * <b>pkey is only for debugging.)
606 static int check_directory_signature(const char *digest,
607 directory_token_t *tok,
608 crypto_pk_env_t *pkey,
609 crypto_pk_env_t *declared_key)
611 char signed_digest[PK_BYTES];
612 routerinfo_t *r;
613 crypto_pk_env_t *_pkey = NULL;
615 if (tok->n_args != 1) {
616 log_fn(LOG_WARN, "Too many or too few arguments to directory-signature");
617 return -1;
620 if (declared_key) {
621 if (dir_signing_key_is_trusted(declared_key))
622 _pkey = declared_key;
624 if (!_pkey) {
625 r = router_get_by_nickname(tok->args[0]);
626 log_fn(LOG_DEBUG, "Got directory signed by %s", tok->args[0]);
627 if (r && r->is_trusted_dir) {
628 _pkey = r->identity_pkey;
629 } else if (!r && pkey) {
630 /* pkey provided for debugging purposes. */
631 _pkey = pkey;
632 } else if (!r) {
633 log_fn(LOG_WARN, "Directory was signed by unrecognized server %s",
634 tok->args[0]);
635 return -1;
636 } else if (r && !r->is_trusted_dir) {
637 log_fn(LOG_WARN, "Directory was signed by non-trusted server %s",
638 tok->args[0]);
639 return -1;
643 if (strcmp(tok->object_type, "SIGNATURE") || tok->object_size != 128) {
644 log_fn(LOG_WARN, "Bad object type or length on directory signature");
645 return -1;
648 tor_assert(_pkey);
650 if (crypto_pk_public_checksig(_pkey, tok->object_body, 128, signed_digest)
651 != 20) {
652 log_fn(LOG_WARN, "Error reading directory: invalid signature.");
653 return -1;
655 log_fn(LOG_DEBUG,"Signed directory hash starts %s", hex_str(signed_digest,4));
656 if (memcmp(digest, signed_digest, 20)) {
657 log_fn(LOG_WARN, "Error reading directory: signature does not match.");
658 return -1;
660 return 0;
663 /** Given a string *<b>s</b> containing a concatenated sequence of router
664 * descriptors, parses them and stores the result in *<b>dest</b>. If
665 * good_nickname_list is provided, then routers are marked as
666 * running/nonrunning and verified/unverified based on their status in the
667 * list. Otherwise, all routers are marked running and verified. Advances
668 * *s to a point immediately following the last router entry. Returns 0 on
669 * success and -1 on failure.
672 router_parse_list_from_string(const char **s, routerlist_t **dest,
673 smartlist_t *good_nickname_list,
674 time_t published_on)
676 routerinfo_t *router;
677 smartlist_t *routers;
678 const char *end;
680 tor_assert(s && *s);
682 routers = smartlist_create();
684 while (1) {
685 *s = eat_whitespace(*s);
686 /* Don't start parsing the rest of *s unless it contains a router. */
687 if (strcmpstart(*s, "router ")!=0)
688 break;
689 if ((end = strstr(*s+1, "\nrouter "))) {
690 end++;
691 } else if ((end = strstr(*s+1, "\ndirectory-signature"))) {
692 end++;
693 } else {
694 end = *s+strlen(*s);
697 router = router_parse_entry_from_string(*s, end);
698 *s = end;
699 if (!router) {
700 log_fn(LOG_WARN, "Error reading router; skipping");
701 continue;
704 if (good_nickname_list) {
705 router_update_status_from_smartlist(router, published_on,
706 good_nickname_list);
707 } else {
708 router->is_running = 1; /* start out assuming all dirservers are up */
709 router->is_verified = 1;
710 router->status_set_at = time(NULL);
712 smartlist_add(routers, router);
713 log_fn(LOG_DEBUG,"just added router #%d.",smartlist_len(routers));
716 if (*dest)
717 routerlist_free(*dest);
718 *dest = tor_malloc(sizeof(routerlist_t));
719 (*dest)->routers = routers;
720 (*dest)->software_versions = NULL;
722 return 0;
726 /** Helper function: reads a single router entry from *<b>s</b> ...
727 * *<b>end</b>. Mallocs a new router and returns it if all goes well, else
728 * returns NULL.
730 routerinfo_t *router_parse_entry_from_string(const char *s,
731 const char *end) {
732 routerinfo_t *router = NULL;
733 char signed_digest[128];
734 char digest[128];
735 smartlist_t *tokens = NULL, *exit_policy_tokens = NULL;
736 directory_token_t *tok;
737 int t;
738 int ports_set, bw_set;
740 if (!end) {
741 end = s + strlen(s);
744 if (router_get_router_hash(s, digest) < 0) {
745 log_fn(LOG_WARN, "Couldn't compute router hash.");
746 return NULL;
748 tokens = smartlist_create();
749 if (tokenize_string(s,end,tokens,0)) {
750 log_fn(LOG_WARN, "Error tokeninzing router descriptor."); goto err;
753 if (smartlist_len(tokens) < 2) {
754 log_fn(LOG_WARN, "Impossibly short router descriptor.");
755 goto err;
757 if ((tok = find_first_by_keyword(tokens, _UNRECOGNIZED))) {
758 log_fn(LOG_WARN, "Unrecognized keyword \"%s\"; skipping descriptor.",
759 tok->args[0]);
760 goto err;
763 tok = smartlist_get(tokens,0);
764 if (tok->tp != K_ROUTER) {
765 log_fn(LOG_WARN,"Entry does not start with \"router\"");
766 goto err;
769 router = tor_malloc_zero(sizeof(routerinfo_t));
770 router->onion_pkey = router->identity_pkey = NULL;
771 ports_set = bw_set = 0;
773 if (tok->n_args == 2 || tok->n_args == 5 || tok->n_args == 6) {
774 router->nickname = tor_strdup(tok->args[0]);
775 if (!is_legal_nickname(router->nickname)) {
776 log_fn(LOG_WARN,"Router nickname is invalid");
777 goto err;
779 router->address = tor_strdup(tok->args[1]);
780 router->addr = 0;
782 if (tok->n_args >= 5) {
783 router->or_port = (uint16_t) tor_parse_long(tok->args[2],10,0,65535,NULL,NULL);
784 router->socks_port = (uint16_t) tor_parse_long(tok->args[3],10,0,65535,NULL,NULL);
785 router->dir_port = (uint16_t) tor_parse_long(tok->args[4],10,0,65535,NULL,NULL);
786 ports_set = 1;
788 } else {
789 log_fn(LOG_WARN,"Wrong # of arguments to \"router\" (%d)",tok->n_args);
790 goto err;
793 tok = find_first_by_keyword(tokens, K_PORTS);
794 if (tok && ports_set) {
795 log_fn(LOG_WARN,"Redundant ports line");
796 goto err;
797 } else if (tok) {
798 if (tok->n_args != 3) {
799 log_fn(LOG_WARN,"Wrong # of arguments to \"ports\"");
800 goto err;
802 router->or_port = tor_parse_long(tok->args[0],10,0,65535,NULL,NULL);
803 router->socks_port = tor_parse_long(tok->args[1],10,0,65535,NULL,NULL);
804 router->dir_port = tor_parse_long(tok->args[2],10,0,65535,NULL,NULL);
805 ports_set = 1;
808 tok = find_first_by_keyword(tokens, K_DIRCACHEPORT);
809 if (tok) {
810 if (router->dir_port)
811 log_fn(LOG_WARN,"Redundant dircacheport line");
812 if (tok->n_args != 1) {
813 log_fn(LOG_WARN,"Wrong # of arguments to \"dircacheport\"");
814 goto err;
816 router->dir_port = tor_parse_long(tok->args[0],10,1,65535,NULL,NULL);
819 tok = find_first_by_keyword(tokens, K_BANDWIDTH);
820 if (tok && bw_set) {
821 log_fn(LOG_WARN,"Redundant bandwidth line");
822 goto err;
823 } else if (tok) {
824 /* XXX set this to "< 3" once 0.0.7 is obsolete */
825 if (tok->n_args < 2) {
826 log_fn(LOG_WARN,"Not enough arguments to \"bandwidth\"");
827 goto err;
829 router->bandwidthrate = tor_parse_long(tok->args[0],10,0,INT_MAX,NULL,NULL);
830 router->bandwidthburst = tor_parse_long(tok->args[1],10,0,INT_MAX,NULL,NULL);
831 if(tok->n_args > 2)
832 router->bandwidthcapacity = tor_parse_long(tok->args[2],10,0,INT_MAX,NULL,NULL);
833 bw_set = 1;
836 if ((tok = find_first_by_keyword(tokens, K_UPTIME))) {
837 if (tok->n_args != 1) {
838 log_fn(LOG_WARN, "Unrecognized number of args on K_UPTIME; skipping.");
839 } else {
840 router->uptime = tor_parse_long(tok->args[0],10,0,LONG_MAX,NULL,NULL);
844 if (!(tok = find_first_by_keyword(tokens, K_PUBLISHED))) {
845 log_fn(LOG_WARN, "Missing published time"); goto err;
847 tor_assert(tok->n_args == 1);
848 if (parse_iso_time(tok->args[0], &router->published_on) < 0)
849 goto err;
851 if (!(tok = find_first_by_keyword(tokens, K_ONION_KEY))) {
852 log_fn(LOG_WARN, "Missing onion key"); goto err;
854 /* XXX Check key length */
855 router->onion_pkey = tok->key;
856 tok->key = NULL; /* Prevent free */
858 if (!(tok = find_first_by_keyword(tokens, K_SIGNING_KEY))) {
859 log_fn(LOG_WARN, "Missing identity key"); goto err;
861 /* XXX Check key length */
862 router->identity_pkey = tok->key;
863 tok->key = NULL; /* Prevent free */
864 if (crypto_pk_get_digest(router->identity_pkey,router->identity_digest)){
865 log_fn(LOG_WARN, "Couldn't calculate key digest"); goto err;
868 if ((tok = find_first_by_keyword(tokens, K_PLATFORM))) {
869 router->platform = tor_strdup(tok->args[0]);
872 exit_policy_tokens = find_all_exitpolicy(tokens);
873 SMARTLIST_FOREACH(exit_policy_tokens, directory_token_t *, t,
874 if (router_add_exit_policy(router,t)<0) {
875 log_fn(LOG_WARN,"Error in exit policy"); goto err;}
878 if (!(tok = find_first_by_keyword(tokens, K_ROUTER_SIGNATURE))) {
879 log_fn(LOG_WARN, "Missing router signature"); goto err;
881 if (strcmp(tok->object_type, "SIGNATURE") || tok->object_size != 128) {
882 log_fn(LOG_WARN, "Bad object type or length on router signature");
883 goto err;
885 if ((t=crypto_pk_public_checksig(router->identity_pkey, tok->object_body,
886 128, signed_digest)) != 20) {
887 log_fn(LOG_WARN, "Invalid signature %d",t); goto err;
889 if (memcmp(digest, signed_digest, 20)) {
890 log_fn(LOG_WARN, "Mismatched signature"); goto err;
893 if (!ports_set) {
894 log_fn(LOG_WARN,"No ports declared; failing."); goto err;
896 if (!bw_set) {
897 log_fn(LOG_WARN,"No bandwidth declared; failing."); goto err;
899 if(!router->or_port) {
900 log_fn(LOG_WARN,"or_port unreadable or 0. Failing.");
901 goto err;
903 if (!router->bandwidthrate) {
904 log_fn(LOG_WARN,"bandwidthrate unreadable or 0. Failing.");
905 goto err;
907 if (!router->platform) {
908 router->platform = tor_strdup("<unknown>");
911 log_fn(LOG_DEBUG,"or_port %d, socks_port %d, dir_port %d, bandwidthrate %u, bandwidthburst %u.",
912 router->or_port, router->socks_port, router->dir_port,
913 (unsigned) router->bandwidthrate, (unsigned) router->bandwidthburst);
916 goto done;
917 return router;
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 /** Parse the exit policy in the string <b>s</b> and return it.
935 struct exit_policy_t *
936 router_parse_exit_policy_from_string(const char *s)
938 directory_token_t *tok = NULL;
939 const char *cp;
940 char *tmp;
941 struct exit_policy_t *r;
942 size_t len, idx;
944 /* *s might not end with \n, so we need to extend it with one. */
945 len = strlen(s);
946 cp = tmp = tor_malloc(len+2);
947 for (idx = 0; idx < len; ++idx) {
948 tmp[idx] = tolower(s[idx]);
950 tmp[len]='\n';
951 tmp[len+1]='\0';
952 tok = get_next_token(&cp, RTR_ONLY);
953 if (tok->tp == _ERR) {
954 log_fn(LOG_WARN, "Error reading exit policy: %s", tok->error);
955 goto err;
957 if (tok->tp != K_ACCEPT && tok->tp != K_REJECT) {
958 log_fn(LOG_WARN, "Expected 'accept' or 'reject'.");
959 goto err;
962 /* Now that we've gotten an exit policy, add it to the router. */
963 r = router_parse_exit_policy(tok);
964 goto done;
965 err:
966 r = NULL;
967 done:
968 tor_free(tmp);
969 token_free(tok);
970 return r;
973 int router_add_exit_policy_from_string(routerinfo_t *router, const char *s)
975 struct exit_policy_t *newe, *tmpe;
976 newe = router_parse_exit_policy_from_string(s);
977 if (!newe)
978 return -1;
979 for (tmpe = router->exit_policy; tmpe; tmpe=tmpe->next)
981 tmpe->next = newe;
983 return 0;
987 static int router_add_exit_policy(routerinfo_t *router,directory_token_t *tok)
989 struct exit_policy_t *newe, **tmpe;
990 newe = router_parse_exit_policy(tok);
991 if (!newe)
992 return -1;
993 for (tmpe = &router->exit_policy; *tmpe; tmpe=&((*tmpe)->next))
995 *tmpe = newe;
997 return 0;
1000 /** Given a K_ACCEPT or K_REJECT token and a router, create and return
1001 * a new exit_policy_t corresponding to the token. */
1002 static struct exit_policy_t *
1003 router_parse_exit_policy(directory_token_t *tok) {
1005 struct exit_policy_t*newe;
1006 struct in_addr in;
1007 char *arg, *address, *mask, *port, *endptr;
1008 int bits;
1010 tor_assert(tok->tp == K_REJECT || tok->tp == K_ACCEPT);
1012 if (tok->n_args != 1)
1013 return NULL;
1014 arg = tok->args[0];
1016 newe = tor_malloc_zero(sizeof(struct exit_policy_t));
1018 newe->string = tor_malloc(8+strlen(arg));
1019 if (tok->tp == K_REJECT) {
1020 strcpy(newe->string, "reject ");
1021 newe->policy_type = EXIT_POLICY_REJECT;
1022 } else {
1023 strcpy(newe->string, "accept ");
1024 newe->policy_type = EXIT_POLICY_ACCEPT;
1026 strcat(newe->string, arg); /* can't overflow */
1028 address = arg;
1029 mask = strchr(arg,'/');
1030 port = strchr(mask?mask:arg,':');
1031 /* Break 'arg' into separate strings. 'arg' was already strdup'd by
1032 * _router_get_next_token, so it's safe to modify.
1034 if (mask)
1035 *mask++ = 0;
1036 if (port)
1037 *port++ = 0;
1039 if (strcmp(address, "*") == 0) {
1040 newe->addr = 0;
1041 } else if (tor_inet_aton(address, &in) != 0) {
1042 newe->addr = ntohl(in.s_addr);
1043 } else {
1044 log_fn(LOG_WARN, "Malformed IP %s in exit policy; rejecting.",
1045 address);
1046 goto policy_read_failed;
1048 if (!mask) {
1049 if (strcmp(address, "*") == 0)
1050 newe->msk = 0;
1051 else
1052 newe->msk = 0xFFFFFFFFu;
1053 } else {
1054 endptr = NULL;
1055 bits = (int) strtol(mask, &endptr, 10);
1056 if (!*endptr) {
1057 /* strtol handled the whole mask. */
1058 if (bits < 0 || bits > 32) {
1059 log_fn(LOG_WARN, "Bad number of mask bits on exit policy; rejecting.");
1060 goto policy_read_failed;
1062 newe->msk = ~((1<<(32-bits))-1);
1063 } else if (tor_inet_aton(mask, &in) != 0) {
1064 newe->msk = ntohl(in.s_addr);
1065 } else {
1066 log_fn(LOG_WARN, "Malformed mask %s on exit policy; rejecting.",
1067 mask);
1068 goto policy_read_failed;
1071 if (!port || strcmp(port, "*") == 0) {
1072 newe->prt_min = 0;
1073 newe->prt_max = 65535;
1074 } else {
1075 endptr = NULL;
1076 newe->prt_min = (uint16_t) tor_parse_long(port, 10, 1, 65535,
1077 NULL, &endptr);
1078 if (*endptr == '-') {
1079 port = endptr+1;
1080 endptr = NULL;
1081 newe->prt_max = (uint16_t) tor_parse_long(port, 10, 1, 65535, NULL,
1082 &endptr);
1083 if (*endptr || !newe->prt_max) {
1084 log_fn(LOG_WARN, "Malformed port %s on exit policy; rejecting.",
1085 port);
1087 } else if (*endptr || !newe->prt_min) {
1088 log_fn(LOG_WARN, "Malformed port %s on exit policy; rejecting.",
1089 port);
1090 goto policy_read_failed;
1091 } else {
1092 newe->prt_max = newe->prt_min;
1094 if (newe->prt_min > newe->prt_max) {
1095 log_fn(LOG_WARN,"Insane port range on exit policy; rejecting.");
1096 goto policy_read_failed;
1100 in.s_addr = htonl(newe->addr);
1101 address = tor_strdup(inet_ntoa(in));
1102 in.s_addr = htonl(newe->msk);
1103 log_fn(LOG_DEBUG,"%s %s/%s:%d-%d",
1104 newe->policy_type == EXIT_POLICY_REJECT ? "reject" : "accept",
1105 address, inet_ntoa(in), newe->prt_min, newe->prt_max);
1106 tor_free(address);
1108 newe->next = NULL;
1109 return newe;
1111 policy_read_failed:
1112 tor_assert(newe->string);
1113 log_fn(LOG_WARN,"Couldn't parse line '%s'. Dropping", newe->string);
1114 tor_free(newe->string);
1115 tor_free(newe);
1116 return NULL;
1120 * Low-level tokenizer for router descriptors and directories.
1123 /** Free all resources allocated for <b>tok</b> */
1124 static void
1125 token_free(directory_token_t *tok)
1127 int i;
1128 tor_assert(tok);
1129 if (tok->args) {
1130 for (i = 0; i < tok->n_args; ++i) {
1131 tor_free(tok->args[i]);
1133 tor_free(tok->args);
1135 tor_free(tok->object_type);
1136 tor_free(tok->object_body);
1137 if (tok->key)
1138 crypto_free_pk_env(tok->key);
1139 tor_free(tok);
1142 /** Helper function: read the next token from *s, advance *s to the end
1143 * of the token, and return the parsed token. If 'where' is DIR_ONLY
1144 * or RTR_ONLY, reject all tokens of the wrong type.
1146 static directory_token_t *
1147 get_next_token(const char **s, where_syntax where) {
1148 const char *next, *obstart;
1149 int i, done, allocated, is_opt;
1150 directory_token_t *tok;
1151 arg_syntax a_syn;
1152 obj_syntax o_syn = NO_OBJ;
1154 #define RET_ERR(msg) \
1155 do { if (tok) token_free(tok); \
1156 tok = tor_malloc_zero(sizeof(directory_token_t));\
1157 tok->tp = _ERR; \
1158 tok->error = msg; \
1159 goto done_tokenizing; } while (0)
1161 tok = tor_malloc_zero(sizeof(directory_token_t));
1162 tok->tp = _ERR;
1164 *s = eat_whitespace(*s);
1165 if (!**s) {
1166 tok->tp = _EOF;
1167 return tok;
1169 next = find_whitespace(*s);
1170 if (!next) {
1171 tok->error = "Unexpected EOF"; return tok;
1173 /* It's a keyword... but which one? */
1174 is_opt = !strncmp("opt", *s, next-*s);
1175 if (is_opt) {
1176 *s = eat_whitespace(next);
1177 next = NULL;
1178 if (**s)
1179 next = find_whitespace(*s);
1180 if (!**s || !next) {
1181 RET_ERR("opt without keyword");
1184 for (i = 0; token_table[i].t ; ++i) {
1185 if (!strncmp(token_table[i].t, *s, next-*s)) {
1186 /* We've found the keyword. */
1187 tok->tp = token_table[i].v;
1188 a_syn = token_table[i].s;
1189 o_syn = token_table[i].os;
1190 if (token_table[i].ws != ANY && token_table[i].ws != where) {
1191 if (where == DIR_ONLY) {
1192 RET_ERR("Found a router-only token in a directory section");
1193 } else {
1194 RET_ERR("Found a directory-only token in a router descriptor");
1197 if (a_syn == ARGS) {
1198 /* This keyword takes multiple arguments. */
1199 i = 0;
1200 done = (*next == '\n');
1201 allocated = 32;
1202 tok->args = tor_malloc(sizeof(char*)*32);
1203 *s = eat_whitespace_no_nl(next);
1204 while (**s != '\n' && !done) {
1205 next = find_whitespace(*s);
1206 if (*next == '\n')
1207 done = 1;
1208 if (i == allocated) {
1209 allocated *= 2;
1210 tok->args = tor_realloc(tok->args,sizeof(char*)*allocated);
1212 tok->args[i++] = tor_strndup(*s,next-*s);
1213 *s = eat_whitespace_no_nl(next+1);
1215 tok->n_args = i;
1216 } else if (a_syn == CONCAT_ARGS) {
1217 /* The keyword takes the line as a single argument */
1218 *s = eat_whitespace_no_nl(next);
1219 next = strchr(*s, '\n');
1220 if (!next)
1221 RET_ERR("Unexpected EOF");
1222 tok->args = tor_malloc(sizeof(char*));
1223 tok->args[0] = tor_strndup(*s,next-*s);
1224 tok->n_args = 1;
1225 *s = eat_whitespace_no_nl(next+1);
1226 } else {
1227 /* The keyword takes no arguments. */
1228 tor_assert(a_syn == NO_ARGS);
1229 *s = eat_whitespace_no_nl(next);
1230 if (**s != '\n') {
1231 RET_ERR("Unexpected arguments");
1233 tok->n_args = 0;
1234 *s = eat_whitespace_no_nl(*s+1);
1236 break;
1239 if (tok->tp == _ERR) {
1240 if (is_opt) {
1241 tok->tp = K_OPT;
1242 *s = eat_whitespace_no_nl(next);
1243 next = strchr(*s,'\n');
1244 if (!next)
1245 RET_ERR("Unexpected EOF");
1246 tok->args = tor_malloc(sizeof(char*));
1247 tok->args[0] = tor_strndup(*s,next-*s);
1248 tok->n_args = 1;
1249 *s = eat_whitespace_no_nl(next+1);
1250 o_syn = OBJ_OK;
1251 } else {
1252 tok->tp = _UNRECOGNIZED;
1253 next = strchr(*s, '\n');
1254 if (!next) {
1255 RET_ERR("Unexpected EOF");
1257 tok->args = tor_malloc(sizeof(char*));
1258 tok->args[0] = tor_strndup(*s,next-*s);
1259 tok->n_args = 1;
1260 *s = next+1;
1261 o_syn = OBJ_OK;
1264 *s = eat_whitespace(*s);
1265 if (strcmpstart(*s, "-----BEGIN ")) {
1266 goto done_tokenizing;
1268 obstart = *s;
1269 *s += 11; /* length of "-----BEGIN ". */
1270 next = strchr(*s, '\n');
1271 if (next-*s < 6 || strcmpstart(next-5, "-----\n")) {
1272 RET_ERR("Malformed object: bad begin line");
1274 tok->object_type = tor_strndup(*s, next-*s-5);
1275 *s = next+1;
1276 next = strstr(*s, "-----END ");
1277 if (!next) {
1278 RET_ERR("Malformed object: missing end line");
1280 if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) {
1281 if (strcmpstart(next, "-----END RSA PUBLIC KEY-----\n"))
1282 RET_ERR("Malformed object: mismatched end line");
1283 next = strchr(next,'\n')+1;
1284 tok->key = crypto_new_pk_env();
1285 if (crypto_pk_read_public_key_from_string(tok->key, obstart, next-obstart))
1286 RET_ERR("Couldn't parse public key.");
1287 *s = next;
1288 } else {
1289 tok->object_body = tor_malloc(next-*s); /* really, this is too much RAM. */
1290 i = base64_decode(tok->object_body, 256, *s, next-*s);
1291 if (i<0) {
1292 RET_ERR("Malformed object: bad base64-encoded data");
1294 tok->object_size = i;
1295 *s = next + 9; /* length of "-----END ". */
1296 i = strlen(tok->object_type);
1297 if (strncmp(*s, tok->object_type, i) || strcmpstart(*s+i, "-----\n")) {
1298 RET_ERR("Malformed object: mismatched end tag");
1300 *s += i+6;
1302 switch(o_syn)
1304 case NO_OBJ:
1305 if (tok->object_body)
1306 RET_ERR("Unexpected object for keyword");
1307 if (tok->key)
1308 RET_ERR("Unexpected public key for keyword");
1309 break;
1310 case NEED_OBJ:
1311 if (!tok->object_body)
1312 RET_ERR("Missing object for keyword");
1313 break;
1314 case NEED_KEY:
1315 if (!tok->key)
1316 RET_ERR("Missing public key for keyword");
1317 break;
1318 case OBJ_OK:
1319 break;
1322 done_tokenizing:
1324 #if 0
1325 for (i = 0; token_table[i].t ; ++i) {
1326 if (token_table[i].v == tok->tp) {
1327 fputs(token_table[i].t, stdout);
1328 break;
1329 i = -1;
1332 if (i) {
1333 if (tok->tp == _UNRECOGNIZED) fputs("UNRECOGNIZED", stdout);
1334 if (tok->tp == _ERR) fputs("ERR",stdout);
1335 if (tok->tp == _EOF) fputs("EOF",stdout);
1336 if (tok->tp == _NIL) fputs("_NIL",stdout);
1338 for(i = 0; i < tok->n_args; ++i) {
1339 fprintf(stdout," \"%s\"", tok->args[i]);
1341 if (tok->error) { fprintf(stdout," *%s*", tok->error); }
1342 fputs("\n",stdout);
1343 #endif
1346 return tok;
1347 #undef RET_ERR
1350 /** Read all tokens from a string between <b>start</b> and <b>end</b>, and add
1351 * them to <b>out</b>. If <b>is_dir</b> is true, reject all non-directory
1352 * tokens; else reject all non-routerdescriptor tokens.
1354 static int
1355 tokenize_string(const char *start, const char *end, smartlist_t *out,
1356 int is_dir)
1358 const char **s;
1359 directory_token_t *tok = NULL;
1360 where_syntax where = is_dir ? DIR_ONLY : RTR_ONLY;
1361 s = &start;
1362 while (*s < end && (!tok || tok->tp != _EOF)) {
1363 tok = get_next_token(s, where);
1364 if (tok->tp == _ERR) {
1365 log_fn(LOG_WARN, "parse error: %s", tok->error);
1366 return -1;
1368 smartlist_add(out, tok);
1369 *s = eat_whitespace(*s);
1372 return 0;
1375 /** Find the first token in <b>s</b> whose keyword is <b>keyword</b>; return
1376 * NULL if no such keyword is found.
1378 static directory_token_t *
1379 find_first_by_keyword(smartlist_t *s, directory_keyword keyword)
1381 SMARTLIST_FOREACH(s, directory_token_t *, t, if (t->tp == keyword) return t);
1382 return NULL;
1385 /** Return a newly allocated smartlist of all accept or reject tokens in
1386 * <b>s</b>.
1388 static smartlist_t *
1389 find_all_exitpolicy(smartlist_t *s)
1391 smartlist_t *out = smartlist_create();
1392 SMARTLIST_FOREACH(s, directory_token_t *, t,
1393 if (t->tp == K_ACCEPT || t->tp == K_REJECT)
1394 smartlist_add(out,t));
1395 return out;
1398 /** Compute the SHA digest of the substring of <b>s</b> taken from the first
1399 * occurrence of <b>start_str</b> through the first newline after the first
1400 * subsequent occurrence of <b>end_str</b>; store the 20-byte result in
1401 * <b>digest</b>; return 0 on success.
1403 * If no such substring exists, return -1.
1405 static int router_get_hash_impl(const char *s, char *digest,
1406 const char *start_str,
1407 const char *end_str)
1409 char *start, *end;
1410 start = strstr(s, start_str);
1411 if (!start) {
1412 log_fn(LOG_WARN,"couldn't find \"%s\"",start_str);
1413 return -1;
1415 if (start != s && *(start-1) != '\n') {
1416 log_fn(LOG_WARN, "first occurrence of \"%s\" is not at the start of a line",
1417 start_str);
1418 return -1;
1420 end = strstr(start+strlen(start_str), end_str);
1421 if (!end) {
1422 log_fn(LOG_WARN,"couldn't find \"%s\"",end_str);
1423 return -1;
1425 end = strchr(end+strlen(end_str), '\n');
1426 if (!end) {
1427 log_fn(LOG_WARN,"couldn't find EOL");
1428 return -1;
1430 ++end;
1432 if (crypto_digest(start, end-start, digest)) {
1433 log_fn(LOG_WARN,"couldn't compute digest");
1434 return -1;
1437 return 0;
1440 /** Parse the Tor version of the platform string <b>platform</b>,
1441 * and compare it to the version in <b>cutoff</b>. Return 1 if
1442 * the router is at least as new as the cutoff, else return 0.
1444 int tor_version_as_new_as(const char *platform, const char *cutoff) {
1445 tor_version_t cutoff_version, router_version;
1446 char *s, *start;
1447 char tmp[128];
1449 if(tor_version_parse(cutoff, &cutoff_version)<0) {
1450 log_fn(LOG_WARN,"Bug: cutoff version '%s' unparsable.",cutoff);
1451 return 0;
1453 if(strcmpstart(platform,"Tor ")) /* nonstandard Tor; be safe and say yes */
1454 return 1;
1456 start = (char *)eat_whitespace(platform+3);
1457 if (!*start) return 0;
1458 s = (char *)find_whitespace(start); /* also finds '\0', which is fine */
1459 if((size_t)(s-start+1) >= sizeof(tmp)) /* too big, no */
1460 return 0;
1461 strlcpy(tmp, start, s-start+1);
1463 if(tor_version_parse(tmp, &router_version)<0) {
1464 log_fn(LOG_INFO,"Router version '%s' unparsable.",tmp);
1465 return 1; /* be safe and say yes */
1468 return tor_version_compare(&router_version, &cutoff_version) >= 0;
1471 /** DOCDOC */
1472 int tor_version_parse(const char *s, tor_version_t *out)
1474 char *eos=NULL, *cp=NULL;
1475 /* Format is:
1476 * NUM dot NUM dot NUM [ ( pre | rc | dot ) NUM [ -cvs ] ]
1478 tor_assert(s && out);
1479 memset(out, 0, sizeof(tor_version_t));
1481 /* Get major. */
1482 out->major = strtol(s,&eos,10);
1483 if (!eos || eos==s || *eos != '.') return -1;
1484 cp = eos+1;
1486 /* Get minor */
1487 out->minor = strtol(cp,&eos,10);
1488 if (!eos || eos==cp || *eos != '.') return -1;
1489 cp = eos+1;
1491 /* Get micro */
1492 out->micro = strtol(cp,&eos,10);
1493 if (!eos || eos==cp) return -1;
1494 if (!*eos) {
1495 out->status = VER_RELEASE;
1496 out->patchlevel = 0;
1497 out->cvs = IS_NOT_CVS;
1498 return 0;
1500 cp = eos;
1502 /* Get status */
1503 if (*cp == '.') {
1504 out->status = VER_RELEASE;
1505 ++cp;
1506 } else if (0==strncmp(cp, "pre", 3)) {
1507 out->status = VER_PRE;
1508 cp += 3;
1509 } else if (0==strncmp(cp, "rc", 2)) {
1510 out->status = VER_RC;
1511 cp += 2;
1512 } else {
1513 return -1;
1516 /* Get patchlevel */
1517 out->patchlevel = strtol(cp,&eos,10);
1518 if (!eos || eos==cp) return -1;
1519 cp = eos;
1521 /* Get cvs status. */
1522 if (!*eos) {
1523 out->cvs = IS_NOT_CVS;
1524 } else if (0==strcmp(cp, "-cvs")) {
1525 out->cvs = IS_CVS;
1526 } else {
1527 return -1;
1530 return 0;
1533 /** Compare two tor versions; Return <0 if a < b; 0 if a ==b, >0 if a >
1534 * b. */
1535 int tor_version_compare(tor_version_t *a, tor_version_t *b)
1537 int i;
1538 tor_assert(a && b);
1539 if ((i = a->major - b->major))
1540 return i;
1541 else if ((i = a->minor - b->minor))
1542 return i;
1543 else if ((i = a->micro - b->micro))
1544 return i;
1545 else if ((i = a->status - b->status))
1546 return i;
1547 else if ((i = a->patchlevel - b->patchlevel))
1548 return i;
1549 else if ((i = a->cvs - b->cvs))
1550 return i;
1551 else
1552 return 0;
1556 Local Variables:
1557 mode:c
1558 indent-tabs-mode:nil
1559 c-basic-offset:2
1560 End: