improve rdata deserialization support and better implemetation of get zones records

main
Hannaeko 2021-03-20 02:08:55 -04:00
parent 1a238ea01f
commit 7155c1cc54
5 changed files with 144 additions and 46 deletions

1
Cargo.lock generated
View File

@ -710,6 +710,7 @@ dependencies = [
name = "nomilo" name = "nomilo"
version = "0.1.0-dev" version = "0.1.0-dev"
dependencies = [ dependencies = [
"base64 0.13.0",
"rocket", "rocket",
"rocket_contrib", "rocket_contrib",
"serde", "serde",

View File

@ -14,3 +14,4 @@ serde_json = "1.0"
rocket = "0.4.7" rocket = "0.4.7"
rocket_contrib = { version = "0.4", default-features = false, features = ["json"]} rocket_contrib = { version = "0.4", default-features = false, features = ["json"]}
toml = "0.5" toml = "0.5"
base64 = "0.13.0"

View File

@ -19,11 +19,17 @@ fn zone_records(client: State<SyncClient<TcpClientConnection>>, zone: String) ->
// TODO: Implement FromParam for Name // TODO: Implement FromParam for Name
let name = Name::from_str(&zone).unwrap(); let name = Name::from_str(&zone).unwrap();
// TODO: add support for all trust-dns record types let response: DnsResponse = client.query(&name, DNSClass::IN, RecordType::AXFR).unwrap();
// then use AXFR here and filter out dnssec related fields
let response: DnsResponse = client.query(&name, DNSClass::IN, RecordType::AAAA).unwrap();
let answers: &[Record] = response.answers(); let answers: &[Record] = response.answers();
let records: Vec<types::dns::Record> = answers.to_vec().into_iter().map(|record| record.into()).collect(); let mut records: Vec<_> = answers.to_vec().into_iter()
.map(|record| types::dns::Record::from(record))
.filter(|record| match record.rdata {
types::dns::RData::NULL { .. } | types::dns::RData::DNSSEC(_) => false,
_ => true,
}).collect();
// AXFR response ends with SOA, we remove it so it is not doubled in the response.
records.pop();
Json(records) Json(records)
} }

View File

@ -1,16 +1,11 @@
use std::net::{Ipv6Addr, Ipv4Addr}; use std::net::{Ipv6Addr, Ipv4Addr};
use std::fmt;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use trust_dns_client::serialize::binary::BinEncoder;
use base64;
pub mod trust_dns_types { use super::trust_dns_types;
pub use trust_dns_client::rr::rdata::{
DNSSECRecordType, NULL,
};
pub use trust_dns_client::rr::{
RecordType, RData, DNSClass, Record
};
pub use trust_dns_proto::rr::Name;
}
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub enum RecordType { pub enum RecordType {
@ -140,6 +135,7 @@ impl Into<trust_dns_types::RecordType> for RecordType {
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
#[serde(tag = "Type")] #[serde(tag = "Type")]
#[serde(rename_all = "UPPERCASE")]
pub enum RData { pub enum RData {
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]
A { A {
@ -149,28 +145,61 @@ pub enum RData {
AAAA { AAAA {
address: Ipv6Addr address: Ipv6Addr
}, },
ANAME(StringName), #[serde(rename_all = "PascalCase")]
// CAA(CAA), CAA {
CNAME(StringName), issuer_critical: bool,
value: String,
property_tag: String,
},
#[serde(rename_all = "PascalCase")]
CNAME {
target: String
},
// HINFO(HINFO), // HINFO(HINFO),
// HTTPS(SVCB), // HTTPS(SVCB),
// MX(MX), #[serde(rename_all = "PascalCase")]
MX {
preference: u16,
mail_exchanger: String
},
// NAPTR(NAPTR), // NAPTR(NAPTR),
NULL(NULL), #[serde(rename_all = "PascalCase")]
NS(StringName), NULL {
data: String
},
#[serde(rename_all = "PascalCase")]
NS {
target: String
},
// OPENPGPKEY(OPENPGPKEY), // OPENPGPKEY(OPENPGPKEY),
// OPT(OPT), // OPT(OPT),
PTR(StringName), #[serde(rename_all = "PascalCase")]
// SOA(SOA), PTR {
target: String
},
#[serde(rename_all = "PascalCase")]
SOA {
master_server_name: String,
maintainer_name: String,
refresh: i32,
retry: i32,
expire: i32,
minimum: u32,
serial: u32
},
// SRV(SRV), // SRV(SRV),
// SSHFP(SSHFP), // SSHFP(SSHFP),
// SVCB(SVCB), // SVCB(SVCB),
// TLSA(TLSA), // TLSA(TLSA),
// TXT(TXT), // TXT(TXT),
// DNSSEC(DNSSECRData),
// TODO: Eventually allow deserialization of DNSSEC records
#[serde(skip)]
DNSSEC(trust_dns_types::DNSSECRData),
#[serde(rename_all = "PascalCase")]
Unknown { Unknown {
code: u16, code: u16,
rdata: NULL, data: String,
}, },
// ZERO, // ZERO,
} }
@ -180,33 +209,84 @@ impl From<trust_dns_types::RData> for RData {
match rdata { match rdata {
trust_dns_types::RData::A(address) => RData::A { address }, trust_dns_types::RData::A(address) => RData::A { address },
trust_dns_types::RData::AAAA(address) => RData::AAAA { address }, trust_dns_types::RData::AAAA(address) => RData::AAAA { address },
_ => unimplemented!() // Still a draft, no iana number yet, I don't to put something that is not currently supported so that's why NULL and not unknown.
// TODO: probably need better error here, I don't know what to do about that as this would require to change the From for something else.
// (empty data because I'm lazy)
trust_dns_types::RData::ANAME(_) => RData::NULL {
data: String::new()
},
trust_dns_types::RData::CNAME(target) => RData::CNAME {
target: target.to_utf8()
},
trust_dns_types::RData::CAA(caa) => RData::CAA {
issuer_critical: caa.issuer_critical(),
value: format!("{}", CAAValue(caa.value())),
property_tag: caa.tag().as_str().to_string(),
},
trust_dns_types::RData::MX(mx) => RData::MX {
preference: mx.preference(),
mail_exchanger: mx.exchange().to_utf8()
},
trust_dns_types::RData::NULL(null) => RData::NULL {
data: base64::encode(null.anything().map(|data| data.to_vec()).unwrap_or_default())
},
trust_dns_types::RData::NS(target) => RData::NS {
target: target.to_utf8()
},
trust_dns_types::RData::PTR(target) => RData::PTR {
target: target.to_utf8()
},
trust_dns_types::RData::SOA(soa) => RData::SOA {
master_server_name: soa.mname().to_utf8(),
maintainer_name: soa.rname().to_utf8(),
refresh: soa.refresh(),
retry: soa.retry(),
expire: soa.expire(),
minimum: soa.minimum(),
serial: soa.serial()
},
trust_dns_types::RData::DNSSEC(data) => RData::DNSSEC(data),
rdata => {
let code = rdata.to_record_type().into();
let mut data = Vec::new();
let mut encoder = BinEncoder::new(&mut data);
rdata.emit(&mut encoder).expect("could not encode data");
RData::Unknown {
code,
data: base64::encode(data),
} }
} }
} }
#[derive(Deserialize, Serialize)]
pub struct StringName(String);
impl From<trust_dns_types::Name> for StringName {
fn from(name: trust_dns_types::Name) -> StringName {
StringName(name.to_utf8())
} }
} }
struct CAAValue<'a>(&'a trust_dns_types::caa::Value);
#[derive(Deserialize, Serialize)] // trust_dns Display implementation panics if no parameters
pub struct NULL { // Implementation based on caa::emit_value
pub anything: Option<Vec<u8>>, // Also the quotes are strips to render in JSON
impl<'a> fmt::Display for CAAValue<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self.0 {
trust_dns_types::caa::Value::Issuer(name, parameters) => {
if let Some(name) = name {
write!(f, "{}", name)?;
} }
impl From<trust_dns_types::NULL> for NULL { if name.is_none() && parameters.is_empty() {
fn from(null: trust_dns_types::NULL) -> NULL { write!(f, ";")?;
NULL {
anything: null.anything().map(|e| e.to_vec())
} }
for value in parameters {
write!(f, "; {}", value)?;
}
}
trust_dns_types::caa::Value::Url(url) => write!(f, "{}", url)?,
trust_dns_types::caa::Value::Unknown(v) => write!(f, "{:?}", v)?,
}
Ok(())
} }
} }
@ -237,21 +317,21 @@ impl From<trust_dns_types::DNSClass> for DNSClass {
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct Record { pub struct Record {
#[serde(rename = "Name")] #[serde(rename = "Name")]
name: StringName, pub name: String,
//#[serde(rename = "Type")] //#[serde(rename = "Type")]
//rr_type: RecordType, //rr_type: RecordType,
#[serde(rename = "Class")] #[serde(rename = "Class")]
dns_class: DNSClass, pub dns_class: DNSClass,
#[serde(rename = "TTL")] #[serde(rename = "TTL")]
ttl: u32, pub ttl: u32,
#[serde(flatten)] #[serde(flatten)]
rdata: RData, pub rdata: RData,
} }
impl From<trust_dns_types::Record> for Record { impl From<trust_dns_types::Record> for Record {
fn from(record: trust_dns_types::Record) -> Record { fn from(record: trust_dns_types::Record) -> Record {
Record { Record {
name: StringName(record.name().to_utf8()), name: record.name().to_utf8(),
//rr_type: record.rr_type().into(), //rr_type: record.rr_type().into(),
dns_class: record.dns_class().into(), dns_class: record.dns_class().into(),
ttl: record.ttl(), ttl: record.ttl(),

View File

@ -1 +1,11 @@
pub mod dns; pub mod dns;
pub mod trust_dns_types {
pub use trust_dns_client::rr::rdata::{
DNSSECRecordType, NULL, caa, DNSSECRData
};
pub use trust_dns_client::rr::{
RecordType, RData, DNSClass, Record
};
pub use trust_dns_proto::rr::Name;
}