subscan/modules/
zonetransfer.rs1use std::{net::SocketAddr, str::FromStr};
2
3use async_trait::async_trait;
4use flume::Sender;
5use hickory_client::{
6 client::{Client, ClientHandle},
7 proto::{
8 rr::{domain::Name, DNSClass, Record, RecordType},
9 runtime::TokioRuntimeProvider,
10 tcp::TcpClientStream,
11 },
12};
13use hickory_resolver::config::NameServerConfig;
14use tokio::sync::Mutex;
15
16use crate::{
17 enums::{
18 dispatchers::{RequesterDispatcher, SubdomainExtractorDispatcher, SubscanModuleDispatcher},
19 result::OptionalSubscanModuleResult,
20 },
21 error::ModuleErrorKind::Custom,
22 interfaces::module::SubscanModuleInterface,
23 types::{
24 core::{Result, Subdomain},
25 result::status::SubscanModuleStatus::Finished,
26 },
27 utilities::{net, regex},
28};
29
30pub const ZONETRANSFER_MODULE_NAME: &str = "zonetransfer";
31
32pub struct ZoneTransfer {
48 pub name: String,
49 pub ns: Option<NameServerConfig>,
50}
51
52impl ZoneTransfer {
53 pub fn dispatcher() -> SubscanModuleDispatcher {
54 let zonetransfer = Self {
55 name: ZONETRANSFER_MODULE_NAME.into(),
56 ns: net::get_default_ns(),
57 };
58
59 zonetransfer.into()
60 }
61
62 pub async fn get_tcp_client(&self, server: SocketAddr) -> Result<Client> {
63 let provider = TokioRuntimeProvider::new();
64 let (stream, handler) = TcpClientStream::new(server, None, None, provider);
65 let result = Client::new(stream, handler, None).await;
66
67 result.map_err(|_| Custom("client error".into())).map(|(client, bg)| {
68 tokio::spawn(bg);
69 Ok(client)
70 })?
71 }
72
73 pub async fn get_ns_as_ip(&self, server: SocketAddr, domain: &str) -> Option<Vec<SocketAddr>> {
74 let mut ips = vec![];
75 let mut client = self.get_tcp_client(server).await.ok()?;
76
77 let name = Name::from_str(domain).ok()?;
78 let ns_response = client.query(name, DNSClass::IN, RecordType::NS);
79
80 for answer in ns_response.await.ok()?.answers() {
81 let name = Name::from_str(&answer.data().as_ns()?.to_utf8()).ok()?;
82 let a_response = client.query(name, DNSClass::IN, RecordType::A).await;
83
84 let with_port = |answer: &Record| Some(format!("{}:{}", answer.data(), server.port()));
85 let as_ip = |with_port: String| SocketAddr::from_str(&with_port).ok();
86
87 ips.extend(a_response.ok()?.answers().iter().filter_map(with_port).filter_map(as_ip));
88 }
89
90 Some(ips)
91 }
92
93 pub async fn attempt_zone_transfer(
94 &self,
95 server: SocketAddr,
96 domain: &str,
97 ) -> Result<Vec<Subdomain>> {
98 let pattern = regex::generate_subdomain_regex(domain)?;
99
100 let mut subs = Vec::new();
101 let mut client = self.get_tcp_client(server).await?;
102
103 let name = Name::from_str(domain).unwrap();
104 let axfr_response = client.query(name, DNSClass::IN, RecordType::AXFR);
105
106 if let Ok(response) = axfr_response.await {
107 for answer in response.answers() {
108 let rtype = answer.data().record_type();
109
110 if rtype == RecordType::A || rtype == RecordType::AAAA {
111 let name = answer.name().to_string();
112 let sub = name.strip_suffix(".").unwrap_or(&name);
113
114 if let Some(matches) = pattern.find(sub) {
115 subs.push(matches.as_str().to_lowercase());
116 }
117 }
118 }
119 }
120
121 Ok(subs)
122 }
123}
124
125#[async_trait]
126impl SubscanModuleInterface for ZoneTransfer {
127 async fn name(&self) -> &str {
128 &self.name
129 }
130
131 async fn requester(&self) -> Option<&Mutex<RequesterDispatcher>> {
132 None
133 }
134
135 async fn extractor(&self) -> Option<&SubdomainExtractorDispatcher> {
136 None
137 }
138
139 async fn run(&mut self, domain: &str, results: Sender<OptionalSubscanModuleResult>) {
140 match &self.ns {
141 Some(ns) => {
142 let err = Custom("connection error".into());
143
144 match self.get_ns_as_ip(ns.socket_addr, domain).await.ok_or(err) {
145 Ok(ips) => {
146 for ip in ips {
147 let subdomains = self.attempt_zone_transfer(ip, domain).await;
148
149 for subdomain in &subdomains.unwrap_or_default() {
150 results.send(self.item(subdomain).await).unwrap();
151 }
152 }
153 results.send(self.status(Finished).await).unwrap();
154 }
155 Err(err) => results.send(self.status(err.into()).await).unwrap(),
156 }
157 }
158 None => results.send(self.error("no default ns").await).unwrap(),
159 };
160 }
161}