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

            
13
use 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")]
32
ns_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(feature = "parse2", derive(Deftly), derive_deftly(ItemValueParseable))]
53
#[derive(Debug, Clone)]
54
#[non_exhaustive]
55
pub struct RouterStatusIntroItem {
56
    /// The nickname for this relay.
57
    ///
58
    /// Nicknames can be used for convenience purpose, but no more:
59
    /// there is no mechanism to enforce their uniqueness.
60
    pub nickname: Nickname,
61
    /// Fingerprint of the old-style RSA identity for this relay.
62
    pub identity: Base64Fingerprint,
63
    /// Digest of the document for this relay (except md consensuses)
64
    // TODO SPEC rename in the spec from `digest` to "doc_digest"
65
    // TODO SPEC in md consensuses the referenced document digest is in a separate `m` item
66
    #[cfg_attr(feature = "parse2", deftly(netdoc(with = "doc_digest_parse2_r")))]
67
    pub doc_digest: ns_type!(DocDigest, NotPresent, DocDigest),
68
    /// Publication time.
69
    pub publication: ns_type!(
70
        IgnoredPublicationTimeSp,
71
        IgnoredPublicationTimeSp,
72
        Iso8601TimeSp
73
    ),
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(feature = "parse2", derive(Deftly), derive_deftly(NetdocParseable))]
91
#[derive(Debug, Clone)]
92
#[non_exhaustive]
93
pub struct RouterStatus {
94
    /// `r` --- Introduce a routerstatus entry
95
    ///
96
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:r>
97
    /// (and, the the md version, which is different).
98
    pub r: RouterStatusIntroItem,
99

            
100
    /// `m` --- Microdescriptor or document digest
101
    ///
102
    /// In an md consensus, the hash of the document for this relay.
103
    /// In a vote, microdescriptor hashes for the various consensus methods.
104
    ///
105
    /// <https://spec.torproject.org/dir-spec/computing-consensus.html#flavor:microdesc>
106
    /// `r` item.
107
    // We call this field `m` rather than `doc_digest` because it's not always the doc digest.
108
    // TODO SPEC in all but md consensuses the referenced document digest is in the `r` intro item
109
    #[cfg_attr(feature = "parse2", deftly(netdoc(with = "doc_digest_parse2_m")))]
110
    pub m: ns_type!(NotPresent, DocDigest, Vec<RouterStatusMdDigestsVote>),
111

            
112
    /// `a` --- Further router address(es) (IPv6)
113
    ///
114
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:a>
115
    /// (and, the the md version, which is different).
116
    #[cfg_attr(feature = "parse2", deftly(netdoc(single_arg)))]
117
    pub a: Vec<net::SocketAddr>,
118

            
119
    /// `s` --- Router status flags
120
    ///
121
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:s>
122
    #[cfg_attr(
123
        feature = "parse2",
124
        deftly(netdoc(keyword = "s")),
125
        deftly(netdoc(with = "relay_flags::Parser::<VarietyRelayFlagsRepr>"))
126
    )]
127
    pub flags: DocRelayFlags,
128

            
129
    /// `v` --- Relay's Tor software version
130
    ///
131
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:v>
132
    #[cfg_attr(feature = "parse2", deftly(netdoc(keyword = "v")))]
133
    pub version: Option<SoftwareVersion>,
134

            
135
    /// `pr` --- Subprotocol capabilities supported
136
    ///
137
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:v>
138
    #[cfg_attr(feature = "parse2", deftly(netdoc(keyword = "pr")))]
139
    pub protos: Protocols,
140

            
141
    /// `w` --- Bandwidth estimates
142
    ///
143
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:w>
144
    #[cfg_attr(feature = "parse2", deftly(netdoc(keyword = "w")))]
145
    pub weight: RelayWeight,
146
}
147

            
148
impl RouterStatus {
149
    /// Return the digest of the document identified by this
150
    /// routerstatus.
151
    ///
152
    /// The `doc_digest` method is provided on all varieties of routerstatus entry
153
    /// to help paper over the protocol anomaly, that the digest is in a different place
154
    /// in md routerstatus entries.
155
65752073
    pub fn doc_digest(&self) -> &DocDigest {
156
65752073
        ns_expr!(&self.r.doc_digest, &self.m, &self.r.doc_digest,)
157
65752073
    }
158
}
159

            
160
/// Netdoc format helper module for referenced doc digest field in `r` and `m`
161
///
162
/// This field is present in `r` items, except for md consensuses, where it's in `m`.
163
/// Hence the `_real`, which lets us swap it out for each variety.
164
#[cfg(feature = "parse2")]
165
pub(crate) mod doc_digest_parse2_real {
166
    use super::*;
167
    use crate::parse2::ArgumentError as AE;
168
    use crate::parse2::ArgumentStream;
169
    use std::result::Result;
170

            
171
    /// Parse a single argument
172
36
    pub(crate) fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<DocDigest, AE> {
173
36
        let data = args
174
36
            .next()
175
36
            .ok_or(AE::Missing)?
176
36
            .parse::<B64>()
177
36
            .map_err(|_| AE::Invalid)?;
178
36
        data.into_array().map_err(|_| AE::Invalid)
179
36
    }
180
}