move validation to Domain ctor

This commit is contained in:
Jorge Aparicio 2024-02-05 15:53:48 +01:00
parent bab595a412
commit 1b0f1ef59f
4 changed files with 50 additions and 35 deletions

View File

@ -1,4 +1,5 @@
use std::{net::Ipv4Addr, process::Child};
use std::net::Ipv4Addr;
use std::process::Child;
use crate::{container::Container, Domain, Result, CHMOD_RW_EVERYONE};
@ -14,15 +15,12 @@ impl AuthoritativeNameServer {
container.status_ok(&["mkdir", "-p", "/etc/nsd/zones"])?;
let zone_path = "/etc/nsd/zones/main.zone";
container.cp(
"/etc/nsd/nsd.conf",
&nsd_conf(domain.fqdn()),
CHMOD_RW_EVERYONE,
)?;
container.cp("/etc/nsd/nsd.conf", &nsd_conf(domain), CHMOD_RW_EVERYONE)?;
let zone_file_contents = match domain {
Domain::Root => root_zone(),
Domain::Tld { domain } => tld_zone(domain),
let zone_file_contents = if domain.is_root() {
root_zone()
} else {
tld_zone(domain)
};
container.cp(zone_path, &zone_file_contents, CHMOD_RW_EVERYONE)?;
@ -43,13 +41,12 @@ impl Drop for AuthoritativeNameServer {
}
}
fn tld_zone(domain: &str) -> String {
assert!(domain.ends_with('.'));
assert!(!domain.starts_with('.'));
fn tld_zone(domain: Domain) -> String {
assert!(!domain.is_root());
minijinja::render!(
include_str!("templates/tld.zone.jinja"),
tld => domain,
tld => domain.as_str()
)
}
@ -57,12 +54,10 @@ fn root_zone() -> String {
minijinja::render!(include_str!("templates/root.zone.jinja"),)
}
fn nsd_conf(domain: &str) -> String {
assert!(domain.ends_with('.'));
fn nsd_conf(domain: Domain) -> String {
minijinja::render!(
include_str!("templates/nsd.conf.jinja"),
domain => domain
domain => domain.as_str()
)
}
@ -72,7 +67,7 @@ mod tests {
#[test]
fn tld_setup() -> Result<()> {
let tld_ns = AuthoritativeNameServer::start(Domain::Tld { domain: "com." })?;
let tld_ns = AuthoritativeNameServer::start(Domain("com.")?)?;
let ip_addr = tld_ns.ipv4_addr();
let client = Container::run()?;
@ -87,7 +82,7 @@ mod tests {
#[test]
fn root_setup() -> Result<()> {
let root_ns = AuthoritativeNameServer::start(Domain::Root)?;
let root_ns = AuthoritativeNameServer::start(Domain::ROOT)?;
let ip_addr = root_ns.ipv4_addr();
let client = Container::run()?;

32
src/domain.rs Normal file
View File

@ -0,0 +1,32 @@
use crate::Result;
#[derive(Clone, Copy)]
pub struct Domain<'a> {
inner: &'a str,
}
// TODO likely needs further validation
#[allow(non_snake_case)]
pub fn Domain(input: &str) -> Result<Domain<'_>> {
if !input.ends_with('.') {
return Err("domain must end with a `.`".into());
}
if input != "." && input.starts_with('.') {
return Err("non-root domain cannot start with a `.`".into());
}
Ok(Domain { inner: input })
}
impl<'a> Domain<'a> {
pub const ROOT: Domain<'static> = Domain { inner: "." };
pub fn is_root(&self) -> bool {
self.inner == "."
}
pub fn as_str(&self) -> &'a str {
self.inner
}
}

View File

@ -1,4 +1,5 @@
pub use crate::authoritative_name_server::AuthoritativeNameServer;
pub use crate::domain::Domain;
pub use crate::recursive_resolver::RecursiveResolver;
pub type Error = Box<dyn std::error::Error>;
@ -8,18 +9,5 @@ const CHMOD_RW_EVERYONE: &str = "666";
mod authoritative_name_server;
pub mod container;
mod domain;
mod recursive_resolver;
pub enum Domain<'a> {
Root,
Tld { domain: &'a str },
}
impl Domain<'_> {
fn fqdn(&self) -> &str {
match self {
Domain::Root => ".",
Domain::Tld { domain } => domain,
}
}
}

View File

@ -52,14 +52,14 @@ impl Drop for RecursiveResolver {
#[cfg(test)]
mod tests {
use crate::AuthoritativeNameServer;
use crate::{AuthoritativeNameServer, Domain};
use super::*;
#[test]
#[ignore = "FIXME"]
fn can_resolve() -> Result<()> {
let root_ns = AuthoritativeNameServer::start(crate::Domain::Root)?;
let root_ns = AuthoritativeNameServer::start(Domain::ROOT)?;
let roots = &[RootServer {
name: "my.root-server.com".to_string(),
ip_addr: root_ns.ipv4_addr(),