refactor to async tokio with modular architecture
- Replace synchronous std::net::UdpSocket with tokio async runtime - Spawn concurrent task per incoming DNS query via tokio::spawn - Extract monolithic main.rs into modules: buffer, header, question, record, packet, config, cache, forward, stats - Share state across tasks via Arc<ServerCtx> with scoped Mutex locks - Add TOML config loading, TTL-aware cache, structured logging, stats - Add CLAUDE.md, README, dns_fun.toml config, and design docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
105
src/packet.rs
Normal file
105
src/packet.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use crate::buffer::BytePacketBuffer;
|
||||
use crate::header::DnsHeader;
|
||||
use crate::question::{DnsQuestion, QueryType};
|
||||
use crate::record::DnsRecord;
|
||||
use crate::Result;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DnsPacket {
|
||||
pub header: DnsHeader,
|
||||
pub questions: Vec<DnsQuestion>,
|
||||
pub answers: Vec<DnsRecord>,
|
||||
pub authorities: Vec<DnsRecord>,
|
||||
pub resources: Vec<DnsRecord>,
|
||||
}
|
||||
|
||||
impl DnsPacket {
|
||||
pub fn new() -> DnsPacket {
|
||||
DnsPacket {
|
||||
header: DnsHeader::new(),
|
||||
questions: Vec::new(),
|
||||
answers: Vec::new(),
|
||||
authorities: Vec::new(),
|
||||
resources: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn response_from(query: &DnsPacket, rescode: crate::header::ResultCode) -> DnsPacket {
|
||||
let mut resp = DnsPacket::new();
|
||||
resp.header.id = query.header.id;
|
||||
resp.header.response = true;
|
||||
resp.header.recursion_desired = query.header.recursion_desired;
|
||||
resp.header.recursion_available = true;
|
||||
resp.header.rescode = rescode;
|
||||
resp.questions = query.questions.clone();
|
||||
resp
|
||||
}
|
||||
|
||||
pub fn from_buffer(buffer: &mut BytePacketBuffer) -> Result<DnsPacket> {
|
||||
let mut result = DnsPacket::new();
|
||||
result.header.read(buffer)?;
|
||||
|
||||
for _ in 0..result.header.questions {
|
||||
let mut question = DnsQuestion::new("".to_string(), QueryType::UNKNOWN(0));
|
||||
question.read(buffer)?;
|
||||
result.questions.push(question);
|
||||
}
|
||||
|
||||
for _ in 0..result.header.answers {
|
||||
let rec = DnsRecord::read(buffer)?;
|
||||
result.answers.push(rec);
|
||||
}
|
||||
for _ in 0..result.header.authoritative_entries {
|
||||
let rec = DnsRecord::read(buffer)?;
|
||||
result.authorities.push(rec);
|
||||
}
|
||||
for _ in 0..result.header.resource_entries {
|
||||
let rec = DnsRecord::read(buffer)?;
|
||||
result.resources.push(rec);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn write(&self, buffer: &mut BytePacketBuffer) -> Result<()> {
|
||||
let mut header = self.header.clone();
|
||||
header.questions = self.questions.len() as u16;
|
||||
header.answers = self.answers.len() as u16;
|
||||
header.authoritative_entries = self.authorities.len() as u16;
|
||||
header.resource_entries = self.resources.len() as u16;
|
||||
|
||||
header.write(buffer)?;
|
||||
|
||||
for question in &self.questions {
|
||||
question.write(buffer)?;
|
||||
}
|
||||
for rec in &self.answers {
|
||||
rec.write(buffer)?;
|
||||
}
|
||||
for rec in &self.authorities {
|
||||
rec.write(buffer)?;
|
||||
}
|
||||
for rec in &self.resources {
|
||||
rec.write(buffer)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn display(&self) {
|
||||
println!("{:#?}", self.header);
|
||||
|
||||
for q in &self.questions {
|
||||
println!("{:#?}", q);
|
||||
}
|
||||
for rec in &self.answers {
|
||||
println!("{:#?}", rec);
|
||||
}
|
||||
for rec in &self.authorities {
|
||||
println!("{:#?}", rec);
|
||||
}
|
||||
for rec in &self.resources {
|
||||
println!("{:#?}", rec);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user