Tor 0.4.9.0-alpha-dev
versions.c
Go to the documentation of this file.
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 */
6
7/**
8 * \file versions.c
9 * \brief Code to manipulate, parse, and compare Tor versions.
10 */
11#include "core/or/or.h"
12
13#include "core/or/protover.h"
14#include "core/or/versions.h"
16
18
19/**
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
22 * configure.ac
23 **/
24time_t
26{
27 char tbuf[ISO_TIME_LEN+1];
28 tor_snprintf(tbuf, sizeof(tbuf),
29 "%s 00:00:00", APPROX_RELEASE_DATE);
30 time_t result = 0;
31 int r = parse_iso_time(tbuf, &result);
32 if (BUG(r < 0)) {
33 result = 0;
34 }
35 return result;
36}
37
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
46 * VS_UNRECOMMENDED.
47 *
48 * (versionlist is a comma-separated list of version strings,
49 * optionally prefixed with "Tor". Versions that can't be parsed are
50 * ignored.)
51 */
53tor_version_is_obsolete(const char *myversion, const char *versionlist)
54{
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;
59 smartlist_t *version_sl;
60
61 log_debug(LD_CONFIG,"Checking whether version '%s' is in '%s'",
62 myversion, versionlist);
63
64 if (tor_version_parse(myversion, &mine)) {
65 log_err(LD_BUG,"I couldn't parse my own version (%s)", myversion);
66 tor_assert(0);
67 }
68 version_sl = smartlist_new();
69 smartlist_split_string(version_sl, versionlist, ",", SPLIT_SKIP_SPACE, 0);
70
71 if (!strlen(versionlist)) { /* no authorities cared or agreed */
72 ret = VS_EMPTY;
73 goto done;
74 }
75
76 SMARTLIST_FOREACH_BEGIN(version_sl, const char *, cp) {
77 if (!strcmpstart(cp, "Tor "))
78 cp += 4;
79
80 if (tor_version_parse(cp, &other)) {
81 /* Couldn't parse other; it can't be a match. */
82 } else {
83 same = tor_version_same_series(&mine, &other);
84 if (same)
85 found_any_in_series = 1;
86 r = tor_version_compare(&mine, &other);
87 if (r==0) {
88 ret = VS_RECOMMENDED;
89 goto done;
90 } else if (r<0) {
91 found_newer = 1;
92 if (same)
93 found_newer_in_series = 1;
94 } else if (r>0) {
95 found_older = 1;
96 }
97 }
98 } SMARTLIST_FOREACH_END(cp);
99
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) {
104 ret = VS_OLD;
105 } else if (found_older && !found_newer) {
106 ret = VS_NEW;
107 } else {
108 ret = VS_UNRECOMMENDED;
109 }
110
111 done:
112 SMARTLIST_FOREACH(version_sl, char *, version, tor_free(version));
113 smartlist_free(version_sl);
114 return ret;
115}
116
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>.
119 *
120 * Return 1 on success, -1 on parsing failure, and 0 if the
121 * platform line does not indicate some version of Tor.
122 *
123 * If <b>strict</b> is non-zero, finding any weird version components
124 * (like negative numbers) counts as a parsing failure.
125 */
126int
127tor_version_parse_platform(const char *platform,
128 tor_version_t *router_version,
129 int strict)
130{
131 char tmp[128];
132 char *s, *s2, *start;
133
134 if (strcmpstart(platform,"Tor ")) /* nonstandard Tor; say 0. */
135 return 0;
136
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);
143
144 if ((size_t)(s-start+1) >= sizeof(tmp)) /* too big, no */
145 return -1;
146 strlcpy(tmp, start, s-start+1);
147
148 if (tor_version_parse(tmp, router_version)<0) {
149 log_info(LD_DIR,"Router version '%s' unparseable.",tmp);
150 return -1;
151 }
152
153 if (strict) {
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) {
159 return -1;
160 }
161 }
162
163 return 1;
164}
165
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.
169 */
170int
171tor_version_as_new_as(const char *platform, const char *cutoff)
172{
173 tor_version_t cutoff_version, router_version;
174 int r;
175 tor_assert(platform);
176
177 if (tor_version_parse(cutoff, &cutoff_version)<0) {
178 log_warn(LD_BUG,"cutoff version '%s' unparseable.",cutoff);
179 return 0;
180 }
181
182 r = tor_version_parse_platform(platform, &router_version, 0);
183 if (r == 0) {
184 /* nonstandard Tor; be safe and say yes */
185 return 1;
186 } else if (r < 0) {
187 /* unparseable version; be safe and say yes. */
188 return 1;
189 }
190
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.
198 */
199
200 return tor_version_compare(&router_version, &cutoff_version) >= 0;
201}
202
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. */
205int
207{
208 char *eos=NULL;
209 const char *cp=NULL;
210 int ok = 1;
211 /* Format is:
212 * "Tor " ? NUM dot NUM [ dot NUM [ ( pre | rc | dot ) NUM ] ] [ - tag ]
213 */
214 tor_assert(s);
215 tor_assert(out);
216
217 memset(out, 0, sizeof(tor_version_t));
218 out->status = VER_RELEASE;
219 if (!strcasecmpstart(s, "Tor "))
220 s += 4;
221
222 cp = s;
223
224#define NUMBER(m) \
225 do { \
226 if (!cp || *cp < '0' || *cp > '9') \
227 return -1; \
228 out->m = (int)tor_parse_uint64(cp, 10, 0, INT32_MAX, &ok, &eos); \
229 if (!ok) \
230 return -1; \
231 if (!eos || eos == cp) \
232 return -1; \
233 cp = eos; \
234 } while (0)
235
236#define DOT() \
237 do { \
238 if (*cp != '.') \
239 return -1; \
240 ++cp; \
241 } while (0)
242
243 NUMBER(major);
244 DOT();
245 NUMBER(minor);
246 if (*cp == 0)
247 return 0;
248 else if (*cp == '-')
249 goto status_tag;
250 DOT();
251 NUMBER(micro);
252
253 /* Get status */
254 if (*cp == 0) {
255 return 0;
256 } else if (*cp == '.') {
257 ++cp;
258 } else if (*cp == '-') {
259 goto status_tag;
260 } else if (0==strncmp(cp, "pre", 3)) {
261 out->status = VER_PRE;
262 cp += 3;
263 } else if (0==strncmp(cp, "rc", 2)) {
264 out->status = VER_RC;
265 cp += 2;
266 } else {
267 return -1;
268 }
269
270 NUMBER(patchlevel);
271
272 status_tag:
273 /* Get status tag. */
274 if (*cp == '-' || *cp == '.')
275 ++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));
279 else {
280 memcpy(out->status_tag, cp, eos-cp);
281 out->status_tag[eos-cp] = 0;
282 }
283 cp = eat_whitespace(eos);
284
285 if (!strcmpstart(cp, "(r")) {
286 cp += 2;
287 out->svn_revision = (int) strtol(cp,&eos,10);
288 } else if (!strcmpstart(cp, "(git-")) {
289 char *close_paren = strchr(cp, ')');
290 int hexlen;
291 char digest[DIGEST_LEN];
292 if (! close_paren)
293 return -1;
294 cp += 5;
295 if (close_paren-cp > HEX_DIGEST_LEN)
296 return -1;
297 hexlen = (int)(close_paren-cp);
298 memwipe(digest, 0, sizeof(digest));
299 if (hexlen == 0 || (hexlen % 2) == 1)
300 return -1;
301 if (base16_decode(digest, hexlen/2, cp, hexlen) != hexlen/2)
302 return -1;
303 memcpy(out->git_tag, digest, hexlen/2);
304 out->git_tag_len = hexlen/2;
305 }
306
307 return 0;
308#undef NUMBER
309#undef DOT
310}
311
312/** Compare two tor versions; Return <0 if a < b; 0 if a ==b, >0 if a >
313 * b. */
314int
316{
317 int i;
318 tor_assert(a);
319 tor_assert(b);
320
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.
325 */
326#define CMP(field) do { \
327 unsigned aval = (unsigned) a->field; \
328 unsigned bval = (unsigned) b->field; \
329 int result = (int) (aval - bval); \
330 if (result < 0) \
331 return -1; \
332 else if (result > 0) \
333 return 1; \
334 } while (0)
335
336 CMP(major);
337 CMP(minor);
338 CMP(micro);
339 CMP(status);
340 CMP(patchlevel);
341 if ((i = strcmp(a->status_tag, b->status_tag)))
342 return i;
343 CMP(svn_revision);
344 CMP(git_tag_len);
345 if (a->git_tag_len)
346 return fast_memcmp(a->git_tag, b->git_tag, a->git_tag_len);
347 else
348 return 0;
349
350#undef CMP
351}
352
353/** Return true iff versions <b>a</b> and <b>b</b> belong to the same series.
354 */
355int
357{
358 tor_assert(a);
359 tor_assert(b);
360 return ((a->major == b->major) &&
361 (a->minor == b->minor) &&
362 (a->micro == b->micro));
363}
364
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. */
368static int
369compare_tor_version_str_ptr_(const void **_a, const void **_b)
370{
371 const char *a = *_a, *b = *_b;
372 int ca, cb;
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. */
377 if (!ca && !cb)
378 return tor_version_compare(&va,&vb);
379 /* If one parses, it comes first. */
380 if (!ca && cb)
381 return -1;
382 if (ca && !cb)
383 return 1;
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. */
386 return strcmp(a,b);
387}
388
389/** Sort a list of string-representations of versions in ascending order. */
390void
391sort_version_list(smartlist_t *versions, int remove_duplicates)
392{
394
395 if (remove_duplicates)
397}
398
399/** If there are more than this many entries, we're probably under
400 * some kind of weird DoS. */
401static const int MAX_PROTOVER_SUMMARY_MAP_LEN = 1024;
402
403/**
404 * Map from protover string to protover_summary_flags_t.
405 */
406static strmap_t *protover_summary_map = NULL;
407
408/**
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>.
411 *
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
414 * treated this way.)
415 */
416static void
418 const char *protocols)
419{
421 protover_summary_map = strmap_new();
422
426 protover_summary_map = strmap_new();
427 }
428
429 const protover_summary_flags_t *cached =
430 strmap_get(protover_summary_map, protocols);
431
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));
436 return;
437 }
438
439 memset(out, 0, sizeof(*out));
440 out->protocols_known = 1;
441
443 protocol_list_supports_protocol(protocols, PRT_LINKAUTH,
447 protocols,
448 PRT_LINKAUTH,
450
452 protocol_list_supports_protocol(protocols, PRT_RELAY,
455 protocol_list_supports_protocol(protocols, PRT_RELAY,
457 protocol_list_supports_protocol(protocols, PRT_RELAY,
460 protocol_list_supports_protocol(protocols, PRT_RELAY,
463 protocol_list_supports_protocol(protocols, PRT_RELAY,
465
467 protocol_list_supports_protocol(protocols, PRT_HSINTRO,
470 protocol_list_supports_protocol(protocols, PRT_HSINTRO,
472
474 protocol_list_supports_protocol(protocols, PRT_HSREND,
476
477 out->supports_v3_hsdir =
478 protocol_list_supports_protocol(protocols, PRT_HSDIR,
480
482 protocol_list_supports_protocol(protocols, PRT_PADDING,
484
486 protocol_list_supports_protocol(protocols, PRT_FLOWCTRL,
488 protocol_list_supports_protocol(protocols, PRT_RELAY,
490
491 /* Conflux requires congestion control. */
492 out->supports_conflux =
493 protocol_list_supports_protocol(protocols, PRT_FLOWCTRL,
495 protocol_list_supports_protocol(protocols, PRT_CONFLUX,
497
498 protover_summary_flags_t *new_cached = tor_memdup(out, sizeof(*out));
499 cached = strmap_set(protover_summary_map, protocols, new_cached);
500 tor_assert(!cached);
501}
502
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.
505 *
506 * If protocols and version are both NULL or "", returns a summary with no
507 * flags set.
508 *
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.)
512 */
513void
515 const char *protocols,
516 const char *version)
517{
518 tor_assert(out);
519 memset(out, 0, sizeof(*out));
520 if (protocols && strcmp(protocols, "")) {
521 memoize_protover_summary(out, protocols);
522 }
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. */
527
529 tor_version_as_new_as(version, "0.2.4.8-alpha");
530 out->protocols_known = 1;
531 } else {
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;
535 }
536 }
537 }
538}
539
540/**
541 * Free all space held in the protover_summary_map.
542 */
543void
545{
546 strmap_free(protover_summary_map, tor_free_);
548}
int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen)
Definition: binascii.c:506
#define HEX_DIGEST_LEN
Definition: crypto_digest.h:35
void memwipe(void *mem, uint8_t byte, size_t sz)
Definition: crypto_util.c:55
Common functions for cryptographic routines.
#define fast_memcmp(a, b, c)
Definition: di_ops.h:28
#define DIGEST_LEN
Definition: digest_sizes.h:20
#define LD_BUG
Definition: log.h:86
#define LD_DIR
Definition: log.h:88
#define LD_CONFIG
Definition: log.h:68
void tor_free_(void *mem)
Definition: malloc.c:227
#define tor_free(p)
Definition: malloc.h:56
Master header file for Tor-specific functionality.
int tor_snprintf(char *str, size_t size, const char *format,...)
Definition: printf.c:27
int protocol_list_supports_protocol(const char *list, protocol_type_t tp, uint32_t version)
Definition: protover.c:330
int protocol_list_supports_protocol_or_later(const char *list, protocol_type_t tp, uint32_t version)
Definition: protover.c:355
Headers and type declarations for protover.c.
#define PROTOVER_RELAY_CANONICAL_IPV6
Definition: protover.h:37
#define PROTOVER_FLOWCTRL_CC
Definition: protover.h:59
#define PROTOVER_HS_INTRO_DOS
Definition: protover.h:47
#define PROTOVER_HSDIR_V3
Definition: protover.h:53
#define PROTOVER_HS_RENDEZVOUS_POINT_V3
Definition: protover.h:50
#define PROTOVER_RELAY_ACCEPT_IPV6
Definition: protover.h:32
#define PROTOVER_RELAY_EXTEND_IPV6
Definition: protover.h:34
#define PROTOVER_HS_INTRO_V3
Definition: protover.h:44
#define PROTOVER_LINKAUTH_ED25519_HANDSHAKE
Definition: protover.h:27
#define PROTOVER_CONFLUX_V1
Definition: protover.h:41
#define PROTOVER_RELAY_NTOR_V3
Definition: protover.h:39
#define PROTOVER_RELAY_EXTEND2
Definition: protover.h:30
#define PROTOVER_HS_SETUP_PADDING
Definition: protover.h:56
void smartlist_sort(smartlist_t *sl, int(*compare)(const void **a, const void **b))
Definition: smartlist.c:334
void smartlist_uniq(smartlist_t *sl, int(*compare)(const void **a, const void **b), void(*free_fn)(void *a))
Definition: smartlist.c:390
smartlist_t * smartlist_new(void)
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
#define SMARTLIST_FOREACH(sl, type, var, cmd)
int smartlist_split_string(smartlist_t *sl, const char *str, const char *sep, int flags, int max)
unsigned int supports_extend2_cells
Definition: or.h:693
unsigned int supports_ed25519_link_handshake_compat
Definition: or.h:710
unsigned int supports_v3_rendezvous_point
Definition: or.h:734
unsigned int supports_hs_setup_padding
Definition: or.h:738
unsigned int supports_initiating_ipv6_extends
Definition: or.h:701
unsigned int supports_v3_hsdir
Definition: or.h:729
unsigned int supports_ed25519_link_handshake_any
Definition: or.h:715
unsigned int supports_congestion_control
Definition: or.h:742
unsigned int supports_canonical_ipv6_conns
Definition: or.h:705
unsigned int supports_conflux
Definition: or.h:745
unsigned int protocols_known
Definition: or.h:689
unsigned int supports_accepting_ipv6_extends
Definition: or.h:697
unsigned int supports_ed25519_hs_intro
Definition: or.h:720
unsigned int supports_establish_intro_dos_extension
Definition: or.h:724
enum tor_version_t::@13 status
int parse_iso_time(const char *cp, time_t *t)
Definition: time_fmt.c:423
Parsed Tor version structure.
#define tor_assert(expr)
Definition: util_bug.h:103
int strcasecmpstart(const char *s1, const char *s2)
Definition: util_string.c:227
int strcmpstart(const char *s1, const char *s2)
Definition: util_string.c:217
const char * find_whitespace(const char *s)
Definition: util_string.c:355
const char * eat_whitespace(const char *s)
Definition: util_string.c:279
void protover_summary_cache_free_all(void)
Definition: versions.c:544
static strmap_t * protover_summary_map
Definition: versions.c:406
time_t tor_get_approx_release_date(void)
Definition: versions.c:25
void summarize_protover_flags(protover_summary_flags_t *out, const char *protocols, const char *version)
Definition: versions.c:514
int tor_version_as_new_as(const char *platform, const char *cutoff)
Definition: versions.c:171
void sort_version_list(smartlist_t *versions, int remove_duplicates)
Definition: versions.c:391
int tor_version_parse_platform(const char *platform, tor_version_t *router_version, int strict)
Definition: versions.c:127
static void memoize_protover_summary(protover_summary_flags_t *out, const char *protocols)
Definition: versions.c:417
int tor_version_compare(tor_version_t *a, tor_version_t *b)
Definition: versions.c:315
static int compare_tor_version_str_ptr_(const void **_a, const void **_b)
Definition: versions.c:369
static const int MAX_PROTOVER_SUMMARY_MAP_LEN
Definition: versions.c:401
int tor_version_parse(const char *s, tor_version_t *out)
Definition: versions.c:206
int tor_version_same_series(tor_version_t *a, tor_version_t *b)
Definition: versions.c:356
version_status_t tor_version_is_obsolete(const char *myversion, const char *versionlist)
Definition: versions.c:53
Header file for versions.c.
version_status_t
Definition: versions.h:17
@ VS_OLD
Definition: versions.h:19
@ VS_EMPTY
Definition: versions.h:25
@ VS_NEW_IN_SERIES
Definition: versions.h:21
@ VS_UNRECOMMENDED
Definition: versions.h:24
@ VS_RECOMMENDED
Definition: versions.h:18
@ VS_NEW
Definition: versions.h:20