Dario Nieuwenhuis 3 жил өмнө
parent
commit
72d66dd219
3 өөрчлөгдсөн 790 нэмэгдсэн , 2 устгасан
  1. 3 2
      Cargo.toml
  2. 785 0
      src/wire/dns.rs
  3. 2 0
      src/wire/mod.rs

+ 3 - 2
Cargo.toml

@@ -46,7 +46,8 @@ verbose = []
 "proto-dhcpv4" = ["proto-ipv4"]
 "proto-ipv6" = []
 "proto-sixlowpan" = ["proto-ipv6"]
-
+"proto-dns" = []
+ 
 "socket" = []
 "socket-raw" = ["socket"]
 "socket-udp" = ["socket"]
@@ -60,7 +61,7 @@ default = [
   "std", "log", # needed for `cargo test --no-default-features --features default` :/
   "medium-ethernet", "medium-ip", "medium-ieee802154",
   "phy-raw_socket", "phy-tuntap_interface",
-  "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-sixlowpan",
+  "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-sixlowpan", "proto-dns",
   "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4",
   "async"
 ]

+ 785 - 0
src/wire/dns.rs

@@ -0,0 +1,785 @@
+#![allow(dead_code)]
+
+use bitflags::bitflags;
+use byteorder::{ByteOrder, NetworkEndian};
+use core::iter;
+use core::iter::Iterator;
+
+#[cfg(feature = "proto-ipv4")]
+use crate::wire::Ipv4Address;
+#[cfg(feature = "proto-ipv6")]
+use crate::wire::Ipv6Address;
+use crate::{Error, Result};
+
+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! {
+    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)]
+pub struct Packet<T: AsRef<[u8]>> {
+    buffer: T,
+}
+
+impl<T: AsRef<[u8]>> Packet<T> {
+    /// Imbue a raw octet buffer with DNS packet structure.
+    pub fn new_unchecked(buffer: T) -> Packet<T> {
+        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<Packet<T>> {
+        let packet = Self::new_unchecked(buffer);
+        packet.check_len()?;
+        Ok(packet)
+    }
+
+    /// Ensure that no accessor method will panic if called.
+    /// Returns `Err(Error::Malformed)` 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::Malformed)
+        } 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<Item = Result<&'a [u8]>> {
+        let mut packet = self.buffer.as_ref();
+
+        iter::from_fn(move || loop {
+            if bytes.is_empty() {
+                return Some(Err(Error::Malformed));
+            }
+            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::Malformed));
+                    }
+                    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::Malformed));
+                    }
+                    let y = bytes[1];
+                    let ptr = ((x & 0x3F) as usize) << 8 | (y as usize);
+                    if packet.len() <= ptr {
+                        return Some(Err(Error::Malformed));
+                    }
+
+                    // 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*** occurance
+                    //      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::Malformed)),
+            }
+        })
+    }
+}
+
+impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
+    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<usize>)> {
+    loop {
+        let x = *bytes.get(0).ok_or(Error::Malformed)?;
+        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::Malformed)?;
+                bytes = &bytes[len..];
+                f(label);
+            }
+            x if x & 0xC0 == 0xC0 => {
+                let y = *bytes.get(0).ok_or(Error::Malformed)?;
+                bytes = &bytes[1..];
+
+                let ptr = ((x & 0x3F) as usize) << 8 | (y as usize);
+                return Ok((bytes, Some(ptr)));
+            }
+            _ => return Err(Error::Malformed),
+        }
+    }
+}
+
+#[derive(Debug, PartialEq)]
+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::Malformed);
+        }
+        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::Malformed);
+        }
+
+        Ok((rest, Question { name, type_ }))
+    }
+
+    /// Return the length of a packet that will be emitted from this high-level representation.
+    pub 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)]
+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<RecordData<'a>> {
+        match type_ {
+            Type::A => {
+                if data.len() != 4 {
+                    return Err(Error::Malformed);
+                }
+                Ok(RecordData::A(Ipv4Address::from_bytes(data)))
+            }
+            Type::Aaaa => {
+                if data.len() != 16 {
+                    return Err(Error::Malformed);
+                }
+                Ok(RecordData::Aaaa(Ipv6Address::from_bytes(data)))
+            }
+            Type::Cname => Ok(RecordData::Cname(data)),
+            x => Ok(RecordData::Other(x, data)),
+        }
+    }
+}
+
+#[derive(Debug, PartialEq)]
+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::Malformed);
+        }
+        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::Malformed);
+        }
+
+        let data = rest.get(..len).ok_or(Error::Malformed)?;
+        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)]
+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 fn buffer_len(&self) -> usize {
+        field::HEADER_END + self.question.buffer_len()
+    }
+
+    /// Emit a high-level representation into a DNS packet.
+    pub fn emit<T: ?Sized>(&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(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<Question<'a>>,
+        answers: Vec<Record<'a>>,
+        authorities: Vec<Record<'a>>,
+        additionals: Vec<Record<'a>>,
+    }
+
+    impl<'a> Parsed<'a> {
+        fn parse(bytes: &'a [u8]) -> Result<Self> {
+            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);
+    }
+}

+ 2 - 0
src/wire/mod.rs

@@ -81,6 +81,8 @@ pub mod pretty_print;
 mod arp;
 #[cfg(feature = "proto-dhcpv4")]
 pub(crate) mod dhcpv4;
+#[cfg(feature = "proto-dns")]
+pub(crate) mod dns;
 #[cfg(feature = "medium-ethernet")]
 mod ethernet;
 #[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]