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 field in `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 RouterStatus.m.
23
//
24
// We need to make this an import that can be used with `deftly(netdoc(with = ))`.
25
// `with` expects a path, not a type, so we can't use ns_type!.
26
//
27
// (Normally when trying to parse an item whose single field implements ItemArgumentParseable
28
// but not ItemValueParseable, we would use #[deftly(netdoc(single_arg))]
29
// but here we can't do that because we can't have variety-dependent attributes.)
30
ns_choose! { (
31
    use NotPresentEachValue as doc_digest_item_m;
32
) (
33
    // doc_digest_item_m implemented in rs/md.rs
34
) (
35
    use RouterStatusMdDigestsVote as doc_digest_item_m;
36
) }
37

            
38
/// Type of the referenced document digest in form suitable for parsing and encoding
39
type DocDigestB64 = FixedB64<DOC_DIGEST_LEN>;
40

            
41
/// Intro item for a router status entry
42
///
43
/// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:r>
44
///
45
/// <https://spec.torproject.org/dir-spec/computing-consensus.html#flavor:microdesc>
46
/// `r` item.
47
#[derive(Debug, Clone, Deftly)]
48
#[derive_deftly(Constructor, ItemValueEncodable, ItemValueParseable)]
49
#[allow(clippy::exhaustive_structs)]
50
pub struct RouterStatusIntroItem {
51
    /// The nickname for this relay.
52
    ///
53
    /// Nicknames can be used for convenience purpose, but no more:
54
    /// there is no mechanism to enforce their uniqueness.
55
    #[deftly(constructor)]
56
    pub nickname: Nickname,
57

            
58
    /// Fingerprint of the old-style RSA identity for this relay.
59
    #[deftly(constructor)]
60
    pub identity: Base64Fingerprint,
61

            
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(constructor)]
66
    pub doc_digest: ns_type!(DocDigestB64, NotPresent, DocDigestB64),
67

            
68
    /// Publication time.
69
    #[deftly(constructor)]
70
    pub publication: ns_type!(
71
        IgnoredPublicationTimeSp,
72
        IgnoredPublicationTimeSp,
73
        Iso8601TimeSp
74
    ),
75

            
76
    /// IPv4 address
77
    #[deftly(constructor)]
78
    pub ip: std::net::Ipv4Addr,
79

            
80
    /// Relay port
81
    pub or_port: u16,
82

            
83
    /// Directory port
84
    ///
85
    /// Always 0 when read by the old parser.
86
    pub dir_port: u16,
87

            
88
    #[doc(hidden)]
89
    #[deftly(netdoc(skip))]
90
    pub __non_exhaustive: (),
91
}
92

            
93
/// A single relay's status, in a network status document.
94
///
95
/// <https://spec.torproject.org/dir-spec/consensus-formats.html#section:router-status>
96
///
97
/// <https://spec.torproject.org/dir-spec/computing-consensus.html#flavor:microdesc>
98
/// under "Changes to router status entries".
99
//
100
// In most netdocs we would use the item keywords as the field names.  But routerstatus
101
// entry keywords are chosen to be very short to minimise the consensus size, so we
102
// use longer names in the struct and specify the keyword separately.
103
#[derive(Debug, Clone, Deftly)]
104
#[derive_deftly(Constructor, NetdocEncodable, NetdocParseable)]
105
#[allow(clippy::exhaustive_structs)]
106
pub struct RouterStatus {
107
    /// `r` --- Introduce a routerstatus entry
108
    ///
109
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:r>
110
    /// (and, the md version, which is different).
111
    #[deftly(constructor)]
112
    pub r: RouterStatusIntroItem,
113

            
114
    /// `m` --- Microdescriptor or document digest
115
    ///
116
    /// In an md consensus, the hash of the document for this relay.
117
    /// In a vote, microdescriptor hashes for the various consensus methods.
118
    ///
119
    /// <https://spec.torproject.org/dir-spec/computing-consensus.html#flavor:microdesc>
120
    /// `r` item.
121
    // We call this field `m` rather than `doc_digest` because it's not always the doc digest.
122
    // TODO SPEC in all but md consensuses the referenced document digest is in the `r` intro item
123
    //
124
    // TODO SPEC Adjust microdesc consensus `m` item position in the spec.
125
    // This item is here because this is where C Tor puts it.  
126
    #[deftly(constructor)]
127
    #[deftly(netdoc(with = doc_digest_item_m))]
128
    pub m: ns_type!(NotPresent, DocDigestB64, Vec<RouterStatusMdDigestsVote>),
129

            
130
    /// `a` --- Further router address(es) (IPv6)
131
    ///
132
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:a>
133
    /// (and, the md version, which is different).
134
    #[deftly(netdoc(single_arg))]
135
    pub a: Vec<net::SocketAddr>,
136

            
137
    /// `s` --- Router status flags
138
    ///
139
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:s>
140
    #[deftly(constructor)]
141
    #[deftly(netdoc(
142
        keyword = "s",
143
        with = {
144
            relay_flags::ParserEncoder::<ns_type!(
145
                relay_flags::ConsensusRepr,
146
                relay_flags::ConsensusRepr,
147
                relay_flags::NoImplicitRepr,
148
            )>
149
        },
150
    ))]
151
    pub flags: DocRelayFlags,
152

            
153
    /// `v` --- Relay's Tor software version
154
    ///
155
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:v>
156
    #[deftly(netdoc(keyword = "v"))]
157
    pub version: Option<SoftwareVersion>,
158

            
159
    /// `pr` --- Subprotocol capabilities supported
160
    ///
161
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:v>
162
    #[deftly(constructor)]
163
    #[deftly(netdoc(keyword = "pr", with = protovers_flexible))]
164
    pub protos: Protocols,
165

            
166
    /// `w` --- Bandwidth estimates
167
    ///
168
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:w>
169
    ///
170
    /// In `Constructor`, you can use `RelayWeightsItem::default()`
171
    /// if `retain-unknown` is enabled.
172
    // RelayWeightsItem is only Default (meaning "no `w` item") with retain-unknown.
173
    // But without that feature, that contains info not represented, so then it's not `Default`.
174
    // We mustn't have fields that are in Constructor if some feature is *missing*,
175
    // because that would be non-additive.
176
    #[deftly(constructor)]
177
    #[deftly(netdoc(flatten))]
178
    pub weight: ns_type!(RelayWeightsItem, RelayWeightsItem, VoteRelayWeightsItem),
179

            
180
    /// `p` --- Exit ports summary
181
    ///
182
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:p>
183
    ///
184
    /// This field is not properly parsed in plain consensuses by the old parser.
185
    #[deftly(netdoc(keyword = "p"))]
186
    pub port_policy: ns_type!(Option<Intern<PortPolicy>>, NotPresent, Option<Intern<PortPolicy>>),
187

            
188
    /// `id` --- Relay’s (ed25519) identity
189
    ///
190
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:id>
191
    #[deftly(constructor)]
192
    #[deftly(netdoc(keyword = "id"))] 
193
    pub ed25519_id: ns_type!(NotPresent, NotPresent, Ed25519IdentityLine),
194

            
195
    /// `stats` -- Statistics for this relay
196
    ///
197
    /// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:stats>
198
    pub stats: ns_type!(NotPresent, NotPresent, NetParams<F64Finite>),
199

            
200
    #[doc(hidden)]
201
    #[deftly(netdoc(skip))]
202
    pub __non_exhaustive: (),
203
}
204

            
205
impl RouterStatus {
206
    /// Return the digest of the document identified by this
207
    /// routerstatus.
208
    ///
209
    /// The `doc_digest` method is provided on all varieties of routerstatus entry
210
    /// to help paper over the protocol anomaly, that the digest is in a different place
211
    /// in md routerstatus entries.
212
71117733
    pub fn doc_digest(&self) -> &DocDigest {
213
71112804
        ns_expr!(&self.r.doc_digest, &self.m, &self.r.doc_digest,)
214
71117733
    }
215
}
216

            
217
impl EncodeOrd for RouterStatus {
218
30
    fn encode_cmp(&self, other: &Self) -> Ordering {
219
        // Type inference seems to need a *lot* of help here!
220
30
        let k: for <'i> fn(&'i RouterStatus) -> &'i _  = |rs| &rs .r.identity;
221
30
        EncodeOrd::encode_cmp(k(self), k(other))
222
30
    }
223
}