1use std::error::Error;
2
3use async_trait::async_trait;
4use hickory_resolver::{config::ResolverConfig, name_server::TokioConnectionProvider};
5
6#[async_trait]
8pub trait DnsProvider {
9 async fn set_acme_txt_record(
10 &self,
11 acme_challenge_identifier: &str,
12 dns_value: &str,
13 ) -> Result<(), Box<dyn Error + Send + Sync>>;
14
15 #[allow(unused_variables)]
16 async fn remove_acme_txt_record(&self, acme_challenge_identifier: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
17 Ok(())
18 }
19}
20
21pub async fn separate_subdomain_from_domain_name(domain_name: &str) -> (String, String) {
23 let parts: Vec<&str> = domain_name
24 .strip_suffix(".")
25 .unwrap_or(domain_name)
26 .split('.')
27 .collect();
28 let resolver = hickory_resolver::Resolver::builder_tokio()
29 .unwrap_or(hickory_resolver::Resolver::builder_with_config(
30 ResolverConfig::default(),
31 TokioConnectionProvider::default(),
32 ))
33 .build();
34
35 for parts_index in 0..parts.len() {
36 if resolver
37 .soa_lookup(format!("{}.", parts[parts_index..].join(".")))
38 .await
39 .is_ok()
40 {
41 let subdomain = parts[..parts_index].join(".");
43 let domain = parts[parts_index..].join(".");
44 return (subdomain, domain);
45 }
46 }
47
48 ("".to_string(), parts.join("."))
49}