From ac99703d789e17fe42a0479077a8eadde43dfbc1 Mon Sep 17 00:00:00 2001 From: juxhin Date: Mon, 25 Sep 2023 19:55:33 +0100 Subject: [PATCH 1/3] misc(permutation): wip: refactor permutations --- twistrs/src/permutate.rs | 89 ++++++++++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 18 deletions(-) diff --git a/twistrs/src/permutate.rs b/twistrs/src/permutate.rs index bf415ff..742b46e 100644 --- a/twistrs/src/permutate.rs +++ b/twistrs/src/permutate.rs @@ -34,9 +34,9 @@ include!(concat!(env!("OUT_DIR"), "/data.rs")); /// Wrapper around an FQDN to perform permutations against. #[derive(Default, Debug, Serialize)] -pub struct Domain<'a> { +pub struct Domain { /// The domain FQDN to generate permutations from. - pub fqdn: &'a str, + pub fqdn: String, /// The top-level domain of the FQDN (e.g. `.com`). tld: String, @@ -45,6 +45,29 @@ pub struct Domain<'a> { domain: String, } +#[derive(Debug, Eq, PartialEq)] +pub struct Permutation { + domain: Domain, + kind: PermutationKind, +} + +#[derive(Clone, Copy, Debug)] +pub enum PermutationKind { + Addition, + Bitsquatting, + Hyphenation, + Insertion, + Omission, + Repetition, + Replacement, + Subdomain, + Transposition, + VowelSwap, + Keyword, + Tld, + Homoglyph, +} + #[derive(Clone, thiserror::Error, Debug)] pub enum PermutationError { #[error("invalid domain name, (expected {expected:?}, found {found:?})")] @@ -54,11 +77,11 @@ pub enum PermutationError { InvalidHomoglyph { domain: String, homoglyph: String }, } -impl<'a> Domain<'a> { +impl Domain { /// Wrap a desired FQDN into a `Domain` container. Internally /// will perform additional operations to break the domain into /// one or more chunks to be used during domain permutations. - pub fn new(fqdn: &'a str) -> Result, Error> { + pub fn new(fqdn: &str) -> Result { let parsed_domain = List.parse_domain_name(fqdn) .map_err(|_| PermutationError::InvalidDomain { @@ -82,7 +105,11 @@ impl<'a> Domain<'a> { })? .to_string(); - Ok(Domain { fqdn, tld, domain }) + Ok(Domain { + fqdn: fqdn.to_string(), + tld, + domain, + }) } /// Generate any and all possible domain permutations for a given `Domain`. @@ -111,12 +138,14 @@ impl<'a> Domain<'a> { /// Add every ASCII lowercase character between the Domain /// (e.g. `google`) and top-level domain (e.g. `.com`). - pub fn addition(&self) -> impl Iterator + '_ { - Domain::filter_domains( - ASCII_LOWER - .iter() - .map(move |c| format!("{}{}.{}", self.domain, c, self.tld)), - ) + pub fn addition(&self) -> impl Iterator + '_ { + Domain::filter_domains(ASCII_LOWER.iter().map(move |c| { + let fqdn = format!("{}{}.{}", self.domain, c, self.tld); + Permutation { + domain: Domain::new(fqdn.as_str()).unwrap(), + kind: PermutationKind::Addition, + } + })) } /// Following implementation takes inspiration from the following content: @@ -136,7 +165,7 @@ impl<'a> Domain<'a> { /// 10000000 ^ chr /// /// Then check if the resulting bit operation falls within ASCII range. - pub fn bitsquatting(&self) -> impl Iterator + '_ { + pub fn bitsquatting(&self) -> impl Iterator + '_ { let permutations = self .fqdn .chars() @@ -162,14 +191,18 @@ impl<'a> Domain<'a> { } }) }) - .flatten(); + .flatten() + .map(move |fqdn| Permutation { + domain: Domain::new(fqdn.as_str()).unwrap(), + kind: PermutationKind::Bitsquatting, + }); Domain::filter_domains(permutations) } /// Permutation method that replaces ASCII characters with multiple homoglyphs /// similar to the respective ASCII character. - pub fn homoglyph(&self) -> Result + '_, Error> { + pub fn homoglyph(&self) -> Result + '_, Error> { // @CLEANUP(jdb): Tidy this entire mess up let mut result_first_pass: HashSet = HashSet::new(); let mut result_second_pass: HashSet = HashSet::new(); @@ -245,6 +278,22 @@ impl<'a> Domain<'a> { } } + let result_first_pass = result_first_pass + .into_iter() + .map(move |fqdn| Permutation { + domain: Domain::new(fqdn.as_str()).unwrap(), + kind: PermutationKind::Homoglyph, + }) + .collect::>(); + + let result_second_pass = result_second_pass + .into_iter() + .map(move |fqdn| Permutation { + domain: Domain::new(fqdn.as_str()).unwrap(), + kind: PermutationKind::Homoglyph, + }) + .collect::>(); + Ok(Domain::filter_domains( (&result_first_pass | &result_second_pass).into_iter(), )) @@ -437,12 +486,16 @@ impl<'a> Domain<'a> { /// /// 2nd pass - simple regular expression pass to see if the resulting domains /// are indeed valid domains. - pub fn filter_domains(permutations: T) -> impl Iterator + pub fn filter_domains

(permutations: P) -> impl Iterator where - T: Iterator, + P: Iterator, { - permutations - .filter(|x| is_valid_punycode(x) && IDNA_FILTER_REGEX.is_match(x).unwrap_or(false)) + permutations.filter(|x| { + is_valid_punycode(x.domain.fqdn.as_str()) + && IDNA_FILTER_REGEX + .is_match(x.domain.fqdn.as_str()) + .unwrap_or(false) + }) } } From 79625ac31964fbe09d4539e89fe2348c243392b9 Mon Sep 17 00:00:00 2001 From: juxhin Date: Wed, 27 Sep 2023 21:03:35 +0100 Subject: [PATCH 2/3] misc(permute): refactor all signature to return permutation data --- twistrs/src/permutate.rs | 109 +++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 38 deletions(-) diff --git a/twistrs/src/permutate.rs b/twistrs/src/permutate.rs index 742b46e..0bfaee6 100644 --- a/twistrs/src/permutate.rs +++ b/twistrs/src/permutate.rs @@ -33,7 +33,7 @@ use serde::Serialize; include!(concat!(env!("OUT_DIR"), "/data.rs")); /// Wrapper around an FQDN to perform permutations against. -#[derive(Default, Debug, Serialize)] +#[derive(Clone, Hash, Default, Debug, Serialize, Eq, PartialEq)] pub struct Domain { /// The domain FQDN to generate permutations from. pub fqdn: String, @@ -45,13 +45,13 @@ pub struct Domain { domain: String, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Clone, Hash, Debug, Eq, PartialEq)] pub struct Permutation { domain: Domain, kind: PermutationKind, } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Hash, Debug, Eq, PartialEq)] pub enum PermutationKind { Addition, Bitsquatting, @@ -119,7 +119,7 @@ impl Domain { /// /// Any future permutations will also be included into this function call /// without any changes required from any client implementations. - pub fn all(&self) -> Result + '_, Error> { + pub fn all(&self) -> Result + '_, Error> { Ok(self .addition() .chain(self.bitsquatting()) @@ -301,18 +301,22 @@ impl Domain { /// Permutation method that inserts hyphens (i.e. `-`) between each /// character in the domain where valid. - pub fn hyphentation(&self) -> impl Iterator + '_ { + pub fn hyphentation(&self) -> impl Iterator + '_ { Domain::filter_domains(self.fqdn.chars().skip(1).enumerate().map(move |(i, _)| { let mut permutation = self.fqdn.to_string(); permutation.insert(i, '-'); - permutation + + Permutation { + domain: Domain::new(permutation.as_str()).unwrap(), + kind: PermutationKind::Hyphenation, + } })) } /// Permutation method that inserts specific characters that are close to /// any character in the domain depending on the keyboard (e.g. `Q` next /// to `W` in qwerty keyboard layout. - pub fn insertion(&self) -> impl Iterator + '_ { + pub fn insertion(&self) -> impl Iterator + '_ { Domain::filter_domains( self.fqdn .chars() @@ -327,7 +331,11 @@ impl Domain { keyboard_chars.chars().map(move |keyboard_char| { let mut permutation = self.fqdn.to_string(); permutation.insert(i, keyboard_char); - permutation + + Permutation { + domain: Domain::new(permutation.as_str()).unwrap(), + kind: PermutationKind::Insertion, + } }) }) }) @@ -337,20 +345,28 @@ impl Domain { } /// Permutation method that selectively removes a character from the domain. - pub fn omission(&self) -> impl Iterator + '_ { + pub fn omission(&self) -> impl Iterator + '_ { Domain::filter_domains(self.fqdn.chars().enumerate().map(move |(i, _)| { let mut permutation = self.fqdn.to_string(); permutation.remove(i); - permutation + + Permutation { + domain: Domain::new(permutation.as_str()).unwrap(), + kind: PermutationKind::Omission, + } })) } /// Permutation method that repeats characters twice provided they are /// alphabetic characters (e.g. `google.com` -> `gooogle.com`). - pub fn repetition(&self) -> impl Iterator + '_ { + pub fn repetition(&self) -> impl Iterator + '_ { Domain::filter_domains(self.fqdn.chars().enumerate().filter_map(move |(i, c)| { if c.is_alphabetic() { - Some(format!("{}{}{}", &self.fqdn[..=i], c, &self.fqdn[i + 1..])) + let permutation = format!("{}{}{}", &self.fqdn[..=i], c, &self.fqdn[i + 1..]); + Some(Permutation { + domain: Domain::new(permutation.as_str()).unwrap(), + kind: PermutationKind::Repetition, + }) } else { None } @@ -359,7 +375,7 @@ impl Domain { /// Permutation method similar to insertion, except that it replaces a given /// character with another character in proximity depending on keyboard layout. - pub fn replacement(&self) -> impl Iterator + '_ { + pub fn replacement(&self) -> impl Iterator + '_ { Self::filter_domains( self.fqdn .chars() @@ -370,12 +386,17 @@ impl Domain { KEYBOARD_LAYOUTS.iter().filter_map(move |layout| { layout.get(&c).map(move |keyboard_chars| { keyboard_chars.chars().map(move |keyboard_char| { - format!( + let permutation = format!( "{}{}{}", &self.fqdn[..i], keyboard_char, &self.fqdn[i + 1..] - ) + ); + + Permutation { + domain: Domain::new(permutation.as_str()).unwrap(), + kind: PermutationKind::Replacement, + } }) }) }) @@ -384,7 +405,7 @@ impl Domain { ) } - pub fn subdomain(&self) -> impl Iterator + '_ { + pub fn subdomain(&self) -> impl Iterator + '_ { Domain::filter_domains( self.fqdn .chars() @@ -395,7 +416,11 @@ impl Domain { if ['-', '.'].iter().all(|x| [c1, c2].contains(x)) { None } else { - Some(format!("{}.{}", &self.fqdn[..i2], &self.fqdn[i2..])) + let permutation = format!("{}.{}", &self.fqdn[..i2], &self.fqdn[i2..]); + Some(Permutation { + domain: Domain::new(permutation.as_str()).unwrap(), + kind: PermutationKind::Subdomain, + }) } }), ) @@ -403,19 +428,18 @@ impl Domain { /// Permutation method that swaps out characters in the domain (e.g. /// `google.com` -> `goolge.com`). - pub fn transposition(&self) -> impl Iterator + '_ { + pub fn transposition(&self) -> impl Iterator + '_ { Domain::filter_domains(self.fqdn.chars().enumerate().tuple_windows().filter_map( move |((i1, c1), (i2, c2))| { if c1 == c2 { None } else { - Some(format!( - "{}{}{}{}", - &self.fqdn[..i1], - c2, - c1, - &self.fqdn[i2 + 1..] - )) + let permutation = + format!("{}{}{}{}", &self.fqdn[..i1], c2, c1, &self.fqdn[i2 + 1..]); + Some(Permutation { + domain: Domain::new(permutation.as_str()).unwrap(), + kind: PermutationKind::Transposition, + }) } }, )) @@ -423,7 +447,7 @@ impl Domain { /// Permutation method that swaps vowels for other vowels (e.g. /// `google.com` -> `gougle.com`). - pub fn vowel_swap(&self) -> impl Iterator + '_ { + pub fn vowel_swap(&self) -> impl Iterator + '_ { Domain::filter_domains( self.fqdn .chars() @@ -434,12 +458,13 @@ impl Domain { if *vowel == c { None } else { - Some(format!( - "{}{}{}", - &self.fqdn[..i], - vowel, - &self.fqdn[i + 1..] - )) + let permutation = + format!("{}{}{}", &self.fqdn[..i], vowel, &self.fqdn[i + 1..]); + + Some(Permutation { + domain: Domain::new(permutation.as_str()).unwrap(), + kind: PermutationKind::VowelSwap, + }) } })) } else { @@ -457,7 +482,7 @@ impl Domain { /// 2. Prepend keyword (e.g. `foo.com` -> `wordfoo.com`) /// 3. Append keyword and dash (e.g. `foo.com` -> `foo-word.com`) /// 4. Append keyword and dash (e.g. `foo.com` -> `fooword.com`) - pub fn keyword(&self) -> impl Iterator + '_ { + pub fn keyword(&self) -> impl Iterator + '_ { Domain::filter_domains(KEYWORDS.iter().flat_map(move |keyword| { vec![ format!("{}-{}.{}", &self.domain, keyword, &self.tld), @@ -465,16 +490,24 @@ impl Domain { format!("{}-{}.{}", keyword, &self.domain, &self.tld), format!("{}{}.{}", keyword, &self.domain, &self.tld), ] + .into_iter() + .map(move |fqdn| Permutation { + domain: Domain::new(fqdn.as_str()).unwrap(), + kind: PermutationKind::Keyword, + }) })) } /// Permutation method that replaces all TLDs as variations of the /// root domain passed. - pub fn tld(&self) -> impl Iterator + '_ { - Domain::filter_domains( - TLDS.iter() - .map(move |tld| format!("{}.{}", &self.domain, tld)), - ) + pub fn tld(&self) -> impl Iterator + '_ { + Domain::filter_domains(TLDS.iter().map(move |tld| { + let fqdn = format!("{}.{}", &self.domain, tld); + Permutation { + domain: Domain::new(fqdn.as_str()).unwrap(), + kind: PermutationKind::Tld, + } + })) } /// Utility function that filters an iterator of domains that are valid. This From 03d1aa129749ff1888142c6d87a6d1c93da08efe Mon Sep 17 00:00:00 2001 From: juxhin Date: Sat, 30 Sep 2023 14:11:30 +0100 Subject: [PATCH 3/3] misc(examples): update examples to use - Clean up permutation - Clippy warnings --- examples/twistrs-cli/src/main.rs | 8 ++-- examples/twistrs-grpc/src/client.rs | 21 ++++------ examples/twistrs-grpc/src/server.rs | 8 ++-- examples/twistrs-ws/src/main.rs | 7 ++-- twistrs/src/enrich.rs | 2 +- twistrs/src/permutate.rs | 59 ++++++++++++----------------- 6 files changed, 43 insertions(+), 62 deletions(-) diff --git a/examples/twistrs-cli/src/main.rs b/examples/twistrs-cli/src/main.rs index fa86b17..c59fd70 100644 --- a/examples/twistrs-cli/src/main.rs +++ b/examples/twistrs-cli/src/main.rs @@ -3,7 +3,7 @@ use colored::*; use tokio::sync::mpsc; use twistrs::enrich::DomainMetadata; -use twistrs::permutate::Domain; +use twistrs::permutate::{Domain, Permutation}; use anyhow::Result; use std::collections::HashSet; @@ -21,15 +21,13 @@ async fn main() -> Result<()> { let domain = Domain::new(matches.value_of("domain").unwrap()).unwrap(); - let mut domain_permutations = domain.all()?.collect::>(); + let domain_permutations = domain.all()?.collect::>(); let domain_permutation_count = domain_permutations.len(); - domain_permutations.insert(String::from(domain.fqdn)); - let (tx, mut rx) = mpsc::channel(5000); for (i, v) in domain_permutations.into_iter().enumerate() { - let domain_metadata = DomainMetadata::new(v.clone()); + let domain_metadata = DomainMetadata::new(v.domain.fqdn.clone()); let mut tx = tx.clone(); tokio::spawn(async move { diff --git a/examples/twistrs-grpc/src/client.rs b/examples/twistrs-grpc/src/client.rs index 57a9e47..7faf585 100644 --- a/examples/twistrs-grpc/src/client.rs +++ b/examples/twistrs-grpc/src/client.rs @@ -3,22 +3,19 @@ use domain_enumeration::Fqdn; mod domain_enumeration; - #[tokio::main] async fn main() -> Result<(), Box> { let channel = tonic::transport::Channel::from_static("http://127.0.0.1:8080") .connect() .await?; - + let mut client = DomainEnumerationClient::new(channel); println!("[+] Starting DNS resolutions..."); - let request = tonic::Request::new( - Fqdn { - fqdn:String::from("google.com") - }, - ); + let request = tonic::Request::new(Fqdn { + fqdn: String::from("google.com"), + }); let mut response = client.send_dns_resolution(request).await?.into_inner(); @@ -28,11 +25,9 @@ async fn main() -> Result<(), Box> { println!("[+] Starting MX Checks..."); - let request = tonic::Request::new( - Fqdn { - fqdn:String::from("google.com") - }, - ); + let request = tonic::Request::new(Fqdn { + fqdn: String::from("google.com"), + }); let mut response = client.send_mx_check(request).await?.into_inner(); @@ -41,4 +36,4 @@ async fn main() -> Result<(), Box> { } Ok(()) -} \ No newline at end of file +} diff --git a/examples/twistrs-grpc/src/server.rs b/examples/twistrs-grpc/src/server.rs index bf5668d..6bcb998 100644 --- a/examples/twistrs-grpc/src/server.rs +++ b/examples/twistrs-grpc/src/server.rs @@ -25,7 +25,7 @@ impl DomainEnumeration for DomainEnumerationService { let (tx, rx) = mpsc::channel(64); for permutation in Domain::new(&request.get_ref().fqdn).unwrap().all().unwrap() { - let domain_metadata = DomainMetadata::new(permutation.clone()); + let domain_metadata = DomainMetadata::new(permutation.domain.fqdn.clone()); let mut tx = tx.clone(); // Spawn DNS Resolution check @@ -34,7 +34,7 @@ impl DomainEnumeration for DomainEnumerationService { if let Some(ips) = metadata.ips { if tx .send(Ok(DomainEnumerationResponse { - fqdn: permutation.clone().to_string(), + fqdn: permutation.domain.fqdn.to_string(), ips: ips.into_iter().map(|x| format!("{}", x)).collect(), })) .await @@ -62,7 +62,7 @@ impl DomainEnumeration for DomainEnumerationService { let (tx, rx) = mpsc::channel(64); for permutation in Domain::new(&request.get_ref().fqdn).unwrap().all().unwrap() { - let domain_metadata = DomainMetadata::new(permutation.clone()); + let domain_metadata = DomainMetadata::new(permutation.domain.fqdn.clone()); let mut tx = tx.clone(); // Spawn DNS Resolution check @@ -71,7 +71,7 @@ impl DomainEnumeration for DomainEnumerationService { if let Some(smtp) = metadata.smtp { if tx .send(Ok(MxCheckResponse { - fqdn: permutation.clone().to_string(), + fqdn: permutation.domain.fqdn.to_string(), is_positive: smtp.is_positive, message: smtp.message, })) diff --git a/examples/twistrs-ws/src/main.rs b/examples/twistrs-ws/src/main.rs index 1bfa4d0..093b255 100644 --- a/examples/twistrs-ws/src/main.rs +++ b/examples/twistrs-ws/src/main.rs @@ -11,7 +11,7 @@ use warp::ws::{Message, WebSocket}; use warp::Filter; use twistrs::enrich::DomainMetadata; -use twistrs::permutate::Domain; +use twistrs::permutate::{Domain, Permutation}; /// Our global unique user id counter. static NEXT_USER_ID: AtomicUsize = AtomicUsize::new(1); @@ -93,11 +93,10 @@ async fn user_message(my_id: usize, msg: Message, users: &Users) { eprintln!("initiating dns resolution checks for user: {}", my_id); let domain = Domain::new(msg).unwrap(); - let mut domain_permutations = domain.all().unwrap().collect::>(); - domain_permutations.insert(String::from(domain.fqdn)); + let domain_permutations = domain.all().unwrap().collect::>(); for v in domain_permutations.into_iter() { - let domain_metadata = DomainMetadata::new(v.clone()); + let domain_metadata = DomainMetadata::new(v.domain.fqdn.clone()); let tx = tx.clone(); tokio::spawn(async move { diff --git a/twistrs/src/enrich.rs b/twistrs/src/enrich.rs index 720a209..c9ac89a 100644 --- a/twistrs/src/enrich.rs +++ b/twistrs/src/enrich.rs @@ -175,7 +175,7 @@ impl DomainMetadata { ); let stream = BufStream::new( - TcpStream::connect(&format!("{}:25", self.fqdn.to_string())) + TcpStream::connect(&format!("{}:25", self.fqdn)) .await .map_err(|e| EnrichmentError::SmtpLookupError { domain: self.fqdn.clone(), diff --git a/twistrs/src/permutate.rs b/twistrs/src/permutate.rs index 0bfaee6..9064528 100644 --- a/twistrs/src/permutate.rs +++ b/twistrs/src/permutate.rs @@ -47,8 +47,8 @@ pub struct Domain { #[derive(Clone, Hash, Debug, Eq, PartialEq)] pub struct Permutation { - domain: Domain, - kind: PermutationKind, + pub domain: Domain, + pub kind: PermutationKind, } #[derive(Clone, Copy, Hash, Debug, Eq, PartialEq)] @@ -204,14 +204,12 @@ impl Domain { /// similar to the respective ASCII character. pub fn homoglyph(&self) -> Result + '_, Error> { // @CLEANUP(jdb): Tidy this entire mess up - let mut result_first_pass: HashSet = HashSet::new(); - let mut result_second_pass: HashSet = HashSet::new(); - - let fqdn = self.fqdn.to_string().chars().collect::>(); + let mut result_first_pass: HashSet = HashSet::new(); + let mut result_second_pass: HashSet = HashSet::new(); for ws in 1..self.fqdn.len() { for i in 0..(self.fqdn.len() - ws) + 1 { - let win: String = fqdn[i..i + ws].iter().collect(); + let win: String = self.fqdn[i..i + ws].to_string().chars().collect(); let mut j = 0; while j < ws { @@ -226,12 +224,14 @@ impl Domain { if let Some(glyph) = HOMOGLYPHS.get(&c) { for g in glyph.chars().collect::>() { let new_win = win.replace(c, &g.to_string()); - result_first_pass.insert(format!( - "{}{}{}", - &self.fqdn[..i], - &new_win, - &self.fqdn[i + ws..] - )); + + let fqdn = + format!("{}{}{}", &self.fqdn[..i], &new_win, &self.fqdn[i + ws..]); + + result_first_pass.insert(Permutation { + domain: Domain::new(fqdn.as_str()).unwrap(), + kind: PermutationKind::Homoglyph, + }); } } @@ -240,13 +240,14 @@ impl Domain { } } - for domain in &result_first_pass { - for ws in 1..fqdn.len() { - for i in 0..(fqdn.len() - ws) + 1 { + for permutation in &result_first_pass { + for ws in 1..self.fqdn.len() { + for i in 0..(self.fqdn.len() - ws) + 1 { // We need to do this as we are dealing with UTF8 characters // meaning that we cannot simple iterate over single byte // values (as certain characters are composed of two or more) - let win: String = domain.chars().collect::>()[i..i + ws] + let win: String = permutation.domain.fqdn.chars().collect::>() + [i..i + ws] .iter() .collect(); let mut j = 0; @@ -263,12 +264,16 @@ impl Domain { if let Some(glyph) = HOMOGLYPHS.get(&c) { for g in glyph.chars().collect::>() { let new_win = win.replace(c, &g.to_string()); - result_second_pass.insert(format!( + let fqdn = format!( "{}{}{}", &self.fqdn[..i], &new_win, &self.fqdn[i + ws..] - )); + ); + result_second_pass.insert(Permutation { + domain: Domain::new(fqdn.as_str()).unwrap(), + kind: PermutationKind::Homoglyph, + }); } } @@ -278,22 +283,6 @@ impl Domain { } } - let result_first_pass = result_first_pass - .into_iter() - .map(move |fqdn| Permutation { - domain: Domain::new(fqdn.as_str()).unwrap(), - kind: PermutationKind::Homoglyph, - }) - .collect::>(); - - let result_second_pass = result_second_pass - .into_iter() - .map(move |fqdn| Permutation { - domain: Domain::new(fqdn.as_str()).unwrap(), - kind: PermutationKind::Homoglyph, - }) - .collect::>(); - Ok(Domain::filter_domains( (&result_first_pass | &result_second_pass).into_iter(), ))