rpz

Response policy zone (RPZ) file generator.
git clone https://git.philomathiclife.com/repos/rpz
Log | Files | Refs | README

commit 06b89378239dbcc1eb0a2679bf8e62171e9d78e8
parent 80e6b40b45a7ae88e0360a888dc661d5aa504ab4
Author: Zack Newman <zack@philomathiclife.com>
Date:   Tue, 30 Jan 2024 21:01:46 -0700

fix eq for domains

Diffstat:
MCargo.toml | 12++++++------
Msrc/dom.rs | 210++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/file.rs | 1+
3 files changed, 125 insertions(+), 98 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" name = "rpz" readme = "README.md" repository = "https://git.philomathiclife.com/repos/rpz/" -version = "0.2.0" +version = "0.2.1" [lib] name = "rpz" @@ -21,12 +21,12 @@ path = "src/main.rs" [dependencies] num-bigint = { version = "0.4.4", default-features = false } -reqwest = { version = "0.11.22", default-features = false, features = ["brotli", "deflate", "gzip", "rustls-tls-native-roots", "trust-dns"] } -serde = { version = "1.0.192", default-features = false } -superset_map = { version = "0.2.1", default-features = false } -tokio = { version = "1.33.0", default-features = false, features = ["rt", "time"] } +reqwest = { version = "0.11.23", default-features = false, features = ["brotli", "deflate", "gzip", "rustls-tls-native-roots", "trust-dns"] } +serde = { version = "1.0.196", default-features = false } +superset_map = { version = "0.2.2", default-features = false } +tokio = { version = "1.35.1", default-features = false, features = ["rt", "time"] } toml = { version = "0.8.8", default-features = false, features = ["parse"] } -url = { version = "2.4.1", default-features = false, features = ["serde"] } +url = { version = "2.5.0", default-features = false, features = ["serde"] } zfc = { version = "0.3.1", default-features = false } [target.'cfg(target_os = "openbsd")'.dependencies] diff --git a/src/dom.rs b/src/dom.rs @@ -16,13 +16,13 @@ use core::convert::{self, AsRef}; use core::fmt::{self, Display, Formatter}; use core::hash::{Hash, Hasher}; use core::iter::FusedIterator; +use core::net::Ipv4Addr; use core::num::NonZeroU8; use core::ops::Deref; use core::str; use num_bigint::BigUint; use std::error; use std::io::{Error, Write}; -use std::net::Ipv4Addr; use superset_map::SetOrd; use zfc::{BoundedCardinality, Cardinality, Set}; /// A flag used to indicate information about the characters @@ -35,15 +35,20 @@ use zfc::{BoundedCardinality, Cardinality, Set}; enum CharFlag { /// No letters, ticks, or underscores. None, - /// No uppercase letters; but there are lowercase letters, ticks, or underscores. - LowerOrTick, - /// No lowercase letters, ticks, or underscores; but there are uppercase letters. + /// Lowercase letters but no uppercase letters, ticks, or underscores. + Lower, + /// Uppercase letters but no lowercase letters, ticks, or underscores. Upper, - /// There are uppercase letters and ticks or underscores. - /// Note this means there can also be lowercase letters, but that is fine. - UpperAndTick, - /// There are both lowercase and uppercase letters, but there are no ticks or underscores. - Mixed, + /// Ticks or underscores but no letters. + Ticks, + /// Uppercase and lowercase letters but no ticks or underscores. + LowerUpper, + /// Lowercase letters and ticks or underscores, but no uppercase letters. + LowerTicks, + /// Uppercase letters and ticks or underscores, but no lowercase letters. + UpperTicks, + /// Uppercase letters, lowercase letters, and ticks or underscores. + All, } impl CharFlag { /// Returns a `bool` that indicates @@ -52,12 +57,16 @@ impl CharFlag { #[inline] const fn eq_ignore_case(self, other: Self) -> bool { match self { - Self::None => false, - Self::LowerOrTick => !matches!(other, Self::None | Self::LowerOrTick), - Self::Upper | Self::UpperAndTick => { - !matches!(other, Self::None | Self::Upper | Self::UpperAndTick) - } - Self::Mixed => !matches!(other, Self::None), + Self::None | Self::Ticks => false, + Self::Lower | Self::LowerTicks => !matches!( + other, + Self::None | Self::Ticks | Self::Lower | Self::LowerTicks + ), + Self::Upper | Self::UpperTicks => !matches!( + other, + Self::None | Self::Ticks | Self::Upper | Self::UpperTicks + ), + Self::LowerUpper | Self::All => !matches!(other, Self::None | Self::Ticks), } } } @@ -205,21 +214,18 @@ impl<'a> Domain<'a> { )] #[inline] fn try_from_slice<'b: 'a>(mut value: &'b [u8]) -> Result<Self, DomainErr> { - if Ipv4Addr::parse_ascii(value).is_ok() { - return Err(DomainErr::Ipv4); - } - value = value.last().map_or(value, |byt| { - if *byt == b'.' { - &value[..value.len() - 1] - } else { - value + value = match value.last() { + None => return Err(DomainErr::Empty), + Some(byt) => { + if *byt == b'.' { + &value[..value.len() - 1] + } else { + value + } } - }); - let len = value.len(); - if len < Self::MIN_LEN.get() as usize { - Err(DomainErr::Empty) - } else if value.len() > Self::MAX_LEN.get() as usize { - Err(DomainErr::LenExceeds253(len)) + }; + if value.len() > Self::MAX_LEN.get() as usize { + Err(DomainErr::LenExceeds253(value.len())) } else { let mut label_lens = Vec::with_capacity(3); let mut label_len = 0; @@ -229,61 +235,72 @@ impl<'a> Domain<'a> { // 2 => uppercase letters; but no lowercase letters, ticks, or underscores. // 4 => ticks or underscores, but no letters. let mut flag = 0u8; - match value.into_iter().try_fold((), |(), byt| { - match *byt { - b'.' => { - return NonZeroU8::new(label_len).map_or( - Err(DomainErr::EmptyLabel), - |length| { - label_lens.push(length); - label_len = 0; - Ok(()) - }, - ) + value + .into_iter() + .try_fold((), |(), byt| { + match *byt { + b'.' => { + return NonZeroU8::new(label_len).map_or( + Err(DomainErr::EmptyLabel), + |length| { + label_lens.push(length); + label_len = 0; + Ok(()) + }, + ) + } + b'A'..=b'Z' => flag |= 2, + b'`' | b'_' => flag |= 4, + b'a'..=b'z' => flag |= 1, + 0..=b' ' + | b'"'..=b'#' + | b'%' + | b'*' + | b'/' + | b':' + | b'<' + | b'>'..=b'@' + | b'['..=b'^' + | b'|' + | 127.. => return Err(DomainErr::InvalidByte(*byt)), + _ => (), } - b'A'..=b'Z' => flag |= 2, - b'`' | b'_' => flag |= 4, - b'a'..=b'z' => flag |= 1, - 0..=b' ' - | b'"'..=b'#' - | b'%' - | b'*' - | b'/' - | b':' - | b'<' - | b'>'..=b'@' - | b'['..=b'^' - | b'|' - | 127.. => return Err(DomainErr::InvalidByte(*byt)), - _ => (), - } - if label_len == 63 { - Err(DomainErr::LabelLenExceeds63) - } else { - label_len += 1; - Ok(()) - } - }) { - Ok(()) => match NonZeroU8::new(label_len) { - Some(length) => { - label_lens.push(length); - Ok(Self { - value, - label_lens, - flag: match flag { - 0 => CharFlag::None, - 1 | 4 | 5 => CharFlag::LowerOrTick, - 2 => CharFlag::Upper, - 3 => CharFlag::Mixed, - 6 | 7 => CharFlag::UpperAndTick, - _ => unreachable!("there is a bug in Domain::try_from_slice"), - }, - }) + if label_len == 63 { + Err(DomainErr::LabelLenExceeds63) + } else { + label_len += 1; + Ok(()) } - None => Err(DomainErr::EmptyLabel), - }, - Err(err) => Err(err), - } + }) + .and_then(|()| { + NonZeroU8::new(label_len) + .ok_or(DomainErr::EmptyLabel) + .and_then(|length| { + Ipv4Addr::parse_ascii(value).map_or_else( + |_| { + label_lens.push(length); + Ok(Self { + value, + label_lens, + flag: match flag { + 0 => CharFlag::None, + 1 => CharFlag::Lower, + 2 => CharFlag::Upper, + 3 => CharFlag::LowerUpper, + 4 => CharFlag::Ticks, + 5 => CharFlag::LowerTicks, + 6 => CharFlag::UpperTicks, + 7 => CharFlag::All, + _ => unreachable!( + "there is a bug in Domain::try_from_slice" + ), + }, + }) + }, + |_| Err(DomainErr::Ipv4), + ) + }) + }) } } } @@ -328,18 +345,21 @@ fn cmp_doms(left: &Domain<'_>, right: &Domain<'_>) -> Result<(), Ordering> { let (left_ref, right_ref) = match (left.flag, right.flag) { (CharFlag::None, _) | (_, CharFlag::None) - | (CharFlag::LowerOrTick, CharFlag::LowerOrTick) + | ( + CharFlag::Lower | CharFlag::Ticks | CharFlag::LowerTicks, + CharFlag::Lower | CharFlag::Ticks | CharFlag::LowerTicks, + ) | (CharFlag::Upper, CharFlag::Upper) => (left, right), - (CharFlag::LowerOrTick, _) => { + (CharFlag::Lower | CharFlag::LowerTicks | CharFlag::Ticks, _) => { right_input = right.value.to_ascii_lowercase(); right_dom = Domain { value: right_input.as_slice(), label_lens: right.label_lens.clone(), - flag: CharFlag::LowerOrTick, + flag: CharFlag::LowerTicks, }; (left, &right_dom) } - (CharFlag::Upper, CharFlag::Mixed) => { + (CharFlag::Upper, CharFlag::LowerUpper) => { right_input = right.value.to_ascii_uppercase(); right_dom = Domain { value: right_input.as_slice(), @@ -348,16 +368,16 @@ fn cmp_doms(left: &Domain<'_>, right: &Domain<'_>) -> Result<(), Ordering> { }; (left, &right_dom) } - (_, CharFlag::LowerOrTick) => { + (_, CharFlag::Lower | CharFlag::Ticks | CharFlag::LowerTicks) => { left_input = left.value.to_ascii_lowercase(); left_dom = Domain { value: left_input.as_slice(), label_lens: left.label_lens.clone(), - flag: CharFlag::LowerOrTick, + flag: CharFlag::LowerTicks, }; (&left_dom, right) } - (CharFlag::Mixed, CharFlag::Upper) => { + (CharFlag::LowerUpper, CharFlag::Upper) => { left_input = left.value.to_ascii_uppercase(); left_dom = Domain { value: left_input.as_slice(), @@ -371,13 +391,13 @@ fn cmp_doms(left: &Domain<'_>, right: &Domain<'_>) -> Result<(), Ordering> { left_dom = Domain { value: left_input.as_slice(), label_lens: left.label_lens.clone(), - flag: CharFlag::LowerOrTick, + flag: CharFlag::LowerTicks, }; right_input = right.value.to_ascii_lowercase(); right_dom = Domain { value: right_input.as_slice(), label_lens: right.label_lens.clone(), - flag: CharFlag::LowerOrTick, + flag: CharFlag::LowerTicks, }; (&left_dom, &right_dom) } @@ -418,7 +438,9 @@ impl Hash for Domain<'_> { #[inline] fn hash<H: Hasher>(&self, state: &mut H) { match self.flag { - CharFlag::None | CharFlag::LowerOrTick => self.value.hash(state), + CharFlag::None | CharFlag::Lower | CharFlag::Ticks | CharFlag::LowerTicks => { + self.value.hash(state); + } _ => self.value.to_ascii_lowercase().hash(state), } } @@ -2650,6 +2672,10 @@ mod tests { assert!(Domain::try_from("wwW.ExAMple.COm") .map_or(false, |d| Domain::try_from("www.example.com") .map_or(false, |d2| d == d2 && d.cmp(&d2) == Ordering::Equal))); + assert!( + Domain::try_from("ww_W.com").map_or(false, |d| Domain::try_from("Ww_w.com") + .map_or(false, |d2| d == d2 && d.cmp(&d2) == Ordering::Equal)) + ); // Test valid bytes let mut vec = Vec::new(); let mut counter = 0; diff --git a/src/file.rs b/src/file.rs @@ -858,6 +858,7 @@ impl ExternalFiles { } /// Downloads the files from `urls` and converts them to `String`s adding the /// tasks to `set`. + #[allow(clippy::iter_over_hash_type)] #[inline] fn get_external_files( set: &mut JoinSet<Result<File, ExtFileErr>>,