LCOV - code coverage report
Current view: top level - core/or - protover.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 269 285 94.4 %
Date: 2021-11-24 03:28:48 Functions: 20 22 90.9 %

          Line data    Source code
       1             : /* Copyright (c) 2016-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /**
       5             :  * \file protover.c
       6             :  * \brief Versioning information for different pieces of the Tor protocol.
       7             :  *
       8             :  * Starting in version 0.2.9.3-alpha, Tor places separate version numbers on
       9             :  * each of the different components of its protocol. Relays use these numbers
      10             :  * to advertise what versions of the protocols they can support, and clients
      11             :  * use them to find what they can ask a given relay to do.  Authorities vote
      12             :  * on the supported protocol versions for each relay, and also vote on the
      13             :  * which protocols you should have to support in order to be on the Tor
      14             :  * network. All Tor instances use these required/recommended protocol versions
      15             :  * to tell what level of support for recent protocols each relay has, and
      16             :  * to decide whether they should be running given their current protocols.
      17             :  *
      18             :  * The main advantage of these protocol versions numbers over using Tor
      19             :  * version numbers is that they allow different implementations of the Tor
      20             :  * protocols to develop independently, without having to claim compatibility
      21             :  * with specific versions of Tor.
      22             :  **/
      23             : 
      24             : #define PROTOVER_PRIVATE
      25             : 
      26             : #include "core/or/or.h"
      27             : #include "core/or/protover.h"
      28             : #include "core/or/versions.h"
      29             : #include "lib/tls/tortls.h"
      30             : 
      31             : #ifndef HAVE_RUST
      32             : 
      33             : static const smartlist_t *get_supported_protocol_list(void);
      34             : static int protocol_list_contains(const smartlist_t *protos,
      35             :                                   protocol_type_t pr, uint32_t ver);
      36             : static const proto_entry_t *find_entry_by_name(const smartlist_t *protos,
      37             :                                                const char *name);
      38             : 
      39             : /** Mapping between protocol type string and protocol type. */
      40             : /// C_RUST_COUPLED: src/rust/protover/protover.rs `PROTOCOL_NAMES`
      41             : static const struct {
      42             :   protocol_type_t protover_type;
      43             :   const char *name;
      44             : /* If you add a new protocol here, you probably also want to add
      45             :  * parsing for it in summarize_protover_flags(), so that it has a
      46             :  * summary flag in routerstatus_t */
      47             : } PROTOCOL_NAMES[] = {
      48             :   { PRT_LINK, "Link" },
      49             :   { PRT_LINKAUTH, "LinkAuth" },
      50             :   { PRT_RELAY, "Relay" },
      51             :   { PRT_DIRCACHE, "DirCache" },
      52             :   { PRT_HSDIR, "HSDir" },
      53             :   { PRT_HSINTRO, "HSIntro" },
      54             :   { PRT_HSREND, "HSRend" },
      55             :   { PRT_DESC, "Desc" },
      56             :   { PRT_MICRODESC, "Microdesc"},
      57             :   { PRT_PADDING, "Padding"},
      58             :   { PRT_CONS, "Cons" },
      59             :   { PRT_FLOWCTRL, "FlowCtrl"},
      60             : };
      61             : 
      62             : #define N_PROTOCOL_NAMES ARRAY_LENGTH(PROTOCOL_NAMES)
      63             : 
      64             : /* Maximum allowed length of any single subprotocol name. */
      65             : // C_RUST_COUPLED: src/rust/protover/protover.rs
      66             : //                 `MAX_PROTOCOL_NAME_LENGTH`
      67             : static const unsigned MAX_PROTOCOL_NAME_LENGTH = 100;
      68             : 
      69             : /**
      70             :  * Given a protocol_type_t, return the corresponding string used in
      71             :  * descriptors.
      72             :  */
      73             : STATIC const char *
      74        2019 : protocol_type_to_str(protocol_type_t pr)
      75             : {
      76        2019 :   unsigned i;
      77        5458 :   for (i=0; i < N_PROTOCOL_NAMES; ++i) {
      78        5458 :     if (PROTOCOL_NAMES[i].protover_type == pr)
      79        2019 :       return PROTOCOL_NAMES[i].name;
      80             :   }
      81             :   /* LCOV_EXCL_START */
      82             :   tor_assert_nonfatal_unreached_once();
      83             :   return "UNKNOWN";
      84             :   /* LCOV_EXCL_STOP */
      85             : }
      86             : 
      87             : /**
      88             :  * Release all space held by a single proto_entry_t structure
      89             :  */
      90             : STATIC void
      91       22459 : proto_entry_free_(proto_entry_t *entry)
      92             : {
      93       22459 :   if (!entry)
      94             :     return;
      95       22459 :   tor_free(entry->name);
      96       22459 :   tor_free(entry);
      97             : }
      98             : 
      99             : /** The largest possible protocol version. */
     100             : #define MAX_PROTOCOL_VERSION (63)
     101             : 
     102             : /**
     103             :  * Given a string <b>s</b> and optional end-of-string pointer
     104             :  * <b>end_of_range</b>, parse the protocol range and store it in
     105             :  * <b>low_out</b> and <b>high_out</b>.  A protocol range has the format U, or
     106             :  * U-U, where U is an unsigned integer between 0 and 63 inclusive.
     107             :  */
     108             : static int
     109       23847 : parse_version_range(const char *s, const char *end_of_range,
     110             :                     uint32_t *low_out, uint32_t *high_out)
     111             : {
     112       23847 :   uint32_t low, high;
     113       23847 :   char *next = NULL;
     114       23847 :   int ok;
     115             : 
     116       23847 :   tor_assert(high_out);
     117       23847 :   tor_assert(low_out);
     118             : 
     119       23847 :   if (BUG(!end_of_range))
     120             :     end_of_range = s + strlen(s); // LCOV_EXCL_LINE
     121             : 
     122             :   /* A range must start with a digit. */
     123       23847 :   if (!TOR_ISDIGIT(*s)) {
     124          56 :     goto error;
     125             :   }
     126             : 
     127             :   /* Note that this wouldn't be safe if we didn't know that eventually,
     128             :    * we'd hit a NUL */
     129       23791 :   low = (uint32_t) tor_parse_ulong(s, 10, 0, MAX_PROTOCOL_VERSION, &ok, &next);
     130       23791 :   if (!ok)
     131          22 :     goto error;
     132       23769 :   if (next > end_of_range)
     133           0 :     goto error;
     134       23769 :   if (next == end_of_range) {
     135       12141 :     high = low;
     136       12141 :     goto done;
     137             :   }
     138             : 
     139       11628 :   if (*next != '-')
     140          15 :     goto error;
     141       11613 :   s = next+1;
     142             : 
     143             :   /* ibid */
     144       11613 :   if (!TOR_ISDIGIT(*s)) {
     145         201 :     goto error;
     146             :   }
     147       11412 :   high = (uint32_t) tor_parse_ulong(s, 10, 0,
     148             :                                     MAX_PROTOCOL_VERSION, &ok, &next);
     149       11412 :   if (!ok)
     150           3 :     goto error;
     151       11409 :   if (next != end_of_range)
     152          11 :     goto error;
     153             : 
     154       11398 :   if (low > high)
     155           4 :     goto error;
     156             : 
     157       11394 :  done:
     158       23535 :   *high_out = high;
     159       23535 :   *low_out = low;
     160       23535 :   return 0;
     161             : 
     162             :  error:
     163             :   return -1;
     164             : }
     165             : 
     166             : static int
     167       22407 : is_valid_keyword(const char *s, size_t n)
     168             : {
     169      155181 :   for (size_t i = 0; i < n; i++) {
     170      132833 :     if (!TOR_ISALNUM(s[i]) && s[i] != '-')
     171             :       return 0;
     172             :   }
     173             :   return 1;
     174             : }
     175             : 
     176             : /** The x'th bit in a bitmask. */
     177             : #define BIT(x) (UINT64_C(1)<<(x))
     178             : 
     179             : /**
     180             :  * Return a bitmask so that bits 'low' through 'high' inclusive are set,
     181             :  * and all other bits are cleared.
     182             :  **/
     183             : static uint64_t
     184       23620 : bitmask_for_range(uint32_t low, uint32_t high)
     185             : {
     186       23620 :   uint64_t mask = ~(uint64_t)0;
     187       23620 :   mask <<= 63 - high;
     188       23620 :   mask >>= 63 - high + low;
     189       23620 :   mask <<= low;
     190       23620 :   return mask;
     191             : }
     192             : 
     193             : /** Parse a single protocol entry from <b>s</b> up to an optional
     194             :  * <b>end_of_entry</b> pointer, and return that protocol entry. Return NULL
     195             :  * on error.
     196             :  *
     197             :  * A protocol entry has a keyword, an = sign, and zero or more ranges. */
     198             : static proto_entry_t *
     199       22452 : parse_single_entry(const char *s, const char *end_of_entry)
     200             : {
     201       22452 :   proto_entry_t *out = tor_malloc_zero(sizeof(proto_entry_t));
     202       22452 :   const char *equals;
     203             : 
     204       22452 :   if (BUG (!end_of_entry))
     205             :     end_of_entry = s + strlen(s); // LCOV_EXCL_LINE
     206             : 
     207             :   /* There must be an =. */
     208       22452 :   equals = memchr(s, '=', end_of_entry - s);
     209       22452 :   if (!equals)
     210          32 :     goto error;
     211             : 
     212             :   /* The name must be nonempty */
     213       22420 :   if (equals == s)
     214           7 :     goto error;
     215             : 
     216             :   /* The name must not be longer than MAX_PROTOCOL_NAME_LENGTH. */
     217       22413 :   if (equals - s > (int)MAX_PROTOCOL_NAME_LENGTH) {
     218           6 :     log_warn(LD_NET, "When parsing a protocol entry, I got a very large "
     219             :              "protocol name. This is possibly an attack or a bug, unless "
     220             :              "the Tor network truly supports protocol names larger than "
     221             :              "%ud characters. The offending string was: %s",
     222             :              MAX_PROTOCOL_NAME_LENGTH, escaped(out->name));
     223           6 :     goto error;
     224             :   }
     225             : 
     226             :   /* The name must contain only alphanumeric characters and hyphens. */
     227       22407 :   if (!is_valid_keyword(s, equals-s))
     228          59 :     goto error;
     229             : 
     230       22348 :   out->name = tor_strndup(s, equals-s);
     231             : 
     232       22348 :   tor_assert(equals < end_of_entry);
     233             : 
     234       22348 :   s = equals + 1;
     235       45883 :   while (s < end_of_entry) {
     236       23847 :     const char *comma = memchr(s, ',', end_of_entry-s);
     237       23847 :     if (! comma)
     238       21834 :       comma = end_of_entry;
     239             : 
     240       23847 :     uint32_t low=0, high=0;
     241       23847 :     if (parse_version_range(s, comma, &low, &high) < 0) {
     242         312 :       goto error;
     243             :     }
     244             : 
     245       23535 :     out->bitmask |= bitmask_for_range(low,high);
     246             : 
     247       23535 :     s = comma;
     248             :     // Skip the comma separator between ranges. Don't ignore a trailing comma.
     249       23535 :     if (s < (end_of_entry - 1))
     250        1966 :       ++s;
     251             :   }
     252             : 
     253             :   return out;
     254             : 
     255         416 :  error:
     256         416 :   proto_entry_free(out);
     257         416 :   return NULL;
     258             : }
     259             : 
     260             : /**
     261             :  * Parse the protocol list from <b>s</b> and return it as a smartlist of
     262             :  * proto_entry_t
     263             :  */
     264             : STATIC smartlist_t *
     265        3998 : parse_protocol_list(const char *s)
     266             : {
     267        3998 :   smartlist_t *entries = smartlist_new();
     268             : 
     269       26034 :   while (*s) {
     270             :     /* Find the next space or the NUL. */
     271       22452 :     const char *end_of_entry = strchr(s, ' ');
     272       22452 :     proto_entry_t *entry;
     273       22452 :     if (!end_of_entry)
     274        3082 :       end_of_entry = s + strlen(s);
     275             : 
     276       22452 :     entry = parse_single_entry(s, end_of_entry);
     277             : 
     278       22452 :     if (! entry)
     279         416 :       goto error;
     280             : 
     281       22036 :     smartlist_add(entries, entry);
     282             : 
     283       22036 :     s = end_of_entry;
     284       42538 :     while (*s == ' ')
     285       20502 :       ++s;
     286             :   }
     287             : 
     288             :   return entries;
     289             : 
     290         416 :  error:
     291         719 :   SMARTLIST_FOREACH(entries, proto_entry_t *, ent, proto_entry_free(ent));
     292         416 :   smartlist_free(entries);
     293         416 :   return NULL;
     294             : }
     295             : 
     296             : /**
     297             :  * Return true if the unparsed protover list in <b>s</b> contains a
     298             :  * parsing error, such as extra commas, a bad number, or an over-long
     299             :  * name.
     300             :  */
     301             : bool
     302        1683 : protover_list_is_invalid(const char *s)
     303             : {
     304        1683 :   smartlist_t *list = parse_protocol_list(s);
     305        1683 :   if (!list)
     306             :     return true; /* yes, has a dangerous name */
     307        7065 :   SMARTLIST_FOREACH(list, proto_entry_t *, ent, proto_entry_free(ent));
     308        1510 :   smartlist_free(list);
     309        1510 :   return false; /* no, looks fine */
     310             : }
     311             : 
     312             : /**
     313             :  * Given a protocol type and version number, return true iff we know
     314             :  * how to speak that protocol.
     315             :  */
     316             : int
     317           0 : protover_is_supported_here(protocol_type_t pr, uint32_t ver)
     318             : {
     319           0 :   const smartlist_t *ours = get_supported_protocol_list();
     320           0 :   return protocol_list_contains(ours, pr, ver);
     321             : }
     322             : 
     323             : /**
     324             :  * Return true iff "list" encodes a protocol list that includes support for
     325             :  * the indicated protocol and version.
     326             :  *
     327             :  * If the protocol list is unparseable, treat it as if it defines no
     328             :  * protocols, and return 0.
     329             :  */
     330             : int
     331        2110 : protocol_list_supports_protocol(const char *list, protocol_type_t tp,
     332             :                                 uint32_t version)
     333             : {
     334             :   /* NOTE: This is a pretty inefficient implementation. If it ever shows
     335             :    * up in profiles, we should memoize it.
     336             :    */
     337        2110 :   smartlist_t *protocols = parse_protocol_list(list);
     338        2110 :   if (!protocols) {
     339             :     return 0;
     340             :   }
     341        1934 :   int contains = protocol_list_contains(protocols, tp, version);
     342             : 
     343       17638 :   SMARTLIST_FOREACH(protocols, proto_entry_t *, ent, proto_entry_free(ent));
     344        1934 :   smartlist_free(protocols);
     345        1934 :   return contains;
     346             : }
     347             : 
     348             : /**
     349             :  * Return true iff "list" encodes a protocol list that includes support for
     350             :  * the indicated protocol and version, or some later version.
     351             :  *
     352             :  * If the protocol list is unparseable, treat it as if it defines no
     353             :  * protocols, and return 0.
     354             :  */
     355             : int
     356         101 : protocol_list_supports_protocol_or_later(const char *list,
     357             :                                          protocol_type_t tp,
     358             :                                          uint32_t version)
     359             : {
     360             :   /* NOTE: This is a pretty inefficient implementation. If it ever shows
     361             :    * up in profiles, we should memoize it.
     362             :    */
     363         101 :   smartlist_t *protocols = parse_protocol_list(list);
     364         101 :   if (!protocols) {
     365             :     return 0;
     366             :   }
     367          85 :   const char *pr_name = protocol_type_to_str(tp);
     368             : 
     369          85 :   int contains = 0;
     370          85 :   const uint64_t mask = bitmask_for_range(version, 63);
     371             : 
     372         332 :   SMARTLIST_FOREACH_BEGIN(protocols, proto_entry_t *, proto) {
     373         269 :     if (strcasecmp(proto->name, pr_name))
     374         241 :       continue;
     375          28 :     if (0 != (proto->bitmask & mask)) {
     376          22 :       contains = 1;
     377          22 :       goto found;
     378             :     }
     379         247 :   } SMARTLIST_FOREACH_END(proto);
     380             : 
     381          63 :  found:
     382         385 :   SMARTLIST_FOREACH(protocols, proto_entry_t *, ent, proto_entry_free(ent));
     383          85 :   smartlist_free(protocols);
     384          85 :   return contains;
     385             : }
     386             : 
     387             : /** Return the canonical string containing the list of protocols
     388             :  * that we support. */
     389             : /// C_RUST_COUPLED: src/rust/protover/protover.rs `SUPPORTED_PROTOCOLS`
     390             : const char *
     391          20 : protover_get_supported_protocols(void)
     392             : {
     393             :   /* WARNING!
     394             :    *
     395             :    * Remember to edit the SUPPORTED_PROTOCOLS list in protover.rs if you
     396             :    * are editing this list.
     397             :    */
     398          20 :   return
     399          20 :     "Cons=1-2 "
     400             :     "Desc=1-2 "
     401             :     "DirCache=2 "
     402             :     "FlowCtrl=1 "
     403             :     "HSDir=1-2 "
     404             :     "HSIntro=3-5 "
     405             :     "HSRend=1-2 "
     406             :     "Link=1-5 "
     407             : #ifdef HAVE_WORKING_TOR_TLS_GET_TLSSECRETS
     408             :     "LinkAuth=1,3 "
     409             : #else
     410             :     "LinkAuth=3 "
     411             : #endif
     412             :     "Microdesc=1-2 "
     413             :     "Padding=2 "
     414             :     "Relay=1-3";
     415             : }
     416             : 
     417             : /** The protocols from protover_get_supported_protocols(), as parsed into a
     418             :  * list of proto_entry_t values. Access this via
     419             :  * get_supported_protocol_list. */
     420             : static smartlist_t *supported_protocol_list = NULL;
     421             : 
     422             : /** Return a pointer to a smartlist of proto_entry_t for the protocols
     423             :  * we support. */
     424             : static const smartlist_t *
     425          22 : get_supported_protocol_list(void)
     426             : {
     427          22 :   if (PREDICT_UNLIKELY(supported_protocol_list == NULL)) {
     428           6 :     supported_protocol_list =
     429           6 :       parse_protocol_list(protover_get_supported_protocols());
     430             :   }
     431          22 :   return supported_protocol_list;
     432             : }
     433             : 
     434             : /** Return the number of trailing zeros in x.  Undefined if x is 0. */
     435             : static int
     436         186 : trailing_zeros(uint64_t x)
     437             : {
     438             : #ifdef __GNUC__
     439         186 :   return __builtin_ctzll((unsigned long long)x);
     440             : #else
     441             :   int i;
     442             :   for (i = 0; i <= 64; ++i) {
     443             :     if (x&1)
     444             :       return i;
     445             :     x>>=1;
     446             :   }
     447             :   return i;
     448             : #endif /* defined(__GNUC__) */
     449             : }
     450             : 
     451             : /**
     452             :  * Given a protocol entry, encode it at the end of the smartlist <b>chunks</b>
     453             :  * as one or more newly allocated strings.
     454             :  */
     455             : static void
     456          82 : proto_entry_encode_into(smartlist_t *chunks, const proto_entry_t *entry)
     457             : {
     458          82 :   smartlist_add_asprintf(chunks, "%s=", entry->name);
     459             : 
     460          82 :   uint64_t mask = entry->bitmask;
     461          82 :   int shift = 0; // how much have we shifted by so far?
     462          82 :   bool first = true;
     463         175 :   while (mask) {
     464          93 :     const char *comma = first ? "" : ",";
     465          93 :     if (first) {
     466             :       first = false;
     467             :     }
     468          93 :     int zeros = trailing_zeros(mask);
     469          93 :     mask >>= zeros;
     470          93 :     shift += zeros;
     471          93 :     int ones = !mask ? 64 : trailing_zeros(~mask);
     472          93 :     if (ones == 1) {
     473          62 :       smartlist_add_asprintf(chunks, "%s%d", comma, shift);
     474             :     } else {
     475          31 :       smartlist_add_asprintf(chunks, "%s%d-%d", comma,
     476          31 :                              shift, shift + ones - 1);
     477             :     }
     478          93 :     if (ones == 64) {
     479             :       break; // avoid undefined behavior; can't shift by 64.
     480             :     }
     481          93 :     mask >>= ones;
     482          93 :     shift += ones;
     483             :   }
     484          82 : }
     485             : 
     486             : /** Given a list of space-separated proto_entry_t items,
     487             :  * encode it into a newly allocated space-separated string. */
     488             : STATIC char *
     489          68 : encode_protocol_list(const smartlist_t *sl)
     490             : {
     491          68 :   const char *separator = "";
     492          68 :   smartlist_t *chunks = smartlist_new();
     493         150 :   SMARTLIST_FOREACH_BEGIN(sl, const proto_entry_t *, ent) {
     494          82 :     smartlist_add_strdup(chunks, separator);
     495             : 
     496          82 :     proto_entry_encode_into(chunks, ent);
     497             : 
     498          82 :     separator = " ";
     499          82 :   } SMARTLIST_FOREACH_END(ent);
     500             : 
     501          68 :   char *result = smartlist_join_strings(chunks, "", 0, NULL);
     502             : 
     503         325 :   SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
     504          68 :   smartlist_free(chunks);
     505             : 
     506          68 :   return result;
     507             : }
     508             : 
     509             : /**
     510             :  * Protocol voting implementation.
     511             :  *
     512             :  * Given a list of strings describing protocol versions, return a newly
     513             :  * allocated string encoding all of the protocols that are listed by at
     514             :  * least <b>threshold</b> of the inputs.
     515             :  *
     516             :  * The string is minimal and sorted according to the rules of
     517             :  * contract_protocol_list above.
     518             :  */
     519             : char *
     520         157 : protover_compute_vote(const smartlist_t *list_of_proto_strings,
     521             :                       int threshold)
     522             : {
     523             :   // we use u8 counters below.
     524         157 :   tor_assert(smartlist_len(list_of_proto_strings) < 256);
     525             : 
     526         157 :   if (smartlist_len(list_of_proto_strings) == 0) {
     527          97 :     return tor_strdup("");
     528             :   }
     529             : 
     530          60 :   smartlist_t *parsed = smartlist_new(); // smartlist of smartlist of entries
     531          60 :   smartlist_t *proto_names = smartlist_new(); // smartlist of strings
     532          60 :   smartlist_t *result = smartlist_new(); // smartlist of entries
     533             : 
     534             :   // First, parse the inputs, and accumulate a list of protocol names.
     535         124 :   SMARTLIST_FOREACH_BEGIN(list_of_proto_strings, const char *, vote) {
     536          64 :     smartlist_t *unexpanded = parse_protocol_list(vote);
     537          64 :     if (! unexpanded) {
     538          40 :       log_warn(LD_NET, "I failed with parsing a protocol list from "
     539             :                "an authority. The offending string was: %s",
     540             :                escaped(vote));
     541          40 :       continue;
     542             :     }
     543         108 :     SMARTLIST_FOREACH_BEGIN(unexpanded, const proto_entry_t *, ent) {
     544          84 :       if (!smartlist_contains_string(proto_names,ent->name)) {
     545          77 :         smartlist_add(proto_names, ent->name);
     546             :       }
     547          84 :     } SMARTLIST_FOREACH_END(ent);
     548          24 :     smartlist_add(parsed, unexpanded);
     549          64 :   } SMARTLIST_FOREACH_END(vote);
     550             : 
     551             :   // Sort the list of names.
     552          60 :   smartlist_sort_strings(proto_names);
     553             : 
     554             :   // For each named protocol, compute the consensus.
     555             :   //
     556             :   // This is not super-efficient, but it's not critical path.
     557         137 :   SMARTLIST_FOREACH_BEGIN(proto_names, const char *, name) {
     558          77 :     uint8_t counts[64];
     559          77 :     memset(counts, 0, sizeof(counts));
     560             :     // Count how many votes we got for each bit.
     561         165 :     SMARTLIST_FOREACH_BEGIN(parsed, const smartlist_t *, vote) {
     562          88 :       const proto_entry_t *ent = find_entry_by_name(vote, name);
     563          88 :       if (! ent)
     564           5 :         continue;
     565             : 
     566        5395 :       for (int i = 0; i < 64; ++i) {
     567        5312 :         if ((ent->bitmask & BIT(i)) != 0) {
     568         464 :           ++ counts[i];
     569             :         }
     570             :       }
     571          88 :     } SMARTLIST_FOREACH_END(vote);
     572             : 
     573             :     uint64_t result_bitmask = 0;
     574        5005 :     for (int i = 0; i < 64; ++i) {
     575        4928 :       if (counts[i] >= threshold) {
     576         342 :         result_bitmask |= BIT(i);
     577             :       }
     578             :     }
     579          77 :     if (result_bitmask != 0) {
     580          70 :       proto_entry_t *newent = tor_malloc_zero(sizeof(proto_entry_t));
     581          70 :       newent->name = tor_strdup(name);
     582          70 :       newent->bitmask = result_bitmask;
     583          70 :       smartlist_add(result, newent);
     584             :     }
     585          77 :   } SMARTLIST_FOREACH_END(name);
     586             : 
     587          60 :   char *consensus = encode_protocol_list(result);
     588             : 
     589         130 :   SMARTLIST_FOREACH(result, proto_entry_t *, ent, proto_entry_free(ent));
     590          60 :   smartlist_free(result);
     591          60 :   smartlist_free(proto_names); // no need to free members; they are aliases.
     592          84 :   SMARTLIST_FOREACH_BEGIN(parsed, smartlist_t *, v) {
     593         108 :     SMARTLIST_FOREACH(v, proto_entry_t *, ent, proto_entry_free(ent));
     594          24 :     smartlist_free(v);
     595          24 :   } SMARTLIST_FOREACH_END(v);
     596          60 :   smartlist_free(parsed);
     597             : 
     598          60 :   return consensus;
     599             : }
     600             : 
     601             : /** Return true if every protocol version described in the string <b>s</b> is
     602             :  * one that we support, and false otherwise.  If <b>missing_out</b> is
     603             :  * provided, set it to the list of protocols we do not support.
     604             :  *
     605             :  * If the protocol version string is unparseable, treat it as if it defines no
     606             :  * protocols, and return 1.
     607             :  **/
     608             : int
     609          26 : protover_all_supported(const char *s, char **missing_out)
     610             : {
     611          26 :   if (!s) {
     612             :     return 1;
     613             :   }
     614             : 
     615          25 :   smartlist_t *entries = parse_protocol_list(s);
     616          25 :   if (BUG(entries == NULL)) {
     617           3 :     log_warn(LD_NET, "Received an unparseable protocol list %s"
     618             :              " from the consensus", escaped(s));
     619           3 :     return 1;
     620             :   }
     621          22 :   const smartlist_t *supported = get_supported_protocol_list();
     622          22 :   smartlist_t *missing = smartlist_new();
     623             : 
     624          36 :   SMARTLIST_FOREACH_BEGIN(entries, const proto_entry_t *, ent) {
     625          14 :     const proto_entry_t *mine = find_entry_by_name(supported, ent->name);
     626          14 :     if (mine == NULL) {
     627           5 :       if (ent->bitmask != 0) {
     628           4 :         proto_entry_t *m = tor_malloc_zero(sizeof(proto_entry_t));
     629           4 :         m->name = tor_strdup(ent->name);
     630           4 :         m->bitmask = ent->bitmask;
     631           4 :         smartlist_add(missing, m);
     632             :       }
     633           5 :       continue;
     634             :     }
     635             : 
     636           9 :     uint64_t missing_mask = ent->bitmask & ~mine->bitmask;
     637           9 :     if (missing_mask != 0) {
     638           5 :       proto_entry_t *m = tor_malloc_zero(sizeof(proto_entry_t));
     639           5 :       m->name = tor_strdup(ent->name);
     640           5 :       m->bitmask = missing_mask;
     641           5 :       smartlist_add(missing, m);
     642             :     }
     643          14 :   } SMARTLIST_FOREACH_END(ent);
     644             : 
     645          22 :   const int all_supported = (smartlist_len(missing) == 0);
     646          22 :   if (!all_supported && missing_out) {
     647           7 :     *missing_out = encode_protocol_list(missing);
     648             :   }
     649             : 
     650          31 :   SMARTLIST_FOREACH(missing, proto_entry_t *, ent, proto_entry_free(ent));
     651          22 :   smartlist_free(missing);
     652             : 
     653          36 :   SMARTLIST_FOREACH(entries, proto_entry_t *, ent, proto_entry_free(ent));
     654          22 :   smartlist_free(entries);
     655             : 
     656          22 :   return all_supported;
     657             : }
     658             : 
     659             : /** Helper: return the member of 'protos' whose name is
     660             :  * 'name', or NULL if there is no such member. */
     661             : static const proto_entry_t *
     662        1076 : find_entry_by_name(const smartlist_t *protos, const char *name)
     663             : {
     664        1076 :   if (!protos) {
     665             :     return NULL;
     666             :   }
     667        4557 :   SMARTLIST_FOREACH_BEGIN(protos, const proto_entry_t *, ent) {
     668        3873 :     if (!strcmp(ent->name, name)) {
     669         392 :       return ent;
     670             :     }
     671        3481 :   } SMARTLIST_FOREACH_END(ent);
     672             : 
     673             :   return NULL;
     674             : }
     675             : 
     676             : /** Helper: Given a list of proto_entry_t, return true iff
     677             :  * <b>pr</b>=<b>ver</b> is included in that list. */
     678             : static int
     679        1934 : protocol_list_contains(const smartlist_t *protos,
     680             :                        protocol_type_t pr, uint32_t ver)
     681             : {
     682        1934 :   if (BUG(protos == NULL)) {
     683             :     return 0; // LCOV_EXCL_LINE
     684             :   }
     685        1934 :   const char *pr_name = protocol_type_to_str(pr);
     686        1934 :   if (BUG(pr_name == NULL)) {
     687             :     return 0; // LCOV_EXCL_LINE
     688             :   }
     689        1934 :   if (ver > MAX_PROTOCOL_VERSION) {
     690             :     return 0;
     691             :   }
     692             : 
     693         974 :   const proto_entry_t *ent = find_entry_by_name(protos, pr_name);
     694         974 :   if (ent) {
     695         300 :     return (ent->bitmask & BIT(ver)) != 0;
     696             :   }
     697             :   return 0;
     698             : }
     699             : 
     700             : /** Return a string describing the protocols supported by tor version
     701             :  * <b>version</b>, or an empty string if we cannot tell.
     702             :  *
     703             :  * Note that this is only used to infer protocols for Tor versions that
     704             :  * can't declare their own.
     705             :  **/
     706             : /// C_RUST_COUPLED: src/rust/protover/protover.rs `compute_for_old_tor`
     707             : const char *
     708           0 : protover_compute_for_old_tor(const char *version)
     709             : {
     710           0 :   if (version == NULL) {
     711             :     /* No known version; guess the oldest series that is still supported. */
     712           0 :     version = "0.2.5.15";
     713             :   }
     714             : 
     715           0 :   if (tor_version_as_new_as(version,
     716             :                             FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS)) {
     717             :     return "";
     718           0 :   } else if (tor_version_as_new_as(version, "0.2.9.1-alpha")) {
     719             :     /* 0.2.9.1-alpha HSRend=2 */
     720             :     return "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1-2 "
     721             :       "Link=1-4 LinkAuth=1 "
     722             :       "Microdesc=1-2 Relay=1-2";
     723           0 :   } else if (tor_version_as_new_as(version, "0.2.7.5")) {
     724             :     /* 0.2.7-stable added Desc=2, Microdesc=2, Cons=2, which indicate
     725             :      * ed25519 support.  We'll call them present only in "stable" 027,
     726             :      * though. */
     727             :     return "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
     728             :       "Link=1-4 LinkAuth=1 "
     729             :       "Microdesc=1-2 Relay=1-2";
     730           0 :   } else if (tor_version_as_new_as(version, "0.2.4.19")) {
     731             :     /* No currently supported Tor server versions are older than this, or
     732             :      * lack these protocols. */
     733             :     return "Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
     734             :       "Link=1-4 LinkAuth=1 "
     735             :       "Microdesc=1 Relay=1-2";
     736             :   } else {
     737             :     /* Cannot infer protocols. */
     738           0 :     return "";
     739             :   }
     740             : }
     741             : 
     742             : /**
     743             :  * Release all storage held by static fields in protover.c
     744             :  */
     745             : void
     746         235 : protover_free_all(void)
     747             : {
     748         235 :   if (supported_protocol_list) {
     749           0 :     smartlist_t *entries = supported_protocol_list;
     750           0 :     SMARTLIST_FOREACH(entries, proto_entry_t *, ent, proto_entry_free(ent));
     751           0 :     smartlist_free(entries);
     752           0 :     supported_protocol_list = NULL;
     753             :   }
     754         235 : }
     755             : 
     756             : #endif /* !defined(HAVE_RUST) */

Generated by: LCOV version 1.14