|
@@ -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
|