ferron_common/
dns.rs

1use std::error::Error;
2
3use async_trait::async_trait;
4use hickory_resolver::{config::ResolverConfig, name_server::TokioConnectionProvider};
5
6/// Trait for DNS providers used for DNS-01 ACME challenge.
7#[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
21/// Separates subdomain from domain name.
22pub 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      // SOA record found
39      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}