diff --git a/Cargo.toml b/Cargo.toml index 77e7073..e56906c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ description = "Rust reimplementation of important ldns programs." categories = ["command-line-utilities"] license = "BSD-3-Clause" keywords = ["DNS", "domain", "ldns"] -rust-version = "1.80" +rust-version = "1.85" [[bin]] name = "ldns" @@ -34,7 +34,7 @@ clap = { version = "4.3.4", features = ["cargo", "derive", "wrap_help"] } # Until a release of domain is made that includes the features we need, pin # to a specific commit of the domain main branch to ensure that the dnst main # branch does not get broken if domain main is not stable. -domain = { git = "https://github.com/NLnetLabs/domain.git", rev = "17fb0e38120c9939ca28462af082d88ae8bc8b1d", features = [ +domain = { git = "https://github.com/NLnetLabs/domain.git", branch = "main", features = [ "bytes", "net", "resolv", @@ -69,7 +69,7 @@ const_format = " 0.2.33" test_bin = "0.4.0" tempfile = "3.20.0" regex = "1.11.1" -domain = { git = "https://github.com/NLnetLabs/domain.git", rev = "17fb0e38120c9939ca28462af082d88ae8bc8b1d", features = [ +domain = { git = "https://github.com/NLnetLabs/domain.git", branch = "main", features = [ "unstable-stelline", ] } pretty_assertions = "1.4.1" diff --git a/src/commands/nsec3hash.rs b/src/commands/nsec3hash.rs index 54cde07..27e8ddf 100644 --- a/src/commands/nsec3hash.rs +++ b/src/commands/nsec3hash.rs @@ -1,9 +1,11 @@ +use std::char::from_u32; use std::ffi::OsString; use std::str::FromStr; use clap::builder::ValueParser; use domain::base::iana::nsec3::Nsec3HashAlgorithm; use domain::base::name::Name; +use domain::base::NameBuilder; use domain::dnssec::common::nsec3_hash; use domain::rdata::nsec3::Nsec3Salt; use lexopt::Arg; @@ -47,6 +49,9 @@ pub struct Nsec3Hash { )] salt: Nsec3Salt>, + #[arg(long = "find-prefix")] + find_prefix: Option, + /// The domain name to hash #[arg(value_name = "DOMAIN NAME", value_parser = ValueParser::new(parse_name))] name: Name>, @@ -111,6 +116,7 @@ impl LdnsCommand for Nsec3Hash { algorithm, iterations, salt, + find_prefix: None, name, }))) } @@ -153,6 +159,16 @@ impl Nsec3Hash { impl Nsec3Hash { pub fn execute(self, env: impl Env) -> Result<(), Error> { + if let Some(prefix) = self.find_prefix { + return find_prefix( + &prefix, + &self.name, + self.algorithm, + self.iterations, + &self.salt, + env, + ); + } let hash = nsec3_hash::<_, _, Vec>(&self.name, self.algorithm, self.iterations, &self.salt) .map_err(|err| format!("Error creating NSEC3 hash: {err}"))? @@ -163,6 +179,46 @@ impl Nsec3Hash { } } +fn find_prefix( + prefix: &str, + origin_name: &Name>, + algorithm: Nsec3HashAlgorithm, + iterations: u16, + salt: &Nsec3Salt>, + env: impl Env, +) -> Result<(), Error> { + for u in 0.. { + let label = number_to_label(u); + let mut name = NameBuilder::>::new(); + name.append_chars(label).expect("should not fail"); + let name = name.append_origin(&origin_name).expect("should not fail"); + let hash = nsec3_hash::<_, _, Vec>(&name, algorithm, iterations, salt) + .map_err(|err| format!("Error creating NSEC3 hash: {err}"))? + .to_string() + .to_lowercase(); + if hash.starts_with(prefix) { + writeln!(env.stdout(), "{name}"); + writeln!(env.stdout(), "({hash})"); + return Ok(()); + } + } + panic!("Nothing found for pattern {prefix}"); +} + +/// Create a label for a number using lower case letters. +fn number_to_label(mut n: u64) -> Vec { + let mut label = Vec::::new(); + while n != 0 { + let r = n % 26; + n /= 26; + label.push(from_u32('a' as u32 + r as u32).expect("should not fail")); + } + if label.is_empty() { + label.push('a') + } + label +} + // These are just basic tests as there is very little code in this module, the // actual NSEC3 generation should be tested as part of the domain crate. #[cfg(test)] @@ -202,6 +258,7 @@ mod tests { algorithm, iterations: 0, salt: Nsec3Salt::empty(), + find_prefix: None, name: Name::root(), }; nsec3_hash.execute(&env).unwrap(); @@ -212,6 +269,7 @@ mod tests { algorithm: Nsec3HashAlgorithm::SHA1, iterations, salt: Nsec3Salt::empty(), + find_prefix: None, name: Name::root(), }; nsec3_hash.execute(&env).unwrap(); @@ -224,6 +282,7 @@ mod tests { algorithm: Nsec3HashAlgorithm::SHA1, iterations: 0, salt, + find_prefix: None, name: Name::root(), }; nsec3_hash.execute(&env).unwrap(); @@ -239,6 +298,7 @@ mod tests { algorithm: Nsec3HashAlgorithm::SHA1, iterations: 0, salt: Nsec3Salt::empty(), + find_prefix: None, name, }; nsec3_hash.execute(&env).unwrap();