.
This commit is contained in:
+76
-12
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user