mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-28 06:13:31 +01:00
Extract the version-managing code from routerparse.c
Leave the versions.h include in routerparse.h for now; I'll remove it later.
This commit is contained in:
parent
fec3b3bb93
commit
2f5dc48699
@ -46,6 +46,7 @@ LIBTOR_APP_A_SOURCES = \
|
||||
src/core/or/scheduler_kist.c \
|
||||
src/core/or/scheduler_vanilla.c \
|
||||
src/core/or/status.c \
|
||||
src/core/or/versions.c \
|
||||
src/core/proto/proto_cell.c \
|
||||
src/core/proto/proto_control0.c \
|
||||
src/core/proto/proto_ext_or.c \
|
||||
@ -241,6 +242,7 @@ noinst_HEADERS += \
|
||||
src/core/or/status.h \
|
||||
src/core/or/tor_version_st.h \
|
||||
src/core/or/var_cell_st.h \
|
||||
src/core/or/versions.h \
|
||||
src/core/proto/proto_cell.h \
|
||||
src/core/proto/proto_control0.h \
|
||||
src/core/proto/proto_ext_or.h \
|
||||
|
377
src/core/or/versions.c
Normal file
377
src/core/or/versions.c
Normal file
@ -0,0 +1,377 @@
|
||||
/* Copyright (c) 2001 Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file versions.c
|
||||
* \brief Code to manipulate, parse, and compare Tor versions.
|
||||
*/
|
||||
#include "core/or/or.h"
|
||||
|
||||
#include "core/or/versions.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
|
||||
#include "core/or/tor_version_st.h"
|
||||
|
||||
/** Return VS_RECOMMENDED if <b>myversion</b> is contained in
|
||||
* <b>versionlist</b>. Else, return VS_EMPTY if versionlist has no
|
||||
* entries. Else, return VS_OLD if every member of
|
||||
* <b>versionlist</b> is newer than <b>myversion</b>. Else, return
|
||||
* VS_NEW_IN_SERIES if there is at least one member of <b>versionlist</b> in
|
||||
* the same series (major.minor.micro) as <b>myversion</b>, but no such member
|
||||
* is newer than <b>myversion.</b>. Else, return VS_NEW if every member of
|
||||
* <b>versionlist</b> is older than <b>myversion</b>. Else, return
|
||||
* VS_UNRECOMMENDED.
|
||||
*
|
||||
* (versionlist is a comma-separated list of version strings,
|
||||
* optionally prefixed with "Tor". Versions that can't be parsed are
|
||||
* ignored.)
|
||||
*/
|
||||
version_status_t
|
||||
tor_version_is_obsolete(const char *myversion, const char *versionlist)
|
||||
{
|
||||
tor_version_t mine, other;
|
||||
int found_newer = 0, found_older = 0, found_newer_in_series = 0,
|
||||
found_any_in_series = 0, r, same;
|
||||
version_status_t ret = VS_UNRECOMMENDED;
|
||||
smartlist_t *version_sl;
|
||||
|
||||
log_debug(LD_CONFIG,"Checking whether version '%s' is in '%s'",
|
||||
myversion, versionlist);
|
||||
|
||||
if (tor_version_parse(myversion, &mine)) {
|
||||
log_err(LD_BUG,"I couldn't parse my own version (%s)", myversion);
|
||||
tor_assert(0);
|
||||
}
|
||||
version_sl = smartlist_new();
|
||||
smartlist_split_string(version_sl, versionlist, ",", SPLIT_SKIP_SPACE, 0);
|
||||
|
||||
if (!strlen(versionlist)) { /* no authorities cared or agreed */
|
||||
ret = VS_EMPTY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(version_sl, const char *, cp) {
|
||||
if (!strcmpstart(cp, "Tor "))
|
||||
cp += 4;
|
||||
|
||||
if (tor_version_parse(cp, &other)) {
|
||||
/* Couldn't parse other; it can't be a match. */
|
||||
} else {
|
||||
same = tor_version_same_series(&mine, &other);
|
||||
if (same)
|
||||
found_any_in_series = 1;
|
||||
r = tor_version_compare(&mine, &other);
|
||||
if (r==0) {
|
||||
ret = VS_RECOMMENDED;
|
||||
goto done;
|
||||
} else if (r<0) {
|
||||
found_newer = 1;
|
||||
if (same)
|
||||
found_newer_in_series = 1;
|
||||
} else if (r>0) {
|
||||
found_older = 1;
|
||||
}
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(cp);
|
||||
|
||||
/* We didn't find the listed version. Is it new or old? */
|
||||
if (found_any_in_series && !found_newer_in_series && found_newer) {
|
||||
ret = VS_NEW_IN_SERIES;
|
||||
} else if (found_newer && !found_older) {
|
||||
ret = VS_OLD;
|
||||
} else if (found_older && !found_newer) {
|
||||
ret = VS_NEW;
|
||||
} else {
|
||||
ret = VS_UNRECOMMENDED;
|
||||
}
|
||||
|
||||
done:
|
||||
SMARTLIST_FOREACH(version_sl, char *, version, tor_free(version));
|
||||
smartlist_free(version_sl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Extract a Tor version from a <b>platform</b> line from a router
|
||||
* descriptor, and place the result in <b>router_version</b>.
|
||||
*
|
||||
* Return 1 on success, -1 on parsing failure, and 0 if the
|
||||
* platform line does not indicate some version of Tor.
|
||||
*
|
||||
* If <b>strict</b> is non-zero, finding any weird version components
|
||||
* (like negative numbers) counts as a parsing failure.
|
||||
*/
|
||||
int
|
||||
tor_version_parse_platform(const char *platform,
|
||||
tor_version_t *router_version,
|
||||
int strict)
|
||||
{
|
||||
char tmp[128];
|
||||
char *s, *s2, *start;
|
||||
|
||||
if (strcmpstart(platform,"Tor ")) /* nonstandard Tor; say 0. */
|
||||
return 0;
|
||||
|
||||
start = (char *)eat_whitespace(platform+3);
|
||||
if (!*start) return -1;
|
||||
s = (char *)find_whitespace(start); /* also finds '\0', which is fine */
|
||||
s2 = (char*)eat_whitespace(s);
|
||||
if (!strcmpstart(s2, "(r") || !strcmpstart(s2, "(git-"))
|
||||
s = (char*)find_whitespace(s2);
|
||||
|
||||
if ((size_t)(s-start+1) >= sizeof(tmp)) /* too big, no */
|
||||
return -1;
|
||||
strlcpy(tmp, start, s-start+1);
|
||||
|
||||
if (tor_version_parse(tmp, router_version)<0) {
|
||||
log_info(LD_DIR,"Router version '%s' unparseable.",tmp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strict) {
|
||||
if (router_version->major < 0 ||
|
||||
router_version->minor < 0 ||
|
||||
router_version->micro < 0 ||
|
||||
router_version->patchlevel < 0 ||
|
||||
router_version->svn_revision < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Parse the Tor version of the platform string <b>platform</b>,
|
||||
* and compare it to the version in <b>cutoff</b>. Return 1 if
|
||||
* the router is at least as new as the cutoff, else return 0.
|
||||
*/
|
||||
int
|
||||
tor_version_as_new_as(const char *platform, const char *cutoff)
|
||||
{
|
||||
tor_version_t cutoff_version, router_version;
|
||||
int r;
|
||||
tor_assert(platform);
|
||||
|
||||
if (tor_version_parse(cutoff, &cutoff_version)<0) {
|
||||
log_warn(LD_BUG,"cutoff version '%s' unparseable.",cutoff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = tor_version_parse_platform(platform, &router_version, 0);
|
||||
if (r == 0) {
|
||||
/* nonstandard Tor; be safe and say yes */
|
||||
return 1;
|
||||
} else if (r < 0) {
|
||||
/* unparseable version; be safe and say yes. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Here's why we don't need to do any special handling for svn revisions:
|
||||
* - If neither has an svn revision, we're fine.
|
||||
* - If the router doesn't have an svn revision, we can't assume that it
|
||||
* is "at least" any svn revision, so we need to return 0.
|
||||
* - If the target version doesn't have an svn revision, any svn revision
|
||||
* (or none at all) is good enough, so return 1.
|
||||
* - If both target and router have an svn revision, we compare them.
|
||||
*/
|
||||
|
||||
return tor_version_compare(&router_version, &cutoff_version) >= 0;
|
||||
}
|
||||
|
||||
/** Parse a tor version from <b>s</b>, and store the result in <b>out</b>.
|
||||
* Return 0 on success, -1 on failure. */
|
||||
int
|
||||
tor_version_parse(const char *s, tor_version_t *out)
|
||||
{
|
||||
char *eos=NULL;
|
||||
const char *cp=NULL;
|
||||
int ok = 1;
|
||||
/* Format is:
|
||||
* "Tor " ? NUM dot NUM [ dot NUM [ ( pre | rc | dot ) NUM ] ] [ - tag ]
|
||||
*/
|
||||
tor_assert(s);
|
||||
tor_assert(out);
|
||||
|
||||
memset(out, 0, sizeof(tor_version_t));
|
||||
out->status = VER_RELEASE;
|
||||
if (!strcasecmpstart(s, "Tor "))
|
||||
s += 4;
|
||||
|
||||
cp = s;
|
||||
|
||||
#define NUMBER(m) \
|
||||
do { \
|
||||
if (!cp || *cp < '0' || *cp > '9') \
|
||||
return -1; \
|
||||
out->m = (int)tor_parse_uint64(cp, 10, 0, INT32_MAX, &ok, &eos); \
|
||||
if (!ok) \
|
||||
return -1; \
|
||||
if (!eos || eos == cp) \
|
||||
return -1; \
|
||||
cp = eos; \
|
||||
} while (0)
|
||||
|
||||
#define DOT() \
|
||||
do { \
|
||||
if (*cp != '.') \
|
||||
return -1; \
|
||||
++cp; \
|
||||
} while (0)
|
||||
|
||||
NUMBER(major);
|
||||
DOT();
|
||||
NUMBER(minor);
|
||||
if (*cp == 0)
|
||||
return 0;
|
||||
else if (*cp == '-')
|
||||
goto status_tag;
|
||||
DOT();
|
||||
NUMBER(micro);
|
||||
|
||||
/* Get status */
|
||||
if (*cp == 0) {
|
||||
return 0;
|
||||
} else if (*cp == '.') {
|
||||
++cp;
|
||||
} else if (*cp == '-') {
|
||||
goto status_tag;
|
||||
} else if (0==strncmp(cp, "pre", 3)) {
|
||||
out->status = VER_PRE;
|
||||
cp += 3;
|
||||
} else if (0==strncmp(cp, "rc", 2)) {
|
||||
out->status = VER_RC;
|
||||
cp += 2;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
NUMBER(patchlevel);
|
||||
|
||||
status_tag:
|
||||
/* Get status tag. */
|
||||
if (*cp == '-' || *cp == '.')
|
||||
++cp;
|
||||
eos = (char*) find_whitespace(cp);
|
||||
if (eos-cp >= (int)sizeof(out->status_tag))
|
||||
strlcpy(out->status_tag, cp, sizeof(out->status_tag));
|
||||
else {
|
||||
memcpy(out->status_tag, cp, eos-cp);
|
||||
out->status_tag[eos-cp] = 0;
|
||||
}
|
||||
cp = eat_whitespace(eos);
|
||||
|
||||
if (!strcmpstart(cp, "(r")) {
|
||||
cp += 2;
|
||||
out->svn_revision = (int) strtol(cp,&eos,10);
|
||||
} else if (!strcmpstart(cp, "(git-")) {
|
||||
char *close_paren = strchr(cp, ')');
|
||||
int hexlen;
|
||||
char digest[DIGEST_LEN];
|
||||
if (! close_paren)
|
||||
return -1;
|
||||
cp += 5;
|
||||
if (close_paren-cp > HEX_DIGEST_LEN)
|
||||
return -1;
|
||||
hexlen = (int)(close_paren-cp);
|
||||
memwipe(digest, 0, sizeof(digest));
|
||||
if ( hexlen == 0 || (hexlen % 2) == 1)
|
||||
return -1;
|
||||
if (base16_decode(digest, hexlen/2, cp, hexlen) != hexlen/2)
|
||||
return -1;
|
||||
memcpy(out->git_tag, digest, hexlen/2);
|
||||
out->git_tag_len = hexlen/2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#undef NUMBER
|
||||
#undef DOT
|
||||
}
|
||||
|
||||
/** Compare two tor versions; Return <0 if a < b; 0 if a ==b, >0 if a >
|
||||
* b. */
|
||||
int
|
||||
tor_version_compare(tor_version_t *a, tor_version_t *b)
|
||||
{
|
||||
int i;
|
||||
tor_assert(a);
|
||||
tor_assert(b);
|
||||
|
||||
/* We take this approach to comparison to ensure the same (bogus!) behavior
|
||||
* on all inputs as we would have seen before bug #21278 was fixed. The
|
||||
* only important difference here is that this method doesn't cause
|
||||
* a signed integer underflow.
|
||||
*/
|
||||
#define CMP(field) do { \
|
||||
unsigned aval = (unsigned) a->field; \
|
||||
unsigned bval = (unsigned) b->field; \
|
||||
int result = (int) (aval - bval); \
|
||||
if (result < 0) \
|
||||
return -1; \
|
||||
else if (result > 0) \
|
||||
return 1; \
|
||||
} while (0)
|
||||
|
||||
CMP(major);
|
||||
CMP(minor);
|
||||
CMP(micro);
|
||||
CMP(status);
|
||||
CMP(patchlevel);
|
||||
if ((i = strcmp(a->status_tag, b->status_tag)))
|
||||
return i;
|
||||
CMP(svn_revision);
|
||||
CMP(git_tag_len);
|
||||
if (a->git_tag_len)
|
||||
return fast_memcmp(a->git_tag, b->git_tag, a->git_tag_len);
|
||||
else
|
||||
return 0;
|
||||
|
||||
#undef CMP
|
||||
}
|
||||
|
||||
/** Return true iff versions <b>a</b> and <b>b</b> belong to the same series.
|
||||
*/
|
||||
int
|
||||
tor_version_same_series(tor_version_t *a, tor_version_t *b)
|
||||
{
|
||||
tor_assert(a);
|
||||
tor_assert(b);
|
||||
return ((a->major == b->major) &&
|
||||
(a->minor == b->minor) &&
|
||||
(a->micro == b->micro));
|
||||
}
|
||||
|
||||
/** Helper: Given pointers to two strings describing tor versions, return -1
|
||||
* if _a precedes _b, 1 if _b precedes _a, and 0 if they are equivalent.
|
||||
* Used to sort a list of versions. */
|
||||
static int
|
||||
compare_tor_version_str_ptr_(const void **_a, const void **_b)
|
||||
{
|
||||
const char *a = *_a, *b = *_b;
|
||||
int ca, cb;
|
||||
tor_version_t va, vb;
|
||||
ca = tor_version_parse(a, &va);
|
||||
cb = tor_version_parse(b, &vb);
|
||||
/* If they both parse, compare them. */
|
||||
if (!ca && !cb)
|
||||
return tor_version_compare(&va,&vb);
|
||||
/* If one parses, it comes first. */
|
||||
if (!ca && cb)
|
||||
return -1;
|
||||
if (ca && !cb)
|
||||
return 1;
|
||||
/* If neither parses, compare strings. Also, the directory server admin
|
||||
** needs to be smacked upside the head. But Tor is tolerant and gentle. */
|
||||
return strcmp(a,b);
|
||||
}
|
||||
|
||||
/** Sort a list of string-representations of versions in ascending order. */
|
||||
void
|
||||
sort_version_list(smartlist_t *versions, int remove_duplicates)
|
||||
{
|
||||
smartlist_sort(versions, compare_tor_version_str_ptr_);
|
||||
|
||||
if (remove_duplicates)
|
||||
smartlist_uniq(versions, compare_tor_version_str_ptr_, tor_free_);
|
||||
}
|
40
src/core/or/versions.h
Normal file
40
src/core/or/versions.h
Normal file
@ -0,0 +1,40 @@
|
||||
/* Copyright (c) 2001 Matej Pfajfar.
|
||||
* Copyright (c) 2001-2004, Roger Dingledine.
|
||||
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
||||
* Copyright (c) 2007-2018, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file versions.h
|
||||
* \brief Header file for versions.c.
|
||||
**/
|
||||
|
||||
#ifndef TOR_VERSIONS_H
|
||||
#define TOR_VERSIONS_H
|
||||
|
||||
/** Possible statuses of a version of Tor, given opinions from the directory
|
||||
* servers. */
|
||||
typedef enum version_status_t {
|
||||
VS_RECOMMENDED=0, /**< This version is listed as recommended. */
|
||||
VS_OLD=1, /**< This version is older than any recommended version. */
|
||||
VS_NEW=2, /**< This version is newer than any recommended version. */
|
||||
VS_NEW_IN_SERIES=3, /**< This version is newer than any recommended version
|
||||
* in its series, but later recommended versions exist.
|
||||
*/
|
||||
VS_UNRECOMMENDED=4, /**< This version is not recommended (general case). */
|
||||
VS_EMPTY=5, /**< The version list was empty; no agreed-on versions. */
|
||||
VS_UNKNOWN, /**< We have no idea. */
|
||||
} version_status_t;
|
||||
|
||||
version_status_t tor_version_is_obsolete(const char *myversion,
|
||||
const char *versionlist);
|
||||
int tor_version_parse_platform(const char *platform,
|
||||
tor_version_t *version_out,
|
||||
int strict);
|
||||
int tor_version_as_new_as(const char *platform, const char *cutoff);
|
||||
int tor_version_parse(const char *s, tor_version_t *out);
|
||||
int tor_version_compare(tor_version_t *a, tor_version_t *b);
|
||||
int tor_version_same_series(tor_version_t *a, tor_version_t *b);
|
||||
void sort_version_list(smartlist_t *lst, int remove_duplicates);
|
||||
|
||||
#endif /* !defined(TOR_VERSIONS_H) */
|
@ -108,9 +108,6 @@
|
||||
|
||||
#undef log
|
||||
#include <math.h>
|
||||
//#ifdef HAVE_SYS_STAT_H
|
||||
//#include <sys/stat.h>
|
||||
//#endif
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
@ -586,85 +583,6 @@ router_append_dirobj_signature(char *buf, size_t buf_len, const char *digest,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Return VS_RECOMMENDED if <b>myversion</b> is contained in
|
||||
* <b>versionlist</b>. Else, return VS_EMPTY if versionlist has no
|
||||
* entries. Else, return VS_OLD if every member of
|
||||
* <b>versionlist</b> is newer than <b>myversion</b>. Else, return
|
||||
* VS_NEW_IN_SERIES if there is at least one member of <b>versionlist</b> in
|
||||
* the same series (major.minor.micro) as <b>myversion</b>, but no such member
|
||||
* is newer than <b>myversion.</b>. Else, return VS_NEW if every member of
|
||||
* <b>versionlist</b> is older than <b>myversion</b>. Else, return
|
||||
* VS_UNRECOMMENDED.
|
||||
*
|
||||
* (versionlist is a comma-separated list of version strings,
|
||||
* optionally prefixed with "Tor". Versions that can't be parsed are
|
||||
* ignored.)
|
||||
*/
|
||||
version_status_t
|
||||
tor_version_is_obsolete(const char *myversion, const char *versionlist)
|
||||
{
|
||||
tor_version_t mine, other;
|
||||
int found_newer = 0, found_older = 0, found_newer_in_series = 0,
|
||||
found_any_in_series = 0, r, same;
|
||||
version_status_t ret = VS_UNRECOMMENDED;
|
||||
smartlist_t *version_sl;
|
||||
|
||||
log_debug(LD_CONFIG,"Checking whether version '%s' is in '%s'",
|
||||
myversion, versionlist);
|
||||
|
||||
if (tor_version_parse(myversion, &mine)) {
|
||||
log_err(LD_BUG,"I couldn't parse my own version (%s)", myversion);
|
||||
tor_assert(0);
|
||||
}
|
||||
version_sl = smartlist_new();
|
||||
smartlist_split_string(version_sl, versionlist, ",", SPLIT_SKIP_SPACE, 0);
|
||||
|
||||
if (!strlen(versionlist)) { /* no authorities cared or agreed */
|
||||
ret = VS_EMPTY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(version_sl, const char *, cp) {
|
||||
if (!strcmpstart(cp, "Tor "))
|
||||
cp += 4;
|
||||
|
||||
if (tor_version_parse(cp, &other)) {
|
||||
/* Couldn't parse other; it can't be a match. */
|
||||
} else {
|
||||
same = tor_version_same_series(&mine, &other);
|
||||
if (same)
|
||||
found_any_in_series = 1;
|
||||
r = tor_version_compare(&mine, &other);
|
||||
if (r==0) {
|
||||
ret = VS_RECOMMENDED;
|
||||
goto done;
|
||||
} else if (r<0) {
|
||||
found_newer = 1;
|
||||
if (same)
|
||||
found_newer_in_series = 1;
|
||||
} else if (r>0) {
|
||||
found_older = 1;
|
||||
}
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(cp);
|
||||
|
||||
/* We didn't find the listed version. Is it new or old? */
|
||||
if (found_any_in_series && !found_newer_in_series && found_newer) {
|
||||
ret = VS_NEW_IN_SERIES;
|
||||
} else if (found_newer && !found_older) {
|
||||
ret = VS_OLD;
|
||||
} else if (found_older && !found_newer) {
|
||||
ret = VS_NEW;
|
||||
} else {
|
||||
ret = VS_UNRECOMMENDED;
|
||||
}
|
||||
|
||||
done:
|
||||
SMARTLIST_FOREACH(version_sl, char *, version, tor_free(version));
|
||||
smartlist_free(version_sl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
MOCK_IMPL(STATIC int,
|
||||
signed_digest_equals, (const uint8_t *d1, const uint8_t *d2, size_t len))
|
||||
{
|
||||
@ -4276,288 +4194,6 @@ microdescs_parse_from_string(const char *s, const char *eos,
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Extract a Tor version from a <b>platform</b> line from a router
|
||||
* descriptor, and place the result in <b>router_version</b>.
|
||||
*
|
||||
* Return 1 on success, -1 on parsing failure, and 0 if the
|
||||
* platform line does not indicate some version of Tor.
|
||||
*
|
||||
* If <b>strict</b> is non-zero, finding any weird version components
|
||||
* (like negative numbers) counts as a parsing failure.
|
||||
*/
|
||||
int
|
||||
tor_version_parse_platform(const char *platform,
|
||||
tor_version_t *router_version,
|
||||
int strict)
|
||||
{
|
||||
char tmp[128];
|
||||
char *s, *s2, *start;
|
||||
|
||||
if (strcmpstart(platform,"Tor ")) /* nonstandard Tor; say 0. */
|
||||
return 0;
|
||||
|
||||
start = (char *)eat_whitespace(platform+3);
|
||||
if (!*start) return -1;
|
||||
s = (char *)find_whitespace(start); /* also finds '\0', which is fine */
|
||||
s2 = (char*)eat_whitespace(s);
|
||||
if (!strcmpstart(s2, "(r") || !strcmpstart(s2, "(git-"))
|
||||
s = (char*)find_whitespace(s2);
|
||||
|
||||
if ((size_t)(s-start+1) >= sizeof(tmp)) /* too big, no */
|
||||
return -1;
|
||||
strlcpy(tmp, start, s-start+1);
|
||||
|
||||
if (tor_version_parse(tmp, router_version)<0) {
|
||||
log_info(LD_DIR,"Router version '%s' unparseable.",tmp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strict) {
|
||||
if (router_version->major < 0 ||
|
||||
router_version->minor < 0 ||
|
||||
router_version->micro < 0 ||
|
||||
router_version->patchlevel < 0 ||
|
||||
router_version->svn_revision < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Parse the Tor version of the platform string <b>platform</b>,
|
||||
* and compare it to the version in <b>cutoff</b>. Return 1 if
|
||||
* the router is at least as new as the cutoff, else return 0.
|
||||
*/
|
||||
int
|
||||
tor_version_as_new_as(const char *platform, const char *cutoff)
|
||||
{
|
||||
tor_version_t cutoff_version, router_version;
|
||||
int r;
|
||||
tor_assert(platform);
|
||||
|
||||
if (tor_version_parse(cutoff, &cutoff_version)<0) {
|
||||
log_warn(LD_BUG,"cutoff version '%s' unparseable.",cutoff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = tor_version_parse_platform(platform, &router_version, 0);
|
||||
if (r == 0) {
|
||||
/* nonstandard Tor; be safe and say yes */
|
||||
return 1;
|
||||
} else if (r < 0) {
|
||||
/* unparseable version; be safe and say yes. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Here's why we don't need to do any special handling for svn revisions:
|
||||
* - If neither has an svn revision, we're fine.
|
||||
* - If the router doesn't have an svn revision, we can't assume that it
|
||||
* is "at least" any svn revision, so we need to return 0.
|
||||
* - If the target version doesn't have an svn revision, any svn revision
|
||||
* (or none at all) is good enough, so return 1.
|
||||
* - If both target and router have an svn revision, we compare them.
|
||||
*/
|
||||
|
||||
return tor_version_compare(&router_version, &cutoff_version) >= 0;
|
||||
}
|
||||
|
||||
/** Parse a tor version from <b>s</b>, and store the result in <b>out</b>.
|
||||
* Return 0 on success, -1 on failure. */
|
||||
int
|
||||
tor_version_parse(const char *s, tor_version_t *out)
|
||||
{
|
||||
char *eos=NULL;
|
||||
const char *cp=NULL;
|
||||
int ok = 1;
|
||||
/* Format is:
|
||||
* "Tor " ? NUM dot NUM [ dot NUM [ ( pre | rc | dot ) NUM ] ] [ - tag ]
|
||||
*/
|
||||
tor_assert(s);
|
||||
tor_assert(out);
|
||||
|
||||
memset(out, 0, sizeof(tor_version_t));
|
||||
out->status = VER_RELEASE;
|
||||
if (!strcasecmpstart(s, "Tor "))
|
||||
s += 4;
|
||||
|
||||
cp = s;
|
||||
|
||||
#define NUMBER(m) \
|
||||
do { \
|
||||
if (!cp || *cp < '0' || *cp > '9') \
|
||||
return -1; \
|
||||
out->m = (int)tor_parse_uint64(cp, 10, 0, INT32_MAX, &ok, &eos); \
|
||||
if (!ok) \
|
||||
return -1; \
|
||||
if (!eos || eos == cp) \
|
||||
return -1; \
|
||||
cp = eos; \
|
||||
} while (0)
|
||||
|
||||
#define DOT() \
|
||||
do { \
|
||||
if (*cp != '.') \
|
||||
return -1; \
|
||||
++cp; \
|
||||
} while (0)
|
||||
|
||||
NUMBER(major);
|
||||
DOT();
|
||||
NUMBER(minor);
|
||||
if (*cp == 0)
|
||||
return 0;
|
||||
else if (*cp == '-')
|
||||
goto status_tag;
|
||||
DOT();
|
||||
NUMBER(micro);
|
||||
|
||||
/* Get status */
|
||||
if (*cp == 0) {
|
||||
return 0;
|
||||
} else if (*cp == '.') {
|
||||
++cp;
|
||||
} else if (*cp == '-') {
|
||||
goto status_tag;
|
||||
} else if (0==strncmp(cp, "pre", 3)) {
|
||||
out->status = VER_PRE;
|
||||
cp += 3;
|
||||
} else if (0==strncmp(cp, "rc", 2)) {
|
||||
out->status = VER_RC;
|
||||
cp += 2;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
NUMBER(patchlevel);
|
||||
|
||||
status_tag:
|
||||
/* Get status tag. */
|
||||
if (*cp == '-' || *cp == '.')
|
||||
++cp;
|
||||
eos = (char*) find_whitespace(cp);
|
||||
if (eos-cp >= (int)sizeof(out->status_tag))
|
||||
strlcpy(out->status_tag, cp, sizeof(out->status_tag));
|
||||
else {
|
||||
memcpy(out->status_tag, cp, eos-cp);
|
||||
out->status_tag[eos-cp] = 0;
|
||||
}
|
||||
cp = eat_whitespace(eos);
|
||||
|
||||
if (!strcmpstart(cp, "(r")) {
|
||||
cp += 2;
|
||||
out->svn_revision = (int) strtol(cp,&eos,10);
|
||||
} else if (!strcmpstart(cp, "(git-")) {
|
||||
char *close_paren = strchr(cp, ')');
|
||||
int hexlen;
|
||||
char digest[DIGEST_LEN];
|
||||
if (! close_paren)
|
||||
return -1;
|
||||
cp += 5;
|
||||
if (close_paren-cp > HEX_DIGEST_LEN)
|
||||
return -1;
|
||||
hexlen = (int)(close_paren-cp);
|
||||
memwipe(digest, 0, sizeof(digest));
|
||||
if ( hexlen == 0 || (hexlen % 2) == 1)
|
||||
return -1;
|
||||
if (base16_decode(digest, hexlen/2, cp, hexlen) != hexlen/2)
|
||||
return -1;
|
||||
memcpy(out->git_tag, digest, hexlen/2);
|
||||
out->git_tag_len = hexlen/2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#undef NUMBER
|
||||
#undef DOT
|
||||
}
|
||||
|
||||
/** Compare two tor versions; Return <0 if a < b; 0 if a ==b, >0 if a >
|
||||
* b. */
|
||||
int
|
||||
tor_version_compare(tor_version_t *a, tor_version_t *b)
|
||||
{
|
||||
int i;
|
||||
tor_assert(a);
|
||||
tor_assert(b);
|
||||
|
||||
/* We take this approach to comparison to ensure the same (bogus!) behavior
|
||||
* on all inputs as we would have seen before bug #21278 was fixed. The
|
||||
* only important difference here is that this method doesn't cause
|
||||
* a signed integer underflow.
|
||||
*/
|
||||
#define CMP(field) do { \
|
||||
unsigned aval = (unsigned) a->field; \
|
||||
unsigned bval = (unsigned) b->field; \
|
||||
int result = (int) (aval - bval); \
|
||||
if (result < 0) \
|
||||
return -1; \
|
||||
else if (result > 0) \
|
||||
return 1; \
|
||||
} while (0)
|
||||
|
||||
CMP(major);
|
||||
CMP(minor);
|
||||
CMP(micro);
|
||||
CMP(status);
|
||||
CMP(patchlevel);
|
||||
if ((i = strcmp(a->status_tag, b->status_tag)))
|
||||
return i;
|
||||
CMP(svn_revision);
|
||||
CMP(git_tag_len);
|
||||
if (a->git_tag_len)
|
||||
return fast_memcmp(a->git_tag, b->git_tag, a->git_tag_len);
|
||||
else
|
||||
return 0;
|
||||
|
||||
#undef CMP
|
||||
}
|
||||
|
||||
/** Return true iff versions <b>a</b> and <b>b</b> belong to the same series.
|
||||
*/
|
||||
int
|
||||
tor_version_same_series(tor_version_t *a, tor_version_t *b)
|
||||
{
|
||||
tor_assert(a);
|
||||
tor_assert(b);
|
||||
return ((a->major == b->major) &&
|
||||
(a->minor == b->minor) &&
|
||||
(a->micro == b->micro));
|
||||
}
|
||||
|
||||
/** Helper: Given pointers to two strings describing tor versions, return -1
|
||||
* if _a precedes _b, 1 if _b precedes _a, and 0 if they are equivalent.
|
||||
* Used to sort a list of versions. */
|
||||
static int
|
||||
compare_tor_version_str_ptr_(const void **_a, const void **_b)
|
||||
{
|
||||
const char *a = *_a, *b = *_b;
|
||||
int ca, cb;
|
||||
tor_version_t va, vb;
|
||||
ca = tor_version_parse(a, &va);
|
||||
cb = tor_version_parse(b, &vb);
|
||||
/* If they both parse, compare them. */
|
||||
if (!ca && !cb)
|
||||
return tor_version_compare(&va,&vb);
|
||||
/* If one parses, it comes first. */
|
||||
if (!ca && cb)
|
||||
return -1;
|
||||
if (ca && !cb)
|
||||
return 1;
|
||||
/* If neither parses, compare strings. Also, the directory server admin
|
||||
** needs to be smacked upside the head. But Tor is tolerant and gentle. */
|
||||
return strcmp(a,b);
|
||||
}
|
||||
|
||||
/** Sort a list of string-representations of versions in ascending order. */
|
||||
void
|
||||
sort_version_list(smartlist_t *versions, int remove_duplicates)
|
||||
{
|
||||
smartlist_sort(versions, compare_tor_version_str_ptr_);
|
||||
|
||||
if (remove_duplicates)
|
||||
smartlist_uniq(versions, compare_tor_version_str_ptr_, tor_free_);
|
||||
}
|
||||
|
||||
/** Parse and validate the ASCII-encoded v2 descriptor in <b>desc</b>,
|
||||
* write the parsed descriptor to the newly allocated *<b>parsed_out</b>, the
|
||||
* binary descriptor ID of length DIGEST_LEN to <b>desc_id_out</b>, the
|
||||
|
@ -12,19 +12,7 @@
|
||||
#ifndef TOR_ROUTERPARSE_H
|
||||
#define TOR_ROUTERPARSE_H
|
||||
|
||||
/** Possible statuses of a version of Tor, given opinions from the directory
|
||||
* servers. */
|
||||
typedef enum version_status_t {
|
||||
VS_RECOMMENDED=0, /**< This version is listed as recommended. */
|
||||
VS_OLD=1, /**< This version is older than any recommended version. */
|
||||
VS_NEW=2, /**< This version is newer than any recommended version. */
|
||||
VS_NEW_IN_SERIES=3, /**< This version is newer than any recommended version
|
||||
* in its series, but later recommended versions exist.
|
||||
*/
|
||||
VS_UNRECOMMENDED=4, /**< This version is not recommended (general case). */
|
||||
VS_EMPTY=5, /**< The version list was empty; no agreed-on versions. */
|
||||
VS_UNKNOWN, /**< We have no idea. */
|
||||
} version_status_t;
|
||||
#include "core/or/versions.h"
|
||||
|
||||
enum networkstatus_type_t;
|
||||
|
||||
@ -65,16 +53,7 @@ extrainfo_t *extrainfo_parse_entry_from_string(const char *s, const char *end,
|
||||
int *can_dl_again_out);
|
||||
MOCK_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
|
||||
(const char *s, int assume_action, int *malformed_list));
|
||||
version_status_t tor_version_is_obsolete(const char *myversion,
|
||||
const char *versionlist);
|
||||
int tor_version_parse_platform(const char *platform,
|
||||
tor_version_t *version_out,
|
||||
int strict);
|
||||
int tor_version_as_new_as(const char *platform, const char *cutoff);
|
||||
int tor_version_parse(const char *s, tor_version_t *out);
|
||||
int tor_version_compare(tor_version_t *a, tor_version_t *b);
|
||||
int tor_version_same_series(tor_version_t *a, tor_version_t *b);
|
||||
void sort_version_list(smartlist_t *lst, int remove_duplicates);
|
||||
|
||||
void assert_addr_policy_ok(smartlist_t *t);
|
||||
void dump_distinct_digest_count(int severity);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user