tor  0.4.0.0-alpha-dev
protover.c
Go to the documentation of this file.
1 /* Copyright (c) 2016-2018, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
3 
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 
38 static const struct {
40  protocol_type_t protover_type;
41  const char *name;
42 /* If you add a new protocol here, you probably also want to add
43  * parsing for it in routerstatus_parse_entry_from_string() so that
44  * it is set in routerstatus_t */
45 } PROTOCOL_NAMES[] = {
46  { PRT_LINK, "Link" },
47  { PRT_LINKAUTH, "LinkAuth" },
48  { PRT_RELAY, "Relay" },
49  { PRT_DIRCACHE, "DirCache" },
50  { PRT_HSDIR, "HSDir" },
51  { PRT_HSINTRO, "HSIntro" },
52  { PRT_HSREND, "HSRend" },
53  { PRT_DESC, "Desc" },
54  { PRT_MICRODESC, "Microdesc"},
55  { PRT_PADDING, "Padding"},
56  { PRT_CONS, "Cons" }
57 };
58 
59 #define N_PROTOCOL_NAMES ARRAY_LENGTH(PROTOCOL_NAMES)
60 
61 /* Maximum allowed length of any single subprotocol name. */
62 // C_RUST_COUPLED: src/rust/protover/protover.rs
63 // `MAX_PROTOCOL_NAME_LENGTH`
64 static const unsigned MAX_PROTOCOL_NAME_LENGTH = 100;
65 
70 STATIC const char *
72 {
73  unsigned i;
74  for (i=0; i < N_PROTOCOL_NAMES; ++i) {
75  if (PROTOCOL_NAMES[i].protover_type == pr)
76  return PROTOCOL_NAMES[i].name;
77  }
78  /* LCOV_EXCL_START */
79  tor_assert_nonfatal_unreached_once();
80  return "UNKNOWN";
81  /* LCOV_EXCL_STOP */
82 }
83 
88 STATIC int
89 str_to_protocol_type(const char *s, protocol_type_t *pr_out)
90 {
91  if (BUG(!pr_out))
92  return -1;
93 
94  unsigned i;
95  for (i=0; i < N_PROTOCOL_NAMES; ++i) {
96  if (0 == strcmp(s, PROTOCOL_NAMES[i].name)) {
97  *pr_out = PROTOCOL_NAMES[i].protover_type;
98  return 0;
99  }
100  }
101 
102  return -1;
103 }
104 
108 STATIC void
109 proto_entry_free_(proto_entry_t *entry)
110 {
111  if (!entry)
112  return;
113  tor_free(entry->name);
114  SMARTLIST_FOREACH(entry->ranges, proto_range_t *, r, tor_free(r));
115  smartlist_free(entry->ranges);
116  tor_free(entry);
117 }
118 
120 #define MAX_PROTOCOL_VERSION (UINT32_MAX-1)
121 
128 static int
129 parse_version_range(const char *s, const char *end_of_range,
130  uint32_t *low_out, uint32_t *high_out)
131 {
132  uint32_t low, high;
133  char *next = NULL;
134  int ok;
135 
136  tor_assert(high_out);
137  tor_assert(low_out);
138 
139  if (BUG(!end_of_range))
140  end_of_range = s + strlen(s); // LCOV_EXCL_LINE
141 
142  /* A range must start with a digit. */
143  if (!TOR_ISDIGIT(*s)) {
144  goto error;
145  }
146 
147  /* Note that this wouldn't be safe if we didn't know that eventually,
148  * we'd hit a NUL */
149  low = (uint32_t) tor_parse_ulong(s, 10, 0, MAX_PROTOCOL_VERSION, &ok, &next);
150  if (!ok)
151  goto error;
152  if (next > end_of_range)
153  goto error;
154  if (next == end_of_range) {
155  high = low;
156  goto done;
157  }
158 
159  if (*next != '-')
160  goto error;
161  s = next+1;
162 
163  /* ibid */
164  if (!TOR_ISDIGIT(*s)) {
165  goto error;
166  }
167  high = (uint32_t) tor_parse_ulong(s, 10, 0,
168  MAX_PROTOCOL_VERSION, &ok, &next);
169  if (!ok)
170  goto error;
171  if (next != end_of_range)
172  goto error;
173 
174  if (low > high)
175  goto error;
176 
177  done:
178  *high_out = high;
179  *low_out = low;
180  return 0;
181 
182  error:
183  return -1;
184 }
185 
186 static int
187 is_valid_keyword(const char *s, size_t n)
188 {
189  for (size_t i = 0; i < n; i++) {
190  if (!TOR_ISALNUM(s[i]) && s[i] != '-')
191  return 0;
192  }
193  return 1;
194 }
195 
201 static proto_entry_t *
202 parse_single_entry(const char *s, const char *end_of_entry)
203 {
204  proto_entry_t *out = tor_malloc_zero(sizeof(proto_entry_t));
205  const char *equals;
206 
207  out->ranges = smartlist_new();
208 
209  if (BUG (!end_of_entry))
210  end_of_entry = s + strlen(s); // LCOV_EXCL_LINE
211 
212  /* There must be an =. */
213  equals = memchr(s, '=', end_of_entry - s);
214  if (!equals)
215  goto error;
216 
217  /* The name must be nonempty */
218  if (equals == s)
219  goto error;
220 
221  /* The name must not be longer than MAX_PROTOCOL_NAME_LENGTH. */
222  if (equals - s > (int)MAX_PROTOCOL_NAME_LENGTH) {
223  log_warn(LD_NET, "When parsing a protocol entry, I got a very large "
224  "protocol name. This is possibly an attack or a bug, unless "
225  "the Tor network truly supports protocol names larger than "
226  "%ud characters. The offending string was: %s",
227  MAX_PROTOCOL_NAME_LENGTH, escaped(out->name));
228  goto error;
229  }
230 
231  /* The name must contain only alphanumeric characters and hyphens. */
232  if (!is_valid_keyword(s, equals-s))
233  goto error;
234 
235  out->name = tor_strndup(s, equals-s);
236 
237  tor_assert(equals < end_of_entry);
238 
239  s = equals + 1;
240  while (s < end_of_entry) {
241  const char *comma = memchr(s, ',', end_of_entry-s);
242  proto_range_t *range = tor_malloc_zero(sizeof(proto_range_t));
243  if (! comma)
244  comma = end_of_entry;
245 
246  smartlist_add(out->ranges, range);
247  if (parse_version_range(s, comma, &range->low, &range->high) < 0) {
248  goto error;
249  }
250 
251  s = comma;
252  while (*s == ',' && s < end_of_entry)
253  ++s;
254  }
255 
256  return out;
257 
258  error:
259  proto_entry_free(out);
260  return NULL;
261 }
262 
267 STATIC smartlist_t *
268 parse_protocol_list(const char *s)
269 {
270  smartlist_t *entries = smartlist_new();
271 
272  while (*s) {
273  /* Find the next space or the NUL. */
274  const char *end_of_entry = strchr(s, ' ');
275  proto_entry_t *entry;
276  if (!end_of_entry)
277  end_of_entry = s + strlen(s);
278 
279  entry = parse_single_entry(s, end_of_entry);
280 
281  if (! entry)
282  goto error;
283 
284  smartlist_add(entries, entry);
285 
286  s = end_of_entry;
287  while (*s == ' ')
288  ++s;
289  }
290 
291  return entries;
292 
293  error:
294  SMARTLIST_FOREACH(entries, proto_entry_t *, ent, proto_entry_free(ent));
295  smartlist_free(entries);
296  return NULL;
297 }
298 
303 bool
305 {
306  smartlist_t *list = parse_protocol_list(s);
307  if (!list)
308  return true; /* yes, has a dangerous name */
309  SMARTLIST_FOREACH(list, proto_entry_t *, ent, proto_entry_free(ent));
310  smartlist_free(list);
311  return false; /* no, looks fine */
312 }
313 
318 int
320 {
322  return protocol_list_contains(ours, pr, ver);
323 }
324 
329 int
331  uint32_t version)
332 {
333  /* NOTE: This is a pretty inefficient implementation. If it ever shows
334  * up in profiles, we should memoize it.
335  */
336  smartlist_t *protocols = parse_protocol_list(list);
337  if (!protocols) {
338  return 0;
339  }
340  int contains = protocol_list_contains(protocols, tp, version);
341 
342  SMARTLIST_FOREACH(protocols, proto_entry_t *, ent, proto_entry_free(ent));
343  smartlist_free(protocols);
344  return contains;
345 }
346 
351 int
353  protocol_type_t tp,
354  uint32_t version)
355 {
356  /* NOTE: This is a pretty inefficient implementation. If it ever shows
357  * up in profiles, we should memoize it.
358  */
359  smartlist_t *protocols = parse_protocol_list(list);
360  if (!protocols) {
361  return 0;
362  }
363  const char *pr_name = protocol_type_to_str(tp);
364 
365  int contains = 0;
366  SMARTLIST_FOREACH_BEGIN(protocols, proto_entry_t *, proto) {
367  if (strcasecmp(proto->name, pr_name))
368  continue;
369  SMARTLIST_FOREACH_BEGIN(proto->ranges, const proto_range_t *, range) {
370  if (range->high >= version) {
371  contains = 1;
372  goto found;
373  }
374  } SMARTLIST_FOREACH_END(range);
375  } SMARTLIST_FOREACH_END(proto);
376 
377  found:
378  SMARTLIST_FOREACH(protocols, proto_entry_t *, ent, proto_entry_free(ent));
379  smartlist_free(protocols);
380  return contains;
381 }
382 
385 const char *
388 {
389  return
390  "Cons=1-2 "
391  "Desc=1-2 "
392  "DirCache=1-2 "
393  "HSDir=1-2 "
394  "HSIntro=3-4 "
395  "HSRend=1-2 "
396  "Link=1-5 "
397 #ifdef HAVE_WORKING_TOR_TLS_GET_TLSSECRETS
398  "LinkAuth=1,3 "
399 #else
400  "LinkAuth=3 "
401 #endif
402  "Microdesc=1-2 "
403  "Relay=1-2 "
404  "Padding=1";
405 }
406 
411 
414 static const smartlist_t *
416 {
417  if (PREDICT_UNLIKELY(supported_protocol_list == NULL)) {
420  }
422 }
423 
428 static void
429 proto_entry_encode_into(smartlist_t *chunks, const proto_entry_t *entry)
430 {
431  smartlist_add_asprintf(chunks, "%s=", entry->name);
432 
433  SMARTLIST_FOREACH_BEGIN(entry->ranges, proto_range_t *, range) {
434  const char *comma = "";
435  if (range_sl_idx != 0)
436  comma = ",";
437 
438  if (range->low == range->high) {
439  smartlist_add_asprintf(chunks, "%s%lu",
440  comma, (unsigned long)range->low);
441  } else {
442  smartlist_add_asprintf(chunks, "%s%lu-%lu",
443  comma, (unsigned long)range->low,
444  (unsigned long)range->high);
445  }
446  } SMARTLIST_FOREACH_END(range);
447 }
448 
451 STATIC char *
453 {
454  const char *separator = "";
455  smartlist_t *chunks = smartlist_new();
456  SMARTLIST_FOREACH_BEGIN(sl, const proto_entry_t *, ent) {
457  smartlist_add_strdup(chunks, separator);
458 
459  proto_entry_encode_into(chunks, ent);
460 
461  separator = " ";
462  } SMARTLIST_FOREACH_END(ent);
463 
464  char *result = smartlist_join_strings(chunks, "", 0, NULL);
465 
466  SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
467  smartlist_free(chunks);
468 
469  return result;
470 }
471 
472 /* We treat any protocol list with more than this many subprotocols in it
473  * as a DoS attempt. */
476 static const int MAX_PROTOCOLS_TO_EXPAND = (1<<16);
477 
487 static smartlist_t *
489 {
490  smartlist_t *expanded = smartlist_new();
491  if (!protos)
492  return expanded;
493 
494  SMARTLIST_FOREACH_BEGIN(protos, const proto_entry_t *, ent) {
495  const char *name = ent->name;
496  if (strlen(name) > MAX_PROTOCOL_NAME_LENGTH) {
497  log_warn(LD_NET, "When expanding a protocol entry, I got a very large "
498  "protocol name. This is possibly an attack or a bug, unless "
499  "the Tor network truly supports protocol names larger than "
500  "%ud characters. The offending string was: %s",
501  MAX_PROTOCOL_NAME_LENGTH, escaped(name));
502  continue;
503  }
504  SMARTLIST_FOREACH_BEGIN(ent->ranges, const proto_range_t *, range) {
505  uint32_t u;
506  for (u = range->low; u <= range->high; ++u) {
507  smartlist_add_asprintf(expanded, "%s=%lu", name, (unsigned long)u);
508  if (smartlist_len(expanded) > MAX_PROTOCOLS_TO_EXPAND)
509  goto too_many;
510  }
511  } SMARTLIST_FOREACH_END(range);
512  } SMARTLIST_FOREACH_END(ent);
513 
514  smartlist_sort_strings(expanded);
515  smartlist_uniq_strings(expanded); // This makes voting work. do not remove
516  return expanded;
517 
518  too_many:
519  SMARTLIST_FOREACH(expanded, char *, cp, tor_free(cp));
520  smartlist_free(expanded);
521  return NULL;
522 }
523 
527 static int
528 cmp_single_ent_by_version(const void **a_, const void **b_)
529 {
530  const proto_entry_t *ent_a = *a_;
531  const proto_entry_t *ent_b = *b_;
532 
533  tor_assert(smartlist_len(ent_a->ranges) == 1);
534  tor_assert(smartlist_len(ent_b->ranges) == 1);
535 
536  const proto_range_t *a = smartlist_get(ent_a->ranges, 0);
537  const proto_range_t *b = smartlist_get(ent_b->ranges, 0);
538 
539  tor_assert(a->low == a->high);
540  tor_assert(b->low == b->high);
541 
542  if (a->low < b->low) {
543  return -1;
544  } else if (a->low == b->low) {
545  return 0;
546  } else {
547  return 1;
548  }
549 }
550 
557 static char *
558 contract_protocol_list(const smartlist_t *proto_strings)
559 {
560  if (smartlist_len(proto_strings) == 0) {
561  return tor_strdup("");
562  }
563 
564  // map from name to list of single-version entries
565  strmap_t *entry_lists_by_name = strmap_new();
566  // list of protocol names
567  smartlist_t *all_names = smartlist_new();
568  // list of strings for the output we're building
569  smartlist_t *chunks = smartlist_new();
570 
571  // Parse each item and stick it entry_lists_by_name. Build
572  // 'all_names' at the same time.
573  SMARTLIST_FOREACH_BEGIN(proto_strings, const char *, s) {
574  if (BUG(!s))
575  continue;// LCOV_EXCL_LINE
576  proto_entry_t *ent = parse_single_entry(s, s+strlen(s));
577  if (BUG(!ent))
578  continue; // LCOV_EXCL_LINE
579  smartlist_t *lst = strmap_get(entry_lists_by_name, ent->name);
580  if (!lst) {
581  smartlist_add(all_names, ent->name);
582  lst = smartlist_new();
583  strmap_set(entry_lists_by_name, ent->name, lst);
584  }
585  smartlist_add(lst, ent);
586  } SMARTLIST_FOREACH_END(s);
587 
588  // We want to output the protocols sorted by their name.
589  smartlist_sort_strings(all_names);
590 
591  SMARTLIST_FOREACH_BEGIN(all_names, const char *, name) {
592  const int first_entry = (name_sl_idx == 0);
593  smartlist_t *lst = strmap_get(entry_lists_by_name, name);
594  tor_assert(lst);
595  // Sort every entry with this name by version. They are
596  // singletons, so there can't be overlap.
598 
599  if (! first_entry)
600  smartlist_add_strdup(chunks, " ");
601 
602  /* We're going to construct this entry from the ranges. */
603  proto_entry_t *entry = tor_malloc_zero(sizeof(proto_entry_t));
604  entry->ranges = smartlist_new();
605  entry->name = tor_strdup(name);
606 
607  // Now, find all the ranges of versions start..end where
608  // all of start, start+1, start+2, ..end are included.
609  int start_of_cur_series = 0;
610  while (start_of_cur_series < smartlist_len(lst)) {
611  const proto_entry_t *ent = smartlist_get(lst, start_of_cur_series);
612  const proto_range_t *range = smartlist_get(ent->ranges, 0);
613  const uint32_t ver_low = range->low;
614  uint32_t ver_high = ver_low;
615 
616  int idx;
617  for (idx = start_of_cur_series+1; idx < smartlist_len(lst); ++idx) {
618  ent = smartlist_get(lst, idx);
619  range = smartlist_get(ent->ranges, 0);
620  if (range->low != ver_high + 1)
621  break;
622  ver_high += 1;
623  }
624 
625  // Now idx is either off the end of the list, or the first sequence
626  // break in the list.
627  start_of_cur_series = idx;
628 
629  proto_range_t *new_range = tor_malloc_zero(sizeof(proto_range_t));
630  new_range->low = ver_low;
631  new_range->high = ver_high;
632  smartlist_add(entry->ranges, new_range);
633  }
634  proto_entry_encode_into(chunks, entry);
635  proto_entry_free(entry);
636 
637  } SMARTLIST_FOREACH_END(name);
638 
639  // Build the result...
640  char *result = smartlist_join_strings(chunks, "", 0, NULL);
641 
642  // And free all the stuff we allocated.
643  SMARTLIST_FOREACH_BEGIN(all_names, const char *, name) {
644  smartlist_t *lst = strmap_get(entry_lists_by_name, name);
645  tor_assert(lst);
646  SMARTLIST_FOREACH(lst, proto_entry_t *, e, proto_entry_free(e));
647  smartlist_free(lst);
648  } SMARTLIST_FOREACH_END(name);
649 
650  strmap_free(entry_lists_by_name, NULL);
651  smartlist_free(all_names);
652  SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
653  smartlist_free(chunks);
654 
655  return result;
656 }
657 
668 char *
669 protover_compute_vote(const smartlist_t *list_of_proto_strings,
670  int threshold)
671 {
672  if (smartlist_len(list_of_proto_strings) == 0) {
673  return tor_strdup("");
674  }
675 
676  smartlist_t *all_entries = smartlist_new();
677 
678  // First, parse the inputs and break them into singleton entries.
679  SMARTLIST_FOREACH_BEGIN(list_of_proto_strings, const char *, vote) {
680  smartlist_t *unexpanded = parse_protocol_list(vote);
681  if (! unexpanded) {
682  log_warn(LD_NET, "I failed with parsing a protocol list from "
683  "an authority. The offending string was: %s",
684  escaped(vote));
685  continue;
686  }
687  smartlist_t *this_vote = expand_protocol_list(unexpanded);
688  if (this_vote == NULL) {
689  log_warn(LD_NET, "When expanding a protocol list from an authority, I "
690  "got too many protocols. This is possibly an attack or a bug, "
691  "unless the Tor network truly has expanded to support over %d "
692  "different subprotocol versions. The offending string was: %s",
694  } else {
695  smartlist_add_all(all_entries, this_vote);
696  smartlist_free(this_vote);
697  }
698  SMARTLIST_FOREACH(unexpanded, proto_entry_t *, e, proto_entry_free(e));
699  smartlist_free(unexpanded);
700  } SMARTLIST_FOREACH_END(vote);
701 
702  if (smartlist_len(all_entries) == 0) {
703  smartlist_free(all_entries);
704  return tor_strdup("");
705  }
706 
707  // Now sort the singleton entries
708  smartlist_sort_strings(all_entries);
709 
710  // Now find all the strings that appear at least 'threshold' times.
711  smartlist_t *include_entries = smartlist_new();
712  const char *cur_entry = smartlist_get(all_entries, 0);
713  int n_times = 0;
714  SMARTLIST_FOREACH_BEGIN(all_entries, const char *, ent) {
715  if (!strcmp(ent, cur_entry)) {
716  n_times++;
717  } else {
718  if (n_times >= threshold && cur_entry)
719  smartlist_add(include_entries, (void*)cur_entry);
720  cur_entry = ent;
721  n_times = 1 ;
722  }
723  } SMARTLIST_FOREACH_END(ent);
724 
725  if (n_times >= threshold && cur_entry)
726  smartlist_add(include_entries, (void*)cur_entry);
727 
728  // Finally, compress that list.
729  char *result = contract_protocol_list(include_entries);
730  smartlist_free(include_entries);
731  SMARTLIST_FOREACH(all_entries, char *, cp, tor_free(cp));
732  smartlist_free(all_entries);
733 
734  return result;
735 }
736 
745 int
746 protover_all_supported(const char *s, char **missing_out)
747 {
748  int all_supported = 1;
749  smartlist_t *missing_some;
750  smartlist_t *missing_completely;
751  smartlist_t *missing_all;
752 
753  if (!s) {
754  return 1;
755  }
756 
757  smartlist_t *entries = parse_protocol_list(s);
758  if (BUG(entries == NULL)) {
759  log_warn(LD_NET, "Received an unparseable protocol list %s"
760  " from the consensus", escaped(s));
761  return 1;
762  }
763 
764  missing_some = smartlist_new();
765  missing_completely = smartlist_new();
766 
767  SMARTLIST_FOREACH_BEGIN(entries, const proto_entry_t *, ent) {
768  protocol_type_t tp;
769  if (str_to_protocol_type(ent->name, &tp) < 0) {
770  if (smartlist_len(ent->ranges)) {
771  goto unsupported;
772  }
773  continue;
774  }
775 
776  SMARTLIST_FOREACH_BEGIN(ent->ranges, const proto_range_t *, range) {
777  proto_entry_t *unsupported = tor_malloc_zero(sizeof(proto_entry_t));
778  proto_range_t *versions = tor_malloc_zero(sizeof(proto_range_t));
779  uint32_t i;
780 
781  unsupported->name = tor_strdup(ent->name);
782  unsupported->ranges = smartlist_new();
783 
784  for (i = range->low; i <= range->high; ++i) {
785  if (!protover_is_supported_here(tp, i)) {
786  if (versions->low == 0 && versions->high == 0) {
787  versions->low = i;
788  /* Pre-emptively add the high now, just in case we're in a single
789  * version range (e.g. "Link=999"). */
790  versions->high = i;
791  }
792  /* If the last one to be unsupported is one less than the current
793  * one, we're in a continuous range, so set the high field. */
794  if ((versions->high && versions->high == i - 1) ||
795  /* Similarly, if the last high wasn't set and we're currently
796  * one higher than the low, add current index as the highest
797  * known high. */
798  (!versions->high && versions->low == i - 1)) {
799  versions->high = i;
800  continue;
801  }
802  } else {
803  /* If we hit a supported version, and we previously had a range,
804  * we've hit a non-continuity. Copy the previous range and add it to
805  * the unsupported->ranges list and zero-out the previous range for
806  * the next iteration. */
807  if (versions->low != 0 && versions->high != 0) {
808  proto_range_t *versions_to_add = tor_malloc(sizeof(proto_range_t));
809 
810  versions_to_add->low = versions->low;
811  versions_to_add->high = versions->high;
812  smartlist_add(unsupported->ranges, versions_to_add);
813 
814  versions->low = 0;
815  versions->high = 0;
816  }
817  }
818  }
819  /* Once we've run out of versions to check, see if we had any unsupported
820  * ones and, if so, add them to unsupported->ranges. */
821  if (versions->low != 0 && versions->high != 0) {
822  smartlist_add(unsupported->ranges, versions);
823  }
824  /* Finally, if we had something unsupported, add it to the list of
825  * missing_some things and mark that there was something missing. */
826  if (smartlist_len(unsupported->ranges) != 0) {
827  smartlist_add(missing_some, (void*) unsupported);
828  all_supported = 0;
829  } else {
830  proto_entry_free(unsupported);
831  tor_free(versions);
832  }
833  } SMARTLIST_FOREACH_END(range);
834 
835  continue;
836 
837  unsupported:
838  all_supported = 0;
839  smartlist_add(missing_completely, (void*) ent);
840  } SMARTLIST_FOREACH_END(ent);
841 
842  /* We keep the two smartlists separate so that we can free the proto_entry_t
843  * we created and put in missing_some, so here we add them together to build
844  * the string. */
845  missing_all = smartlist_new();
846  smartlist_add_all(missing_all, missing_some);
847  smartlist_add_all(missing_all, missing_completely);
848 
849  if (missing_out && !all_supported) {
850  tor_assert(smartlist_len(missing_all) != 0);
851  *missing_out = encode_protocol_list(missing_all);
852  }
853  SMARTLIST_FOREACH(missing_some, proto_entry_t *, ent, proto_entry_free(ent));
854  smartlist_free(missing_some);
855  smartlist_free(missing_completely);
856  smartlist_free(missing_all);
857 
858  SMARTLIST_FOREACH(entries, proto_entry_t *, ent, proto_entry_free(ent));
859  smartlist_free(entries);
860 
861  return all_supported;
862 }
863 
866 static int
868  protocol_type_t pr, uint32_t ver)
869 {
870  if (BUG(protos == NULL)) {
871  return 0; // LCOV_EXCL_LINE
872  }
873  const char *pr_name = protocol_type_to_str(pr);
874  if (BUG(pr_name == NULL)) {
875  return 0; // LCOV_EXCL_LINE
876  }
877 
878  SMARTLIST_FOREACH_BEGIN(protos, const proto_entry_t *, ent) {
879  if (strcasecmp(ent->name, pr_name))
880  continue;
881  /* name matches; check the ranges */
882  SMARTLIST_FOREACH_BEGIN(ent->ranges, const proto_range_t *, range) {
883  if (ver >= range->low && ver <= range->high)
884  return 1;
885  } SMARTLIST_FOREACH_END(range);
886  } SMARTLIST_FOREACH_END(ent);
887 
888  return 0;
889 }
890 
897 const char *
899 protover_compute_for_old_tor(const char *version)
900 {
901  if (version == NULL) {
902  /* No known version; guess the oldest series that is still supported. */
903  version = "0.2.5.15";
904  }
905 
906  if (tor_version_as_new_as(version,
908  return "";
909  } else if (tor_version_as_new_as(version, "0.2.9.1-alpha")) {
910  /* 0.2.9.1-alpha HSRend=2 */
911  return "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1-2 "
912  "Link=1-4 LinkAuth=1 "
913  "Microdesc=1-2 Relay=1-2";
914  } else if (tor_version_as_new_as(version, "0.2.7.5")) {
915  /* 0.2.7-stable added Desc=2, Microdesc=2, Cons=2, which indicate
916  * ed25519 support. We'll call them present only in "stable" 027,
917  * though. */
918  return "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
919  "Link=1-4 LinkAuth=1 "
920  "Microdesc=1-2 Relay=1-2";
921  } else if (tor_version_as_new_as(version, "0.2.4.19")) {
922  /* No currently supported Tor server versions are older than this, or
923  * lack these protocols. */
924  return "Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
925  "Link=1-4 LinkAuth=1 "
926  "Microdesc=1 Relay=1-2";
927  } else {
928  /* Cannot infer protocols. */
929  return "";
930  }
931 }
932 
936 void
938 {
941  SMARTLIST_FOREACH(entries, proto_entry_t *, ent, proto_entry_free(ent));
942  smartlist_free(entries);
944  }
945 }
946 
947 #endif /* !defined(HAVE_RUST) */
#define FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS
Definition: protover.h:23
void protover_free_all(void)
Definition: protover.c:937
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
STATIC smartlist_t * parse_protocol_list(const char *s)
Definition: protover.c:268
void smartlist_add_strdup(struct smartlist_t *sl, const char *string)
static int cmp_single_ent_by_version(const void **a_, const void **b_)
Definition: protover.c:528
static int protocol_list_contains(const smartlist_t *protos, protocol_type_t pr, uint32_t ver)
Definition: protover.c:867
unsigned long tor_parse_ulong(const char *s, int base, unsigned long min, unsigned long max, int *ok, char **next)
Definition: parse_int.c:75
int tor_version_as_new_as(const char *platform, const char *cutoff)
Definition: versions.c:171
void smartlist_add(smartlist_t *sl, void *element)
void smartlist_uniq_strings(smartlist_t *sl)
Definition: smartlist.c:574
#define tor_free(p)
Definition: malloc.h:52
void smartlist_sort_strings(smartlist_t *sl)
Definition: smartlist.c:549
static proto_entry_t * parse_single_entry(const char *s, const char *end_of_entry)
Definition: protover.c:202
Header file for versions.c.
tor_assert(buffer)
protocol_type_t
Definition: protover.h:35
static const smartlist_t * get_supported_protocol_list(void)
Definition: protover.c:415
void smartlist_add_asprintf(struct smartlist_t *sl, const char *pattern,...)
Definition: smartlist.c:36
static smartlist_t * expand_protocol_list(const smartlist_t *protos)
Definition: protover.c:488
Master header file for Tor-specific functionality.
static smartlist_t * supported_protocol_list
Definition: protover.c:410
STATIC int str_to_protocol_type(const char *s, protocol_type_t *pr_out)
Definition: protover.c:89
int protocol_list_supports_protocol(const char *list, protocol_type_t tp, uint32_t version)
Definition: protover.c:330
STATIC void proto_entry_free_(proto_entry_t *entry)
Definition: protover.c:109
#define MAX_PROTOCOL_VERSION
Definition: protover.c:120
Headers and type declarations for protover.c.
bool protover_contains_long_protocol_names(const char *s)
Definition: protover.c:304
char * smartlist_join_strings(smartlist_t *sl, const char *join, int terminate, size_t *len_out)
Definition: smartlist.c:279
static const int MAX_PROTOCOLS_TO_EXPAND
Definition: protover.c:476
#define SMARTLIST_FOREACH(sl, type, var, cmd)
const char * escaped(const char *s)
Definition: escape.c:126
static char * contract_protocol_list(const smartlist_t *proto_strings)
Definition: protover.c:558
void smartlist_add_all(smartlist_t *s1, const smartlist_t *s2)
Headers for tortls.c.
int protover_all_supported(const char *s, char **missing_out)
Definition: protover.c:746
STATIC const char * protocol_type_to_str(protocol_type_t pr)
Definition: protover.c:71
const char * protover_compute_for_old_tor(const char *version)
C_RUST_COUPLED: src/rust/protover/protover.rs compute_for_old_tor
Definition: protover.c:899
static void proto_entry_encode_into(smartlist_t *chunks, const proto_entry_t *entry)
Definition: protover.c:429
int protocol_list_supports_protocol_or_later(const char *list, protocol_type_t tp, uint32_t version)
Definition: protover.c:352
static int parse_version_range(const char *s, const char *end_of_range, uint32_t *low_out, uint32_t *high_out)
Definition: protover.c:129
#define LD_NET
Definition: log.h:62
int protover_is_supported_here(protocol_type_t pr, uint32_t ver)
Definition: protover.c:319
void smartlist_sort(smartlist_t *sl, int(*compare)(const void **a, const void **b))
Definition: smartlist.c:334
char * protover_compute_vote(const smartlist_t *list_of_proto_strings, int threshold)
Definition: protover.c:669
static const struct @14 PROTOCOL_NAMES[]
C_RUST_COUPLED: src/rust/protover/protover.rs PROTOCOL_NAMES
const char * protover_get_supported_protocols(void)
C_RUST_COUPLED: src/rust/protover/protover.rs SUPPORTED_PROTOCOLS
Definition: protover.c:387
STATIC char * encode_protocol_list(const smartlist_t *sl)
Definition: protover.c:452