diff options
author | Nick Mathewson <nickm@torproject.org> | 2018-10-12 11:39:37 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2018-10-12 11:39:37 -0400 |
commit | 67351f672450d5f13754294405243a59ddd86de9 (patch) | |
tree | 016368ae0c3b6e56c0dc03c185fe5a095d72f22e /src/core | |
parent | 391756f262c93d4361fb6189a3ba9704283f73aa (diff) | |
parent | a1504f138d978d73b2c6129957dd0ee344a97efa (diff) | |
download | tor-67351f672450d5f13754294405243a59ddd86de9.tar.gz tor-67351f672450d5f13754294405243a59ddd86de9.zip |
Merge remote-tracking branch 'tor-github/pr/380'
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/include.am | 29 | ||||
-rw-r--r-- | src/core/mainloop/connection.c | 3 | ||||
-rw-r--r-- | src/core/or/circuitbuild.c | 1 | ||||
-rw-r--r-- | src/core/or/policies.c | 4 | ||||
-rw-r--r-- | src/core/or/protover.c | 2 | ||||
-rw-r--r-- | src/core/or/relay.c | 1 | ||||
-rw-r--r-- | src/core/or/versions.c | 422 | ||||
-rw-r--r-- | src/core/or/versions.h | 44 |
8 files changed, 495 insertions, 11 deletions
diff --git a/src/core/include.am b/src/core/include.am index e51819fa20..1b8ef2ac58 100644 --- a/src/core/include.am +++ b/src/core/include.am @@ -46,6 +46,7 @@ LIBTOR_APP_A_SOURCES = \ src/core/or/scheduler_kist.c \ src/core/or/scheduler_vanilla.c \ src/core/or/status.c \ + src/core/or/versions.c \ src/core/proto/proto_cell.c \ src/core/proto/proto_control0.c \ src/core/proto/proto_ext_or.c \ @@ -72,6 +73,15 @@ LIBTOR_APP_A_SOURCES = \ src/feature/dircommon/directory.c \ src/feature/dircommon/fp_pair.c \ src/feature/dircommon/voting_schedule.c \ + src/feature/dirparse/authcert_parse.c \ + src/feature/dirparse/microdesc_parse.c \ + src/feature/dirparse/ns_parse.c \ + src/feature/dirparse/parsecommon.c \ + src/feature/dirparse/policy_parse.c \ + src/feature/dirparse/routerparse.c \ + src/feature/dirparse/sigcommon.c \ + src/feature/dirparse/signing.c \ + src/feature/dirparse/unparseable.c \ src/feature/hibernate/hibernate.c \ src/feature/hs/hs_cache.c \ src/feature/hs/hs_cell.c \ @@ -98,10 +108,8 @@ LIBTOR_APP_A_SOURCES = \ src/feature/nodelist/nickname.c \ src/feature/nodelist/nodelist.c \ src/feature/nodelist/node_select.c \ - src/feature/nodelist/parsecommon.c \ src/feature/nodelist/routerinfo.c \ src/feature/nodelist/routerlist.c \ - src/feature/nodelist/routerparse.c \ src/feature/nodelist/routerset.c \ src/feature/nodelist/fmt_routerstatus.c \ src/feature/nodelist/torcert.c \ @@ -116,6 +124,7 @@ LIBTOR_APP_A_SOURCES = \ src/feature/rend/rendclient.c \ src/feature/rend/rendcommon.c \ src/feature/rend/rendmid.c \ + src/feature/rend/rendparse.c \ src/feature/rend/rendservice.c \ src/feature/stats/geoip_stats.c \ src/feature/stats/rephist.c \ @@ -125,6 +134,7 @@ LIBTOR_APP_A_SOURCES = \ # the separation is only in the code location. LIBTOR_APP_A_SOURCES += \ src/feature/dirauth/bwauth.c \ + src/feature/dirauth/dsigs_parse.c \ src/feature/dirauth/guardfraction.c \ src/feature/dirauth/reachability.c \ src/feature/dirauth/recommend_pkg.c \ @@ -240,6 +250,7 @@ noinst_HEADERS += \ src/core/or/status.h \ src/core/or/tor_version_st.h \ src/core/or/var_cell_st.h \ + src/core/or/versions.h \ src/core/proto/proto_cell.h \ src/core/proto/proto_control0.h \ src/core/proto/proto_ext_or.h \ @@ -260,6 +271,7 @@ noinst_HEADERS += \ src/feature/dirauth/bwauth.h \ src/feature/dirauth/dircollate.h \ src/feature/dirauth/dirvote.h \ + src/feature/dirauth/dsigs_parse.h \ src/feature/dirauth/guardfraction.h \ src/feature/dirauth/keypin.h \ src/feature/dirauth/ns_detached_signatures_st.h \ @@ -285,6 +297,16 @@ noinst_HEADERS += \ src/feature/dircommon/fp_pair.h \ src/feature/dircommon/vote_timing_st.h \ src/feature/dircommon/voting_schedule.h \ + src/feature/dirparse/authcert_members.i \ + src/feature/dirparse/authcert_parse.h \ + src/feature/dirparse/microdesc_parse.h \ + src/feature/dirparse/ns_parse.h \ + src/feature/dirparse/parsecommon.h \ + src/feature/dirparse/policy_parse.h \ + src/feature/dirparse/routerparse.h \ + src/feature/dirparse/sigcommon.h \ + src/feature/dirparse/signing.h \ + src/feature/dirparse/unparseable.h \ src/feature/hibernate/hibernate.h \ src/feature/hs/hs_cache.h \ src/feature/hs/hs_cell.h \ @@ -320,12 +342,10 @@ noinst_HEADERS += \ src/feature/nodelist/node_st.h \ src/feature/nodelist/nodelist.h \ src/feature/nodelist/node_select.h \ - src/feature/nodelist/parsecommon.h \ src/feature/nodelist/routerinfo.h \ src/feature/nodelist/routerinfo_st.h \ src/feature/nodelist/routerlist.h \ src/feature/nodelist/routerlist_st.h \ - src/feature/nodelist/routerparse.h \ src/feature/nodelist/routerset.h \ src/feature/nodelist/fmt_routerstatus.h \ src/feature/nodelist/routerstatus_st.h \ @@ -348,6 +368,7 @@ noinst_HEADERS += \ src/feature/rend/rendclient.h \ src/feature/rend/rendcommon.h \ src/feature/rend/rendmid.h \ + src/feature/rend/rendparse.h \ src/feature/rend/rendservice.h \ src/feature/stats/geoip_stats.h \ src/feature/stats/rephist.h \ diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c index ef8c60a3da..3f3b13e069 100644 --- a/src/core/mainloop/connection.c +++ b/src/core/mainloop/connection.c @@ -96,15 +96,14 @@ #include "feature/hs/hs_ident.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerlist.h" -#include "feature/nodelist/routerparse.h" #include "feature/relay/dns.h" #include "feature/relay/ext_orport.h" #include "feature/relay/routermode.h" #include "feature/rend/rendclient.h" #include "feature/rend/rendcommon.h" -#include "lib/geoip/geoip.h" #include "feature/stats/rephist.h" #include "lib/crypt_ops/crypto_util.h" +#include "lib/geoip/geoip.h" #include "lib/sandbox/sandbox.h" #include "lib/net/buffers_net.h" diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c index 822129d80e..a69457571e 100644 --- a/src/core/or/circuitbuild.c +++ b/src/core/or/circuitbuild.c @@ -61,7 +61,6 @@ #include "feature/nodelist/node_select.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerlist.h" -#include "feature/nodelist/routerparse.h" #include "feature/nodelist/routerset.h" #include "feature/relay/router.h" #include "feature/relay/routermode.h" diff --git a/src/core/or/policies.c b/src/core/or/policies.c index c3fded1fbd..0eda93c5f3 100644 --- a/src/core/or/policies.c +++ b/src/core/or/policies.c @@ -20,13 +20,13 @@ #include "core/or/or.h" #include "feature/client/bridges.h" #include "app/config/config.h" +#include "core/or/policies.h" +#include "feature/dirparse/policy_parse.h" #include "feature/nodelist/microdesc.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/nodelist.h" -#include "core/or/policies.h" #include "feature/relay/router.h" #include "feature/relay/routermode.h" -#include "feature/nodelist/routerparse.h" #include "lib/geoip/geoip.h" #include "ht.h" #include "lib/encoding/confline.h" diff --git a/src/core/or/protover.c b/src/core/or/protover.c index f0791366fe..e80fbfae81 100644 --- a/src/core/or/protover.c +++ b/src/core/or/protover.c @@ -25,7 +25,7 @@ #include "core/or/or.h" #include "core/or/protover.h" -#include "feature/nodelist/routerparse.h" +#include "core/or/versions.h" #include "lib/tls/tortls.h" #ifndef HAVE_RUST diff --git a/src/core/or/relay.c b/src/core/or/relay.c index 9e45a35246..510d96c648 100644 --- a/src/core/or/relay.c +++ b/src/core/or/relay.c @@ -79,7 +79,6 @@ #include "feature/rend/rendcommon.h" #include "feature/nodelist/describe.h" #include "feature/nodelist/routerlist.h" -#include "feature/nodelist/routerparse.h" #include "core/or/scheduler.h" #include "feature/stats/rephist.h" diff --git a/src/core/or/versions.c b/src/core/or/versions.c new file mode 100644 index 0000000000..06274996a7 --- /dev/null +++ b/src/core/or/versions.c @@ -0,0 +1,422 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file versions.c + * \brief Code to manipulate, parse, and compare Tor versions. + */ +#include "core/or/or.h" + +#include "core/or/protover.h" +#include "core/or/versions.h" +#include "lib/crypt_ops/crypto_util.h" + +#include "core/or/tor_version_st.h" + +/** Return VS_RECOMMENDED if <b>myversion</b> is contained in + * <b>versionlist</b>. Else, return VS_EMPTY if versionlist has no + * entries. Else, return VS_OLD if every member of + * <b>versionlist</b> is newer than <b>myversion</b>. Else, return + * VS_NEW_IN_SERIES if there is at least one member of <b>versionlist</b> in + * the same series (major.minor.micro) as <b>myversion</b>, but no such member + * is newer than <b>myversion.</b>. Else, return VS_NEW if every member of + * <b>versionlist</b> is older than <b>myversion</b>. Else, return + * VS_UNRECOMMENDED. + * + * (versionlist is a comma-separated list of version strings, + * optionally prefixed with "Tor". Versions that can't be parsed are + * ignored.) + */ +version_status_t +tor_version_is_obsolete(const char *myversion, const char *versionlist) +{ + tor_version_t mine, other; + int found_newer = 0, found_older = 0, found_newer_in_series = 0, + found_any_in_series = 0, r, same; + version_status_t ret = VS_UNRECOMMENDED; + smartlist_t *version_sl; + + log_debug(LD_CONFIG,"Checking whether version '%s' is in '%s'", + myversion, versionlist); + + if (tor_version_parse(myversion, &mine)) { + log_err(LD_BUG,"I couldn't parse my own version (%s)", myversion); + tor_assert(0); + } + version_sl = smartlist_new(); + smartlist_split_string(version_sl, versionlist, ",", SPLIT_SKIP_SPACE, 0); + + if (!strlen(versionlist)) { /* no authorities cared or agreed */ + ret = VS_EMPTY; + goto done; + } + + SMARTLIST_FOREACH_BEGIN(version_sl, const char *, cp) { + if (!strcmpstart(cp, "Tor ")) + cp += 4; + + if (tor_version_parse(cp, &other)) { + /* Couldn't parse other; it can't be a match. */ + } else { + same = tor_version_same_series(&mine, &other); + if (same) + found_any_in_series = 1; + r = tor_version_compare(&mine, &other); + if (r==0) { + ret = VS_RECOMMENDED; + goto done; + } else if (r<0) { + found_newer = 1; + if (same) + found_newer_in_series = 1; + } else if (r>0) { + found_older = 1; + } + } + } SMARTLIST_FOREACH_END(cp); + + /* We didn't find the listed version. Is it new or old? */ + if (found_any_in_series && !found_newer_in_series && found_newer) { + ret = VS_NEW_IN_SERIES; + } else if (found_newer && !found_older) { + ret = VS_OLD; + } else if (found_older && !found_newer) { + ret = VS_NEW; + } else { + ret = VS_UNRECOMMENDED; + } + + done: + SMARTLIST_FOREACH(version_sl, char *, version, tor_free(version)); + smartlist_free(version_sl); + return ret; +} + +/** Extract a Tor version from a <b>platform</b> line from a router + * descriptor, and place the result in <b>router_version</b>. + * + * Return 1 on success, -1 on parsing failure, and 0 if the + * platform line does not indicate some version of Tor. + * + * If <b>strict</b> is non-zero, finding any weird version components + * (like negative numbers) counts as a parsing failure. + */ +int +tor_version_parse_platform(const char *platform, + tor_version_t *router_version, + int strict) +{ + char tmp[128]; + char *s, *s2, *start; + + if (strcmpstart(platform,"Tor ")) /* nonstandard Tor; say 0. */ + return 0; + + start = (char *)eat_whitespace(platform+3); + if (!*start) return -1; + s = (char *)find_whitespace(start); /* also finds '\0', which is fine */ + s2 = (char*)eat_whitespace(s); + if (!strcmpstart(s2, "(r") || !strcmpstart(s2, "(git-")) + s = (char*)find_whitespace(s2); + + if ((size_t)(s-start+1) >= sizeof(tmp)) /* too big, no */ + return -1; + strlcpy(tmp, start, s-start+1); + + if (tor_version_parse(tmp, router_version)<0) { + log_info(LD_DIR,"Router version '%s' unparseable.",tmp); + return -1; + } + + if (strict) { + if (router_version->major < 0 || + router_version->minor < 0 || + router_version->micro < 0 || + router_version->patchlevel < 0 || + router_version->svn_revision < 0) { + return -1; + } + } + + return 1; +} + +/** Parse the Tor version of the platform string <b>platform</b>, + * and compare it to the version in <b>cutoff</b>. Return 1 if + * the router is at least as new as the cutoff, else return 0. + */ +int +tor_version_as_new_as(const char *platform, const char *cutoff) +{ + tor_version_t cutoff_version, router_version; + int r; + tor_assert(platform); + + if (tor_version_parse(cutoff, &cutoff_version)<0) { + log_warn(LD_BUG,"cutoff version '%s' unparseable.",cutoff); + return 0; + } + + r = tor_version_parse_platform(platform, &router_version, 0); + if (r == 0) { + /* nonstandard Tor; be safe and say yes */ + return 1; + } else if (r < 0) { + /* unparseable version; be safe and say yes. */ + return 1; + } + + /* Here's why we don't need to do any special handling for svn revisions: + * - If neither has an svn revision, we're fine. + * - If the router doesn't have an svn revision, we can't assume that it + * is "at least" any svn revision, so we need to return 0. + * - If the target version doesn't have an svn revision, any svn revision + * (or none at all) is good enough, so return 1. + * - If both target and router have an svn revision, we compare them. + */ + + return tor_version_compare(&router_version, &cutoff_version) >= 0; +} + +/** Parse a tor version from <b>s</b>, and store the result in <b>out</b>. + * Return 0 on success, -1 on failure. */ +int +tor_version_parse(const char *s, tor_version_t *out) +{ + char *eos=NULL; + const char *cp=NULL; + int ok = 1; + /* Format is: + * "Tor " ? NUM dot NUM [ dot NUM [ ( pre | rc | dot ) NUM ] ] [ - tag ] + */ + tor_assert(s); + tor_assert(out); + + memset(out, 0, sizeof(tor_version_t)); + out->status = VER_RELEASE; + if (!strcasecmpstart(s, "Tor ")) + s += 4; + + cp = s; + +#define NUMBER(m) \ + do { \ + if (!cp || *cp < '0' || *cp > '9') \ + return -1; \ + out->m = (int)tor_parse_uint64(cp, 10, 0, INT32_MAX, &ok, &eos); \ + if (!ok) \ + return -1; \ + if (!eos || eos == cp) \ + return -1; \ + cp = eos; \ + } while (0) + +#define DOT() \ + do { \ + if (*cp != '.') \ + return -1; \ + ++cp; \ + } while (0) + + NUMBER(major); + DOT(); + NUMBER(minor); + if (*cp == 0) + return 0; + else if (*cp == '-') + goto status_tag; + DOT(); + NUMBER(micro); + + /* Get status */ + if (*cp == 0) { + return 0; + } else if (*cp == '.') { + ++cp; + } else if (*cp == '-') { + goto status_tag; + } else if (0==strncmp(cp, "pre", 3)) { + out->status = VER_PRE; + cp += 3; + } else if (0==strncmp(cp, "rc", 2)) { + out->status = VER_RC; + cp += 2; + } else { + return -1; + } + + NUMBER(patchlevel); + + status_tag: + /* Get status tag. */ + if (*cp == '-' || *cp == '.') + ++cp; + eos = (char*) find_whitespace(cp); + if (eos-cp >= (int)sizeof(out->status_tag)) + strlcpy(out->status_tag, cp, sizeof(out->status_tag)); + else { + memcpy(out->status_tag, cp, eos-cp); + out->status_tag[eos-cp] = 0; + } + cp = eat_whitespace(eos); + + if (!strcmpstart(cp, "(r")) { + cp += 2; + out->svn_revision = (int) strtol(cp,&eos,10); + } else if (!strcmpstart(cp, "(git-")) { + char *close_paren = strchr(cp, ')'); + int hexlen; + char digest[DIGEST_LEN]; + if (! close_paren) + return -1; + cp += 5; + if (close_paren-cp > HEX_DIGEST_LEN) + return -1; + hexlen = (int)(close_paren-cp); + memwipe(digest, 0, sizeof(digest)); + if ( hexlen == 0 || (hexlen % 2) == 1) + return -1; + if (base16_decode(digest, hexlen/2, cp, hexlen) != hexlen/2) + return -1; + memcpy(out->git_tag, digest, hexlen/2); + out->git_tag_len = hexlen/2; + } + + return 0; +#undef NUMBER +#undef DOT +} + +/** Compare two tor versions; Return <0 if a < b; 0 if a ==b, >0 if a > + * b. */ +int +tor_version_compare(tor_version_t *a, tor_version_t *b) +{ + int i; + tor_assert(a); + tor_assert(b); + + /* We take this approach to comparison to ensure the same (bogus!) behavior + * on all inputs as we would have seen before bug #21278 was fixed. The + * only important difference here is that this method doesn't cause + * a signed integer underflow. + */ +#define CMP(field) do { \ + unsigned aval = (unsigned) a->field; \ + unsigned bval = (unsigned) b->field; \ + int result = (int) (aval - bval); \ + if (result < 0) \ + return -1; \ + else if (result > 0) \ + return 1; \ + } while (0) + + CMP(major); + CMP(minor); + CMP(micro); + CMP(status); + CMP(patchlevel); + if ((i = strcmp(a->status_tag, b->status_tag))) + return i; + CMP(svn_revision); + CMP(git_tag_len); + if (a->git_tag_len) + return fast_memcmp(a->git_tag, b->git_tag, a->git_tag_len); + else + return 0; + +#undef CMP +} + +/** Return true iff versions <b>a</b> and <b>b</b> belong to the same series. + */ +int +tor_version_same_series(tor_version_t *a, tor_version_t *b) +{ + tor_assert(a); + tor_assert(b); + return ((a->major == b->major) && + (a->minor == b->minor) && + (a->micro == b->micro)); +} + +/** Helper: Given pointers to two strings describing tor versions, return -1 + * if _a precedes _b, 1 if _b precedes _a, and 0 if they are equivalent. + * Used to sort a list of versions. */ +static int +compare_tor_version_str_ptr_(const void **_a, const void **_b) +{ + const char *a = *_a, *b = *_b; + int ca, cb; + tor_version_t va, vb; + ca = tor_version_parse(a, &va); + cb = tor_version_parse(b, &vb); + /* If they both parse, compare them. */ + if (!ca && !cb) + return tor_version_compare(&va,&vb); + /* If one parses, it comes first. */ + if (!ca && cb) + return -1; + if (ca && !cb) + return 1; + /* If neither parses, compare strings. Also, the directory server admin + ** needs to be smacked upside the head. But Tor is tolerant and gentle. */ + return strcmp(a,b); +} + +/** Sort a list of string-representations of versions in ascending order. */ +void +sort_version_list(smartlist_t *versions, int remove_duplicates) +{ + smartlist_sort(versions, compare_tor_version_str_ptr_); + + if (remove_duplicates) + smartlist_uniq(versions, compare_tor_version_str_ptr_, tor_free_); +} + +/** Summarize the protocols listed in <b>protocols</b> into <b>out</b>, + * falling back or correcting them based on <b>version</b> as appropriate. + */ +void +summarize_protover_flags(protover_summary_flags_t *out, + const char *protocols, + const char *version) +{ + tor_assert(out); + memset(out, 0, sizeof(*out)); + if (protocols) { + out->protocols_known = 1; + out->supports_extend2_cells = + protocol_list_supports_protocol(protocols, PRT_RELAY, 2); + out->supports_ed25519_link_handshake_compat = + protocol_list_supports_protocol(protocols, PRT_LINKAUTH, 3); + out->supports_ed25519_link_handshake_any = + protocol_list_supports_protocol_or_later(protocols, PRT_LINKAUTH, 3); + out->supports_ed25519_hs_intro = + protocol_list_supports_protocol(protocols, PRT_HSINTRO, 4); + out->supports_v3_hsdir = + protocol_list_supports_protocol(protocols, PRT_HSDIR, + PROTOVER_HSDIR_V3); + out->supports_v3_rendezvous_point = + protocol_list_supports_protocol(protocols, PRT_HSREND, + PROTOVER_HS_RENDEZVOUS_POINT_V3); + } + if (version && !strcmpstart(version, "Tor ")) { + if (!out->protocols_known) { + /* The version is a "Tor" version, and where there is no + * list of protocol versions that we should be looking at instead. */ + + out->supports_extend2_cells = + tor_version_as_new_as(version, "0.2.4.8-alpha"); + out->protocols_known = 1; + } else { + /* Bug #22447 forces us to filter on this version. */ + if (!tor_version_as_new_as(version, "0.3.0.8")) { + out->supports_v3_hsdir = 0; + } + } + } +} diff --git a/src/core/or/versions.h b/src/core/or/versions.h new file mode 100644 index 0000000000..0c773f3f4c --- /dev/null +++ b/src/core/or/versions.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file versions.h + * \brief Header file for versions.c. + **/ + +#ifndef TOR_VERSIONS_H +#define TOR_VERSIONS_H + +/** Possible statuses of a version of Tor, given opinions from the directory + * servers. */ +typedef enum version_status_t { + VS_RECOMMENDED=0, /**< This version is listed as recommended. */ + VS_OLD=1, /**< This version is older than any recommended version. */ + VS_NEW=2, /**< This version is newer than any recommended version. */ + VS_NEW_IN_SERIES=3, /**< This version is newer than any recommended version + * in its series, but later recommended versions exist. + */ + VS_UNRECOMMENDED=4, /**< This version is not recommended (general case). */ + VS_EMPTY=5, /**< The version list was empty; no agreed-on versions. */ + VS_UNKNOWN, /**< We have no idea. */ +} version_status_t; + +version_status_t tor_version_is_obsolete(const char *myversion, + const char *versionlist); +int tor_version_parse_platform(const char *platform, + tor_version_t *version_out, + int strict); +int tor_version_as_new_as(const char *platform, const char *cutoff); +int tor_version_parse(const char *s, tor_version_t *out); +int tor_version_compare(tor_version_t *a, tor_version_t *b); +int tor_version_same_series(tor_version_t *a, tor_version_t *b); +void sort_version_list(smartlist_t *lst, int remove_duplicates); + +void summarize_protover_flags(protover_summary_flags_t *out, + const char *protocols, + const char *version); + +#endif /* !defined(TOR_VERSIONS_H) */ |