tor_netdoc/doc/netstatus/rs/each_variety.rs
1//! router status entries - items for all varieties, that vary
2//!
3//! **This file is reincluded multiple times**,
4//! by the macros in [`crate::doc::ns_variety_definition_macros`],
5//! once for votes, and once for each consensus flavour.
6//! It is *not* a module `crate::doc::netstatus::rs::each_variety`.
7//!
8//! Each time this file is included by one of the macros mentioned above,
9//! the `ns_***` macros (such as `ns_const_name!`) may expand to different values.
10//!
11//! See [`crate::doc::ns_variety_definition_macros`].
12
13use super::*;
14
15// Explicit parsing arrangements for document digest fields in `r` and `m` items.
16//
17// https://spec.torproject.org/dir-spec/consensus-formats.html#item:r
18// https://spec.torproject.org/dir-spec/consensus-formats.html#item:m
19// https://spec.torproject.org/dir-spec/computing-consensus.html#flavor:microdesc
20//
21// The document digest moves about, and vote `m` items are even more exciting.
22// This is for the benefit of the `with` annotations for these two fields:
23//
24// RouterStatus.r.doc_digest aka RouterStatusIntroItem.doc_digest
25// RouterStatus.m
26//
27// This would have been a bit easier if the various DocDigest types implemented parse2 traits,
28// but they're just byte arrays and such impls would imply that byte arrays are always
29// represented the same way in netdocs which is very far from being true.
30// TODO consider introducing newtypes for routerdesc and microdesc hashes?
31ns_choose! { (
32 use doc_digest_parse2_real as doc_digest_parse2_r; // implemented here in rs/each_variety.rs
33 use Ignored as doc_digest_parse2_m;
34 use relay_flags::ConsensusRepr as VarietyRelayFlagsRepr;
35) (
36 use NotPresent as doc_digest_parse2_r;
37 use doc_digest_parse2_real_item as doc_digest_parse2_m; // implemented in rs/md.rs
38 use relay_flags::ConsensusRepr as VarietyRelayFlagsRepr;
39) (
40 use doc_digest_parse2_real as doc_digest_parse2_r; // implemented here in rs/each_variety.rs
41 use RouterStatusMdDigestsVote as doc_digest_parse2_m;
42 use relay_flags::NoImplicitRepr as VarietyRelayFlagsRepr;
43) }
44
45/// Intro item for a router status entry
46///
47/// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:r>
48///
49/// <https://spec.torproject.org/dir-spec/computing-consensus.html#flavor:microdesc>
50/// `r` item.
51#[derive(Debug, Clone, Deftly)]
52#[derive_deftly(ItemValueParseable)]
53#[non_exhaustive]
54pub struct RouterStatusIntroItem {
55 /// The nickname for this relay.
56 ///
57 /// Nicknames can be used for convenience purpose, but no more:
58 /// there is no mechanism to enforce their uniqueness.
59 pub nickname: Nickname,
60 /// Fingerprint of the old-style RSA identity for this relay.
61 pub identity: Base64Fingerprint,
62 /// Digest of the document for this relay (except md consensuses)
63 // TODO SPEC rename in the spec from `digest` to "doc_digest"
64 // TODO SPEC in md consensuses the referenced document digest is in a separate `m` item
65 #[deftly(netdoc(with = doc_digest_parse2_r))]
66 pub doc_digest: ns_type!(DocDigest, NotPresent, DocDigest),
67 /// Publication time.
68 pub publication: ns_type!(
69 IgnoredPublicationTimeSp,
70 IgnoredPublicationTimeSp,
71 Iso8601TimeSp
72 ),
73 /// IPv4 address
74 pub ip: std::net::Ipv4Addr,
75 /// Relay port
76 pub or_port: u16,
77}
78
79/// A single relay's status, in a network status document.
80///
81/// <https://spec.torproject.org/dir-spec/consensus-formats.html#section:router-status>
82///
83/// <https://spec.torproject.org/dir-spec/computing-consensus.html#flavor:microdesc>
84/// under "Changes to router status entries".
85//
86// In most netdocs we would use the item keywords as the field names. But routerstatus
87// entry keywords are chosen to be very short to minimise the consensus size, so we
88// use longer names in the struct and specify the keyword separately.
89#[derive(Debug, Clone, Deftly)]
90#[derive_deftly(NetdocParseable)]
91#[non_exhaustive]
92pub struct RouterStatus {
93 /// `r` --- Introduce a routerstatus entry
94 ///
95 /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:r>
96 /// (and, the md version, which is different).
97 pub r: RouterStatusIntroItem,
98
99 /// `m` --- Microdescriptor or document digest
100 ///
101 /// In an md consensus, the hash of the document for this relay.
102 /// In a vote, microdescriptor hashes for the various consensus methods.
103 ///
104 /// <https://spec.torproject.org/dir-spec/computing-consensus.html#flavor:microdesc>
105 /// `r` item.
106 // We call this field `m` rather than `doc_digest` because it's not always the doc digest.
107 // TODO SPEC in all but md consensuses the referenced document digest is in the `r` intro item
108 #[deftly(netdoc(with = doc_digest_parse2_m))]
109 pub m: ns_type!(NotPresent, DocDigest, Vec<RouterStatusMdDigestsVote>),
110
111 /// `a` --- Further router address(es) (IPv6)
112 ///
113 /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:a>
114 /// (and, the md version, which is different).
115 #[deftly(netdoc(single_arg))]
116 pub a: Vec<net::SocketAddr>,
117
118 /// `s` --- Router status flags
119 ///
120 /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:s>
121 #[deftly(netdoc(
122 keyword = "s",
123 with = { relay_flags::ParserEncoder::<VarietyRelayFlagsRepr> },
124 ))]
125 pub flags: DocRelayFlags,
126
127 /// `v` --- Relay's Tor software version
128 ///
129 /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:v>
130 #[deftly(netdoc(keyword = "v"))]
131 pub version: Option<SoftwareVersion>,
132
133 /// `pr` --- Subprotocol capabilities supported
134 ///
135 /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:v>
136 #[deftly(netdoc(keyword = "pr"))]
137 pub protos: Protocols,
138
139 /// `w` --- Bandwidth estimates
140 ///
141 /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:w>
142 #[deftly(netdoc(keyword = "w"))]
143 pub weight: RelayWeight,
144}
145
146impl RouterStatus {
147 /// Return the digest of the document identified by this
148 /// routerstatus.
149 ///
150 /// The `doc_digest` method is provided on all varieties of routerstatus entry
151 /// to help paper over the protocol anomaly, that the digest is in a different place
152 /// in md routerstatus entries.
153 pub fn doc_digest(&self) -> &DocDigest {
154 ns_expr!(&self.r.doc_digest, &self.m, &self.r.doc_digest,)
155 }
156}
157
158/// Netdoc format helper module for referenced doc digest field in `r` and `m`
159///
160/// This field is present in `r` items, except for md consensuses, where it's in `m`.
161/// Hence the `_real`, which lets us swap it out for each variety.
162pub(crate) mod doc_digest_parse2_real {
163 use super::*;
164 use crate::parse2::ArgumentError as AE;
165 use crate::parse2::ArgumentStream;
166 use std::result::Result;
167
168 /// Parse a single argument
169 pub(crate) fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<DocDigest, AE> {
170 let data = args
171 .next()
172 .ok_or(AE::Missing)?
173 .parse::<B64>()
174 .map_err(|_| AE::Invalid)?;
175 data.into_array().map_err(|_| AE::Invalid)
176 }
177}