Skip to content

Commit

Permalink
parse message
Browse files Browse the repository at this point in the history
  • Loading branch information
BrendanBall committed Feb 4, 2024
1 parent a6e06dd commit cb23cef
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 12 deletions.
56 changes: 56 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion dns_decode/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ edition = "2021"

[dependencies]
nom = "7.1.3"
hex = "0.4.3"
hex = "0.4.3"
thiserror = "1.0.56"
1 change: 1 addition & 0 deletions dns_decode/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod message;
pub mod message_header;
pub mod query;
pub mod resource_record;
131 changes: 131 additions & 0 deletions dns_decode/src/message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use crate::{message_header::*, query::*, resource_record::*};
use nom::{multi::count, IResult};
use thiserror::Error;

#[derive(Error, Debug)]
pub enum ParseError {
#[error("unknown parse error")]
Unknown,
}

#[derive(Debug, PartialEq)]
pub struct Message {
pub header: MessageHeader,
pub queries: Vec<Query>,
pub answers: Vec<ResourceRecord>,
}

impl TryFrom<&[u8]> for Message {
type Error = ParseError;

fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
// TODO improve error reporting
let (_, m) = message(input).map_err(|_op| ParseError::Unknown)?;
Ok(m)
}
}

fn message(input: &[u8]) -> IResult<&[u8], Message> {
let (input, header) = message_header(input)?;
let (input, queries) = count(query, header.query_count as usize)(input)?;
let (input, answers) = count(resource_record, header.answer_count as usize)(input)?;

Ok((
input,
Message {
header,
queries,
answers,
},
))
}

#[cfg(test)]
mod tests {
use super::*;
use std::net::Ipv4Addr;

#[test]
fn test_message_request() {
let message_bytes =
hex::decode("690601000001000000000000076578616d706c6503636f6d0000010001").unwrap();
let result = message(&message_bytes);

assert_eq!(
result,
Ok((
&b""[..],
Message {
header: MessageHeader {
message_id: 0x6906,
flags: Flags {
qr: QR::Query,
opcode: Opcode::Query,
aa: AuthoritativeAnswer::NonAuthoritative,
truncated: Truncated::NotTruncated,
recursion_desired: RecursionDesired::Desired,
recursion_available: RecursionAvailable::NotAvailable,
rcode: Rcode::NoError,
},
query_count: 1,
answer_count: 0,
name_server_count: 0,
additional_count: 0,
},
queries: vec![Query {
name: vec![String::from("example"), String::from("com")],
query_type: QueryType::A,
query_class: QueryClass::Internet,
}],
answers: vec![]
}
))
);
}

#[test]
fn test_message_response() {
let message_bytes = hex::decode(
"690681800001000100000000076578616d706c6503636f6d0000010001c00c0001000100005a0200045db8d822"
)
.unwrap();
let result = message(&message_bytes);

assert_eq!(
result,
Ok((
&b""[..],
Message {
header: MessageHeader {
message_id: 0x6906,
flags: Flags {
qr: QR::Response,
opcode: Opcode::Query,
aa: AuthoritativeAnswer::NonAuthoritative,
truncated: Truncated::NotTruncated,
recursion_desired: RecursionDesired::Desired,
recursion_available: RecursionAvailable::Available,
rcode: Rcode::NoError,
},
query_count: 1,
answer_count: 1,
name_server_count: 0,
additional_count: 0,
},
queries: vec![Query {
name: vec![String::from("example"), String::from("com")],
query_type: QueryType::A,
query_class: QueryClass::Internet,
}],
answers: vec![ResourceRecord {
name: Name::Pointer(49164),
resource_type: ResourceType::A,
resource_class: ResourceClass::Internet,
ttl: 23042,
rdata: ResourceData::A(Ipv4Addr::from(0x5db8d822))
}]
}
))
);
}
}
6 changes: 3 additions & 3 deletions dns_decode/src/message_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ fn dns_flags(input: &[u8]) -> IResult<&[u8], Flags> {
bits::<_, _, Error<(&[u8], usize)>, _, _>(dns_flags_inner)(input)
}

fn dns_message(input: &[u8]) -> IResult<&[u8], MessageHeader> {
pub fn message_header(input: &[u8]) -> IResult<&[u8], MessageHeader> {
let (input, message_id) = be_u16(input)?;
let (input, flags) = dns_flags(input)?;
let (input, query_count) = be_u16(input)?;
Expand Down Expand Up @@ -208,7 +208,7 @@ mod tests {
fn message_header_query() {
let dns_message_bytes = hex::decode("690601000001000000000000").unwrap();

let result = dns_message(&dns_message_bytes);
let result = message_header(&dns_message_bytes);
assert_eq!(
result,
Ok((
Expand Down Expand Up @@ -237,7 +237,7 @@ mod tests {
fn message_header_response() {
let dns_message_bytes = hex::decode("690681800001000100000000").unwrap();

let result = dns_message(&dns_message_bytes);
let result = message_header(&dns_message_bytes);
assert_eq!(
result,
Ok((
Expand Down
2 changes: 1 addition & 1 deletion dns_decode/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ fn name(input: &[u8]) -> IResult<&[u8], Vec<String>> {
Ok((input, labels))
}

fn query(input: &[u8]) -> IResult<&[u8], Query> {
pub fn query(input: &[u8]) -> IResult<&[u8], Query> {
let (input, name) = name(input)?;
let (input, query_type) = be_u16(input)?;
let (input, query_class) = be_u16(input)?;
Expand Down
11 changes: 4 additions & 7 deletions dns_decode/src/resource_record.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
use nom::{
bytes::complete::{tag, take},
combinator::iterator,
error::{Error, ErrorKind, ParseError},
multi::length_value,
number::complete::{be_u128, be_u16, be_u32, be_u8},
Compare, Err, IResult, InputIter, InputLength, InputTake, Slice,
number::complete::{be_u128, be_u16, be_u32},
IResult, InputIter, InputLength, Slice,
};
use std::ops::{Range, RangeFrom, RangeTo};
use std::ops::RangeFrom;
use std::{
convert::Into,
net::{Ipv4Addr, Ipv6Addr},
Expand Down Expand Up @@ -125,7 +122,7 @@ where
}
}

fn resource_record(input: &[u8]) -> IResult<&[u8], ResourceRecord> {
pub fn resource_record(input: &[u8]) -> IResult<&[u8], ResourceRecord> {
let (input, name) = name(input)?;
let (input, resource_type) = be_u16(input)?;
let (input, resource_class) = be_u16(input)?;
Expand Down

0 comments on commit cb23cef

Please sign in to comment.