diff --git a/src/dns/api.rs b/src/dns/api.rs new file mode 100644 index 0000000..83b2038 --- /dev/null +++ b/src/dns/api.rs @@ -0,0 +1,24 @@ +use crate::dns; + +// TODO: Use model types instead of dns types as input / output and only convert internaly? + +// Zone content api +// E.g.: DNS update + axfr, zone file read + write +#[async_trait] +pub trait RecordApi { + type Error; + + async fn get_records(&mut self, zone: dns::Name, class: dns::DNSClass) -> Result, Self::Error>; + async fn add_records(&mut self, zone: dns::Name, class: dns::DNSClass, new_records: Vec) -> Result<(), Self::Error>; + // update_records + // delete_records +} + +// Zone management api, todo +// E.g.: Manage catalog zone, dynamically generate knot / bind / nsd config... +pub trait ZoneApi { + // get_zones + // add_zone + // delete_zone + // exists +} \ No newline at end of file diff --git a/src/dns/client.rs b/src/dns/client.rs index da942fe..dfcd761 100644 --- a/src/dns/client.rs +++ b/src/dns/client.rs @@ -8,7 +8,7 @@ use trust_dns_proto::error::ProtoError; use trust_dns_proto::iocompat::AsyncIoTokioAsStd; use crate::config::Config; -use super::message::DnsMessage; + pub struct DnsClient(AsyncClient); @@ -27,8 +27,6 @@ impl DerefMut for DnsClient { } } -impl DnsMessage for AsyncClient {} - #[rocket::async_trait] impl<'r> FromRequest<'r> for DnsClient { diff --git a/src/dns/message.rs b/src/dns/dns_api.rs similarity index 73% rename from src/dns/message.rs rename to src/dns/dns_api.rs index 5ff620f..38a8557 100644 --- a/src/dns/message.rs +++ b/src/dns/dns_api.rs @@ -2,11 +2,11 @@ use trust_dns_proto::DnsHandle; 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 trust_dns_proto::error::ProtoError; use trust_dns_client::proto::xfer::{DnsRequestOptions}; use super::{Name, Record, RData}; -use super::client::{ClientResponse}; +use super::client::{ClientResponse, DnsClient}; +use super::api::RecordApi; #[derive(Debug)] @@ -21,15 +21,29 @@ pub enum MessageError { ResponceNotOk(ResponseCode) } +pub struct DnsApiClient { + client: DnsClient +} + +impl DnsApiClient { + pub fn new(client: DnsClient) -> Self { + DnsApiClient { + client + } + } +} + #[async_trait] -pub trait DnsMessage: DnsHandle + Send { - async fn get_records(&mut self, zone: Name, class: DNSClass) -> Result, MessageError> +impl RecordApi for DnsApiClient { + type Error = MessageError; + + async fn get_records(&mut self, zone: Name, class: DNSClass) -> Result, Self::Error> { let response = { let mut query = Query::query(zone, RecordType::AXFR); query.set_query_class(class); - ClientResponse(self.lookup(query, DnsRequestOptions::default())).await.map_err(|e| MessageError::ClientError(e))? + ClientResponse(self.client.lookup(query, DnsRequestOptions::default())).await.map_err(|e| MessageError::ClientError(e))? }; if response.response_code() != ResponseCode::NoError { @@ -46,7 +60,7 @@ pub trait DnsMessage: DnsHandle + Send { Ok(records) } - fn add_records(&mut self, zone: Name, class: DNSClass, new_records: Vec) -> Result::Response>, MessageError> + async fn add_records(&mut self, zone: Name, class: DNSClass, new_records: Vec) -> Result<(), Self::Error> { let mut mismatched_class = Vec::new(); let mut mismatched_zone = Vec::new(); @@ -90,6 +104,12 @@ pub trait DnsMessage: DnsHandle + Send { edns.set_version(0); } - return Ok(ClientResponse(self.send(message))); + let response = ClientResponse(self.client.send(message)).await.map_err(|e| MessageError::ClientError(e))?; + + if response.response_code() != ResponseCode::NoError { + return Err(MessageError::ResponceNotOk(response.response_code())); + } + + Ok(()) } } \ No newline at end of file diff --git a/src/dns/mod.rs b/src/dns/mod.rs index 2aa75ab..fb43308 100644 --- a/src/dns/mod.rs +++ b/src/dns/mod.rs @@ -1,5 +1,6 @@ pub mod client; -pub mod message; +pub mod dns_api; +pub mod api; // Reexport trust dns types for convenience pub use trust_dns_client::rr::rdata::{ @@ -8,4 +9,7 @@ pub use trust_dns_client::rr::rdata::{ pub use trust_dns_client::rr::{ RData, DNSClass, Record }; -pub use trust_dns_proto::rr::Name; \ No newline at end of file +pub use trust_dns_proto::rr::Name; + +// Reexport module types +pub use api::{RecordApi, ZoneApi}; \ No newline at end of file diff --git a/src/routes/zones.rs b/src/routes/zones.rs index b884661..1276b6d 100644 --- a/src/routes/zones.rs +++ b/src/routes/zones.rs @@ -11,14 +11,15 @@ use trust_dns_client::rr::{DNSClass, RecordType}; use crate::DbConn; use crate::dns; -use crate::dns::message::DnsMessage; -use crate::dns::message::MessageError; +use crate::dns::api::RecordApi; +use crate::dns::dns_api::DnsApiClient; +use crate::dns::dns_api::MessageError; use crate::models; #[get("/zones//records")] pub async fn get_zone_records( - mut client: dns::client::DnsClient, + client: dns::client::DnsClient, conn: DbConn, user_info: Result, zone: models::AbsoluteName @@ -35,7 +36,9 @@ pub async fn get_zone_records( } }).await?; - let records: Vec<_> = match client.get_records(zone.clone(), DNSClass::IN).await { + let mut dns_api = DnsApiClient::new(client); + + let records: Vec<_> = match dns_api.get_records(zone.clone(), DNSClass::IN).await { Ok(records) => records.into_iter().map(models::Record::from).collect(), Err(MessageError::ResponceNotOk(code)) => { @@ -55,7 +58,7 @@ pub async fn get_zone_records( #[post("/zones//records", data = "")] pub async fn create_zone_records( - mut client: dns::client::DnsClient, + client: dns::client::DnsClient, conn: DbConn, user_info: Result, zone: models::AbsoluteName, @@ -98,8 +101,13 @@ pub async fn create_zone_records( ).err(); } - let response = match client.add_records(zone.clone(), DNSClass::IN, records) { - Ok(query) => query.await.map_err(models::make_500)?, + let mut dns_api = DnsApiClient::new(client); + + match dns_api.add_records(zone.clone(), DNSClass::IN, records).await { + Ok(_) => { + return Ok(Json(())); + //query.await.map_err(models::make_500)?; + } Err(MessageError::RecordNotInZone { zone, class, mismatched_class, mismatched_zone}) => { return models::ErrorResponse::new( Status::BadRequest, @@ -113,21 +121,17 @@ pub async fn create_zone_records( }) ).err(); }, + Err(MessageError::ResponceNotOk(code)) => { + println!("Update of zone {} failed with code {}", *zone, code); + return models::ErrorResponse::new( + Status::NotFound, + "Update of zone failed".into() + ).with_details(json!({ + "zone_name": zone.to_utf8() + })).err(); + }, Err(e) => return models::make_500(e).err() }; - - // TODO: better error handling - if response.response_code() != ResponseCode::NoError { - println!("Update of zone {} failed with code {}", *zone, response.response_code()); - return models::ErrorResponse::new( - Status::NotFound, - "Update of zone failed".into() - ).with_details(json!({ - "zone_name": zone.to_utf8() - })).err(); - } - - Ok(Json(())) } #[get("/zones")]