add dns-server-tutorial

This commit is contained in:
2025-03-08 21:24:00 +08:00
parent 10672a86b1
commit 48ad5122f2
12 changed files with 994 additions and 3 deletions

View File

@@ -0,0 +1,78 @@
// src/dns.rs
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ErrorCondition {
#[error("Serialization Error: {0}")]
SerializationErr(String),
#[error("Deserialization Error: {0}")]
DeserializationErr(String),
}
#[derive(Debug)]
pub struct Header {
pub id: u16, // identifier
pub qr: bool, // 0 for query, 1 for response
pub opcode: u8, // 0 for standard query
pub aa: bool, // authoritative answer
pub tc: bool, // truncated message
pub rd: bool, // recursion desired
pub ra: bool, // recursion available
pub z: u8, // reserved for future use
pub rcode: u8, // 0 for no error
pub qdcount: u16, // number of entries in the question section
pub ancount: u16, // number of resource records in the answer section
pub nscount: u16, // number of name server resource records in the authority records section
pub arcount: u16, // number of resource records in the additional records section
}
impl Header {
const DNS_HEADER_LEN: usize = 12;
// Serialize the header to a byte array
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(Header::DNS_HEADER_LEN);
buf.extend_from_slice(&self.id.to_be_bytes());
buf.push(
(self.qr as u8) << 7
| self.opcode << 3
| (self.aa as u8) << 2
| (self.tc as u8) << 1
| self.rd as u8,
);
buf.push((self.ra as u8) << 7 | self.z << 4 | self.rcode);
buf.extend_from_slice(&self.qdcount.to_be_bytes());
buf.extend_from_slice(&self.ancount.to_be_bytes());
buf.extend_from_slice(&self.nscount.to_be_bytes());
buf.extend_from_slice(&self.arcount.to_be_bytes());
buf
}
// Deserialize the header from a byte array
pub fn from_bytes(buf: &[u8]) -> Result<Header, ErrorCondition> {
if buf.len() < Header::DNS_HEADER_LEN {
return Err(ErrorCondition::DeserializationErr(
"Buffer length is less than header length".to_string(),
));
}
Ok(Header {
id: u16::from_be_bytes([buf[0], buf[1]]),
qr: (buf[2] & 0b1000_0000) != 0,
opcode: (buf[2] & 0b0111_1000) >> 3,
aa: (buf[2] & 0b0000_0100) != 0,
tc: (buf[2] & 0b0000_0010) != 0,
rd: (buf[2] & 0b0000_0001) != 0,
ra: (buf[3] & 0b1000_0000) != 0,
z: (buf[3] & 0b0111_0000) >> 4,
rcode: buf[3] & 0b0000_1111,
qdcount: u16::from_be_bytes([buf[4], buf[5]]),
ancount: u16::from_be_bytes([buf[6], buf[7]]),
nscount: u16::from_be_bytes([buf[8], buf[9]]),
arcount: u16::from_be_bytes([buf[10], buf[11]]),
})
}
}

View File

@@ -0,0 +1,44 @@
// src/main.rs
use std::net::UdpSocket;
mod dns;
use dns::Header;
// Debug print hex bytes of a buffer 16 bytes width followed by the ASCII representation of the bytes
fn debug_print_bytes(buf: &[u8]) {
for (i, chunk) in buf.chunks(16).enumerate() {
print!("{:08x}: ", i * 16);
for byte in chunk {
print!("{:02x} ", byte);
}
for _ in 0..(16 - chunk.len()) {
print!(" ");
}
print!(" ");
for byte in chunk {
if *byte >= 32 && *byte <= 126 {
print!("{}", *byte as char);
} else {
print!(".");
}
}
println!();
}
}
fn main() {
let socket = UdpSocket::bind("0.0.0.0:1053").expect("Could not bind to port 1053");
let mut buf = [0; 512];
println!("DNS server is running at port 1053");
loop {
let (len, addr) = socket.recv_from(&mut buf).expect("Could not receive data");
println!("\nReceived query from {} with length {} bytes", addr, len);
debug_print_bytes(&buf[..len]);
let header = Header::from_bytes(&buf[..len]).expect("Could not parse DNS header");
println!("\n{:?}", header);
}
}