#![allow(dead_code)] use bitflags::bitflags; use byteorder::{ByteOrder, NetworkEndian}; use core::iter; use core::iter::Iterator; use super::{Error, Result}; #[cfg(feature = "proto-ipv4")] use crate::wire::Ipv4Address; #[cfg(feature = "proto-ipv6")] use crate::wire::Ipv6Address; enum_with_unknown! { /// DNS OpCodes pub enum Opcode(u8) { Query = 0x00, Status = 0x01, } } enum_with_unknown! { /// DNS OpCodes pub enum Rcode(u8) { NoError = 0x00, FormErr = 0x01, ServFail = 0x02, NXDomain = 0x03, NotImp = 0x04, Refused = 0x05, YXDomain = 0x06, YXRRSet = 0x07, NXRRSet = 0x08, NotAuth = 0x09, NotZone = 0x0a, } } enum_with_unknown! { /// DNS record types pub enum Type(u16) { A = 0x0001, Ns = 0x0002, Cname = 0x0005, Soa = 0x0006, Aaaa = 0x001c, } } bitflags! { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Flags: u16 { const RESPONSE = 0b1000_0000_0000_0000; const AUTHORITATIVE = 0b0000_0100_0000_0000; const TRUNCATED = 0b0000_0010_0000_0000; const RECURSION_DESIRED = 0b0000_0001_0000_0000; const RECURSION_AVAILABLE = 0b0000_0000_1000_0000; const AUTHENTIC_DATA = 0b0000_0000_0010_0000; const CHECK_DISABLED = 0b0000_0000_0001_0000; } } mod field { use crate::wire::field::*; pub const ID: Field = 0..2; pub const FLAGS: Field = 2..4; pub const QDCOUNT: Field = 4..6; pub const ANCOUNT: Field = 6..8; pub const NSCOUNT: Field = 8..10; pub const ARCOUNT: Field = 10..12; pub const HEADER_END: usize = 12; } // DNS class IN (Internet) const CLASS_IN: u16 = 1; /// A read/write wrapper around a DNS packet buffer. #[derive(Debug, PartialEq, Eq)] pub struct Packet> { buffer: T, } impl> Packet { /// Imbue a raw octet buffer with DNS packet structure. pub const fn new_unchecked(buffer: T) -> Packet { Packet { buffer } } /// Shorthand for a combination of [new_unchecked] and [check_len]. /// /// [new_unchecked]: #method.new_unchecked /// [check_len]: #method.check_len pub fn new_checked(buffer: T) -> Result> { let packet = Self::new_unchecked(buffer); packet.check_len()?; Ok(packet) } /// Ensure that no accessor method will panic if called. /// Returns `Err(Error)` if the buffer is smaller than /// the header length. pub fn check_len(&self) -> Result<()> { let len = self.buffer.as_ref().len(); if len < field::HEADER_END { Err(Error) } else { Ok(()) } } /// Consume the packet, returning the underlying buffer. pub fn into_inner(self) -> T { self.buffer } pub fn payload(&self) -> &[u8] { &self.buffer.as_ref()[field::HEADER_END..] } pub fn transaction_id(&self) -> u16 { let field = &self.buffer.as_ref()[field::ID]; NetworkEndian::read_u16(field) } pub fn flags(&self) -> Flags { let field = &self.buffer.as_ref()[field::FLAGS]; Flags::from_bits_truncate(NetworkEndian::read_u16(field)) } pub fn opcode(&self) -> Opcode { let field = &self.buffer.as_ref()[field::FLAGS]; let flags = NetworkEndian::read_u16(field); Opcode::from((flags >> 11 & 0xF) as u8) } pub fn rcode(&self) -> Rcode { let field = &self.buffer.as_ref()[field::FLAGS]; let flags = NetworkEndian::read_u16(field); Rcode::from((flags & 0xF) as u8) } pub fn question_count(&self) -> u16 { let field = &self.buffer.as_ref()[field::QDCOUNT]; NetworkEndian::read_u16(field) } pub fn answer_record_count(&self) -> u16 { let field = &self.buffer.as_ref()[field::ANCOUNT]; NetworkEndian::read_u16(field) } pub fn authority_record_count(&self) -> u16 { let field = &self.buffer.as_ref()[field::NSCOUNT]; NetworkEndian::read_u16(field) } pub fn additional_record_count(&self) -> u16 { let field = &self.buffer.as_ref()[field::ARCOUNT]; NetworkEndian::read_u16(field) } /// Parse part of a name from `bytes`, following pointers if any. pub fn parse_name<'a>(&'a self, mut bytes: &'a [u8]) -> impl Iterator> { let mut packet = self.buffer.as_ref(); iter::from_fn(move || loop { if bytes.is_empty() { return Some(Err(Error)); } match bytes[0] { 0x00 => return None, x if x & 0xC0 == 0x00 => { let len = (x & 0x3F) as usize; if bytes.len() < 1 + len { return Some(Err(Error)); } let label = &bytes[1..1 + len]; bytes = &bytes[1 + len..]; return Some(Ok(label)); } x if x & 0xC0 == 0xC0 => { if bytes.len() < 2 { return Some(Err(Error)); } let y = bytes[1]; let ptr = ((x & 0x3F) as usize) << 8 | (y as usize); if packet.len() <= ptr { return Some(Err(Error)); } // RFC1035 says: "In this scheme, an entire domain name or a list of labels at // the end of a domain name is replaced with a pointer to a ***prior*** occurrence // of the same name. // // Is it unclear if this means the pointer MUST point backwards in the packet or not. Either way, // pointers that don't point backwards are never seen in the fields, so use this to check that // there are no pointer loops. // Split packet into parts before and after `ptr`. // parse the part after, keep only the part before in `packet`. This ensure we never // parse the same byte twice, therefore eliminating pointer loops. bytes = &packet[ptr..]; packet = &packet[..ptr]; } _ => return Some(Err(Error)), } }) } } impl + AsMut<[u8]>> Packet { pub fn payload_mut(&mut self) -> &mut [u8] { let data = self.buffer.as_mut(); &mut data[field::HEADER_END..] } pub fn set_transaction_id(&mut self, val: u16) { let field = &mut self.buffer.as_mut()[field::ID]; NetworkEndian::write_u16(field, val) } pub fn set_flags(&mut self, val: Flags) { let field = &mut self.buffer.as_mut()[field::FLAGS]; let mask = Flags::all().bits; let old = NetworkEndian::read_u16(field); NetworkEndian::write_u16(field, (old & !mask) | val.bits()); } pub fn set_opcode(&mut self, val: Opcode) { let field = &mut self.buffer.as_mut()[field::FLAGS]; let mask = 0x3800; let val: u8 = val.into(); let val = (val as u16) << 11; let old = NetworkEndian::read_u16(field); NetworkEndian::write_u16(field, (old & !mask) | val); } pub fn set_question_count(&mut self, val: u16) { let field = &mut self.buffer.as_mut()[field::QDCOUNT]; NetworkEndian::write_u16(field, val) } pub fn set_answer_record_count(&mut self, val: u16) { let field = &mut self.buffer.as_mut()[field::ANCOUNT]; NetworkEndian::write_u16(field, val) } pub fn set_authority_record_count(&mut self, val: u16) { let field = &mut self.buffer.as_mut()[field::NSCOUNT]; NetworkEndian::write_u16(field, val) } pub fn set_additional_record_count(&mut self, val: u16) { let field = &mut self.buffer.as_mut()[field::ARCOUNT]; NetworkEndian::write_u16(field, val) } } /// Parse part of a name from `bytes`, not following pointers. /// Returns the unused part of `bytes`, and the pointer offset if the sequence ends with a pointer. fn parse_name_part<'a>( mut bytes: &'a [u8], mut f: impl FnMut(&'a [u8]), ) -> Result<(&'a [u8], Option)> { loop { let x = *bytes.first().ok_or(Error)?; bytes = &bytes[1..]; match x { 0x00 => return Ok((bytes, None)), x if x & 0xC0 == 0x00 => { let len = (x & 0x3F) as usize; let label = bytes.get(..len).ok_or(Error)?; bytes = &bytes[len..]; f(label); } x if x & 0xC0 == 0xC0 => { let y = *bytes.first().ok_or(Error)?; bytes = &bytes[1..]; let ptr = ((x & 0x3F) as usize) << 8 | (y as usize); return Ok((bytes, Some(ptr))); } _ => return Err(Error), } } } #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Question<'a> { pub name: &'a [u8], pub type_: Type, } impl<'a> Question<'a> { pub fn parse(buffer: &'a [u8]) -> Result<(&'a [u8], Question<'a>)> { let (rest, _) = parse_name_part(buffer, |_| ())?; let name = &buffer[..buffer.len() - rest.len()]; if rest.len() < 4 { return Err(Error); } let type_ = NetworkEndian::read_u16(&rest[0..2]).into(); let class = NetworkEndian::read_u16(&rest[2..4]); let rest = &rest[4..]; if class != CLASS_IN { return Err(Error); } Ok((rest, Question { name, type_ })) } /// Return the length of a packet that will be emitted from this high-level representation. pub const fn buffer_len(&self) -> usize { self.name.len() + 4 } /// Emit a high-level representation into a DNS packet. pub fn emit(&self, packet: &mut [u8]) { packet[..self.name.len()].copy_from_slice(self.name); let rest = &mut packet[self.name.len()..]; NetworkEndian::write_u16(&mut rest[0..2], self.type_.into()); NetworkEndian::write_u16(&mut rest[2..4], CLASS_IN); } } #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Record<'a> { pub name: &'a [u8], pub ttl: u32, pub data: RecordData<'a>, } impl<'a> RecordData<'a> { pub fn parse(type_: Type, data: &'a [u8]) -> Result> { match type_ { #[cfg(feature = "proto-ipv4")] Type::A => { if data.len() != 4 { return Err(Error); } Ok(RecordData::A(Ipv4Address::from_bytes(data))) } #[cfg(feature = "proto-ipv6")] Type::Aaaa => { if data.len() != 16 { return Err(Error); } Ok(RecordData::Aaaa(Ipv6Address::from_bytes(data))) } Type::Cname => Ok(RecordData::Cname(data)), x => Ok(RecordData::Other(x, data)), } } } #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum RecordData<'a> { #[cfg(feature = "proto-ipv4")] A(Ipv4Address), #[cfg(feature = "proto-ipv6")] Aaaa(Ipv6Address), Cname(&'a [u8]), Other(Type, &'a [u8]), } impl<'a> Record<'a> { pub fn parse(buffer: &'a [u8]) -> Result<(&'a [u8], Record<'a>)> { let (rest, _) = parse_name_part(buffer, |_| ())?; let name = &buffer[..buffer.len() - rest.len()]; if rest.len() < 10 { return Err(Error); } let type_ = NetworkEndian::read_u16(&rest[0..2]).into(); let class = NetworkEndian::read_u16(&rest[2..4]); let ttl = NetworkEndian::read_u32(&rest[4..8]); let len = NetworkEndian::read_u16(&rest[8..10]) as usize; let rest = &rest[10..]; if class != CLASS_IN { return Err(Error); } let data = rest.get(..len).ok_or(Error)?; let rest = &rest[len..]; Ok(( rest, Record { name, ttl, data: RecordData::parse(type_, data)?, }, )) } } /// High-level DNS packet representation. /// /// Currently only supports query packets. #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Repr<'a> { pub transaction_id: u16, pub opcode: Opcode, pub flags: Flags, pub question: Question<'a>, } impl<'a> Repr<'a> { /// Return the length of a packet that will be emitted from this high-level representation. pub const fn buffer_len(&self) -> usize { field::HEADER_END + self.question.buffer_len() } /// Emit a high-level representation into a DNS packet. pub fn emit(&self, packet: &mut Packet<&mut T>) where T: AsRef<[u8]> + AsMut<[u8]>, { packet.set_transaction_id(self.transaction_id); packet.set_flags(self.flags); packet.set_opcode(self.opcode); packet.set_question_count(1); packet.set_answer_record_count(0); packet.set_authority_record_count(0); packet.set_additional_record_count(0); self.question.emit(packet.payload_mut()) } } #[cfg(feature = "proto-ipv4")] // tests assume ipv4 #[cfg(test)] mod test { use super::*; use std::vec::Vec; #[test] fn test_parse_name() { let bytes = &[ 0x78, 0x6c, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x08, 0x66, 0x61, 0x63, 0x65, 0x62, 0x6f, 0x6f, 0x6b, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x05, 0xf3, 0x00, 0x11, 0x09, 0x73, 0x74, 0x61, 0x72, 0x2d, 0x6d, 0x69, 0x6e, 0x69, 0x04, 0x63, 0x31, 0x30, 0x72, 0xc0, 0x10, 0xc0, 0x2e, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x1f, 0x0d, 0x53, 0x24, ]; let packet = Packet::new_unchecked(bytes); let name_vec = |bytes| { let mut v = Vec::new(); packet .parse_name(bytes) .try_for_each(|label| label.map(|label| v.push(label))) .map(|_| v) }; //assert_eq!(parse_name_len(bytes, 0x0c), Ok(18)); assert_eq!( name_vec(&bytes[0x0c..]), Ok(vec![&b"www"[..], &b"facebook"[..], &b"com"[..]]) ); //assert_eq!(parse_name_len(bytes, 0x22), Ok(2)); assert_eq!( name_vec(&bytes[0x22..]), Ok(vec![&b"www"[..], &b"facebook"[..], &b"com"[..]]) ); //assert_eq!(parse_name_len(bytes, 0x2e), Ok(17)); assert_eq!( name_vec(&bytes[0x2e..]), Ok(vec![ &b"star-mini"[..], &b"c10r"[..], &b"facebook"[..], &b"com"[..] ]) ); //assert_eq!(parse_name_len(bytes, 0x3f), Ok(2)); assert_eq!( name_vec(&bytes[0x3f..]), Ok(vec![ &b"star-mini"[..], &b"c10r"[..], &b"facebook"[..], &b"com"[..] ]) ); } struct Parsed<'a> { packet: Packet<&'a [u8]>, questions: Vec>, answers: Vec>, authorities: Vec>, additionals: Vec>, } impl<'a> Parsed<'a> { fn parse(bytes: &'a [u8]) -> Result { let packet = Packet::new_unchecked(bytes); let mut questions = Vec::new(); let mut answers = Vec::new(); let mut authorities = Vec::new(); let mut additionals = Vec::new(); let mut payload = &bytes[12..]; for _ in 0..packet.question_count() { let (p, r) = Question::parse(payload)?; questions.push(r); payload = p; } for _ in 0..packet.answer_record_count() { let (p, r) = Record::parse(payload)?; answers.push(r); payload = p; } for _ in 0..packet.authority_record_count() { let (p, r) = Record::parse(payload)?; authorities.push(r); payload = p; } for _ in 0..packet.additional_record_count() { let (p, r) = Record::parse(payload)?; additionals.push(r); payload = p; } // Check that there are no bytes left assert_eq!(payload.len(), 0); Ok(Parsed { packet, questions, answers, authorities, additionals, }) } } #[test] fn test_parse_request() { let p = Parsed::parse(&[ 0x51, 0x84, 0x01, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, ]) .unwrap(); assert_eq!(p.packet.transaction_id(), 0x5184); assert_eq!( p.packet.flags(), Flags::RECURSION_DESIRED | Flags::AUTHENTIC_DATA ); assert_eq!(p.packet.opcode(), Opcode::Query); assert_eq!(p.packet.question_count(), 1); assert_eq!(p.packet.answer_record_count(), 0); assert_eq!(p.packet.authority_record_count(), 0); assert_eq!(p.packet.additional_record_count(), 0); assert_eq!(p.questions.len(), 1); assert_eq!( p.questions[0].name, &[0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00] ); assert_eq!(p.questions[0].type_, Type::A); assert_eq!(p.answers.len(), 0); assert_eq!(p.authorities.len(), 0); assert_eq!(p.additionals.len(), 0); } #[test] fn test_parse_response() { let p = Parsed::parse(&[ 0x51, 0x84, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xca, 0x00, 0x04, 0xac, 0xd9, 0xa8, 0xae, ]) .unwrap(); assert_eq!(p.packet.transaction_id(), 0x5184); assert_eq!( p.packet.flags(), Flags::RESPONSE | Flags::RECURSION_DESIRED | Flags::RECURSION_AVAILABLE ); assert_eq!(p.packet.opcode(), Opcode::Query); assert_eq!(p.packet.rcode(), Rcode::NoError); assert_eq!(p.packet.question_count(), 1); assert_eq!(p.packet.answer_record_count(), 1); assert_eq!(p.packet.authority_record_count(), 0); assert_eq!(p.packet.additional_record_count(), 0); assert_eq!( p.questions[0].name, &[0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00] ); assert_eq!(p.questions[0].type_, Type::A); assert_eq!(p.answers[0].name, &[0xc0, 0x0c]); assert_eq!(p.answers[0].ttl, 202); assert_eq!( p.answers[0].data, RecordData::A(Ipv4Address::new(0xac, 0xd9, 0xa8, 0xae)) ); } #[test] fn test_parse_response_multiple_a() { let p = Parsed::parse(&[ 0x4b, 0x9e, 0x81, 0x80, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x04, 0x0d, 0xe0, 0x77, 0x35, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x04, 0x0d, 0xe0, 0x77, 0x28, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x04, 0x0d, 0xe0, 0x77, 0x43, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x04, 0x0d, 0xe0, 0x77, 0x62, ]) .unwrap(); assert_eq!(p.packet.transaction_id(), 0x4b9e); assert_eq!( p.packet.flags(), Flags::RESPONSE | Flags::RECURSION_DESIRED | Flags::RECURSION_AVAILABLE ); assert_eq!(p.packet.opcode(), Opcode::Query); assert_eq!(p.packet.rcode(), Rcode::NoError); assert_eq!(p.packet.question_count(), 1); assert_eq!(p.packet.answer_record_count(), 4); assert_eq!(p.packet.authority_record_count(), 0); assert_eq!(p.packet.additional_record_count(), 0); assert_eq!( p.questions[0].name, &[ 0x09, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x03, 0x6f, 0x72, 0x67, 0x00 ] ); assert_eq!(p.questions[0].type_, Type::A); assert_eq!(p.answers[0].name, &[0xc0, 0x0c]); assert_eq!(p.answers[0].ttl, 9); assert_eq!( p.answers[0].data, RecordData::A(Ipv4Address::new(0x0d, 0xe0, 0x77, 0x35)) ); assert_eq!(p.answers[1].name, &[0xc0, 0x0c]); assert_eq!(p.answers[1].ttl, 9); assert_eq!( p.answers[1].data, RecordData::A(Ipv4Address::new(0x0d, 0xe0, 0x77, 0x28)) ); assert_eq!(p.answers[2].name, &[0xc0, 0x0c]); assert_eq!(p.answers[2].ttl, 9); assert_eq!( p.answers[2].data, RecordData::A(Ipv4Address::new(0x0d, 0xe0, 0x77, 0x43)) ); assert_eq!(p.answers[3].name, &[0xc0, 0x0c]); assert_eq!(p.answers[3].ttl, 9); assert_eq!( p.answers[3].data, RecordData::A(Ipv4Address::new(0x0d, 0xe0, 0x77, 0x62)) ); } #[test] fn test_parse_response_cname() { let p = Parsed::parse(&[ 0x78, 0x6c, 0x81, 0x80, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, 0x77, 0x77, 0x08, 0x66, 0x61, 0x63, 0x65, 0x62, 0x6f, 0x6f, 0x6b, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x05, 0xf3, 0x00, 0x11, 0x09, 0x73, 0x74, 0x61, 0x72, 0x2d, 0x6d, 0x69, 0x6e, 0x69, 0x04, 0x63, 0x31, 0x30, 0x72, 0xc0, 0x10, 0xc0, 0x2e, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x1f, 0x0d, 0x53, 0x24, ]) .unwrap(); assert_eq!(p.packet.transaction_id(), 0x786c); assert_eq!( p.packet.flags(), Flags::RESPONSE | Flags::RECURSION_DESIRED | Flags::RECURSION_AVAILABLE ); assert_eq!(p.packet.opcode(), Opcode::Query); assert_eq!(p.packet.rcode(), Rcode::NoError); assert_eq!(p.packet.question_count(), 1); assert_eq!(p.packet.answer_record_count(), 2); assert_eq!(p.packet.authority_record_count(), 0); assert_eq!(p.packet.additional_record_count(), 0); assert_eq!( p.questions[0].name, &[ 0x03, 0x77, 0x77, 0x77, 0x08, 0x66, 0x61, 0x63, 0x65, 0x62, 0x6f, 0x6f, 0x6b, 0x03, 0x63, 0x6f, 0x6d, 0x00 ] ); assert_eq!(p.questions[0].type_, Type::A); // cname assert_eq!(p.answers[0].name, &[0xc0, 0x0c]); assert_eq!(p.answers[0].ttl, 1523); assert_eq!( p.answers[0].data, RecordData::Cname(&[ 0x09, 0x73, 0x74, 0x61, 0x72, 0x2d, 0x6d, 0x69, 0x6e, 0x69, 0x04, 0x63, 0x31, 0x30, 0x72, 0xc0, 0x10 ]) ); // a assert_eq!(p.answers[1].name, &[0xc0, 0x2e]); assert_eq!(p.answers[1].ttl, 5); assert_eq!( p.answers[1].data, RecordData::A(Ipv4Address::new(0x1f, 0x0d, 0x53, 0x24)) ); } #[test] fn test_parse_response_nxdomain() { let p = Parsed::parse(&[ 0x63, 0xc4, 0x81, 0x83, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x13, 0x61, 0x68, 0x61, 0x73, 0x64, 0x67, 0x68, 0x6c, 0x61, 0x6b, 0x73, 0x6a, 0x68, 0x62, 0x61, 0x61, 0x73, 0x6c, 0x64, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x20, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x03, 0x83, 0x00, 0x3d, 0x01, 0x61, 0x0c, 0x67, 0x74, 0x6c, 0x64, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0x05, 0x6e, 0x73, 0x74, 0x6c, 0x64, 0x0c, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2d, 0x67, 0x72, 0x73, 0xc0, 0x20, 0x5f, 0xce, 0x8b, 0x85, 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x03, 0x84, 0x00, 0x09, 0x3a, 0x80, 0x00, 0x01, 0x51, 0x80, ]) .unwrap(); assert_eq!(p.packet.transaction_id(), 0x63c4); assert_eq!( p.packet.flags(), Flags::RESPONSE | Flags::RECURSION_DESIRED | Flags::RECURSION_AVAILABLE ); assert_eq!(p.packet.opcode(), Opcode::Query); assert_eq!(p.packet.rcode(), Rcode::NXDomain); assert_eq!(p.packet.question_count(), 1); assert_eq!(p.packet.answer_record_count(), 0); assert_eq!(p.packet.authority_record_count(), 1); assert_eq!(p.packet.additional_record_count(), 0); assert_eq!(p.questions[0].type_, Type::A); // SOA authority assert_eq!(p.authorities[0].name, &[0xc0, 0x20]); // com. assert_eq!(p.authorities[0].ttl, 899); assert!(matches!( p.authorities[0].data, RecordData::Other(Type::Soa, _) )); } #[test] fn test_emit() { let name = &[ 0x09, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x03, 0x6f, 0x72, 0x67, 0x00, ]; let repr = Repr { transaction_id: 0x1234, flags: Flags::RECURSION_DESIRED, opcode: Opcode::Query, question: Question { name, type_: Type::A, }, }; let mut buf = Vec::new(); buf.resize(repr.buffer_len(), 0); repr.emit(&mut Packet::new_unchecked(&mut buf)); let want = &[ 0x12, 0x34, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x72, 0x75, 0x73, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x03, 0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, ]; assert_eq!(&buf, want); } }