This commit is contained in:
2026-07-04 01:40:03 +02:00
parent 77f24e2abf
commit b0e230d4d8
12 changed files with 576 additions and 199 deletions
+76 -12
View File
@@ -1,42 +1,106 @@
mod config;
mod dns;
mod logging;
mod macros;
use std::net::SocketAddr;
use anyhow::Result;
use dotenv::dotenv;
use tokio::net::UdpSocket;
use tracing::{Instrument, Level};
use crate::dns::*;
#[tokio::main]
async fn main() -> Result<()> {
dotenv().ok();
let config = config::load()?;
logging::setup();
let config = config::load()?;
tracing::info!("start server on port {}", config.port);
let sock = UdpSocket::bind(("0.0.0.0", config.port)).await?;
let mut buffer = [0u8; 512];
loop {
let (size, addr) = sock.recv_from(&mut buffer).await?;
tracing::info!("Received datagram from {}", addr.to_string());
if let Err(err) = handle_datagram(&buffer[..size]) {
let result = handle_datagram(&config, &sock, &addr, &buffer[..size]).await;
if let Err(err) = result {
tracing::error!("failed to handle datagram: {err}");
}
}
}
fn handle_datagram(packet: &[u8]) -> Result<()> {
let header = DnsHeader::parse(packet)?;
println!("Received:\n{}", header);
async fn handle_datagram(
config: &config::ConfigData,
sock: &UdpSocket,
addr: &SocketAddr,
datagram: &[u8],
) -> Result<()> {
let message = Message::parse(datagram)?;
let span = tracing::span!(
Level::INFO,
"handle_datagram",
transaction_id = %txid!(message.header.transaction_id),
client = %addr,
);
let mut pos = 12usize;
for _ in 0..(header.number_of_questions) {
let question = DnsQuestion::parse(packet, &mut pos)?;
println!("Question: {}", question);
let result: Result<()> = async {
tracing::info!("received message");
if message.header.query_response {
tracing::error!("message is response, drop");
return Ok(());
}
if message.header.op_code != 0 {
tracing::error!("op code is not 0, drop");
return Ok(());
}
let mut response = message.prepare_response();
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 {
tracing::error!(
"only IN class records are supported yet, requested: {}",
question.question_class
);
return Ok(());
}
let address = config.get_address_by_domain(&question.domain);
match address {
Some(address) => {
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 => {
response.header.response_code = RESPONSE_CODE_NXDOMAIN;
}
}
}
let response_bytes = response.to_bytes()?;
sock.send_to(&response_bytes, addr).await?;
Ok(())
}
.instrument(span)
.await;
Ok(())
result
}