1 /* Copyright (c) 2001 Matej Pfajfar.
2 * Copyright (c) 2001-2004, Roger Dingledine.
3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
4 * Copyright (c) 2007-2021, The Tor Project, Inc. */
5 /* See LICENSE for licensing information */
9 * \brief Code to manipulate, parse, and compare Tor versions.
11 #include "core/or/or.h"
13 #include "core/or/protover.h"
14 #include "core/or/versions.h"
15 #include "lib/crypt_ops/crypto_util.h"
17 #include "core/or/tor_version_st.h"
20 * Return the approximate date when this release came out, or was
21 * scheduled to come out, according to the APPROX_RELEASE_DATE set in
25 tor_get_approx_release_date(void)
27 char tbuf
[ISO_TIME_LEN
+1];
28 tor_snprintf(tbuf
, sizeof(tbuf
),
29 "%s 00:00:00", APPROX_RELEASE_DATE
);
31 int r
= parse_iso_time(tbuf
, &result
);
38 /** Return VS_RECOMMENDED if <b>myversion</b> is contained in
39 * <b>versionlist</b>. Else, return VS_EMPTY if versionlist has no
40 * entries. Else, return VS_OLD if every member of
41 * <b>versionlist</b> is newer than <b>myversion</b>. Else, return
42 * VS_NEW_IN_SERIES if there is at least one member of <b>versionlist</b> in
43 * the same series (major.minor.micro) as <b>myversion</b>, but no such member
44 * is newer than <b>myversion.</b>. Else, return VS_NEW if every member of
45 * <b>versionlist</b> is older than <b>myversion</b>. Else, return
48 * (versionlist is a comma-separated list of version strings,
49 * optionally prefixed with "Tor". Versions that can't be parsed are
53 tor_version_is_obsolete(const char *myversion
, const char *versionlist
)
55 tor_version_t mine
, other
;
56 int found_newer
= 0, found_older
= 0, found_newer_in_series
= 0,
57 found_any_in_series
= 0, r
, same
;
58 version_status_t ret
= VS_UNRECOMMENDED
;
59 smartlist_t
*version_sl
;
61 log_debug(LD_CONFIG
,"Checking whether version '%s' is in '%s'",
62 myversion
, versionlist
);
64 if (tor_version_parse(myversion
, &mine
)) {
65 log_err(LD_BUG
,"I couldn't parse my own version (%s)", myversion
);
68 version_sl
= smartlist_new();
69 smartlist_split_string(version_sl
, versionlist
, ",", SPLIT_SKIP_SPACE
, 0);
71 if (!strlen(versionlist
)) { /* no authorities cared or agreed */
76 SMARTLIST_FOREACH_BEGIN(version_sl
, const char *, cp
) {
77 if (!strcmpstart(cp
, "Tor "))
80 if (tor_version_parse(cp
, &other
)) {
81 /* Couldn't parse other; it can't be a match. */
83 same
= tor_version_same_series(&mine
, &other
);
85 found_any_in_series
= 1;
86 r
= tor_version_compare(&mine
, &other
);
93 found_newer_in_series
= 1;
98 } SMARTLIST_FOREACH_END(cp
);
100 /* We didn't find the listed version. Is it new or old? */
101 if (found_any_in_series
&& !found_newer_in_series
&& found_newer
) {
102 ret
= VS_NEW_IN_SERIES
;
103 } else if (found_newer
&& !found_older
) {
105 } else if (found_older
&& !found_newer
) {
108 ret
= VS_UNRECOMMENDED
;
112 SMARTLIST_FOREACH(version_sl
, char *, version
, tor_free(version
));
113 smartlist_free(version_sl
);
117 /** Extract a Tor version from a <b>platform</b> line from a router
118 * descriptor, and place the result in <b>router_version</b>.
120 * Return 1 on success, -1 on parsing failure, and 0 if the
121 * platform line does not indicate some version of Tor.
123 * If <b>strict</b> is non-zero, finding any weird version components
124 * (like negative numbers) counts as a parsing failure.
127 tor_version_parse_platform(const char *platform
,
128 tor_version_t
*router_version
,
132 char *s
, *s2
, *start
;
134 if (strcmpstart(platform
,"Tor ")) /* nonstandard Tor; say 0. */
137 start
= (char *)eat_whitespace(platform
+3);
138 if (!*start
) return -1;
139 s
= (char *)find_whitespace(start
); /* also finds '\0', which is fine */
140 s2
= (char*)eat_whitespace(s
);
141 if (!strcmpstart(s2
, "(r") || !strcmpstart(s2
, "(git-"))
142 s
= (char*)find_whitespace(s2
);
144 if ((size_t)(s
-start
+1) >= sizeof(tmp
)) /* too big, no */
146 strlcpy(tmp
, start
, s
-start
+1);
148 if (tor_version_parse(tmp
, router_version
)<0) {
149 log_info(LD_DIR
,"Router version '%s' unparseable.",tmp
);
154 if (router_version
->major
< 0 ||
155 router_version
->minor
< 0 ||
156 router_version
->micro
< 0 ||
157 router_version
->patchlevel
< 0 ||
158 router_version
->svn_revision
< 0) {
166 /** Parse the Tor version of the platform string <b>platform</b>,
167 * and compare it to the version in <b>cutoff</b>. Return 1 if
168 * the router is at least as new as the cutoff, else return 0.
171 tor_version_as_new_as(const char *platform
, const char *cutoff
)
173 tor_version_t cutoff_version
, router_version
;
175 tor_assert(platform
);
177 if (tor_version_parse(cutoff
, &cutoff_version
)<0) {
178 log_warn(LD_BUG
,"cutoff version '%s' unparseable.",cutoff
);
182 r
= tor_version_parse_platform(platform
, &router_version
, 0);
184 /* nonstandard Tor; be safe and say yes */
187 /* unparseable version; be safe and say yes. */
191 /* Here's why we don't need to do any special handling for svn revisions:
192 * - If neither has an svn revision, we're fine.
193 * - If the router doesn't have an svn revision, we can't assume that it
194 * is "at least" any svn revision, so we need to return 0.
195 * - If the target version doesn't have an svn revision, any svn revision
196 * (or none at all) is good enough, so return 1.
197 * - If both target and router have an svn revision, we compare them.
200 return tor_version_compare(&router_version
, &cutoff_version
) >= 0;
203 /** Parse a tor version from <b>s</b>, and store the result in <b>out</b>.
204 * Return 0 on success, -1 on failure. */
206 tor_version_parse(const char *s
, tor_version_t
*out
)
212 * "Tor " ? NUM dot NUM [ dot NUM [ ( pre | rc | dot ) NUM ] ] [ - tag ]
217 memset(out
, 0, sizeof(tor_version_t
));
218 out
->status
= VER_RELEASE
;
219 if (!strcasecmpstart(s
, "Tor "))
226 if (!cp || *cp < '0' || *cp > '9') \
228 out->m = (int)tor_parse_uint64(cp, 10, 0, INT32_MAX, &ok, &eos); \
231 if (!eos || eos == cp) \
256 } else if (*cp
== '.') {
258 } else if (*cp
== '-') {
260 } else if (0==strncmp(cp
, "pre", 3)) {
261 out
->status
= VER_PRE
;
263 } else if (0==strncmp(cp
, "rc", 2)) {
264 out
->status
= VER_RC
;
273 /* Get status tag. */
274 if (*cp
== '-' || *cp
== '.')
276 eos
= (char*) find_whitespace(cp
);
277 if (eos
-cp
>= (int)sizeof(out
->status_tag
))
278 strlcpy(out
->status_tag
, cp
, sizeof(out
->status_tag
));
280 memcpy(out
->status_tag
, cp
, eos
-cp
);
281 out
->status_tag
[eos
-cp
] = 0;
283 cp
= eat_whitespace(eos
);
285 if (!strcmpstart(cp
, "(r")) {
287 out
->svn_revision
= (int) strtol(cp
,&eos
,10);
288 } else if (!strcmpstart(cp
, "(git-")) {
289 char *close_paren
= strchr(cp
, ')');
291 char digest
[DIGEST_LEN
];
295 if (close_paren
-cp
> HEX_DIGEST_LEN
)
297 hexlen
= (int)(close_paren
-cp
);
298 memwipe(digest
, 0, sizeof(digest
));
299 if (hexlen
== 0 || (hexlen
% 2) == 1)
301 if (base16_decode(digest
, hexlen
/2, cp
, hexlen
) != hexlen
/2)
303 memcpy(out
->git_tag
, digest
, hexlen
/2);
304 out
->git_tag_len
= hexlen
/2;
312 /** Compare two tor versions; Return <0 if a < b; 0 if a ==b, >0 if a >
315 tor_version_compare(tor_version_t
*a
, tor_version_t
*b
)
321 /* We take this approach to comparison to ensure the same (bogus!) behavior
322 * on all inputs as we would have seen before bug #21278 was fixed. The
323 * only important difference here is that this method doesn't cause
324 * a signed integer underflow.
326 #define CMP(field) do { \
327 unsigned aval = (unsigned) a->field; \
328 unsigned bval = (unsigned) b->field; \
329 int result = (int) (aval - bval); \
332 else if (result > 0) \
341 if ((i
= strcmp(a
->status_tag
, b
->status_tag
)))
346 return fast_memcmp(a
->git_tag
, b
->git_tag
, a
->git_tag_len
);
353 /** Return true iff versions <b>a</b> and <b>b</b> belong to the same series.
356 tor_version_same_series(tor_version_t
*a
, tor_version_t
*b
)
360 return ((a
->major
== b
->major
) &&
361 (a
->minor
== b
->minor
) &&
362 (a
->micro
== b
->micro
));
365 /** Helper: Given pointers to two strings describing tor versions, return -1
366 * if _a precedes _b, 1 if _b precedes _a, and 0 if they are equivalent.
367 * Used to sort a list of versions. */
369 compare_tor_version_str_ptr_(const void **_a
, const void **_b
)
371 const char *a
= *_a
, *b
= *_b
;
373 tor_version_t va
, vb
;
374 ca
= tor_version_parse(a
, &va
);
375 cb
= tor_version_parse(b
, &vb
);
376 /* If they both parse, compare them. */
378 return tor_version_compare(&va
,&vb
);
379 /* If one parses, it comes first. */
384 /* If neither parses, compare strings. Also, the directory server admin
385 ** needs to be smacked upside the head. But Tor is tolerant and gentle. */
389 /** Sort a list of string-representations of versions in ascending order. */
391 sort_version_list(smartlist_t
*versions
, int remove_duplicates
)
393 smartlist_sort(versions
, compare_tor_version_str_ptr_
);
395 if (remove_duplicates
)
396 smartlist_uniq(versions
, compare_tor_version_str_ptr_
, tor_free_
);
399 /** If there are more than this many entries, we're probably under
400 * some kind of weird DoS. */
401 static const int MAX_PROTOVER_SUMMARY_MAP_LEN
= 1024;
404 * Map from protover string to protover_summary_flags_t.
406 static strmap_t
*protover_summary_map
= NULL
;
409 * Helper. Given a non-NULL protover string <b>protocols</b>, set <b>out</b>
410 * to its summary, and memoize the result in <b>protover_summary_map</b>.
412 * If the protover string does not contain any recognised protocols, sets
413 * protocols_known, but does not set any other flags. (Empty strings are also
417 memoize_protover_summary(protover_summary_flags_t
*out
,
418 const char *protocols
)
420 if (!protover_summary_map
)
421 protover_summary_map
= strmap_new();
423 if (strmap_size(protover_summary_map
) >= MAX_PROTOVER_SUMMARY_MAP_LEN
) {
424 protover_summary_cache_free_all();
425 tor_assert(protover_summary_map
== NULL
);
426 protover_summary_map
= strmap_new();
429 const protover_summary_flags_t
*cached
=
430 strmap_get(protover_summary_map
, protocols
);
432 if (cached
!= NULL
) {
433 /* We found a cached entry; no need to parse this one. */
434 memcpy(out
, cached
, sizeof(protover_summary_flags_t
));
435 tor_assert(out
->protocols_known
);
439 memset(out
, 0, sizeof(*out
));
440 out
->protocols_known
= 1;
442 out
->supports_ed25519_link_handshake_compat
=
443 protocol_list_supports_protocol(protocols
, PRT_LINKAUTH
,
444 PROTOVER_LINKAUTH_ED25519_HANDSHAKE
);
445 out
->supports_ed25519_link_handshake_any
=
446 protocol_list_supports_protocol_or_later(
449 PROTOVER_LINKAUTH_ED25519_HANDSHAKE
);
451 out
->supports_extend2_cells
=
452 protocol_list_supports_protocol(protocols
, PRT_RELAY
,
453 PROTOVER_RELAY_EXTEND2
);
454 out
->supports_accepting_ipv6_extends
= (
455 protocol_list_supports_protocol(protocols
, PRT_RELAY
,
456 PROTOVER_RELAY_ACCEPT_IPV6
) ||
457 protocol_list_supports_protocol(protocols
, PRT_RELAY
,
458 PROTOVER_RELAY_EXTEND_IPV6
));
459 out
->supports_initiating_ipv6_extends
=
460 protocol_list_supports_protocol(protocols
, PRT_RELAY
,
461 PROTOVER_RELAY_EXTEND_IPV6
);
462 out
->supports_canonical_ipv6_conns
=
463 protocol_list_supports_protocol(protocols
, PRT_RELAY
,
464 PROTOVER_RELAY_CANONICAL_IPV6
);
466 out
->supports_ed25519_hs_intro
=
467 protocol_list_supports_protocol(protocols
, PRT_HSINTRO
,
468 PROTOVER_HS_INTRO_V3
);
469 out
->supports_establish_intro_dos_extension
=
470 protocol_list_supports_protocol(protocols
, PRT_HSINTRO
,
471 PROTOVER_HS_INTRO_DOS
);
473 out
->supports_v3_rendezvous_point
=
474 protocol_list_supports_protocol(protocols
, PRT_HSREND
,
475 PROTOVER_HS_RENDEZVOUS_POINT_V3
);
477 out
->supports_v3_hsdir
=
478 protocol_list_supports_protocol(protocols
, PRT_HSDIR
,
481 out
->supports_hs_setup_padding
=
482 protocol_list_supports_protocol(protocols
, PRT_PADDING
,
483 PROTOVER_HS_SETUP_PADDING
);
485 out
->supports_congestion_control
=
486 protocol_list_supports_protocol(protocols
, PRT_FLOWCTRL
,
487 PROTOVER_FLOWCTRL_CC
) &&
488 protocol_list_supports_protocol(protocols
, PRT_RELAY
,
489 PROTOVER_RELAY_NTOR_V3
);
491 /* Conflux requires congestion control. */
492 out
->supports_conflux
=
493 protocol_list_supports_protocol(protocols
, PRT_FLOWCTRL
,
494 PROTOVER_FLOWCTRL_CC
) &&
495 protocol_list_supports_protocol(protocols
, PRT_CONFLUX
,
496 PROTOVER_CONFLUX_V1
);
498 protover_summary_flags_t
*new_cached
= tor_memdup(out
, sizeof(*out
));
499 cached
= strmap_set(protover_summary_map
, protocols
, new_cached
);
503 /** Summarize the protocols listed in <b>protocols</b> into <b>out</b>,
504 * falling back or correcting them based on <b>version</b> as appropriate.
506 * If protocols and version are both NULL or "", returns a summary with no
509 * If the protover string does not contain any recognised protocols, and the
510 * version is not recognised, sets protocols_known, but does not set any other
511 * flags. (Empty strings are also treated this way.)
514 summarize_protover_flags(protover_summary_flags_t
*out
,
515 const char *protocols
,
519 memset(out
, 0, sizeof(*out
));
520 if (protocols
&& strcmp(protocols
, "")) {
521 memoize_protover_summary(out
, protocols
);
523 if (version
&& strcmp(version
, "") && !strcmpstart(version
, "Tor ")) {
524 if (!out
->protocols_known
) {
525 /* The version is a "Tor" version, and where there is no
526 * list of protocol versions that we should be looking at instead. */
528 out
->supports_extend2_cells
=
529 tor_version_as_new_as(version
, "0.2.4.8-alpha");
530 out
->protocols_known
= 1;
532 /* Bug #22447 forces us to filter on this version. */
533 if (!tor_version_as_new_as(version
, "0.3.0.8")) {
534 out
->supports_v3_hsdir
= 0;
541 * Free all space held in the protover_summary_map.
544 protover_summary_cache_free_all(void)
546 strmap_free(protover_summary_map
, tor_free_
);
547 protover_summary_map
= NULL
;