diff options
Diffstat (limited to '')
-rw-r--r-- | packet/Cargo.lock | 7 | ||||
-rw-r--r-- | packet/Cargo.toml | 6 | ||||
-rw-r--r-- | packet/src/record.rs | 227 | ||||
-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/result.rs (renamed from packet/src/result.rs) | 2 |
9 files changed, 112 insertions, 317 deletions
diff --git a/packet/Cargo.lock b/packet/Cargo.lock deleted file mode 100644 index 3f0df66..0000000 --- a/packet/Cargo.lock +++ /dev/null @@ -1,7 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "packet" -version = "0.1.0" diff --git a/packet/Cargo.toml b/packet/Cargo.toml deleted file mode 100644 index 2e3797f..0000000 --- a/packet/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "packet" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/packet/src/record.rs b/packet/src/record.rs deleted file mode 100644 index c7ff1ac..0000000 --- a/packet/src/record.rs +++ /dev/null @@ -1,227 +0,0 @@ -use std::net::{Ipv4Addr, Ipv6Addr}; - -use super::{query::QueryType, buffer::PacketBuffer, 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 - MX { - domain: String, - priority: u16, - host: String, - ttl: u32, - }, // 15 - AAAA { - domain: String, - addr: Ipv6Addr, - ttl: u32, - }, // 28 -} - -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()?; - - 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::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::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(); - - 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::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::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::UNKNOWN { .. } => { - println!("Skipping record: {self:?}"); - } - } - - Ok(buffer.pos() - start_pos) - } -}
\ No newline at end of file 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/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 +} |