dns: split header and body parsing

As part of extra header validation, split out DNS body parsing to
avoid the overhead of parsing the header twice.

(cherry picked from commit d720ead470)
pull/8995/head
Jason Ish 4 years ago committed by Victor Julien
parent 0b283ef4a6
commit 2e4aade51d

@ -29,7 +29,7 @@ use crate::core::STREAM_TOSERVER;
use crate::core::{self, AppProto, ALPROTO_UNKNOWN, IPPROTO_UDP, IPPROTO_TCP}; use crate::core::{self, AppProto, ALPROTO_UNKNOWN, IPPROTO_UDP, IPPROTO_TCP};
use crate::dns::parser; use crate::dns::parser;
use nom::IResult; use nom::{Err, IResult};
use nom::number::streaming::be_u16; use nom::number::streaming::be_u16;
/// DNS record types. /// DNS record types.
@ -500,18 +500,23 @@ impl DNSState {
event as u8); event as u8);
} }
fn validate_header(&self, input: &[u8]) -> bool { fn validate_header<'a>(&self, input: &'a [u8]) -> Option<(&'a [u8], DNSHeader)> {
parser::dns_parse_header(input) if let Ok((body, header)) = parser::dns_parse_header(input) {
.map(|(_, header)| probe_header_validity(header, input.len()).0) if probe_header_validity(&header, input.len()).0 {
.unwrap_or(false) return Some((body, header));
}
}
None
} }
fn parse_request(&mut self, input: &[u8], is_tcp: bool) -> bool { fn parse_request(&mut self, input: &[u8], is_tcp: bool) -> bool {
if !self.validate_header(input) { let (body, header) = if let Some((body, header)) = self.validate_header(input) {
(body, header)
} else {
return !is_tcp; return !is_tcp;
} };
match parser::dns_parse_request(input) { match parser::dns_parse_request_body(body, input, header) {
Ok((_, request)) => { Ok((_, request)) => {
if request.header.flags & 0x8000 != 0 { if request.header.flags & 0x8000 != 0 {
SCLogDebug!("DNS message is not a request"); SCLogDebug!("DNS message is not a request");
@ -562,11 +567,13 @@ impl DNSState {
} }
pub fn parse_response(&mut self, input: &[u8], is_tcp: bool) -> bool { pub fn parse_response(&mut self, input: &[u8], is_tcp: bool) -> bool {
if !self.validate_header(input) { let (body, header) = if let Some((body, header)) = self.validate_header(input) {
(body, header)
} else {
return !is_tcp; return !is_tcp;
} };
match parser::dns_parse_response(input) { match parser::dns_parse_response_body(body, input, header) {
Ok((_, response)) => { Ok((_, response)) => {
SCLogDebug!("Response header flags: {}", response.header.flags); SCLogDebug!("Response header flags: {}", response.header.flags);
@ -727,7 +734,7 @@ impl DNSState {
const DNS_HEADER_SIZE: usize = 12; const DNS_HEADER_SIZE: usize = 12;
fn probe_header_validity(header: DNSHeader, rlen: usize) -> (bool, bool, bool) { fn probe_header_validity(header: &DNSHeader, rlen: usize) -> (bool, bool, bool) {
let min_msg_size = 2 let min_msg_size = 2
* (header.additional_rr as usize * (header.additional_rr as usize
+ header.answer_rr as usize + header.answer_rr as usize
@ -756,7 +763,7 @@ fn probe(input: &[u8], dlen: usize) -> (bool, bool, bool) {
// parse a complete message, so perform header validation only. // parse a complete message, so perform header validation only.
if input.len() < dlen { if input.len() < dlen {
if let Ok((_, header)) = parser::dns_parse_header(input) { if let Ok((_, header)) = parser::dns_parse_header(input) {
return probe_header_validity(header, dlen); return probe_header_validity(&header, dlen);
} else { } else {
return (false, false, false); return (false, false, false);
} }
@ -764,17 +771,15 @@ fn probe(input: &[u8], dlen: usize) -> (bool, bool, bool) {
match parser::dns_parse_request(input) { match parser::dns_parse_request(input) {
Ok((_, request)) => { Ok((_, request)) => {
return probe_header_validity(request.header, dlen); return probe_header_validity(&request.header, dlen);
},
Err(nom::Err::Incomplete(_)) => {
match parser::dns_parse_header(input) {
Ok((_, header)) => {
return probe_header_validity(header, dlen);
}
Err(nom::Err::Incomplete(_)) => (false, false, true),
Err(_) => (false, false, false),
}
} }
Err(Err::Incomplete(_)) => match parser::dns_parse_header(input) {
Ok((_, header)) => {
return probe_header_validity(&header, dlen);
}
Err(Err::Incomplete(_)) => (false, false, true),
Err(_) => (false, false, false),
},
Err(_) => (false, false, false), Err(_) => (false, false, false),
} }
} }

@ -20,6 +20,7 @@
use nom::IResult; use nom::IResult;
use nom::combinator::rest; use nom::combinator::rest;
use nom::error::ErrorKind; use nom::error::ErrorKind;
use nom::multi::count;
use nom::number::streaming::{be_u8, be_u16, be_u32}; use nom::number::streaming::{be_u8, be_u16, be_u32};
use nom; use nom;
use crate::dns::dns::*; use crate::dns::dns::*;
@ -50,10 +51,8 @@ named!(pub dns_parse_header<DNSHeader>,
/// ///
/// Parameters: /// Parameters:
/// start: the start of the name /// start: the start of the name
/// message: the complete message that start is a part of /// message: the complete message that start is a part of with the DNS header
pub fn dns_parse_name<'a, 'b>(start: &'b [u8], pub fn dns_parse_name<'b>(start: &'b [u8], message: &'b [u8]) -> IResult<&'b [u8], Vec<u8>> {
message: &'b [u8])
-> IResult<&'b [u8], Vec<u8>> {
let mut pos = start; let mut pos = start;
let mut pivot = start; let mut pivot = start;
let mut name: Vec<u8> = Vec::with_capacity(32); let mut name: Vec<u8> = Vec::with_capacity(32);
@ -197,26 +196,27 @@ fn dns_parse_answer<'a>(slice: &'a [u8], message: &'a [u8], count: usize)
/// Parse a DNS response. /// Parse a DNS response.
pub fn dns_parse_response<'a>(slice: &'a [u8]) pub fn dns_parse_response(message: &[u8]) -> IResult<&[u8], DNSResponse> {
-> IResult<&[u8], DNSResponse> { let i = message;
do_parse!( let (i, header) = dns_parse_header(i)?;
slice, dns_parse_response_body(i, message, header)
header: dns_parse_header }
>> queries: count!(
call!(dns_parse_query, slice), header.questions as usize) pub fn dns_parse_response_body<'a>(
>> answers: call!( i: &'a [u8], message: &'a [u8], header: DNSHeader,
dns_parse_answer, slice, header.answer_rr as usize) ) -> IResult<&'a [u8], DNSResponse> {
>> authorities: call!( let (i, queries) = count(|b| dns_parse_query(b, message), header.questions as usize)(i)?;
dns_parse_answer, slice, header.authority_rr as usize) let (i, answers) = dns_parse_answer(i, message, header.answer_rr as usize)?;
>> ( let (i, authorities) = dns_parse_answer(i, message, header.authority_rr as usize)?;
DNSResponse{ Ok((
header: header, i,
queries: queries, DNSResponse {
answers: answers, header,
authorities: authorities, queries,
} answers,
) authorities,
) },
))
} }
/// Parse a single DNS query. /// Parse a single DNS query.
@ -344,19 +344,18 @@ pub fn dns_parse_rdata<'a>(input: &'a [u8], message: &'a [u8], rrtype: u16)
} }
/// Parse a DNS request. /// Parse a DNS request.
pub fn dns_parse_request<'a>(input: &'a [u8]) -> IResult<&[u8], DNSRequest> { pub fn dns_parse_request(input: &[u8]) -> IResult<&[u8], DNSRequest> {
do_parse!( let i = input;
input, let (i, header) = dns_parse_header(i)?;
header: dns_parse_header >> dns_parse_request_body(i, input, header)
queries: count!(call!(dns_parse_query, input), }
header.questions as usize) >>
( pub fn dns_parse_request_body<'a>(
DNSRequest{ input: &'a [u8], message: &'a [u8], header: DNSHeader,
header: header, ) -> IResult<&'a [u8], DNSRequest> {
queries: queries, let i = input;
} let (i, queries) = count(|b| dns_parse_query(b, message), header.questions as usize)(i)?;
) Ok((i, DNSRequest { header, queries }))
)
} }
#[cfg(test)] #[cfg(test)]

Loading…
Cancel
Save