From: Philippe Antoine Date: Tue, 12 Dec 2023 13:25:37 +0000 (+0100) Subject: dns: prepare for dns over http2 support X-Git-Tag: suricata-8.0.0-beta1~991 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b5f55b5b1f72155c0e5b3bb2d1d477c0ba0d7b1b;p=thirdparty%2Fsuricata.git dns: prepare for dns over http2 support by making tx parsing and creation more easily available, without needing a dns state. Dns event NotResponse is now set on the right tx, and not the one before. Also debug log for Z-flag on request says "request" instead of "response" Also rustfmt dns.rs --- diff --git a/rust/src/dns/dns.rs b/rust/src/dns/dns.rs index 1c1358a9f3..906f2a0288 100644 --- a/rust/src/dns/dns.rs +++ b/rust/src/dns/dns.rs @@ -281,6 +281,11 @@ impl DNSTransaction { } return 0; } + + /// Set an event. The event is set on the most recent transaction. + pub fn set_event(&mut self, event: DNSEvent) { + self.tx_data.set_event(event as u8); + } } struct ConfigTracker { @@ -338,18 +343,116 @@ impl State for DNSState { } } +fn dns_validate_header(input: &[u8]) -> Option<(&[u8], DNSHeader)> { + if let Ok((body, header)) = parser::dns_parse_header(input) { + if probe_header_validity(&header, input.len()).0 { + return Some((body, header)); + } + } + None +} + +#[derive(Debug, PartialEq, Eq)] +pub(crate) enum DNSParseError { + HeaderValidation, + NotRequest, + Incomplete, + OtherError, +} + +pub(crate) fn dns_parse_request(input: &[u8]) -> Result { + let (body, header) = if let Some((body, header)) = dns_validate_header(input) { + (body, header) + } else { + return Err(DNSParseError::HeaderValidation); + }; + + match parser::dns_parse_body(body, input, header) { + Ok((_, request)) => { + if request.header.flags & 0x8000 != 0 { + SCLogDebug!("DNS message is not a request"); + return Err(DNSParseError::NotRequest); + } + + let z_flag = request.header.flags & 0x0040 != 0; + let opcode = ((request.header.flags >> 11) & 0xf) as u8; + + let mut tx = DNSTransaction::new(Direction::ToServer); + tx.request = Some(request); + + if z_flag { + SCLogDebug!("Z-flag set on DNS request"); + tx.set_event(DNSEvent::ZFlagSet); + } + if opcode >= 7 { + tx.set_event(DNSEvent::InvalidOpcode); + } + + return Ok(tx); + } + Err(Err::Incomplete(_)) => { + // Insufficient data. + SCLogDebug!("Insufficient data while parsing DNS request"); + return Err(DNSParseError::Incomplete); + } + Err(_) => { + // Error, probably malformed data. + SCLogDebug!("An error occurred while parsing DNS request"); + return Err(DNSParseError::OtherError); + } + } +} + +pub(crate) fn dns_parse_response(input: &[u8]) -> Result { + let (body, header) = if let Some((body, header)) = dns_validate_header(input) { + (body, header) + } else { + return Err(DNSParseError::HeaderValidation); + }; + + match parser::dns_parse_body(body, input, header) { + Ok((_, response)) => { + SCLogDebug!("Response header flags: {}", response.header.flags); + let z_flag = response.header.flags & 0x0040 != 0; + let opcode = ((response.header.flags >> 11) & 0xf) as u8; + let flags = response.header.flags; + + let mut tx = DNSTransaction::new(Direction::ToClient); + tx.response = Some(response); + + if flags & 0x8000 == 0 { + SCLogDebug!("DNS message is not a response"); + tx.set_event(DNSEvent::NotResponse); + } + + if z_flag { + SCLogDebug!("Z-flag set on DNS response"); + tx.set_event(DNSEvent::ZFlagSet); + } + if opcode >= 7 { + tx.set_event(DNSEvent::InvalidOpcode); + } + + return Ok(tx); + } + Err(Err::Incomplete(_)) => { + // Insufficient data. + SCLogDebug!("Insufficient data while parsing DNS request"); + return Err(DNSParseError::Incomplete); + } + Err(_) => { + // Error, probably malformed data. + SCLogDebug!("An error occurred while parsing DNS request"); + return Err(DNSParseError::OtherError); + } + } +} + impl DNSState { fn new() -> Self { Default::default() } - fn new_tx(&mut self, direction: Direction) -> DNSTransaction { - let mut tx = DNSTransaction::new(direction); - self.tx_id += 1; - tx.id = self.tx_id; - return tx; - } - fn free_tx(&mut self, tx_id: u64) { let len = self.transactions.len(); let mut found = false; @@ -382,63 +485,34 @@ impl DNSState { tx.tx_data.set_event(event as u8); } - fn validate_header<'a>(&self, input: &'a [u8]) -> Option<(&'a [u8], DNSHeader)> { - if let Ok((body, header)) = parser::dns_parse_header(input) { - if probe_header_validity(&header, input.len()).0 { - return Some((body, header)); - } - } - None - } - fn parse_request(&mut self, input: &[u8], is_tcp: bool, frame: Option, flow: *const core::Flow,) -> bool { - let (body, header) = if let Some((body, header)) = self.validate_header(input) { - (body, header) - } else { - return !is_tcp; - }; - - match parser::dns_parse_body(body, input, header) { - Ok((_, request)) => { - if request.header.flags & 0x8000 != 0 { - SCLogDebug!("DNS message is not a request"); - self.set_event(DNSEvent::NotRequest); - return false; - } - - let z_flag = request.header.flags & 0x0040 != 0; - let opcode = ((request.header.flags >> 11) & 0xf) as u8; - - let mut tx = self.new_tx(Direction::ToServer); + match dns_parse_request(input) { + Ok(mut tx) => { + self.tx_id += 1; + tx.id = self.tx_id; if let Some(frame) = frame { frame.set_tx(flow, tx.id); } - tx.request = Some(request); self.transactions.push_back(tx); - - if z_flag { - SCLogDebug!("Z-flag set on DNS response"); - self.set_event(DNSEvent::ZFlagSet); - } - - if opcode >= 7 { - self.set_event(DNSEvent::InvalidOpcode); - } - return true; } - Err(Err::Incomplete(_)) => { - // Insufficient data. - SCLogDebug!("Insufficient data while parsing DNS request"); - self.set_event(DNSEvent::MalformedData); - return false; - } - Err(_) => { - // Error, probably malformed data. - SCLogDebug!("An error occurred while parsing DNS request"); - self.set_event(DNSEvent::MalformedData); - return false; - } + Err(e) => match e { + DNSParseError::HeaderValidation => { + return !is_tcp; + } + DNSParseError::NotRequest => { + self.set_event(DNSEvent::NotRequest); + return false; + } + DNSParseError::Incomplete => { + self.set_event(DNSEvent::MalformedData); + return false; + } + DNSParseError::OtherError => { + self.set_event(DNSEvent::MalformedData); + return false; + } + }, } } @@ -469,59 +543,32 @@ impl DNSState { } fn parse_response(&mut self, input: &[u8], is_tcp: bool, frame: Option, flow: *const core::Flow) -> bool { - let (body, header) = if let Some((body, header)) = self.validate_header(input) { - (body, header) - } else { - return !is_tcp; - }; - - match parser::dns_parse_body(body, input, header) { - Ok((_, response)) => { - SCLogDebug!("Response header flags: {}", response.header.flags); - - if response.header.flags & 0x8000 == 0 { - SCLogDebug!("DNS message is not a response"); - self.set_event(DNSEvent::NotResponse); + match dns_parse_response(input) { + Ok(mut tx) => { + self.tx_id += 1; + tx.id = self.tx_id; + if let Some(ref mut config) = &mut self.config { + if let Some(response) = &tx.response { + if let Some(config) = config.remove(&response.header.tx_id) { + tx.tx_data.config = config; + } + } } - - let z_flag = response.header.flags & 0x0040 != 0; - let opcode = ((response.header.flags >> 11) & 0xf) as u8; - - let mut tx = self.new_tx(Direction::ToClient); if let Some(frame) = frame { frame.set_tx(flow, tx.id); } - if let Some(ref mut config) = &mut self.config { - if let Some(config) = config.remove(&response.header.tx_id) { - tx.tx_data.config = config; - } - } - tx.response = Some(response); self.transactions.push_back(tx); - - if z_flag { - SCLogDebug!("Z-flag set on DNS response"); - self.set_event(DNSEvent::ZFlagSet); - } - - if opcode >= 7 { - self.set_event(DNSEvent::InvalidOpcode); - } - return true; } - Err(Err::Incomplete(_)) => { - // Insufficient data. - SCLogDebug!("Insufficient data while parsing DNS response"); - self.set_event(DNSEvent::MalformedData); - return false; - } - Err(_) => { - // Error, probably malformed data. - SCLogDebug!("An error occurred while parsing DNS response"); - self.set_event(DNSEvent::MalformedData); - return false; - } + Err(e) => match e { + DNSParseError::HeaderValidation => { + return !is_tcp; + } + _ => { + self.set_event(DNSEvent::MalformedData); + return false; + } + }, } }