wip: layered api

This commit is contained in:
Hannaeko 2025-03-24 23:03:54 +01:00
parent 376a6bd319
commit 419b78b55e
20 changed files with 995 additions and 564 deletions

View file

@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::ressouces::zone::ZoneModel; use crate::resources::zone::ZoneModel;
pub trait Db: ZoneModel + Send + Sync {} pub trait Db: ZoneModel + Send + Sync {}
pub type BoxedDb = Arc<dyn Db>; pub type BoxedDb = Arc<dyn Db>;

View file

@ -11,7 +11,8 @@ use domain::tsig::{Algorithm, Key, KeyName};
use domain::net::client::request::{self, RequestMessage, RequestMessageMulti, SendRequest, SendRequestMulti}; use domain::net::client::request::{self, RequestMessage, RequestMessageMulti, SendRequest, SendRequestMulti};
use tokio::net::TcpStream; use tokio::net::TcpStream;
use crate::ressouces::{rdata, record}; use crate::resources::record;
use crate::proto;
use super::{RecordDriver, ZoneDriver, DnsDriverError}; use super::{RecordDriver, ZoneDriver, DnsDriverError};
use async_trait::async_trait; use async_trait::async_trait;
@ -162,7 +163,7 @@ impl RecordDriver for DnsDriver {
let answer = reply.answer()?; let answer = reply.answer()?;
for record in answer.limit_to::<rdata::ParsedRData<_, _>>() { for record in answer.limit_to::<proto::dns::ParsedRData<_, _>>() {
let record = record?; let record = record?;
records.push(record.into()) records.push(record.into())
} }

View file

@ -4,7 +4,7 @@ use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use crate::ressouces::record; use crate::resources::record;
pub type BoxedZoneDriver = Arc<dyn ZoneDriver>; pub type BoxedZoneDriver = Arc<dyn ZoneDriver>;
pub type BoxedRecordDriver = Arc<dyn RecordDriver>; pub type BoxedRecordDriver = Arc<dyn RecordDriver>;

View file

@ -8,10 +8,11 @@ use serde::{Serialize, Serializer};
use serde_json::{Value, json}; use serde_json::{Value, json};
use crate::dns::DnsDriverError; use crate::dns::DnsDriverError;
use crate::ressouces::record::{RecordError, RecordParseError}; use crate::resources::record::{RecordError, RecordParseError};
use crate::ressouces::zone::ZoneError; use crate::resources::zone::ZoneError;
use crate::validation::{DomainValidationError, TxtParseError}; use crate::validation::{DomainValidationError, TxtParseError};
use crate::template::TemplateError; use crate::template::TemplateError;
use crate::proto::dns::ProtoDnsError;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct Error { pub struct Error {
@ -387,3 +388,18 @@ impl From<RecordError > for Error {
} }
} }
} }
impl From<ProtoDnsError> for Error {
fn from(value: ProtoDnsError) -> Self {
match value {
ProtoDnsError::RDataUnknown { input, field, rtype } => {
Error::new("proto:dns:rdata_unknown", "Unknown error while converting internal rdata to proto rdata")
.with_details(json!({
"input": input,
"field": field,
"rtype": rtype,
}))
},
}
}
}

View file

@ -1,11 +1,12 @@
mod errors; mod errors;
mod dns; mod dns;
mod routes; mod routes;
mod ressouces; mod resources;
mod database; mod database;
mod validation; mod validation;
mod macros; mod macros;
mod template; mod template;
mod proto;
use std::sync::Arc; use std::sync::Arc;

425
src/proto/dns.rs Normal file
View file

@ -0,0 +1,425 @@
use std::fmt::Write;
use domain::base::rdata::ComposeRecordData;
use domain::base::wire::{Composer, ParseError};
use domain::base::{Name, ParseRecordData, ParsedName, RecordData, Rtype, ToName, Ttl};
use domain::rdata;
use domain::dep::octseq::{Parser, Octets};
use crate::resources::dns::internal;
use crate::errors::Error;
pub enum ProtoDnsError {
RDataUnknown { input: String, field: String, rtype: String },
}
/* --------- A --------- */
impl From<domain::rdata::A> for internal::A {
fn from(record_data: domain::rdata::A) -> Self {
internal::A { address: record_data.addr() }
}
}
impl TryFrom<internal::A> for domain::rdata::A {
type Error = Error;
fn try_from(record_data: internal::A) -> Result<Self, Self::Error> {
Ok(domain::rdata::A::new(record_data.address))
}
}
/* --------- AAAA --------- */
impl From<domain::rdata::Aaaa> for internal::Aaaa {
fn from(record_data: domain::rdata::Aaaa) -> Self {
internal::Aaaa { address: record_data.addr() }
}
}
impl TryFrom<internal::Aaaa> for domain::rdata::Aaaa {
type Error = Error;
fn try_from(record_data: internal::Aaaa) -> Result<Self, Self::Error> {
Ok(domain::rdata::Aaaa::new(record_data.address))
}
}
/* --------- CNAME --------- */
impl<N: ToString> From<domain::rdata::Cname<N>> for internal::Cname {
fn from(record_data: domain::rdata::Cname<N>) -> Self {
internal::Cname { target: internal::Name::new(record_data.cname().to_string()) }
}
}
impl TryFrom<internal::Cname> for domain::rdata::Cname<Name<Vec<u8>>> {
type Error = Error;
fn try_from(record_data: internal::Cname) -> Result<domain::rdata::Cname<Name<Vec<u8>>>, Self::Error> {
let cname = record_data.target.to_string();
let cname: Name<_> = cname.parse::<Name<_>>().map_err(|e| {
Error::from(ProtoDnsError::RDataUnknown {
input: cname,
field: "target".into(),
rtype: "CNAME".into(),
}).with_cause(&e.to_string())
})?;
Ok(domain::rdata::Cname::new(cname))
}
}
/* --------- MX --------- */
impl<N: ToString> From<domain::rdata::Mx<N>> for internal::Mx {
fn from(record_data: domain::rdata::Mx<N>) -> Self {
internal::Mx {
preference: record_data.preference(),
mail_exchanger: internal::Name::new(record_data.exchange().to_string()),
}
}
}
impl TryFrom<internal::Mx> for domain::rdata::Mx<Name<Vec<u8>>> {
type Error = Error;
fn try_from(record_data: internal::Mx) -> Result<domain::rdata::Mx<Name<Vec<u8>>>, Self::Error> {
let mail_exchanger = record_data.mail_exchanger.to_string();
let mail_exchanger: Name<_> = mail_exchanger.parse::<Name<_>>().map_err(|e| {
Error::from(ProtoDnsError::RDataUnknown {
input: mail_exchanger,
field: "mail_exchanger".into(),
rtype: "MX".into(),
}).with_cause(&e.to_string())
})?;
Ok(domain::rdata::Mx::new(record_data.preference, mail_exchanger))
}
}
/* --------- NS --------- */
impl<N: ToString> From<domain::rdata::Ns<N>> for internal::Ns {
fn from(record_data: domain::rdata::Ns<N>) -> Self {
internal::Ns {
target: internal::Name::new(record_data.nsdname().to_string()),
}
}
}
impl TryFrom<internal::Ns> for domain::rdata::Ns<Name<Vec<u8>>> {
type Error = Error;
fn try_from(record_data: internal::Ns) -> Result<domain::rdata::Ns<Name<Vec<u8>>>, Self::Error> {
let target = record_data.target.to_string();
let target: Name<_> = target.parse::<Name<_>>().map_err(|e| {
Error::from(ProtoDnsError::RDataUnknown {
input: target,
field: "target".into(),
rtype: "NS".into(),
}).with_cause(&e.to_string())
})?;
Ok(domain::rdata::Ns::new(target))
}
}
/* --------- PTR --------- */
impl<N: ToString> From<domain::rdata::Ptr<N>> for internal::Ptr {
fn from(record_data: domain::rdata::Ptr<N>) -> Self {
internal::Ptr {
target: internal::Name::new(record_data.ptrdname().to_string()),
}
}
}
impl TryFrom<internal::Ptr> for domain::rdata::Ptr<Name<Vec<u8>>> {
type Error = Error;
fn try_from(record_data: internal::Ptr) -> Result<domain::rdata::Ptr<Name<Vec<u8>>>, Self::Error> {
let target = record_data.target.to_string();
let target: Name<_> = target.parse::<Name<_>>().map_err(|e| {
Error::from(ProtoDnsError::RDataUnknown {
input: target,
field: "target".into(),
rtype: "PTR".into(),
}).with_cause(&e.to_string())
})?;
Ok(domain::rdata::Ptr::new(target))
}
}
/* --------- SOA --------- */
impl<N: ToString> From<domain::rdata::Soa<N>> for internal::Soa {
fn from(record_rdata: domain::rdata::Soa<N>) -> Self {
internal::Soa {
primary_server: internal::Name::new(record_rdata.mname().to_string()),
maintainer: internal::Name::new(record_rdata.rname().to_string()),
refresh: record_rdata.refresh().as_secs(),
retry: record_rdata.retry().as_secs(),
expire: record_rdata.expire().as_secs(),
minimum: record_rdata.minimum().as_secs(),
serial: record_rdata.serial().into(),
}
}
}
impl TryFrom<internal::Soa> for domain::rdata::Soa<Name<Vec<u8>>> {
type Error = Error;
fn try_from(record_data: internal::Soa) -> Result<domain::rdata::Soa<Name<Vec<u8>>>, Self::Error> {
let primary_server = record_data.primary_server.to_string();
let primary_server: Name<_> = primary_server.parse::<Name<_>>().map_err(|e| {
Error::from(ProtoDnsError::RDataUnknown {
input: primary_server,
field: "primary_server".into(),
rtype: "SOA".into(),
}).with_cause(&e.to_string())
})?;
let maintainer = record_data.maintainer.to_string();
let maintainer: Name<_> = maintainer.parse::<Name<_>>().map_err(|e| {
Error::from(ProtoDnsError::RDataUnknown {
input: maintainer,
field: "maintainer".into(),
rtype: "SOA".into(),
}).with_cause(&e.to_string())
})?;
Ok(domain::rdata::Soa::new(
primary_server,
maintainer,
record_data.serial.into(),
Ttl::from_secs(record_data.refresh),
Ttl::from_secs(record_data.retry),
Ttl::from_secs(record_data.expire),
Ttl::from_secs(record_data.minimum),
))
}
}
/* --------- SRV --------- */
impl<N: ToString> From<domain::rdata::Srv<N>> for internal::Srv {
fn from(record_data: domain::rdata::Srv<N>) -> Self {
internal::Srv {
server: internal::Name::new(record_data.target().to_string()),
priority: record_data.priority(),
weight: record_data.weight(),
port: record_data.port(),
}
}
}
impl TryFrom<internal::Srv> for domain::rdata::Srv<Name<Vec<u8>>> {
type Error = Error;
fn try_from(record_data: internal::Srv) -> Result<domain::rdata::Srv<Name<Vec<u8>>>, Self::Error> {
let server = record_data.server.to_string();
let server: Name<_> = server.parse::<Name<_>>().map_err(|e| {
Error::from(ProtoDnsError::RDataUnknown {
input: server,
field: "server".into(),
rtype: "SRV".into(),
}).with_cause(&e.to_string())
})?;
Ok(domain::rdata::Srv::new(
record_data.priority,
record_data.weight,
record_data.port,
server
))
}
}
/* --------- TXT --------- */
impl<O: AsRef<[u8]>> From<domain::rdata::Txt<O>> for internal::Txt {
fn from(record_data: rdata::Txt<O>) -> Self {
let concatenated_text: Vec<_> = record_data.iter()
.flatten()
.cloned()
.collect();
internal::Txt {
text: concatenated_text
}
}
}
impl TryFrom<internal::Txt> for domain::rdata::Txt<Vec<u8>> {
type Error = Error;
fn try_from(record_data: internal::Txt) -> Result<domain::rdata::Txt<Vec<u8>>, Self::Error> {
let txt: domain::rdata::Txt<_> = rdata::Txt::build_from_slice(&record_data.text).map_err(|e| {
let text = record_data.text.iter()
.fold(String::new(), |mut output, b| {
write!(output, "{:02X}", b).unwrap();
output
});
Error::from(ProtoDnsError::RDataUnknown {
input: format!("0x{}", text),
field: "text".into(),
rtype: "TXT".into(),
}).with_cause(&e.to_string())
})?;
Ok(txt)
}
}
/* --------- ParsedRData --------- */
pub enum ParsedRData<Name, Octs> {
A(domain::rdata::A),
Aaaa(domain::rdata::Aaaa),
Cname(domain::rdata::Cname<Name>),
Mx(domain::rdata::Mx<Name>),
Ns(domain::rdata::Ns<Name>),
Ptr(domain::rdata::Ptr<Name>),
Soa(domain::rdata::Soa<Name>),
Srv(domain::rdata::Srv<Name>),
Txt(domain::rdata::Txt<Octs>),
}
impl<Name: ToString, Octs: AsRef<[u8]>> From<ParsedRData<Name, Octs>> for internal::RData {
fn from(value: ParsedRData<Name, Octs>) -> Self {
match value {
ParsedRData::A(record_rdata) => internal::RData::A(record_rdata.into()),
ParsedRData::Aaaa(record_rdata) => internal::RData::Aaaa(record_rdata.into()),
ParsedRData::Cname(record_rdata) => internal::RData::Cname(record_rdata.into()),
ParsedRData::Mx(record_rdata) => internal::RData::Mx(record_rdata.into()),
ParsedRData::Ns(record_rdata) => internal::RData::Ns(record_rdata.into()),
ParsedRData::Ptr(record_rdata) => internal::RData::Ptr(record_rdata.into()),
ParsedRData::Soa(record_rdata) => internal::RData::Soa(record_rdata.into()),
ParsedRData::Srv(record_rdata) => internal::RData::Srv(record_rdata.into()),
ParsedRData::Txt(record_rdata) => internal::RData::Txt(record_rdata.into()),
}
}
}
impl TryFrom<internal::RData> for ParsedRData<Name<Vec<u8>>, Vec<u8>> {
type Error = Error;
fn try_from(value: internal::RData) -> Result<Self, Self::Error> {
let rdata = match value {
internal::RData::A(record_rdata) => ParsedRData::A(record_rdata.try_into()?),
internal::RData::Aaaa(record_rdata) => ParsedRData::Aaaa(record_rdata.try_into()?),
internal::RData::Cname(record_rdata) => ParsedRData::Cname(record_rdata.try_into()?),
internal::RData::Mx(record_rdata) => ParsedRData::Mx(record_rdata.try_into()?),
internal::RData::Ns(record_rdata) => ParsedRData::Ns(record_rdata.try_into()?),
internal::RData::Ptr(record_rdata) => ParsedRData::Ptr(record_rdata.try_into()?),
internal::RData::Soa(record_rdata) => ParsedRData::Soa(record_rdata.try_into()?),
internal::RData::Srv(record_rdata) => ParsedRData::Srv(record_rdata.try_into()?),
internal::RData::Txt(record_rdata) => ParsedRData::Txt(record_rdata.try_into()?),
};
Ok(rdata)
}
}
impl<Name, Octs> ParsedRData<Name, Octs> {
pub fn rtype(&self) -> Rtype {
match self {
ParsedRData::A(_) => Rtype::A,
ParsedRData::Aaaa(_) => Rtype::AAAA,
ParsedRData::Cname(_) => Rtype::CNAME,
ParsedRData::Mx(_) => Rtype::MX,
ParsedRData::Ns(_) => Rtype::NS,
ParsedRData::Ptr(_) => Rtype::PTR,
ParsedRData::Soa(_) => Rtype::SOA,
ParsedRData::Srv(_) => Rtype::SRV,
ParsedRData::Txt(_) => Rtype::TXT,
}
}
}
impl<Name, Octs> RecordData for ParsedRData<Name, Octs> {
fn rtype(&self) -> Rtype {
ParsedRData::rtype(self)
}
}
impl<'a, Octs: Octets + ?Sized> ParseRecordData<'a, Octs> for ParsedRData<ParsedName<Octs::Range<'a>>, Octs::Range<'a>> {
fn parse_rdata(
rtype: Rtype,
parser: &mut Parser<'a, Octs>,
) -> Result<Option<Self>, ParseError> {
let record = match rtype {
Rtype::A => ParsedRData::A(rdata::A::parse(parser)?),
Rtype::AAAA => ParsedRData::Aaaa(rdata::Aaaa::parse(parser)?),
Rtype::CNAME => ParsedRData::Cname(rdata::Cname::parse(parser)?),
Rtype::MX => ParsedRData::Mx(rdata::Mx::parse(parser)?),
Rtype::NS => ParsedRData::Ns(rdata::Ns::parse(parser)?),
Rtype::PTR => ParsedRData::Ptr(rdata::Ptr::parse(parser)?),
Rtype::SOA => ParsedRData::Soa(rdata::Soa::parse(parser)?),
Rtype::SRV => ParsedRData::Srv(rdata::Srv::parse(parser)?),
Rtype::TXT => ParsedRData::Txt(rdata::Txt::parse(parser)?),
_ => return Ok(None)
};
Ok(Some(record))
}
}
impl<Name: ToName, Octs: AsRef<[u8]>> ComposeRecordData for ParsedRData<Name, Octs> {
fn rdlen(&self, compress: bool) -> Option<u16> {
match self {
ParsedRData::A(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Aaaa(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Cname(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Mx(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Ns(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Ptr(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Soa(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Srv(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Txt(record_rdata) => record_rdata.rdlen(compress),
}
}
fn compose_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
match self {
ParsedRData::A(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Aaaa(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Cname(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Mx(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Ns(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Ptr(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Soa(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Srv(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Txt(record_rdata) => record_rdata.compose_rdata(target),
}
}
fn compose_canonical_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
match self {
ParsedRData::A(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Aaaa(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Cname(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Mx(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Ns(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Ptr(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Soa(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Srv(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Txt(record_rdata) => record_rdata.compose_canonical_rdata(target),
}
}
}

1
src/proto/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod dns;

2
src/resources/dns/external/mod.rs vendored Normal file
View file

@ -0,0 +1,2 @@
pub mod rdata;
pub use rdata::*;

437
src/resources/dns/external/rdata.rs vendored Normal file
View file

@ -0,0 +1,437 @@
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<internal::RData, Vec<Error>> {
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<internal::RData> 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<internal::A> for A {
fn from(value: internal::A) -> Self {
A {
address: value.address.to_string(),
}
}
}
impl A {
pub fn validate(self) -> Result<internal::A, Vec<Error>> {
let mut errors = Vec::new();
let address = push_error!(self.address.parse::<Ipv4Addr>().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<internal::Aaaa> for Aaaa {
fn from(value: internal::Aaaa) -> Self {
Aaaa {
address: value.address.to_string(),
}
}
}
impl Aaaa {
pub fn validate(self) -> Result<internal::Aaaa, Vec<Error>> {
let mut errors = Vec::new();
// TODO: replace with custom validation
let address = push_error!(self.address.parse::<Ipv6Addr>().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<internal::Cname> for Cname {
fn from(value: internal::Cname) -> Self {
Cname {
target: value.target.to_string(),
}
}
}
impl Cname {
pub fn validate(self) -> Result<internal::Cname, Vec<Error>> {
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<internal::Mx> 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<internal::Mx, Vec<Error>> {
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<internal::Ns> for Ns {
fn from(value: internal::Ns) -> Self {
Ns {
target: value.target.to_string(),
}
}
}
impl Ns {
pub fn validate(self) -> Result<internal::Ns, Vec<Error>> {
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<internal::Ptr> for Ptr {
fn from(value: internal::Ptr) -> Self {
Ptr {
target: value.target.to_string(),
}
}
}
impl Ptr {
pub fn validate(self) -> Result<internal::Ptr, Vec<Error>> {
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<internal::Soa> 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<internal::Soa, Vec<Error>> {
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<internal::Srv> 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<internal::Srv, Vec<Error>> {
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<internal::Txt> 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<internal::Txt, Vec<Error>> {
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)
}
}
}

View file

@ -0,0 +1,3 @@
pub mod rdata;
pub use rdata::*;

View file

@ -0,0 +1,78 @@
use std::net::{Ipv4Addr, Ipv6Addr};
use std::fmt;
pub enum RData {
A(A),
Aaaa(Aaaa),
Cname(Cname),
Mx(Mx),
Ns(Ns),
Ptr(Ptr),
Soa(Soa),
Srv(Srv),
Txt(Txt),
}
pub struct Name {
name: String
}
impl Name {
pub fn new(name: String) -> Self {
Name {
name,
}
}
}
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)
}
}
pub struct A {
pub address: Ipv4Addr,
}
pub struct Aaaa {
pub address: Ipv6Addr,
}
pub struct Cname {
pub target: Name,
}
pub struct Mx {
pub preference: u16,
pub mail_exchanger: Name,
}
pub struct Ns {
pub target: Name,
}
pub struct Ptr {
pub target: Name,
}
pub struct Soa {
pub primary_server: Name,
pub maintainer: Name,
pub refresh: u32,
pub retry: u32,
pub expire: u32,
pub minimum: u32,
pub serial: u32,
}
pub struct Srv {
pub server: Name,
pub port: u16,
pub priority: u16,
pub weight: u16,
}
pub struct Txt {
pub text: Vec<u8>,
}

3
src/resources/dns/mod.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod external;
pub mod internal;
//pub mod friendly;

View file

@ -20,5 +20,6 @@ pub use zone::{Zone, AddZoneMemberRequest, CreateZoneRequest};
*/ */
pub mod zone; pub mod zone;
pub mod rdata; //pub mod rdata;
pub mod record; pub mod record;
pub mod dns;

View file

@ -4,7 +4,10 @@ use domain::base::{iana::Class, Name, Record as DnsRecord, Ttl};
use crate::{errors::Error, validation}; use crate::{errors::Error, validation};
use crate::macros::{append_errors, push_error}; use crate::macros::{append_errors, push_error};
use super::rdata::{ParsedRData, RData};
use crate::resources::dns::external;
use crate::resources::dns::internal;
use crate::proto;
pub enum RecordParseError { pub enum RecordParseError {
Ip4Address { input: String }, Ip4Address { input: String },
@ -20,7 +23,7 @@ pub enum RecordError {
pub(crate) type DnsRecordImpl = DnsRecord< pub(crate) type DnsRecordImpl = DnsRecord<
Name<Vec<u8>>, Name<Vec<u8>>,
ParsedRData<Name<Vec<u8>>,Vec<u8>> proto::dns::ParsedRData<Name<Vec<u8>>,Vec<u8>>
>; >;
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
@ -28,15 +31,16 @@ pub struct Record {
pub name: String, pub name: String,
pub ttl: u32, pub ttl: u32,
#[serde(flatten)] #[serde(flatten)]
pub rdata: RData pub rdata: external::RData
} }
impl<Name: ToString, Oct: AsRef<[u8]>> From<DnsRecord<Name, ParsedRData<Name, Oct>>> for Record { // TODO: Proto
fn from(value: DnsRecord<Name, ParsedRData<Name, Oct>>) -> Self { impl<Name: ToString, Oct: AsRef<[u8]>> From<DnsRecord<Name, proto::dns::ParsedRData<Name, Oct>>> for Record {
fn from(value: DnsRecord<Name, proto::dns::ParsedRData<Name, Oct>>) -> Self {
Record { Record {
name: value.owner().to_string(), name: value.owner().to_string(),
ttl: value.ttl().as_secs(), ttl: value.ttl().as_secs(),
rdata: value.into_data().into(), rdata: internal::RData::from(value.into_data()).into(),
} }
} }
} }
@ -66,10 +70,13 @@ impl Record {
}); });
let ttl = Ttl::from_secs(self.ttl); let ttl = Ttl::from_secs(self.ttl);
let rdata = append_errors!(ParsedRData::try_from(self.rdata), errors, "/rdata"); let rdata = append_errors!(self.rdata.validate(), errors, "/rdata");
if errors.is_empty() { if errors.is_empty() {
Ok(DnsRecord::new(name.unwrap(), Class::IN, ttl, rdata.unwrap())) // TODO: Split this in proto / external
let rdata = proto::dns::ParsedRData::try_from(rdata.unwrap()).unwrap();
Ok(DnsRecord::new(name.unwrap(), Class::IN, ttl, rdata))
} else { } else {
Err(errors) Err(errors)
} }

View file

@ -7,7 +7,7 @@ use crate::database::{BoxedDb, sqlite::SqliteDB};
use crate::dns::{BoxedZoneDriver, BoxedRecordDriver, DnsDriverError}; use crate::dns::{BoxedZoneDriver, BoxedRecordDriver, DnsDriverError};
use crate::errors::Error; use crate::errors::Error;
use crate::macros::push_error; use crate::macros::push_error;
use crate::ressouces::record::RecordList; use crate::resources::record::RecordList;
use crate::validation; use crate::validation;
pub enum ZoneError { pub enum ZoneError {

View file

@ -1,544 +0,0 @@
use std::fmt::Write;
use std::net::{Ipv4Addr, Ipv6Addr};
use domain::base::rdata::ComposeRecordData;
use domain::base::scan::Symbol;
use domain::base::wire::{Composer, ParseError};
use domain::base::{Name, ParseRecordData, ParsedName, RecordData, Rtype, ToName, Ttl};
use domain::rdata;
use domain::dep::octseq::{Parser, Octets};
use serde::{Deserialize, Serialize};
use crate::errors::Error;
use crate::validation;
use crate::macros::{append_errors, push_error};
use super::record::RecordParseError;
/// 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 enum ParsedRData<Name, Octs> {
A(rdata::A),
Aaaa(rdata::Aaaa),
Cname(rdata::Cname<Name>),
Mx(rdata::Mx<Name>),
Ns(rdata::Ns<Name>),
Ptr(rdata::Ptr<Name>),
Soa(rdata::Soa<Name>),
Srv(rdata::Srv<Name>),
Txt(rdata::Txt<Octs>),
}
impl<Name: ToString, Octs: AsRef<[u8]>> From<ParsedRData<Name, Octs>> for RData {
fn from(value: ParsedRData<Name, Octs>) -> Self {
match value {
ParsedRData::A(record_rdata) => RData::A(record_rdata.into()),
ParsedRData::Aaaa(record_rdata) => RData::Aaaa(record_rdata.into()),
ParsedRData::Cname(record_rdata) => RData::Cname(record_rdata.into()),
ParsedRData::Mx(record_rdata) => RData::Mx(record_rdata.into()),
ParsedRData::Ns(record_rdata) => RData::Ns(record_rdata.into()),
ParsedRData::Ptr(record_rdata) => RData::Ptr(record_rdata.into()),
ParsedRData::Soa(record_rdata) => RData::Soa(record_rdata.into()),
ParsedRData::Srv(record_rdata) => RData::Srv(record_rdata.into()),
ParsedRData::Txt(record_rdata) => RData::Txt(record_rdata.into()),
}
}
}
impl TryFrom<RData> for ParsedRData<Name<Vec<u8>>, Vec<u8>> {
type Error = Vec<Error>;
fn try_from(value: RData) -> Result<Self, Self::Error> {
let rdata = match value {
RData::A(record_rdata) => ParsedRData::A(record_rdata.parse_record()?),
RData::Aaaa(record_rdata) => ParsedRData::Aaaa(record_rdata.parse_record()?),
RData::Cname(record_rdata) => ParsedRData::Cname(record_rdata.parse_record()?),
RData::Mx(record_rdata) => ParsedRData::Mx(record_rdata.parse_record()?),
RData::Ns(record_rdata) => ParsedRData::Ns(record_rdata.parse_record()?),
RData::Ptr(record_rdata) => ParsedRData::Ptr(record_rdata.parse_record()?),
RData::Soa(record_rdata) => ParsedRData::Soa(record_rdata.parse_record()?),
RData::Srv(record_rdata) => ParsedRData::Srv(record_rdata.parse_record()?),
RData::Txt(record_rdata) => ParsedRData::Txt(record_rdata.parse_record()?),
};
Ok(rdata)
}
}
macro_rules! parse_name {
($value:expr, $field:ident, $rtype:literal, $errors:expr) => {
{
let name = push_error!(
validation::normalize_domain(&$value.$field),
$errors, concat!("/", stringify!($field))
);
let name = name.and_then(|name| {
push_error!(
name.parse::<Name<_>>().map_err(|e| {
Error::from(RecordParseError::RDataUnknown {
input: $value.$field,
field: stringify!(field).to_string(),
rtype: $rtype.to_string(),
}).with_cause(&e.to_string())
}),
$errors, concat!("/", stringify!($field))
)
});
name
}
};
}
/* --------- A --------- */
#[derive(Debug, Deserialize, Serialize)]
pub struct A {
pub address: String,
}
impl From<rdata::A> for A {
fn from(record_data: rdata::A) -> Self {
A { address: record_data.addr().to_string() }
}
}
impl A {
pub fn parse_record(self) -> Result<rdata::A, Vec<Error>> {
let mut errors = Vec::new();
let address = push_error!(self.address.parse::<Ipv4Addr>().map_err(|e| {
Error::from(RecordParseError::Ip4Address { input: self.address })
.with_cause(&e.to_string())
.with_path("/address")
}), errors);
if errors.is_empty() {
Ok(rdata::A::new(address.unwrap()))
} else {
Err(errors)
}
}
}
/* --------- AAAA --------- */
#[derive(Debug, Deserialize, Serialize)]
pub struct Aaaa {
pub address: String,
}
impl From<rdata::Aaaa> for Aaaa {
fn from(record_data: rdata::Aaaa) -> Self {
Aaaa { address: record_data.addr().to_string() }
}
}
impl Aaaa {
pub fn parse_record(self) -> Result<rdata::Aaaa, Vec<Error>> {
let mut errors = Vec::new();
let address = push_error!(self.address.parse::<Ipv6Addr>().map_err(|e| {
Error::from(RecordParseError::Ip6Address { input: self.address })
.with_cause(&e.to_string())
.with_path("/address")
}), errors);
if errors.is_empty() {
Ok(rdata::Aaaa::new(address.unwrap()))
} else {
Err(errors)
}
}
}
/* --------- CNAME --------- */
#[derive(Debug, Deserialize, Serialize)]
pub struct Cname {
pub target: String,
}
impl<N: ToString> From<rdata::Cname<N>> for Cname {
fn from(record_data: rdata::Cname<N>) -> Self {
Cname { target: record_data.cname().to_string() }
}
}
impl Cname {
pub fn parse_record(self) -> Result<rdata::Cname<Name<Vec<u8>>>, Vec<Error>> {
let mut errors = Vec::new();
let cname = parse_name!(self, target, "CNAME", errors);
if errors.is_empty() {
Ok(rdata::Cname::new(cname.unwrap()))
} else {
Err(errors)
}
}
}
/* --------- MX --------- */
#[derive(Debug, Deserialize, Serialize)]
pub struct Mx {
pub preference: u16,
pub mail_exchanger: String,
}
impl<N: ToString> From<rdata::Mx<N>> for Mx {
fn from(record_data: rdata::Mx<N>) -> Self {
Mx {
preference: record_data.preference(),
mail_exchanger: record_data.exchange().to_string()
}
}
}
impl Mx {
fn parse_record(self) -> Result<rdata::Mx<Name<Vec<u8>>>, Vec<Error>> {
let mut errors = Vec::new();
let mail_exchanger = parse_name!(self, mail_exchanger, "MX", errors);
if errors.is_empty() {
Ok(rdata::Mx::new(self.preference, mail_exchanger.unwrap()))
} else {
Err(errors)
}
}
}
/* --------- NS --------- */
#[derive(Debug, Deserialize, Serialize)]
pub struct Ns {
pub target: String,
}
impl<N: ToString> From<rdata::Ns<N>> for Ns {
fn from(record_rdata: rdata::Ns<N>) -> Self {
Ns {
target: record_rdata.nsdname().to_string(),
}
}
}
impl Ns {
fn parse_record(self) -> Result<rdata::Ns<Name<Vec<u8>>>, Vec<Error>> {
let mut errors = Vec::new();
let ns_name = parse_name!(self, target, "NS", errors);
if errors.is_empty() {
Ok(rdata::Ns::new(ns_name.unwrap()))
} else {
Err(errors)
}
}
}
/* --------- PTR --------- */
#[derive(Debug, Deserialize, Serialize)]
pub struct Ptr {
pub target: String,
}
impl<N: ToString> From<rdata::Ptr<N>> for Ptr {
fn from(record_rdata: rdata::Ptr<N>) -> Self {
Ptr {
target: record_rdata.ptrdname().to_string(),
}
}
}
impl Ptr {
fn parse_record(self) -> Result<rdata::Ptr<Name<Vec<u8>>>, Vec<Error>> {
let mut errors = Vec::new();
let ptr_name = parse_name!(self, target, "PTR", errors);
if errors.is_empty() {
Ok(rdata::Ptr::new(ptr_name.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<N: ToString> From<rdata::Soa<N>> for Soa {
fn from(record_rdata: rdata::Soa<N>) -> Self {
Soa {
primary_server: record_rdata.mname().to_string(),
maintainer: record_rdata.rname().to_string(),
refresh: record_rdata.refresh().as_secs(),
retry: record_rdata.retry().as_secs(),
expire: record_rdata.expire().as_secs(),
minimum: record_rdata.minimum().as_secs(),
serial: record_rdata.serial().into(),
}
}
}
impl Soa {
fn parse_record(self) -> Result<rdata::Soa<Name<Vec<u8>>>, Vec<Error>> {
let mut errors = Vec::new();
let primary_ns = parse_name!(self, primary_server, "SOA", errors);
let maintainer = parse_name!(self, maintainer, "SOA", errors);
if errors.is_empty() {
Ok(rdata::Soa::new(
primary_ns.unwrap(),
maintainer.unwrap(),
self.refresh.into(),
Ttl::from_secs(self.retry),
Ttl::from_secs(self.expire),
Ttl::from_secs(self.minimum),
Ttl::from_secs(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<N: ToString> From<rdata::Srv<N>> for Srv {
fn from(record_data: rdata::Srv<N>) -> Self {
Srv {
server: record_data.target().to_string(),
priority: record_data.priority(),
weight: record_data.weight(),
port: record_data.port(),
}
}
}
impl Srv {
fn parse_record(self) -> Result<rdata::Srv<Name<Vec<u8>>>, Vec<Error>> {
let mut errors = Vec::new();
let server = parse_name!(self, server, "SRV", errors);
if errors.is_empty() {
Ok(rdata::Srv::new(
self.priority,
self.weight,
self.port,
server.unwrap(),
))
} else {
Err(errors)
}
}
}
/* --------- TXT --------- */
#[derive(Debug, Deserialize, Serialize)]
pub struct Txt {
pub text: String,
}
impl<O: AsRef<[u8]>> From<rdata::Txt<O>> for Txt {
fn from(record_data: rdata::Txt<O>) -> Self {
let mut concatenated_text = String::new();
for text in record_data.iter() {
for c in text {
// Escapes '\' and non printable chars
let c = Symbol::display_from_octet(*c);
write!(concatenated_text, "{}", c).unwrap();
}
}
Txt {
text: concatenated_text
}
}
}
impl Txt {
fn parse_record(self) -> Result<rdata::Txt<Vec<u8>>, Vec<Error>> {
let mut errors = Vec::new();
let data = append_errors!(validation::parse_txt_data(&self.text), errors, "/text");
let data = data.and_then(|data| {
push_error!(rdata::Txt::build_from_slice(&data).map_err(|e| {
Error::from(RecordParseError::RDataUnknown {
input: self.text,
field: "text".into(),
rtype: "TXT".into(),
}).with_cause(&e.to_string())
.with_path("/text")
}), errors)
});
if errors.is_empty() {
Ok(data.unwrap())
} else {
Err(errors)
}
}
}
/* --------- ParsedRData: domain traits impl --------- */
impl<Name, Octs> ParsedRData<Name, Octs> {
pub fn rtype(&self) -> Rtype {
match self {
ParsedRData::A(_) => Rtype::A,
ParsedRData::Aaaa(_) => Rtype::AAAA,
ParsedRData::Cname(_) => Rtype::CNAME,
ParsedRData::Mx(_) => Rtype::MX,
ParsedRData::Ns(_) => Rtype::NS,
ParsedRData::Ptr(_) => Rtype::PTR,
ParsedRData::Soa(_) => Rtype::SOA,
ParsedRData::Srv(_) => Rtype::SRV,
ParsedRData::Txt(_) => Rtype::TXT,
}
}
}
impl<Name, Octs> RecordData for ParsedRData<Name, Octs> {
fn rtype(&self) -> Rtype {
ParsedRData::rtype(self)
}
}
impl<'a, Octs: Octets + ?Sized> ParseRecordData<'a, Octs> for ParsedRData<ParsedName<Octs::Range<'a>>, Octs::Range<'a>> {
fn parse_rdata(
rtype: Rtype,
parser: &mut Parser<'a, Octs>,
) -> Result<Option<Self>, ParseError> {
let record = match rtype {
Rtype::A => ParsedRData::A(rdata::A::parse(parser)?),
Rtype::AAAA => ParsedRData::Aaaa(rdata::Aaaa::parse(parser)?),
Rtype::CNAME => ParsedRData::Cname(rdata::Cname::parse(parser)?),
Rtype::MX => ParsedRData::Mx(rdata::Mx::parse(parser)?),
Rtype::NS => ParsedRData::Ns(rdata::Ns::parse(parser)?),
Rtype::PTR => ParsedRData::Ptr(rdata::Ptr::parse(parser)?),
Rtype::SOA => ParsedRData::Soa(rdata::Soa::parse(parser)?),
Rtype::SRV => ParsedRData::Srv(rdata::Srv::parse(parser)?),
Rtype::TXT => ParsedRData::Txt(rdata::Txt::parse(parser)?),
_ => return Ok(None)
};
Ok(Some(record))
}
}
impl<Name: ToName, Octs: AsRef<[u8]>> ComposeRecordData for ParsedRData<Name, Octs> {
fn rdlen(&self, compress: bool) -> Option<u16> {
match self {
ParsedRData::A(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Aaaa(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Cname(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Mx(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Ns(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Ptr(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Soa(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Srv(record_rdata) => record_rdata.rdlen(compress),
ParsedRData::Txt(record_rdata) => record_rdata.rdlen(compress),
}
}
fn compose_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
match self {
ParsedRData::A(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Aaaa(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Cname(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Mx(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Ns(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Ptr(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Soa(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Srv(record_rdata) => record_rdata.compose_rdata(target),
ParsedRData::Txt(record_rdata) => record_rdata.compose_rdata(target),
}
}
fn compose_canonical_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
match self {
ParsedRData::A(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Aaaa(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Cname(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Mx(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Ns(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Ptr(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Soa(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Srv(record_rdata) => record_rdata.compose_canonical_rdata(target),
ParsedRData::Txt(record_rdata) => record_rdata.compose_canonical_rdata(target),
}
}
}

View file

@ -3,8 +3,8 @@ use axum::Json;
use crate::AppState; use crate::AppState;
use crate::errors::Error; use crate::errors::Error;
use crate::ressouces::zone::{CreateZoneRequest, Zone}; use crate::resources::zone::{CreateZoneRequest, Zone};
use crate::ressouces::record::{AddRecordsRequest, Record, RecordList}; use crate::resources::record::{AddRecordsRequest, Record, RecordList};
pub async fn create_zone( pub async fn create_zone(

View file

@ -3,7 +3,7 @@ use serde_json::{Value, json};
use crate::AppState; use crate::AppState;
use crate::errors::Error; use crate::errors::Error;
use crate::ressouces::zone::Zone; use crate::resources::zone::Zone;
use crate::template::Template; use crate::template::Template;