1
//! Routerstatus-specific parts of networkstatus parsing.
2
//!
3
//! This is a private module; relevant pieces are re-exported by its
4
//! parent.
5

            
6
#[cfg(feature = "build_docs")]
7
pub(crate) mod build;
8
mod md;
9
#[cfg(feature = "ns_consensus")]
10
mod ns;
11

            
12
use super::{NetstatusKwd, RelayFlags, RelayWeight};
13
use crate::parse::parser::Section;
14
use crate::types::misc::*;
15
use crate::types::version::TorVersion;
16
use crate::util::intern::InternCache;
17
use crate::{Error, ParseErrorKind as EK, Result};
18
use std::sync::Arc;
19
use std::{net, time};
20

            
21
use tor_llcrypto::pk::rsa::RsaIdentity;
22
use tor_protover::Protocols;
23

            
24
pub use md::MdConsensusRouterStatus;
25
#[cfg(feature = "ns_consensus")]
26
pub use ns::NsConsensusRouterStatus;
27

            
28
/// Shared implementation of MdConsensusRouterStatus and NsConsensusRouterStatus.
29
28165
#[derive(Debug, Clone)]
30
struct GenericRouterStatus<D> {
31
    /// The nickname for this relay.
32
    ///
33
    /// Nicknames can be used for convenience purpose, but no more:
34
    /// there is no mechanism to enforce their uniqueness.
35
    nickname: String,
36
    /// Fingerprint of the old-style RSA identity for this relay.
37
    identity: RsaIdentity,
38
    /// A list of address:port values where this relay can be reached.
39
    addrs: Vec<net::SocketAddr>,
40
    /// Digest of the document for this relay.
41
    doc_digest: D,
42
    /// Flags applied by the authorities to this relay.
43
    flags: RelayFlags,
44
    /// Version of the software that this relay is running.
45
    version: Option<Version>,
46
    /// List of subprotocol versions supported by this relay.
47
    protos: Protocols,
48
    /// Information about how to weight this relay when choosing a
49
    /// relay at random.
50
    weight: RelayWeight,
51
}
52

            
53
/// A version as presented in a router status.
54
///
55
/// This can either be a parsed Tor version, or an unparsed string.
56
//
57
// TODO: This might want to merge, at some point, with routerdesc::RelayPlatform.
58
1
#[derive(Clone, Debug, Eq, PartialEq, Hash, derive_more::Display)]
59
#[non_exhaustive]
60
pub enum Version {
61
    /// A Tor version
62
    Tor(TorVersion),
63
    /// A string we couldn't parse.
64
    Other(Arc<str>),
65
}
66

            
67
/// A cache of unparsable version strings.
68
///
69
/// We use this because we expect there not to be very many distinct versions of
70
/// relay software in existence.
71
static OTHER_VERSION_CACHE: InternCache<str> = InternCache::new();
72

            
73
impl std::str::FromStr for Version {
74
    type Err = Error;
75

            
76
679
    fn from_str(s: &str) -> Result<Self> {
77
679
        let mut elts = s.splitn(3, ' ');
78
679
        if elts.next() == Some("Tor") {
79
678
            if let Some(Ok(v)) = elts.next().map(str::parse) {
80
678
                return Ok(Version::Tor(v));
81
            }
82
1
        }
83

            
84
1
        Ok(Version::Other(OTHER_VERSION_CACHE.intern_ref(s)))
85
679
    }
86
}
87

            
88
/// Implement a set of accessor functions on a given routerstatus type.
89
// TODO: These methods should probably become, in whole or in part,
90
// methods on the RouterStatus trait.
91
macro_rules! implement_accessors {
92
    ($name:ident) => {
93
        impl $name {
94
            /// Return an iterator of ORPort addresses for this routerstatus
95
9966133
            pub fn orport_addrs(&self) -> impl Iterator<Item = &net::SocketAddr> {
96
9966133
                self.rs.addrs.iter()
97
9966133
            }
98
            /// Return the declared weight of this routerstatus in the directory.
99
5380434
            pub fn weight(&self) -> &RelayWeight {
100
5380434
                &self.rs.weight
101
5380434
            }
102
            /// Return the ORPort addresses of this routerstatus
103
220303
            pub fn addrs(&self) -> &[net::SocketAddr] {
104
220303
                &self.rs.addrs[..]
105
220303
            }
106
            /// Return the protovers that this routerstatus says it implements.
107
1024727
            pub fn protovers(&self) -> &Protocols {
108
1024727
                &self.rs.protos
109
1024727
            }
110
            /// Return the nickname of this routerstatus.
111
            pub fn nickname(&self) -> &String {
112
                &self.rs.nickname
113
            }
114
            /// Return the relay flags of this routerstatus.
115
            pub fn flags(&self) -> &RelayFlags {
116
                &self.rs.flags
117
            }
118
            /// Return the version of this routerstatus.
119
            pub fn version(&self) -> Option<&crate::doc::netstatus::rs::Version> {
120
                self.rs.version.as_ref()
121
            }
122
            /// Return true if the ed25519 identity on this relay reflects a
123
            /// true consensus among the authorities.
124
10685384
            pub fn ed25519_id_is_usable(&self) -> bool {
125
10685384
                !self.rs.flags.contains(RelayFlags::NO_ED_CONSENSUS)
126
10685384
            }
127
            /// Return true if this routerstatus is listed with the BadExit flag.
128
5791271
            pub fn is_flagged_bad_exit(&self) -> bool {
129
5791271
                self.rs.flags.contains(RelayFlags::BAD_EXIT)
130
5791271
            }
131
            /// Return true if this routerstatus is listed with the v2dir flag.
132
6187762
            pub fn is_flagged_v2dir(&self) -> bool {
133
6187762
                self.rs.flags.contains(RelayFlags::V2DIR)
134
6187762
            }
135
            /// Return true if this routerstatus is listed with the Exit flag.
136
5404283
            pub fn is_flagged_exit(&self) -> bool {
137
5404283
                self.rs.flags.contains(RelayFlags::EXIT)
138
5404283
            }
139
            /// Return true if this routerstatus is listed with the Guard flag.
140
7703720
            pub fn is_flagged_guard(&self) -> bool {
141
7703720
                self.rs.flags.contains(RelayFlags::GUARD)
142
7703720
            }
143
        }
144
    };
145
}
146

            
147
// Make the macro public in the crate.
148
pub(crate) use implement_accessors;
149

            
150
/// Helper to decode a document digest in the format in which it
151
/// appears in a given kind of routerstatus.
152
trait FromRsString: Sized {
153
    /// Try to decode the given object.
154
    fn decode(s: &str) -> Result<Self>;
155
}
156

            
157
impl<D> GenericRouterStatus<D>
158
where
159
    D: FromRsString,
160
{
161
    /// Parse a generic routerstatus from a section.
162
    ///
163
    /// Requires that the section obeys the right SectionRules,
164
    /// matching microdesc_format.
165
679
    fn from_section(
166
679
        sec: &Section<'_, NetstatusKwd>,
167
679
        microdesc_format: bool,
168
679
    ) -> Result<GenericRouterStatus<D>> {
169
        use NetstatusKwd::*;
170
        // R line
171
679
        let r_item = sec.required(RS_R)?;
172
679
        let nickname = r_item.required_arg(0)?.to_string();
173
679
        let ident = r_item.required_arg(1)?.parse::<B64>()?;
174
679
        let identity = RsaIdentity::from_bytes(ident.as_bytes()).ok_or_else(|| {
175
            EK::BadArgument
176
                .at_pos(r_item.pos())
177
                .with_msg("Wrong identity length")
178
679
        })?;
179
679
        let skip = if microdesc_format { 0 } else { 1 };
180
        // We check that the published time is well-formed, but we never use it
181
        // for anything in a consensus document.
182
679
        let _ignore_published: time::SystemTime = {
183
            // TODO: It's annoying to have to do this allocation, since we
184
            // already have a slice that contains both of these arguments.
185
            // Instead, we could get a slice of arguments: we'd have to add
186
            // a feature for that.
187
679
            let mut p = r_item.required_arg(2 + skip)?.to_string();
188
679
            p.push(' ');
189
679
            p.push_str(r_item.required_arg(3 + skip)?);
190
679
            p.parse::<Iso8601TimeSp>()?.into()
191
        };
192
679
        let ipv4addr = r_item.required_arg(4 + skip)?.parse::<net::Ipv4Addr>()?;
193
679
        let or_port = r_item.required_arg(5 + skip)?.parse::<u16>()?;
194
679
        let _ = r_item.required_arg(6 + skip)?.parse::<u16>()?;
195

            
196
        // main address and A lines.
197
679
        let a_items = sec.slice(RS_A);
198
679
        let mut addrs = Vec::with_capacity(1 + a_items.len());
199
679
        addrs.push(net::SocketAddr::V4(net::SocketAddrV4::new(
200
679
            ipv4addr, or_port,
201
679
        )));
202
1214
        for a_item in a_items {
203
535
            addrs.push(a_item.required_arg(0)?.parse::<net::SocketAddr>()?);
204
        }
205

            
206
        // S line
207
679
        let flags = RelayFlags::from_item(sec.required(RS_S)?)?;
208

            
209
        // V line
210
678
        let version = sec.maybe(RS_V).args_as_str().map(str::parse).transpose()?;
211

            
212
        // PR line
213
678
        let protos = {
214
678
            let tok = sec.required(RS_PR)?;
215
678
            tok.args_as_str()
216
678
                .parse::<Protocols>()
217
678
                .map_err(|e| EK::BadArgument.at_pos(tok.pos()).with_source(e))?
218
        };
219

            
220
        // W line
221
678
        let weight = sec
222
678
            .get(RS_W)
223
678
            .map(RelayWeight::from_item)
224
678
            .transpose()?
225
677
            .unwrap_or_default();
226

            
227
        // No p line
228
        // no ID line
229

            
230
        // Try to find the document digest.  This is in different
231
        // places depending on the kind of consensus we're in.
232
677
        let doc_digest: D = if microdesc_format {
233
            // M line
234
669
            let m_item = sec.required(RS_M)?;
235
669
            D::decode(m_item.required_arg(0)?)?
236
        } else {
237
8
            D::decode(r_item.required_arg(2)?)?
238
        };
239

            
240
676
        Ok(GenericRouterStatus {
241
676
            nickname,
242
676
            identity,
243
676
            addrs,
244
676
            doc_digest,
245
676
            flags,
246
676
            version,
247
676
            protos,
248
676
            weight,
249
676
        })
250
679
    }
251
}