Tor  0.4.3.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-2019, 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 
17 #include "core/or/tor_version_st.h"
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  **/
24 time_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  */
53 tor_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  */
126 int
127 tor_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  */
170 int
171 tor_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. */
205 int
206 tor_version_parse(const char *s, tor_version_t *out)
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. */
314 int
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  */
355 int
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. */
368 static int
369 compare_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. */
390 void
391 sort_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. */
401 static const int MAX_PROTOVER_SUMMARY_MAP_LEN = 1024;
402 
403 /**
404  * Map from protover string to protover_summary_flags_t.
405  */
406 static 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 static void
414  const char *protocols)
415 {
417  protover_summary_map = strmap_new();
418 
422  protover_summary_map = strmap_new();
423  }
424 
425  const protover_summary_flags_t *cached =
426  strmap_get(protover_summary_map, protocols);
427 
428  if (cached != NULL) {
429  /* We found a cached entry; no need to parse this one. */
430  memcpy(out, cached, sizeof(protover_summary_flags_t));
432  return;
433  }
434 
435  memset(out, 0, sizeof(*out));
436  out->protocols_known = 1;
438  protocol_list_supports_protocol(protocols, PRT_RELAY, 2);
440  protocol_list_supports_protocol(protocols, PRT_LINKAUTH, 3);
442  protocol_list_supports_protocol_or_later(protocols, PRT_LINKAUTH, 3);
444  protocol_list_supports_protocol(protocols, PRT_HSINTRO, 4);
445  out->supports_v3_hsdir =
446  protocol_list_supports_protocol(protocols, PRT_HSDIR,
449  protocol_list_supports_protocol(protocols, PRT_HSREND,
452  protocol_list_supports_protocol(protocols, PRT_PADDING,
455  protocol_list_supports_protocol(protocols, PRT_HSINTRO, 5);
456 
457  protover_summary_flags_t *new_cached = tor_memdup(out, sizeof(*out));
458  cached = strmap_set(protover_summary_map, protocols, new_cached);
459  tor_assert(!cached);
460 }
461 
462 /** Summarize the protocols listed in <b>protocols</b> into <b>out</b>,
463  * falling back or correcting them based on <b>version</b> as appropriate.
464  */
465 void
467  const char *protocols,
468  const char *version)
469 {
470  tor_assert(out);
471  memset(out, 0, sizeof(*out));
472  if (protocols) {
473  memoize_protover_summary(out, protocols);
474  }
475  if (version && !strcmpstart(version, "Tor ")) {
476  if (!out->protocols_known) {
477  /* The version is a "Tor" version, and where there is no
478  * list of protocol versions that we should be looking at instead. */
479 
481  tor_version_as_new_as(version, "0.2.4.8-alpha");
482  out->protocols_known = 1;
483  } else {
484  /* Bug #22447 forces us to filter on this version. */
485  if (!tor_version_as_new_as(version, "0.3.0.8")) {
486  out->supports_v3_hsdir = 0;
487  }
488  }
489  }
490 }
491 
492 /**
493  * Free all space held in the protover_summary_map.
494  */
495 void
497 {
498  strmap_free(protover_summary_map, tor_free_);
499  protover_summary_map = NULL;
500 }
static const int MAX_PROTOVER_SUMMARY_MAP_LEN
Definition: versions.c:401
static int compare_tor_version_str_ptr_(const void **_a, const void **_b)
Definition: versions.c:369
unsigned int supports_hs_setup_padding
Definition: or.h:845
version_status_t tor_version_is_obsolete(const char *myversion, const char *versionlist)
Definition: versions.c:53
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
void summarize_protover_flags(protover_summary_flags_t *out, const char *protocols, const char *version)
Definition: versions.c:466
int tor_version_compare(tor_version_t *a, tor_version_t *b)
Definition: versions.c:315
Parsed Tor version structure.
int tor_version_as_new_as(const char *platform, const char *cutoff)
Definition: versions.c:171
unsigned int supports_v3_hsdir
Definition: or.h:836
version_status_t
Definition: versions.h:17
const char * find_whitespace(const char *s)
Definition: util_string.c:344
static void memoize_protover_summary(protover_summary_flags_t *out, const char *protocols)
Definition: versions.c:413
#define tor_assert(expr)
Definition: util_bug.h:102
unsigned int supports_extend2_cells
Definition: or.h:816
int strcmpstart(const char *s1, const char *s2)
Definition: util_string.c:206
#define tor_free(p)
Definition: malloc.h:52
void memwipe(void *mem, uint8_t byte, size_t sz)
Definition: crypto_util.c:57
smartlist_t * smartlist_new(void)
Header file for versions.c.
int tor_version_parse(const char *s, tor_version_t *out)
Definition: versions.c:206
Common functions for cryptographic routines.
void smartlist_uniq(smartlist_t *sl, int(*compare)(const void **a, const void **b), void(*free_fn)(void *a))
Definition: smartlist.c:390
unsigned int supports_ed25519_hs_intro
Definition: or.h:831
void protover_summary_cache_free_all(void)
Definition: versions.c:496
#define DIGEST_LEN
Definition: digest_sizes.h:20
unsigned int supports_ed25519_link_handshake_any
Definition: or.h:826
Master header file for Tor-specific functionality.
unsigned int protocols_known
Definition: or.h:812
const char * eat_whitespace(const char *s)
Definition: util_string.c:268
unsigned int supports_establish_intro_dos_extension
Definition: or.h:849
int tor_version_same_series(tor_version_t *a, tor_version_t *b)
Definition: versions.c:356
void tor_free_(void *mem)
Definition: malloc.c:227
unsigned int supports_v3_rendezvous_point
Definition: or.h:841
int protocol_list_supports_protocol(const char *list, protocol_type_t tp, uint32_t version)
Definition: protover.c:331
Headers and type declarations for protover.c.
void sort_version_list(smartlist_t *versions, int remove_duplicates)
Definition: versions.c:391
#define HEX_DIGEST_LEN
Definition: crypto_digest.h:35
#define LD_DIR
Definition: log.h:88
time_t tor_get_approx_release_date(void)
Definition: versions.c:25
int tor_snprintf(char *str, size_t size, const char *format,...)
Definition: printf.c:27
#define SMARTLIST_FOREACH(sl, type, var, cmd)
unsigned int supports_ed25519_link_handshake_compat
Definition: or.h:821
#define PROTOVER_HS_RENDEZVOUS_POINT_V3
Definition: protover.h:30
#define fast_memcmp(a, b, c)
Definition: di_ops.h:28
int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen)
Definition: binascii.c:506
#define PROTOVER_HS_SETUP_PADDING
Definition: protover.h:32
int protocol_list_supports_protocol_or_later(const char *list, protocol_type_t tp, uint32_t version)
Definition: protover.c:353
#define PROTOVER_HSDIR_V3
Definition: protover.h:26
int parse_iso_time(const char *cp, time_t *t)
Definition: time_fmt.c:392
void smartlist_sort(smartlist_t *sl, int(*compare)(const void **a, const void **b))
Definition: smartlist.c:334
static strmap_t * protover_summary_map
Definition: versions.c:406
int strcasecmpstart(const char *s1, const char *s2)
Definition: util_string.c:216
enum tor_version_t::@13 status
#define LD_BUG
Definition: log.h:86
#define LD_CONFIG
Definition: log.h:68
int tor_version_parse_platform(const char *platform, tor_version_t *router_version, int strict)
Definition: versions.c:127
int smartlist_split_string(smartlist_t *sl, const char *str, const char *sep, int flags, int max)