From eff4cf20a0d20831036b690451e454b7a28d070c Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Fri, 29 Nov 2024 14:38:56 +0100 Subject: [PATCH 01/15] Various documentation fixes (#307) --- src/ca/idexchange.rs | 14 +++++++------- src/ca/provisioning.rs | 10 +++++----- src/ca/publication.rs | 14 +++++++------- src/ca/sigmsg.rs | 7 +++++-- src/crypto/digest.rs | 4 ++-- src/crypto/keys.rs | 12 ++++++------ src/repository/cert.rs | 29 +++++++++++++---------------- src/repository/crl.rs | 8 ++------ src/repository/manifest.rs | 3 --- src/repository/resources/ipres.rs | 4 ++-- src/repository/resources/mod.rs | 2 -- src/repository/sigobj.rs | 6 +++--- src/rtr/mod.rs | 4 ++-- src/rtr/server.rs | 14 +++++--------- src/rtr/state.rs | 5 ----- 15 files changed, 59 insertions(+), 77 deletions(-) diff --git a/src/ca/idexchange.rs b/src/ca/idexchange.rs index 3a7927af..c5d5141e 100644 --- a/src/ca/idexchange.rs +++ b/src/ca/idexchange.rs @@ -482,7 +482,7 @@ impl ChildRequest { writer.done() } - /// Writes the ChildRequest's XML representation to a new Vec. + /// Writes the ChildRequest's XML representation to a new `Vec`. pub fn to_xml_vec(&self) -> Vec { let mut vec = vec![]; self.write_xml(&mut vec).unwrap(); // safe @@ -730,7 +730,7 @@ impl ParentResponse { validate_idcert_at(&self.id_cert, when) } - /// Writes the ParentResponse's XML representation to a new Vec. + /// Writes the ParentResponse's XML representation to a new `Vec`. pub fn to_xml_vec(&self) -> Vec { let mut vec = vec![]; self.write_xml(&mut vec).unwrap(); // safe @@ -756,12 +756,12 @@ impl fmt::Display for ParentResponse { //------------ PublisherRequest ---------------------------------------------- -/// Type representing a +/// Type representing a `` /// /// This is the XML message with identity information that a CA sends to a /// Publication Server. /// -/// For more info, see: https://tools.ietf.org/html/rfc8183#section-5.2.3 +/// For more info, see: #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct PublisherRequest { /// The self-signed IdCert containing the publisher's public key. @@ -912,7 +912,7 @@ impl PublisherRequest { writer.done() } - /// Writes the PublisherRequest's XML representation to a new Vec. + /// Writes the PublisherRequest's XML representation to a new `Vec`. pub fn to_xml_vec(&self) -> Vec { let mut vec = vec![]; self.write_xml(&mut vec).unwrap(); // safe @@ -945,7 +945,7 @@ impl fmt::Display for PublisherRequest { /// This is the response sent to a CA by the publication server. It contains /// the details needed by the CA to send publication messages to the server. /// -/// See https://tools.ietf.org/html/rfc8183#section-5.2.4 +/// See #[derive(Clone, Debug, Deserialize, Eq, Serialize, PartialEq)] pub struct RepositoryResponse { /// The Publication Server Identity Certificate @@ -1155,7 +1155,7 @@ impl RepositoryResponse { validate_idcert_at(&self.id_cert, when) } - /// Writes the RepositoryResponse's XML representation to a new Vec. + /// Writes the RepositoryResponse's XML representation to a new `Vec`. pub fn to_xml_vec(&self) -> Vec { let mut vec = vec![]; self.write_xml(&mut vec).unwrap(); // safe diff --git a/src/ca/provisioning.rs b/src/ca/provisioning.rs index 85c40a86..28631693 100644 --- a/src/ca/provisioning.rs +++ b/src/ca/provisioning.rs @@ -270,7 +270,7 @@ impl Message { /// # Decoding from XML /// impl Message { - /// Parses an RFC 6492 + /// Parses an RFC 6492 `` pub fn decode(reader: R) -> Result { let mut reader = xml::decode::Reader::new(reader); @@ -375,7 +375,7 @@ impl Payload { /// # Encoding to XML /// impl Payload { - /// Value for the type attribute in the element. + /// Value for the type attribute in the `` element. pub fn payload_type(&self) -> PayloadType { match self { Payload::List => PayloadType::List, @@ -723,7 +723,7 @@ impl IssuanceResponse { /// Note that the IssuanceRequest will be rejected by the parent, if the limit /// exceeds the child's entitlements. /// -/// See: https://tools.ietf.org/html/rfc6492#section-3.4.1 +/// See: #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct RequestResourceLimit { #[serde( @@ -998,7 +998,7 @@ impl From<&RevocationRequest> for RevocationResponse { //------------ KeyElement ---------------------------------------------------- -/// This type represents a element as used in both the Certificate +/// This type represents a `` element as used in both the Certificate /// Revocation Request and Response, sections 3.5.1 and 3.5.2 respectively, /// of RFC6492. #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -1096,7 +1096,7 @@ impl fmt::Display for KeyElement { /// This structure is what is called the "Resource Class List Response" /// in section 3.3.2 of RFC6492. /// -/// This response can have 0 or more elements containing the +/// This response can have 0 or more `` elements containing the /// entitlements for 0 or more corresponding resource classes. #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct ResourceClassListResponse { diff --git a/src/ca/publication.rs b/src/ca/publication.rs index 7f2461df..2ca46dcc 100644 --- a/src/ca/publication.rs +++ b/src/ca/publication.rs @@ -222,7 +222,7 @@ impl Message { /// # Decoding from XML /// impl Message { - /// Parses an RFC 8181 + /// Parses an RFC 8181 `` pub fn decode(reader: R) -> Result { let mut reader = xml::decode::Reader::new(reader); @@ -539,7 +539,7 @@ impl QueryPdu { //------------ PublishDelta ------------------------------------------------ /// This type represents a multi element query as described in -/// https://tools.ietf.org/html/rfc8181#section-3.7 +/// #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct PublishDelta(Vec); @@ -613,7 +613,7 @@ impl PublishDeltaElement { //------------ Publish ------------------------------------------------------- /// Represents a publish element, that does not update any existing object. -/// See: https://tools.ietf.org/html/rfc8181#section-3.1 +/// See: #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct Publish { tag: Option, @@ -679,7 +679,7 @@ impl Publish { //------------ Update -------------------------------------------------------- /// Represents a publish element, that replaces an existing object. -/// See: https://tools.ietf.org/html/rfc8181#section-3.2 +/// See: #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct Update { tag: Option, @@ -766,7 +766,7 @@ impl Update { //------------ Withdraw ------------------------------------------------------ /// Represents a withdraw element that removes an object. -/// See: https://tools.ietf.org/html/rfc8181#section-3.3 +/// See: #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct Withdraw { tag: Option, @@ -1062,7 +1062,7 @@ impl ReplyPdu { //------------ ListReply ----------------------------------------------------- /// This type represents the list reply as described in -/// https://tools.ietf.org/html/rfc8181#section-2.3 +/// #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub struct ListReply { elements: Vec, @@ -1156,7 +1156,7 @@ impl ListElement { //------------ ErrorReply ---------------------------------------------------- /// This type represents the error report as described in -/// https://tools.ietf.org/html/rfc8181#section-3.5 and 3.6 +/// and 3.6 #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct ErrorReply { errors: Vec, diff --git a/src/ca/sigmsg.rs b/src/ca/sigmsg.rs index 523c86ec..95fff5e4 100644 --- a/src/ca/sigmsg.rs +++ b/src/ca/sigmsg.rs @@ -28,8 +28,11 @@ use super::idcert::IdCert; /// but similar structure. /// /// Most important differences to watch out for: -/// = This uses [`IdCert`] instead of [`Cert`] as the EE (no INRs needed) -/// = This MUST include a CRL +/// - This uses [`IdCert`] instead of [`Cert`] as the EE (no INRs needed) +/// - This MUST include a CRL +/// +/// [`Cert`]: crate::repository::cert::Cert +/// [`SignedObject`]: crate::repository::sigobj::SignedObject #[derive(Clone, Debug)] pub struct SignedMessage { //--- From SignedData diff --git a/src/crypto/digest.rs b/src/crypto/digest.rs index 430f38a8..22d7aa2b 100644 --- a/src/crypto/digest.rs +++ b/src/crypto/digest.rs @@ -103,7 +103,7 @@ impl DigestAlgorithm { /// The functions and methods in this section allow decoding and encoding /// such values. /// -/// [`SignatureAlgorithm`]: ../signature/struct.SignatureAlgorithm.html +/// [`SignatureAlgorithm`]: super::SignatureAlgorithm /// [RFC 5754]: https://tools.ietf.org/html/rfc5754 /// [RFC 7935]: https://tools.ietf.org/html/rfc7935 impl DigestAlgorithm { @@ -180,7 +180,7 @@ impl DigestAlgorithm { encode::sequence(oid::SHA256.encode()) } - /// Provides an encoder for a indentifer as the sole value of a set. + /// Provides an encoder for a identifier as the sole value of a set. pub fn encode_set(self) -> impl encode::Values { encode::set( self.encode() diff --git a/src/crypto/keys.rs b/src/crypto/keys.rs index caa4e894..e32d1e88 100644 --- a/src/crypto/keys.rs +++ b/src/crypto/keys.rs @@ -78,7 +78,7 @@ impl PublicKeyFormat { /// defined by [RFC 4055] and the parameters must be present and NULL. /// When parsing, we generously also allow it to be absent altogether. /// -/// For ECDSA keys, the object identifer needs to be `ecPublicKey` defined +/// For ECDSA keys, the object identifier needs to be `ecPublicKey` defined /// in [RFC 5480] with the parameter being the object identifier `secp256r1` /// defined in the same RFC. /// @@ -113,7 +113,7 @@ impl PublicKeyFormat{ } } - /// Provides an encoder for the algorihm identifier. + /// Provides an encoder for the algorithm identifier. pub fn encode(self) -> impl encode::Values { match self { PublicKeyFormat::Rsa => { @@ -177,7 +177,7 @@ impl PublicKey { /// Creates an RSA Public Key based on the supplied exponent and modulus. /// /// See: - /// [RFC 4055]: https://tools.ietf.org/html/rfc4055 + /// [RFC 4055]: /// /// An RSA Public Key uses the following DER encoded structure inside its /// BitString component: @@ -421,7 +421,7 @@ impl PrimitiveContent for PublicKeyCn { pub struct KeyIdentifier([u8; 20]); impl KeyIdentifier { - /// Returns an octet slice of the key identifer’s value. + /// Returns an octet slice of the key identifier’s value. pub fn as_slice(&self) -> &[u8] { self.0.as_ref() } @@ -456,7 +456,7 @@ impl KeyIdentifier { cons.take_opt_value_if(bcder::Tag::OCTET_STRING, Self::from_content) } - /// Parses an encoded key identifer from encoded content. + /// Parses an encoded key identifier from encoded content. pub fn from_content( content: &mut decode::Content ) -> Result> { @@ -481,7 +481,7 @@ impl KeyIdentifier { } } - /// Skips over an encoded key indentifier. + /// Skips over an encoded key identifier. pub fn skip_opt_in( cons: &mut decode::Constructed ) -> Result, DecodeError> { diff --git a/src/repository/cert.rs b/src/repository/cert.rs index f13f264c..583abe0f 100644 --- a/src/repository/cert.rs +++ b/src/repository/cert.rs @@ -12,8 +12,6 @@ //! //! In addition, there are several types for the components of a certificate. //! -//! [`Cert`]: struct.Cert.html -//! [`ResourceCert`]: struct.ResourceCert.html //! [RFC 5280]: https://tools.ietf.org/html/rfc5280 //! [RFC 6487]: https://tools.ietf.org/html/rfc5487 @@ -73,13 +71,12 @@ use super::x509::{ /// further processing. In addition, various methods exist to access /// information contained in the certificate. /// -/// [`ResourceCert`]: struct.ResourceCert.html -/// [`decode`]: #method.decode -/// [`take_from`]: #method.take_from -/// [`from_constructed`]: #method.from_constructed -/// [`validate_ca`]: #method.validate_ca -/// [`validate_ee`]: #method.validate_ee -/// [`validate_ta`]: #method.validate_ta +/// [`decode`]: Cert::decode +/// [`take_from`]: Cert::take_from +/// [`from_constructed`]: Cert::from_constructed +/// [`validate_ca`]: Cert::validate_ca +/// [`validate_ee`]: Cert::validate_ee +/// [`validate_ta`]: Cert::validate_ta #[derive(Clone, Debug)] pub struct Cert { /// The outer structure of the certificate. @@ -256,7 +253,7 @@ impl Cert { self.verify_ee_at(issuer, strict, now).map_err(Into::into) } - /// Validates the certificate as a detached EE certficate. + /// Validates the certificate as a detached EE certificate. /// /// Such a certificate is used by signed objects that are not published /// through RPKI repositories. @@ -384,7 +381,7 @@ impl Cert { // 4.8.1. Basic Constraints: Must not be present. if self.basic_ca.is_some(){ return Err(InspectionError::new( - "Basic Contraints extension \ + "Basic Constraints extension \ not allowed in end entity certificate" )) } @@ -422,7 +419,7 @@ impl Cert { Ok(()) } - /// Inspects the certificate as a detached EE certficate. + /// Inspects the certificate as a detached EE certificate. /// /// Checks that the certificate fulfills all formal requirements of such /// a certificate. @@ -435,7 +432,7 @@ impl Cert { // 4.8.1. Basic Constraints: Must not be present. if self.basic_ca.is_some(){ return Err(InspectionError::new( - "Basic Contraints extension \ + "Basic Constraints extension \ not allowed in end entity certificate" )) } @@ -506,7 +503,7 @@ impl Cert { // 4.8.1. Basic Constraints. Must not be present. if self.basic_ca.is_some(){ return Err(InspectionError::new( - "Basic Contraints extension \ + "Basic Constraints extension \ not allowed in end entity certificate" )) } @@ -517,7 +514,7 @@ impl Cert { self.subject_public_key_info().key_identifier() { return Err(InspectionError::new( - "Subject Key Identifer extension doesn't match \ + "Subject Key Identifier extension doesn't match \ the public key" )) } @@ -588,7 +585,7 @@ impl Cert { } if self.as_resources().is_inherited() { return Err(InspectionError::new( - "inherited AS Resources in router certifiate" + "inherited AS Resources in router certificate" )) } diff --git a/src/repository/crl.rs b/src/repository/crl.rs index 43bd070a..9337c528 100644 --- a/src/repository/crl.rs +++ b/src/repository/crl.rs @@ -10,10 +10,6 @@ //! //! The RPKI CRL profile is defined in RFC 6487 based on the Internet RPIX //! profile defined in RFC 5280. -//! -//! [`Crl`]: struct.Crl.html -//! [`CrlStore`]: struct.CrlStore.html - use std::{fmt, ops}; use std::collections::HashSet; use std::str::FromStr; @@ -341,7 +337,7 @@ impl TbsCertList { &self.authority_key_id } - /// Sets the authority key identifer. + /// Sets the authority key identifier. pub fn set_authority_key_identifier(&mut self, id: KeyIdentifier) { self.authority_key_id = id } @@ -627,7 +623,7 @@ impl CrlEntry { cons.take_sequence(Self::from_constructed) } - /// Takes an optional CRL entry from the beginning of a contructed value. + /// Takes an optional CRL entry from the beginning of a constructed value. pub fn take_opt_from( cons: &mut decode::Constructed ) -> Result, DecodeError> { diff --git a/src/repository/manifest.rs b/src/repository/manifest.rs index 9790c428..b8b733fb 100644 --- a/src/repository/manifest.rs +++ b/src/repository/manifest.rs @@ -6,9 +6,6 @@ //! This module defines the type [`Manifest`] that represents a decoded //! manifest and the type [`ManifestContent`] for the content of a validated //! manifest, as well as some helper types for accessing the content. -//! -//! [`Manifest`]: struct.Manifest.html -//! [`ManifestContent`]: struct.ManifestContent.html use std::{borrow, fmt, ops}; use bcder::{decode, encode}; diff --git a/src/repository/resources/ipres.rs b/src/repository/resources/ipres.rs index 1874342a..ef73cad9 100644 --- a/src/repository/resources/ipres.rs +++ b/src/repository/resources/ipres.rs @@ -295,7 +295,7 @@ impl<'a> fmt::Display for IpBlocksForFamily<'a> { /// A sequence of address ranges for one address family. /// -/// Values of this type are guaranteed to contain a sequence of `IpBlocks` +/// Values of this type are guaranteed to contain a sequence of [`IpBlock`]s /// that fulfills the requirements of RFC 3779. Specifically, the blocks will /// not overlap, will not be consecutive (i.e., there’s at least one address /// between neighbouring blocks), will be in order, and anything that can be @@ -739,7 +739,7 @@ impl IpBlock { DisplayV4Block(self) } - /// Returns an object that displays the block as an IPv4 block. + /// Returns an object that displays the block as an IPv6 block. pub fn display_v6(self) -> DisplayV6Block { DisplayV6Block(self) } diff --git a/src/repository/resources/mod.rs b/src/repository/resources/mod.rs index 9c057cbe..62a7d9b3 100644 --- a/src/repository/resources/mod.rs +++ b/src/repository/resources/mod.rs @@ -9,8 +9,6 @@ //! Delegation Extension and [`AsResources`] implements the Autonomous System //! Identifier Delegation Extension. //! -//! [`AsResources`]: struct.AsResources.html -//! [`IpResources`]: struct.IpResources.html //! [RFC 3779]: https://tools.ietf.org/html/rfc3779 //! [RFC 6487]: https://tools.ietf.org/html/rfc6487 diff --git a/src/repository/sigobj.rs b/src/repository/sigobj.rs index 0ab4447c..cbdc2fc3 100644 --- a/src/repository/sigobj.rs +++ b/src/repository/sigobj.rs @@ -1270,9 +1270,9 @@ mod signer_test { /// [RFC 5652]: https://tools.ietf.org/html/rfc5652 /// [RFC 6488]: https://tools.ietf.org/html/rfc6488 /// [RFC 7935]: https://tools.ietf.org/html/rfc7935 -/// [`Cert`]: ../../cert/struct.Cert.html -/// [`DigestAlgorithm`]: ../../crypto/keys/struct.DigestAlgorithm.html -/// [`oid`]: ../../oid/index.html +/// [`Cert`]: super::cert::Cert +/// [`DigestAlgorithm`]: crate::crypto::digest::DigestAlgorithm +/// [`oid`]: crate::oid pub mod spec { } diff --git a/src/rtr/mod.rs b/src/rtr/mod.rs index 1ed444e3..2266ad50 100644 --- a/src/rtr/mod.rs +++ b/src/rtr/mod.rs @@ -21,8 +21,8 @@ //! You can read more about RPKI in [RFC 6480]. RTR is currently specified in //! [RFC 8210]. //! -//! [`Client`]: client/struct.Client.html -//! [`Server`]: server/struct.Server.html +//! [`Client`]: client::Client +//! [`Server`]: server::Server //! [Tokio]: https://crates.io/crates/tokio //! [RFC 6480]: https://tools.ietf.org/html/rfc6480 //! [RFC 8210]: https://tools.ietf.org/html/rfc8210 diff --git a/src/rtr/server.rs b/src/rtr/server.rs index 77b3d8c4..81dac069 100644 --- a/src/rtr/server.rs +++ b/src/rtr/server.rs @@ -2,9 +2,7 @@ //! //! This module implements a generic RTR server through [`Server`]. The server //! receives its data from a type implementing [`PayloadSource`]. -//! -//! [`Server`]: struct.Server.html -//! [`VrpSource`]: trait.VrpSource.html + use std::io; use futures_util::future; use futures_util::pin_mut; @@ -73,7 +71,7 @@ pub trait PayloadSource: Clone + Sync + Send + 'static { /// Returns the current state and an iterator over the full set of VRPs. fn full(&self) -> (State, Self::Set); - /// Returns the current state and an interator over differences in VPRs. + /// Returns the current state and an iterator over differences in VPRs. /// /// The difference is between the state given in `state` and the current /// state. If the source cannot provide this difference, for instance @@ -101,7 +99,7 @@ pub trait PayloadDiff: Sync + Send + 'static { /// A stream socket to be used for an RTR connection. /// -/// Apart from being abile to read and write asynchronously and being spawned +/// Apart from being able to read and write asynchronously and being spawned /// as an async task, the trait allows additional processing when the client /// has successfully updated. pub trait Socket: AsyncRead + AsyncWrite + Unpin + Sync + Send + 'static { @@ -125,8 +123,6 @@ impl Socket for tokio::net::TcpStream { } /// a VRP source and serves RTR data. In order to also serve notifications /// whenever new data is available, the server uses a notification dispatch /// system via the [`Dispatch`] system. -/// -/// [`Dispatch`]: struct.Dispatch.html pub struct Server { /// The listener socket. listener: Listener, @@ -151,7 +147,7 @@ impl Server { /// Runs the server. /// /// The asynchronous function will return successfully when the listener - /// socket (which is a stream over new connectons) finishes. It will + /// socket (which is a stream over new connections) finishes. It will /// return with an error if the listener socket errors out. pub async fn run(mut self) -> Result<(), io::Error> where @@ -202,7 +198,7 @@ impl Connection { /// Returns the protocol version we agreed on. /// - /// If there hasn’t been a negotation yet, returns the lowest protocol + /// If there hasn’t been a negotiation yet, returns the lowest protocol /// version we support, which currently is 0. fn version(&self) -> u8 { self.version.unwrap_or(0) diff --git a/src/rtr/state.rs b/src/rtr/state.rs index 678e4da6..0da60042 100644 --- a/src/rtr/state.rs +++ b/src/rtr/state.rs @@ -4,9 +4,6 @@ //! particular RTR server. The complete state, encapsulated in the type //! [`State`] consists of a sixteen bit session id and a serial number. Since //! the serial number follows special rules, it has its own type [`Serial`]. -//! -//! [`Serial`]: struct.Serial.html -//! [`State`]: struct.State.html use std::{cmp, fmt, hash, str}; use std::time::SystemTime; @@ -72,8 +69,6 @@ impl State { /// /// Serial number may wrap but that’s totally fine. See [`Serial`] for /// more details. - /// - /// [`Serial`]: struct.Serial.html pub fn inc(&mut self) { self.serial = self.serial.add(1) } From 31431df1b665e1de283786c175357b8e23045a0b Mon Sep 17 00:00:00 2001 From: Koen van Hove Date: Fri, 29 Nov 2024 14:58:34 +0100 Subject: [PATCH 02/15] Add function to turn AddressRange into set of prefixes (#306) This PR makes the `AddressRange` type public and adds methods to convert a range into a set of prefixes. --- src/repository/resources/ipres.rs | 105 +++++++++++++++++++++++++++++- src/repository/resources/mod.rs | 6 +- 2 files changed, 107 insertions(+), 4 deletions(-) diff --git a/src/repository/resources/ipres.rs b/src/repository/resources/ipres.rs index ef73cad9..5821251e 100644 --- a/src/repository/resources/ipres.rs +++ b/src/repository/resources/ipres.rs @@ -12,6 +12,7 @@ use std::fmt::Display; use std::net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr}; use std::num::ParseIntError; use std::str::FromStr; +use std::cmp; use bcder::{decode, encode}; use bcder::{BitString, Mode, OctetString, Tag}; use bcder::decode::{ContentError, DecodeError}; @@ -1037,6 +1038,84 @@ impl AddressRange { } } + /// Convert a range into a set of V6 prefixes + pub fn to_v6_prefixes(self) -> impl Iterator { + let mut start = self.min.to_bits(); + let end = self.max.to_bits(); + + std::iter::from_fn(move || { + // The idea is to take the largest prefix possible from the start + // then move the start to the address after the last address in + // that prefix, and do it again until there are no addresses left. + + // Based loosely on + if start > end { + return None; + } + + // Determine how many of the last bits of the address are prefixable + // e.g. for 2001:DB8:: that would be 99 + let addr_host_bits = start.trailing_zeros(); + + // Determine how many of the first bits are shared between the + // start and the end, to determine an upper bound for the prefix + // e.g. 2001:DB8:: and 2001:DB8::8000 share 112 bits, so the max + // is 16 + let mut max_allowed = 128 - (start ^ end).leading_zeros(); + if end.trailing_ones() < max_allowed { + // Prevent overshooting the prefix + // e.g. for 2001:DB8::8000 the trailing_ones = 0, so the max + // is now 15 to prevent covering space after 2001:DB8::8000 + max_allowed -= 1; + } + + // Obtain the bits at the end that are the same, which is the + // shortest of either the amount of 0 bits at the current address + // or the amount of bits not shared at the start + let same_bits = cmp::min(addr_host_bits, max_allowed); + let prefix_len = 128 - same_bits; + + debug_assert!(prefix_len <= 128); + let prefix = Prefix::new(Addr::from_bits(start), prefix_len as u8); + + start += 2_u128.pow(same_bits); + + Some(prefix) + }) + } + + /// Convert a range into a set of V4 prefixes + pub fn to_v4_prefixes(self) -> impl Iterator { + let mut start = (self.min.to_bits() >> 96) as u32; + let end = (self.max.to_bits() >> 96) as u32; + + std::iter::from_fn(move || { + // This works the same as `to_v6_prefixes` above + if start > end { + return None; + } + + let addr_host_bits = start.trailing_zeros(); + let mut max_allowed = 32 - (start ^ end).leading_zeros(); + if end.trailing_ones() < max_allowed { + max_allowed -= 1; + } + + let same_bits = cmp::min(addr_host_bits, max_allowed); + let prefix_len = 32 - same_bits; + + debug_assert!(prefix_len <= 32); + let prefix = Prefix::new( + Addr::from(Ipv4Addr::from(start)), + prefix_len as u8 + ); + + start += 2u32.pow(same_bits); + + Some(prefix) + }) + } + /// Formats the range as an IPv4 range. pub fn fmt_v4(self, f: &mut fmt::Formatter) -> fmt::Result { let min = self.min.to_v4(); @@ -2207,7 +2286,7 @@ impl From for VerificationError { //============ Tests ========================================================= #[cfg(test)] -mod test { +mod tests { use bcder::encode::Values; use super::*; @@ -2658,6 +2737,30 @@ mod test { 0x123f_ffff_ffff_ffff_ffff_ffff_ffff_ffff ); } + + #[test] + fn to_prefixes() { + { + let range = AddressRange::new( + Addr::from(Ipv6Addr::from_str("::1").unwrap()), + Addr::from(Ipv6Addr::from_str("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe").unwrap()) + ); + + let prefixes = range.to_v6_prefixes(); + + assert_eq!(254, prefixes.count()); + } + { + let range = AddressRange::new( + Addr::from(Ipv4Addr::from_str("192.168.0.0").unwrap()), + Addr::from(Ipv4Addr::from_str("192.168.2.255").unwrap()) + ); + + let prefixes = range.to_v4_prefixes(); + + assert_eq!(2, prefixes.count()); + } + } } #[cfg(all(test, feature="compat"))] diff --git a/src/repository/resources/mod.rs b/src/repository/resources/mod.rs index 62a7d9b3..5d5f9092 100644 --- a/src/repository/resources/mod.rs +++ b/src/repository/resources/mod.rs @@ -18,9 +18,9 @@ pub use self::asres::{ }; pub use self::choice::ResourcesChoice; pub use self::ipres::{ - Addr, AddressFamily, InheritedIpResources, IpBlock, IpBlocks, Ipv4Block, - Ipv4Blocks, Ipv6Block, Ipv6Blocks, IpBlocksBuilder, IpBlocksForFamily, - IpResources, IpResourcesBuilder, OverclaimedIpResources, + Addr, AddressFamily, AddressRange, InheritedIpResources, IpBlock, IpBlocks, + Ipv4Block, Ipv4Blocks, Ipv6Block, Ipv6Blocks, IpBlocksBuilder, + IpBlocksForFamily, IpResources, IpResourcesBuilder, OverclaimedIpResources, OverclaimedIpv4Resources, OverclaimedIpv6Resources, Prefix }; pub use self::set::{ From e65b6975e153f0f5e88c33142907f70734e0204d Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Fri, 29 Nov 2024 15:00:48 +0100 Subject: [PATCH 03/15] Updare changelog. --- Changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Changelog.md b/Changelog.md index 277cf7d3..40832997 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,9 +12,13 @@ New * Added `uri::{Rsync,Https}::path_into_dir` ([#302]) * Added `Ipv4Block` and `Ipv6Block` and `FromIterator` impls for `Ipv4Blocks` and `Ipv6Blocks`. ([#298]) +* Made `AddressRange` public and added methods to convert ranges into + a set of prefixes. ([#306]) Bug fixes +* Do not allow backslashes in idexchange handles. ([#304]) + Other changes * The minimum supported Rust version is now 1.70. ([#303]) @@ -25,6 +29,8 @@ Other changes [#300]: https://github.com/NLnetLabs/rpki-rs/pull/300 [#302]: https://github.com/NLnetLabs/rpki-rs/pull/302 [#303]: https://github.com/NLnetLabs/rpki-rs/pull/303 +[#304]: https://github.com/NLnetLabs/rpki-rs/pull/304 +[#306]: https://github.com/NLnetLabs/rpki-rs/pull/306 ## 0.18.4 From 89640b8c93da8c005114f6d1ec6615c2ae9ce324 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Thu, 5 Dec 2024 11:47:18 +0100 Subject: [PATCH 04/15] Clippy-suggested code improvements. (#308) --- src/crypto/keys.rs | 2 +- src/repository/aspa.rs | 2 +- src/repository/resources/ipres.rs | 2 +- src/repository/roa.rs | 2 +- src/repository/x509.rs | 4 +-- src/resources/addr.rs | 2 +- src/resources/asn.rs | 12 ++++----- src/rrdp.rs | 6 ++--- src/rtr/payload.rs | 2 +- src/rtr/pdu.rs | 42 +++++++++++++------------------ src/slurm.rs | 4 +-- src/uri.rs | 8 +++--- src/util/base64.rs | 4 +-- src/xml/decode.rs | 10 ++++---- src/xml/encode.rs | 8 +++--- 15 files changed, 52 insertions(+), 58 deletions(-) diff --git a/src/crypto/keys.rs b/src/crypto/keys.rs index e32d1e88..02241733 100644 --- a/src/crypto/keys.rs +++ b/src/crypto/keys.rs @@ -609,7 +609,7 @@ impl<'de> serde::Deserialize<'de> for KeyIdentifier { ) -> Result { struct KeyIdentifierVisitor; - impl<'de> serde::de::Visitor<'de> for KeyIdentifierVisitor { + impl serde::de::Visitor<'_> for KeyIdentifierVisitor { type Value = KeyIdentifier; fn expecting( diff --git a/src/repository/aspa.rs b/src/repository/aspa.rs index d676311b..c170cc88 100644 --- a/src/repository/aspa.rs +++ b/src/repository/aspa.rs @@ -267,7 +267,7 @@ impl ProviderAsSet { #[derive(Clone, Debug)] pub struct ProviderAsIter<'a>(SliceSource<'a>); -impl<'a> Iterator for ProviderAsIter<'a> { +impl Iterator for ProviderAsIter<'_> { type Item = Asn; fn next(&mut self) -> Option { diff --git a/src/repository/resources/ipres.rs b/src/repository/resources/ipres.rs index 5821251e..2b5a7012 100644 --- a/src/repository/resources/ipres.rs +++ b/src/repository/resources/ipres.rs @@ -269,7 +269,7 @@ impl<'a> IpBlocksForFamily<'a> { } } -impl<'a> fmt::Display for IpBlocksForFamily<'a> { +impl fmt::Display for IpBlocksForFamily<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut blocks_iter = self.blocks.iter(); diff --git a/src/repository/roa.rs b/src/repository/roa.rs index 0fcfc8f5..a0121855 100644 --- a/src/repository/roa.rs +++ b/src/repository/roa.rs @@ -312,7 +312,7 @@ impl RoaIpAddresses { #[derive(Clone, Debug)] pub struct RoaIpAddressIter<'a>(SliceSource<'a>); -impl<'a> Iterator for RoaIpAddressIter<'a> { +impl Iterator for RoaIpAddressIter<'_> { type Item = RoaIpAddress; fn next(&mut self) -> Option { diff --git a/src/repository/x509.rs b/src/repository/x509.rs index 897763c6..cbfc1d29 100644 --- a/src/repository/x509.rs +++ b/src/repository/x509.rs @@ -561,7 +561,7 @@ impl<'de> serde::Deserialize<'de> for Serial { ) -> Result { struct SerialVisitor; - impl<'de> serde::de::Visitor<'de> for SerialVisitor { + impl serde::de::Visitor<'_> for SerialVisitor { type Value = Serial; fn expecting( @@ -665,7 +665,7 @@ impl Eq for SignedData {} #[derive(Clone, Copy, Debug)] pub struct SignatureValueContent<'a, Alg>(&'a SignedData); -impl<'a, Alg> PrimitiveContent for SignatureValueContent<'a, Alg> { +impl PrimitiveContent for SignatureValueContent<'_, Alg> { const TAG: Tag = Tag::BIT_STRING; fn encoded_len(&self, _: Mode) -> usize { diff --git a/src/resources/addr.rs b/src/resources/addr.rs index 8ebd71bd..e1e2a03a 100644 --- a/src/resources/addr.rs +++ b/src/resources/addr.rs @@ -472,7 +472,7 @@ impl<'de> serde::Deserialize<'de> for Prefix { ) -> Result { struct Visitor; - impl<'de> serde::de::Visitor<'de> for Visitor { + impl serde::de::Visitor<'_> for Visitor { type Value = Prefix; fn expecting( diff --git a/src/resources/asn.rs b/src/resources/asn.rs index 54bfee90..52c83d90 100644 --- a/src/resources/asn.rs +++ b/src/resources/asn.rs @@ -169,7 +169,7 @@ impl Asn { ) -> Result { struct Visitor; - impl<'de> serde::de::Visitor<'de> for Visitor { + impl serde::de::Visitor<'_> for Visitor { type Value = Asn; fn expecting( @@ -198,7 +198,7 @@ impl Asn { ) -> Result { struct Visitor; - impl<'de> serde::de::Visitor<'de> for Visitor { + impl serde::de::Visitor<'_> for Visitor { type Value = Asn; fn expecting( @@ -390,7 +390,7 @@ pub struct SmallSetDifference<'a> { right: Peekable>, } -impl<'a> Iterator for SmallSetDifference<'a> { +impl Iterator for SmallSetDifference<'_> { type Item = Asn; fn next(&mut self) -> Option { @@ -423,7 +423,7 @@ pub struct SmallSetSymmetricDifference<'a> { right: Peekable>, } -impl<'a> Iterator for SmallSetSymmetricDifference<'a> { +impl Iterator for SmallSetSymmetricDifference<'_> { type Item = Asn; fn next(&mut self) -> Option { @@ -455,7 +455,7 @@ pub struct SmallSetIntersection<'a> { right: Peekable>, } -impl<'a> Iterator for SmallSetIntersection<'a> { +impl Iterator for SmallSetIntersection<'_> { type Item = Asn; fn next(&mut self) -> Option { @@ -489,7 +489,7 @@ pub struct SmallSetUnion<'a> { right: Peekable>, } -impl<'a> Iterator for SmallSetUnion<'a> { +impl Iterator for SmallSetUnion<'_> { type Item = Asn; fn next(&mut self) -> Option { diff --git a/src/rrdp.rs b/src/rrdp.rs index 845f7a68..90f9f75f 100644 --- a/src/rrdp.rs +++ b/src/rrdp.rs @@ -569,7 +569,7 @@ impl DeltaElement { } } -///--- From +//--- From impl From for DeltaElement { fn from(src: PublishElement) -> Self { @@ -1434,7 +1434,7 @@ pub struct ObjectReader<'a>( base64::XmlDecoderReader<'a> ); -impl<'a> ObjectReader<'a> { +impl ObjectReader<'_> { /// Processes an element with optional XML PCDATA as object content. /// /// An object reader is created and passed to the closure `op` for @@ -1481,7 +1481,7 @@ impl<'a> ObjectReader<'a> { } } -impl<'a> io::Read for ObjectReader<'a> { +impl io::Read for ObjectReader<'_> { fn read(&mut self, buf: &mut [u8]) -> Result { self.0.read(buf) } diff --git a/src/rtr/payload.rs b/src/rtr/payload.rs index 30766d1b..772e2ece 100644 --- a/src/rtr/payload.rs +++ b/src/rtr/payload.rs @@ -287,7 +287,7 @@ pub enum PayloadRef<'a> { //--- From -impl<'a> From for PayloadRef<'a> { +impl From for PayloadRef<'_> { fn from(src: RouteOrigin) -> Self { PayloadRef::Origin(src) } diff --git a/src/rtr/pdu.rs b/src/rtr/pdu.rs index 6ae63cd2..0d38becf 100644 --- a/src/rtr/pdu.rs +++ b/src/rtr/pdu.rs @@ -787,11 +787,6 @@ pub struct Aspa { #[repr(packed)] struct AspaFixed { header: Header, - flags: u8, - afi_flags: u8, - - #[allow(dead_code)] - provider_count: u16, customer: u32, } @@ -818,10 +813,11 @@ impl Aspa { ).expect("ASPA RTR PDU size overflow"); Aspa { fixed: AspaFixed { - header: Header::new(version, Self::PDU, 0, len), - flags, - afi_flags: 0, - provider_count: providers.asn_count().to_be(), + header: Header::new( + version, Self::PDU, + (flags as u16) << 8, + len + ), customer: customer.into_u32().to_be(), }, providers @@ -848,7 +844,7 @@ impl Aspa { /// The only flag currently used is the least significant bit that is /// 1 for an announcement and 0 for a withdrawal. pub fn flags(&self) -> u8 { - self.fixed.flags + (self.fixed.header.session >> 8) as u8 } /// Returns the customer ASN. @@ -899,7 +895,15 @@ impl Aspa { let provider_len = match header.pdu_len()?.checked_sub(mem::size_of::()) { - Some(len) => len, + Some(len) => { + if len % 4 != 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "invalid length for ASPA PDU" + )) + } + len + } None => { return Err(io::Error::new( io::ErrorKind::InvalidData, @@ -909,16 +913,6 @@ impl Aspa { }; let mut fixed = AspaFixed { header, .. Default::default() }; sock.read_exact(&mut fixed.as_mut()[Header::LEN..]).await?; - if provider_len - != usize::from( - u16::from_be(fixed.provider_count) - ) * mem::size_of::() - { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "invalid length for ASPA PDU" - )) - } let providers = ProviderAsns::read(sock, provider_len).await?; Ok(Aspa { fixed, providers }) } @@ -1831,9 +1825,9 @@ mod test { ]).unwrap(), ), [ - 2, 11, 0, 0, 0, 0, 0, 24, - 1, 0, 0, 2, 0, 1, 0, 15, - 0, 1, 0, 13, 0, 1, 0, 14, + 2, 11, 1, 0, 0, 0, 0, 20, + 0, 1, 0, 15, 0, 1, 0, 13, + 0, 1, 0, 14, ] ); } diff --git a/src/slurm.rs b/src/slurm.rs index 5bcfa022..fe28d573 100644 --- a/src/slurm.rs +++ b/src/slurm.rs @@ -660,7 +660,7 @@ impl<'de> Deserialize<'de> for Base64KeyInfo { ) -> Result { struct Visitor; - impl<'de> serde::de::Visitor<'de> for Visitor { + impl serde::de::Visitor<'_> for Visitor { type Value = Base64KeyInfo; fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -736,7 +736,7 @@ mod serde_key_identifier { ) -> Result { struct Visitor; - impl<'de> serde::de::Visitor<'de> for Visitor { + impl serde::de::Visitor<'_> for Visitor { type Value = KeyIdentifier; fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/src/uri.rs b/src/uri.rs index 05a36444..b2822739 100644 --- a/src/uri.rs +++ b/src/uri.rs @@ -481,7 +481,7 @@ impl<'de> Deserialize<'de> for Rsync { //--- PrimitiveContent #[cfg(feature = "repository")] -impl<'a> encode::PrimitiveContent for &'a Rsync { +impl encode::PrimitiveContent for &'_ Rsync { const TAG: Tag = Tag::IA5_STRING; fn encoded_len(&self, _: Mode) -> usize { @@ -779,7 +779,7 @@ impl<'de> Deserialize<'de> for Https { //--- PrimitiveContent #[cfg(feature = "repository")] -impl<'a> encode::PrimitiveContent for &'a Https { +impl encode::PrimitiveContent for &'_ Https { const TAG: Tag = Tag::IA5_STRING; fn encoded_len(&self, _: Mode) -> usize { @@ -871,7 +871,7 @@ impl Default for UriVisitor { } #[cfg(feature = "serde")] -impl<'de, V> serde::de::Visitor<'de> for UriVisitor +impl serde::de::Visitor<'_> for UriVisitor where V: FromStr + TryFrom, ::Err: fmt::Display, @@ -980,7 +980,7 @@ mod arbitrary { } } - impl<'a> Arbitrary<'a> for super::Https { + impl Arbitrary<'_> for super::Https { fn arbitrary(u: &mut Unstructured) -> arbitrary::Result { let mut res = String::from("https://"); append_host(&mut res, u)?; diff --git a/src/util/base64.rs b/src/util/base64.rs index 87367e45..9b774765 100644 --- a/src/util/base64.rs +++ b/src/util/base64.rs @@ -166,7 +166,7 @@ impl<'a> SkipWhitespace<'a> { } } -impl<'a> io::Read for SkipWhitespace<'a> { +impl io::Read for SkipWhitespace<'_> { fn read(&mut self, mut buf: &mut[u8]) -> Result { let mut res = 0; @@ -205,7 +205,7 @@ pub struct XmlDecoderReader<'a>( > ); -impl<'a> io::Read for XmlDecoderReader<'a> { +impl io::Read for XmlDecoderReader<'_> { fn read(&mut self, buf: &mut [u8]) -> Result { self.0.read(buf) } diff --git a/src/xml/decode.rs b/src/xml/decode.rs index 3ab16190..6e2bdf97 100644 --- a/src/xml/decode.rs +++ b/src/xml/decode.rs @@ -371,7 +371,7 @@ impl<'n, 'l> Name<'n, 'l> { } } -impl<'n, 'l> fmt::Debug for Name<'n, 'l> { +impl fmt::Debug for Name<'_, '_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Name(")?; if let Some(ns) = self.namespace { @@ -381,13 +381,13 @@ impl<'n, 'l> fmt::Debug for Name<'n, 'l> { } } -impl<'n, 'l> From<&'l [u8]> for Name<'n, 'l> { +impl<'l> From<&'l [u8]> for Name<'_, 'l> { fn from(local: &'l [u8]) -> Self { Name::unqualified(local) } } -impl<'n, 'l> From<&'l str> for Name<'n, 'l> { +impl<'l> From<&'l str> for Name<'_, 'l> { fn from(local: &'l str) -> Self { Name::unqualified(local.as_bytes()) } @@ -412,7 +412,7 @@ impl<'n, 'l> From<(&'n str, &'l str)> for Name<'n, 'l> { #[derive(Clone)] pub struct AttrValue<'a>(quick_xml::events::attributes::Attribute<'a>); -impl<'a> AttrValue<'a> { +impl AttrValue<'_> { pub fn ascii_into(self) -> Result { let s = self.0.unescape_value()?; if !s.is_ascii() { @@ -435,7 +435,7 @@ impl<'a> AttrValue<'a> { pub struct Text<'a>(quick_xml::events::BytesText<'a>); -impl<'a> Text<'a> { +impl Text<'_> { pub fn to_utf8(&self) -> Result, Error> { Ok(self.0.unescape()?) } diff --git a/src/xml/encode.rs b/src/xml/encode.rs index eb82cae2..6a4b8392 100644 --- a/src/xml/encode.rs +++ b/src/xml/encode.rs @@ -225,7 +225,7 @@ impl<'a, W: io::Write> Element<'a, W> { } } -impl<'a, W: io::Write> Drop for Element<'a, W> { +impl Drop for Element<'_, W> { fn drop(&mut self) { if let Err(err) = self.end() { self.writer.store_error(err) @@ -246,7 +246,7 @@ pub struct Content<'a, W> { writer: &'a mut Writer, } -impl<'a, W: io::Write> Content<'a, W> { +impl Content<'_, W> { /// Add an element with the given tag. /// /// This will write the beginning of the tag to the writer and therefore @@ -417,8 +417,8 @@ impl<'a, W: io::Write> DisplayText<'a, W> { } } -impl<'a, W: io::Write> fmt::Write for DisplayText<'a, W> { - fn write_str(&mut self, s: &str) -> fmt::Result { +impl fmt::Write for DisplayText<'_, W> { + fn write_str(&mut self, s: &str) -> fmt::Result { match self.escape.write_escaped(s.as_bytes(), self.inner) { Ok(()) => Ok(()), Err(err) => { From 3a3bd5029e05c3b8e9931c42da73b4fa4c1aee3b Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Thu, 5 Dec 2024 11:55:56 +0100 Subject: [PATCH 05/15] Update ASPA RTR PDU. (#309) This PR updates the ASPA RTR PDU according to version 14 of draft-ietf-sidrops-8210bis. From d77f8b00ee6c34f9a15a0ef3f88255362d4b3c17 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Thu, 5 Dec 2024 11:57:52 +0100 Subject: [PATCH 06/15] Update changelog. --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 40832997..255c9f1c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,8 @@ New `Ipv4Blocks` and `Ipv6Blocks`. ([#298]) * Made `AddressRange` public and added methods to convert ranges into a set of prefixes. ([#306]) +* Updated the ASPA RTR PDU to conform with version -14 of + draft-ietf-sidrops-8210bis. ([#309]) Bug fixes @@ -31,6 +33,7 @@ Other changes [#303]: https://github.com/NLnetLabs/rpki-rs/pull/303 [#304]: https://github.com/NLnetLabs/rpki-rs/pull/304 [#306]: https://github.com/NLnetLabs/rpki-rs/pull/306 +[#309]: https://github.com/NLnetLabs/rpki-rs/pull/309 ## 0.18.4 From baf053684e358ec986a44edb49c4ead480534c05 Mon Sep 17 00:00:00 2001 From: Koen van Hove Date: Thu, 12 Dec 2024 14:28:36 +0100 Subject: [PATCH 07/15] =?UTF-8?q?Expose=20IdCert=E2=80=99s=20validity=20in?= =?UTF-8?q?=20public=20API=20(#310)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ca/idcert.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ca/idcert.rs b/src/ca/idcert.rs index a8f27ece..63d4c2e4 100644 --- a/src/ca/idcert.rs +++ b/src/ca/idcert.rs @@ -419,6 +419,10 @@ impl TbsIdCert { pub fn subject(&self) -> &Name { &self.subject } + + pub fn validity(&self) -> &Validity { + &self.validity + } } /// # Decoding and Encoding From aa74957aa2965108ea18adb0c0b81861deb3e11b Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Thu, 12 Dec 2024 14:29:39 +0100 Subject: [PATCH 08/15] Update changelog. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index 255c9f1c..f80ae2bf 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,6 +16,7 @@ New a set of prefixes. ([#306]) * Updated the ASPA RTR PDU to conform with version -14 of draft-ietf-sidrops-8210bis. ([#309]) +* Exposed `ca::idcert::TbsIdCert::validity`. ([#310]); Bug fixes @@ -34,6 +35,7 @@ Other changes [#304]: https://github.com/NLnetLabs/rpki-rs/pull/304 [#306]: https://github.com/NLnetLabs/rpki-rs/pull/306 [#309]: https://github.com/NLnetLabs/rpki-rs/pull/309 +[#310]: https://github.com/NLnetLabs/rpki-rs/pull/310 ## 0.18.4 From 01024d58589e55f3658885b6700d537d1ca71f38 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Fri, 20 Dec 2024 15:18:53 +0100 Subject: [PATCH 09/15] Add Prefix::from_str_relaxed. (#312) This PR adds Prefix::from_str_relaxed which parses the string representation of a prefix even if its host bits are non-zero. --- src/resources/addr.rs | 124 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/src/resources/addr.rs b/src/resources/addr.rs index e1e2a03a..741da7d7 100644 --- a/src/resources/addr.rs +++ b/src/resources/addr.rs @@ -327,6 +327,23 @@ impl Prefix { }) } + /// Creates a prefix from a string even if the host portion is non-zero. + pub fn from_str_relaxed(s: &str) -> Result { + if s.is_empty() { + return Err(ParsePrefixError::Empty) + } + let slash = s.find('/').ok_or(ParsePrefixError::MissingLen)?; + let addr = IpAddr::from_str(&s[..slash]).map_err( + ParsePrefixError::InvalidAddr + )?; + let len = u8::from_str(&s[slash + 1..]).map_err( + ParsePrefixError::InvalidLen + )?; + Prefix::new_relaxed( + addr, len + ).map_err(ParsePrefixError::InvalidPrefix) + } + /// Returns whether the prefix is for an IPv4 address. pub fn is_v4(self) -> bool { self.family_and_len.is_v4() @@ -981,6 +998,36 @@ mod test { Err(ParsePrefixError::InvalidLen(_)) ) ); + assert!( + matches!( + Prefix::from_str("127.0.0.1/33"), + Err(ParsePrefixError::InvalidPrefix( + PrefixError::LenOverflow + )) + ) + ); + assert!( + matches!( + Prefix::from_str("2001:db8::/129"), + Err(ParsePrefixError::InvalidPrefix( + PrefixError::LenOverflow + )) + ) + ); + assert!( + matches!( + Prefix::from_str("2001:db8::/9000"), + Err(ParsePrefixError::InvalidLen(_)) + ) + ); + assert!( + matches!( + Prefix::from_str("127.0.0.1/24"), + Err(ParsePrefixError::InvalidPrefix( + PrefixError::NonZeroHost + )) + ) + ); assert!( matches!( Prefix::from_str(""), @@ -989,6 +1036,83 @@ mod test { ); } + #[test] + fn prefix_from_str_relaxed() { + assert_eq!( + Prefix::from_str_relaxed("127.0.0.0/12").unwrap().addr_and_len(), + (IpAddr::from_str("127.0.0.0").unwrap(), 12) + ); + assert_eq!( + Prefix::from_str_relaxed( + "2001:db8:10:20::/64" + ).unwrap().addr_and_len(), + (IpAddr::from_str("2001:db8:10:20::").unwrap(), 64) + ); + assert_eq!( + Prefix::from_str_relaxed("0.0.0.0/0").unwrap().addr_and_len(), + (IpAddr::from_str("0.0.0.0").unwrap(), 0) + ); + assert_eq!( + Prefix::from_str_relaxed("::/0").unwrap().addr_and_len(), + (IpAddr::from_str("::").unwrap(), 0) + ); + + assert_eq!( + Prefix::from_str_relaxed("127.0.0.0"), + Err(ParsePrefixError::MissingLen) + ); + assert_eq!( + Prefix::from_str_relaxed("2001:db8::"), + Err(ParsePrefixError::MissingLen) + ); + assert!( + matches!( + Prefix::from_str_relaxed("127.0.0.0/"), + Err(ParsePrefixError::InvalidLen(_)) + ) + ); + assert!( + matches!( + Prefix::from_str_relaxed("2001:db8::/"), + Err(ParsePrefixError::InvalidLen(_)) + ) + ); + assert!( + matches!( + Prefix::from_str_relaxed("127.0.0.1/33"), + Err(ParsePrefixError::InvalidPrefix( + PrefixError::LenOverflow + )) + ) + ); + assert!( + matches!( + Prefix::from_str_relaxed("2001:db8::/129"), + Err(ParsePrefixError::InvalidPrefix( + PrefixError::LenOverflow + )) + ) + ); + assert!( + matches!( + Prefix::from_str_relaxed("2001:db8::/9000"), + Err(ParsePrefixError::InvalidLen(_)) + ) + ); + assert_eq!( + Prefix::from_str_relaxed( + "127.0.0.1/24" + ).unwrap().addr_and_len(), + (IpAddr::from_str("127.0.0.0").unwrap(), 24) + ); + assert!( + matches!( + Prefix::from_str_relaxed(""), + Err(ParsePrefixError::Empty) + ) + ); + } + #[test] fn ordering() { assert!( From e0d5869b687d594a207b0f9f85f1116dd1cf9cde Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Tue, 7 Jan 2025 14:12:17 +0100 Subject: [PATCH 10/15] =?UTF-8?q?ASPA=E2=80=99s=20ProviderAsSet=20keeps=20?= =?UTF-8?q?track=20of=20its=20size.=20(#315)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR changes the ASPA ProviderAsSet type to keep track of the number of ASNs it contains and make it available via a len method. --- src/repository/aspa.rs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/repository/aspa.rs b/src/repository/aspa.rs index c170cc88..59eb3272 100644 --- a/src/repository/aspa.rs +++ b/src/repository/aspa.rs @@ -181,7 +181,7 @@ impl AsProviderAttestation { encode::sequence(( encode::sequence_as(Tag::CTX_0, 1u8.encode()), self.customer_as.encode(), - &self.provider_as_set.0, + &self.provider_as_set.captured, )) } @@ -203,9 +203,17 @@ impl AsProviderAttestation { /// the AS in this set are ordered, free of duplicates and there is at least /// one AS. #[derive(Clone, Debug)] -pub struct ProviderAsSet(Captured); +pub struct ProviderAsSet { + captured: Captured, + len: usize, +} impl ProviderAsSet { + #[allow(clippy::len_without_is_empty)] // never empty + pub fn len(&self) -> usize { + self.len + } + pub fn to_set(&self) -> SmallAsnSet { unsafe { SmallAsnSet::from_vec_unchecked( @@ -215,14 +223,15 @@ impl ProviderAsSet { } pub fn iter(&self) -> ProviderAsIter { - ProviderAsIter(self.0.as_slice().into_source()) + ProviderAsIter(self.captured.as_slice().into_source()) } fn take_from( cons: &mut decode::Constructed, customer_as: Asn, ) -> Result> { - cons.take_sequence(|cons| { + let mut len = 0; + let captured = cons.take_sequence(|cons| { cons.capture(|cons| { let mut last: Option = None; while let Some(asn) = Asn::take_opt_from( @@ -249,6 +258,7 @@ impl ProviderAsSet { } } last = Some(asn); + len += 1; } if last.is_none() { return Err(cons.content_err( @@ -257,7 +267,8 @@ impl ProviderAsSet { } Ok(()) }) - }).map(ProviderAsSet) + })?; + Ok(Self { captured, len }) } } @@ -336,7 +347,10 @@ impl AspaBuilder { ) ); - let provider_as_set = ProviderAsSet(provider_as_set_captured); + let provider_as_set = ProviderAsSet { + captured: provider_as_set_captured, + len: self.providers.len() + }; AsProviderAttestation { customer_as: self.customer_as, From 325687dce3e7cc5b02e54bfb8945040665c967a2 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Tue, 7 Jan 2025 14:13:57 +0100 Subject: [PATCH 11/15] Update changelog. --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index f80ae2bf..99dc33b8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,6 +17,8 @@ New * Updated the ASPA RTR PDU to conform with version -14 of draft-ietf-sidrops-8210bis. ([#309]) * Exposed `ca::idcert::TbsIdCert::validity`. ([#310]); +* The ASPA `ProviderAsSet` now keeps track of its length and exposes it + via the new `len` method. ([#315]) Bug fixes @@ -36,6 +38,7 @@ Other changes [#306]: https://github.com/NLnetLabs/rpki-rs/pull/306 [#309]: https://github.com/NLnetLabs/rpki-rs/pull/309 [#310]: https://github.com/NLnetLabs/rpki-rs/pull/310 +[#315]: https://github.com/NLnetLabs/rpki-rs/pull/315 ## 0.18.4 From 9123b346dfb60744caac922c92bc3c78e696259c Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 20 Jan 2025 11:07:29 +0100 Subject: [PATCH 12/15] Bump MSRV to 1.73. --- .github/workflows/ci.yml | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f7da299..9a028461 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] - rust: [1.70.0, stable, beta, nightly] + rust: [1.73.0, stable, beta, nightly] steps: - name: Checkout repository uses: actions/checkout@v1 diff --git a/Cargo.toml b/Cargo.toml index 93276ba7..fd0376c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "rpki" version = "0.18.4" edition = "2021" -rust-version = "1.70" +rust-version = "1.73" authors = ["NLnet Labs "] description = "A library for validating and creating RPKI data." documentation = "https://docs.rs/rpki/" From 6589ce08b237434063739012c72d338e69e11c26 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 20 Jan 2025 14:19:55 +0100 Subject: [PATCH 13/15] Reformatting. --- src/rrdp.rs | 260 ++++++++++++++++++++------------------- src/xml/decode.rs | 121 +++++++++--------- tests/rrdp-resilience.rs | 11 +- 3 files changed, 206 insertions(+), 186 deletions(-) diff --git a/src/rrdp.rs b/src/rrdp.rs index a440be26..6a428790 100644 --- a/src/rrdp.rs +++ b/src/rrdp.rs @@ -217,108 +217,111 @@ impl NotificationFile { delta_limit: Option, ) -> Result { let mut reader = Reader::new(reader); - let mut session_id = None; let mut serial = None; - let mut outer = reader.start_with_limit(|element| { - if element.name() != NOTIFICATION { - return Err(XmlError::Malformed) - } - - element.attributes(|name, value| { - match name { - b"version" => { - if value.ascii_into::()? != 1 { - return Err(XmlError::Malformed) - } - Ok(()) - } - b"session_id" => { - session_id = Some(value.ascii_into()?); - Ok(()) - } - b"serial" => { - serial = Some(value.ascii_into()?); - Ok(()) - } - _ => Err(XmlError::Malformed) + let mut outer = reader.start_with_limit( + |element| { + if element.name() != NOTIFICATION { + return Err(XmlError::Malformed) } - }) - }, 100_000_000)?; - - let mut snapshot = None; - let mut deltas = Ok(vec![]); - - while let Some(mut content) = outer.take_opt_element_with_limit(&mut reader, - |element| { - match element.name() { - SNAPSHOT => { - if snapshot.is_some() { - return Err(XmlError::Malformed) - } - let mut uri = None; - let mut hash = None; - element.attributes(|name, value| match name { - b"uri" => { - uri = Some(value.ascii_into()?); + element.attributes(|name, value| { + match name { + b"version" => { + if value.ascii_into::()? != 1 { + return Err(XmlError::Malformed) + } Ok(()) } - b"hash" => { - hash = Some(value.ascii_into()?); + b"session_id" => { + session_id = Some(value.ascii_into()?); Ok(()) } - _ => Err(XmlError::Malformed) - })?; - match (uri, hash) { - (Some(uri), Some(hash)) => { - snapshot = Some(UriAndHash::new(uri, hash)); - Ok(()) - } - _ => Err(XmlError::Malformed) - } - } - DELTA => { - let mut serial = None; - let mut uri = None; - let mut hash = None; - element.attributes(|name, value| match name { b"serial" => { serial = Some(value.ascii_into()?); Ok(()) } - b"uri" => { - uri = Some(value.ascii_into()?); - Ok(()) - } - b"hash" => { - hash = Some(value.ascii_into()?); - Ok(()) - } _ => Err(XmlError::Malformed) - })?; - let (serial, uri, hash) = match (serial, uri, hash) { - (Some(serial), Some(uri), Some(hash)) => { - (serial, uri, hash) + } + }) + }, + 100_000_000 + )?; + + let mut snapshot = None; + let mut deltas = Ok(vec![]); + while let Some(mut content) = outer.take_opt_element_with_limit( + &mut reader, + |element| { + match element.name() { + SNAPSHOT => { + if snapshot.is_some() { + return Err(XmlError::Malformed) } - _ => return Err(XmlError::Malformed) - }; - if let Some(limit) = delta_limit { - let len = deltas.as_ref().map(|deltas| { - deltas.len() - }).unwrap_or(0); - if len >= limit { - deltas = Err(DeltaListError::Oversized); + let mut uri = None; + let mut hash = None; + element.attributes(|name, value| match name { + b"uri" => { + uri = Some(value.ascii_into()?); + Ok(()) + } + b"hash" => { + hash = Some(value.ascii_into()?); + Ok(()) + } + _ => Err(XmlError::Malformed) + })?; + match (uri, hash) { + (Some(uri), Some(hash)) => { + snapshot = Some(UriAndHash::new(uri, hash)); + Ok(()) + } + _ => Err(XmlError::Malformed) } } - if let Ok(ref mut deltas) = deltas { - deltas.push(DeltaInfo::new(serial, uri, hash)) + DELTA => { + let mut serial = None; + let mut uri = None; + let mut hash = None; + element.attributes(|name, value| match name { + b"serial" => { + serial = Some(value.ascii_into()?); + Ok(()) + } + b"uri" => { + uri = Some(value.ascii_into()?); + Ok(()) + } + b"hash" => { + hash = Some(value.ascii_into()?); + Ok(()) + } + _ => Err(XmlError::Malformed) + })?; + let (serial, uri, hash) = match (serial, uri, hash) { + (Some(serial), Some(uri), Some(hash)) => { + (serial, uri, hash) + } + _ => return Err(XmlError::Malformed) + }; + if let Some(limit) = delta_limit { + let len = deltas.as_ref().map(|deltas| { + deltas.len() + }).unwrap_or(0); + if len >= limit { + deltas = Err(DeltaListError::Oversized); + } + } + if let Ok(ref mut deltas) = deltas { + deltas.push(DeltaInfo::new(serial, uri, hash)) + } + Ok(()) } - Ok(()) + _ => Err(XmlError::Malformed) } - _ => Err(XmlError::Malformed) - } - }, 100_000_000)? { + }, + 100_000_000 + )? { content.take_end(&mut reader)?; } @@ -776,33 +779,36 @@ pub trait ProcessSnapshot { let mut session_id = None; let mut serial = None; - let mut outer = reader.start_with_limit(|element| { - if element.name() != SNAPSHOT { - info!("Bad outer: not snapshot, but {:?}", element.name()); - return Err(XmlError::Malformed) - } - element.attributes(|name, value| match name { - b"version" => { - if value.ascii_into::()? != 1 { - info!("Bad version"); - return Err(XmlError::Malformed) - } - Ok(()) + let mut outer = reader.start_with_limit( + |element| { + if element.name() != SNAPSHOT { + info!("Bad outer: not snapshot, but {:?}", element.name()); + return Err(XmlError::Malformed) } - b"session_id" => { - session_id = Some(value.ascii_into()?); - Ok(()) - } - b"serial" => { - serial = Some(value.ascii_into()?); - Ok(()) - } - _ => { - info!("Bad attribute on snapshot."); - Err(XmlError::Malformed) - } - }) - }, 100_000_000).map_err(Into::into)?; + element.attributes(|name, value| match name { + b"version" => { + if value.ascii_into::()? != 1 { + info!("Bad version"); + return Err(XmlError::Malformed) + } + Ok(()) + } + b"session_id" => { + session_id = Some(value.ascii_into()?); + Ok(()) + } + b"serial" => { + serial = Some(value.ascii_into()?); + Ok(()) + } + _ => { + info!("Bad attribute on snapshot."); + Err(XmlError::Malformed) + } + }) + }, + 100_000_000 + ).map_err(Into::into)?; match (session_id, serial) { (Some(session_id), Some(serial)) => { @@ -816,22 +822,26 @@ pub trait ProcessSnapshot { loop { let mut uri = None; - let inner = outer.take_opt_element_with_limit(&mut reader, |element| { - if element.name() != PUBLISH { - info!("Bad inner: not publish"); - return Err(ProcessError::malformed()) - } - element.attributes(|name, value| match name { - b"uri" => { - uri = Some(value.ascii_into()?); - Ok(()) - } - _ => { - info!("Bad attribute on publish."); - Err(ProcessError::malformed()) + let inner = outer.take_opt_element_with_limit( + &mut reader, + |element| { + if element.name() != PUBLISH { + info!("Bad inner: not publish"); + return Err(ProcessError::malformed()) } - }) - },100_000_000)?; + element.attributes(|name, value| match name { + b"uri" => { + uri = Some(value.ascii_into()?); + Ok(()) + } + _ => { + info!("Bad attribute on publish."); + Err(ProcessError::malformed()) + } + }) + }, + 100_000_000 + )?; let mut inner = match inner { Some(inner) => inner, None => break diff --git a/src/xml/decode.rs b/src/xml/decode.rs index 4624348a..4968fa43 100644 --- a/src/xml/decode.rs +++ b/src/xml/decode.rs @@ -8,64 +8,7 @@ use quick_xml::name::Namespace; use crate::util::base64; -/// A simple BufRead passthrough proxy that acts as a "trip computer" -/// -/// It keeps track of the amount of bytes read since it was last reset. -/// If a limit is set, it will return an IO error when attempting to read -/// past that limit. -struct BufReadCounter { - reader: R, - trip: u128, - limit: u128 -} - -impl BufReadCounter { - - /// Create a new trip computer (resetting counter) for a BufRead - /// Acts transparently to the implementation of a BufRead below - pub fn new(reader: R) -> Self { - BufReadCounter { - reader, - trip: 0, - limit: 0 - } - } - - /// Reset the amount of bytes read back to 0 - pub fn reset(&mut self) { - self.trip = 0; - } - - /// Set a limit or pass 0 to disable the limit to the maximum bytes to - /// read. This overrides the previous limit. - pub fn limit(&mut self, limit: u128) { - self.limit = limit; - } -} - -impl io::Read for BufReadCounter { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.reader.read(buf) - } -} - -impl io::BufRead for BufReadCounter { - fn fill_buf(&mut self) -> io::Result<&[u8]> { - if self.limit > 0 && self.trip > self.limit { - return Err( - io::Error::new(io::ErrorKind::Other, - format!("Trip is over limit ({:?}/{:?})", - &self.trip, &self.limit)) - ); - } - self.reader.fill_buf() - } - - fn consume(&mut self, amt: usize) { - self.trip += u128::try_from(amt).unwrap_or_default(); - self.reader.consume(amt) - } -} +//------------ Reader -------------------------------------------------------- /// An XML reader. /// @@ -568,6 +511,68 @@ impl Text<'_> { } +//------------ BufReadCounter ------------------------------------------------ + +/// A simple BufRead passthrough proxy that acts as a "trip computer" +/// +/// It keeps track of the amount of bytes read since it was last reset. +/// If a limit is set, it will return an IO error when attempting to read +/// past that limit. +struct BufReadCounter { + reader: R, + trip: u128, + limit: u128 +} + +impl BufReadCounter { + /// Create a new trip computer (resetting counter) for a BufRead. + /// + /// Acts transparently to the implementation of a BufRead below. + pub fn new(reader: R) -> Self { + BufReadCounter { + reader, + trip: 0, + limit: 0 + } + } + + /// Reset the amount of bytes read back to 0 + pub fn reset(&mut self) { + self.trip = 0; + } + + /// Set a limit or pass 0 to disable the limit to the maximum bytes to + /// read. This overrides the previous limit. + pub fn limit(&mut self, limit: u128) { + self.limit = limit; + } +} + +impl io::Read for BufReadCounter { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.reader.read(buf) + } +} + +impl io::BufRead for BufReadCounter { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + if self.limit > 0 && self.trip > self.limit { + return Err( + io::Error::new(io::ErrorKind::Other, + format!("Trip is over limit ({:?}/{:?})", + &self.trip, &self.limit)) + ); + } + self.reader.fill_buf() + } + + fn consume(&mut self, amt: usize) { + self.trip += u128::try_from(amt).unwrap_or_default(); + self.reader.consume(amt) + } +} + + //------------ Error --------------------------------------------------------- #[derive(Debug)] diff --git a/tests/rrdp-resilience.rs b/tests/rrdp-resilience.rs index 2c2213db..c1c10ca4 100644 --- a/tests/rrdp-resilience.rs +++ b/tests/rrdp-resilience.rs @@ -12,7 +12,10 @@ mod tests { use tokio::net::TcpListener; use rpki::rrdp::NotificationFile; - async fn serve(test_bytes: &'static[u8],_: Request) -> Result>, Infallible> { + async fn serve( + test_bytes: &'static[u8], + _: Request + ) -> Result>, Infallible> { let body = Full::new(Bytes::from_static(test_bytes)); Ok(Response::builder() @@ -65,7 +68,9 @@ mod tests { #[tokio::test] async fn test_snapshot_uri() { - let bytes = include_bytes!("../test-data/rrdp/bomb-snapshot-uri.xml.gz"); + let bytes = include_bytes!( + "../test-data/rrdp/bomb-snapshot-uri.xml.gz" + ); assert!(run(bytes).await.is_ok()); } @@ -74,4 +79,4 @@ mod tests { let bytes = include_bytes!("../test-data/rrdp/bomb-whitespace.xml.gz"); assert!(run(bytes).await.is_ok()); } -} \ No newline at end of file +} From d92e0c017b7cc3e79b0ee07cbf12d25d0a977d38 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 20 Jan 2025 14:22:19 +0100 Subject: [PATCH 14/15] Add limits also to deltas. --- src/rrdp.rs | 85 +++++++++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/src/rrdp.rs b/src/rrdp.rs index 6a428790..3443b461 100644 --- a/src/rrdp.rs +++ b/src/rrdp.rs @@ -1096,28 +1096,31 @@ pub trait ProcessDelta { let mut session_id = None; let mut serial = None; - let mut outer = reader.start(|element| { - if element.name() != DELTA { - return Err(ProcessError::malformed()) - } - element.attributes(|name, value| match name { - b"version" => { - if value.ascii_into::()? != 1 { - return Err(ProcessError::malformed()) - } - Ok(()) - } - b"session_id" => { - session_id = Some(value.ascii_into()?); - Ok(()) - } - b"serial" => { - serial = Some(value.ascii_into()?); - Ok(()) + let mut outer = reader.start_with_limit( + |element| { + if element.name() != DELTA { + return Err(ProcessError::malformed()) } - _ => Err(ProcessError::malformed()) - }) - })?; + element.attributes(|name, value| match name { + b"version" => { + if value.ascii_into::()? != 1 { + return Err(ProcessError::malformed()) + } + Ok(()) + } + b"session_id" => { + session_id = Some(value.ascii_into()?); + Ok(()) + } + b"serial" => { + serial = Some(value.ascii_into()?); + Ok(()) + } + _ => Err(ProcessError::malformed()) + }) + }, + 100_000_000, + )?; match (session_id, serial) { (Some(session_id), Some(serial)) => { @@ -1130,24 +1133,28 @@ pub trait ProcessDelta { let mut action = None; let mut uri = None; let mut hash = None; - let inner = outer.take_opt_element(&mut reader, |element| { - match element.name() { - PUBLISH => action = Some(Action::Publish), - WITHDRAW => action = Some(Action::Withdraw), - _ => return Err(ProcessError::malformed()), - }; - element.attributes(|name, value| match name { - b"uri" => { - uri = Some(value.ascii_into()?); - Ok(()) - } - b"hash" => { - hash = Some(value.ascii_into()?); - Ok(()) - } - _ => Err(ProcessError::malformed()) - }) - })?; + let inner = outer.take_opt_element_with_limit( + &mut reader, + |element| { + match element.name() { + PUBLISH => action = Some(Action::Publish), + WITHDRAW => action = Some(Action::Withdraw), + _ => return Err(ProcessError::malformed()), + }; + element.attributes(|name, value| match name { + b"uri" => { + uri = Some(value.ascii_into()?); + Ok(()) + } + b"hash" => { + hash = Some(value.ascii_into()?); + Ok(()) + } + _ => Err(ProcessError::malformed()) + }) + }, + 100_000_000, + )?; let mut inner = match inner { Some(inner) => inner, None => break From 54ef21343c1c953bd3067d6069f995098eb0b3f1 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 20 Jan 2025 14:26:41 +0100 Subject: [PATCH 15/15] Use u64 instead of u128. --- src/xml/decode.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/xml/decode.rs b/src/xml/decode.rs index 4968fa43..a45870b7 100644 --- a/src/xml/decode.rs +++ b/src/xml/decode.rs @@ -30,7 +30,7 @@ impl Reader { } } - pub fn reset_and_limit(&mut self, limit: u128) { + pub fn reset_and_limit(&mut self, limit: u64) { self.reader.get_mut().reset(); self.reader.get_mut().limit(limit); } @@ -67,7 +67,7 @@ impl Reader { } pub fn start_with_limit( - &mut self, op: F, limit: u128) -> Result + &mut self, op: F, limit: u64) -> Result where F: FnOnce(Element) -> Result<(), E>, E: From { self.reset_and_limit(limit); self.start(op) @@ -185,7 +185,7 @@ impl Content { &self, reader: &mut Reader, op: F, - limit: u128 + limit: u64 ) -> Result where R: io::BufRead, F: FnOnce(Element) -> Result<(), E>, E: From { reader.reset_and_limit(limit); @@ -240,7 +240,7 @@ impl Content { &mut self, reader: &mut Reader, op: F, - limit: u128 + limit: u64 ) -> Result, E> where R: io::BufRead, @@ -284,7 +284,7 @@ impl Content { &mut self, reader: &mut Reader, op: F, - limit: u128 + limit: u64 ) -> Result where R: io::BufRead, @@ -520,8 +520,8 @@ impl Text<'_> { /// past that limit. struct BufReadCounter { reader: R, - trip: u128, - limit: u128 + trip: u64, + limit: u64, } impl BufReadCounter { @@ -543,7 +543,7 @@ impl BufReadCounter { /// Set a limit or pass 0 to disable the limit to the maximum bytes to /// read. This overrides the previous limit. - pub fn limit(&mut self, limit: u128) { + pub fn limit(&mut self, limit: u64) { self.limit = limit; } } @@ -567,7 +567,9 @@ impl io::BufRead for BufReadCounter { } fn consume(&mut self, amt: usize) { - self.trip += u128::try_from(amt).unwrap_or_default(); + self.trip = self.trip.saturating_add( + u64::try_from(amt).unwrap_or_default() + ); self.reader.consume(amt) } }