├── .gitignore
├── .idea
├── .gitignore
├── modules.xml
├── ripgen.iml
└── vcs.xml
├── Cargo.toml
├── README.md
├── ripgen
├── Cargo.toml
├── README.md
└── src
│ ├── args.rs
│ └── main.rs
└── ripgen_lib
├── Cargo.toml
├── README.md
└── src
├── chain.rs
├── dnsgen
├── dash.rs
├── mod.rs
├── numbers.rs
├── permute.rs
└── swap.rs
├── domain.rs
├── error.rs
├── lib.rs
├── manager.rs
├── transform.rs
└── words.rs
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.toptal.com/developers/gitignore/api/rust,clion
3 | # Edit at https://www.toptal.com/developers/gitignore?templates=rust,clion
4 |
5 | ### CLion ###
6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
8 |
9 | # User-specific stuff
10 | .idea/**/workspace.xml
11 | .idea/**/tasks.xml
12 | .idea/**/usage.statistics.xml
13 | .idea/**/dictionaries
14 | .idea/**/shelf
15 |
16 | # AWS User-specific
17 | .idea/**/aws.xml
18 |
19 | # Generated files
20 | .idea/**/contentModel.xml
21 |
22 | # Sensitive or high-churn files
23 | .idea/**/dataSources/
24 | .idea/**/dataSources.ids
25 | .idea/**/dataSources.local.xml
26 | .idea/**/sqlDataSources.xml
27 | .idea/**/dynamic.xml
28 | .idea/**/uiDesigner.xml
29 | .idea/**/dbnavigator.xml
30 |
31 | # Gradle
32 | .idea/**/gradle.xml
33 | .idea/**/libraries
34 |
35 | # Gradle and Maven with auto-import
36 | # When using Gradle or Maven with auto-import, you should exclude module files,
37 | # since they will be recreated, and may cause churn. Uncomment if using
38 | # auto-import.
39 | # .idea/artifacts
40 | # .idea/compiler.xml
41 | # .idea/jarRepositories.xml
42 | # .idea/modules.xml
43 | # .idea/*.iml
44 | # .idea/modules
45 | # *.iml
46 | # *.ipr
47 |
48 | # CMake
49 | cmake-build-*/
50 |
51 | # Mongo Explorer plugin
52 | .idea/**/mongoSettings.xml
53 |
54 | # File-based project format
55 | *.iws
56 |
57 | # IntelliJ
58 | out/
59 |
60 | # mpeltonen/sbt-idea plugin
61 | .idea_modules/
62 |
63 | # JIRA plugin
64 | atlassian-ide-plugin.xml
65 |
66 | # Cursive Clojure plugin
67 | .idea/replstate.xml
68 |
69 | # SonarLint plugin
70 | .idea/sonarlint/
71 |
72 | # Crashlytics plugin (for Android Studio and IntelliJ)
73 | com_crashlytics_export_strings.xml
74 | crashlytics.properties
75 | crashlytics-build.properties
76 | fabric.properties
77 |
78 | # Editor-based Rest Client
79 | .idea/httpRequests
80 |
81 | # Android studio 3.1+ serialized cache file
82 | .idea/caches/build_file_checksums.ser
83 |
84 | ### CLion Patch ###
85 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
86 |
87 | # *.iml
88 | # modules.xml
89 | # .idea/misc.xml
90 | # *.ipr
91 |
92 | # Sonarlint plugin
93 | # https://plugins.jetbrains.com/plugin/7973-sonarlint
94 | .idea/**/sonarlint/
95 |
96 | # SonarQube Plugin
97 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
98 | .idea/**/sonarIssues.xml
99 |
100 | # Markdown Navigator plugin
101 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
102 | .idea/**/markdown-navigator.xml
103 | .idea/**/markdown-navigator-enh.xml
104 | .idea/**/markdown-navigator/
105 |
106 | # Cache file creation bug
107 | # See https://youtrack.jetbrains.com/issue/JBR-2257
108 | .idea/$CACHE_FILE$
109 |
110 | # CodeStream plugin
111 | # https://plugins.jetbrains.com/plugin/12206-codestream
112 | .idea/codestream.xml
113 |
114 | ### Rust ###
115 | # Generated by Cargo
116 | # will have compiled files and executables
117 | debug/
118 | target/
119 |
120 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
121 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
122 | Cargo.lock
123 |
124 | # These are backup files generated by rustfmt
125 | **/*.rs.bk
126 |
127 | # MSVC Windows builds of rustc generate these, which store debugging information
128 | *.pdb
129 |
130 | # End of https://www.toptal.com/developers/gitignore/api/rust,clion
131 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/ripgen.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "ripgen",
4 | "ripgen_lib"
5 | ]
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ripgen
2 | A rust-based version of the popular [dnsgen](https://github.com/ProjectAnte/dnsgen) python utility.
3 |
4 | `ripgen` is split into two main parts:
5 |
6 | * **ripgen**: _A CLI utility that calls into `ripgen_lib` and uses dnsgen's transforms_.
7 | * **ripgen_lib**: _A library that allows you to create high performance permutations of domain names._
8 |
9 | # How to Install!
10 | Installation of `ripgen` is very simple - follow the steps below.
11 |
12 | ### Step 1 - rustup.rs
13 | Visit https://rustup.rs/ and follow the instructions to get started with `rust` and `cargo`.
14 |
15 | ### Step 2 - cargo install
16 | Run `cargo install ripgen`
17 |
18 | ## How to Use - `ripgen`
19 | `ripgen` optionally takes a domain file, a wordlist file, and a minimum word length argument.
20 |
21 | If no domain file is listed, domains are expected through `stdin` making it easy to pipe into from other tools.
22 |
23 | e.g.
24 | ```
25 | $ echo "www1.google.com" | ripgen
26 | ```
27 |
28 | One deviation from dnsgen's behavior is that if no wordlist is specified then no wordlist items are included automatically. To compare `ripgen` and `dnsgen` appropriately you should make sure to specify a wordlist.
29 |
30 | ## How to use - `ripgen_lib`
31 | `ripgen_lib` exposes a `RipGenManager` struct that takes in three components:
32 |
33 | * an iterator for domain names
34 | * an iterator for wordlist entries
35 | * a function that converts `&&str` into `bool` for the purposes of filtering wordlist entries
36 |
37 | After creating a `RipGenManager`, transforms can be added on with `transform` and `chain_transform`. These transforms require a function definition (closure or otherwise) be passed in that can take the `&DomainComponent` and `WordListIterator` types and return an `Iterator- `.
38 |
39 | Look at the non-default dnsgen transform implementations for examples on how these are implemented typically.
40 |
41 | # FAQ
42 | ## `linker 'cc' not found`
43 | If this happens, it means that you need to install some dependencies on your system to build `ripgen`. Here's how to fix that:
44 |
45 |
46 | ### Debian (Ubuntu, Kali, WSL (_you probably used Ubuntu_))
47 | ```
48 | sudo apt-get update
49 | sudo apt install build-essential
50 | ```
51 |
52 | ### Arch
53 | ```
54 | sudo pacman -S base-devel
55 | ```
56 |
57 | ### Centos
58 | ```
59 | sudo yum install gcc
60 | ```
61 |
62 | ### Alpine
63 | ```
64 | apk add build-base --no-cache
65 | ```
66 |
67 |
--------------------------------------------------------------------------------
/ripgen/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "ripgen"
3 | version = "0.1.5"
4 | edition = "2021"
5 | authors = ["d0nut ", "youstin"]
6 | description = "A rust-based version of the popular dnsgen python utility."
7 | readme = "README.md"
8 | keywords = ["bug", "bounty", "dnsgen", "recon", "security"]
9 | license = "MIT"
10 | repository = "https://github.com/resyncgg/ripgen"
11 |
12 | [dependencies]
13 | anyhow = "1"
14 | clap = { version = "3", features = ["derive"]}
15 | ripgen_lib = { version = "0.1", path = "../ripgen_lib", features = ["dnsgen"] }
16 |
--------------------------------------------------------------------------------
/ripgen/README.md:
--------------------------------------------------------------------------------
1 | ../README.md
--------------------------------------------------------------------------------
/ripgen/src/args.rs:
--------------------------------------------------------------------------------
1 | use std::fs::read_to_string;
2 | use std::io::{BufRead, stdin};
3 | use clap::Parser;
4 | use anyhow::Result;
5 |
6 | #[derive(Parser, Debug)]
7 | #[clap(author, version, about)]
8 | pub struct Args {
9 | #[clap(
10 | short = 'd',
11 | long = "domains",
12 | help = "The file containing domains you want to generate permutations from. If this is not specified, domains are read from stdin."
13 | )]
14 | pub(crate) domain_file_path: Option,
15 |
16 | #[clap(
17 | short = 'w',
18 | long = "wordlist",
19 | help = "The supplementary wordlist file to include."
20 | )]
21 | pub(crate) wordlist: Option,
22 |
23 | #[clap(
24 | short = 'l',
25 | long = "len",
26 | help = "The minimum length for a word to be considered important. If not specified, all words are accepted."
27 | )]
28 | pub(crate) min_word_len: Option,
29 |
30 | #[clap(
31 | short = 'f',
32 | long = "fast",
33 | help = "Uses the most likely words only in permutations"
34 | )]
35 | pub(crate) fast: Option
36 | }
37 |
38 | impl Args {
39 | pub(crate) fn get_domain_str(&self) -> Result {
40 | let output = match self.domain_file_path {
41 | Some(ref path) => read_to_string(path)?,
42 | None => {
43 | stdin()
44 | .lock()
45 | .lines()
46 | .collect::, _>>()?
47 | .join("\n")
48 | }
49 | };
50 |
51 | Ok(output)
52 | }
53 |
54 | pub(crate) fn get_wordlist_str(&self) -> Result {
55 | let output = match self.wordlist {
56 | Some(ref path) => read_to_string(path)?,
57 | None => String::new()
58 | };
59 |
60 | Ok(output)
61 | }
62 | }
--------------------------------------------------------------------------------
/ripgen/src/main.rs:
--------------------------------------------------------------------------------
1 | #![deny(warnings)]
2 |
3 | mod args;
4 |
5 | use std::io::{BufWriter, stdout, Write};
6 | use ripgen_lib::{RipGenIterator, RipGenManager};
7 | use crate::args::Args;
8 | use clap::Parser;
9 |
10 | const FAST_MODE_WORDLIST_LEN: usize = 10;
11 | const DEFAULT_WORD_LEN: usize = 5;
12 |
13 | fn main() {
14 | let args: Args = Args::parse();
15 |
16 | let domains = args.get_domain_str()
17 | .expect("Failed to read in domains.");
18 | let wordlist = args.get_wordlist_str()
19 | .expect("Failed to read in wordlist file.");
20 | let word_len = args.min_word_len.unwrap_or(DEFAULT_WORD_LEN);
21 |
22 | let wordlist_lines = get_wordlist(&wordlist, &args);
23 |
24 | let manager = RipGenManager::new(
25 | domains.lines(),
26 | wordlist_lines,
27 | &|word| word.len() >= word_len
28 | ).expect("Failed to create ripgen iterator");
29 |
30 | let rip_iter = manager
31 | .transform(ripgen_lib::dnsgen::swap_word_transform)
32 | .chain_transform(ripgen_lib::dnsgen::permute_words_transform)
33 | .chain_transform(ripgen_lib::dnsgen::numbers_transform)
34 | .chain_transform(ripgen_lib::dnsgen::dash_transform);
35 |
36 | stream_output(rip_iter);
37 | }
38 |
39 | fn stream_output(rip_iter: impl Iterator
- ) {
40 | let out = stdout();
41 | let stdout_lock = out.lock();
42 | let mut buf = BufWriter::new(stdout_lock);
43 |
44 | for line in rip_iter {
45 | if writeln!(buf, "{}", line).is_err() {
46 | // user might be using `head` to only grab the first couple of entries - we should exit
47 | let _ = buf.flush();
48 | return;
49 | }
50 | }
51 |
52 | let _ = buf.flush();
53 | }
54 |
55 | fn get_wordlist<'a>(wordlist: &'a str, args: &Args) -> impl Iterator
- {
56 | // https://github.com/ProjectAnte/dnsgen/blob/16daeef81205e7663708b3ee11d759215c7168fe/dnsgen/dnsgen.py#L220
57 | let mut wordlist_iter = None;
58 | let mut fast_wordlist_iter = None;
59 |
60 | match args.fast {
61 | Some(fast_mode) if fast_mode => {
62 | fast_wordlist_iter = Some(wordlist.lines().take(FAST_MODE_WORDLIST_LEN))
63 | },
64 | _ => wordlist_iter = Some(wordlist.lines())
65 | }
66 |
67 | wordlist_iter
68 | .into_iter()
69 | .flatten()
70 | .chain(fast_wordlist_iter.into_iter().flatten())
71 | }
--------------------------------------------------------------------------------
/ripgen_lib/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "ripgen_lib"
3 | version = "0.1.4"
4 | edition = "2021"
5 | authors = ["d0nut ", "youstin"]
6 | description = "High-performance domain-name permutation generator."
7 | keywords = ["bug", "bounty", "dnsgen", "recon", "security"]
8 | readme = "README.md"
9 | license = "MIT"
10 | repository = "https://github.com/resyncgg/ripgen"
11 |
12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13 |
14 | [dependencies]
15 | thiserror = "1"
16 | addr = "0.15"
17 | fxhash = "0.2.1"
18 | regex = { version = "1", optional = true }
19 | lazy_static = { version = "1", optional = true }
20 |
21 | [features]
22 | dnsgen = ["regex", "lazy_static"]
23 | default = []
24 |
--------------------------------------------------------------------------------
/ripgen_lib/README.md:
--------------------------------------------------------------------------------
1 | ../README.md
--------------------------------------------------------------------------------
/ripgen_lib/src/chain.rs:
--------------------------------------------------------------------------------
1 | use std::marker::PhantomData;
2 | use crate::DomainComponents;
3 | use crate::transform::RipGenTransform;
4 |
5 | /// Describes an iterator that can be apart of a [RipGenChain](crate::RipGenChain).
6 | pub trait RipGenIterator<'manager, 'domain, D, W>
7 | where
8 | Self: Iterator
- + Sized,
9 | D: Iterator
- > + Clone,
10 | W: Iterator
- + Clone,
11 | 'domain: 'manager
12 | {
13 | /// Returns an iterator over the underlying [DomainComponents](crate::DomainComponents).
14 | fn get_domains_iter(&self) -> D;
15 |
16 | /// Returns an iterator over the underlying wordlist.
17 | fn get_words_iter(&self) -> W;
18 |
19 | /// Chain this `RipGenIterator` with another `RipGenIterator` over the specified transform.
20 | ///
21 | /// ```
22 | /// # use ripgen_lib::{DomainComponents, RipGenIterator, RipGenManager, WordlistIterator};
23 | /// # fn add_prefix(word: impl Into) -> impl Fn(&DomainComponents, WordlistIterator) -> std::vec::IntoIter {
24 | /// # let word_str = word.into();
25 | /// # move |domain_components: &DomainComponents, _: WordlistIterator| -> std::vec::IntoIter {
26 | /// # let domain_str: String = domain_components
27 | /// # .all()
28 | /// # .join(".");
29 | /// #
30 | /// # vec![format!("{}.{}", word_str, domain_str)].into_iter()
31 | /// # }
32 | /// # }
33 | /// let domains = vec!["example.com"];
34 | /// let domain_iter = domains.iter().map(|elem| *elem);
35 | /// let wordlist = vec![];
36 | /// let wordlist_iter = wordlist.iter().map(|elem| *elem);
37 | ///
38 | /// let manager = RipGenManager::new(domain_iter, wordlist_iter)
39 | /// .expect("Failed to parse domains.");
40 | ///
41 | /// let mut iter = manager
42 | /// .transform(add_prefix("admin"))
43 | /// .chain_transform(add_prefix("internal"))
44 | /// .chain_transform(add_prefix("manager"));
45 | ///
46 | /// assert_eq!(iter.next(), Some("admin.example.com".to_string()));
47 | /// assert_eq!(iter.next(), Some("internal.example.com".to_string()));
48 | /// assert_eq!(iter.next(), Some("manager.example.com".to_string()));
49 | /// ```
50 | fn chain_transform(self, transform: F) -> RipGenChain<'manager, 'domain, Self, RipGenTransform<'manager, 'domain, F, D, W, O>, D, W>
51 | where
52 | F: Fn(&'manager DomainComponents<'domain>, W) -> O,
53 | O: Iterator
- ,
54 | 'domain: 'manager
55 | {
56 | let domain_transform = RipGenTransform::new(self.get_domains_iter(), self.get_words_iter(), transform);
57 |
58 | RipGenChain::new(self, domain_transform)
59 | }
60 | }
61 |
62 | pub struct RipGenChain<'manager, 'domain, L, R, D, W>
63 | where
64 | L: RipGenIterator<'manager, 'domain, D, W>,
65 | R: RipGenIterator<'manager, 'domain, D, W>,
66 | D: Iterator
- > + Clone,
67 | W: Iterator
- + Clone,
68 | 'domain: 'manager
69 | {
70 | left: Option,
71 | right: Option,
72 | manager_phantom: PhantomData<&'manager ()>,
73 | domain_phantom: PhantomData<&'domain ()>,
74 | domain_iterator_phantom: PhantomData,
75 | word_iterator_phantom: PhantomData
76 | }
77 |
78 | impl<'manager, 'domain, L, R, D, W> RipGenChain<'manager, 'domain, L, R, D, W>
79 | where
80 | L: RipGenIterator<'manager, 'domain, D, W>,
81 | R: RipGenIterator<'manager, 'domain, D, W>,
82 | D: Iterator
- > + Clone,
83 | W: Iterator
- + Clone,
84 | 'domain: 'manager
85 | {
86 | fn new(left: L, right: R) -> Self {
87 | Self {
88 | left: Some(left),
89 | right: Some(right),
90 | manager_phantom: PhantomData,
91 | domain_phantom: PhantomData,
92 | domain_iterator_phantom: PhantomData,
93 | word_iterator_phantom: PhantomData
94 | }
95 | }
96 | }
97 |
98 | impl<'manager, 'domain, L, R, D, W> Iterator for RipGenChain<'manager, 'domain, L, R, D, W>
99 | where
100 | L: RipGenIterator<'manager, 'domain, D, W>,
101 | R: RipGenIterator<'manager, 'domain, D, W>,
102 | D: Iterator
- > + Clone,
103 | W: Iterator
- + Clone,
104 | 'domain: 'manager
105 | {
106 | type Item = String;
107 |
108 | fn next(&mut self) -> Option {
109 | if let Some(ref mut inner) = self.left {
110 | if let Some(output) = inner.next() {
111 | return Some(output);
112 | }
113 |
114 | self.left = None;
115 | }
116 |
117 | if let Some(ref mut inner) = self.right {
118 | if let Some(output) = inner.next() {
119 | return Some(output);
120 | }
121 |
122 | self.right = None;
123 | }
124 |
125 | None
126 | }
127 | }
128 |
129 | impl<'manager, 'domain, L, R, D, W> RipGenIterator<'manager, 'domain, D, W> for RipGenChain<'manager, 'domain, L, R, D, W>
130 | where
131 | L: RipGenIterator<'manager, 'domain, D, W>,
132 | R: RipGenIterator<'manager, 'domain, D, W>,
133 | D: Iterator
- > + Clone,
134 | W: Iterator
- + Clone,
135 | 'domain: 'manager
136 | {
137 | fn get_domains_iter(&self) -> D {
138 | match self.left {
139 | Some(ref inner) => inner.get_domains_iter(),
140 | None => match self.right {
141 | Some(ref inner) => inner.get_domains_iter(),
142 | None => panic!("huh")
143 | }
144 | }
145 | }
146 |
147 | fn get_words_iter(&self) -> W {
148 | match self.left {
149 | Some(ref inner) => inner.get_words_iter(),
150 | None => match self.right {
151 | Some(ref inner) => inner.get_words_iter(),
152 | None => panic!("huh")
153 | }
154 | }
155 | }
156 | }
--------------------------------------------------------------------------------
/ripgen_lib/src/dnsgen/dash.rs:
--------------------------------------------------------------------------------
1 | use crate::{DomainComponents, WordlistIterator};
2 |
3 | pub fn dash_transform<'domain>(
4 | domain_components: &'domain DomainComponents,
5 | words: WordlistIterator<'domain>,
6 | ) -> impl Iterator
- + 'domain {
7 | words.flat_map(move |word| {
8 | transform_components(domain_components, *word, dash)
9 | .chain(transform_components(domain_components, *word, rdash))
10 | .chain(transform_components(domain_components, *word, concat))
11 | .chain(transform_components(domain_components, *word, rconcat))
12 | })
13 | }
14 |
15 | fn transform_components<'domain, F: Fn(&str, &str) -> String + 'domain>(
16 | domain_components: &'domain DomainComponents<'domain>,
17 | word: &'domain str,
18 | transform: F
19 | ) -> impl Iterator
- + 'domain {
20 | (0 .. domain_components.count() - 1)
21 | .map(move |idx| {
22 | let new_word = transform(domain_components.all()[idx], word);
23 |
24 | let new_sub: &[&str] = &[
25 | &domain_components.all()[.. idx],
26 | [new_word.as_str()].as_slice(),
27 | &domain_components.all()[idx + 1 ..]
28 | ].concat();
29 |
30 | new_sub.join(".")
31 | })
32 | }
33 |
34 | fn dash(left: &str, right: &str) -> String {
35 | format!("{}-{}", left, right)
36 | }
37 |
38 | fn rdash(left: &str, right: &str) -> String {
39 | dash(right, left)
40 | }
41 |
42 | fn concat(left: &str, right: &str) -> String {
43 | format!("{}{}", left, right)
44 | }
45 |
46 | fn rconcat(left: &str, right: &str) -> String {
47 | concat(right, left)
48 | }
--------------------------------------------------------------------------------
/ripgen_lib/src/dnsgen/mod.rs:
--------------------------------------------------------------------------------
1 | mod numbers;
2 | mod permute;
3 | mod swap;
4 | mod dash;
5 |
6 | pub use numbers::numbers_transform;
7 | pub use permute::permute_words_transform;
8 | pub use swap::swap_word_transform;
9 | pub use dash::dash_transform;
--------------------------------------------------------------------------------
/ripgen_lib/src/dnsgen/numbers.rs:
--------------------------------------------------------------------------------
1 | use regex::Regex;
2 | use lazy_static::lazy_static;
3 | use crate::{DomainComponents, WordlistIterator};
4 |
5 | lazy_static! {
6 | static ref DIGIT_REGEX: Regex = Regex::new(r"\d{1,3}").unwrap();
7 | }
8 |
9 | #[inline(always)]
10 | pub fn numbers_transform(
11 | domain_components: &DomainComponents,
12 | _: WordlistIterator
13 | ) -> impl Iterator
- {
14 | let domain_str: String = domain_components
15 | .subdomains()
16 | .join(".");
17 |
18 | let root = domain_components
19 | .root()
20 | .to_string();
21 |
22 | DIGIT_REGEX
23 | .captures_iter(&domain_str)
24 | .flat_map(|captures| {
25 | let detected_int_str = &captures[0];
26 | let detected_int: u64 = detected_int_str.parse().unwrap();
27 | let mut results = Vec::with_capacity(2);
28 |
29 | for offset in 1 .. 4 {
30 | if let Some(sub_int) = detected_int.checked_sub(offset) {
31 | let replaced_front = domain_str.replace(detected_int_str, &sub_int.to_string());
32 |
33 | results.push(format!("{}.{}", replaced_front, root));
34 | }
35 |
36 | if let Some(add_int) = detected_int.checked_add(offset) {
37 | let replaced_front = domain_str.replace(detected_int_str, &add_int.to_string());
38 |
39 | results.push(format!("{}.{}", replaced_front, root));
40 | }
41 | }
42 |
43 | results
44 | })
45 | .collect::>()
46 | .into_iter()
47 | }
--------------------------------------------------------------------------------
/ripgen_lib/src/dnsgen/permute.rs:
--------------------------------------------------------------------------------
1 | use crate::{DomainComponents, WordlistIterator};
2 |
3 | pub fn permute_words_transform<'domain>(
4 | domain_components: &'domain DomainComponents,
5 | words: WordlistIterator<'domain>,
6 | ) -> impl Iterator
- + 'domain {
7 | words
8 | .flat_map(move |word| {
9 | (0 .. domain_components.count()).map(move |idx| {
10 | let domain_elems = domain_components.all();
11 | // this is the domain but with the word injected into it
12 | let augmented_domain_components: Vec<&str> = [
13 | &domain_elems[.. idx],
14 | [*word].as_slice(),
15 | &domain_elems[idx ..]
16 | ].concat();
17 |
18 | // combine back into the full domain string
19 | augmented_domain_components.join(".")
20 | })
21 | })
22 | }
--------------------------------------------------------------------------------
/ripgen_lib/src/dnsgen/swap.rs:
--------------------------------------------------------------------------------
1 | use crate::{DomainComponents, WordlistIterator};
2 |
3 | pub fn swap_word_transform<'domain>(
4 | domain_components: &'domain DomainComponents,
5 | words: WordlistIterator<'domain>,
6 | ) -> impl Iterator
- + 'domain {
7 | let root_string = domain_components.root().to_string();
8 | let subdomain_string: String = domain_components.subdomains().join(".");
9 | let subdomain_replace = subdomain_string.clone();
10 |
11 | let word_copy = words.clone();
12 |
13 | words
14 | .filter(move |word| subdomain_string.contains(*word))
15 | .flat_map(move |word| {
16 | let word_clone = <&str>::clone(word);
17 | let subdomain_replace = subdomain_replace.clone();
18 | let root_string = root_string.clone();
19 |
20 | word_copy
21 | .clone()
22 | .filter(move |sub_word| **sub_word != word_clone)
23 | .map(move |sub_word| {
24 | let replaced_subdomain = subdomain_replace.replace(word, sub_word);
25 | format!("{replaced_subdomain}.{root_string}")
26 | })
27 | })
28 | }
--------------------------------------------------------------------------------
/ripgen_lib/src/domain.rs:
--------------------------------------------------------------------------------
1 | use addr::dns::Name;
2 | use addr::parse_dns_name;
3 | use crate::error::RipGenError;
4 |
5 | #[derive(Clone)]
6 | /// Contains the byproduct of parsing a domain
7 | pub struct DomainComponents<'domain> {
8 | components: Vec<&'domain str>
9 | }
10 |
11 | impl<'domain> DomainComponents<'domain> {
12 | /// Returns an iterator that contains all of the names in the subdomains.
13 | pub fn subdomains_iter(&self) -> impl Iterator
- {
14 | self.all_iter().take(self.count() - 1)
15 | }
16 |
17 | /// Returns an iterator that contains all of the names in the original domain.
18 | /// The root is treated as the public suffix and therefore entirely occupies the last slot.
19 | ///
20 | /// ```
21 | /// # use ripgen_lib::DomainComponents;
22 | /// # use std::convert::TryFrom;
23 | /// let domain_component = DomainComponents::try_from("www.google.com")
24 | /// .expect("Failed to parse.");
25 | ///
26 | /// let mut iter = domain_component
27 | /// .all_iter()
28 | /// .map(|elem| *elem);
29 | ///
30 | /// assert_eq!(iter.next(), Some("www"));
31 | /// assert_eq!(iter.next(), Some("google.com"));
32 | /// ```
33 | pub fn all_iter(&self) -> impl Iterator
- {
34 | self.components.iter()
35 | }
36 |
37 | /// Returns the number of subdomain names + 1 (for root)
38 | pub fn count(&self) -> usize { self.components.len() }
39 |
40 | /// Returns a slice of the inner domain components that represent the subdomain names
41 | pub fn subdomains(&self) -> &[&str] {
42 | &self.components[.. self.count() - 1]
43 | }
44 |
45 | /// Returns the root
46 | ///
47 | /// ```
48 | /// # use ripgen_lib::DomainComponents;
49 | /// # use std::convert::TryFrom;
50 | /// let domain_component = DomainComponents::try_from("www.google.com")
51 | /// .expect("Failed to parse.");
52 | ///
53 | /// assert_eq!(domain_component.root(), "google.com");
54 | /// ```
55 | pub fn root(&self) -> &str {
56 | self.components[self.components.len() - 1]
57 | }
58 |
59 | /// Returns a slice with all domain components in it. This includes the root element.
60 | pub fn all(&self) -> &[&str] {
61 | &self.components
62 | }
63 | }
64 |
65 | impl<'domain> TryFrom<&'domain str> for DomainComponents<'domain> {
66 | type Error = RipGenError;
67 |
68 | fn try_from(domain: &'domain str) -> Result {
69 | let parsed_domain_name: Name<'domain> = parse_dns_name(domain)
70 | .map_err(|_| RipGenError::ErrorParsingDomain(domain.to_string()))?;
71 |
72 | let root: &str = parsed_domain_name
73 | .root()
74 | .unwrap_or(domain);
75 |
76 | // we do this so we can appease lifetimes
77 | let root_start = domain.len() - root.len();
78 | let root: &'domain str = &domain[root_start..];
79 |
80 | let components: Vec<&'domain str> = domain
81 | .trim_end_matches(root)
82 | .split('.')
83 | .filter(|elem| !elem.is_empty())
84 | .chain(vec![root])
85 | .collect::>();
86 |
87 | let new_components = Self {
88 | components
89 | };
90 |
91 | Ok(new_components)
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/ripgen_lib/src/error.rs:
--------------------------------------------------------------------------------
1 | use thiserror::Error;
2 |
3 | #[derive(Error, Debug)]
4 | /// The RipGen error type.
5 | pub enum RipGenError {
6 | #[error("Unable to parse provided domain.")]
7 | ErrorParsingDomain(String),
8 | }
--------------------------------------------------------------------------------
/ripgen_lib/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![deny(warnings)]
2 |
3 | pub use chain::{
4 | RipGenChain,
5 | RipGenIterator
6 | };
7 | pub use domain::DomainComponents;
8 | pub use error::RipGenError;
9 | pub use manager::RipGenManager;
10 |
11 | mod manager;
12 | mod domain;
13 | mod error;
14 | mod words;
15 | mod chain;
16 | pub(crate) mod transform;
17 |
18 | #[cfg(feature = "dnsgen")]
19 | pub mod dnsgen;
20 |
21 | /// Placeholder for a HashSet iterator with annoying lifetimes
22 | pub type WordlistIterator<'domain> = std::collections::hash_set::Iter<'domain, &'domain str>;
--------------------------------------------------------------------------------
/ripgen_lib/src/manager.rs:
--------------------------------------------------------------------------------
1 | use crate::domain::DomainComponents;
2 | use crate::error::RipGenError;
3 | use fxhash::FxHashSet;
4 | use std::slice::Iter as SliceIter;
5 | use std::collections::hash_set::Iter as HashSetIter;
6 | use crate::transform::RipGenTransform;
7 |
8 | #[derive(Clone)]
9 | /// Processes and manages domains and wordlist elements to enable creating [RipGenIterator](crate::RipGenIterator)
10 | /// via [transformations](Self::transform).
11 | pub struct RipGenManager<'domains> {
12 | domain_components: Vec>,
13 | elements: FxHashSet<&'domains str>,
14 | }
15 |
16 | impl<'domain> RipGenManager<'domain> {
17 | /// Creates a new `RipGenManager`.
18 | ///
19 | /// This can fail if any of the `domains` are unable to be parsed.
20 | pub fn new(
21 | domains: impl Iterator
- ,
22 | words: impl Iterator
- ,
23 | word_filter: &impl Fn(&&str) -> bool
24 | ) -> Result, RipGenError>
25 | {
26 | let domain_components: Vec = domains
27 | .filter(|line| !line.is_empty())
28 | .map(DomainComponents::try_from)
29 | .collect::>()?;
30 |
31 | let elements: FxHashSet<&'domain str> = crate::words::extract_words(domain_components.iter(), word_filter)
32 | .chain(words)
33 | .collect();
34 |
35 | let manager = RipGenManager {
36 | domain_components,
37 | elements
38 | };
39 |
40 | Ok(manager)
41 | }
42 |
43 | /// Begins a RipGen transform iterator.
44 | ///
45 | /// Requires a function that can take both a reference to a [DomainComponents](crate::DomainComponents)
46 | /// as well as an iterator that produces `&&str`.
47 | pub fn transform<'manager, F, O>(&'manager self, transform: F) -> RipGenTransform<'manager, 'domain, F, SliceIter>, HashSetIter<'manager, &'domain str>, O>
48 | where
49 | F: Fn(&'manager DomainComponents<'domain>, HashSetIter<'manager, &'domain str>) -> O,
50 | O: Iterator
- ,
51 | 'domain: 'manager
52 | {
53 | RipGenTransform::new(self.domain_components.iter(), self.elements.iter(), transform)
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/ripgen_lib/src/transform.rs:
--------------------------------------------------------------------------------
1 | use crate::{DomainComponents, RipGenIterator};
2 |
3 | pub struct RipGenTransform<'manager, 'domain, F, D, W, O>
4 | where
5 | F: Fn(&'manager DomainComponents<'domain>, W) -> O,
6 | D: Iterator
- > + Clone,
7 | W: Iterator
- + Clone,
8 | O: Iterator
- ,
9 | 'domain: 'manager
10 | {
11 | domains: D,
12 | words: W,
13 | result_pool: Vec,
14 | transform: F
15 | }
16 |
17 | impl<'manager, 'domain, F, D, W, O> RipGenTransform<'manager, 'domain, F, D, W, O>
18 | where
19 | F: Fn(&'manager DomainComponents<'domain>, W) -> O,
20 | D: Iterator
- > + Clone,
21 | W: Iterator
- + Clone,
22 | O: Iterator
- ,
23 | 'domain: 'manager
24 | {
25 | pub fn new(domains: D, words: W, transform: F) -> Self {
26 | Self {
27 | domains,
28 | words,
29 | result_pool: Vec::with_capacity(1024 * 4),
30 | transform
31 | }
32 | }
33 | }
34 |
35 |
36 | impl<'manager, 'domain, F, D, W, O> Iterator for RipGenTransform<'manager, 'domain, F, D, W, O>
37 | where
38 | F: Fn(&'manager DomainComponents<'domain>, W) -> O,
39 | D: Iterator
- > + Clone,
40 | W: Iterator
- + Clone,
41 | O: Iterator
- ,
42 | 'domain: 'manager
43 | {
44 | type Item = String;
45 |
46 | fn next(&mut self) -> Option {
47 | if let Some(next) = self.result_pool.pop() {
48 | return Some(next);
49 | }
50 |
51 | loop {
52 | let domain = self.domains.next()?;
53 |
54 | self.result_pool.extend((self.transform)(domain, self.words.clone()));
55 |
56 | if let Some(result) = self.result_pool.pop() {
57 | return Some(result);
58 | }
59 | }
60 | }
61 | }
62 |
63 |
64 | impl<'manager, 'domain, F, D, W, O> RipGenIterator<'manager, 'domain, D, W> for RipGenTransform<'manager, 'domain, F, D, W, O>
65 | where
66 | F: Fn(&'manager DomainComponents<'domain>, W) -> O,
67 | D: Iterator
- > + Clone,
68 | W: Iterator
- + Clone,
69 | O: Iterator
- ,
70 | 'domain: 'manager
71 | {
72 | fn get_domains_iter(&self) -> D {
73 | self.domains.clone()
74 | }
75 |
76 | fn get_words_iter(&self) -> W {
77 | self.words.clone()
78 | }
79 | }
--------------------------------------------------------------------------------
/ripgen_lib/src/words.rs:
--------------------------------------------------------------------------------
1 | use crate::domain::DomainComponents;
2 |
3 |
4 | pub(crate) fn extract_words<'iter, 'domain>(
5 | domain_components: impl Iterator
- > + 'iter,
6 | filter_function: &'iter impl Fn(&&str) -> bool,
7 | ) -> impl Iterator
- + 'iter
8 | where
9 | 'domain: 'iter
10 | {
11 | domain_components
12 | .flat_map(move |domain| {
13 | let augments = domain
14 | .subdomains_iter()
15 | .flat_map(|elem| elem.split('-'));
16 |
17 | domain
18 | .subdomains_iter().copied()
19 | .chain(augments)
20 | .filter(filter_function)
21 | })
22 | }
23 |
--------------------------------------------------------------------------------