diff --git a/src/models/dns.rs b/src/models/dns.rs index 8c0f752..c9d13b8 100644 --- a/src/models/dns.rs +++ b/src/models/dns.rs @@ -18,7 +18,7 @@ use super::trust_dns_types::{self, Name}; use crate::config::Config; -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Clone)] #[serde(tag = "Type")] #[serde(rename_all = "UPPERCASE")] pub enum RData { @@ -101,6 +101,8 @@ pub enum RData { data: String, }, // ZERO, + + // TODO: DS } impl From for RData { @@ -156,6 +158,7 @@ impl From for RData { fingerprint: trust_dns_types::sshfp::HEX.encode(sshfp.fingerprint()), }, //TODO: This might alter data if not utf8 compatible, probably need to be replaced + //TODO: check whether concatenating txt data is harmful or not trust_dns_types::RData::TXT(txt) => RData::TXT { text: format!("{}", txt) }, trust_dns_types::RData::DNSSEC(data) => RData::DNSSEC(data), rdata => { @@ -284,7 +287,7 @@ impl<'a> fmt::Display for CAAValue<'a> { } } -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Clone)] pub enum DNSClass { IN, CH, @@ -340,7 +343,7 @@ where -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Clone)] pub struct Record { #[serde(rename = "Name")] pub name: SerdeName, @@ -374,7 +377,7 @@ impl TryFrom for trust_dns_types::Record { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct SerdeName(Name); impl<'de> Deserialize<'de> for SerdeName { diff --git a/src/models/users.rs b/src/models/users.rs index eb713b4..d19142c 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -24,7 +24,7 @@ use crate::models::errors::{ErrorResponse, make_500}; use crate::models::dns::AbsoluteName; const BEARER: &str = "Bearer "; -const AUTH_HEADER: &str = "Authentication"; +const AUTH_HEADER: &str = "Authorization"; #[derive(Debug, DbEnum, Deserialize, Clone)] diff --git a/src/routes/zones.rs b/src/routes/zones.rs index 4da2978..ae7ddeb 100644 --- a/src/routes/zones.rs +++ b/src/routes/zones.rs @@ -1,13 +1,10 @@ -use std::convert::TryFrom; use std::convert::TryInto; - +use serde_json::json; use rocket::Response; use rocket::http::Status; use rocket_contrib::json::Json; -use serde_json::json; - use trust_dns_client::{client::ClientHandle, op::UpdateMessage}; use trust_dns_client::op::ResponseCode; use trust_dns_client::rr::{DNSClass, RecordType}; @@ -50,8 +47,10 @@ pub async fn get_zone_records( println!("Querrying AXFR of zone {} failed with code {}", *zone, response.response_code()); return ErrorResponse::new( Status::NotFound, - format!("Zone {} could not be found", *zone) - ).err() + "Zone could not be found".into() + ).with_details(json!({ + "zone_name": zone.to_utf8() + })).err(); } let answers = response.answers(); @@ -85,21 +84,46 @@ pub async fn create_zone_records( user_info.get_zone(c, &zone_name) } }).await?; + // TODO: What about relative names (also in cnames and stuff) - // TODO: error handling - let records: Vec = new_records.into_inner().into_iter().map(|r| r.try_into().unwrap()).collect(); + let mut bad_records = Vec::new(); + let mut records: Vec = Vec::new(); + + for record in new_records.into_inner().into_iter() { + let this_record = record.clone(); + if let Ok(record) = record.try_into() { + records.push(record); + } else { + bad_records.push(this_record.clone()); + } + } let bad_zone_records: Vec<_> = records.iter().filter(|record| !zone.zone_of(record.name())).collect(); // TODO: Get zone class from somewhere instead of always assuming IN let bad_class_records: Vec<_> = records.iter().filter(|record| record.dns_class() != DNSClass::IN).collect(); + if !bad_records.is_empty() { + return ErrorResponse::new( + Status::BadRequest, + "Record list contains records that could not been parsed into DNS records".into() + ).with_details( + json!({ + "zone_name": zone.to_utf8(), + "records": bad_records + }) + ).err(); + } + if !bad_zone_records.is_empty() { return ErrorResponse::new( Status::BadRequest, - format!("Record list contains records whose name that do not belong to the zone {}", *zone) + "Record list contains records whose name does not belong to the zone".into() ).with_details( - json!(bad_zone_records.into_iter().map(|r| r.name().to_utf8()).collect::>()) - ).err() + json!({ + "zone_name": zone.to_utf8(), + "records": bad_zone_records.into_iter().map(|r| r.clone().into()).collect::>() + }) + ).err(); } if !bad_class_records.is_empty() { @@ -107,8 +131,11 @@ pub async fn create_zone_records( Status::BadRequest, "Record list contains records whose class differs from the zone class `IN`".into() ).with_details( - json!(bad_class_records.into_iter().map(|r| r.name().to_utf8()).collect::>()) - ).err() + json!({ + "zone_name": zone.to_utf8(), + "records": bad_class_records.into_iter().map(|r| r.clone().into()).collect::>() + }) + ).err(); } let mut zone_query = Query::new(); @@ -132,12 +159,22 @@ pub async fn create_zone_records( edns.set_version(0); } - // TODO: check if NOERROR or something - let _response = { + let response = { let query = dns::ClientResponse(client.send(message)); query.await.map_err(make_500)? }; + // TODO: better error handling + if response.response_code() != ResponseCode::NoError { + println!("Update of zone {} failed with code {}", *zone, response.response_code()); + return ErrorResponse::new( + Status::NotFound, + "Update of zone failed".into() + ).with_details(json!({ + "zone_name": zone.to_utf8() + })).err(); + } + Ok(Json(())) }