use trust_dns_proto::DnsHandle; use trust_dns_client::client::ClientHandle; use trust_dns_client::rr::{DNSClass, RecordType}; use trust_dns_client::op::{UpdateMessage, OpCode, MessageType, Message, Query, ResponseCode}; use trust_dns_client::error::ClientError; use super::{Name, Record, RData}; use super::client::{ClientResponse, DnsClient}; use super::api::{RecordApi, ZoneApi}; #[derive(Debug)] pub enum DnsApiError { ClientError(ClientError), ResponceNotOk { code: ResponseCode, zone: Name, }, } pub struct DnsApiClient { client: DnsClient } impl DnsApiClient { pub fn new(client: DnsClient) -> Self { DnsApiClient { client } } } #[async_trait] impl RecordApi for DnsApiClient { type Error = DnsApiError; async fn get_records(&mut self, zone: Name, class: DNSClass) -> Result, Self::Error> { let response = { let query = self.client.query(zone.clone(), class, RecordType::AXFR); query.await.map_err(|e| DnsApiError::ClientError(e))? }; if response.response_code() != ResponseCode::NoError { return Err(DnsApiError::ResponceNotOk { 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) -> 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(); edns.set_max_payload(1232); edns.set_version(0); } let response = ClientResponse(self.client.send(message)).await.map_err(|e| DnsApiError::ClientError(e))?; if response.response_code() != ResponseCode::NoError { return Err(DnsApiError::ResponceNotOk { code: response.response_code(), zone: zone, }); } Ok(()) } async fn update_records(&mut self, zone: Name, class: DNSClass, old_records: Vec, new_records: Vec) -> 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 let mut zone_query: 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); // 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; // the class must be none for delete for record in delete.iter_mut() { 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(); edns.set_max_payload(1232); edns.set_version(0); } let response = ClientResponse(self.client.send(message)).await.map_err(|e| DnsApiError::ClientError(e))?; if response.response_code() != ResponseCode::NoError { return Err(DnsApiError::ResponceNotOk { code: response.response_code(), zone: zone, }); } Ok(()) } } #[async_trait] impl ZoneApi for DnsApiClient { type Error = DnsApiError; 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); query.await.map_err(|e| DnsApiError::ClientError(e))? }; if response.response_code() != ResponseCode::NoError { return Err(DnsApiError::ResponceNotOk { code: response.response_code(), zone: zone, }); } Ok(()) } }