diff --git a/src/main.rs b/src/main.rs index ea20c80..8fa868b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,37 +1,47 @@ #![feature(proc_macro_hygiene, decl_macro)] -use std::str::FromStr; #[macro_use] extern crate rocket; use rocket::State; +use rocket::http::Status; use rocket_contrib::json::Json; use trust_dns_client::client::{Client, SyncClient}; use trust_dns_client::tcp::TcpClientConnection; -use trust_dns_client::op::DnsResponse; +use trust_dns_client::op::{DnsResponse, ResponseCode}; use trust_dns_client::rr::{DNSClass, Name, Record, RecordType}; -mod types; +mod models; mod config; +use models::errors::ErrorResponse; + #[get("/zones//records")] -fn zone_records(client: State>, zone: String) -> Json> { +fn zone_records(client: State>, zone: String) -> Result>, ErrorResponse<()>> { // TODO: Implement FromParam for Name - let name = Name::from_str(&zone).unwrap(); + let name = Name::from_utf8(&zone).unwrap(); let response: DnsResponse = client.query(&name, DNSClass::IN, RecordType::AXFR).unwrap(); + + if response.response_code() != ResponseCode::NoError { + return ErrorResponse::new( + Status::NotFound, + format!("zone {} could not be found", name.to_utf8()) + ).err() + } + let answers: &[Record] = response.answers(); let mut records: Vec<_> = answers.to_vec().into_iter() - .map(|record| types::dns::Record::from(record)) + .map(|record| models::dns::Record::from(record)) .filter(|record| match record.rdata { - types::dns::RData::NULL { .. } | types::dns::RData::DNSSEC(_) => false, + models::dns::RData::NULL { .. } | models::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) + Ok(Json(records)) } fn main() { diff --git a/src/types/dns.rs b/src/models/dns.rs similarity index 100% rename from src/types/dns.rs rename to src/models/dns.rs diff --git a/src/models/errors.rs b/src/models/errors.rs new file mode 100644 index 0000000..d08b697 --- /dev/null +++ b/src/models/errors.rs @@ -0,0 +1,55 @@ +use serde::Serialize; +use rocket::http::Status; +use rocket::request::Request; +use rocket::response::{self, Response, Responder}; +use rocket_contrib::json::Json; + + +#[derive(Serialize, Debug)] +pub struct ErrorResponse { + #[serde(with = "StatusDef")] + #[serde(flatten)] + pub status: Status, + pub message: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub details: Option +} + + +#[derive(Serialize)] +#[serde(remote = "Status")] +struct StatusDef { + code: u16, + #[serde(rename = "status")] + reason: &'static str, +} + + +impl ErrorResponse { + pub fn new(status: Status, message: String) -> ErrorResponse { + ErrorResponse { + status, + message, + details: None, + } + } + + pub fn with_details(self, details: T) -> ErrorResponse { + ErrorResponse { + details: Some(details), + ..self + } + } + + pub fn err(self) -> Result> { + Err(self) + } +} + + +impl<'r, T: Serialize> Responder<'r> for ErrorResponse { + fn respond_to(self, req: &Request) -> response::Result<'r> { + let status = self.status; + Response::build_from(Json(self).respond_to(req)?).status(status).ok() + } +} diff --git a/src/types/mod.rs b/src/models/mod.rs similarity index 94% rename from src/types/mod.rs rename to src/models/mod.rs index 0dd7f57..4677179 100644 --- a/src/types/mod.rs +++ b/src/models/mod.rs @@ -1,4 +1,5 @@ pub mod dns; +pub mod errors; pub mod trust_dns_types { pub use trust_dns_client::rr::rdata::{