1
//! router status entries - items that vary by consensus flavor
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_flavor`.
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
impl RouterStatus {
16
    /// Return an iterator of ORPort addresses for this routerstatus
17
155501644
    pub fn addrs(&self) -> impl Iterator<Item = net::SocketAddr> {
18
155501644
        chain!(
19
155501644
            [std::net::SocketAddrV4::new(self.r.ip, self.r.or_port).into()],
20
155501644
            self.a.iter().copied(),
21
        )
22
155501644
    }
23
    /// Return the declared weight of this routerstatus in the directory.
24
34417012
    pub fn weight(&self) -> &RelayWeight {
25
34417012
        &self.weight
26
34417012
    }
27
    /// Return the protovers that this routerstatus says it implements.
28
3330452
    pub fn protovers(&self) -> &Protocols {
29
3330452
        &self.protos
30
3330452
    }
31
    /// Return the nickname of this routerstatus.
32
    pub fn nickname(&self) -> &str {
33
        self.r.nickname.as_str()
34
    }
35
    /// Return the relay flags of this routerstatus.
36
    pub fn flags(&self) -> &RelayFlags {
37
        &self.flags.known
38
    }
39
    /// Return the version of this routerstatus.
40
    pub fn version(&self) -> Option<&crate::doc::netstatus::rs::SoftwareVersion> {
41
        self.version.as_ref()
42
    }
43
    /// Return true if the ed25519 identity on this relay reflects a
44
    /// true consensus among the authorities.
45
69557562
    pub fn ed25519_id_is_usable(&self) -> bool {
46
69557562
        !self.flags.contains(RelayFlag::NoEdConsensus)
47
69557562
    }
48
    /// Return true if this routerstatus is listed with the BadExit flag.
49
46909692
    pub fn is_flagged_bad_exit(&self) -> bool {
50
46909692
        self.flags.contains(RelayFlag::BadExit)
51
46909692
    }
52
    /// Return true if this routerstatus is listed with the v2dir flag.
53
35219394
    pub fn is_flagged_v2dir(&self) -> bool {
54
35219394
        self.flags.contains(RelayFlag::V2Dir)
55
35219394
    }
56
    /// Return true if this routerstatus is listed with the Exit flag.
57
34505028
    pub fn is_flagged_exit(&self) -> bool {
58
34505028
        self.flags.contains(RelayFlag::Exit)
59
34505028
    }
60
    /// Return true if this routerstatus is listed with the Guard flag.
61
36708714
    pub fn is_flagged_guard(&self) -> bool {
62
36708714
        self.flags.contains(RelayFlag::Guard)
63
36708714
    }
64
    /// Return true if this routerstatus is listed with the HSDir flag.
65
471690
    pub fn is_flagged_hsdir(&self) -> bool {
66
471690
        self.flags.contains(RelayFlag::HSDir)
67
471690
    }
68
    /// Return true if this routerstatus is listed with the Stable flag.
69
3954852
    pub fn is_flagged_stable(&self) -> bool {
70
3954852
        self.flags.contains(RelayFlag::Stable)
71
3954852
    }
72
    /// Return true if this routerstatus is listed with the Fast flag.
73
56527254
    pub fn is_flagged_fast(&self) -> bool {
74
56527254
        self.flags.contains(RelayFlag::Fast)
75
56527254
    }
76
    /// Return true if this routerstatus is listed with the MiddleOnly flag.
77
31752
    pub fn is_flagged_middle_only(&self) -> bool {
78
31752
        self.flags.contains(RelayFlag::MiddleOnly)
79
31752
    }
80
}
81

            
82
impl RouterStatus {
83
    /// Return RSA identity for the relay described by this RouterStatus
84
255586414
    pub fn rsa_identity(&self) -> &RsaIdentity {
85
255586414
        &self.r.identity
86
255586414
    }
87

            
88
    /// Return the networkstatus consensus flavor in which this
89
    /// routerstatus appears.
90
17744
    pub(crate) fn flavor() -> ConsensusFlavor {
91
17744
        FLAVOR
92
17744
    }
93

            
94
    /// Parse a generic routerstatus from a section.
95
    ///
96
    /// Requires that the section obeys the right SectionRules,
97
    /// matching `consensus_flavor`.
98
2116
    pub(crate) fn from_section(sec: &Section<'_, NetstatusKwd>) -> Result<RouterStatus> {
99
        use NetstatusKwd::*;
100
        // R line
101
2116
        let r_item = sec.required(RS_R)?;
102
2116
        let nickname = r_item.required_arg(0)?.parse().map_err(|e: InvalidNickname| {
103
            EK::BadArgument.with_msg(e.to_string()).at_pos(r_item.pos())
104
        })?;
105
2116
        let ident = r_item.required_arg(1)?;
106
2116
        let identity = ident.parse::<Base64Fingerprint>()?;
107
        // Fields to skip in the "r" line.
108
2116
        let n_skip = match FLAVOR {
109
2102
            ConsensusFlavor::Microdesc => 0,
110
14
            ConsensusFlavor::Plain => 1,
111
        };
112
        // We check that the published time is well-formed, but we never use it
113
        // for anything in a consensus document.
114
2116
        let _ignore_published: time::SystemTime = {
115
            // TODO: It's annoying to have to do this allocation, since we
116
            // already have a slice that contains both of these arguments.
117
            // Instead, we could get a slice of arguments: we'd have to add
118
            // a feature for that.
119
2116
            let mut p = r_item.required_arg(2 + n_skip)?.to_string();
120
2116
            p.push(' ');
121
2116
            p.push_str(r_item.required_arg(3 + n_skip)?);
122
2116
            p.parse::<Iso8601TimeSp>()?.into()
123
        };
124
2116
        let ip = r_item.required_arg(4 + n_skip)?.parse::<net::Ipv4Addr>()?;
125
2116
        let or_port = r_item.required_arg(5 + n_skip)?.parse::<u16>()?;
126
2116
        let _ = r_item.required_arg(6 + n_skip)?.parse::<u16>()?;
127

            
128
        // main address and A lines.
129
2116
        let a_items = sec.slice(RS_A);
130
2116
        let a = a_items
131
2116
            .iter()
132
2171
            .map(|a_item| Ok(a_item.required_arg(0)?.parse::<net::SocketAddr>()?))
133
2116
            .collect::<Result<Vec<_>>>()?;
134

            
135
        // S line
136
        //
137
        // Wrong for votes, but this code doesn't run for votes.
138
2116
        let flags = DocRelayFlags::from_item_consensus(sec.required(RS_S)?)?;
139

            
140
        // V line
141
2114
        let version = sec.maybe(RS_V).args_as_str().map(str::parse).transpose()?;
142

            
143
        // PR line
144
2114
        let protos = {
145
2114
            let tok = sec.required(RS_PR)?;
146
2114
            tok.args_as_str()
147
2114
                .parse::<Protocols>()
148
2114
                .map_err(|e| EK::BadArgument.at_pos(tok.pos()).with_source(e))?
149
        };
150

            
151
        // W line
152
2114
        let weight = sec
153
2114
            .get(RS_W)
154
2114
            .map(RelayWeight::from_item)
155
2114
            .transpose()?
156
2112
            .unwrap_or_default();
157

            
158
        // No p line
159
        // no ID line
160

            
161
        // Try to find the document digest.  This is in different
162
        // places depending on the kind of consensus we're in.
163
2112
        let doc_digest: DocDigest = match FLAVOR {
164
            ConsensusFlavor::Microdesc => {
165
                // M line
166
2098
                let m_item = sec.required(RS_M)?;
167
2098
                DocDigest::decode(m_item.required_arg(0)?)?
168
            }
169
14
            ConsensusFlavor::Plain => DocDigest::decode(r_item.required_arg(2)?)?,
170
        };
171

            
172
        ns_choose! { (
173
14
            let r_doc_digest = doc_digest;
174
14
            let m_doc_digest = NotPresent;
175
        ) (
176
2096
            let r_doc_digest = NotPresent;
177
2096
            let m_doc_digest = doc_digest;
178
        ) (
179
            let r_doc_digest = doc_digest;
180
            let m_doc_digest = NotPresent;
181
        ) };
182

            
183
2110
        Ok(RouterStatus {
184
2110
            r: RouterStatusIntroItem {
185
2110
                nickname,
186
2110
                identity,
187
2110
                or_port,
188
2110
                doc_digest: r_doc_digest,
189
2110
                publication: IgnoredPublicationTimeSp,
190
2110
                ip,
191
2110
            },
192
2110
            m: m_doc_digest,
193
2110
            a,
194
2110
            flags,
195
2110
            version,
196
2110
            protos,
197
2110
            weight,
198
2110
        })
199
2116
    }
200
}
201

            
202
impl FromRsString for DocDigest {
203
2112
    fn decode(s: &str) -> Result<DocDigest> {
204
2112
        s.parse::<B64>()?
205
2110
            .check_len(DOC_DIGEST_LEN..=DOC_DIGEST_LEN)?
206
2110
            .as_bytes()
207
2110
            .try_into()
208
2110
            .map_err(|_| Error::from(internal!("correct length on digest, but unable to convert")))
209
2112
    }
210
}