Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dns over http2 5773 v2.1 #10040

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions etc/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3743,6 +3743,9 @@
"dns_udp": {
"$ref": "#/$defs/stats_applayer_error"
},
"doh2": {
"$ref": "#/$defs/stats_applayer_error"
},
"enip_tcp": {
"$ref": "#/$defs/stats_applayer_error"
},
Expand Down Expand Up @@ -3851,6 +3854,9 @@
"dns_udp": {
"type": "integer"
},
"doh2": {
"type": "integer"
},
"enip_tcp": {
"type": "integer"
},
Expand Down Expand Up @@ -3968,6 +3974,9 @@
"dns_udp": {
"type": "integer"
},
"doh2": {
"type": "integer"
},
"enip_tcp": {
"type": "integer"
},
Expand Down
1 change: 1 addition & 0 deletions rust/src/applayer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ pub unsafe fn AppLayerRegisterParser(parser: *const RustParser, alproto: AppProt

// Defined in app-layer-detect-proto.h
extern {
pub fn AppLayerForceProtocolChange(f: *const Flow, new_proto: AppProto);
pub fn AppLayerProtoDetectPPRegister(ipproto: u8, portstr: *const c_char, alproto: AppProto,
min_depth: u16, max_depth: u16, dir: u8,
pparser1: ProbeFn, pparser2: ProbeFn);
Expand Down
244 changes: 149 additions & 95 deletions rust/src/dns/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,10 @@ impl Transaction for DNSTransaction {

impl DNSTransaction {
pub fn new(direction: Direction) -> Self {
Self {
tx_data: AppLayerTxData::for_direction(direction),
Self {
tx_data: AppLayerTxData::for_direction(direction),
..Default::default()
}
}
}

/// Get the DNS transactions ID (not the internal tracking ID).
Expand All @@ -277,6 +277,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 {
Expand Down Expand Up @@ -334,6 +339,111 @@ impl State<DNSTransaction> for DNSState {
}
}

fn dns_validate_header<'a>(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
}

#[derive(Debug, PartialEq, Eq)]
pub enum DNSParseError {
HeaderValidation,
NotRequest,
Incomplete,
OtherError,
}

pub(crate) fn dns_parse_request(input: &[u8]) -> Result<DNSTransaction, DNSParseError> {
let (body, header) = if let Some((body, header)) = dns_validate_header(input) {
(body, header)
} else {
return Err(DNSParseError::HeaderValidation);
};

match parser::dns_parse_request_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<DNSTransaction, DNSParseError> {
let (body, header) = if let Some((body, header)) = dns_validate_header(input) {
(body, header)
} else {
return Err(DNSParseError::HeaderValidation);
};

match parser::dns_parse_response_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 {
pub fn new() -> Self {
Default::default()
Expand Down Expand Up @@ -386,60 +496,31 @@ 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) -> bool {
let (body, header) = if let Some((body, header)) = self.validate_header(input) {
(body, header)
} else {
return !is_tcp;
};

match parser::dns_parse_request_body(body, input, header) {
Ok((_, request)) => {
if request.header.flags & 0x8000 != 0 {
SCLogDebug!("DNS message is not a request");
match dns_parse_request(input) {
Ok(mut tx) => {
self.tx_id += 1;
tx.id = self.tx_id;
self.transactions.push_back(tx);
return true;
}
Err(e) => match e {
DNSParseError::HeaderValidation => {
return !is_tcp;
}
DNSParseError::NotRequest => {
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);
tx.request = Some(request);
self.transactions.push_back(tx);

if z_flag {
SCLogDebug!("Z-flag set on DNS response");
self.set_event(DNSEvent::ZFlagSet);
DNSParseError::Incomplete => {
self.set_event(DNSEvent::MalformedData);
return false;
}

if opcode >= 7 {
self.set_event(DNSEvent::InvalidOpcode);
DNSParseError::OtherError => {
self.set_event(DNSEvent::MalformedData);
return false;
}

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;
}
},
}
}

Expand Down Expand Up @@ -468,56 +549,29 @@ impl DNSState {
}

pub fn parse_response(&mut self, input: &[u8], is_tcp: bool) -> bool {
let (body, header) = if let Some((body, header)) = self.validate_header(input) {
(body, header)
} else {
return !is_tcp;
};

match parser::dns_parse_response_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);
}

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);
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(config) = config.remove(&response.header.tx_id) {
tx.tx_data.config = config;
if let Some(response) = &tx.response {
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;
}
},
}
}

Expand Down
8 changes: 4 additions & 4 deletions rust/src/dns/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ fn dns_log_json_answer(
}

fn dns_log_query(
tx: &mut DNSTransaction, i: u16, flags: u64, jb: &mut JsonBuilder,
tx: &DNSTransaction, i: u16, flags: u64, jb: &mut JsonBuilder,
) -> Result<bool, JsonError> {
let index = i as usize;
if let Some(request) = &tx.request {
Expand All @@ -632,7 +632,7 @@ fn dns_log_query(

#[no_mangle]
pub extern "C" fn rs_dns_log_json_query(
tx: &mut DNSTransaction, i: u16, flags: u64, jb: &mut JsonBuilder,
tx: &DNSTransaction, i: u16, flags: u64, jb: &mut JsonBuilder,
) -> bool {
match dns_log_query(tx, i, flags, jb) {
Ok(false) | Err(_) => {
Expand All @@ -646,7 +646,7 @@ pub extern "C" fn rs_dns_log_json_query(

#[no_mangle]
pub extern "C" fn rs_dns_log_json_answer(
tx: &mut DNSTransaction, flags: u64, js: &mut JsonBuilder,
tx: &DNSTransaction, flags: u64, js: &mut JsonBuilder,
) -> bool {
if let Some(response) = &tx.response {
for query in &response.queries {
Expand All @@ -659,7 +659,7 @@ pub extern "C" fn rs_dns_log_json_answer(
}

#[no_mangle]
pub extern "C" fn rs_dns_do_log_answer(tx: &mut DNSTransaction, flags: u64) -> bool {
pub extern "C" fn rs_dns_do_log_answer(tx: &DNSTransaction, flags: u64) -> bool {
if let Some(response) = &tx.response {
for query in &response.queries {
if dns_log_rrtype_enabled(query.rrtype, flags) {
Expand Down
6 changes: 4 additions & 2 deletions rust/src/http2/decompression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,12 @@ impl HTTP2DecoderHalf {
if self.encoding == HTTP2ContentEncoding::Unknown {
if input == b"gzip" {
self.encoding = HTTP2ContentEncoding::Gzip;
self.decoder = HTTP2Decompresser::Gzip(Box::new(GzDecoder::new(HTTP2cursor::new())));
self.decoder =
HTTP2Decompresser::Gzip(Box::new(GzDecoder::new(HTTP2cursor::new())));
} else if input == b"deflate" {
self.encoding = HTTP2ContentEncoding::Deflate;
self.decoder = HTTP2Decompresser::Deflate(Box::new(DeflateDecoder::new(HTTP2cursor::new())));
self.decoder =
HTTP2Decompresser::Deflate(Box::new(DeflateDecoder::new(HTTP2cursor::new())));
} else if input == b"br" {
self.encoding = HTTP2ContentEncoding::Br;
self.decoder = HTTP2Decompresser::Brotli(Box::new(brotli::Decompressor::new(
Expand Down