tor_netdoc/doc/
routerdesc.rs

1//!
2//! A "router descriptor" is a signed statement that a relay makes
3//! about itself, explaining its keys, its capabilities, its location,
4//! and its status.
5//!
6//! Relays upload their router descriptors to authorities, which use
7//! them to build consensus documents.  Old clients and relays used to
8//! fetch and use router descriptors for all the relays, but nowadays they use
9//! microdescriptors instead.
10//!
11//! Clients still use router descriptors when communicating with
12//! bridges: since bridges are not passed through an authority,
13//! clients accept their descriptors directly.
14//!
15//! For full information about the router descriptor format, see
16//! [dir-spec.txt](https://spec.torproject.org/dir-spec).
17//!
18//! # Limitations
19//!
20//! TODO: This needs to get tested much more!
21//!
22//! TODO: This implementation can be memory-inefficient.  In practice,
23//! it gets really expensive storing policy entries, family
24//! descriptions, parsed keys, and things like that.  We will probably want to
25//! de-duplicate those.
26//!
27//! TODO: There should be accessor functions for some or all of the
28//! fields in RouterDesc.  I'm deferring those until I know what they
29//! should be.
30//!
31//! # Availability
32//!
33//! Most of this module is only available when this crate is built with the
34//! `routerdesc` feature enabled.
35use crate::parse::keyword::Keyword;
36use crate::parse::parser::{Section, SectionRules};
37use crate::parse::tokenize::{ItemResult, NetDocReader};
38use crate::types::family::{RelayFamily, RelayFamilyId};
39use crate::types::misc::*;
40use crate::types::policy::*;
41use crate::types::version::TorVersion;
42use crate::util::PeekableIterator;
43use crate::{doc, AllowAnnotations, Error, NetdocErrorKind as EK, Result};
44
45use ll::pk::ed25519::Ed25519Identity;
46use once_cell::sync::Lazy;
47use std::sync::Arc;
48use std::{net, time};
49use tor_cert::CertType;
50use tor_checkable::{signed, timed, Timebound};
51use tor_error::{internal, into_internal};
52use tor_llcrypto as ll;
53use tor_llcrypto::pk::rsa::RsaIdentity;
54
55use digest::Digest;
56
57/// The digest of a RouterDesc document, as reported in a NS consensus.
58pub type RdDigest = [u8; 20];
59
60/// A router descriptor, with possible annotations.
61#[allow(dead_code)]
62#[cfg_attr(
63    feature = "dangerous-expose-struct-fields",
64    visible::StructFields(pub),
65    non_exhaustive
66)]
67pub struct AnnotatedRouterDesc {
68    /// Annotation for this router descriptor; possibly empty.
69    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
70    ann: RouterAnnotation,
71    /// Underlying router descriptor; signatures not checked yet.
72    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
73    router: UncheckedRouterDesc,
74}
75
76/// Annotations about a router descriptor, as stored on disc.
77#[allow(dead_code)] // don't warn about fields not getting read.
78#[cfg_attr(
79    feature = "dangerous-expose-struct-fields",
80    visible::StructFields(pub),
81    non_exhaustive
82)]
83#[derive(Default)]
84pub struct RouterAnnotation {
85    /// Description of where we got this router descriptor
86    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
87    source: Option<String>,
88    /// When this descriptor was first downloaded.
89    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
90    downloaded: Option<time::SystemTime>,
91    /// Description of what we're willing to use this descriptor for.
92    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
93    purpose: Option<String>,
94}
95
96/// Information about a relay, parsed from a router descriptor.
97///
98/// This type does not hold all the information in the router descriptor
99///
100/// # Limitations
101///
102/// See module documentation.
103///
104/// Additionally, some fields that from router descriptors are not yet
105/// parsed: see the comments in ROUTER_BODY_RULES for information about those.
106///
107/// Before using this type to connect to a relay, you MUST check that
108/// it is valid, using is_expired_at().
109#[allow(dead_code)] // don't warn about fields not getting read.
110#[cfg_attr(
111    feature = "dangerous-expose-struct-fields",
112    visible::StructFields(pub),
113    non_exhaustive
114)]
115#[derive(Clone, Debug)]
116pub struct RouterDesc {
117    /// Human-readable nickname for this relay.
118    ///
119    /// This is not secure, and not guaranteed to be unique.
120    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
121    nickname: Nickname,
122    /// IPv4 address for this relay.
123    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
124    ipv4addr: Option<net::Ipv4Addr>,
125    /// IPv4 ORPort for this relay.
126    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
127    orport: u16,
128    /// IPv6 address and port for this relay.
129    // TODO: we don't use a socketaddrv6 because we don't care about
130    // the flow and scope fields.  We should decide whether that's a
131    // good idea.
132    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
133    ipv6addr: Option<(net::Ipv6Addr, u16)>,
134    /// Directory port for contacting this relay for direct HTTP
135    /// directory downloads.
136    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
137    dirport: u16,
138    /// Declared uptime for this relay, in seconds.
139    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
140    uptime: Option<u64>,
141    /// Time when this router descriptor was published.
142    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
143    published: time::SystemTime,
144    /// Ed25519 identity certificate (identity key authenticating a
145    /// signing key)
146    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
147    identity_cert: tor_cert::Ed25519Cert,
148    /// RSA identity key for this relay. (Deprecated; never use this without
149    /// the ed25519 identity as well).
150    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
151    rsa_identity_key: ll::pk::rsa::PublicKey,
152    /// RSA identity key for this relay. (Deprecated; never use this without
153    /// the ed25519 identity as well).
154    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
155    rsa_identity: ll::pk::rsa::RsaIdentity,
156    /// Key for extending a circuit to this relay using the ntor protocol.
157    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
158    ntor_onion_key: ll::pk::curve25519::PublicKey,
159    /// Key for extending a circuit to this relay using the
160    /// (deprecated) TAP protocol.
161    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
162    tap_onion_key: Option<ll::pk::rsa::PublicKey>,
163    /// List of subprotocol versions supported by this relay.
164    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
165    proto: Arc<tor_protover::Protocols>,
166    /// True if this relay says it's a directory cache.
167    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
168    is_dircache: bool,
169    /// True if this relay says that it caches extrainfo documents.
170    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
171    is_extrainfo_cache: bool,
172    /// Declared family members for this relay.  If two relays are in the
173    /// same family, they shouldn't be used in the same circuit.
174    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
175    family: Arc<RelayFamily>,
176    /// Declared (and proven) family IDs for this relay. If two relays
177    /// share a family ID, they shouldn't be used in the same circuit.
178    family_ids: Vec<RelayFamilyId>,
179    /// Software and version that this relay says it's running.
180    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
181    platform: Option<RelayPlatform>,
182    /// A complete address-level policy for which IPv4 addresses this relay
183    /// says it supports.
184    // TODO: these polices can get bulky too. Perhaps we should
185    // de-duplicate them too.
186    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
187    ipv4_policy: AddrPolicy,
188    /// A summary of which ports this relay is willing to connect to
189    /// on IPv6.
190    #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
191    ipv6_policy: Arc<PortPolicy>,
192}
193
194/// Description of the software a relay is running.
195#[derive(Debug, Clone, PartialEq, Eq)]
196#[non_exhaustive]
197pub enum RelayPlatform {
198    /// Software advertised to be some version of Tor, on some platform.
199    Tor(TorVersion, String),
200    /// Software not advertised to be Tor.
201    Other(String),
202}
203
204impl std::str::FromStr for RelayPlatform {
205    type Err = Error;
206    fn from_str(args: &str) -> Result<Self> {
207        if args.starts_with("Tor ") {
208            let v: Vec<_> = args.splitn(4, ' ').collect();
209            match &v[..] {
210                ["Tor", ver, "on", p] => Ok(RelayPlatform::Tor(ver.parse()?, (*p).to_string())),
211                ["Tor", ver, ..] => Ok(RelayPlatform::Tor(ver.parse()?, "".to_string())),
212                _ => unreachable!(),
213            }
214        } else {
215            Ok(RelayPlatform::Other(args.to_string()))
216        }
217    }
218}
219
220decl_keyword! {
221    /// RouterKwd is an instance of Keyword, used to denote the different
222    /// Items that are recognized as appearing in a router descriptor.
223    RouterKwd {
224        annotation "@source" => ANN_SOURCE,
225        annotation "@downloaded-at" => ANN_DOWNLOADED_AT,
226        annotation "@purpose" => ANN_PURPOSE,
227        "accept" | "reject" => POLICY,
228        "bandwidth" => BANDWIDTH,
229        "bridge-distribution-request" => BRIDGE_DISTRIBUTION_REQUEST,
230        "caches-extra-info" => CACHES_EXTRA_INFO,
231        "contact" => CONTACT,
232        "extra-info-digest" => EXTRA_INFO_DIGEST,
233        "family" => FAMILY,
234        "family-cert" => FAMILY_CERT,
235        "fingerprint" => FINGERPRINT,
236        "hibernating" => HIBERNATING,
237        "identity-ed25519" => IDENTITY_ED25519,
238        "ipv6-policy" => IPV6_POLICY,
239        "master-key-ed25519" => MASTER_KEY_ED25519,
240        "ntor-onion-key" => NTOR_ONION_KEY,
241        "ntor-onion-key-crosscert" => NTOR_ONION_KEY_CROSSCERT,
242        "onion-key" => ONION_KEY,
243        "onion-key-crosscert" => ONION_KEY_CROSSCERT,
244        "or-address" => OR_ADDRESS,
245        "platform" => PLATFORM,
246        "proto" => PROTO,
247        "published" => PUBLISHED,
248        "router" => ROUTER,
249        "router-sig-ed25519" => ROUTER_SIG_ED25519,
250        "router-signature" => ROUTER_SIGNATURE,
251        "signing-key" => SIGNING_KEY,
252        "tunnelled_dir_server" => TUNNELLED_DIR_SERVER,
253        "uptime" => UPTIME,
254        // "protocols" once existed, but is obsolete
255        // "eventdns" once existed, but is obsolete
256        // "allow-single-hop-exits" is also obsolete.
257    }
258}
259
260/// Rules for parsing a set of router descriptor annotations.
261static ROUTER_ANNOTATIONS: Lazy<SectionRules<RouterKwd>> = Lazy::new(|| {
262    use RouterKwd::*;
263
264    let mut rules = SectionRules::builder();
265    rules.add(ANN_SOURCE.rule());
266    rules.add(ANN_DOWNLOADED_AT.rule().args(1..));
267    rules.add(ANN_PURPOSE.rule().args(1..));
268    rules.add(ANN_UNRECOGNIZED.rule().may_repeat().obj_optional());
269    // Unrecognized annotations are fine; anything else is an error in this
270    // context.
271    rules.reject_unrecognized();
272    rules.build()
273});
274/// Rules for tokens that are allowed in the first part of a
275/// router descriptor.
276static ROUTER_HEADER_RULES: Lazy<SectionRules<RouterKwd>> = Lazy::new(|| {
277    use RouterKwd::*;
278
279    let mut rules = SectionRules::builder();
280    rules.add(ROUTER.rule().required().args(5..));
281    rules.add(IDENTITY_ED25519.rule().required().no_args().obj_required());
282    // No other intervening tokens are permitted in the header.
283    rules.reject_unrecognized();
284    rules.build()
285});
286/// Rules for  tokens that are allowed in the first part of a
287/// router descriptor.
288static ROUTER_BODY_RULES: Lazy<SectionRules<RouterKwd>> = Lazy::new(|| {
289    use RouterKwd::*;
290
291    let mut rules = SectionRules::builder();
292    rules.add(MASTER_KEY_ED25519.rule().required().args(1..));
293    rules.add(PLATFORM.rule());
294    rules.add(PUBLISHED.rule().required());
295    rules.add(FINGERPRINT.rule());
296    rules.add(UPTIME.rule().args(1..));
297    rules.add(ONION_KEY.rule().no_args().obj_required());
298    rules.add(ONION_KEY_CROSSCERT.rule().no_args().obj_required());
299    rules.add(NTOR_ONION_KEY.rule().required().args(1..));
300    rules.add(
301        NTOR_ONION_KEY_CROSSCERT
302            .rule()
303            .required()
304            .args(1..=1)
305            .obj_required(),
306    );
307    rules.add(SIGNING_KEY.rule().no_args().required().obj_required());
308    rules.add(POLICY.rule().may_repeat().args(1..));
309    rules.add(IPV6_POLICY.rule().args(2..));
310    rules.add(FAMILY.rule().args(1..));
311    rules.add(FAMILY_CERT.rule().obj_required().may_repeat());
312    rules.add(CACHES_EXTRA_INFO.rule().no_args());
313    rules.add(OR_ADDRESS.rule().may_repeat().args(1..));
314    rules.add(TUNNELLED_DIR_SERVER.rule());
315    rules.add(PROTO.rule().required().args(1..));
316    rules.add(UNRECOGNIZED.rule().may_repeat().obj_optional());
317    // TODO: these aren't parsed yet.  Only authorities use them.
318    {
319        rules.add(BANDWIDTH.rule().required().args(3..));
320        rules.add(BRIDGE_DISTRIBUTION_REQUEST.rule().args(1..));
321        rules.add(HIBERNATING.rule().args(1..));
322        rules.add(CONTACT.rule());
323    }
324    // TODO: this is ignored for now.
325    {
326        rules.add(EXTRA_INFO_DIGEST.rule().args(1..));
327    }
328    rules.build()
329});
330
331/// Rules for items that appear at the end of a router descriptor.
332static ROUTER_SIG_RULES: Lazy<SectionRules<RouterKwd>> = Lazy::new(|| {
333    use RouterKwd::*;
334
335    let mut rules = SectionRules::builder();
336    rules.add(ROUTER_SIG_ED25519.rule().required().args(1..));
337    rules.add(ROUTER_SIGNATURE.rule().required().no_args().obj_required());
338    // No intervening tokens are allowed in the footer.
339    rules.reject_unrecognized();
340    rules.build()
341});
342
343impl RouterAnnotation {
344    /// Extract a single RouterAnnotation (possibly empty) from a reader.
345    fn take_from_reader(reader: &mut NetDocReader<'_, RouterKwd>) -> Result<RouterAnnotation> {
346        use RouterKwd::*;
347        let mut items = reader.pause_at(|item| item.is_ok_with_non_annotation());
348
349        let body = ROUTER_ANNOTATIONS.parse(&mut items)?;
350
351        let source = body.maybe(ANN_SOURCE).args_as_str().map(String::from);
352        let purpose = body.maybe(ANN_PURPOSE).args_as_str().map(String::from);
353        let downloaded = body
354            .maybe(ANN_DOWNLOADED_AT)
355            .parse_args_as_str::<Iso8601TimeSp>()?
356            .map(|t| t.into());
357        Ok(RouterAnnotation {
358            source,
359            downloaded,
360            purpose,
361        })
362    }
363}
364
365/// A parsed router descriptor whose signatures and/or validity times
366/// may or may not be invalid.
367pub type UncheckedRouterDesc = signed::SignatureGated<timed::TimerangeBound<RouterDesc>>;
368
369/// How long after its published time is a router descriptor officially
370/// supposed to be usable?
371const ROUTER_EXPIRY_SECONDS: u64 = 5 * 86400;
372
373/// How long before its published time is a router descriptor usable?
374// TODO(nickm): This valid doesn't match C tor, which only enforces this rule
375// ("routers should not some from the future") at directory authorities, and
376// there only enforces a 12-hour limit (`ROUTER_ALLOW_SKEW`).  Eventually we
377// should probably harmonize these cutoffs.
378const ROUTER_PRE_VALIDITY_SECONDS: u64 = 86400;
379
380impl RouterDesc {
381    /// Return a reference to this relay's RSA identity.
382    pub fn rsa_identity(&self) -> &RsaIdentity {
383        &self.rsa_identity
384    }
385
386    /// Return a reference to this relay's Ed25519 identity.
387    pub fn ed_identity(&self) -> &Ed25519Identity {
388        self.identity_cert
389            .signing_key()
390            .expect("No ed25519 identity key on identity cert")
391    }
392
393    /// Return a reference to the list of subprotocol versions supported by this
394    /// relay.
395    pub fn protocols(&self) -> &tor_protover::Protocols {
396        self.proto.as_ref()
397    }
398
399    /// Return a reference to this relay's Ntor onion key.
400    pub fn ntor_onion_key(&self) -> &ll::pk::curve25519::PublicKey {
401        &self.ntor_onion_key
402    }
403
404    /// Return the publication
405    pub fn published(&self) -> time::SystemTime {
406        self.published
407    }
408
409    /// Return an iterator of every `SocketAddr` at which this descriptor says
410    /// its relay can be reached.
411    pub fn or_ports(&self) -> impl Iterator<Item = net::SocketAddr> + '_ {
412        self.ipv4addr
413            .map(|a| net::SocketAddr::new(a.into(), self.orport))
414            .into_iter()
415            .chain(self.ipv6addr.map(net::SocketAddr::from))
416    }
417
418    /// Return the declared family of this descriptor.
419    pub fn family(&self) -> Arc<RelayFamily> {
420        Arc::clone(&self.family)
421    }
422
423    /// Return the authenticated family IDs of this descriptor.
424    pub fn family_ids(&self) -> &[RelayFamilyId] {
425        &self.family_ids[..]
426    }
427
428    /// Helper: tokenize `s`, and divide it into three validated sections.
429    fn parse_sections<'a>(
430        reader: &mut NetDocReader<'a, RouterKwd>,
431    ) -> Result<(
432        Section<'a, RouterKwd>,
433        Section<'a, RouterKwd>,
434        Section<'a, RouterKwd>,
435    )> {
436        use RouterKwd::*;
437
438        // Parse everything up through the header.
439        let header = ROUTER_HEADER_RULES.parse(
440            reader.pause_at(|item| item.is_ok_with_kwd_not_in(&[ROUTER, IDENTITY_ED25519])),
441        )?;
442
443        // Parse everything up to but not including the signature.
444        let body =
445            ROUTER_BODY_RULES.parse(reader.pause_at(|item| {
446                item.is_ok_with_kwd_in(&[ROUTER_SIGNATURE, ROUTER_SIG_ED25519])
447            }))?;
448
449        // Parse the signature.
450        let sig = ROUTER_SIG_RULES.parse(reader.pause_at(|item| {
451            item.is_ok_with_annotation() || item.is_ok_with_kwd(ROUTER) || item.is_empty_line()
452        }))?;
453
454        Ok((header, body, sig))
455    }
456
457    /// Try to parse `s` as a router descriptor.
458    ///
459    /// Does not actually check liveness or signatures; you need to do that
460    /// yourself before you can do the output.
461    pub fn parse(s: &str) -> Result<UncheckedRouterDesc> {
462        let mut reader = crate::parse::tokenize::NetDocReader::new(s)?;
463        let result = Self::parse_internal(&mut reader).map_err(|e| e.within(s))?;
464        // We permit empty lines at the end of router descriptors, since there's
465        // a known issue in Tor relays that causes them to return them this way.
466        reader
467            .should_be_exhausted_but_for_empty_lines()
468            .map_err(|e| e.within(s))?;
469        Ok(result)
470    }
471
472    /// Helper: parse a router descriptor from `s`.
473    ///
474    /// This function does the same as parse(), but returns errors based on
475    /// byte-wise positions.  The parse() function converts such errors
476    /// into line-and-byte positions.
477    fn parse_internal(r: &mut NetDocReader<'_, RouterKwd>) -> Result<UncheckedRouterDesc> {
478        // TODO: This function is too long!  The little "paragraphs" here
479        // that parse one item at a time should be made into sub-functions.
480        use RouterKwd::*;
481
482        let s = r.str();
483        let (header, body, sig) = RouterDesc::parse_sections(r)?;
484
485        // Unwrap should be safe because inline `required` call should return
486        // `Error::MissingToken` if `ROUTER` is not `Ok`
487        #[allow(clippy::unwrap_used)]
488        let start_offset = header.required(ROUTER)?.offset_in(s).unwrap();
489
490        // ed25519 identity and signing key.
491        let (identity_cert, ed25519_signing_key) = {
492            let cert_tok = header.required(IDENTITY_ED25519)?;
493            // Unwrap should be safe because above `required` call should
494            // return `Error::MissingToken` if `IDENTITY_ED25519` is not `Ok`
495            #[allow(clippy::unwrap_used)]
496            if cert_tok.offset_in(s).unwrap() < start_offset {
497                return Err(EK::MisplacedToken
498                    .with_msg("identity-ed25519")
499                    .at_pos(cert_tok.pos()));
500            }
501            let cert: tor_cert::UncheckedCert = cert_tok
502                .parse_obj::<UnvalidatedEdCert>("ED25519 CERT")?
503                .check_cert_type(tor_cert::CertType::IDENTITY_V_SIGNING)?
504                .into_unchecked()
505                .should_have_signing_key()
506                .map_err(|err| {
507                    EK::BadObjectVal
508                        .err()
509                        .with_source(err)
510                        .at_pos(cert_tok.pos())
511                })?;
512            let sk = *cert.peek_subject_key().as_ed25519().ok_or_else(|| {
513                EK::BadObjectVal
514                    .at_pos(cert_tok.pos())
515                    .with_msg("wrong type for signing key in cert")
516            })?;
517            let sk: ll::pk::ed25519::PublicKey = sk.try_into().map_err(|_| {
518                EK::BadObjectVal
519                    .at_pos(cert_tok.pos())
520                    .with_msg("invalid ed25519 signing key")
521            })?;
522            (cert, sk)
523        };
524
525        // master-key-ed25519: required, and should match certificate.
526        #[allow(unexpected_cfgs)]
527        {
528            let master_key_tok = body.required(MASTER_KEY_ED25519)?;
529            let ed_id: Ed25519Public = master_key_tok.parse_arg(0)?;
530            let ed_id: ll::pk::ed25519::Ed25519Identity = ed_id.into();
531            if ed_id != *identity_cert.peek_signing_key() {
532                #[cfg(not(fuzzing))] // No feature here; never omit in production.
533                return Err(EK::BadObjectVal
534                    .at_pos(master_key_tok.pos())
535                    .with_msg("master-key-ed25519 does not match key in identity-ed25519"));
536            }
537        }
538
539        // Legacy RSA identity
540        let rsa_identity_key: ll::pk::rsa::PublicKey = body
541            .required(SIGNING_KEY)?
542            .parse_obj::<RsaPublic>("RSA PUBLIC KEY")?
543            .check_len_eq(1024)?
544            .check_exponent(65537)?
545            .into();
546        let rsa_identity = rsa_identity_key.to_rsa_identity();
547
548        let ed_sig = sig.required(ROUTER_SIG_ED25519)?;
549        let rsa_sig = sig.required(ROUTER_SIGNATURE)?;
550        // Unwrap should be safe because above `required` calls should return
551        // an `Error::MissingToken` if `ROUTER_...` is not `Ok`
552        #[allow(clippy::unwrap_used)]
553        let ed_sig_pos = ed_sig.offset_in(s).unwrap();
554        #[allow(clippy::unwrap_used)]
555        let rsa_sig_pos = rsa_sig.offset_in(s).unwrap();
556
557        if ed_sig_pos > rsa_sig_pos {
558            return Err(EK::UnexpectedToken
559                .with_msg(ROUTER_SIG_ED25519.to_str())
560                .at_pos(ed_sig.pos()));
561        }
562
563        // Extract ed25519 signature.
564        let ed_signature: ll::pk::ed25519::ValidatableEd25519Signature = {
565            let mut d = ll::d::Sha256::new();
566            d.update(&b"Tor router descriptor signature v1"[..]);
567            let signed_end = ed_sig_pos + b"router-sig-ed25519 ".len();
568            d.update(&s[start_offset..signed_end]);
569            let d = d.finalize();
570            let sig: [u8; 64] = ed_sig
571                .parse_arg::<B64>(0)?
572                .into_array()
573                .map_err(|_| EK::BadSignature.at_pos(ed_sig.pos()))?;
574            let sig = ll::pk::ed25519::Signature::from(sig);
575            ll::pk::ed25519::ValidatableEd25519Signature::new(ed25519_signing_key, sig, &d)
576        };
577
578        // Extract legacy RSA signature.
579        let rsa_signature: ll::pk::rsa::ValidatableRsaSignature = {
580            let mut d = ll::d::Sha1::new();
581            let signed_end = rsa_sig_pos + b"router-signature\n".len();
582            d.update(&s[start_offset..signed_end]);
583            let d = d.finalize();
584            let sig = rsa_sig.obj("SIGNATURE")?;
585            // TODO: we need to accept prefixes here. COMPAT BLOCKER.
586
587            ll::pk::rsa::ValidatableRsaSignature::new(&rsa_identity_key, &sig, &d)
588        };
589
590        // router nickname ipv4addr orport socksport dirport
591        let (nickname, ipv4addr, orport, dirport) = {
592            let rtrline = header.required(ROUTER)?;
593            (
594                rtrline.parse_arg::<Nickname>(0)?,
595                Some(rtrline.parse_arg::<net::Ipv4Addr>(1)?),
596                rtrline.parse_arg(2)?,
597                // Skipping socksport.
598                rtrline.parse_arg(4)?,
599            )
600        };
601
602        // uptime
603        let uptime = body.maybe(UPTIME).parse_arg(0)?;
604
605        // published time.
606        let published = body
607            .required(PUBLISHED)?
608            .args_as_str()
609            .parse::<Iso8601TimeSp>()?
610            .into();
611
612        // ntor key
613        let ntor_onion_key: Curve25519Public = body.required(NTOR_ONION_KEY)?.parse_arg(0)?;
614        let ntor_onion_key: ll::pk::curve25519::PublicKey = ntor_onion_key.into();
615        // ntor crosscert
616        let crosscert_cert: tor_cert::UncheckedCert = {
617            let cc = body.required(NTOR_ONION_KEY_CROSSCERT)?;
618            let sign: u8 = cc.parse_arg(0)?;
619            if sign != 0 && sign != 1 {
620                return Err(EK::BadArgument.at_pos(cc.arg_pos(0)).with_msg("not 0 or 1"));
621            }
622            let ntor_as_ed: ll::pk::ed25519::PublicKey =
623                ll::pk::keymanip::convert_curve25519_to_ed25519_public(&ntor_onion_key, sign)
624                    .ok_or_else(|| {
625                        EK::BadArgument
626                            .at_pos(cc.pos())
627                            .with_msg("Uncheckable crosscert")
628                    })?;
629
630            cc.parse_obj::<UnvalidatedEdCert>("ED25519 CERT")?
631                .check_cert_type(tor_cert::CertType::NTOR_CC_IDENTITY)?
632                .check_subject_key_is(identity_cert.peek_signing_key())?
633                .into_unchecked()
634                .should_be_signed_with(&ntor_as_ed.into())
635                .map_err(|err| EK::BadSignature.err().with_source(err))?
636        };
637
638        // TAP key
639        let tap_onion_key: Option<ll::pk::rsa::PublicKey> = if let Some(tok) = body.get(ONION_KEY) {
640            Some(
641                tok.parse_obj::<RsaPublic>("RSA PUBLIC KEY")?
642                    .check_len_eq(1024)?
643                    .check_exponent(65537)?
644                    .into(),
645            )
646        } else {
647            None
648        };
649
650        // TAP crosscert
651        let tap_crosscert_sig = if let Some(cc_tok) = body.get(ONION_KEY_CROSSCERT) {
652            let cc_val = cc_tok.obj("CROSSCERT")?;
653            let mut signed = Vec::new();
654            signed.extend(rsa_identity.as_bytes());
655            signed.extend(identity_cert.peek_signing_key().as_bytes());
656            Some(ll::pk::rsa::ValidatableRsaSignature::new(
657                tap_onion_key.as_ref().ok_or_else(|| {
658                    EK::MissingToken.with_msg("onion-key-crosscert without onion-key")
659                })?,
660                &cc_val,
661                &signed,
662            ))
663        } else if tap_onion_key.is_some() {
664            return Err(EK::MissingToken.with_msg("onion-key without onion-key-crosscert"));
665        } else {
666            None
667        };
668
669        // List of subprotocol versions
670        let proto = {
671            let proto_tok = body.required(PROTO)?;
672            doc::PROTOVERS_CACHE.intern(
673                proto_tok
674                    .args_as_str()
675                    .parse::<tor_protover::Protocols>()
676                    .map_err(|e| EK::BadArgument.at_pos(proto_tok.pos()).with_source(e))?,
677            )
678        };
679
680        // tunneled-dir-server
681        let is_dircache = (dirport != 0) || body.get(TUNNELLED_DIR_SERVER).is_some();
682
683        // caches-extra-info
684        let is_extrainfo_cache = body.get(CACHES_EXTRA_INFO).is_some();
685
686        // fingerprint: check for consistency with RSA identity.
687        if let Some(fp_tok) = body.get(FINGERPRINT) {
688            let fp: RsaIdentity = fp_tok.args_as_str().parse::<SpFingerprint>()?.into();
689            if fp != rsa_identity {
690                return Err(EK::BadArgument
691                    .at_pos(fp_tok.pos())
692                    .with_msg("fingerprint does not match RSA identity"));
693            }
694        }
695
696        // Family
697        let family = {
698            let mut family = body
699                .maybe(FAMILY)
700                .parse_args_as_str::<RelayFamily>()?
701                .unwrap_or_else(RelayFamily::new);
702            if !family.is_empty() {
703                // If this family is nonempty, we add our own RSA id to it, on
704                // the theory that doing so will improve the odds of having a
705                // canonical family shared by all of the members of this family.
706                // If the family is empty, there's no point in adding our own ID
707                // to it, and doing so would only waste memory.
708                family.push(rsa_identity);
709            }
710            family.intern()
711        };
712
713        // Family ids (for "happy families")
714        let family_certs: Vec<tor_cert::UncheckedCert> = body
715            .slice(FAMILY_CERT)
716            .iter()
717            .map(|ent| {
718                ent.parse_obj::<UnvalidatedEdCert>("FAMILY CERT")?
719                    .check_cert_type(CertType::FAMILY_V_IDENTITY)?
720                    .check_subject_key_is(identity_cert.peek_signing_key())?
721                    .into_unchecked()
722                    .should_have_signing_key()
723                    .map_err(|e| {
724                        EK::BadObjectVal
725                            .with_msg("missing public key")
726                            .at_pos(ent.pos())
727                            .with_source(e)
728                    })
729            })
730            .collect::<Result<_>>()?;
731
732        let mut family_ids: Vec<_> = family_certs
733            .iter()
734            .map(|cert| RelayFamilyId::Ed25519(*cert.peek_signing_key()))
735            .collect();
736        family_ids.sort();
737        family_ids.dedup();
738
739        // or-address
740        // Extract at most one ipv6 address from the list.  It's not great,
741        // but it's what Tor does.
742        let mut ipv6addr = None;
743        for tok in body.slice(OR_ADDRESS) {
744            if let Ok(net::SocketAddr::V6(a)) = tok.parse_arg::<net::SocketAddr>(0) {
745                ipv6addr = Some((*a.ip(), a.port()));
746                break;
747            }
748            // We skip over unparsable addresses. Is that right?
749        }
750
751        // platform
752        let platform = body.maybe(PLATFORM).parse_args_as_str::<RelayPlatform>()?;
753
754        // ipv4_policy
755        let ipv4_policy = {
756            let mut pol = AddrPolicy::new();
757            for ruletok in body.slice(POLICY).iter() {
758                let accept = match ruletok.kwd_str() {
759                    "accept" => RuleKind::Accept,
760                    "reject" => RuleKind::Reject,
761                    _ => {
762                        return Err(Error::from(internal!(
763                            "tried to parse a strange line as a policy"
764                        ))
765                        .at_pos(ruletok.pos()))
766                    }
767                };
768                let pat: AddrPortPattern = ruletok
769                    .args_as_str()
770                    .parse()
771                    .map_err(|e| EK::BadPolicy.at_pos(ruletok.pos()).with_source(e))?;
772                pol.push(accept, pat);
773            }
774            pol
775        };
776
777        // ipv6 policy
778        let ipv6_policy = match body.get(IPV6_POLICY) {
779            Some(p) => p
780                .args_as_str()
781                .parse()
782                .map_err(|e| EK::BadPolicy.at_pos(p.pos()).with_source(e))?,
783            // Unwrap is safe here because str is not empty
784            #[allow(clippy::unwrap_used)]
785            None => "reject 1-65535".parse::<PortPolicy>().unwrap(),
786        };
787
788        // Now we're going to collect signatures and expiration times.
789        let (identity_cert, identity_sig) = identity_cert.dangerously_split().map_err(|err| {
790            EK::BadObjectVal
791                .with_msg("missing public key")
792                .with_source(err)
793        })?;
794        let (crosscert_cert, cc_sig) = crosscert_cert.dangerously_split().map_err(|err| {
795            EK::BadObjectVal
796                .with_msg("missing public key")
797                .with_source(err)
798        })?;
799        let mut signatures: Vec<Box<dyn ll::pk::ValidatableSignature>> = vec![
800            Box::new(rsa_signature),
801            Box::new(ed_signature),
802            Box::new(identity_sig),
803            Box::new(cc_sig),
804        ];
805        if let Some(s) = tap_crosscert_sig {
806            signatures.push(Box::new(s));
807        }
808
809        let identity_cert = identity_cert.dangerously_assume_timely();
810        let crosscert_cert = crosscert_cert.dangerously_assume_timely();
811        let mut expirations = vec![
812            published + time::Duration::new(ROUTER_EXPIRY_SECONDS, 0),
813            identity_cert.expiry(),
814            crosscert_cert.expiry(),
815        ];
816
817        for cert in family_certs {
818            let (inner, sig) = cert.dangerously_split().map_err(into_internal!(
819                "Missing a public key that was previously there."
820            ))?;
821            signatures.push(Box::new(sig));
822            expirations.push(inner.dangerously_assume_timely().expiry());
823        }
824
825        // Unwrap is safe here because `expirations` array is not empty
826        #[allow(clippy::unwrap_used)]
827        let expiry = *expirations.iter().min().unwrap();
828
829        let start_time = published - time::Duration::new(ROUTER_PRE_VALIDITY_SECONDS, 0);
830
831        let desc = RouterDesc {
832            nickname,
833            ipv4addr,
834            orport,
835            ipv6addr,
836            dirport,
837            uptime,
838            published,
839            identity_cert,
840            rsa_identity_key,
841            rsa_identity,
842            ntor_onion_key,
843            tap_onion_key,
844            proto,
845            is_dircache,
846            is_extrainfo_cache,
847            family,
848            family_ids,
849            platform,
850            ipv4_policy,
851            ipv6_policy: ipv6_policy.intern(),
852        };
853
854        let time_gated = timed::TimerangeBound::new(desc, start_time..expiry);
855        let sig_gated = signed::SignatureGated::new(time_gated, signatures);
856
857        Ok(sig_gated)
858    }
859}
860
861/// An iterator that parses one or more (possibly annotated
862/// router descriptors from a string.
863//
864// TODO: This is largely copy-pasted from MicrodescReader. Can/should they
865// be merged?
866pub struct RouterReader<'a> {
867    /// True iff we accept annotations
868    annotated: bool,
869    /// Reader that we're extracting items from.
870    reader: NetDocReader<'a, RouterKwd>,
871}
872
873/// Skip this reader forward until the next thing it reads looks like the
874/// start of a router descriptor.
875///
876/// Used to recover from errors.
877fn advance_to_next_routerdesc(reader: &mut NetDocReader<'_, RouterKwd>, annotated: bool) {
878    use RouterKwd::*;
879    loop {
880        let item = reader.peek();
881        match item {
882            Some(Ok(t)) => {
883                let kwd = t.kwd();
884                if (annotated && kwd.is_annotation()) || kwd == ROUTER {
885                    return;
886                }
887            }
888            Some(Err(_)) => {
889                // Skip over broken tokens.
890            }
891            None => {
892                return;
893            }
894        }
895        let _ = reader.next();
896    }
897}
898
899impl<'a> RouterReader<'a> {
900    /// Construct a RouterReader to take router descriptors from a string.
901    pub fn new(s: &'a str, allow: &AllowAnnotations) -> Result<Self> {
902        let reader = NetDocReader::new(s)?;
903        let annotated = allow == &AllowAnnotations::AnnotationsAllowed;
904        Ok(RouterReader { annotated, reader })
905    }
906
907    /// Extract an annotation from this reader.
908    fn take_annotation(&mut self) -> Result<RouterAnnotation> {
909        if self.annotated {
910            RouterAnnotation::take_from_reader(&mut self.reader)
911        } else {
912            Ok(RouterAnnotation::default())
913        }
914    }
915
916    /// Extract an annotated router descriptor from this reader
917    ///
918    /// (internal helper; does not clean up on failures.)
919    fn take_annotated_routerdesc_raw(&mut self) -> Result<AnnotatedRouterDesc> {
920        let ann = self.take_annotation()?;
921        let router = RouterDesc::parse_internal(&mut self.reader)?;
922        Ok(AnnotatedRouterDesc { ann, router })
923    }
924
925    /// Extract an annotated router descriptor from this reader
926    ///
927    /// Ensure that at least one token is consumed
928    fn take_annotated_routerdesc(&mut self) -> Result<AnnotatedRouterDesc> {
929        let pos_orig = self.reader.pos();
930        let result = self.take_annotated_routerdesc_raw();
931        if result.is_err() {
932            if self.reader.pos() == pos_orig {
933                // No tokens were consumed from the reader.  We need
934                // to drop at least one token to ensure we aren't in
935                // an infinite loop.
936                //
937                // (This might not be able to happen, but it's easier to
938                // explicitly catch this case than it is to prove that
939                // it's impossible.)
940                let _ = self.reader.next();
941            }
942            advance_to_next_routerdesc(&mut self.reader, self.annotated);
943        }
944        result
945    }
946}
947
948impl<'a> Iterator for RouterReader<'a> {
949    type Item = Result<AnnotatedRouterDesc>;
950    fn next(&mut self) -> Option<Self::Item> {
951        // Is there a next token? If not, we're done.
952        self.reader.peek()?;
953
954        Some(
955            self.take_annotated_routerdesc()
956                .map_err(|e| e.within(self.reader.str())),
957        )
958    }
959}
960
961#[cfg(test)]
962mod test {
963    // @@ begin test lint list maintained by maint/add_warning @@
964    #![allow(clippy::bool_assert_comparison)]
965    #![allow(clippy::clone_on_copy)]
966    #![allow(clippy::dbg_macro)]
967    #![allow(clippy::mixed_attributes_style)]
968    #![allow(clippy::print_stderr)]
969    #![allow(clippy::print_stdout)]
970    #![allow(clippy::single_char_pattern)]
971    #![allow(clippy::unwrap_used)]
972    #![allow(clippy::unchecked_duration_subtraction)]
973    #![allow(clippy::useless_vec)]
974    #![allow(clippy::needless_pass_by_value)]
975    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
976    use super::*;
977    const TESTDATA: &str = include_str!("../../testdata/routerdesc1.txt");
978    const TESTDATA2: &str = include_str!("../../testdata/routerdesc2.txt");
979    // Generated with a patched C tor to include "happy family" IDs.
980    const TESTDATA3: &str = include_str!("../../testdata/routerdesc3.txt");
981
982    fn read_bad(fname: &str) -> String {
983        use std::fs;
984        use std::path::PathBuf;
985        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
986        path.push("testdata");
987        path.push("bad-routerdesc");
988        path.push(fname);
989
990        fs::read_to_string(path).unwrap()
991    }
992
993    #[test]
994    fn parse_arbitrary() -> Result<()> {
995        use std::str::FromStr;
996        use tor_checkable::{SelfSigned, Timebound};
997        let rd = RouterDesc::parse(TESTDATA)?
998            .check_signature()?
999            .dangerously_assume_timely();
1000
1001        assert_eq!(rd.nickname.as_str(), "Akka");
1002        assert_eq!(rd.orport, 443);
1003        assert_eq!(rd.dirport, 0);
1004        assert_eq!(rd.uptime, Some(1036923));
1005        assert_eq!(
1006            rd.family.as_ref(),
1007            &RelayFamily::from_str(
1008                "$303509ab910ef207b7438c27435c4a2fd579f1b1 \
1009                 $56927e61b51e6f363fb55498150a6ddfcf7077f2"
1010            )
1011            .unwrap()
1012        );
1013
1014        assert_eq!(
1015            rd.rsa_identity().to_string(),
1016            "$56927e61b51e6f363fb55498150a6ddfcf7077f2"
1017        );
1018        assert_eq!(
1019            rd.ed_identity().to_string(),
1020            "CVTjf1oeaL616hH+1+UvYZ8OgkwF3z7UMITvJzm5r7A"
1021        );
1022        assert_eq!(
1023            rd.protocols().to_string(),
1024            "Cons=1-2 Desc=1-2 DirCache=2 FlowCtrl=1-2 HSDir=2 \
1025             HSIntro=4-5 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 \
1026             Padding=2 Relay=1-4"
1027        );
1028
1029        assert_eq!(
1030            hex::encode(rd.ntor_onion_key().to_bytes()),
1031            "329b3b52991613392e35d1a821dd6753e1210458ecc3337f7b7d39bfcf5da273"
1032        );
1033        assert_eq!(
1034            rd.published(),
1035            humantime::parse_rfc3339("2022-11-14T19:58:52Z").unwrap()
1036        );
1037        assert_eq!(
1038            rd.or_ports().collect::<Vec<_>>(),
1039            vec![
1040                "95.216.33.58:443".parse().unwrap(),
1041                "[2a01:4f9:2a:2145::2]:443".parse().unwrap(),
1042            ]
1043        );
1044        assert!(rd.tap_onion_key.is_some());
1045
1046        Ok(())
1047    }
1048
1049    #[test]
1050    fn parse_no_tap_key() -> Result<()> {
1051        use tor_checkable::{SelfSigned, Timebound};
1052        let rd = RouterDesc::parse(TESTDATA2)?
1053            .check_signature()?
1054            .dangerously_assume_timely();
1055        assert!(rd.tap_onion_key.is_none());
1056
1057        Ok(())
1058    }
1059
1060    #[test]
1061    fn test_bad() {
1062        use crate::types::policy::PolicyError;
1063        use crate::Pos;
1064        fn check(fname: &str, e: &Error) {
1065            let text = read_bad(fname);
1066            let rd = RouterDesc::parse(&text);
1067            assert!(rd.is_err());
1068            assert_eq!(&rd.err().unwrap(), e);
1069        }
1070
1071        check(
1072            "bad-sig-order",
1073            &EK::UnexpectedToken
1074                .with_msg("router-sig-ed25519")
1075                .at_pos(Pos::from_line(50, 1)),
1076        );
1077        check(
1078            "bad-start1",
1079            &EK::MisplacedToken
1080                .with_msg("identity-ed25519")
1081                .at_pos(Pos::from_line(1, 1)),
1082        );
1083        check("bad-start2", &EK::MissingToken.with_msg("identity-ed25519"));
1084        check(
1085            "mismatched-fp",
1086            &EK::BadArgument
1087                .at_pos(Pos::from_line(12, 1))
1088                .with_msg("fingerprint does not match RSA identity"),
1089        );
1090        check("no-ed-sk", &EK::MissingToken.with_msg("identity-ed25519"));
1091
1092        check(
1093            "bad-cc-sign",
1094            &EK::BadArgument
1095                .at_pos(Pos::from_line(34, 26))
1096                .with_msg("not 0 or 1"),
1097        );
1098        check(
1099            "bad-ipv6policy",
1100            &EK::BadPolicy
1101                .at_pos(Pos::from_line(43, 1))
1102                .with_source(PolicyError::InvalidPolicy),
1103        );
1104        check(
1105            "no-ed-id-key-in-cert",
1106            &EK::BadObjectVal
1107                .at_pos(Pos::from_line(2, 1))
1108                .with_source(tor_cert::CertError::MissingPubKey),
1109        );
1110        check(
1111            "non-ed-sk-in-cert",
1112            &EK::BadObjectVal
1113                .at_pos(Pos::from_line(2, 1))
1114                .with_msg("wrong type for signing key in cert"),
1115        );
1116        check(
1117            "bad-ed-sk-in-cert",
1118            &EK::BadObjectVal
1119                .at_pos(Pos::from_line(2, 1))
1120                .with_msg("invalid ed25519 signing key"),
1121        );
1122        check(
1123            "mismatched-ed-sk-in-cert",
1124            &EK::BadObjectVal
1125                .at_pos(Pos::from_line(8, 1))
1126                .with_msg("master-key-ed25519 does not match key in identity-ed25519"),
1127        );
1128    }
1129
1130    #[test]
1131    fn parse_multiple_annotated() {
1132        use crate::AllowAnnotations;
1133        let mut s = read_bad("bad-cc-sign");
1134        s += "\
1135@uploaded-at 2020-09-26 18:15:41
1136@source \"127.0.0.1\"
1137";
1138        s += TESTDATA;
1139        s += "\
1140@uploaded-at 2020-09-26 18:15:41
1141@source \"127.0.0.1\"
1142";
1143        s += &read_bad("mismatched-fp");
1144
1145        let rd = RouterReader::new(&s, &AllowAnnotations::AnnotationsAllowed).unwrap();
1146        let v: Vec<_> = rd.collect();
1147        assert!(v[0].is_err());
1148        assert!(v[1].is_ok());
1149        assert_eq!(
1150            v[1].as_ref().unwrap().ann.source,
1151            Some("\"127.0.0.1\"".to_string())
1152        );
1153        assert!(v[2].is_err());
1154    }
1155
1156    #[test]
1157    fn test_platform() {
1158        let p = "Tor 0.4.4.4-alpha on a flying bison".parse::<RelayPlatform>();
1159        assert!(p.is_ok());
1160        assert_eq!(
1161            p.unwrap(),
1162            RelayPlatform::Tor(
1163                "0.4.4.4-alpha".parse().unwrap(),
1164                "a flying bison".to_string()
1165            )
1166        );
1167
1168        let p = "Tor 0.4.4.4-alpha on".parse::<RelayPlatform>();
1169        assert!(p.is_ok());
1170
1171        let p = "Tor 0.4.4.4-alpha ".parse::<RelayPlatform>();
1172        assert!(p.is_ok());
1173        let p = "Tor 0.4.4.4-alpha".parse::<RelayPlatform>();
1174        assert!(p.is_ok());
1175
1176        let p = "arti 0.0.0".parse::<RelayPlatform>();
1177        assert!(p.is_ok());
1178        assert_eq!(p.unwrap(), RelayPlatform::Other("arti 0.0.0".to_string()));
1179    }
1180
1181    #[test]
1182    fn test_family_ids() -> Result<()> {
1183        use tor_checkable::{SelfSigned, Timebound};
1184        let rd = RouterDesc::parse(TESTDATA3)?
1185            .check_signature()?
1186            .dangerously_assume_timely();
1187
1188        assert_eq!(
1189            rd.family_ids(),
1190            &[
1191                "ed25519:7sToQRuge1bU2hS0CG0ViMndc4m82JhO4B4kdrQey80"
1192                    .parse()
1193                    .unwrap(),
1194                "ed25519:szHUS3ItRd9uk85b1UVnOZx1gg4B0266jCpbuIMNjcM"
1195                    .parse()
1196                    .unwrap(),
1197            ]
1198        );
1199
1200        Ok(())
1201    }
1202}