aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHimbeerserverDE <himbeerserverde@gmail.com>2022-10-22 15:22:34 +0200
committerHimbeerserverDE <himbeerserverde@gmail.com>2022-10-22 15:22:34 +0200
commit1cbadb245701a76d2be9d071bc7234146340f95c (patch)
tree8dd3e79689f1a6069718a58365152a2e2ab10925
parent8341aba97f363518b0c299b4b507b61d0cfa19f3 (diff)
add nameserver.info response
-rw-r--r--Cargo.toml1
-rw-r--r--src/call/nameserver.rs33
-rw-r--r--src/error/mod.rs10
-rw-r--r--src/response/mod.rs2
-rw-r--r--src/response/nameserver.rs193
5 files changed, 236 insertions, 3 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 93f7413..d456e79 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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>,
+}