#![forbid(unsafe_code)]
pub mod cache;
pub mod cli;
pub mod constants;
pub mod enums;
pub mod error;
pub mod extractors;
pub mod interfaces;
pub mod logger;
pub mod modules;
pub mod pools;
pub mod requesters;
pub mod resolver;
pub mod types;
pub mod utilities;
use std::sync::Arc;
use constants::LOG_TIME_FORMAT;
use enums::dispatchers::SubscanModuleDispatcher;
use tokio::sync::{Mutex, OnceCell};
use types::config::requester::RequesterConfig;
use crate::{
cache::CacheManager,
cli::Cli,
interfaces::module::SubscanModuleInterface,
pools::{brute::SubscanBrutePool, module::SubscanModulePool},
types::{config::subscan::SubscanConfig, core::SubscanModule, result::subscan::SubscanResult},
};
static INIT: OnceCell<()> = OnceCell::const_new();
#[derive(Default)]
pub struct Subscan {
pub config: SubscanConfig,
pub manager: CacheManager,
}
impl From<Cli> for Subscan {
fn from(cli: Cli) -> Self {
Self {
config: cli.into(),
manager: CacheManager::default(),
}
}
}
impl From<SubscanConfig> for Subscan {
fn from(config: SubscanConfig) -> Self {
Self {
config,
manager: CacheManager::default(),
}
}
}
impl Subscan {
pub fn new(config: SubscanConfig) -> Self {
Self {
config,
manager: CacheManager::default(),
}
}
async fn init(&self, module: Option<&Arc<Mutex<SubscanModuleDispatcher>>>) {
let rconfig: RequesterConfig = self.config.clone().into();
if let Some(module) = module {
INIT.get_or_init(|| async { module.lock().await.configure(rconfig).await })
.await;
} else {
INIT.get_or_init(|| async { self.manager.configure(rconfig).await }).await;
}
}
pub async fn module(&self, name: &str) -> &SubscanModule {
self.manager
.module(name)
.await
.unwrap_or_else(|| panic!("Module not found with {name}!"))
}
pub async fn modules(&self) -> &Vec<SubscanModule> {
self.manager.modules().await
}
pub async fn scan(&self, domain: &str) -> SubscanResult {
self.init(None).await;
let mut result: SubscanResult = domain.into();
let pool: Arc<SubscanModulePool> = self.config.clone().into();
let time = result.metadata.started_at.format(LOG_TIME_FORMAT);
log::info!("Started scan on {domain} ({time})");
pool.clone().start(domain, self.modules().await).await;
result.update_with_pool_result(pool.result().await).await;
result.with_finished().await
}
pub async fn run(&self, name: &str, domain: &str) -> SubscanResult {
let mut result: SubscanResult = domain.into();
let pool: Arc<SubscanModulePool> = self.config.clone().into();
let module = self.module(name).await;
self.init(Some(module)).await;
let time = result.metadata.started_at.format(LOG_TIME_FORMAT);
log::debug!("Running {name} module on {domain} ({time})");
pool.clone().start(domain, &vec![module.clone()]).await;
result.update_with_pool_result(pool.result().await).await;
result.with_finished().await
}
pub async fn brute(&self, domain: &str) -> SubscanResult {
let mut result: SubscanResult = domain.into();
let pool: Arc<SubscanBrutePool> = self.config.clone().into();
let time = result.metadata.started_at.format(LOG_TIME_FORMAT);
let wordlist = self.config.wordlist.clone().expect("Wordlist must be specified!");
log::info!("Started brute force attack on {domain} ({time})");
pool.clone().start(domain, wordlist).await;
result.update_with_pool_result(pool.result().await).await;
result.with_finished().await
}
}