versions.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  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-2018, The Tor Project, Inc. */
  5. /* See LICENSE for licensing information */
  6. /**
  7. * \file versions.c
  8. * \brief Code to manipulate, parse, and compare Tor versions.
  9. */
  10. #include "core/or/or.h"
  11. #include "core/or/versions.h"
  12. #include "lib/crypt_ops/crypto_util.h"
  13. #include "core/or/tor_version_st.h"
  14. /** Return VS_RECOMMENDED if <b>myversion</b> is contained in
  15. * <b>versionlist</b>. Else, return VS_EMPTY if versionlist has no
  16. * entries. Else, return VS_OLD if every member of
  17. * <b>versionlist</b> is newer than <b>myversion</b>. Else, return
  18. * VS_NEW_IN_SERIES if there is at least one member of <b>versionlist</b> in
  19. * the same series (major.minor.micro) as <b>myversion</b>, but no such member
  20. * is newer than <b>myversion.</b>. Else, return VS_NEW if every member of
  21. * <b>versionlist</b> is older than <b>myversion</b>. Else, return
  22. * VS_UNRECOMMENDED.
  23. *
  24. * (versionlist is a comma-separated list of version strings,
  25. * optionally prefixed with "Tor". Versions that can't be parsed are
  26. * ignored.)
  27. */
  28. version_status_t
  29. tor_version_is_obsolete(const char *myversion, const char *versionlist)
  30. {
  31. tor_version_t mine, other;
  32. int found_newer = 0, found_older = 0, found_newer_in_series = 0,
  33. found_any_in_series = 0, r, same;
  34. version_status_t ret = VS_UNRECOMMENDED;
  35. smartlist_t *version_sl;
  36. log_debug(LD_CONFIG,"Checking whether version '%s' is in '%s'",
  37. myversion, versionlist);
  38. if (tor_version_parse(myversion, &mine)) {
  39. log_err(LD_BUG,"I couldn't parse my own version (%s)", myversion);
  40. tor_assert(0);
  41. }
  42. version_sl = smartlist_new();
  43. smartlist_split_string(version_sl, versionlist, ",", SPLIT_SKIP_SPACE, 0);
  44. if (!strlen(versionlist)) { /* no authorities cared or agreed */
  45. ret = VS_EMPTY;
  46. goto done;
  47. }
  48. SMARTLIST_FOREACH_BEGIN(version_sl, const char *, cp) {
  49. if (!strcmpstart(cp, "Tor "))
  50. cp += 4;
  51. if (tor_version_parse(cp, &other)) {
  52. /* Couldn't parse other; it can't be a match. */
  53. } else {
  54. same = tor_version_same_series(&mine, &other);
  55. if (same)
  56. found_any_in_series = 1;
  57. r = tor_version_compare(&mine, &other);
  58. if (r==0) {
  59. ret = VS_RECOMMENDED;
  60. goto done;
  61. } else if (r<0) {
  62. found_newer = 1;
  63. if (same)
  64. found_newer_in_series = 1;
  65. } else if (r>0) {
  66. found_older = 1;
  67. }
  68. }
  69. } SMARTLIST_FOREACH_END(cp);
  70. /* We didn't find the listed version. Is it new or old? */
  71. if (found_any_in_series && !found_newer_in_series && found_newer) {
  72. ret = VS_NEW_IN_SERIES;
  73. } else if (found_newer && !found_older) {
  74. ret = VS_OLD;
  75. } else if (found_older && !found_newer) {
  76. ret = VS_NEW;
  77. } else {
  78. ret = VS_UNRECOMMENDED;
  79. }
  80. done:
  81. SMARTLIST_FOREACH(version_sl, char *, version, tor_free(version));
  82. smartlist_free(version_sl);
  83. return ret;
  84. }
  85. /** Extract a Tor version from a <b>platform</b> line from a router
  86. * descriptor, and place the result in <b>router_version</b>.
  87. *
  88. * Return 1 on success, -1 on parsing failure, and 0 if the
  89. * platform line does not indicate some version of Tor.
  90. *
  91. * If <b>strict</b> is non-zero, finding any weird version components
  92. * (like negative numbers) counts as a parsing failure.
  93. */
  94. int
  95. tor_version_parse_platform(const char *platform,
  96. tor_version_t *router_version,
  97. int strict)
  98. {
  99. char tmp[128];
  100. char *s, *s2, *start;
  101. if (strcmpstart(platform,"Tor ")) /* nonstandard Tor; say 0. */
  102. return 0;
  103. start = (char *)eat_whitespace(platform+3);
  104. if (!*start) return -1;
  105. s = (char *)find_whitespace(start); /* also finds '\0', which is fine */
  106. s2 = (char*)eat_whitespace(s);
  107. if (!strcmpstart(s2, "(r") || !strcmpstart(s2, "(git-"))
  108. s = (char*)find_whitespace(s2);
  109. if ((size_t)(s-start+1) >= sizeof(tmp)) /* too big, no */
  110. return -1;
  111. strlcpy(tmp, start, s-start+1);
  112. if (tor_version_parse(tmp, router_version)<0) {
  113. log_info(LD_DIR,"Router version '%s' unparseable.",tmp);
  114. return -1;
  115. }
  116. if (strict) {
  117. if (router_version->major < 0 ||
  118. router_version->minor < 0 ||
  119. router_version->micro < 0 ||
  120. router_version->patchlevel < 0 ||
  121. router_version->svn_revision < 0) {
  122. return -1;
  123. }
  124. }
  125. return 1;
  126. }
  127. /** Parse the Tor version of the platform string <b>platform</b>,
  128. * and compare it to the version in <b>cutoff</b>. Return 1 if
  129. * the router is at least as new as the cutoff, else return 0.
  130. */
  131. int
  132. tor_version_as_new_as(const char *platform, const char *cutoff)
  133. {
  134. tor_version_t cutoff_version, router_version;
  135. int r;
  136. tor_assert(platform);
  137. if (tor_version_parse(cutoff, &cutoff_version)<0) {
  138. log_warn(LD_BUG,"cutoff version '%s' unparseable.",cutoff);
  139. return 0;
  140. }
  141. r = tor_version_parse_platform(platform, &router_version, 0);
  142. if (r == 0) {
  143. /* nonstandard Tor; be safe and say yes */
  144. return 1;
  145. } else if (r < 0) {
  146. /* unparseable version; be safe and say yes. */
  147. return 1;
  148. }
  149. /* Here's why we don't need to do any special handling for svn revisions:
  150. * - If neither has an svn revision, we're fine.
  151. * - If the router doesn't have an svn revision, we can't assume that it
  152. * is "at least" any svn revision, so we need to return 0.
  153. * - If the target version doesn't have an svn revision, any svn revision
  154. * (or none at all) is good enough, so return 1.
  155. * - If both target and router have an svn revision, we compare them.
  156. */
  157. return tor_version_compare(&router_version, &cutoff_version) >= 0;
  158. }
  159. /** Parse a tor version from <b>s</b>, and store the result in <b>out</b>.
  160. * Return 0 on success, -1 on failure. */
  161. int
  162. tor_version_parse(const char *s, tor_version_t *out)
  163. {
  164. char *eos=NULL;
  165. const char *cp=NULL;
  166. int ok = 1;
  167. /* Format is:
  168. * "Tor " ? NUM dot NUM [ dot NUM [ ( pre | rc | dot ) NUM ] ] [ - tag ]
  169. */
  170. tor_assert(s);
  171. tor_assert(out);
  172. memset(out, 0, sizeof(tor_version_t));
  173. out->status = VER_RELEASE;
  174. if (!strcasecmpstart(s, "Tor "))
  175. s += 4;
  176. cp = s;
  177. #define NUMBER(m) \
  178. do { \
  179. if (!cp || *cp < '0' || *cp > '9') \
  180. return -1; \
  181. out->m = (int)tor_parse_uint64(cp, 10, 0, INT32_MAX, &ok, &eos); \
  182. if (!ok) \
  183. return -1; \
  184. if (!eos || eos == cp) \
  185. return -1; \
  186. cp = eos; \
  187. } while (0)
  188. #define DOT() \
  189. do { \
  190. if (*cp != '.') \
  191. return -1; \
  192. ++cp; \
  193. } while (0)
  194. NUMBER(major);
  195. DOT();
  196. NUMBER(minor);
  197. if (*cp == 0)
  198. return 0;
  199. else if (*cp == '-')
  200. goto status_tag;
  201. DOT();
  202. NUMBER(micro);
  203. /* Get status */
  204. if (*cp == 0) {
  205. return 0;
  206. } else if (*cp == '.') {
  207. ++cp;
  208. } else if (*cp == '-') {
  209. goto status_tag;
  210. } else if (0==strncmp(cp, "pre", 3)) {
  211. out->status = VER_PRE;
  212. cp += 3;
  213. } else if (0==strncmp(cp, "rc", 2)) {
  214. out->status = VER_RC;
  215. cp += 2;
  216. } else {
  217. return -1;
  218. }
  219. NUMBER(patchlevel);
  220. status_tag:
  221. /* Get status tag. */
  222. if (*cp == '-' || *cp == '.')
  223. ++cp;
  224. eos = (char*) find_whitespace(cp);
  225. if (eos-cp >= (int)sizeof(out->status_tag))
  226. strlcpy(out->status_tag, cp, sizeof(out->status_tag));
  227. else {
  228. memcpy(out->status_tag, cp, eos-cp);
  229. out->status_tag[eos-cp] = 0;
  230. }
  231. cp = eat_whitespace(eos);
  232. if (!strcmpstart(cp, "(r")) {
  233. cp += 2;
  234. out->svn_revision = (int) strtol(cp,&eos,10);
  235. } else if (!strcmpstart(cp, "(git-")) {
  236. char *close_paren = strchr(cp, ')');
  237. int hexlen;
  238. char digest[DIGEST_LEN];
  239. if (! close_paren)
  240. return -1;
  241. cp += 5;
  242. if (close_paren-cp > HEX_DIGEST_LEN)
  243. return -1;
  244. hexlen = (int)(close_paren-cp);
  245. memwipe(digest, 0, sizeof(digest));
  246. if ( hexlen == 0 || (hexlen % 2) == 1)
  247. return -1;
  248. if (base16_decode(digest, hexlen/2, cp, hexlen) != hexlen/2)
  249. return -1;
  250. memcpy(out->git_tag, digest, hexlen/2);
  251. out->git_tag_len = hexlen/2;
  252. }
  253. return 0;
  254. #undef NUMBER
  255. #undef DOT
  256. }
  257. /** Compare two tor versions; Return <0 if a < b; 0 if a ==b, >0 if a >
  258. * b. */
  259. int
  260. tor_version_compare(tor_version_t *a, tor_version_t *b)
  261. {
  262. int i;
  263. tor_assert(a);
  264. tor_assert(b);
  265. /* We take this approach to comparison to ensure the same (bogus!) behavior
  266. * on all inputs as we would have seen before bug #21278 was fixed. The
  267. * only important difference here is that this method doesn't cause
  268. * a signed integer underflow.
  269. */
  270. #define CMP(field) do { \
  271. unsigned aval = (unsigned) a->field; \
  272. unsigned bval = (unsigned) b->field; \
  273. int result = (int) (aval - bval); \
  274. if (result < 0) \
  275. return -1; \
  276. else if (result > 0) \
  277. return 1; \
  278. } while (0)
  279. CMP(major);
  280. CMP(minor);
  281. CMP(micro);
  282. CMP(status);
  283. CMP(patchlevel);
  284. if ((i = strcmp(a->status_tag, b->status_tag)))
  285. return i;
  286. CMP(svn_revision);
  287. CMP(git_tag_len);
  288. if (a->git_tag_len)
  289. return fast_memcmp(a->git_tag, b->git_tag, a->git_tag_len);
  290. else
  291. return 0;
  292. #undef CMP
  293. }
  294. /** Return true iff versions <b>a</b> and <b>b</b> belong to the same series.
  295. */
  296. int
  297. tor_version_same_series(tor_version_t *a, tor_version_t *b)
  298. {
  299. tor_assert(a);
  300. tor_assert(b);
  301. return ((a->major == b->major) &&
  302. (a->minor == b->minor) &&
  303. (a->micro == b->micro));
  304. }
  305. /** Helper: Given pointers to two strings describing tor versions, return -1
  306. * if _a precedes _b, 1 if _b precedes _a, and 0 if they are equivalent.
  307. * Used to sort a list of versions. */
  308. static int
  309. compare_tor_version_str_ptr_(const void **_a, const void **_b)
  310. {
  311. const char *a = *_a, *b = *_b;
  312. int ca, cb;
  313. tor_version_t va, vb;
  314. ca = tor_version_parse(a, &va);
  315. cb = tor_version_parse(b, &vb);
  316. /* If they both parse, compare them. */
  317. if (!ca && !cb)
  318. return tor_version_compare(&va,&vb);
  319. /* If one parses, it comes first. */
  320. if (!ca && cb)
  321. return -1;
  322. if (ca && !cb)
  323. return 1;
  324. /* If neither parses, compare strings. Also, the directory server admin
  325. ** needs to be smacked upside the head. But Tor is tolerant and gentle. */
  326. return strcmp(a,b);
  327. }
  328. /** Sort a list of string-representations of versions in ascending order. */
  329. void
  330. sort_version_list(smartlist_t *versions, int remove_duplicates)
  331. {
  332. smartlist_sort(versions, compare_tor_version_str_ptr_);
  333. if (remove_duplicates)
  334. smartlist_uniq(versions, compare_tor_version_str_ptr_, tor_free_);
  335. }