Commit ac552573 authored by Nick Mathewson's avatar Nick Mathewson
Browse files

Make router/directory parsing nondestructive and more const-friendly


svn:r890
parent 8bd7c94b
......@@ -441,7 +441,7 @@ int crypto_pk_write_public_key_to_string(crypto_pk_env_t *env, char **dest, int
return 0;
}
int crypto_pk_read_public_key_from_string(crypto_pk_env_t *env, char *src, int len) {
int crypto_pk_read_public_key_from_string(crypto_pk_env_t *env, const char *src, int len) {
BIO *b;
assert(env && src);
......@@ -820,7 +820,7 @@ crypto_cipher_advance(crypto_cipher_env_t *env, long delta)
/* SHA-1 */
int crypto_SHA_digest(unsigned char *m, int len, unsigned char *digest)
int crypto_SHA_digest(const unsigned char *m, int len, unsigned char *digest)
{
assert(m && digest);
return (SHA1(m,len,digest) == NULL);
......@@ -1034,7 +1034,7 @@ char *crypto_perror()
}
int
base64_encode(char *dest, int destlen, char *src, int srclen)
base64_encode(char *dest, int destlen, const char *src, int srclen)
{
EVP_ENCODE_CTX ctx;
int len, ret;
......@@ -1046,13 +1046,13 @@ base64_encode(char *dest, int destlen, char *src, int srclen)
return -1;
EVP_EncodeInit(&ctx);
EVP_EncodeUpdate(&ctx, dest, &len, src, srclen);
EVP_EncodeUpdate(&ctx, dest, &len, (char*) src, srclen);
EVP_EncodeFinal(&ctx, dest+len, &ret);
ret += len;
return ret;
}
int
base64_decode(char *dest, int destlen, char *src, int srclen)
base64_decode(char *dest, int destlen, const char *src, int srclen)
{
EVP_ENCODE_CTX ctx;
int len, ret;
......@@ -1063,7 +1063,7 @@ base64_decode(char *dest, int destlen, char *src, int srclen)
return -1;
EVP_DecodeInit(&ctx);
EVP_DecodeUpdate(&ctx, dest, &len, src, srclen);
EVP_DecodeUpdate(&ctx, dest, &len, (char*) src, srclen);
EVP_DecodeFinal(&ctx, dest, &ret);
ret += len;
return ret;
......
......@@ -38,7 +38,7 @@ int crypto_pk_generate_key(crypto_pk_env_t *env);
int crypto_pk_read_private_key_from_file(crypto_pk_env_t *env, FILE *src);
int crypto_pk_read_public_key_from_file(crypto_pk_env_t *env, FILE *src);
int crypto_pk_write_public_key_to_string(crypto_pk_env_t *env, char **dest, int *len);
int crypto_pk_read_public_key_from_string(crypto_pk_env_t *env, char *src, int len);
int crypto_pk_read_public_key_from_string(crypto_pk_env_t *env, const char *src, int len);
int crypto_pk_write_private_key_to_file(crypto_pk_env_t *env, FILE *dest);
int crypto_pk_write_private_key_to_filename(crypto_pk_env_t *env, const char *fname);
int crypto_pk_write_public_key_to_file(crypto_pk_env_t *env, FILE *dest);
......@@ -58,8 +58,8 @@ int crypto_pk_public_checksig(crypto_pk_env_t *env, unsigned char *from, int fro
int crypto_pk_get_fingerprint(crypto_pk_env_t *pk, char *fp_out);
int crypto_pk_check_fingerprint_syntax(const char *s);
int base64_encode(char *dest, int destlen, char *src, int srclen);
int base64_decode(char *dest, int destlen, char *src, int srclen);
int base64_encode(char *dest, int destlen, const char *src, int srclen);
int base64_decode(char *dest, int destlen, const char *src, int srclen);
/* Key negotiation */
typedef struct crypto_dh_env_st {
......@@ -95,7 +95,7 @@ int crypto_cipher_advance(crypto_cipher_env_t *env, long delta);
crypto_cipher_env_t *crypto_create_init_cipher(int cipher_type, char *key, char *iv, int encrypt_mode);
/* SHA-1 */
int crypto_SHA_digest(unsigned char *m, int len, unsigned char *digest);
int crypto_SHA_digest(const unsigned char *m, int len, unsigned char *digest);
/* random numbers */
int crypto_seed_rng();
......
......@@ -54,12 +54,21 @@ char *tor_strdup(const char *s) {
return dup;
}
char *tor_strndup(const char *s, size_t n) {
char *dup;
assert(s);
dup = tor_malloc(n+1);
strncpy(dup, s, n);
dup[n] = 0;
return dup;
}
/*
* String manipulation
*/
/* return the first char of s that is not whitespace and not a comment */
char *eat_whitespace(char *s) {
const char *eat_whitespace(const char *s) {
assert(s);
while(isspace(*s) || *s == '#') {
......@@ -75,14 +84,14 @@ char *eat_whitespace(char *s) {
return s;
}
char *eat_whitespace_no_nl(char *s) {
const char *eat_whitespace_no_nl(const char *s) {
while(*s == ' ' || *s == '\t')
++s;
return s;
}
/* return the first char of s that is whitespace or '#' or '\0 */
char *find_whitespace(char *s) {
const char *find_whitespace(const char *s) {
assert(s);
while(*s && !isspace(*s) && *s != '#')
......
......@@ -36,11 +36,12 @@ void *tor_malloc(size_t size);
void *tor_malloc_zero(size_t size);
void *tor_realloc(void *ptr, size_t size);
char *tor_strdup(const char *s);
char *tor_strndup(const char *s, size_t n);
#define tor_free(p) do {if(p) {free(p); (p)=NULL;}} while(0)
char *eat_whitespace(char *s);
char *eat_whitespace_no_nl(char *s);
char *find_whitespace(char *s);
const char *eat_whitespace(const char *s);
const char *eat_whitespace_no_nl(const char *s);
const char *find_whitespace(const char *s);
void tor_gettimeofday(struct timeval *timeval);
long tv_udiff(struct timeval *start, struct timeval *end);
......
......@@ -202,7 +202,8 @@ dirserv_add_descriptor(const char **desc)
routerinfo_t *ri = NULL;
int i, r;
char *start, *end;
char *desc_tmp = NULL, *cp;
char *desc_tmp = NULL;
const char *cp;
size_t desc_len;
start = strstr(*desc, "router ");
......@@ -218,9 +219,7 @@ dirserv_add_descriptor(const char **desc)
end = start+strlen(start);
}
desc_len = end-start;
cp = desc_tmp = tor_malloc(desc_len+1);
strncpy(desc_tmp, start, desc_len);
desc_tmp[desc_len]='\0';
cp = desc_tmp = tor_strndup(start, desc_len);
/* Check: is the descriptor syntactically valid? */
ri = router_get_entry_from_string(&cp);
......
......@@ -749,11 +749,11 @@ void router_get_routerlist(routerlist_t **prouterlist);
void routerinfo_free(routerinfo_t *router);
void router_mark_as_down(char *nickname);
int router_set_routerlist_from_file(char *routerfile);
int router_get_dir_hash(char *s, char *digest);
int router_get_router_hash(char *s, char *digest);
int router_set_routerlist_from_directory(char *s, crypto_pk_env_t *pkey);
routerinfo_t *router_get_entry_from_string(char**s);
int router_add_exit_policy_from_string(routerinfo_t *router, char *s);
int router_get_dir_hash(const char *s, char *digest);
int router_get_router_hash(const char *s, char *digest);
int router_set_routerlist_from_directory(const char *s, crypto_pk_env_t *pkey);
routerinfo_t *router_get_entry_from_string(const char **s);
int router_add_exit_policy_from_string(routerinfo_t *router, const char *s);
int router_supports_exit_address(uint32_t addr, uint16_t port,
routerinfo_t *router);
int router_compare_addr_to_exit_policy(uint32_t addr, uint16_t port,
......
......@@ -378,7 +378,8 @@ int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router,
int result=0;
struct exit_policy_t *tmpe;
#ifdef DEBUG_ROUTER_DUMP_ROUTER_TO_STRING
char *s_tmp, *s_dup;
char *s_tmp, *s_dup;
const char *cp;
routerinfo_t *ri_tmp;
#endif
......@@ -494,8 +495,8 @@ int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router,
s[written+1] = 0;
#ifdef DEBUG_ROUTER_DUMP_ROUTER_TO_STRING
s_tmp = s_dup = tor_strdup(s);
ri_tmp = router_get_entry_from_string(&s_tmp);
cp = s_tmp = s_dup = tor_strdup(s);
ri_tmp = router_get_entry_from_string(&cp);
if (!ri_tmp) {
log_fn(LOG_ERR, "We just generated a router descriptor we can't parse: <<%s>>",
s);
......
......@@ -21,17 +21,17 @@ extern or_options_t options; /* command-line and config-file options */
/****************************************************************************/
struct directory_token;
typedef struct directory_token directory_token_t;
struct directory_token_t;
typedef struct directory_token_t directory_token_t;
/* static function prototypes */
static int router_set_routerlist_from_string(char *s);
static int router_set_routerlist_from_string(const char *s);
static int
router_get_list_from_string_impl(char **s, routerlist_t **dest,
router_get_list_from_string_impl(const char **s, routerlist_t **dest,
int n_good_nicknames,
const char **good_nickname_lst);
static int
router_get_routerlist_from_directory_impl(char *s, routerlist_t **dest,
router_get_routerlist_from_directory_impl(const char *s, routerlist_t **dest,
crypto_pk_env_t *pkey);
static int router_add_exit_policy(routerinfo_t *router,
directory_token_t *tok);
......@@ -245,30 +245,38 @@ typedef enum {
_SIGNATURE,
_PUBLIC_KEY,
_ERR,
_EOF
_EOF,
_NIL
} directory_keyword;
struct token_table_ent { char *t; int v; };
typedef enum {
NO_ARGS,
ARGS,
CONCAT_ARGS,
} arg_syntax;
struct token_table_ent { char *t; int v; arg_syntax s; };
static struct token_table_ent token_table[] = {
{ "accept", K_ACCEPT },
{ "directory-signature", K_DIRECTORY_SIGNATURE },
{ "reject", K_REJECT },
{ "router", K_ROUTER },
{ "recommended-software", K_RECOMMENDED_SOFTWARE },
{ "signed-directory", K_SIGNED_DIRECTORY },
{ "signing-key", K_SIGNING_KEY },
{ "onion-key", K_ONION_KEY },
{ "link-key", K_LINK_KEY },
{ "router-signature", K_ROUTER_SIGNATURE },
{ "published", K_PUBLISHED },
{ "running-routers", K_RUNNING_ROUTERS },
{ "platform", K_PLATFORM },
{ "accept", K_ACCEPT, ARGS },
{ "directory-signature", K_DIRECTORY_SIGNATURE, NO_ARGS },
{ "reject", K_REJECT, ARGS },
{ "router", K_ROUTER, ARGS },
{ "recommended-software", K_RECOMMENDED_SOFTWARE, ARGS },
{ "signed-directory", K_SIGNED_DIRECTORY, NO_ARGS },
{ "signing-key", K_SIGNING_KEY, NO_ARGS },
{ "onion-key", K_ONION_KEY, NO_ARGS },
{ "link-key", K_LINK_KEY, NO_ARGS },
{ "router-signature", K_ROUTER_SIGNATURE, NO_ARGS },
{ "published", K_PUBLISHED, CONCAT_ARGS },
{ "running-routers", K_RUNNING_ROUTERS, ARGS },
{ "platform", K_PLATFORM, ARGS },
{ NULL, -1 }
};
#define MAX_ARGS 1024
struct directory_token {
struct directory_token_t {
directory_keyword tp;
union {
struct {
......@@ -287,6 +295,7 @@ struct directory_token {
static void
router_release_token(directory_token_t *tok)
{
int i;
switch (tok->tp)
{
case _SIGNATURE:
......@@ -295,14 +304,21 @@ router_release_token(directory_token_t *tok)
case _PUBLIC_KEY:
crypto_free_pk_env(tok->val.public_key);
break;
default:
case _ERR:
case _EOF:
case _NIL:
break;
default:
for (i = 0; i < tok->val.cmd.n_args; ++i) {
tor_free(tok->val.cmd.args[i]);
}
}
tok->tp = _NIL;
}
static int
_router_get_next_token(char **s, directory_token_t *tok) {
char *next;
_router_get_next_token(const char **s, directory_token_t *tok) {
const char *next;
crypto_pk_env_t *pkey = NULL;
char *signature = NULL;
int i, done;
......@@ -310,6 +326,8 @@ _router_get_next_token(char **s, directory_token_t *tok) {
tok->tp = _ERR;
tok->val.error = "";
router_release_token(tok);
*s = eat_whitespace(*s);
if (!**s) {
tok->tp = _EOF;
......@@ -368,21 +386,42 @@ _router_get_next_token(char **s, directory_token_t *tok) {
for (i = 0 ; token_table[i].t ; ++i) {
if (!strncmp(token_table[i].t, *s, next-*s)) {
tok->tp = token_table[i].v;
i = 0;
done = (*next == '\n');
*s = eat_whitespace_no_nl(next);
while (**s != '\n' && i <= MAX_ARGS && !done) {
next = find_whitespace(*s);
if (*next == '\n')
done = 1;
*next = 0;
tok->val.cmd.args[i++] = *s;
if (token_table[i].s == ARGS) {
i = 0;
done = (*next == '\n');
*s = eat_whitespace_no_nl(next);
while (**s != '\n' && i < MAX_ARGS && !done) {
next = find_whitespace(*s);
if (*next == '\n')
done = 1;
tok->val.cmd.args[i++] = tor_strndup(*s,next-*s);
*s = eat_whitespace_no_nl(next+1);
}
tok->val.cmd.n_args = i;
if (i >= MAX_ARGS) {
/* XXX free args[0..i] */
tok->tp = _ERR;
tok->val.error = "Too many arguments"; return -1;
}
} else if (token_table[i].s == CONCAT_ARGS) {
*s = eat_whitespace_no_nl(next);
next = strchr(*s, '\n');
if (!next) {
tok->tp = _ERR;
tok->val.error = "Unexpected EOF"; return -1;
}
tok->val.cmd.args[0] = tor_strndup(*s,next-*s);
tok->val.cmd.n_args = 1;
*s = eat_whitespace_no_nl(next+1);
};
tok->val.cmd.n_args = i;
if (i > MAX_ARGS) {
tok->tp = _ERR;
tok->val.error = "Too many arguments"; return -1;
} else {
*s = eat_whitespace_no_nl(next);
if (**s != '\n') {
tok->tp = _ERR;
tok->val.error = "Unexpected arguments"; return -1;
}
tok->val.cmd.n_args = 0;
*s = eat_whitespace_no_nl(*s+1);
}
return 0;
}
......@@ -444,7 +483,7 @@ router_get_next_token(char **s, directory_token_t *tok) {
/* read routerinfo elements from s, and throw out the ones that
* don't parse and resolve. */
static int router_set_routerlist_from_string(char *s)
static int router_set_routerlist_from_string(const char *s)
{
if (router_get_list_from_string_impl(&s, &routerlist, -1, NULL)) {
log(LOG_WARN, "Error parsing router file");
......@@ -457,7 +496,8 @@ static int router_set_routerlist_from_string(char *s)
return 0;
}
static int router_get_hash_impl(char *s, char *digest, const char *start_str,
static int router_get_hash_impl(const char *s, char *digest,
const char *start_str,
const char *end_str)
{
char *start, *end;
......@@ -486,22 +526,22 @@ static int router_get_hash_impl(char *s, char *digest, const char *start_str,
return 0;
}
int router_get_dir_hash(char *s, char *digest)
int router_get_dir_hash(const char *s, char *digest)
{
return router_get_hash_impl(s,digest,
"signed-directory","directory-signature");
}
int router_get_router_hash(char *s, char *digest)
int router_get_router_hash(const char *s, char *digest)
{
return router_get_hash_impl(s,digest,
"router ","router-signature");
}
/* return 0 if myversion is in start. Else return -1. */
int compare_recommended_versions(char *myversion, char *start) {
int compare_recommended_versions(const char *myversion, const char *start) {
int len_myversion = strlen(myversion);
char *comma;
char *end = start + strlen(start);
const char *end = start + strlen(start);
log_fn(LOG_DEBUG,"checking '%s' in '%s'.", myversion, start);
......@@ -517,7 +557,7 @@ int compare_recommended_versions(char *myversion, char *start) {
}
}
int router_set_routerlist_from_directory(char *s, crypto_pk_env_t *pkey)
int router_set_routerlist_from_directory(const char *s, crypto_pk_env_t *pkey)
{
if (router_get_routerlist_from_directory_impl(s, &routerlist, pkey)) {
log_fn(LOG_WARN, "Couldn't parse directory.");
......@@ -544,18 +584,19 @@ int router_set_routerlist_from_directory(char *s, crypto_pk_env_t *pkey)
}
static int
router_get_routerlist_from_directory_impl(char *s, routerlist_t **dest,
router_get_routerlist_from_directory_impl(const char *s, routerlist_t **dest,
crypto_pk_env_t *pkey)
{
directory_token_t tok;
char digest[20];
char signed_digest[128];
routerlist_t *new_dir = NULL;
char *versions;
char *versions = NULL;
struct tm published;
time_t published_on;
const char *good_nickname_lst[1024];
int n_good_nicknames;
char *good_nickname_lst[1024];
int n_good_nicknames = 0;
int i;
#define NEXT_TOK() \
do { \
......@@ -570,6 +611,10 @@ router_get_routerlist_from_directory_impl(char *s, routerlist_t **dest,
log_fn(LOG_WARN, "Error reading directory: expected %s", name);\
return -1; \
} } while(0)
#define N_ARGS tok.val.cmd.n_args
#define ARGS tok.val.cmd.args
tok.tp = _NIL;
if (router_get_dir_hash(s, digest)) {
log_fn(LOG_WARN, "Unable to compute digest of directory");
......@@ -584,35 +629,34 @@ router_get_routerlist_from_directory_impl(char *s, routerlist_t **dest,
NEXT_TOK();
TOK_IS(K_PUBLISHED, "published");
if (tok.val.cmd.n_args != 2) {
log_fn(LOG_WARN, "Invalid published line");
goto err;
}
tok.val.cmd.args[1][-1] = ' ';
if (!strptime(tok.val.cmd.args[0], "%Y-%m-%d %H:%M:%S", &published)) {
assert(N_ARGS == 1);
if (!strptime(ARGS[0], "%Y-%m-%d %H:%M:%S", &published)) {
log_fn(LOG_WARN, "Published time was unparseable"); goto err;
}
published_on = tor_timegm(&published);
NEXT_TOK();
TOK_IS(K_RECOMMENDED_SOFTWARE, "recommended-software");
if (tok.val.cmd.n_args != 1) {
if (N_ARGS != 1) {
log_fn(LOG_WARN, "Invalid recommended-software line");
goto err;
}
versions = tor_strdup(tok.val.cmd.args[0]);
versions = ARGS[0];
tok.val.cmd.n_args = 0; /* Don't let the versions string get freed. */
NEXT_TOK();
TOK_IS(K_RUNNING_ROUTERS, "running-routers");
n_good_nicknames = tok.val.cmd.n_args;
memcpy(good_nickname_lst, tok.val.cmd.args, n_good_nicknames*sizeof(char *));
n_good_nicknames = N_ARGS;
memcpy(good_nickname_lst, ARGS, n_good_nicknames*sizeof(char *));
N_ARGS = 0; /* Don't free the strings in good_nickname_lst. */
if (router_get_list_from_string_impl(&s, &new_dir,
n_good_nicknames, good_nickname_lst)) {
n_good_nicknames,
(const char**)good_nickname_lst)) {
log_fn(LOG_WARN, "Error reading routers from directory");
goto err;
}
new_dir->software_versions = versions;
new_dir->software_versions = versions; versions = NULL;
new_dir->published_on = published_on;
NEXT_TOK();
......@@ -623,7 +667,6 @@ router_get_routerlist_from_directory_impl(char *s, routerlist_t **dest,
if (crypto_pk_public_checksig(pkey, tok.val.signature, 128, signed_digest)
!= 20) {
log_fn(LOG_WARN, "Error reading directory: invalid signature.");
free(tok.val.signature);
goto err;
}
log(LOG_DEBUG,"Signed directory hash starts %02x:%02x:%02x:%02x",
......@@ -631,11 +674,9 @@ router_get_routerlist_from_directory_impl(char *s, routerlist_t **dest,
((int)signed_digest[2])&0xff,((int)signed_digest[3])&0xff);
if (memcmp(digest, signed_digest, 20)) {
log_fn(LOG_WARN, "Error reading directory: signature does not match.");
free(tok.val.signature);
goto err;
}
}
free(tok.val.signature);
NEXT_TOK();
TOK_IS(_EOF, "end of directory");
......@@ -647,15 +688,25 @@ router_get_routerlist_from_directory_impl(char *s, routerlist_t **dest,
return 0;
err:
router_release_token(&tok);
if (new_dir)
routerlist_free(new_dir);
if (versions)
tor_free(versions);
if (n_good_nicknames) {
for (i = 0; i < n_good_nicknames; ++i) {
tor_free(good_nickname_lst[i]);
}
}
return -1;
#undef NEXT_TOK
#undef TOK_IS
#undef ARGS
#undef N_ARGS
}
static int
router_get_list_from_string_impl(char **s, routerlist_t **dest,
router_get_list_from_string_impl(const char **s, routerlist_t **dest,
int n_good_nicknames,
const char **good_nickname_lst)
{
......@@ -759,7 +810,7 @@ router_resolve_routerlist(routerlist_t *rl)
* updates s so it points to after the router it just read.
* mallocs a new router and returns it if all goes well, else returns NULL.
*/
routerinfo_t *router_get_entry_from_string(char**s) {
routerinfo_t *router_get_entry_from_string(const char**s) {
routerinfo_t *router = NULL;
char signed_digest[128];
char digest[128];
......@@ -775,6 +826,9 @@ routerinfo_t *router_get_entry_from_string(char**s) {
} } while(0)
#define ARGS tok->val.cmd.args
#define N_ARGS tok->val.cmd.n_args
_tok.tp = _NIL;
if (router_get_router_hash(*s, digest) < 0) {
log_fn(LOG_WARN, "Couldn't compute router hash.");
......@@ -784,15 +838,14 @@ routerinfo_t *router_get_entry_from_string(char**s) {
NEXT_TOKEN();
if (tok->tp != K_ROUTER) {
router_release_token(tok);
log_fn(LOG_WARN,"Entry does not start with \"router\"");
return NULL;
goto err;
}
router = tor_malloc_zero(sizeof(routerinfo_t));
router->onion_pkey = router->identity_pkey = router->link_pkey = NULL;
if (tok->val.cmd.n_args != 6) {
if (N_ARGS != 6) {
log_fn(LOG_WARN,"Wrong # of arguments to \"router\"");
goto err;
}
......@@ -843,10 +896,7 @@ routerinfo_t *router_get_entry_from_string(char**s) {
if (tok->tp != K_PUBLISHED) {
log_fn(LOG_WARN, "Missing published time"); goto err;
}
if (tok->val.cmd.n_args != 2) {
log_fn(LOG_WARN, "Wrong number of arguments to published"); goto err;
}
ARGS[1][-1] = ' '; /* Re-insert space. */
assert(N_ARGS == 1);
if (!strptime(ARGS[0], "%Y-%m-%d %H:%M:%S", &published)) {
log_fn(LOG_WARN, "Published time was unparseable"); goto err;
}
......@@ -912,24 +962,28 @@ routerinfo_t *router_get_entry_from_string(char**s) {
router_release_token(tok); /* free the signature */
return router;
err:
err:
router_release_token(tok);
routerinfo_free(router);
return NULL;
#undef ARGS
#undef N_ARGS
#undef NEXT_TOKEN
}
int
router_add_exit_policy_from_string(routerinfo_t *router, char *s)
router_add_exit_policy_from_string(routerinfo_t *router, const char *s)
{