Skip to content

Commit

Permalink
comply with rfc1034, section 3.6.2. Aliases and canonical names
Browse files Browse the repository at this point in the history
  • Loading branch information
xerbalind committed Jan 12, 2025
1 parent 35021fa commit d8f8396
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 5 deletions.
37 changes: 35 additions & 2 deletions zns-daemon/src/handlers/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ impl ResponseHandler for QueryHandler {
connection,
)?;

rrs.extend(try_cname(&domain_records));

if domain_records.is_empty() && !question.qname.is_empty() {
rrs.extend(try_wildcard(question, connection)?);
}
Expand Down Expand Up @@ -75,6 +77,14 @@ impl ResponseHandler for QueryHandler {
}
}

fn try_cname(records: &[RR]) -> Vec<RR> {
records
.iter()
.filter(|rr| rr._type == Type::Type(RRType::CNAME))
.cloned()
.collect()
}

fn try_wildcard(question: &Question, connection: &mut PgConnection) -> Result<Vec<RR>, ZNSError> {
let mut qname = question.qname.clone().to_vec();
qname[0] = String::from("*");
Expand All @@ -87,7 +97,6 @@ fn try_wildcard(question: &Question, connection: &mut PgConnection) -> Result<Ve
.into_iter()
.map(|mut rr| {
rr.name.clone_from(&question.qname);
println!("{:#?}", rr);
rr
})
.collect())
Expand Down Expand Up @@ -141,7 +150,7 @@ mod tests {
use crate::db::{lib::tests::get_test_connection, models::insert_into_database};
use zns::{
parser::ToBytes,
test_utils::{get_message, get_rr},
test_utils::{get_cname_rr, get_message, get_rr},
};

#[tokio::test]
Expand Down Expand Up @@ -197,4 +206,28 @@ mod tests {
assert_eq!(result.answer.len(), 2);
assert_eq!(result.answer[0], rr);
}

#[tokio::test]
async fn test_cname() {
let mut connection = get_test_connection();
let rr = get_cname_rr(Some(Config::get().authoritative_zone.clone()));

assert!(insert_into_database(&rr, &mut connection).is_ok());

let mut message = get_message(Some(Config::get().authoritative_zone.clone()));
message.header.ancount = 0;
message.answer = vec![];

let result = QueryHandler::handle(
&message,
&Message::to_bytes(message.clone()),
&mut connection,
)
.await
.unwrap();
assert_eq!(result.header.ancount, 2);
assert_eq!(result.answer.len(), 2);
assert_eq!(result.answer[0], rr);
assert_eq!(result.answer[1], rr);
}
}
23 changes: 21 additions & 2 deletions zns-daemon/src/handlers/update/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use diesel::PgConnection;

use crate::{
config::Config,
db::models::{delete_from_database, insert_into_database},
db::models::{delete_from_database, get_from_database, insert_into_database},
};

use zns::errors::ZNSError;
use zns::structs::{Class, Message, RRClass, RRType, Type};
use zns::{errors::ZNSError, structs::RR};

use self::sig::Sig;

Expand Down Expand Up @@ -90,6 +90,9 @@ impl ResponseHandler for UpdateHandler {

for rr in &message.authority {
if rr.class == zone.qclass {
if let Some(message) = validate_record(rr, connection)? {
return Err(ZNSError::Refused { message });
}
insert_into_database(rr, connection)?;
} else if rr.class == Class::Class(RRClass::ANY) {
if rr._type == Type::Type(RRType::ANY) {
Expand Down Expand Up @@ -133,3 +136,19 @@ impl ResponseHandler for UpdateHandler {
Ok(response)
}
}

fn validate_record(record: &RR, connection: &mut PgConnection) -> Result<Option<String>, ZNSError> {
let rr_type = match record._type {
Type::Type(RRType::CNAME) => None,
_ => Some(Type::Type(RRType::CNAME)),
};

let records = get_from_database(&record.name, rr_type, record.class.clone(), connection)?;
if !records.is_empty() {
Ok(Some(
"Another record with the same name already exists".to_string(),
))
} else {
Ok(None)
}
}
3 changes: 3 additions & 0 deletions zns/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub enum ZNSError {
NotAuth { message: String },
#[error("I refuse to answer the query: {message:?}")]
Refused { message: String },
#[error("Update RR is outside zone: {message:?}")]
UpdateZone { message: String },
}

impl ZNSError {
Expand All @@ -35,6 +37,7 @@ impl ZNSError {
ZNSError::NXDomain { .. } => RCODE::NXDOMAIN,
ZNSError::NotImp { .. } => RCODE::NOTIMP,
ZNSError::Refused { .. } | ZNSError::Key { .. } => RCODE::REFUSED,
ZNSError::UpdateZone { .. } => RCODE::NOTZONE,
}
}
}
29 changes: 28 additions & 1 deletion zns/src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#![cfg(feature = "test-utils")]
use rand::distributions::Alphanumeric;
use rand::Rng;

use crate::structs::*;

#[cfg(feature = "test-utils")]
use crate::labelstring::LabelString;
pub fn get_rr(name: Option<LabelString>) -> RR {
RR {
Expand All @@ -19,6 +21,17 @@ pub fn get_rr(name: Option<LabelString>) -> RR {
}
}

pub fn get_cname_rr(name: Option<LabelString>) -> RR {
RR {
name: name.unwrap_or(LabelString::from("example.org")),
_type: Type::Type(RRType::CNAME),
class: Class::Class(RRClass::IN),
ttl: 10,
rdlength: 4,
rdata: RData::LabelString(random_domain()),
}
}

pub fn get_message(name: Option<LabelString>) -> Message {
Message {
header: Header {
Expand Down Expand Up @@ -46,3 +59,17 @@ pub fn get_message(name: Option<LabelString>) -> Message {
additional: vec![get_rr(name)],
}
}

fn random_domain() -> LabelString {
let part1 = random_string();
let part2 = random_string();
LabelString::from(&format!("{part1}.{part2}"))
}

fn random_string() -> String {
rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(7)
.map(char::from)
.collect()
}

0 comments on commit d8f8396

Please sign in to comment.