nomilo/src/dns/dns_connector.rs

236 lines
7.2 KiB
Rust
Raw Normal View History

2022-03-04 12:08:03 +00:00
use trust_dns_proto::DnsHandle;
2022-03-04 20:55:27 +00:00
use trust_dns_client::client::ClientHandle;
2022-03-04 12:08:03 +00:00
use trust_dns_client::rr::{DNSClass, RecordType};
2022-03-04 12:50:57 +00:00
use trust_dns_client::op::{UpdateMessage, OpCode, MessageType, Message, Query, ResponseCode};
use trust_dns_client::error::ClientError;
2022-03-04 12:08:03 +00:00
2022-03-04 16:17:15 +00:00
use super::{Name, Record, RData};
use super::client::{ClientResponse, DnsClient};
2022-03-05 12:19:31 +00:00
use super::connector::{RecordConnector, ZoneConnector};
2022-03-04 12:08:03 +00:00
2022-03-05 12:07:51 +00:00
const MAX_PAYLOAD_LEN: u16 = 1232;
2022-03-04 12:50:57 +00:00
#[derive(Debug)]
2022-03-05 12:19:31 +00:00
pub enum DnsConnectorError {
2022-03-04 21:24:37 +00:00
ClientError(ClientError),
ResponceNotOk {
code: ResponseCode,
zone: Name,
},
2022-03-04 12:08:03 +00:00
}
2022-03-05 12:19:31 +00:00
pub struct DnsConnectorClient {
2022-03-04 21:24:37 +00:00
client: DnsClient
}
2022-03-05 12:19:31 +00:00
impl DnsConnectorClient {
2022-03-04 21:24:37 +00:00
pub fn new(client: DnsClient) -> Self {
2022-03-05 12:19:31 +00:00
DnsConnectorClient {
2022-03-04 21:24:37 +00:00
client
}
}
}
2022-03-04 12:08:03 +00:00
2022-03-04 12:50:57 +00:00
#[async_trait]
2022-03-05 12:19:31 +00:00
impl RecordConnector for DnsConnectorClient {
type Error = DnsConnectorError;
2022-03-04 21:24:37 +00:00
async fn get_records(&mut self, zone: Name, class: DNSClass) -> Result<Vec<Record>, Self::Error>
{
let response = {
let query = self.client.query(zone.clone(), class, RecordType::AXFR);
2022-03-05 12:19:31 +00:00
query.await.map_err(|e| DnsConnectorError::ClientError(e))?
2022-03-04 21:24:37 +00:00
};
if response.response_code() != ResponseCode::NoError {
2022-03-05 12:19:31 +00:00
return Err(DnsConnectorError::ResponceNotOk {
2022-03-04 21:24:37 +00:00
code: response.response_code(),
zone: zone,
});
}
let answers = response.answers();
let mut records: Vec<_> = answers.to_vec().into_iter()
.filter(|record| !matches!(record.rdata(), RData::NULL { .. } | RData::DNSSEC(_)))
.collect();
// AXFR response ends with SOA, we remove it so it is not doubled in the response.
records.pop();
Ok(records)
}
async fn add_records(&mut self, zone: Name, class: DNSClass, new_records: Vec<Record>) -> Result<(), Self::Error>
{
// Taken from trust_dns_client::op::update_message::append
// The original function can not be used as is because it takes a RecordSet and not a Record list
let mut zone_query = Query::new();
zone_query.set_name(zone.clone())
.set_query_class(class)
.set_query_type(RecordType::SOA);
let mut message = Message::new();
// TODO: set random / time based id
message
.set_id(0)
.set_message_type(MessageType::Query)
.set_op_code(OpCode::Update)
.set_recursion_desired(false);
message.add_zone(zone_query);
message.add_updates(new_records);
{
let edns = message.edns_mut();
2022-03-05 12:07:51 +00:00
edns.set_max_payload(MAX_PAYLOAD_LEN);
2022-03-04 21:24:37 +00:00
edns.set_version(0);
}
2022-03-04 12:08:03 +00:00
2022-03-05 12:19:31 +00:00
let response = ClientResponse(self.client.send(message)).await.map_err(|e| DnsConnectorError::ClientError(e))?;
if response.response_code() != ResponseCode::NoError {
2022-03-05 12:19:31 +00:00
return Err(DnsConnectorError::ResponceNotOk {
2022-03-04 21:24:37 +00:00
code: response.response_code(),
zone: zone,
});
}
Ok(())
2022-03-04 21:24:37 +00:00
}
async fn update_records(&mut self, zone: Name, class: DNSClass, old_records: Vec<Record>, new_records: Vec<Record>) -> Result<(), Self::Error>
{
// Taken from trust_dns_client::op::update_message::compare_and_swap
// The original function can not be used as is because it takes a RecordSet and not a Record list
// for updates, the query section is used for the zone
2022-03-05 12:07:51 +00:00
let mut zone_query = Query::new();
2022-03-04 21:24:37 +00:00
zone_query.set_name(zone.clone())
.set_query_class(class)
.set_query_type(RecordType::SOA);
let mut message: Message = Message::new();
// build the message
// TODO: set random / time based id
message
.set_id(0)
.set_message_type(MessageType::Query)
.set_op_code(OpCode::Update)
.set_recursion_desired(false);
message.add_zone(zone_query);
// make sure the record is what is expected
let mut prerequisite = old_records.clone();
for record in prerequisite.iter_mut() {
record.set_ttl(0);
}
message.add_pre_requisites(prerequisite);
// add the delete for the old record
let mut delete = old_records;
for record in delete.iter_mut() {
2022-03-05 12:07:51 +00:00
// the class must be none for delete
2022-03-04 21:24:37 +00:00
record.set_dns_class(DNSClass::NONE);
// the TTL should be 0
record.set_ttl(0);
}
message.add_updates(delete);
// insert the new record...
message.add_updates(new_records);
// Extended dns
{
let edns = message.edns_mut();
2022-03-05 12:07:51 +00:00
edns.set_max_payload(MAX_PAYLOAD_LEN);
2022-03-04 21:24:37 +00:00
edns.set_version(0);
}
2022-03-05 12:19:31 +00:00
let response = ClientResponse(self.client.send(message)).await.map_err(|e| DnsConnectorError::ClientError(e))?;
2022-03-04 20:55:27 +00:00
if response.response_code() != ResponseCode::NoError {
2022-03-05 12:19:31 +00:00
return Err(DnsConnectorError::ResponceNotOk {
2022-03-04 21:24:37 +00:00
code: response.response_code(),
zone: zone,
});
}
2022-03-04 20:55:27 +00:00
Ok(())
2022-03-04 21:24:37 +00:00
}
2022-03-05 12:07:51 +00:00
async fn delete_records(&mut self, zone: Name, class: DNSClass, records: Vec<Record>) -> Result<(), Self::Error>
{
// for updates, the query section is used for the zone
let mut zone_query = Query::new();
zone_query.set_name(zone.clone())
.set_query_class(class)
.set_query_type(RecordType::SOA);
let mut message: Message = Message::new();
// build the message
// TODO: set random / time based id
message
.set_id(0)
.set_message_type(MessageType::Query)
.set_op_code(OpCode::Update)
.set_recursion_desired(false);
message.add_zone(zone_query);
let mut delete = records;
for record in delete.iter_mut() {
// the class must be none for delete
record.set_dns_class(DNSClass::NONE);
// the TTL should be 0
record.set_ttl(0);
}
message.add_updates(delete);
// Extended dns
{
let edns = message.edns_mut();
edns.set_max_payload(MAX_PAYLOAD_LEN);
edns.set_version(0);
}
2022-03-05 12:19:31 +00:00
let response = ClientResponse(self.client.send(message)).await.map_err(|e| DnsConnectorError::ClientError(e))?;
2022-03-05 12:07:51 +00:00
if response.response_code() != ResponseCode::NoError {
2022-03-05 12:19:31 +00:00
return Err(DnsConnectorError::ResponceNotOk {
2022-03-05 12:07:51 +00:00
code: response.response_code(),
zone: zone,
});
}
Ok(())
}
}
#[async_trait]
2022-03-05 12:19:31 +00:00
impl ZoneConnector for DnsConnectorClient {
type Error = DnsConnectorError;
2022-03-04 21:24:37 +00:00
async fn zone_exists(&mut self, zone: Name, class: DNSClass) -> Result<(), Self::Error>
{
let response = {
let query = self.client.query(zone.clone(), class, RecordType::SOA);
2022-03-05 12:19:31 +00:00
query.await.map_err(|e| DnsConnectorError::ClientError(e))?
2022-03-04 21:24:37 +00:00
};
if response.response_code() != ResponseCode::NoError {
2022-03-05 12:19:31 +00:00
return Err(DnsConnectorError::ResponceNotOk {
2022-03-04 21:24:37 +00:00
code: response.response_code(),
zone: zone,
});
}
Ok(())
}
2022-03-04 12:08:03 +00:00
}