feat: init
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
use anyhow::Result;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ConfigData {
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
pub fn load() -> Result<ConfigData> {
|
||||
let source = config::Environment::default();
|
||||
let builder = config::Config::builder()
|
||||
.add_source(source)
|
||||
.set_default("port", 53)?;
|
||||
let config = builder.build()?.try_deserialize()?;
|
||||
Ok(config)
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
#![allow(unused)]
|
||||
use std::fmt::Display;
|
||||
|
||||
const QUESTION_TYPE_A: u16 = 0x01;
|
||||
const QUESTION_TYPE_NS: u16 = 0x02;
|
||||
const QUESTION_TYPE_CNAME: u16 = 0x05;
|
||||
const QUESTION_TYPE_MX: u16 = 0x0F;
|
||||
const QUESTION_TYPE_AAAA: u16 = 0x40;
|
||||
|
||||
const QUESTION_CLASS_IN: u16 = 0x01;
|
||||
const QUESTION_CLASS_CHAOS: u16 = 0x03;
|
||||
const QUESTION_CLASS_HS: u16 = 0x04;
|
||||
const QUESTION_CLASS_ANY: u16 = 0xFF;
|
||||
|
||||
pub struct DnsHeader {
|
||||
pub transaction_id: [u8; 2],
|
||||
pub query_response: bool,
|
||||
pub op_code: u8, // 4 bits
|
||||
pub authoritative_answer: bool,
|
||||
pub truncation: bool,
|
||||
pub recursion_desired: bool,
|
||||
pub recursion_available: bool,
|
||||
pub authentic_data: bool,
|
||||
pub checking_disabled: bool,
|
||||
pub response_code: u8, // 4 bits
|
||||
pub number_of_questions: u16,
|
||||
pub number_of_answers: u16,
|
||||
pub number_of_authority_resource_records: u16,
|
||||
pub number_of_additional_resource_records: u16,
|
||||
}
|
||||
|
||||
impl Display for DnsHeader {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"transaction_id: {:x}{:x}",
|
||||
self.transaction_id[0], self.transaction_id[1]
|
||||
)?;
|
||||
writeln!(f, "query_response: {}", self.query_response)?;
|
||||
writeln!(f, "op_code: {}", self.op_code)?;
|
||||
writeln!(f, "authoritative_answer: {}", self.authoritative_answer)?;
|
||||
writeln!(f, "truncation: {}", self.truncation)?;
|
||||
writeln!(f, "recursion_desired: {}", self.recursion_desired)?;
|
||||
writeln!(f, "recursion_available: {}", self.recursion_available)?;
|
||||
writeln!(f, "authentic_data: {}", self.authentic_data)?;
|
||||
writeln!(f, "checking_disabled: {}", self.checking_disabled)?;
|
||||
writeln!(f, "response_code: {}", self.response_code)?;
|
||||
writeln!(f, "number_of_questions: {}", self.number_of_questions)?;
|
||||
writeln!(f, "number_of_answers: {}", self.number_of_answers)?;
|
||||
writeln!(
|
||||
f,
|
||||
"number_of_authority_rr: {}",
|
||||
self.number_of_authority_resource_records
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
"number_of_additional_rr: {}",
|
||||
self.number_of_additional_resource_records
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DnsQuestion {
|
||||
pub domain: String,
|
||||
pub question_type: u16,
|
||||
pub question_class: u16,
|
||||
}
|
||||
|
||||
impl Display for DnsQuestion {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}, {}, {}",
|
||||
self.domain, self.question_type, self.question_class
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DnsAnswer {
|
||||
pub domain: String,
|
||||
}
|
||||
|
||||
impl DnsAnswer {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut result = vec![];
|
||||
|
||||
for part in self.domain.split(".") {
|
||||
result.push(part.len() as u8);
|
||||
part.bytes().for_each(|c| result.push(c));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
pub fn setup() {
|
||||
tracing_subscriber::registry()
|
||||
.with(tracing_subscriber::fmt::layer().compact())
|
||||
.with(EnvFilter::from_env("LOG"))
|
||||
.init();
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
mod config;
|
||||
mod dns;
|
||||
mod logging;
|
||||
|
||||
use anyhow::Result;
|
||||
use dotenv::dotenv;
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use crate::dns::*;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
dotenv().ok();
|
||||
let config = config::load()?;
|
||||
logging::setup();
|
||||
|
||||
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 = sock.recv(&mut buffer).await?;
|
||||
handle_datagram(buffer.clone(), size);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_datagram(buffer: [u8; 512], _buffer_size: usize) {
|
||||
let datagram = DnsHeader {
|
||||
transaction_id: [buffer[0], buffer[1]],
|
||||
query_response: buffer[2] & (1 << 7) > 0,
|
||||
op_code: (buffer[2] >> 3) & 0x0F,
|
||||
authoritative_answer: buffer[2] & (1 << 2) > 0,
|
||||
truncation: buffer[2] & (1 << 1) > 0,
|
||||
recursion_desired: buffer[2] & (1 << 0) > 0,
|
||||
recursion_available: buffer[3] & (1 << 7) > 0,
|
||||
authentic_data: buffer[3] & (1 << 5) > 0,
|
||||
checking_disabled: buffer[3] & (1 << 4) > 0,
|
||||
response_code: buffer[3] & 0xF,
|
||||
number_of_questions: ((buffer[4] as u16) << 8) | (buffer[5] as u16),
|
||||
number_of_answers: ((buffer[6] as u16) << 8) | (buffer[7] as u16),
|
||||
number_of_authority_resource_records: ((buffer[8] as u16) << 8) | (buffer[9] as u16),
|
||||
number_of_additional_resource_records: ((buffer[10] as u16) << 8) | (buffer[11] as u16),
|
||||
};
|
||||
|
||||
println!("Received:\n{}", datagram);
|
||||
|
||||
let mut pos = 12usize;
|
||||
for _ in 0..(datagram.number_of_questions) {
|
||||
let mut domain_parts: Vec<String> = vec![];
|
||||
|
||||
loop {
|
||||
let len = buffer[pos] as usize;
|
||||
if len == 0 {
|
||||
pos += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
let domain_part_bytes = &buffer[(pos + 1)..(pos + 1 + len)];
|
||||
let domain_part = String::from_utf8_lossy(&domain_part_bytes);
|
||||
domain_parts.push(domain_part.to_string());
|
||||
pos += 1 + len;
|
||||
}
|
||||
|
||||
let question_type = ((buffer[pos] as u16) << 8) | (buffer[pos + 1] as u16);
|
||||
let question_class = ((buffer[pos + 2] as u16) << 8) | (buffer[pos + 3] as u16);
|
||||
|
||||
let question = DnsQuestion {
|
||||
domain: domain_parts.join("."),
|
||||
question_type,
|
||||
question_class,
|
||||
};
|
||||
println!("Question: {}", question);
|
||||
|
||||
pos += 4;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user