gossan-subdomain 0.3.3

Subdomain discovery scanner for gossan (CT logs, Wayback, permutations, DNS bruteforce), part of the security research ecosystem
//! Subdomain source trait and registry.

pub mod common;
pub use common::*;

macro_rules! source_mods {
    ($($name:ident),+ $(,)?) => {
        $(pub mod $name;)+
    };
}

source_mods! {
    alienvault, anubis, ask, asn, baidu, bevigil, binaryedge, bing, bufferover, builtwith, c99, censys, certspotter, chaos, circl, columbus, commoncrawl, crobat, ct, digitorus, dnsdumpster, dnslytics, dnsrepo, duckduckgo, exalead, facebook_ct, farsight_dnsdb, fofa, fullhunt, github, google, greynoise, hackertarget, hackertarget_dns, hunter, intelx, ipinfo, jter, leakix, mnemonic, netlas, omnisint, passivetotal, pastebin, ptrarchive, pugrecon, quake, rapiddns, riddler, robtex, rook, securitytrails, shodan, sitedossier, sonarsearch, spyse, subdomaincenter, sublist3r, synapsint, threatbook, threatcrowd, threatminer, urlscan, viewdns, virustotal, wayback, whoisxml, yahoo, yandex, zone_walk, zoomeye
}

use async_trait::async_trait;
use gossan_core::{Config, DiscoverySource, Target};
use governor::{DefaultDirectRateLimiter, Quota, RateLimiter};
use std::num::NonZeroU32;

/// Per-source rate limit configuration.
#[derive(Clone, Copy)]
pub struct SourceRate {
    quota: Quota,
}

impl SourceRate {
    /// `n` requests per second.
    pub fn per_second(n: u32) -> Self {
        let nz = NonZeroU32::new(n.max(1)).unwrap_or(NonZeroU32::MIN);
        Self {
            quota: Quota::per_second(nz),
        }
    }

    /// `n` requests per minute.
    pub fn per_minute(n: u32) -> Self {
        let nz = NonZeroU32::new(n.max(1)).unwrap_or(NonZeroU32::MIN);
        Self {
            quota: Quota::per_minute(nz),
        }
    }

    /// Build a `governor` rate limiter from this specification.
    pub fn build_limiter(&self) -> DefaultDirectRateLimiter {
        RateLimiter::direct(self.quota)
    }
}

/// Every passive subdomain source implements this trait.
#[async_trait]
pub trait SubdomainSource: Send + Sync {
    /// Stable source identifier.
    fn name(&self) -> &'static str;

    /// Whether this source needs an API key to function.
    fn requires_api_key(&self) -> bool;

    /// Environment variable / config key name for the API key.
    fn api_key_name(&self) -> &'static str;

    /// Documented rate limit for this source.
    fn rate_limit(&self) -> SourceRate;

    /// How targets discovered by this source should be tagged.
    fn discovery_source(&self) -> DiscoverySource;

    /// Query the source for subdomains of `domain`.
    async fn query(
        &self,
        domain: &str,
        config: &Config,
        client: &reqwest::Client,
        limiter: &DefaultDirectRateLimiter,
    ) -> anyhow::Result<Vec<Target>>;
}

/// Registry of all implemented sources.
pub fn all_sources() -> Vec<Box<dyn SubdomainSource>> {
    vec![
        Box::new(alienvault::Alienvault),
        Box::new(anubis::Anubis),
        Box::new(ask::Ask),
        Box::new(asn::Asn),
        Box::new(baidu::Baidu),
        Box::new(bevigil::Bevigil),
        Box::new(binaryedge::Binaryedge),
        Box::new(bing::Bing),
        Box::new(bufferover::Bufferover),
        Box::new(builtwith::BuiltWith),
        Box::new(c99::C99),
        Box::new(censys::Censys),
        Box::new(certspotter::Certspotter),
        Box::new(chaos::Chaos),
        Box::new(circl::Circl),
        Box::new(columbus::Columbus),
        Box::new(commoncrawl::CommonCrawl),
        Box::new(crobat::Robat),
        Box::new(ct::Ct),
        Box::new(digitorus::Digitorus),
        Box::new(dnsdumpster::DnsDumpster),
        Box::new(dnslytics::Dnslytics),
        Box::new(dnsrepo::Dnsrepo),
        Box::new(duckduckgo::Duckduckgo),
        Box::new(exalead::Exalead),
        Box::new(facebook_ct::FacebookCt),
        Box::new(farsight_dnsdb::FarsightDnsdb),
        Box::new(fofa::Fofa),
        Box::new(fullhunt::Fullhunt),
        Box::new(github::GitHub),
        Box::new(google::Google),
        Box::new(greynoise::Greynoise),
        Box::new(hackertarget::Hackertarget),
        Box::new(hackertarget_dns::HackertargetDns),
        Box::new(hunter::Hunter),
        Box::new(intelx::IntelX),
        Box::new(ipinfo::Ipinfo),
        Box::new(jter::Jter),
        Box::new(leakix::Leakix),
        Box::new(mnemonic::Mnemonic),
        Box::new(netlas::Netlas),
        Box::new(omnisint::Omnisint),
        Box::new(passivetotal::Passivetotal),
        Box::new(pastebin::Pastebin),
        Box::new(ptrarchive::Ptrarchive),
        Box::new(pugrecon::Pugrecon),
        Box::new(quake::Quake),
        Box::new(rapiddns::Rapiddns),
        Box::new(riddler::Riddler),
        Box::new(robtex::Robtex),
        Box::new(rook::Rook),
        Box::new(securitytrails::Securitytrails),
        Box::new(shodan::Shodan),
        Box::new(sitedossier::Sitedossier),
        Box::new(sonarsearch::Sonarsearch),
        Box::new(spyse::Spyse),
        Box::new(subdomaincenter::Subdomaincenter),
        Box::new(sublist3r::Sublist3r),
        Box::new(synapsint::Synapsint),
        Box::new(threatbook::Threatbook),
        Box::new(threatcrowd::Threatcrowd),
        Box::new(threatminer::Threatminer),
        Box::new(urlscan::Urlscan),
        Box::new(viewdns::Viewdns),
        Box::new(virustotal::Virustotal),
        Box::new(wayback::Wayback),
        Box::new(whoisxml::Whoisxml),
        Box::new(yahoo::Yahoo),
        Box::new(yandex::Yandex),
        Box::new(zone_walk::ZoneWalk),
        Box::new(zoomeye::Zoomeye),
    ]
}