Skip to main content

subscan/modules/integrations/
bufferover.rs

1use std::collections::BTreeSet;
2
3use regex::Match;
4use reqwest::Url;
5use serde_json::Value;
6
7use crate::{
8    enums::{
9        auth::AuthenticationMethod,
10        content::Content,
11        dispatchers::{RequesterDispatcher, SubscanModuleDispatcher},
12    },
13    error::ModuleErrorKind::JSONExtract,
14    extractors::json::JSONExtractor,
15    modules::generics::integration::GenericIntegrationModule,
16    requesters::client::HTTPClient,
17    types::{
18        core::{Result, Subdomain, SubscanModuleCoreComponents},
19        func::GenericIntegrationCoreFuncs,
20    },
21    utilities::regex::generate_subdomain_regex,
22};
23
24pub const BUFFEROVER_MODULE_NAME: &str = "bufferover";
25pub const BUFFEROVER_URL: &str = "https://tls.bufferover.run";
26
27/// `BufferOver` API integration module
28///
29/// It uses [`GenericIntegrationModule`] its own inner
30/// here are the configurations
31///
32/// | Property           | Value                                    |
33/// |:------------------:|:----------------------------------------:|
34/// | Module Name        | `bufferover`                             |
35/// | Doc URL            | <https://tls.bufferover.run>             |
36/// | Authentication     | [`AuthenticationMethod::APIKeyAsHeader`] |
37/// | Requester          | [`HTTPClient`]                           |
38/// | Extractor          | [`JSONExtractor`]                        |
39/// | Generic            | [`GenericIntegrationModule`]             |
40pub struct BufferOver {}
41
42impl BufferOver {
43    pub fn dispatcher() -> SubscanModuleDispatcher {
44        let requester: RequesterDispatcher = HTTPClient::default().into();
45        let extractor: JSONExtractor = JSONExtractor::new(Box::new(Self::extract));
46
47        let generic = GenericIntegrationModule {
48            name: BUFFEROVER_MODULE_NAME.into(),
49            auth: AuthenticationMethod::APIKeyAsHeader("X-API-Key".into()),
50            funcs: GenericIntegrationCoreFuncs {
51                url: Box::new(Self::get_query_url),
52                next: Box::new(Self::get_next_url),
53            },
54            components: SubscanModuleCoreComponents {
55                requester: requester.into(),
56                extractor: extractor.into(),
57            },
58        };
59
60        generic.into()
61    }
62
63    pub fn get_query_url(domain: &str) -> String {
64        format!("{BUFFEROVER_URL}/dns?q={domain}")
65    }
66
67    pub fn get_next_url(_url: Url, _content: Content) -> Option<Url> {
68        None
69    }
70
71    pub fn extract(content: Value, domain: &str) -> Result<BTreeSet<Subdomain>> {
72        if let Some(results) = content["Results"].as_array() {
73            let pattern = generate_subdomain_regex(domain)?;
74            let filter = |item: &Value| {
75                let to_string = |matches: Match| matches.as_str().to_string();
76
77                pattern.find(item.as_str()?).map(to_string)
78            };
79
80            return Ok(results.iter().filter_map(filter).collect());
81        }
82
83        Err(JSONExtract.into())
84    }
85}