151 lines
4.5 KiB
Rust
151 lines
4.5 KiB
Rust
#![allow(async_fn_in_trait)]
|
|
|
|
use crate::cloudflare::CloudflareDnsProvider;
|
|
use derive_more::Display;
|
|
use std::fmt::Debug;
|
|
use std::str::FromStr;
|
|
use std::sync::Arc;
|
|
use tokio::sync::RwLock;
|
|
|
|
pub mod cloudflare;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct MicroTld {
|
|
base_url: String,
|
|
provider: DnsProviderType,
|
|
}
|
|
|
|
impl MicroTld {
|
|
pub fn new(base_url: String, provider: DnsProviderType) -> anyhow::Result<Self> {
|
|
Ok(Self {
|
|
base_url,
|
|
provider,
|
|
})
|
|
}
|
|
|
|
pub fn get_base_url(&self) -> &str {
|
|
&self.base_url
|
|
}
|
|
|
|
pub fn get_provider_wrapped(&mut self) -> &mut DnsProviderType {
|
|
&mut self.provider
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Display, Copy, Clone, PartialEq, Eq, Hash)]
|
|
pub enum RecordType {
|
|
A,
|
|
AAAA,
|
|
CNAME,
|
|
MX,
|
|
TXT,
|
|
SRV,
|
|
NS,
|
|
CAA,
|
|
PTR,
|
|
DNSKEY,
|
|
DS,
|
|
HTTPS,
|
|
LOC,
|
|
NAPTR,
|
|
SMIMEA,
|
|
SSHFP,
|
|
SVCB,
|
|
TLSA,
|
|
URI,
|
|
}
|
|
|
|
impl FromStr for RecordType {
|
|
type Err = anyhow::Error;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(match s {
|
|
"A" => RecordType::A,
|
|
"AAAA" => RecordType::AAAA,
|
|
"CNAME" => RecordType::CNAME,
|
|
"MX" => RecordType::MX,
|
|
"TXT" => RecordType::TXT,
|
|
"SRV" => RecordType::SRV,
|
|
"NS" => RecordType::NS,
|
|
"CAA" => RecordType::CAA,
|
|
"PTR" => RecordType::PTR,
|
|
"DNSKEY" => RecordType::DNSKEY,
|
|
"DS" => RecordType::DS,
|
|
"HTTPS" => RecordType::HTTPS,
|
|
"LOC" => RecordType::LOC,
|
|
"NAPTR" => RecordType::NAPTR,
|
|
"SMIMEA" => RecordType::SMIMEA,
|
|
"SSHFP" => RecordType::SSHFP,
|
|
"SVCB" => RecordType::SVCB,
|
|
"TLSA" => RecordType::TLSA,
|
|
"URI" => RecordType::URI,
|
|
_ => return Err(anyhow::anyhow!("Unsupported record type: {}", s)),
|
|
})
|
|
}
|
|
}
|
|
|
|
pub trait DnsRecord<'a> where Self: Sized {
|
|
type Client;
|
|
type Provider: DnsProvider<'a>;
|
|
|
|
fn get_provider(&self) -> &Self::Provider;
|
|
|
|
async fn name(&mut self) -> anyhow::Result<String>;
|
|
async fn target(&mut self) -> anyhow::Result<String>;
|
|
async fn ttl(&mut self) -> anyhow::Result<u32>;
|
|
async fn record_type(&mut self) -> anyhow::Result<RecordType>;
|
|
async fn is_valid(&self) -> anyhow::Result<bool>;
|
|
async fn priority(&mut self) -> anyhow::Result<Option<u16>>;
|
|
|
|
async fn set_target(&mut self, ip: &str) -> anyhow::Result<()>;
|
|
async fn set_ttl(&mut self, ttl: u32) -> anyhow::Result<()>;
|
|
async fn set_record_type(&mut self, record_type: RecordType) -> anyhow::Result<()>;
|
|
async fn set_priority(&mut self, priority: u16) -> anyhow::Result<()>;
|
|
async fn delete(&mut self) -> anyhow::Result<()>;
|
|
async fn refresh(&mut self) -> anyhow::Result<()>;
|
|
}
|
|
|
|
pub trait DnsProvider<'a> where Self: Sized {
|
|
type Client;
|
|
type Record: DnsRecord<'a, Client = Self::Client>;
|
|
fn init_client(&mut self) -> anyhow::Result<()>;
|
|
fn get_client(&self) -> Result<&Self::Client, ClientNotInitializedError>;
|
|
async fn get_dns_records_by_name(&'a self, name: impl Into<String>) -> anyhow::Result<impl IntoIterator<Item = anyhow::Result<Self::Record>>>;
|
|
async fn get_dns_records_by_ip(&'a self, ip: impl Into<String>) -> anyhow::Result<impl IntoIterator<Item = anyhow::Result<Self::Record>>>;
|
|
async fn get_dns_records(&'a self) -> anyhow::Result<impl IntoIterator<Item = anyhow::Result<Self::Record>>>;
|
|
async fn get_dns_record_by_id(&'a self, id: &str) -> anyhow::Result<Self::Record>;
|
|
async fn create_dns_record(&'a self, subdomain: &str, target: &str, record_type: RecordType, ttl: Option<u32>) -> anyhow::Result<Self::Record>;
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum DnsProviderType {
|
|
Cloudflare(Arc<RwLock<CloudflareDnsProvider>>),
|
|
}
|
|
|
|
impl DnsProviderType {
|
|
pub fn cloudflare(api_key: String, zone_id: String) -> anyhow::Result<Self> {
|
|
Ok(Self::Cloudflare(Arc::new(RwLock::new(CloudflareDnsProvider::new(api_key, zone_id)?))))
|
|
}
|
|
|
|
pub fn as_provider(&mut self) -> &mut Arc<RwLock<impl DnsProvider<'_>>> {
|
|
match self {
|
|
Self::Cloudflare(provider) => provider,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Display)]
|
|
#[display("Client not initialized: {}", message)]
|
|
pub struct ClientNotInitializedError {
|
|
message: String,
|
|
}
|
|
|
|
impl std::error::Error for ClientNotInitializedError {}
|
|
|
|
#[derive(Debug, Display)]
|
|
#[display("DNS record does not exist: {}", record_name)]
|
|
pub struct RecordDoesNotExistError {
|
|
record_name: String,
|
|
}
|
|
|
|
impl std::error::Error for RecordDoesNotExistError {} |