#![forbid(unsafe_code)]
#![cfg_attr(
not(test),
deny(
clippy::unwrap_used,
clippy::expect_used,
clippy::todo,
clippy::unimplemented,
clippy::panic
)
)]
#![allow(
clippy::module_name_repetitions,
clippy::must_use_candidate,
clippy::missing_errors_doc
)]
#[cfg(feature = "axfr")]
pub mod axfr;
#[cfg(feature = "dnssec")]
pub mod dnssec;
#[cfg(feature = "email")]
pub mod email;
#[cfg(feature = "posture")]
pub mod posture;
#[cfg(feature = "takeover")]
pub mod takeover;
pub mod resolver;
use async_trait::async_trait;
use futures::StreamExt;
use gossan_core::{Config, ScanInput, Scanner, Target};
use secfinding::Finding;
pub use resolver::build_resolver;
pub struct DnsScanner;
#[async_trait]
impl Scanner for DnsScanner {
fn name(&self) -> &'static str {
"dns"
}
fn tags(&self) -> &'static [&'static str] {
&["active", "dns", "email"]
}
fn accepts(&self, target: &Target) -> bool {
matches!(target, Target::Domain(_))
}
async fn run(&self, input: ScanInput, config: &Config) -> anyhow::Result<()> {
let dns = build_resolver(config)?;
let owned: Vec<Target> = {
let mut rx = input.target_rx.lock().await;
let mut buf = Vec::new();
while let Some(t) = rx.recv().await {
if self.accepts(&t) {
buf.push(t);
}
}
buf
};
let timeout = config.timeout();
let proxy_opt = config.proxy.clone();
let findings: Vec<Vec<Finding>> = futures::stream::iter(owned)
.map(|target| {
let dns = dns.clone();
let proxy = proxy_opt.clone();
async move {
let domain = target.domain().unwrap_or("").to_string();
audit_domain(&dns, &domain, &target, timeout, proxy.as_deref()).await
}
})
.buffer_unordered(config.concurrency)
.collect()
.await;
for batch in findings {
for f in batch {
input.emit(f).await;
}
}
Ok(())
}
}
async fn audit_domain(
dns: &hickory_resolver::TokioResolver,
domain: &str,
target: &Target,
timeout: std::time::Duration,
proxy: Option<&str>,
) -> Vec<Finding> {
let mut findings = Vec::new();
#[cfg(feature = "axfr")]
{
findings.extend(axfr::check(dns, domain, target, timeout, proxy).await);
}
#[cfg(feature = "email")]
{
findings.extend(email::check(dns, domain, target).await);
}
#[cfg(feature = "posture")]
{
findings.extend(posture::check(dns, domain, target).await);
}
#[cfg(feature = "dnssec")]
{
findings.extend(dnssec::check(dns, domain, target).await);
}
#[cfg(feature = "takeover")]
{
findings.extend(takeover::check(dns, domain, target).await);
}
findings
}