use std::{net::IpAddr, process::Command, vec}; use anyhow::anyhow; use serde::{Deserialize, Serialize}; #[derive(Debug)] pub struct DnsResponse { pub domain: String, pub ttl: i32, pub class: String, pub r#type: String, pub address: IpAddr, } impl DnsResponse { pub fn parse(text: impl AsRef) -> anyhow::Result { let text = text.as_ref(); let mut split = text.split(' '); let domain = split .next() .ok_or_else(|| anyhow!("domain not found in answer {text:?}"))? .to_owned(); let ttl = split .next() .ok_or_else(|| anyhow!("ttl not found in answer {text:?}"))?; let ttl: i32 = ttl.parse()?; let class = split .next() .ok_or_else(|| anyhow!("class not found in answer {text:?}"))? .to_owned(); let r#type = split .next() .ok_or_else(|| anyhow!("type not found in answer {text:?}"))? .to_owned(); let address = split .next() .ok_or_else(|| anyhow!("address not found in answer {text:?}"))?; let address: IpAddr = address.parse()?; Ok(Self { domain, ttl, class, r#type, address, }) } } impl DigResponse { pub fn query(domain: impl AsRef) -> anyhow::Result { let cmd = Command::new("dig") .arg("+yaml") .arg(domain.as_ref()) .output()?; if cmd.status.success() { Ok(serde_yaml_ng::from_str( String::from_utf8(cmd.stdout.into_iter().skip(2).collect())?.as_str(), )?) } else { Err(anyhow!( "dig command failed:\n\tstatus: {:?}\n\tstdout: {}\n\tstderr: {}", cmd.status, String::from_utf8_lossy(&cmd.stdout), String::from_utf8_lossy(&cmd.stderr) )) } } pub fn answers(&self) -> anyhow::Result> { let mut answers = vec![]; // let DigResponseType::Message(ref message) = self.message; // for answer in &message.response_message_data.answer_section { for answer in &self.message.response_message_data.answer_section { answers.push(DnsResponse::parse(answer)?); } Ok(answers) } } #[derive(Debug, Clone, Deserialize, Serialize)] #[serde(untagged)] pub enum DigResponseType { Message(DigResponseMessage), } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct DigResponseMessage { pub r#type: String, pub query_time: String, pub response_time: String, pub message_size: String, pub socket_family: String, pub socket_protocol: String, pub response_address: String, pub response_port: String, pub response_message_data: DigResponseMessageData, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct DigResponse { pub r#type: String, pub message: DigResponseMessage, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct DigResponseMessageData { // opcode: QUERY opcode: String, // status: NOERROR status: String, // id: 57945 id: i32, // flags: qr rd ra flags: String, // QUESTION: 1 #[serde(rename = "QUESTION")] question: i32, // ANSWER: 1 #[serde(rename = "ANSWER")] answer: i32, // AUTHORITY: 0 #[serde(rename = "AUTHORITY")] authority: i32, // ADDITIONAL: 1 #[serde(rename = "ADDITIONAL")] additional: i32, // QUESTION_SECTION: // - google.com. IN A #[serde(rename = "QUESTION_SECTION")] question_section: Vec, // ANSWER_SECTION: // - google.com. 228 IN A 142.250.189.142 #[serde(rename = "ANSWER_SECTION")] answer_section: Vec, }