use std::fmt::Write; use std::net::{Ipv4Addr, Ipv6Addr}; use domain::base::{Rtype, scan::Symbol}; use serde::{Deserialize, Serialize}; use crate::errors::Error; use crate::validation; use crate::macros::{append_errors, push_error}; use crate::resources::record::RecordParseError; use crate::resources::dns::internal; /// Type used to serialize / deserialize resource records data to response / request /// #[derive(Debug, Deserialize, Serialize)] #[serde(tag = "type", content = "rdata")] #[serde(rename_all = "UPPERCASE")] pub enum RData { A(A), Aaaa(Aaaa), // TODO: CAA Cname(Cname), // TODO: DS Mx(Mx), Ns(Ns), Ptr(Ptr), Soa(Soa), Srv(Srv), // TODO: SSHFP // TODO: SVCB / HTTPS // TODO: TLSA Txt(Txt), } impl RData { pub fn rtype(&self) -> Rtype { match self { RData::A(_) => Rtype::A, RData::Aaaa(_) => Rtype::AAAA, RData::Cname(_) => Rtype::CNAME, RData::Mx(_) => Rtype::MX, RData::Ns(_) => Rtype::NS, RData::Ptr(_) => Rtype::PTR, RData::Soa(_) => Rtype::SOA, RData::Srv(_) => Rtype::SRV, RData::Txt(_) => Rtype::TXT, } } pub fn validate(self) -> Result> { let rdata = match self { RData::A(data) => internal::RData::A(data.validate()?), RData::Aaaa(data) => internal::RData::Aaaa(data.validate()?), RData::Cname(data) => internal::RData::Cname(data.validate()?), RData::Mx(data) => internal::RData::Mx(data.validate()?), RData::Ns(data) => internal::RData::Ns(data.validate()?), RData::Ptr(data) => internal::RData::Ptr(data.validate()?), RData::Soa(data) => internal::RData::Soa(data.validate()?), RData::Srv(data) => internal::RData::Srv(data.validate()?), RData::Txt(data) => internal::RData::Txt(data.validate()?), }; Ok(rdata) } } impl From for RData { fn from(value: internal::RData) -> Self { match value { internal::RData::A(data) => RData::A(data.into()), internal::RData::Aaaa(data) => RData::Aaaa(data.into()), internal::RData::Cname(data) => RData::Cname(data.into()), internal::RData::Mx(data) => RData::Mx(data.into()), internal::RData::Ns(data) => RData::Ns(data.into()), internal::RData::Ptr(data) => RData::Ptr(data.into()), internal::RData::Soa(data) => RData::Soa(data.into()), internal::RData::Srv(data) => RData::Srv(data.into()), internal::RData::Txt(data) => RData::Txt(data.into()), } } } /* --------- A --------- */ #[derive(Debug, Deserialize, Serialize)] pub struct A { pub address: String, } impl From for A { fn from(value: internal::A) -> Self { A { address: value.address.to_string(), } } } impl A { pub fn validate(self) -> Result> { let mut errors = Vec::new(); let address = push_error!(self.address.parse::().map_err(|e| { Error::from(RecordParseError::Ip4Address { input: self.address }) .with_cause(&e.to_string()) .with_path("/address") }), errors); if errors.is_empty() { Ok(internal::A { address: address.unwrap() }) } else { Err(errors) } } } /* --------- AAAA --------- */ #[derive(Debug, Deserialize, Serialize)] pub struct Aaaa { pub address: String, } impl From for Aaaa { fn from(value: internal::Aaaa) -> Self { Aaaa { address: value.address.to_string(), } } } impl Aaaa { pub fn validate(self) -> Result> { let mut errors = Vec::new(); // TODO: replace with custom validation let address = push_error!(self.address.parse::().map_err(|e| { Error::from(RecordParseError::Ip6Address { input: self.address }) .with_cause(&e.to_string()) .with_path("/address") }), errors); if errors.is_empty() { Ok(internal::Aaaa { address: address.unwrap() }) } else { Err(errors) } } } /* --------- CNAME --------- */ #[derive(Debug, Deserialize, Serialize)] pub struct Cname { pub target: String, } impl From for Cname { fn from(value: internal::Cname) -> Self { Cname { target: value.target.to_string(), } } } impl Cname { pub fn validate(self) -> Result> { let mut errors = Vec::new(); let cname = push_error!( validation::normalize_domain(&self.target), errors, "/target" ); if errors.is_empty() { Ok(internal::Cname { target: internal::Name::new(cname.unwrap()) }) } else { Err(errors) } } } /* --------- MX --------- */ #[derive(Debug, Deserialize, Serialize)] pub struct Mx { // TODO: Validate number pub preference: u16, pub mail_exchanger: String, } impl From for Mx { fn from(value: internal::Mx) -> Self { Mx { preference: value.preference, mail_exchanger: value.mail_exchanger.to_string(), } } } impl Mx { pub fn validate(self) -> Result> { let mut errors = Vec::new(); let mail_exchanger = push_error!( validation::normalize_domain(&self.mail_exchanger), errors, "/mail_exchanger" ); if errors.is_empty() { Ok(internal::Mx { preference: self.preference, mail_exchanger: internal::Name::new(mail_exchanger.unwrap()), }) } else { Err(errors) } } } /* --------- NS --------- */ #[derive(Debug, Deserialize, Serialize)] pub struct Ns { pub target: String, } impl From for Ns { fn from(value: internal::Ns) -> Self { Ns { target: value.target.to_string(), } } } impl Ns { pub fn validate(self) -> Result> { let mut errors = Vec::new(); let target = push_error!( validation::normalize_domain(&self.target), errors, "/target" ); if errors.is_empty() { Ok(internal::Ns { target: internal::Name::new(target.unwrap()), }) } else { Err(errors) } } } /* --------- PTR --------- */ #[derive(Debug, Deserialize, Serialize)] pub struct Ptr { pub target: String, } impl From for Ptr { fn from(value: internal::Ptr) -> Self { Ptr { target: value.target.to_string(), } } } impl Ptr { pub fn validate(self) -> Result> { let mut errors = Vec::new(); let target = push_error!( validation::normalize_domain(&self.target), errors, "/target" ); if errors.is_empty() { Ok(internal::Ptr { target: internal::Name::new(target.unwrap()), }) } else { Err(errors) } } } /* --------- SOA --------- */ #[derive(Debug, Deserialize, Serialize)] pub struct Soa { pub primary_server: String, pub maintainer: String, pub refresh: u32, pub retry: u32, pub expire: u32, pub minimum: u32, pub serial: u32, } impl From for Soa { fn from(value: internal::Soa) -> Self { Soa { primary_server: value.primary_server.to_string(), maintainer: value.maintainer.to_string(), refresh: value.refresh, retry: value.retry, expire: value.expire, minimum: value.minimum, serial: value.serial, } } } impl Soa { pub fn validate(self) -> Result> { let mut errors = Vec::new(); let primary_server = push_error!( validation::normalize_domain(&self.primary_server), errors, "/primary_server" ); let maintainer = push_error!( validation::normalize_domain(&self.maintainer), errors, "/maintainer" ); if errors.is_empty() { Ok(internal::Soa { primary_server: internal::Name::new(primary_server.unwrap()), maintainer: internal::Name::new(maintainer.unwrap()), refresh: self.refresh, retry: self.retry, expire: self.expire, minimum: self.minimum, serial: self.serial, }) } else { Err(errors) } } } /* --------- SRV --------- */ #[derive(Debug, Deserialize, Serialize)] pub struct Srv { pub server: String, pub port: u16, pub priority: u16, pub weight: u16, } impl From for Srv { fn from(value: internal::Srv) -> Self { Srv { server: value.server.to_string(), port: value.port, priority: value.priority, weight: value.weight, } } } impl Srv { pub fn validate(self) -> Result> { let mut errors = Vec::new(); let server = push_error!( validation::normalize_domain(&self.server), errors, "/server" ); if errors.is_empty() { Ok(internal::Srv { server: internal::Name::new(server.unwrap()), priority: self.priority, weight: self.weight, port: self.port, }) } else { Err(errors) } } } /* --------- TXT --------- */ #[derive(Debug, Deserialize, Serialize)] pub struct Txt { pub text: String, } impl From for Txt { fn from(value: internal::Txt) -> Self { let mut concatenated_text = String::new(); for c in value.text.iter() { // Escapes '\' and non printable chars let c = Symbol::display_from_octet(*c); write!(concatenated_text, "{}", c).unwrap(); } Txt { text: concatenated_text } } } impl Txt { pub fn validate(self) -> Result> { let mut errors = Vec::new(); let text = append_errors!( validation::parse_txt_data(&self.text), errors, "/text" ); if errors.is_empty() { Ok(internal::Txt { text: text.unwrap() }) } else { Err(errors) } } }