|
@@ -4785,40 +4785,78 @@ microdescs_parse_from_string(const char *s, const char *eos,
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
-/** 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.
|
|
|
+/** 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_as_new_as(const char *platform, const char *cutoff)
|
|
|
+tor_version_parse_platform(const char *platform,
|
|
|
+ tor_version_t *router_version,
|
|
|
+ int strict)
|
|
|
{
|
|
|
- tor_version_t cutoff_version, router_version;
|
|
|
- char *s, *s2, *start;
|
|
|
char tmp[128];
|
|
|
+ char *s, *s2, *start;
|
|
|
|
|
|
- tor_assert(platform);
|
|
|
-
|
|
|
- if (tor_version_parse(cutoff, &cutoff_version)<0) {
|
|
|
- log_warn(LD_BUG,"cutoff version '%s' unparseable.",cutoff);
|
|
|
+ if (strcmpstart(platform,"Tor ")) /* nonstandard Tor; say 0. */
|
|
|
return 0;
|
|
|
- }
|
|
|
- if (strcmpstart(platform,"Tor ")) /* nonstandard Tor; be safe and say yes */
|
|
|
- return 1;
|
|
|
|
|
|
start = (char *)eat_whitespace(platform+3);
|
|
|
- if (!*start) return 0;
|
|
|
+ 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 0;
|
|
|
+ return -1;
|
|
|
strlcpy(tmp, start, s-start+1);
|
|
|
|
|
|
- if (tor_version_parse(tmp, &router_version)<0) {
|
|
|
+ if (tor_version_parse(tmp, router_version)<0) {
|
|
|
log_info(LD_DIR,"Router version '%s' unparseable.",tmp);
|
|
|
- return 1; /* be safe and say yes */
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (strict) {
|
|
|
+ if (router_version->major < 0 ||
|
|
|
+ router_version->minor < 0 ||
|
|
|
+ router_version->minor < 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:
|
|
@@ -4945,26 +4983,37 @@ tor_version_compare(tor_version_t *a, tor_version_t *b)
|
|
|
int i;
|
|
|
tor_assert(a);
|
|
|
tor_assert(b);
|
|
|
- if ((i = a->major - b->major))
|
|
|
- return i;
|
|
|
- else if ((i = a->minor - b->minor))
|
|
|
- return i;
|
|
|
- else if ((i = a->micro - b->micro))
|
|
|
- return i;
|
|
|
- else if ((i = a->status - b->status))
|
|
|
- return i;
|
|
|
- else if ((i = a->patchlevel - b->patchlevel))
|
|
|
- return i;
|
|
|
- else if ((i = strcmp(a->status_tag, b->status_tag)))
|
|
|
- return i;
|
|
|
- else if ((i = a->svn_revision - b->svn_revision))
|
|
|
- return i;
|
|
|
- else if ((i = a->git_tag_len - b->git_tag_len))
|
|
|
- return i;
|
|
|
- else if (a->git_tag_len)
|
|
|
- return fast_memcmp(a->git_tag, b->git_tag, a->git_tag_len);
|
|
|
+
|
|
|
+ /* 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;
|
|
|
+ return 0;
|
|
|
+
|
|
|
+#undef CMP
|
|
|
}
|
|
|
|
|
|
/** Return true iff versions <b>a</b> and <b>b</b> belong to the same series.
|