wip: add missing files
This commit is contained in:
parent
c1d09cd391
commit
76aa894123
5 changed files with 346 additions and 0 deletions
119
src/resources/dns/external/record.rs
vendored
Normal file
119
src/resources/dns/external/record.rs
vendored
Normal file
|
@ -0,0 +1,119 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::rdata::RData;
|
||||
use crate::resources::dns::internal;
|
||||
use crate::{errors::Error, validation};
|
||||
use crate::macros::{append_errors, push_error};
|
||||
|
||||
pub enum RecordError {
|
||||
Validation { suberrors: Vec<Error> },
|
||||
}
|
||||
|
||||
pub enum RecordValidationError {
|
||||
NotInZone { name: String, zone: String },
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Record {
|
||||
pub name: String,
|
||||
pub ttl: u32,
|
||||
#[serde(flatten)]
|
||||
pub rdata: RData
|
||||
}
|
||||
|
||||
impl From<internal::Record> for Record {
|
||||
fn from(value: internal::Record) -> Self {
|
||||
Record {
|
||||
name: value.name.to_string(),
|
||||
ttl: value.ttl,
|
||||
rdata: value.rdata.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Record {
|
||||
fn validate(self, zone_name: &internal::Name) -> Result<internal::Record, Vec<Error>> {
|
||||
let mut errors = Vec::new();
|
||||
|
||||
let name = push_error!(validation::normalize_domain(&self.name), errors, "/name").map(internal::Name::new);
|
||||
|
||||
let name = name.and_then(|name| {
|
||||
if !name.ends_with(zone_name) {
|
||||
errors.push(
|
||||
Error::from(RecordValidationError::NotInZone { name: self.name, zone: zone_name.to_string() })
|
||||
.with_path("/name")
|
||||
);
|
||||
None
|
||||
} else {
|
||||
Some(name)
|
||||
}
|
||||
});
|
||||
// TODO: validate ttl
|
||||
let rdata = append_errors!(self.rdata.validate(), errors, "/rdata");
|
||||
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(internal::Record {
|
||||
name: name.unwrap(),
|
||||
ttl: self.ttl,
|
||||
rdata: rdata.unwrap(),
|
||||
})
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct RecordList(pub Vec<Record>);
|
||||
|
||||
impl From<internal::RecordList> for RecordList {
|
||||
fn from(value: internal::RecordList) -> Self {
|
||||
let records = value.records.into_iter().map(Record::from).collect();
|
||||
RecordList(records)
|
||||
}
|
||||
}
|
||||
|
||||
impl RecordList {
|
||||
fn validate(self, zone_name: &internal::Name) -> Result<internal::RecordList, Vec<Error>> {
|
||||
let mut errors = Vec::new();
|
||||
let mut records = Vec::new();
|
||||
|
||||
for (index, record) in self.0.into_iter().enumerate() {
|
||||
let record = append_errors!(record.validate(zone_name), errors, &format!("/{index}"));
|
||||
|
||||
if let Some(record) = record {
|
||||
records.push(record)
|
||||
}
|
||||
}
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(internal::RecordList { records })
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug,Deserialize)]
|
||||
pub struct AddRecords {
|
||||
pub new_records: RecordList
|
||||
}
|
||||
|
||||
impl AddRecords {
|
||||
pub fn validate(self, zone_name: &internal::Name) -> Result<internal::AddRecords, Error> {
|
||||
let mut errors = Vec::new();
|
||||
let records = append_errors!(self.new_records.validate(zone_name), errors, "/new_records");
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(internal::AddRecords {
|
||||
new_records: records.unwrap(),
|
||||
})
|
||||
} else {
|
||||
Err(Error::from(RecordError::Validation { suberrors: errors }))
|
||||
}
|
||||
}
|
||||
}
|
1
src/resources/dns/friendly/mod.rs
Normal file
1
src/resources/dns/friendly/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod rdata;
|
109
src/resources/dns/friendly/rdata.rs
Normal file
109
src/resources/dns/friendly/rdata.rs
Normal file
|
@ -0,0 +1,109 @@
|
|||
use std::fmt;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub enum FriendlyRData {
|
||||
Address(Address),
|
||||
Service(Service),
|
||||
MailServer(MailServer), // Include SPF, etc ?!
|
||||
NameServer(NameServer),
|
||||
TextData(TextData),
|
||||
Alias(Alias),
|
||||
|
||||
}
|
||||
|
||||
pub enum FriendlyRType {
|
||||
Address,
|
||||
Service,
|
||||
MailServer,
|
||||
NameServer,
|
||||
TextData,
|
||||
Alias
|
||||
}
|
||||
|
||||
pub struct FriendlyRecord {
|
||||
owner: String,
|
||||
friendly_type: FriendlyRType,
|
||||
rdata: FriendlyRData,
|
||||
ttl: u32,
|
||||
}
|
||||
|
||||
pub struct RecordSet {
|
||||
friendly_type: FriendlyRType,
|
||||
records: Vec<FriendlyRecord>
|
||||
}
|
||||
|
||||
pub struct RecordSetGroup {
|
||||
owner: String,
|
||||
rrsets: Vec<RecordSet>
|
||||
}
|
||||
|
||||
pub struct FriendlyRecords(Vec<RecordSetGroup>);
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Address {
|
||||
pub address: String
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Service {
|
||||
pub service_type: String,
|
||||
pub port: u16,
|
||||
pub weight: u16,
|
||||
pub priority: u16,
|
||||
pub server: String,
|
||||
}
|
||||
|
||||
pub enum ServiceType {
|
||||
ServiceProtocol { service_name: String, protocol: String },
|
||||
Service { service_name: String },
|
||||
None,
|
||||
}
|
||||
|
||||
impl ServiceType {
|
||||
pub fn from_name(name: String) -> (Self, String) {
|
||||
let labels: Vec<_> = name.splitn(3, '.').collect();
|
||||
let prefix: Vec<_> = labels.iter().cloned().take(2).map(|label| label.strip_prefix('_')).collect();
|
||||
|
||||
if prefix.is_empty() || prefix[0].is_none() {
|
||||
(ServiceType::None, labels[0..].join(".").to_owned())
|
||||
} else if prefix.len() == 1 || prefix[1].is_none() {
|
||||
(ServiceType::Service { service_name: prefix[0].unwrap().into() }, labels[1..].join(".").to_owned())
|
||||
} else {
|
||||
(ServiceType::ServiceProtocol { service_name: prefix[0].unwrap().into(), protocol: prefix[1].unwrap().into() }, labels[2].to_owned())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ServiceType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ServiceType::ServiceProtocol { service_name, protocol } => write!(f, "{}/{}", service_name, protocol),
|
||||
ServiceType::Service { service_name, } => write!(f, "{}", service_name),
|
||||
ServiceType::None => write!(f, "-"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct MailServer {
|
||||
pub preference: u16,
|
||||
pub mail_exchanger: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct NameServer {
|
||||
pub target: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct TextData {
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Alias {
|
||||
pub target: String,
|
||||
}
|
90
src/resources/dns/internal/base.rs
Normal file
90
src/resources/dns/internal/base.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Name {
|
||||
name: String
|
||||
}
|
||||
|
||||
impl Name {
|
||||
pub fn new(name: String) -> Self {
|
||||
Name {
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ends_with(&self, other: &Name) -> bool {
|
||||
self.name == other.name || self.name.ends_with(&(String::from(".") + &other.name))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Name {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Name {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
let mut labels = self.name.split('.').rev();
|
||||
let mut other_labels = other.name.split('.').rev();
|
||||
|
||||
loop {
|
||||
match (labels.next(), other_labels.next()) {
|
||||
(Some(label), Some(other_label)) => match label.cmp(other_label) {
|
||||
std::cmp::Ordering::Equal => (),
|
||||
res => return res,
|
||||
},
|
||||
(None, Some(_)) => return std::cmp::Ordering::Less,
|
||||
(Some(_), None) => return std::cmp::Ordering::Greater,
|
||||
(None, None) => return std::cmp::Ordering::Equal,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Name {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub enum Rtype {
|
||||
A,
|
||||
Aaaa,
|
||||
Cname,
|
||||
Mx,
|
||||
Ns,
|
||||
Ptr,
|
||||
Soa,
|
||||
Srv,
|
||||
Txt
|
||||
}
|
||||
|
||||
impl Rtype {
|
||||
pub fn value(&self) -> u16 {
|
||||
match self {
|
||||
Rtype::A => 1,
|
||||
Rtype::Aaaa => 28,
|
||||
Rtype::Cname => 5,
|
||||
Rtype::Mx => 15,
|
||||
Rtype::Ns => 2,
|
||||
Rtype::Ptr => 12,
|
||||
Rtype::Soa => 6,
|
||||
Rtype::Srv => 33,
|
||||
Rtype::Txt => 16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Rtype {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.value().cmp(&other.value())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Rtype {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
27
src/resources/dns/internal/record.rs
Normal file
27
src/resources/dns/internal/record.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
use super::rdata::RData;
|
||||
use super::Name;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Record {
|
||||
pub name: Name,
|
||||
pub ttl: u32,
|
||||
pub rdata: RData
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RecordList {
|
||||
pub records: Vec<Record>,
|
||||
}
|
||||
|
||||
impl RecordList {
|
||||
pub fn sort(&mut self) {
|
||||
self.records.sort_by(|r1, r2| {
|
||||
let key1 = (&r1.name, r1.rdata.rtype());
|
||||
let key2 = (&r2.name, r2.rdata.rtype());
|
||||
key1.cmp(&key2)
|
||||
});
|
||||
}
|
||||
}
|
||||
pub struct AddRecords {
|
||||
pub new_records: RecordList
|
||||
}
|
Loading…
Reference in a new issue