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