blob: 2c9c10d82768c78018723712e826fed48c730a33 [file] [log] [blame]
#![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<T: AsRef<[u8]>> {
buffer: T,
}
impl<T: AsRef<[u8]>> Packet<T> {
/// Imbue a raw octet buffer with DNS packet structure.
pub const 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)` 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<Item = Result<&'a [u8]>> {
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<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.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<RecordData<'a>> {
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<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(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<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);
}
}