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 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?
31
ns_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]
54
pub 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]
92
pub 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

            
146
impl 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
72473456
    pub fn doc_digest(&self) -> &DocDigest {
154
72473456
        ns_expr!(&self.r.doc_digest, &self.m, &self.r.doc_digest,)
155
72473456
    }
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.
162
pub(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
474
    pub(crate) fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<DocDigest, AE> {
170
474
        let data = args
171
474
            .next()
172
474
            .ok_or(AE::Missing)?
173
474
            .parse::<B64>()
174
474
            .map_err(|_| AE::Invalid)?;
175
474
        data.into_array().map_err(|_| AE::Invalid)
176
474
    }
177
}