initial implementation of domain::Checker

This commit is contained in:
Brian Picciano 2023-05-07 17:06:51 +02:00
parent 71dcc94e29
commit 2d57353244
6 changed files with 159 additions and 10 deletions

46
Cargo.lock generated
View File

@ -326,6 +326,12 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "endian-type"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
[[package]]
name = "enum-as-inner"
version = "0.5.1"
@ -470,6 +476,7 @@ dependencies = [
"serde_json",
"sha2",
"tempdir",
"trust-dns-client",
]
[[package]]
@ -1514,6 +1521,15 @@ dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "nibble_vec"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
dependencies = [
"smallvec",
]
[[package]]
name = "nom"
version = "7.1.3"
@ -1639,6 +1655,16 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "radix_trie"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
dependencies = [
"endian-type",
"nibble_vec",
]
[[package]]
name = "rand"
version = "0.4.6"
@ -2236,6 +2262,26 @@ dependencies = [
"once_cell",
]
[[package]]
name = "trust-dns-client"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c408c32e6a9dbb38037cece35740f2cf23c875d8ca134d33631cec83f74d3fe"
dependencies = [
"cfg-if",
"data-encoding",
"futures-channel",
"futures-util",
"lazy_static",
"radix_trie",
"rand 0.8.5",
"thiserror",
"time",
"tokio",
"tracing",
"trust-dns-proto",
]
[[package]]
name = "trust-dns-proto"
version = "0.22.0"

View File

@ -18,3 +18,4 @@ gix = { version = "0.44.1", features = [
tempdir = "0.3.7"
serde = { version = "1.0.162", features = [ "derive" ]}
serde_json = "1.0.96"
trust-dns-client = "0.22.0"

1
src/domain.rs Normal file
View File

@ -0,0 +1 @@
pub mod checker;

105
src/domain/checker.rs Normal file
View File

@ -0,0 +1,105 @@
use std::error::Error;
use std::str::FromStr;
use trust_dns_client::client::{Client, SyncClient};
use trust_dns_client::rr::{DNSClass, Name, RData, RecordType};
use trust_dns_client::udp::UdpClientConnection;
#[derive(Debug)]
pub enum NewDNSCheckerError {
InvalidResolverAddress,
InvalidTargetCNAME,
Unexpected(Box<dyn Error>),
}
impl<E: Error + 'static> From<E> for NewDNSCheckerError {
fn from(e: E) -> NewDNSCheckerError {
NewDNSCheckerError::Unexpected(Box::from(e))
}
}
#[derive(Debug)]
pub enum CheckDomainError {
InvalidDomainName,
TargetCNAMENotSet,
ChallengeTokenNotSet,
Unexpected(Box<dyn Error>),
}
impl<E: Error + 'static> From<E> for CheckDomainError {
fn from(e: E) -> CheckDomainError {
CheckDomainError::Unexpected(Box::from(e))
}
}
pub trait Checker {
fn check_domain(&self, domain: &str, challenge_token: &str) -> Result<(), CheckDomainError>;
}
pub struct DNSChecker {
target_cname: Name,
client: SyncClient<UdpClientConnection>,
}
impl DNSChecker {
pub fn new(target_cname: &str, resolver_addr: &str) -> Result<DNSChecker, NewDNSCheckerError> {
let target_cname =
Name::from_str(target_cname).map_err(|_| NewDNSCheckerError::InvalidTargetCNAME)?;
let resolver_addr = resolver_addr
.parse()
.map_err(|_| NewDNSCheckerError::InvalidResolverAddress)?;
let conn = UdpClientConnection::new(resolver_addr)?;
let client = SyncClient::new(conn);
Ok(DNSChecker {
target_cname,
client,
})
}
}
impl Checker for DNSChecker {
fn check_domain(&self, domain: &str, challenge_token: &str) -> Result<(), CheckDomainError> {
let mut fqdn = Name::from_str(domain).map_err(|_| CheckDomainError::InvalidDomainName)?;
fqdn.set_fqdn(true);
// check that the CNAME is installed correctly on the domain
{
let response = self.client.query(&fqdn, DNSClass::IN, RecordType::CNAME)?;
let records = response.answers();
if records.len() != 1 {
return Err(CheckDomainError::TargetCNAMENotSet);
}
// if the single record isn't a CNAME, or it's not the target CNAME, then return
// TargetCNAMENotSet
match records[0].data() {
Some(RData::CNAME(remote_cname)) if remote_cname == &self.target_cname => (),
_ => return Err(CheckDomainError::TargetCNAMENotSet),
}
}
// check that the TXT record with the challenge token is correctly installed on the domain
{
let fqdn = Name::from_str("_gateway")?.append_domain(&fqdn)?;
let response = self.client.query(&fqdn, DNSClass::IN, RecordType::TXT)?;
let records = response.answers();
if !records.iter().any(|record| -> bool {
match record.data() {
Some(RData::TXT(txt)) => txt.to_string().contains(challenge_token),
_ => false,
}
}) {
return Err(CheckDomainError::ChallengeTokenNotSet);
}
}
Ok(())
}
}

View File

@ -1 +1,2 @@
pub mod origin;
pub mod domain;

View File

@ -1,8 +1,6 @@
use std::error::Error;
use serde_json;
use super::Descr;
use serde_json;
use std::error::Error;
#[derive(Clone, Copy)]
pub struct Limits {}
@ -61,8 +59,6 @@ pub mod git {
use std::path::{Path, PathBuf};
use std::{fs, io};
use gix::progress::Discard;
pub struct Store<'a> {
dir_path: &'a Path,
}
@ -88,8 +84,10 @@ pub mod git {
impl<'a> super::Store for Store<'a> {
fn sync(&self, descr: &Descr, _limits: Limits) -> Result<(), SyncError> {
let should_interrupt = &core::sync::atomic::AtomicBool::new(false);
use gix::clone::Error as gixCloneErr;
use gix::progress::Discard;
let should_interrupt = &core::sync::atomic::AtomicBool::new(false);
let repo_path = &self.repo_path(descr);
// if the path doesn't exist then use the gix clone feature to clone it into the
@ -99,8 +97,6 @@ pub mod git {
let Descr::Git { url, branch_name } = descr;
use gix::clone::Error as gixCloneErr;
let (repo, _) = gix::prepare_clone_bare(url.clone(), repo_path)
.map_err(|e| match e {
gixCloneErr::Init(gix::init::Error::InvalidBranchName { .. }) => {
@ -214,7 +210,6 @@ pub mod git {
mod tests {
use super::super::Store;
use super::Descr;
use tempdir::TempDir;
#[test]