diff options
Diffstat (limited to '')
-rw-r--r-- | src/config.rs | 35 | ||||
-rw-r--r-- | src/main.rs | 54 | ||||
-rw-r--r-- | src/packet/buffer.rs (renamed from packet/src/buffer.rs) | 96 | ||||
-rw-r--r-- | src/packet/header.rs (renamed from packet/src/header.rs) | 5 | ||||
-rw-r--r-- | src/packet/mod.rs (renamed from packet/src/lib.rs) | 62 | ||||
-rw-r--r-- | src/packet/query.rs (renamed from packet/src/query.rs) | 20 | ||||
-rw-r--r-- | src/packet/question.rs (renamed from packet/src/question.rs) | 4 | ||||
-rw-r--r-- | src/packet/record.rs | 498 | ||||
-rw-r--r-- | src/packet/result.rs (renamed from packet/src/result.rs) | 2 | ||||
-rw-r--r-- | src/server/binding.rs | 150 | ||||
-rw-r--r-- | src/server/mod.rs | 3 | ||||
-rw-r--r-- | src/server/resolver.rs | 165 | ||||
-rw-r--r-- | src/server/server.rs | 73 |
13 files changed, 1078 insertions, 89 deletions
diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..9350adf --- /dev/null +++ b/src/config.rs @@ -0,0 +1,35 @@ +use std::net::IpAddr; + +#[derive(Clone)] +pub struct Config { + fallback: IpAddr, + port: u16, +} + +impl Config { + pub fn new() -> Self { + let fallback = "9.9.9.9" + .parse::<IpAddr>() + .expect("Failed to create default ns fallback"); + Self { + fallback, + port: 2000, + } + } + + pub fn get_fallback_ns(&self) -> &IpAddr { + &self.fallback + } + + pub fn get_port(&self) -> u16 { + self.port + } + + pub fn set_fallback_ns(&mut self, addr: &IpAddr) { + self.fallback = *addr; + } + + pub fn set_port(&mut self, port: u16) { + self.port = port; + } +} diff --git a/src/main.rs b/src/main.rs index 0b767de..c891d50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,48 @@ -use std::net::SocketAddr; +use std::{time::{UNIX_EPOCH, SystemTime}, env, net::IpAddr}; -use resolver::{DnsResolver, Config}; +use config::Config; + +use server::server::Server; +use tracing::metadata::LevelFilter; +use tracing_subscriber::{ + filter::filter_fn, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer, +}; + +mod config; +mod packet; +mod server; #[tokio::main] -async fn main () { - - let config = Config::new(); +async fn main() { + tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .with_filter(LevelFilter::TRACE) + .with_filter(filter_fn(|metadata| { + metadata.target().starts_with("wrapper") + })), + ) + .init(); + + let mut config = Config::new(); + + if let Ok(port) = env::var("PORT").unwrap_or(String::new()).parse::<u16>() { + config.set_port(port); + } + + if let Ok(fallback) = env::var("FALLBACK_DNS").unwrap_or(String::new()).parse::<IpAddr>() { + config.set_fallback_ns(&fallback); + } - let resolver = DnsResolver::new(config); + let server = Server::new(config).await.expect("Failed to bind server"); - let addr = "[::]:2000".parse::<SocketAddr>() - .expect("Failed to create binding"); - - resolver.bind(addr).await - .expect("Failed to start dns server"); + server.run().await.unwrap(); +} -}
\ No newline at end of file +pub fn get_time() -> u64 { + let start = SystemTime::now(); + let since_the_epoch = start + .duration_since(UNIX_EPOCH) + .expect("Time went backwards"); + since_the_epoch.as_millis() as u64 +} diff --git a/packet/src/buffer.rs b/src/packet/buffer.rs index 4394705..4ecc605 100644 --- a/packet/src/buffer.rs +++ b/src/packet/buffer.rs @@ -1,15 +1,27 @@ use super::Result; pub struct PacketBuffer { - pub buf: [u8; 512], + pub buf: Vec<u8>, pub pos: usize, + pub size: usize, } impl PacketBuffer { - pub fn new() -> Self { + pub fn new(buf: Vec<u8>) -> Self { Self { - buf: [0; 512], + buf, pos: 0, + size: 0, + } + } + + fn check(&mut self, pos: usize) { + if self.size < pos { + self.size = pos; + } + + if self.buf.len() <= self.size { + self.buf.resize(self.size + 1, 0x00); } } @@ -30,9 +42,11 @@ impl PacketBuffer { } pub fn read(&mut self) -> Result<u8> { - if self.pos >= 512 { - return Err("End of buffer".into()); - } + // if self.pos >= 512 { + // error!("Tried to read past end of buffer"); + // return Err("End of buffer".into()); + // } + self.check(self.pos); let res = self.buf[self.pos]; self.pos += 1; @@ -40,16 +54,20 @@ impl PacketBuffer { } pub fn get(&mut self, pos: usize) -> Result<u8> { - if pos >= 512 { - return Err("End of buffer".into()); - } + // if pos >= 512 { + // error!("Tried to read past end of buffer"); + // return Err("End of buffer".into()); + // } + self.check(pos); Ok(self.buf[pos]) } pub fn get_range(&mut self, start: usize, len: usize) -> Result<&[u8]> { - if start + len >= 512 { - return Err("End of buffer".into()); - } + // if start + len >= 512 { + // error!("Tried to read past end of buffer"); + // return Err("End of buffer".into()); + // } + self.check(start + len); Ok(&self.buf[start..start + len]) } @@ -85,13 +103,7 @@ impl PacketBuffer { let len = self.get(pos)?; - // A two byte sequence, where the two highest bits of the first byte is - // set, represents a offset relative to the start of the buffer. We - // handle this by jumping to the offset, setting a flag to indicate - // that we shouldn't update the shared buffer position once done. if (len & 0xC0) == 0xC0 { - // When a jump is performed, we only modify the shared buffer - // position once, and avoid making the change later on. if !jumped { self.seek(pos + 2)?; } @@ -106,7 +118,6 @@ impl PacketBuffer { pos += 1; - // Names are terminated by an empty label of length 0 if len == 0 { break; } @@ -128,10 +139,38 @@ impl PacketBuffer { Ok(()) } - pub fn write(&mut self, val: u8) -> Result<()> { - if self.pos >= 512 { - return Err("End of buffer".into()); + pub fn read_string(&mut self, outstr: &mut String) -> Result<()> { + let len = self.read()?; + + self.read_string_n(outstr, len)?; + + Ok(()) + } + + pub fn read_string_n(&mut self, outstr: &mut String, len: u8) -> Result<()> { + let mut pos = self.pos; + + let str_buffer = self.get_range(pos, len as usize)?; + + let mut i = 0; + for b in str_buffer { + let c = *b as char; + if c == '\0' { + break; + } + outstr.push(c); + i += 1; } + + pos += i; + self.seek(pos)?; + + Ok(()) + } + + pub fn write(&mut self, val: u8) -> Result<()> { + self.check(self.pos); + self.buf[self.pos] = val; self.pos += 1; Ok(()) @@ -162,9 +201,6 @@ impl PacketBuffer { pub fn write_qname(&mut self, qname: &str) -> Result<()> { for label in qname.split('.') { let len = label.len(); - if len > 0x34 { - return Err("Single label exceeds 63 characters of length".into()); - } self.write_u8(len as u8)?; for b in label.as_bytes() { @@ -177,6 +213,14 @@ impl PacketBuffer { Ok(()) } + pub fn write_string(&mut self, text: &str) -> Result<()> { + for b in text.as_bytes() { + self.write_u8(*b)?; + } + + Ok(()) + } + pub fn set(&mut self, pos: usize, val: u8) -> Result<()> { self.buf[pos] = val; @@ -189,4 +233,4 @@ impl PacketBuffer { Ok(()) } -}
\ No newline at end of file +} diff --git a/packet/src/header.rs b/src/packet/header.rs index b2bf1a1..a75f6ba 100644 --- a/packet/src/header.rs +++ b/src/packet/header.rs @@ -1,4 +1,4 @@ -use super::{buffer::PacketBuffer, Result, result::ResultCode}; +use super::{buffer::PacketBuffer, result::ResultCode, Result}; #[derive(Clone, Debug)] pub struct DnsHeader { @@ -48,7 +48,6 @@ impl DnsHeader { pub fn read(&mut self, buffer: &mut PacketBuffer) -> Result<()> { self.id = buffer.read_u16()?; - let flags = buffer.read_u16()?; let a = (flags >> 8) as u8; let b = (flags & 0xFF) as u8; @@ -99,4 +98,4 @@ impl DnsHeader { Ok(()) } -}
\ No newline at end of file +} diff --git a/packet/src/lib.rs b/src/packet/mod.rs index c7a8eb9..0b7cb7b 100644 --- a/packet/src/lib.rs +++ b/src/packet/mod.rs @@ -1,16 +1,19 @@ use std::net::IpAddr; -use self::{header::DnsHeader, question::DnsQuestion, record::DnsRecord, query::QueryType}; +use self::{ + buffer::PacketBuffer, header::DnsHeader, query::QueryType, question::DnsQuestion, + record::DnsRecord, +}; type Error = Box<dyn std::error::Error>; pub type Result<T> = std::result::Result<T, Error>; -mod buffer; -mod header; -mod query; -mod question; -mod record; -mod result; +pub mod buffer; +pub mod header; +pub mod query; +pub mod question; +pub mod record; +pub mod result; #[derive(Clone, Debug)] pub struct Packet { @@ -21,12 +24,6 @@ pub struct Packet { pub resources: Vec<DnsRecord>, } -pub use buffer::PacketBuffer; -pub use result::ResultCode; - -pub use query::QueryType as PacketType; -pub use question::DnsQuestion as PacketQuestion; - impl Packet { pub fn new() -> Self { Self { @@ -88,9 +85,6 @@ impl Packet { Ok(()) } - /// It's useful to be able to pick a random A record from a packet. When we - /// get multiple IP's for a single name, it doesn't matter which one we - /// choose, so in those cases we can now pick one at random. pub fn get_random_a(&self) -> Option<IpAddr> { self.answers .iter() @@ -102,55 +96,35 @@ impl Packet { .next() } - /// A helper function which returns an iterator over all name servers in - /// the authorities section, represented as (domain, host) tuples fn get_ns<'a>(&'a self, qname: &'a str) -> impl Iterator<Item = (&'a str, &'a str)> { self.authorities .iter() - // In practice, these are always NS records in well formed packages. - // Convert the NS records to a tuple which has only the data we need - // to make it easy to work with. .filter_map(|record| match record { DnsRecord::NS { domain, host, .. } => Some((domain.as_str(), host.as_str())), _ => None, }) - // Discard servers which aren't authoritative to our query .filter(move |(domain, _)| qname.ends_with(*domain)) } - /// We'll use the fact that name servers often bundle the corresponding - /// A records when replying to an NS query to implement a function that - /// returns the actual IP for an NS record if possible. pub fn get_resolved_ns(&self, qname: &str) -> Option<IpAddr> { - // Get an iterator over the nameservers in the authorities section self.get_ns(qname) - // Now we need to look for a matching A record in the additional - // section. Since we just want the first valid record, we can just - // build a stream of matching records. .flat_map(|(_, host)| { self.resources .iter() - // Filter for A records where the domain match the host - // of the NS record that we are currently processing .filter_map(move |record| match record { - DnsRecord::A { domain, addr, .. } if domain == host => Some(IpAddr::V4(*addr)), - DnsRecord::AAAA { domain, addr, .. } if domain == host => Some(IpAddr::V6(*addr)), + DnsRecord::A { domain, addr, .. } if domain == host => { + Some(IpAddr::V4(*addr)) + } + DnsRecord::AAAA { domain, addr, .. } if domain == host => { + Some(IpAddr::V6(*addr)) + } _ => None, }) }) - // Finally, pick the first valid entry .next() } - /// However, not all name servers are as that nice. In certain cases there won't - /// be any A records in the additional section, and we'll have to perform *another* - /// lookup in the midst. For this, we introduce a method for returning the host - /// name of an appropriate name server. pub fn get_unresolved_ns<'a>(&'a self, qname: &'a str) -> Option<&'a str> { - // Get an iterator over the nameservers in the authorities section - self.get_ns(qname) - .map(|(_, host)| host) - // Finally, pick the first valid entry - .next() + self.get_ns(qname).map(|(_, host)| host).next() } -}
\ No newline at end of file +} diff --git a/packet/src/query.rs b/src/packet/query.rs index 8804d15..cae6f09 100644 --- a/packet/src/query.rs +++ b/src/packet/query.rs @@ -4,8 +4,14 @@ pub enum QueryType { A, // 1 NS, // 2 CNAME, // 5 + SOA, // 6 + PTR, // 12 MX, // 15 + TXT, // 16 AAAA, // 28 + SRV, // 33 + OPT, // 41 + CAA, // 257 } impl QueryType { @@ -15,8 +21,14 @@ impl QueryType { Self::A => 1, Self::NS => 2, Self::CNAME => 5, + Self::SOA => 6, + Self::PTR => 12, Self::MX => 15, + Self::TXT => 16, Self::AAAA => 28, + Self::SRV => 33, + Self::OPT => 41, + Self::CAA => 257, } } @@ -25,9 +37,15 @@ impl QueryType { 1 => Self::A, 2 => Self::NS, 5 => Self::CNAME, + 6 => Self::SOA, + 12 => Self::PTR, 15 => Self::MX, + 16 => Self::TXT, 28 => Self::AAAA, + 33 => Self::SRV, + 41 => Self::OPT, + 257 => Self::CAA, _ => Self::UNKNOWN(num), } } -}
\ No newline at end of file +} diff --git a/packet/src/question.rs b/src/packet/question.rs index 076de00..9042e1c 100644 --- a/packet/src/question.rs +++ b/src/packet/question.rs @@ -1,6 +1,6 @@ use super::{buffer::PacketBuffer, query::QueryType, Result}; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct DnsQuestion { pub name: String, pub qtype: QueryType, @@ -28,4 +28,4 @@ impl DnsQuestion { Ok(()) } -}
\ No newline at end of file +} diff --git a/src/packet/record.rs b/src/packet/record.rs new file mode 100644 index 0000000..c29dd8f --- /dev/null +++ b/src/packet/record.rs @@ -0,0 +1,498 @@ +use std::net::{Ipv4Addr, Ipv6Addr}; + +use tracing::{trace, warn}; + +use super::{buffer::PacketBuffer, query::QueryType, Result}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[allow(dead_code)] +pub enum DnsRecord { + UNKNOWN { + domain: String, + qtype: u16, + data_len: u16, + ttl: u32, + }, // 0 + A { + domain: String, + addr: Ipv4Addr, + ttl: u32, + }, // 1 + NS { + domain: String, + host: String, + ttl: u32, + }, // 2 + CNAME { + domain: String, + host: String, + ttl: u32, + }, // 5 + SOA { + domain: String, + mname: String, + nname: String, + serial: u32, + refresh: u32, + retry: u32, + expire: u32, + minimum: u32, + ttl: u32, + }, // 6 + PTR { + domain: String, + pointer: String, + ttl: u32, + }, // 12 + MX { + domain: String, + priority: u16, + host: String, + ttl: u32, + }, // 15 + TXT { + domain: String, + text: Vec<String>, + ttl: u32, + }, //16 + AAAA { + domain: String, + addr: Ipv6Addr, + ttl: u32, + }, // 28 + SRV { + domain: String, + priority: u16, + weight: u16, + port: u16, + target: String, + ttl: u32, + }, // 33 + CAA { + domain: String, + flags: u8, + length: u8, + tag: String, + value: String, + ttl: u32, + }, // 257 +} + +impl DnsRecord { + + pub fn read(buffer: &mut PacketBuffer) -> Result<Self> { + let mut domain = String::new(); + buffer.read_qname(&mut domain)?; + + let qtype_num = buffer.read_u16()?; + let qtype = QueryType::from_num(qtype_num); + let _ = buffer.read_u16()?; + let ttl = buffer.read_u32()?; + let data_len = buffer.read_u16()?; + + let header_pos = buffer.pos(); + + trace!("Reading DNS Record TYPE: {:?}", qtype); + + match qtype { + QueryType::A => { + let raw_addr = buffer.read_u32()?; + let addr = Ipv4Addr::new( + ((raw_addr >> 24) & 0xFF) as u8, + ((raw_addr >> 16) & 0xFF) as u8, + ((raw_addr >> 8) & 0xFF) as u8, + (raw_addr & 0xFF) as u8, + ); + + Ok(Self::A { domain, addr, ttl }) + } + QueryType::AAAA => { + let raw_addr1 = buffer.read_u32()?; + let raw_addr2 = buffer.read_u32()?; + let raw_addr3 = buffer.read_u32()?; + let raw_addr4 = buffer.read_u32()?; + let addr = Ipv6Addr::new( + ((raw_addr1 >> 16) & 0xFFFF) as u16, + (raw_addr1 & 0xFFFF) as u16, + ((raw_addr2 >> 16) & 0xFFFF) as u16, + (raw_addr2 & 0xFFFF) as u16, + ((raw_addr3 >> 16) & 0xFFFF) as u16, + (raw_addr3 & 0xFFFF) as u16, + ((raw_addr4 >> 16) & 0xFFFF) as u16, + (raw_addr4 & 0xFFFF) as u16, + ); + + Ok(Self::AAAA { domain, addr, ttl }) + } + QueryType::NS => { + let mut ns = String::new(); + buffer.read_qname(&mut ns)?; + + Ok(Self::NS { + domain, + host: ns, + ttl, + }) + } + QueryType::CNAME => { + let mut cname = String::new(); + buffer.read_qname(&mut cname)?; + + Ok(Self::CNAME { + domain, + host: cname, + ttl, + }) + } + QueryType::SOA => { + let mut mname = String::new(); + buffer.read_qname(&mut mname)?; + + let mut nname = String::new(); + buffer.read_qname(&mut nname)?; + + let serial = buffer.read_u32()?; + let refresh = buffer.read_u32()?; + let retry = buffer.read_u32()?; + let expire = buffer.read_u32()?; + let minimum = buffer.read_u32()?; + + Ok(Self::SOA { + domain, + mname, + nname, + serial, + refresh, + retry, + expire, + minimum, + ttl, + }) + } + QueryType::PTR => { + let mut pointer = String::new(); + buffer.read_qname(&mut pointer)?; + + Ok(Self::PTR { + domain, + pointer, + ttl, + }) + } + QueryType::MX => { + let priority = buffer.read_u16()?; + let mut mx = String::new(); + buffer.read_qname(&mut mx)?; + + Ok(Self::MX { + domain, + priority, + host: mx, + ttl, + }) + } + QueryType::TXT => { + let mut text = Vec::new(); + + loop { + let mut s = String::new(); + buffer.read_string(&mut s)?; + + if s.len() == 0 { + break; + } else { + text.push(s); + } + } + + Ok(Self::TXT { domain, text, ttl }) + } + QueryType::SRV => { + let priority = buffer.read_u16()?; + let weight = buffer.read_u16()?; + let port = buffer.read_u16()?; + + let mut target = String::new(); + buffer.read_qname(&mut target)?; + + Ok(Self::SRV { + domain, + priority, + weight, + port, + target, + ttl, + }) + } + QueryType::CAA => { + let flags = buffer.read()?; + let length = buffer.read()?; + + let mut tag = String::new(); + buffer.read_string_n(&mut tag, length)?; + + let value_len = (data_len as usize) + header_pos - buffer.pos; + let mut value = String::new(); + buffer.read_string_n(&mut value, value_len as u8)?; + + Ok(Self::CAA { + domain, + flags, + length, + tag, + value, + ttl, + }) + } + QueryType::UNKNOWN(_) | _ => { + buffer.step(data_len as usize)?; + + Ok(Self::UNKNOWN { + domain, + qtype: qtype_num, + data_len, + ttl, + }) + } + } + } + + pub fn write(&self, buffer: &mut PacketBuffer) -> Result<usize> { + let start_pos = buffer.pos(); + + trace!("Writing DNS Record {:?}", self); + + match *self { + Self::A { + ref domain, + ref addr, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::A.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + buffer.write_u16(4)?; + + let octets = addr.octets(); + buffer.write_u8(octets[0])?; + buffer.write_u8(octets[1])?; + buffer.write_u8(octets[2])?; + buffer.write_u8(octets[3])?; + } + Self::NS { + ref domain, + ref host, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::NS.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + buffer.write_qname(host)?; + + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::CNAME { + ref domain, + ref host, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::CNAME.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + buffer.write_qname(host)?; + + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::SOA { + ref domain, + ref mname, + ref nname, + serial, + refresh, + retry, + expire, + minimum, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::SOA.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + buffer.write_qname(mname)?; + buffer.write_qname(nname)?; + buffer.write_u32(serial)?; + buffer.write_u32(refresh)?; + buffer.write_u32(retry)?; + buffer.write_u32(expire)?; + buffer.write_u32(minimum)?; + + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::PTR { + ref domain, + ref pointer, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::NS.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + buffer.write_qname(&pointer)?; + + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::MX { + ref domain, + priority, + ref host, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::MX.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + buffer.write_u16(priority)?; + buffer.write_qname(host)?; + + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::TXT { + ref domain, + ref text, + ttl, + } => { + buffer.write_qname(&domain)?; + buffer.write_u16(QueryType::TXT.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + if text.is_empty() { + return Ok(buffer.pos() - start_pos); + } + + for s in text { + buffer.write_u8(s.len() as u8)?; + buffer.write_string(&s)?; + } + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::AAAA { + ref domain, + ref addr, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::AAAA.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + buffer.write_u16(16)?; + + for octet in &addr.segments() { + buffer.write_u16(*octet)?; + } + } + Self::SRV { + ref domain, + priority, + weight, + port, + ref target, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::SRV.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + buffer.write_u16(priority)?; + buffer.write_u16(weight)?; + buffer.write_u16(port)?; + buffer.write_qname(target)?; + + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::CAA { + ref domain, + flags, + length, + ref tag, + ref value, + ttl, + } => { + buffer.write_qname(domain)?; + buffer.write_u16(QueryType::CAA.to_num())?; + buffer.write_u16(1)?; + buffer.write_u32(ttl)?; + + let pos = buffer.pos(); + buffer.write_u16(0)?; + + buffer.write_u8(flags)?; + buffer.write_u8(length)?; + buffer.write_string(tag)?; + buffer.write_string(value)?; + + let size = buffer.pos() - (pos + 2); + buffer.set_u16(pos, size as u16)?; + } + Self::UNKNOWN { .. } => { + warn!("Skipping record: {self:?}"); + } + } + + Ok(buffer.pos() - start_pos) + } + + pub fn get_ttl(&self) -> u32 { + match *self { + DnsRecord::UNKNOWN { .. } => 0, + DnsRecord::AAAA { ttl, .. } => ttl, + DnsRecord::A { ttl, .. } => ttl, + DnsRecord::NS { ttl, .. } => ttl, + DnsRecord::CNAME { ttl, .. } => ttl, + DnsRecord::SOA { ttl, .. } => ttl, + DnsRecord::PTR { ttl, .. } => ttl, + DnsRecord::MX { ttl, .. } => ttl, + DnsRecord::TXT { ttl, .. } => ttl, + DnsRecord::SRV { ttl, .. } => ttl, + DnsRecord::CAA { ttl, .. } => ttl, + } + } + +} diff --git a/packet/src/result.rs b/src/packet/result.rs index f33bd7d..41c8ba9 100644 --- a/packet/src/result.rs +++ b/src/packet/result.rs @@ -19,4 +19,4 @@ impl ResultCode { 0 | _ => Self::NOERROR, } } -}
\ No newline at end of file +} diff --git a/src/server/binding.rs b/src/server/binding.rs new file mode 100644 index 0000000..1c69651 --- /dev/null +++ b/src/server/binding.rs @@ -0,0 +1,150 @@ +use std::{ + net::{IpAddr, SocketAddr}, + sync::Arc, +}; + +use crate::packet::{buffer::PacketBuffer, Packet, Result}; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::{TcpListener, TcpStream, UdpSocket}, +}; +use tracing::trace; + +pub enum Binding { + UDP(Arc<UdpSocket>), + TCP(TcpListener), +} + +impl Binding { + pub async fn udp(addr: SocketAddr) -> Result<Self> { + let socket = UdpSocket::bind(addr).await?; + Ok(Self::UDP(Arc::new(socket))) + } + + pub async fn tcp(addr: SocketAddr) -> Result<Self> { + let socket = TcpListener::bind(addr).await?; + Ok(Self::TCP(socket)) + } + + pub fn name(&self) -> &str { + match self { + Binding::UDP(_) => "UDP", + Binding::TCP(_) => "TCP", + } + } + + pub async fn connect(&mut self) -> Result<Connection> { + match self { + Self::UDP(socket) => { + let mut buf = [0; 512]; + let (_, addr) = socket.recv_from(&mut buf).await?; + Ok(Connection::UDP(socket.clone(), addr, buf)) + } + Self::TCP(socket) => { + let (stream, _) = socket.accept().await?; + Ok(Connection::TCP(stream)) + } + } + } +} + +pub enum Connection { + UDP(Arc<UdpSocket>, SocketAddr, [u8; 512]), + TCP(TcpStream), +} + +impl Connection { + pub async fn read_packet(&mut self) -> Result<Packet> { + let data = self.read().await?; + let mut packet_buffer = PacketBuffer::new(data); + + let packet = Packet::from_buffer(&mut packet_buffer)?; + Ok(packet) + } + + pub async fn write_packet(self, mut packet: Packet) -> Result<()> { + let mut packet_buffer = PacketBuffer::new(Vec::new()); + packet.write(&mut packet_buffer)?; + + self.write(packet_buffer.buf).await?; + Ok(()) + } + + pub async fn request_packet(&self, mut packet: Packet, dest: (IpAddr, u16)) -> Result<Packet> { + let mut packet_buffer = PacketBuffer::new(Vec::new()); + packet.write(&mut packet_buffer)?; + + let data = self.request(packet_buffer.buf, dest).await?; + let mut packet_buffer = PacketBuffer::new(data); + + let packet = Packet::from_buffer(&mut packet_buffer)?; + Ok(packet) + } + + async fn read(&mut self) -> Result<Vec<u8>> { + trace!("Reading DNS packet"); + match self { + Self::UDP(_, _, src) => Ok(Vec::from(*src)), + Self::TCP(stream) => { + let size = stream.read_u16().await?; + let mut buf = Vec::with_capacity(size as usize); + stream.read_buf(&mut buf).await?; + Ok(buf) + } + } + } + + async fn write(self, mut buf: Vec<u8>) -> Result<()> { + trace!("Returning DNS packet"); + match self { + Self::UDP(socket, addr, _) => { + if buf.len() > 512 { + buf[2] = buf[2] | 0x03; + socket.send_to(&buf[0..512], addr).await?; + } else { + socket.send_to(&buf, addr).await?; + } + Ok(()) + } + Self::TCP(mut stream) => { + stream.write_u16(buf.len() as u16).await?; + stream.write(&buf[0..buf.len()]).await?; + Ok(()) + } + } + } + + async fn request(&self, buf: Vec<u8>, dest: (IpAddr, u16)) -> Result<Vec<u8>> { + match self { + Self::UDP(_socket, _addr, _src) => { + let local_addr = "[::]:0".parse::<SocketAddr>()?; + let socket = UdpSocket::bind(local_addr).await?; + socket.send_to(&buf, dest).await?; + + let mut buf = [0; 512]; + socket.recv_from(&mut buf).await?; + + Ok(Vec::from(buf)) + } + Self::TCP(_stream) => { + let mut stream = TcpStream::connect(dest).await?; + stream.write_u16((buf.len()) as u16).await?; + stream.write_all(&buf[0..buf.len()]).await?; + + stream.readable().await?; + let size = stream.read_u16().await?; + let mut buf = Vec::with_capacity(size as usize); + stream.read_buf(&mut buf).await?; + + Ok(buf) + } + } + } + + // fn pb(buf: &[u8]) { + // for i in 0..buf.len() { + // print!("{:02X?} ", buf[i]); + // } + // println!(""); + // } +} diff --git a/src/server/mod.rs b/src/server/mod.rs new file mode 100644 index 0000000..25076ef --- /dev/null +++ b/src/server/mod.rs @@ -0,0 +1,3 @@ +mod binding; +mod resolver; +pub mod server; diff --git a/src/server/resolver.rs b/src/server/resolver.rs new file mode 100644 index 0000000..464620c --- /dev/null +++ b/src/server/resolver.rs @@ -0,0 +1,165 @@ +use super::binding::Connection; +use crate::{ + config::Config, + packet::{ + query::QueryType, question::DnsQuestion, result::ResultCode, Packet, + Result, + }, get_time, +}; +use async_recursion::async_recursion; +use moka::future::Cache; +use std::{net::IpAddr, sync::Arc, time::Duration}; +use tracing::{error, trace}; + +pub struct Resolver { + request_id: u16, + connection: Connection, + config: Arc<Config>, + cache: Cache<DnsQuestion, (Packet, u64)>, +} + +impl Resolver { + pub fn new( + request_id: u16, + connection: Connection, + config: Arc<Config>, + cache: Cache<DnsQuestion, (Packet, u64)>, + ) -> Self { + Self { + request_id, + connection, + config, + cache, + } + } + + async fn lookup_cache(&mut self, qname: &str, qtype: QueryType) -> Option<Packet> { + let question = DnsQuestion::new(qname.to_string(), qtype); + let Some((packet, date)) = self.cache.get(&question) else { + return None + }; + + let now = get_time(); + let diff = Duration::from_millis(now - date).as_secs() as u32; + + for answer in &packet.answers { + let ttl = answer.get_ttl(); + if diff > ttl { + self.cache.invalidate(&question).await; + return None + } + } + + trace!("Found cached value for {qtype:?} {qname}"); + + Some(packet) + } + + async fn lookup(&mut self, qname: &str, qtype: QueryType, server: (IpAddr, u16)) -> Packet { + let mut packet = Packet::new(); + + packet.header.id = self.request_id; + packet.header.questions = 1; + packet.header.recursion_desired = true; + packet + .questions + .push(DnsQuestion::new(qname.to_string(), qtype)); + + let packet = match self.connection.request_packet(packet, server).await { + Ok(packet) => packet, + Err(e) => { + error!("Failed to complete nameserver request: {e}"); + let mut packet = Packet::new(); + packet.header.rescode = ResultCode::SERVFAIL; + packet + } + }; + + packet + } + + #[async_recursion] + async fn recursive_lookup(&mut self, qname: &str, qtype: QueryType) -> Packet { + let question = DnsQuestion::new(qname.to_string(), qtype); + let mut ns = self.config.get_fallback_ns().clone(); + + if let Some(packet) = self.lookup_cache(qname, qtype).await { return packet } + + loop { + trace!("Attempting lookup of {qtype:?} {qname} with ns {ns}"); + + let ns_copy = ns; + + let server = (ns_copy, 53); + let response = self.lookup(qname, qtype, server).await; + + if !response.answers.is_empty() && response.header.rescode == ResultCode::NOERROR { + self.cache.insert(question, (response.clone(), get_time())).await; + return response; + } + + if response.header.rescode == ResultCode::NXDOMAIN { + self.cache.insert(question, (response.clone(), get_time())).await; + return response; + } + + if let Some(new_ns) = response.get_resolved_ns(qname) { + ns = new_ns; + continue; + } + + let new_ns_name = match response.get_unresolved_ns(qname) { + Some(x) => x, + None => { + self.cache.insert(question, (response.clone(), get_time())).await; + return response + }, + }; + + let recursive_response = self.recursive_lookup(new_ns_name, QueryType::A).await; + + if let Some(new_ns) = recursive_response.get_random_a() { + ns = new_ns; + } else { + self.cache.insert(question, (response.clone(), get_time())).await; + return response; + } + } + } + + pub async fn handle_query(mut self) -> Result<()> { + let mut request = self.connection.read_packet().await?; + + let mut packet = Packet::new(); + packet.header.id = request.header.id; + packet.header.recursion_desired = true; + packet.header.recursion_available = true; + packet.header.response = true; + + if let Some(question) = request.questions.pop() { + trace!("Received query: {question:?}"); + + let result = self.recursive_lookup(&question.name, question.qtype).await; + packet.questions.push(question.clone()); + packet.header.rescode = result.header.rescode; + + for rec in result.answers { + trace!("Answer: {rec:?}"); + packet.answers.push(rec); + } + for rec in result.authorities { + trace!("Authority: {rec:?}"); + packet.authorities.push(rec); + } + for rec in result.resources { + trace!("Resource: {rec:?}"); + packet.resources.push(rec); + } + } else { + packet.header.rescode = ResultCode::FORMERR; + } + + self.connection.write_packet(packet).await?; + Ok(()) + } +} diff --git a/src/server/server.rs b/src/server/server.rs new file mode 100644 index 0000000..e006bb1 --- /dev/null +++ b/src/server/server.rs @@ -0,0 +1,73 @@ +use moka::future::Cache; +use std::net::SocketAddr; +use std::sync::Arc; +use std::time::Duration; +use tokio::task::JoinHandle; +use tracing::{error, info}; + +use crate::config::Config; +use crate::packet::question::DnsQuestion; +use crate::packet::{Result, Packet}; + +use super::binding::Binding; +use super::resolver::Resolver; + +pub struct Server { + addr: SocketAddr, + config: Arc<Config>, + cache: Cache<DnsQuestion, (Packet, u64)>, +} + +impl Server { + pub async fn new(config: Config) -> Result<Self> { + let addr = format!("[::]:{}", config.get_port()).parse::<SocketAddr>()?; + let cache = Cache::builder() + .time_to_live(Duration::from_secs(60 * 60)) + .max_capacity(1_000) + .build(); + Ok(Self { + addr, + config: Arc::new(config), + cache, + }) + } + + pub async fn run(&self) -> Result<()> { + let tcp = Binding::tcp(self.addr).await?; + let tcp_handle = self.listen(tcp); + + let udp = Binding::udp(self.addr).await?; + let udp_handle = self.listen(udp); + + info!("Fallback DNS Server is set to: {:?}", self.config.get_fallback_ns()); + info!("Listening for TCP and UDP traffic on [::]:{}", self.config.get_port()); + + tokio::join!(tcp_handle) + .0 + .expect("Failed to join tcp thread"); + tokio::join!(udp_handle) + .0 + .expect("Failed to join udp thread"); + Ok(()) + } + + fn listen(&self, mut binding: Binding) -> JoinHandle<()> { + let config = self.config.clone(); + let cache = self.cache.clone(); + tokio::spawn(async move { + let mut id = 0; + loop { + let Ok(connection) = binding.connect().await else { continue }; + info!("Received request on {}", binding.name()); + + let resolver = Resolver::new(id, connection, config.clone(), cache.clone()); + + if let Err(err) = resolver.handle_query().await { + error!("{} request {} failed: {:?}", binding.name(), id, err); + }; + + id += 1; + } + }) + } +} |