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 =
29 hickory_resolver::Resolver::builder_with_config(ResolverConfig::default(), TokioConnectionProvider::default())
30 .build();
31
32 for parts_index in 0..parts.len() {
33 if resolver
34 .soa_lookup(format!("{}.", parts[parts_index..].join(".")))
35 .await
36 .is_ok()
37 {
38 let subdomain = parts[..parts_index].join(".");
40 let domain = parts[parts_index..].join(".");
41 return (subdomain, domain);
42 }
43 }
44
45 ("".to_string(), parts.join("."))
46}