diff options
Diffstat (limited to 'packet')
-rw-r--r-- | packet/Cargo.lock | 7 | ||||
-rw-r--r-- | packet/Cargo.toml | 6 | ||||
-rw-r--r-- | packet/src/buffer.rs | 192 | ||||
-rw-r--r-- | packet/src/header.rs | 102 | ||||
-rw-r--r-- | packet/src/lib.rs | 156 | ||||
-rw-r--r-- | packet/src/query.rs | 33 | ||||
-rw-r--r-- | packet/src/question.rs | 31 | ||||
-rw-r--r-- | packet/src/record.rs | 227 | ||||
-rw-r--r-- | packet/src/result.rs | 22 |
9 files changed, 0 insertions, 776 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/buffer.rs b/packet/src/buffer.rs deleted file mode 100644 index 4394705..0000000 --- a/packet/src/buffer.rs +++ /dev/null @@ -1,192 +0,0 @@ -use super::Result; - -pub struct PacketBuffer { - pub buf: [u8; 512], - pub pos: usize, -} - -impl PacketBuffer { - pub fn new() -> Self { - Self { - buf: [0; 512], - pos: 0, - } - } - - pub fn pos(&self) -> usize { - self.pos - } - - pub fn step(&mut self, steps: usize) -> Result<()> { - self.pos += steps; - - Ok(()) - } - - pub fn seek(&mut self, pos: usize) -> Result<()> { - self.pos = pos; - - Ok(()) - } - - pub fn read(&mut self) -> Result<u8> { - if self.pos >= 512 { - return Err("End of buffer".into()); - } - let res = self.buf[self.pos]; - self.pos += 1; - - Ok(res) - } - - pub fn get(&mut self, pos: usize) -> Result<u8> { - if pos >= 512 { - return Err("End of buffer".into()); - } - 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()); - } - Ok(&self.buf[start..start + len]) - } - - pub fn read_u16(&mut self) -> Result<u16> { - let res = ((self.read()? as u16) << 8) | (self.read()? as u16); - - Ok(res) - } - - pub fn read_u32(&mut self) -> Result<u32> { - let res = ((self.read()? as u32) << 24) - | ((self.read()? as u32) << 16) - | ((self.read()? as u32) << 8) - | (self.read()? as u32); - - Ok(res) - } - - pub fn read_qname(&mut self, outstr: &mut String) -> Result<()> { - let mut pos = self.pos(); - let mut jumped = false; - - let mut delim = ""; - let max_jumps = 5; - let mut jumps_performed = 0; - loop { - // Dns Packets are untrusted data, so we need to be paranoid. Someone - // can craft a packet with a cycle in the jump instructions. This guards - // against such packets. - if jumps_performed > max_jumps { - return Err(format!("Limit of {max_jumps} jumps exceeded").into()); - } - - 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)?; - } - - let b2 = self.get(pos + 1)? as u16; - let offset = (((len as u16) ^ 0xC0) << 8) | b2; - pos = offset as usize; - jumped = true; - jumps_performed += 1; - continue; - } - - pos += 1; - - // Names are terminated by an empty label of length 0 - if len == 0 { - break; - } - - outstr.push_str(delim); - - let str_buffer = self.get_range(pos, len as usize)?; - outstr.push_str(&String::from_utf8_lossy(str_buffer).to_lowercase()); - - delim = "."; - - pos += len as usize; - } - - if !jumped { - self.seek(pos)?; - } - - Ok(()) - } - - pub fn write(&mut self, val: u8) -> Result<()> { - if self.pos >= 512 { - return Err("End of buffer".into()); - } - self.buf[self.pos] = val; - self.pos += 1; - Ok(()) - } - - pub fn write_u8(&mut self, val: u8) -> Result<()> { - self.write(val)?; - - Ok(()) - } - - pub fn write_u16(&mut self, val: u16) -> Result<()> { - self.write((val >> 8) as u8)?; - self.write((val & 0xFF) as u8)?; - - Ok(()) - } - - pub fn write_u32(&mut self, val: u32) -> Result<()> { - self.write(((val >> 24) & 0xFF) as u8)?; - self.write(((val >> 16) & 0xFF) as u8)?; - self.write(((val >> 8) & 0xFF) as u8)?; - self.write((val & 0xFF) as u8)?; - - Ok(()) - } - - 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() { - self.write_u8(*b)?; - } - } - - self.write_u8(0)?; - - Ok(()) - } - - pub fn set(&mut self, pos: usize, val: u8) -> Result<()> { - self.buf[pos] = val; - - Ok(()) - } - - pub fn set_u16(&mut self, pos: usize, val: u16) -> Result<()> { - self.set(pos, (val >> 8) as u8)?; - self.set(pos + 1, (val & 0xFF) as u8)?; - - Ok(()) - } -}
\ No newline at end of file diff --git a/packet/src/header.rs b/packet/src/header.rs deleted file mode 100644 index b2bf1a1..0000000 --- a/packet/src/header.rs +++ /dev/null @@ -1,102 +0,0 @@ -use super::{buffer::PacketBuffer, Result, result::ResultCode}; - -#[derive(Clone, Debug)] -pub struct DnsHeader { - pub id: u16, // 16 bits - - pub recursion_desired: bool, // 1 bit - pub truncated_message: bool, // 1 bit - pub authoritative_answer: bool, // 1 bit - pub opcode: u8, // 4 bits - pub response: bool, // 1 bit - - pub rescode: ResultCode, // 4 bits - pub checking_disabled: bool, // 1 bit - pub authed_data: bool, // 1 bit - pub z: bool, // 1 bit - pub recursion_available: bool, // 1 bit - - pub questions: u16, // 16 bits - pub answers: u16, // 16 bits - pub authoritative_entries: u16, // 16 bits - pub resource_entries: u16, // 16 bits -} - -impl DnsHeader { - pub fn new() -> Self { - Self { - id: 0, - - recursion_desired: false, - truncated_message: false, - authoritative_answer: false, - opcode: 0, - response: false, - - rescode: ResultCode::NOERROR, - checking_disabled: false, - authed_data: false, - z: false, - recursion_available: false, - - questions: 0, - answers: 0, - authoritative_entries: 0, - resource_entries: 0, - } - } - - 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; - self.recursion_desired = (a & (1 << 0)) > 0; - self.truncated_message = (a & (1 << 1)) > 0; - self.authoritative_answer = (a & (1 << 2)) > 0; - self.opcode = (a >> 3) & 0x0F; - self.response = (a & (1 << 7)) > 0; - - self.rescode = ResultCode::from_num(b & 0x0F); - self.checking_disabled = (b & (1 << 4)) > 0; - self.authed_data = (b & (1 << 5)) > 0; - self.z = (b & (1 << 6)) > 0; - self.recursion_available = (b & (1 << 7)) > 0; - - self.questions = buffer.read_u16()?; - self.answers = buffer.read_u16()?; - self.authoritative_entries = buffer.read_u16()?; - self.resource_entries = buffer.read_u16()?; - - // Return the constant header size - Ok(()) - } - - pub fn write(&self, buffer: &mut PacketBuffer) -> Result<()> { - buffer.write_u16(self.id)?; - - buffer.write_u8( - (self.recursion_desired as u8) - | ((self.truncated_message as u8) << 1) - | ((self.authoritative_answer as u8) << 2) - | (self.opcode << 3) - | ((self.response as u8) << 7), - )?; - - buffer.write_u8( - (self.rescode as u8) - | ((self.checking_disabled as u8) << 4) - | ((self.authed_data as u8) << 5) - | ((self.z as u8) << 6) - | ((self.recursion_available as u8) << 7), - )?; - - buffer.write_u16(self.questions)?; - buffer.write_u16(self.answers)?; - buffer.write_u16(self.authoritative_entries)?; - buffer.write_u16(self.resource_entries)?; - - Ok(()) - } -}
\ No newline at end of file diff --git a/packet/src/lib.rs b/packet/src/lib.rs deleted file mode 100644 index c7a8eb9..0000000 --- a/packet/src/lib.rs +++ /dev/null @@ -1,156 +0,0 @@ -use std::net::IpAddr; - -use self::{header::DnsHeader, question::DnsQuestion, record::DnsRecord, query::QueryType}; - -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; - -#[derive(Clone, Debug)] -pub struct Packet { - pub header: DnsHeader, - pub questions: Vec<DnsQuestion>, - pub answers: Vec<DnsRecord>, - pub authorities: Vec<DnsRecord>, - 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 { - header: DnsHeader::new(), - questions: Vec::new(), - answers: Vec::new(), - authorities: Vec::new(), - resources: Vec::new(), - } - } - - pub fn from_buffer(buffer: &mut PacketBuffer) -> Result<Self> { - let mut result = Self::new(); - result.header.read(buffer)?; - - for _ in 0..result.header.questions { - let mut question = DnsQuestion::new("".to_string(), QueryType::UNKNOWN(0)); - question.read(buffer)?; - result.questions.push(question); - } - - for _ in 0..result.header.answers { - let rec = DnsRecord::read(buffer)?; - result.answers.push(rec); - } - for _ in 0..result.header.authoritative_entries { - let rec = DnsRecord::read(buffer)?; - result.authorities.push(rec); - } - for _ in 0..result.header.resource_entries { - let rec = DnsRecord::read(buffer)?; - result.resources.push(rec); - } - - Ok(result) - } - - pub fn write(&mut self, buffer: &mut PacketBuffer) -> Result<()> { - self.header.questions = self.questions.len() as u16; - self.header.answers = self.answers.len() as u16; - self.header.authoritative_entries = self.authorities.len() as u16; - self.header.resource_entries = self.resources.len() as u16; - - self.header.write(buffer)?; - - for question in &self.questions { - question.write(buffer)?; - } - for rec in &self.answers { - rec.write(buffer)?; - } - for rec in &self.authorities { - rec.write(buffer)?; - } - for rec in &self.resources { - rec.write(buffer)?; - } - - 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() - .filter_map(|record| match record { - DnsRecord::A { addr, .. } => Some(IpAddr::V4(*addr)), - DnsRecord::AAAA { addr, .. } => Some(IpAddr::V6(*addr)), - _ => None, - }) - .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)), - _ => 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() - } -}
\ No newline at end of file diff --git a/packet/src/query.rs b/packet/src/query.rs deleted file mode 100644 index 8804d15..0000000 --- a/packet/src/query.rs +++ /dev/null @@ -1,33 +0,0 @@ -#[derive(PartialEq, Eq, Debug, Clone, Hash, Copy)] -pub enum QueryType { - UNKNOWN(u16), - A, // 1 - NS, // 2 - CNAME, // 5 - MX, // 15 - AAAA, // 28 -} - -impl QueryType { - pub fn to_num(&self) -> u16 { - match *self { - Self::UNKNOWN(x) => x, - Self::A => 1, - Self::NS => 2, - Self::CNAME => 5, - Self::MX => 15, - Self::AAAA => 28, - } - } - - pub fn from_num(num: u16) -> Self { - match num { - 1 => Self::A, - 2 => Self::NS, - 5 => Self::CNAME, - 15 => Self::MX, - 28 => Self::AAAA, - _ => Self::UNKNOWN(num), - } - } -}
\ No newline at end of file diff --git a/packet/src/question.rs b/packet/src/question.rs deleted file mode 100644 index 076de00..0000000 --- a/packet/src/question.rs +++ /dev/null @@ -1,31 +0,0 @@ -use super::{buffer::PacketBuffer, query::QueryType, Result}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct DnsQuestion { - pub name: String, - pub qtype: QueryType, -} - -impl DnsQuestion { - pub fn new(name: String, qtype: QueryType) -> Self { - Self { name, qtype } - } - - pub fn read(&mut self, buffer: &mut PacketBuffer) -> Result<()> { - buffer.read_qname(&mut self.name)?; - self.qtype = QueryType::from_num(buffer.read_u16()?); // qtype - let _ = buffer.read_u16()?; // class - - Ok(()) - } - - pub fn write(&self, buffer: &mut PacketBuffer) -> Result<()> { - buffer.write_qname(&self.name)?; - - let typenum = self.qtype.to_num(); - buffer.write_u16(typenum)?; - buffer.write_u16(1)?; - - Ok(()) - } -}
\ No newline at end of file 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/result.rs b/packet/src/result.rs deleted file mode 100644 index f33bd7d..0000000 --- a/packet/src/result.rs +++ /dev/null @@ -1,22 +0,0 @@ -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum ResultCode { - NOERROR = 0, - FORMERR = 1, - SERVFAIL = 2, - NXDOMAIN = 3, - NOTIMP = 4, - REFUSED = 5, -} - -impl ResultCode { - pub fn from_num(num: u8) -> Self { - match num { - 1 => Self::FORMERR, - 2 => Self::SERVFAIL, - 3 => Self::NXDOMAIN, - 4 => Self::NOTIMP, - 5 => Self::REFUSED, - 0 | _ => Self::NOERROR, - } - } -}
\ No newline at end of file |