diff options
author | HimbeerserverDE <himbeerserverde@gmail.com> | 2022-10-22 15:22:34 +0200 |
---|---|---|
committer | HimbeerserverDE <himbeerserverde@gmail.com> | 2022-10-22 15:22:34 +0200 |
commit | 1cbadb245701a76d2be9d071bc7234146340f95c (patch) | |
tree | 8dd3e79689f1a6069718a58365152a2e2ab10925 | |
parent | 8341aba97f363518b0c299b4b507b61d0cfa19f3 (diff) |
add nameserver.info response
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/call/nameserver.rs | 33 | ||||
-rw-r--r-- | src/error/mod.rs | 10 | ||||
-rw-r--r-- | src/response/mod.rs | 2 | ||||
-rw-r--r-- | src/response/nameserver.rs | 193 |
5 files changed, 236 insertions, 3 deletions
@@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +iso8601 = "0.4.0" reqwest = { version = "0.11", features = ["blocking", "cookies"] } url = "2.2.2" xmlrpc = "0.15.1" diff --git a/src/call/nameserver.rs b/src/call/nameserver.rs index 6de2141..2509eba 100644 --- a/src/call/nameserver.rs +++ b/src/call/nameserver.rs @@ -1,3 +1,4 @@ +use crate::{Error, Result}; use super::Call; use std::collections::BTreeMap; @@ -69,6 +70,38 @@ impl From<RecordType> for xmlrpc::Value { } } +impl TryFrom<String> for RecordType { + type Error = Error; + fn try_from(s: String) -> Result<Self> { + match s.as_str() { + "A" => Ok(Self::A), + "AAAA" => Ok(Self::Aaaa), + "AFSDB" => Ok(Self::Afsdb), + "ALIAS" => Ok(Self::Alias), + "CAA" => Ok(Self::Caa), + "CERT" => Ok(Self::Cert), + "CNAME" => Ok(Self::Cname), + "HINFO" => Ok(Self::Hinfo), + "KEY" => Ok(Self::Key), + "LOC" => Ok(Self::Loc), + "MX" => Ok(Self::Mx), + "NAPTR" => Ok(Self::NaPtr), + "NS" => Ok(Self::Ns), + "OPENPGPKEY" => Ok(Self::OpenPgpKey), + "PTR" => Ok(Self::Ptr), + "RP" => Ok(Self::Rp), + "SMIMEA" => Ok(Self::SmimeA), + "SOA" => Ok(Self::Soa), + "SSHFP" => Ok(Self::Sshfp), + "TLSA" => Ok(Self::Tlsa), + "TXT" => Ok(Self::Txt), + "URI" => Ok(Self::Uri), + "URL" => Ok(Self::Url), + _ => Err(Error::BadVariant("RecordType", s)), + } + } +} + /// Search parameters to find nameserver records /// the account has access to. #[derive(Clone, Copy, Debug)] diff --git a/src/error/mod.rs b/src/error/mod.rs index 9c8bdfc..29d32f9 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -10,6 +10,7 @@ pub enum Error { Type(&'static str, &'static str, xmlrpc::Value), BadResponse(xmlrpc::Value), BadStatus(&'static [i32], i32), + BadVariant(&'static str, String), } impl std::error::Error for Error {} @@ -22,17 +23,20 @@ impl fmt::Display for Error { Error::XmlRpc(err) => write!(fmt, "xmlrpc error: {}", err), Error::Inexistent(what) => { write!(fmt, "parameter {} does not exist", what) - } + }, Error::Type(what, exp, got) => { write!( fmt, "parameter {what} is of wrong type {got:?} (expected: {exp})" ) - } + }, Error::BadResponse(resp) => write!(fmt, "bad response: {:?}", resp), Error::BadStatus(expected, got) => { write!(fmt, "bad status {} (expected: {:?}", got, expected) - } + }, + Error::BadVariant(ename, var) => { + write!(fmt, "{} is not a valid enum variant for {}", var, ename) + }, } } } diff --git a/src/response/mod.rs b/src/response/mod.rs index 5c1584f..8d95dfb 100644 --- a/src/response/mod.rs +++ b/src/response/mod.rs @@ -7,3 +7,5 @@ pub struct Response { pub status: i32, pub data: BTreeMap<String, xmlrpc::Value>, } + +pub mod nameserver; diff --git a/src/response/nameserver.rs b/src/response/nameserver.rs new file mode 100644 index 0000000..e5211e1 --- /dev/null +++ b/src/response/nameserver.rs @@ -0,0 +1,193 @@ +use crate::{Error, Result}; +use crate::call::nameserver::RecordType; + +use std::collections::BTreeMap; +use std::fmt; + +use iso8601::DateTime; + +fn get_str( + map: &BTreeMap<String, xmlrpc::Value>, + key: &'static str, +) -> Result<String> { + let value = map.get(key) + .ok_or(Error::Inexistent(key))? + .as_str() + .ok_or_else(|| Error::Type(key, "String", map.get(key) + .unwrap() + .clone() + ))?; + + Ok(value.to_owned()) +} + +fn get_i32( + map: &BTreeMap<String, xmlrpc::Value>, + key: &'static str, +) -> Result<i32> { + let value = map.get(key) + .ok_or(Error::Inexistent(key))? + .as_i32() + .ok_or_else(|| Error::Type(key, "Int", map.get(key) + .unwrap() + .clone() + ))?; + + Ok(value) +} + +fn get_bool( + map: &BTreeMap<String, xmlrpc::Value>, + key: &'static str, +) -> Result<bool> { + let value = map.get(key) + .ok_or(Error::Inexistent(key))? + .as_bool() + .ok_or_else(|| Error::Type(key, "Bool", map.get(key) + .unwrap() + .clone() + ))?; + + Ok(value) +} + +/// The domain type. Can be master or slave. +#[derive(Clone, Debug)] +pub enum DomainType { + Master, + Slave, +} + +impl fmt::Display for DomainType { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(match self { + Self::Master => "MASTER", + Self::Slave => "SLAVE", + }) + } +} + +impl TryFrom<xmlrpc::Value> for DomainType { + type Error = Error; + fn try_from(v: xmlrpc::Value) -> Result<Self> { + if let xmlrpc::Value::String(s) = v { + match s.as_str() { + "MASTER" => Ok(Self::Master), + "SLAVE" => Ok(Self::Slave), + _ => Err(Error::BadVariant("DomainType", s)), + } + } else { + Err(Error::Type("type", "String", v)) + } + } +} + +/// Information on a slave nameserver. +#[derive(Clone, Debug)] +pub struct SlaveDns { + pub hostname: String, + pub address: String, +} + +impl TryFrom<xmlrpc::Value> for SlaveDns { + type Error = Error; + fn try_from(v: xmlrpc::Value) -> Result<Self> { + if let xmlrpc::Value::Struct(map) = v { + let slave = Self { + hostname: get_str(&map, "name")?, + address: get_str(&map, "ip")?, + }; + + Ok(slave) + } else { + Err(Error::Type("slaveDns", "Struct", v)) + } + } +} + +/// Type of URL redirect. Can be a HTTP 301, 302 or frame. +#[derive(Clone, Debug)] +pub enum UrlRdrType { + Permanent, + Temporary, + Frame, +} + +impl fmt::Display for UrlRdrType { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let url_rdr_type = match self { + Self::Permanent => "HEADER301", + Self::Temporary => "HEADER302", + Self::Frame => "FRAME", + }; + + fmt.write_str(url_rdr_type) + } +} + +impl TryFrom<String> for UrlRdrType { + type Error = Error; + fn try_from(s: String) -> Result<Self> { + match s.as_str() { + "HEADER301" => Ok(Self::Permanent), + "HEADER302" => Ok(Self::Temporary), + "FRAME" => Ok(Self::Frame), + _ => Err(Error::BadVariant("UrlRdrType", s)), + } + } +} + +/// A nameserver record. Contains DNS information as well as INWX metadata. +#[derive(Clone, Debug)] +pub struct Record { + pub id: i32, + pub name: String, + pub record_type: RecordType, + pub content: String, + pub ttl: i32, + pub priority: i32, + pub url_rdr_type: UrlRdrType, + pub url_rdr_title: String, + pub url_rdr_desc: String, + pub url_rdr_keywords: String, + pub url_rdr_favicon: String, + pub url_append: bool, +} + +impl TryFrom<xmlrpc::Value> for Record { + type Error = Error; + fn try_from(v: xmlrpc::Value) -> Result<Self> { + if let xmlrpc::Value::Struct(map) = v { + let record = Self { + id: get_i32(&map, "id")?, + name: get_str(&map, "name")?, + record_type: get_str(&map, "type")?.try_into()?, + content: get_str(&map, "content")?, + ttl: get_i32(&map, "ttl")?, + priority: get_i32(&map, "prio")?, + url_rdr_type: get_str(&map, "urlRedirectType")?.try_into()?, + url_rdr_title: get_str(&map, "urlRedirectTitle")?, + url_rdr_desc: get_str(&map, "urlRedirectDescription")?, + url_rdr_keywords: get_str(&map, "urlRedirectKeywords")?, + url_rdr_favicon: get_str(&map, "urlRedirectFavIcon")?, + url_append: get_bool(&map, "urlAppend")?, + }; + + Ok(record) + } else { + Err(Error::Type("record", "Struct", v)) + } + } +} + +/// The records that match a search. +pub struct RecordInfo { + pub domain_id: i32, + pub domain_name: String, + pub domain_type: DomainType, + pub master_address: String, + pub last_zone_check: DateTime, + pub slave_dns: SlaveDns, + pub soa_serial: String, + pub records: Vec<Record>, +} |