Subscan Module
SubscanModule
components are the core components that can be executed by Subscan
. Each module capable of performing subdomain discovery is named a SubscanModule
, and when the subscan scan
command is run, these modules are read from an in-memory cache and executed asynchronously. This architecture makes Subscan
extensible and modular
A SubscanModule
may contain various components such as Requester
and Extractor
. Most modules implemented in Subscan
use these components. You can list the implemented modules with their details using the subscan module list
command. If you'd like to view the in-memory cache, you can check the CacheManager struct, which is another component designed for operations like filtering the cache or accessing a specific module
Create Your Own Module
Each SubscanModule
component should be implemented following the interface below. For a better understanding, you can explore the docs.rs page and review the crates listed below
#[async_trait]
#[enum_dispatch]
pub trait SubscanModuleInterface: Sync + Send {
// Returns module name, name should clarify what does module
async fn name(&self) -> &str;
// Loads `.env` file and fetches module environment variables with variable name.
// If system environment variable set with same name, `.env` file will be overrode
async fn envs(&self) -> SubscanModuleEnvs {
self.name().await.into()
}
// Returns module requester address as a mutable reference if available
async fn requester(&self) -> Option<&Mutex<RequesterDispatcher>>;
// Returns module extractor reference if available
async fn extractor(&self) -> Option<&SubdomainExtractorDispatcher>;
// Just like a `main` method, when the module run this `run` method will be called.
// So this method should do everything
async fn run(&mut self, domain: &str) -> Result<SubscanModuleResult>;
}
Below is a simple example of a custom module. For more examples, you can check the examples/ folder on the project's GitHub page. You can also refer to the source code of predefined requester implementations for a better understanding
pub struct CustomModule {
pub requester: Mutex<RequesterDispatcher>,
pub extractor: SubdomainExtractorDispatcher,
}
#[async_trait]
impl SubscanModuleInterface for CustomModule {
async fn name(&self) -> &str {
&"name"
}
async fn requester(&self) -> Option<&Mutex<RequesterDispatcher>> {
Some(&self.requester)
}
async fn extractor(&self) -> Option<&SubdomainExtractorDispatcher> {
Some(&self.extractor)
}
async fn run(&mut self, _domain: &str) -> Result<SubscanModuleResult> {
let mut result: SubscanModuleResult = self.name().await.into();
let subdomains = BTreeSet::from_iter([
Subdomain::from("bar.foo.com"),
Subdomain::from("baz.foo.com"),
]);
result.extend(subdomains);
Ok(result.with_finished().await)
}
}