use std::str::{Chars, FromStr};
use {idna::domain_to_ascii, thiserror::Error};
use crate::check::StringCheck;
#[derive(Debug, Error)]
pub enum DnsTypeError {
#[error("Invalid DNS Algorithm")]
InvalidAlgorithm,
#[error("Invalid DNS Digest")]
InvalidDigest,
}
pub struct DnsAlgorithm {
pub number: u8,
pub mnemonic: &'static str,
pub zone_signing: bool,
pub transaction_signing: bool,
}
pub enum DnsAlgorithmType {
DeleteDs(DnsAlgorithm),
RsaMd5(DnsAlgorithm),
DiffieHellman(DnsAlgorithm),
Dsa(DnsAlgorithm),
RsaSha1(DnsAlgorithm),
DsaNsec3Sha1(DnsAlgorithm),
RsaSha1Nsec3Sha1(DnsAlgorithm),
RsaSha256(DnsAlgorithm),
RsaSha512(DnsAlgorithm),
EccGost(DnsAlgorithm),
EcdsaP256Sha256(DnsAlgorithm),
EcdsaP384Sha384(DnsAlgorithm),
Ed25519(DnsAlgorithm),
Ed448(DnsAlgorithm),
PrivateDns(DnsAlgorithm),
PrivateOid(DnsAlgorithm),
}
impl DnsAlgorithmType {
pub fn from_number(number: u8) -> Result<Self, DnsTypeError> {
Ok(match number {
0 => Self::DeleteDs(DnsAlgorithm {
number: 0,
mnemonic: "DELETE",
zone_signing: false,
transaction_signing: false,
}),
1 => Self::RsaMd5(DnsAlgorithm {
number: 1,
mnemonic: "RSAMD5",
zone_signing: false,
transaction_signing: true,
}),
2 => Self::DiffieHellman(DnsAlgorithm {
number: 2,
mnemonic: "DH",
zone_signing: false,
transaction_signing: true,
}),
3 => Self::Dsa(DnsAlgorithm {
number: 3,
mnemonic: "DSA",
zone_signing: true,
transaction_signing: true,
}),
5 => Self::RsaSha1(DnsAlgorithm {
number: 5,
mnemonic: "RSASHA1",
zone_signing: true,
transaction_signing: true,
}),
6 => Self::DsaNsec3Sha1(DnsAlgorithm {
number: 6,
mnemonic: "DSA-NSEC3-SHA1",
zone_signing: true,
transaction_signing: true,
}),
7 => Self::RsaSha1Nsec3Sha1(DnsAlgorithm {
number: 7,
mnemonic: "RSA-NSEC3-SHA1",
zone_signing: true,
transaction_signing: true,
}),
8 => Self::RsaSha256(DnsAlgorithm {
number: 8,
mnemonic: "RSASHA256",
zone_signing: true,
transaction_signing: false,
}),
10 => Self::RsaSha512(DnsAlgorithm {
number: 10,
mnemonic: "RSASHA512",
zone_signing: true,
transaction_signing: false,
}),
12 => Self::EccGost(DnsAlgorithm {
number: 12,
mnemonic: "ECC-GOST",
zone_signing: true,
transaction_signing: false,
}),
13 => Self::EcdsaP256Sha256(DnsAlgorithm {
number: 13,
mnemonic: "ECDSAP256SHA256",
zone_signing: true,
transaction_signing: false,
}),
14 => Self::EcdsaP384Sha384(DnsAlgorithm {
number: 14,
mnemonic: "ECDSAP384SHA384",
zone_signing: true,
transaction_signing: false,
}),
15 => Self::Ed25519(DnsAlgorithm {
number: 15,
mnemonic: "ED25519",
zone_signing: true,
transaction_signing: false,
}),
16 => Self::Ed448(DnsAlgorithm {
number: 16,
mnemonic: "ED448",
zone_signing: true,
transaction_signing: false,
}),
253 => Self::PrivateDns(DnsAlgorithm {
number: 253,
mnemonic: "PRIVATEDNS",
zone_signing: true,
transaction_signing: true,
}),
254 => Self::PrivateOid(DnsAlgorithm {
number: 254,
mnemonic: "PRIVATEOID",
zone_signing: true,
transaction_signing: true,
}),
_ => return Err(DnsTypeError::InvalidAlgorithm),
})
}
fn algo(self) -> DnsAlgorithm {
match self {
Self::DeleteDs(a)
| Self::RsaMd5(a)
| Self::DiffieHellman(a)
| Self::Dsa(a)
| Self::RsaSha1(a)
| Self::DsaNsec3Sha1(a)
| Self::RsaSha1Nsec3Sha1(a)
| Self::RsaSha256(a)
| Self::RsaSha512(a)
| Self::EccGost(a)
| Self::EcdsaP256Sha256(a)
| Self::EcdsaP384Sha384(a)
| Self::Ed25519(a)
| Self::Ed448(a)
| Self::PrivateDns(a)
| Self::PrivateOid(a) => a,
}
}
pub fn mnemonic(number: u8) -> Result<&'static str, DnsTypeError> {
let alg = Self::from_number(number)?;
Ok(alg.algo().mnemonic)
}
pub fn zone_signing(number: u8) -> Result<bool, DnsTypeError> {
let alg = Self::from_number(number)?;
Ok(alg.algo().zone_signing)
}
}
pub struct DnsDigest {
pub number: u8,
pub mnemonic: &'static str,
pub mandatory: bool,
}
pub enum DnsDigestType {
Sha1(DnsDigest),
Sha256(DnsDigest),
Gost(DnsDigest),
Sha384(DnsDigest),
}
impl DnsDigestType {
pub fn from_number(number: u8) -> Result<Self, DnsTypeError> {
Ok(match number {
1 => Self::Sha1(DnsDigest {
number: 1,
mnemonic: "SHA1",
mandatory: true,
}),
2 => Self::Sha256(DnsDigest {
number: 2,
mnemonic: "SHA256",
mandatory: true,
}),
3 => Self::Gost(DnsDigest {
number: 3,
mnemonic: "GOST",
mandatory: false,
}),
4 => Self::Sha384(DnsDigest {
number: 4,
mnemonic: "SHA384",
mandatory: false,
}),
_ => return Err(DnsTypeError::InvalidDigest),
})
}
pub fn mnemonic(number: u8) -> Result<&'static str, DnsTypeError> {
let digest = Self::from_number(number)?;
Ok(match digest {
Self::Sha1(d) | Self::Sha256(d) | Self::Gost(d) | Self::Sha384(d) => d.mnemonic,
})
}
}
#[derive(Debug, Error)]
pub enum DomainNameError {
#[error("Invalid Domain Name")]
InvalidDomainName,
#[error(transparent)]
IdnaError(#[from] idna::Errors),
}
#[derive(Debug, Clone)]
pub struct DomainName {
domain_name: String,
ascii: String,
}
impl DomainName {
pub fn chars(&self) -> Chars<'_> {
self.domain_name.chars()
}
pub fn is_tld(&self) -> bool {
self.domain_name.is_tld()
}
pub fn to_ascii(&self) -> &str {
&self.ascii
}
pub fn is_idn(&self) -> bool {
!self.ascii.eq(&self.domain_name)
}
pub fn is_root(&self) -> bool {
self.domain_name.eq(".")
}
pub fn with_leading_dot(&self) -> String {
if !self.is_root() {
format!(".{}", self.domain_name)
} else {
self.domain_name.to_string()
}
}
pub fn trim_leading_dot(&self) -> &str {
if !self.is_root() {
self.domain_name.trim_start_matches('.')
} else {
&self.domain_name
}
}
}
impl FromStr for DomainName {
type Err = DomainNameError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if !s.is_unicode_domain_name() {
return Err(DomainNameError::InvalidDomainName);
}
let ascii = domain_to_ascii(s)?;
Ok(Self {
domain_name: s.to_string(),
ascii,
})
}
}