feat: working on multiple request types

This commit is contained in:
2026-07-06 01:58:05 +02:00
parent dee178ebc2
commit 9c0f296aa8
6 changed files with 46 additions and 32 deletions
+10 -1
View File
@@ -1,8 +1,17 @@
use anyhow::{Result, ensure}; use anyhow::{Result, ensure};
use serde::Deserialize; use serde::Deserialize;
#[derive(Deserialize)]
pub enum EntryType {
A,
AAAA,
MX,
}
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct Entry { pub struct Entry {
#[serde(rename = "type")]
pub entry_type: EntryType,
pub name: String, pub name: String,
pub address: String, pub address: String,
#[serde(skip)] #[serde(skip)]
@@ -26,7 +35,7 @@ pub struct ConfigData {
} }
impl ConfigData { impl ConfigData {
pub fn get_address_by_domain(&self, domain: &str) -> Option<[u8; 4]> { pub fn get_ipv4_address_by_domain(&self, domain: &str) -> Option<[u8; 4]> {
self.entries self.entries
.iter() .iter()
.find(|e| e.name.as_str() == domain) .find(|e| e.name.as_str() == domain)
+1 -1
View File
@@ -1,6 +1,6 @@
use std::io::Cursor; use std::io::Cursor;
use anyhow::{Result, bail}; use anyhow::Result;
use crate::{ use crate::{
dns::{domain_to_bytes, parse_domain}, dns::{domain_to_bytes, parse_domain},
+2 -3
View File
@@ -43,9 +43,8 @@ fn parse_domain_inner(cursor: &mut Cursor<&[u8]>, jumps: usize) -> Result<String
break; break;
} }
let domain_part_bytes = cursor.read_bytes(len)?; let domain_part = cursor.read_string(len)?;
let domain_part = String::from_utf8_lossy(&domain_part_bytes); domain_parts.push(domain_part);
domain_parts.push(domain_part.to_string());
} }
Ok(domain_parts.join(".")) Ok(domain_parts.join("."))
+4 -3
View File
@@ -1,6 +1,6 @@
use std::io::Cursor; use std::io::Cursor;
use anyhow::{Result, bail}; use anyhow::Result;
use crate::ext::CursorExt; use crate::ext::CursorExt;
@@ -54,6 +54,7 @@ impl Header {
pub fn to_bytes(&self) -> Result<Vec<u8>> { pub fn to_bytes(&self) -> Result<Vec<u8>> {
let mut buffer = [0u8; 12]; let mut buffer = [0u8; 12];
let transaction_id = self.transaction_id.to_be_bytes();
let number_of_questions = self.number_of_questions.to_be_bytes(); let number_of_questions = self.number_of_questions.to_be_bytes();
let number_of_answers = self.number_of_answers.to_be_bytes(); let number_of_answers = self.number_of_answers.to_be_bytes();
let number_of_authority_resource_records = let number_of_authority_resource_records =
@@ -61,8 +62,8 @@ impl Header {
let number_of_additional_resource_records = let number_of_additional_resource_records =
self.number_of_additional_resource_records.to_be_bytes(); self.number_of_additional_resource_records.to_be_bytes();
buffer[0] = (self.transaction_id >> 8) as u8; buffer[0] = transaction_id[0];
buffer[1] = (self.transaction_id & 0x0F) as u8; buffer[1] = transaction_id[1];
if self.query_response { if self.query_response {
buffer[2] |= 1 << 7; buffer[2] |= 1 << 7;
} }
+1 -1
View File
@@ -1,6 +1,6 @@
use std::io::Cursor; use std::io::Cursor;
use anyhow::{Result, bail}; use anyhow::Result;
use crate::{ use crate::{
dns::{domain_to_bytes, parse_domain}, dns::{domain_to_bytes, parse_domain},
+28 -23
View File
@@ -11,7 +11,7 @@ use dotenv::dotenv;
use tokio::net::UdpSocket; use tokio::net::UdpSocket;
use tracing::{Instrument, Level}; use tracing::{Instrument, Level};
use crate::dns::*; use crate::{config::ConfigData, dns::*};
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
@@ -61,13 +61,6 @@ async fn handle_datagram(
let mut response = message.prepare_response(); let mut response = message.prepare_response();
for question in message.questions { for question in message.questions {
if question.question_type != RECORD_TYPE_A {
tracing::error!(
"only A records are supported yet, requested: {}",
question.question_type
);
return Ok(());
}
if question.question_class != CLASS_IN { if question.question_class != CLASS_IN {
tracing::error!( tracing::error!(
"only IN class records are supported yet, requested: {}", "only IN class records are supported yet, requested: {}",
@@ -76,22 +69,13 @@ async fn handle_datagram(
return Ok(()); return Ok(());
} }
let address = config.get_address_by_domain(&question.domain); match question.question_type {
match address { RECORD_TYPE_A => {
Some(address) => { handle_type_a_question(&config, &question, &mut response);
let answer = Answer {
domain: question.domain,
record_type: RECORD_TYPE_A,
class: CLASS_IN,
ttl: 1,
data: address.to_vec(),
};
response.answers.push(answer);
response.header.number_of_answers += 1;
} }
None => { qt => {
response.header.response_code = RESPONSE_CODE_NXDOMAIN; tracing::error!("unsupported question type, requested: {qt}");
return Ok(());
} }
} }
} }
@@ -106,3 +90,24 @@ async fn handle_datagram(
result result
} }
fn handle_type_a_question(config: &ConfigData, question: &Question, response: &mut Message) {
let address = config.get_ipv4_address_by_domain(&question.domain);
match address {
Some(address) => {
let answer = Answer {
domain: question.domain.clone(),
record_type: RECORD_TYPE_A,
class: CLASS_IN,
ttl: 1,
data: address.to_vec(),
};
response.answers.push(answer);
response.header.number_of_answers += 1;
}
None => {
response.header.response_code = RESPONSE_CODE_NXDOMAIN;
}
}
}