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 theses 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?
31#[cfg(feature = "parse2")]
32ns_choose! { (
33 use doc_digest_parse2_real as doc_digest_parse2_r; // implemented here in rs/each_variety.rs
34 use Ignored as doc_digest_parse2_m;
35 use relay_flags::ConsensusRepr as VarietyRelayFlagsRepr;
36) (
37 use NotPresent as doc_digest_parse2_r;
38 use doc_digest_parse2_real_item as doc_digest_parse2_m; // implemented in rs/md.rs
39 use relay_flags::ConsensusRepr as VarietyRelayFlagsRepr;
40) (
41 use doc_digest_parse2_real as doc_digest_parse2_r; // implemented here in rs/each_variety.rs
42 use RouterStatusMdDigestsVote as doc_digest_parse2_m;
43 use relay_flags::VoteRepr as VarietyRelayFlagsRepr;
44) }
45
46/// Intro item for a router status entry
47///
48/// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:r>
49///
50/// <https://spec.torproject.org/dir-spec/computing-consensus.html#flavor:microdesc>
51/// `r` item.
52#[cfg_attr(
53 feature = "parse2",
54 derive(Deftly),
55 derive_deftly(ItemValueParseable),
56)]
57#[derive(Debug, Clone)]
58#[non_exhaustive]
59pub struct RouterStatusIntroItem {
60 /// The nickname for this relay.
61 ///
62 /// Nicknames can be used for convenience purpose, but no more:
63 /// there is no mechanism to enforce their uniqueness.
64 pub nickname: Nickname,
65 /// Fingerprint of the old-style RSA identity for this relay.
66 pub identity: Base64Fingerprint,
67 /// Digest of the document for this relay (except md consensuses)
68 // TODO SPEC rename in the spec from `digest` to "doc_digest"
69 // TODO SPEC in md consensuses the referenced document digest is in a separate `m` item
70 #[cfg_attr(feature = "parse2", deftly(netdoc(with = "doc_digest_parse2_r")))]
71 pub doc_digest: ns_type!( DocDigest, NotPresent, DocDigest ),
72 /// Publication time.
73 pub publication: ns_type!( IgnoredPublicationTimeSp, IgnoredPublicationTimeSp, Iso8601TimeSp ),
74 /// IPv4 address
75 pub ip: std::net::Ipv4Addr,
76 /// Relay port
77 pub or_port: u16,
78}
79
80/// A single relay's status, in a network status document.
81///
82/// <https://spec.torproject.org/dir-spec/consensus-formats.html#section:router-status>
83///
84/// <https://spec.torproject.org/dir-spec/computing-consensus.html#flavor:microdesc>
85/// under "Changes to router status entries".
86//
87// In most netdocs we would use the item keywords as the field names. But routerstatus
88// entry keywords are chosen to be very short to minimise the consensus size, so we
89// use longer names in the struct and specify the keyword separately.
90#[cfg_attr(
91 feature = "parse2",
92 derive(Deftly),
93 derive_deftly(NetdocParseable),
94)]
95#[derive(Debug, Clone)]
96#[non_exhaustive]
97pub struct RouterStatus {
98 /// `r` --- Introduce a routerstatus entry
99 ///
100 /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:r>
101 /// (and, the the md version, which is different).
102 pub r: RouterStatusIntroItem,
103
104 /// `m` --- Microdescriptor or document digest
105 ///
106 /// In an md consensus, the hash of the document for this relay.
107 /// In a vote, microdescriptor hashes for the various consensus methods.
108 ///
109 /// <https://spec.torproject.org/dir-spec/computing-consensus.html#flavor:microdesc>
110 /// `r` item.
111 // We call this field `m` rather than `doc_digest` because it's not always the doc digest.
112 // TODO SPEC in all but md consensuses the referenced document digest is in the `r` intro item
113 #[cfg_attr(feature = "parse2", deftly(netdoc(with = "doc_digest_parse2_m")))]
114 pub m: ns_type!( NotPresent, DocDigest, Vec<RouterStatusMdDigestsVote> ),
115
116 /// `a` --- Further router address(es) (IPv6)
117 ///
118 /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:a>
119 /// (and, the the md version, which is different).
120 #[cfg_attr(feature = "parse2", deftly(netdoc(single_arg)))]
121 pub a: Vec<net::SocketAddr>,
122
123 /// `s` --- Router status flags
124 ///
125 /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:s>
126 #[cfg_attr(
127 feature = "parse2",
128 deftly(netdoc(keyword = "s")),
129 deftly(netdoc(with = "relay_flags::Parser::<VarietyRelayFlagsRepr>")),
130 )]
131 pub flags: DocRelayFlags,
132
133 /// `v` --- Relay's Tor software version
134 ///
135 /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:v>
136 #[cfg_attr(feature = "parse2", deftly(netdoc(keyword = "v")))]
137 pub version: Option<Version>,
138
139 /// `pr` --- Subprotocol capabilities supported
140 ///
141 /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:v>
142 #[cfg_attr(feature = "parse2", deftly(netdoc(keyword = "pr")))]
143 pub protos: Arc<Protocols>,
144
145 /// `w` --- Bandwidth estimates
146 ///
147 /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:w>
148 #[cfg_attr(feature = "parse2", deftly(netdoc(keyword = "w")))]
149 pub weight: RelayWeight,
150}
151
152impl RouterStatus {
153 /// Return the digest of the document identified by this
154 /// routerstatus.
155 ///
156 /// The `doc_digest` method is provided on all varieties of routerstatus entry
157 /// to help paper over the protocol anomaly, that the digest is in a different place
158 /// in md routerstatus entries.
159 pub fn doc_digest(&self) -> &DocDigest {
160 ns_expr!(
161 &self.r.doc_digest,
162 &self.m,
163 &self.r.doc_digest,
164 )
165 }
166}
167
168/// Netdoc format helper module for referenced doc digest field in `r` and `m`
169///
170/// This field is present in `r` items, except for md consensuses, where it's in `m`.
171/// Hence the `_real`, which lets us swap it out for each variety.
172#[cfg(feature = "parse2")]
173pub(crate) mod doc_digest_parse2_real {
174 use super::*;
175 use std::result::Result;
176 use crate::parse2::ArgumentStream;
177 use crate::parse2::ArgumentError as AE;
178
179 /// Parse a single argument
180 pub(crate) fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<DocDigest, AE> {
181 let data = args.next().ok_or(AE::Missing)?
182 .parse::<B64>().map_err(|_| AE::Invalid)?;
183 data.into_array().map_err(|_| AE::Invalid)
184 }
185}