diff --git a/etc/schema.json b/etc/schema.json index 2aff6cd6f959..0154fbe62ac5 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -4177,6 +4177,9 @@ "description": "Errors encountered parsing DNS/UDP protocol", "$ref": "#/$defs/stats_applayer_error" }, + "doh2": { + "$ref": "#/$defs/stats_applayer_error" + }, "enip_tcp": { "description": "Errors encounterd parsing ENIP/TCP", "$ref": "#/$defs/stats_applayer_error" @@ -4335,6 +4338,9 @@ "description": "Number of flows for DNS/UDP protocol", "type": "integer" }, + "doh2": { + "type": "integer" + }, "enip_tcp": { "description": "Number of flows for ENIP/TCP", "type": "integer" @@ -4500,6 +4506,9 @@ "description": "Number of transactions for DNS/UDP protocol", "type": "integer" }, + "doh2": { + "type": "integer" + }, "enip_tcp": { "description": "Number of transactions for ENIP/TCP", "type": "integer" diff --git a/rust/src/applayer.rs b/rust/src/applayer.rs index 9814143ba5ef..2359a98c232e 100644 --- a/rust/src/applayer.rs +++ b/rust/src/applayer.rs @@ -476,6 +476,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); diff --git a/rust/src/core.rs b/rust/src/core.rs index abb27ea578fe..bca228131124 100644 --- a/rust/src/core.rs +++ b/rust/src/core.rs @@ -61,6 +61,13 @@ impl Direction { pub fn is_to_client(&self) -> bool { matches!(self, Self::ToClient) } + + pub fn index(&self) -> usize { + match self { + Self::ToClient => 0, + _ => 1, + } + } } impl Default for Direction { diff --git a/rust/src/dns/dns.rs b/rust/src/dns/dns.rs index a293a028e142..671423e81af7 100644 --- a/rust/src/dns/dns.rs +++ b/rust/src/dns/dns.rs @@ -271,6 +271,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 { @@ -328,18 +333,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 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; @@ -372,63 +475,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; + } + }, } } @@ -459,59 +533,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; + } + }, } } diff --git a/rust/src/http2/decompression.rs b/rust/src/http2/decompression.rs index 99f8af39032c..31e8547a8134 100644 --- a/rust/src/http2/decompression.rs +++ b/rust/src/http2/decompression.rs @@ -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( diff --git a/rust/src/http2/detect.rs b/rust/src/http2/detect.rs index 67933b6c2446..89089ceefc95 100644 --- a/rust/src/http2/detect.rs +++ b/rust/src/http2/detect.rs @@ -538,7 +538,7 @@ fn http2_tx_get_resp_line(tx: &mut HTTP2Transaction) { return; } let empty = Vec::new(); - let mut resp_line : Vec = Vec::new(); + let mut resp_line: Vec = Vec::new(); let status = if let Ok(value) = http2_frames_get_header_firstvalue(tx, Direction::ToClient, ":status") { @@ -617,7 +617,7 @@ fn http2_lower(value: &[u8]) -> Option> { fn http2_normalize_host(value: &[u8]) -> &[u8] { match value.iter().position(|&x| x == b'@') { Some(i) => { - let value = &value[i+1..]; + let value = &value[i + 1..]; match value.iter().position(|&x| x == b':') { Some(i) => { return &value[..i]; @@ -627,16 +627,14 @@ fn http2_normalize_host(value: &[u8]) -> &[u8] { } } } - None => { - match value.iter().position(|&x| x == b':') { - Some(i) => { - return &value[..i]; - } - None => { - return value; - } + None => match value.iter().position(|&x| x == b':') { + Some(i) => { + return &value[..i]; } - } + None => { + return value; + } + }, } } diff --git a/rust/src/http2/http2.rs b/rust/src/http2/http2.rs index 6a5b3936c981..ec4bf89886e8 100644 --- a/rust/src/http2/http2.rs +++ b/rust/src/http2/http2.rs @@ -25,6 +25,9 @@ use crate::conf::conf_get; use crate::core::*; use crate::filecontainer::*; use crate::filetracker::*; + +use crate::dns::dns::{dns_parse_request, dns_parse_response, DNSTransaction}; + use nom7::Err; use std; use std::collections::VecDeque; @@ -33,6 +36,7 @@ use std::fmt; use std::io; static mut ALPROTO_HTTP2: AppProto = ALPROTO_UNKNOWN; +static mut ALPROTO_DOH2: AppProto = ALPROTO_UNKNOWN; const HTTP2_DEFAULT_MAX_FRAME_SIZE: u32 = 16384; const HTTP2_MAX_HANDLED_FRAME_SIZE: usize = 65536; @@ -61,7 +65,7 @@ const HTTP2_FRAME_RSTSTREAM_LEN: usize = 4; const HTTP2_FRAME_PRIORITY_LEN: usize = 5; const HTTP2_FRAME_WINDOWUPDATE_LEN: usize = 4; pub static mut HTTP2_MAX_TABLESIZE: u32 = 65536; // 0x10000 -// maximum size of reassembly for header + continuation + // maximum size of reassembly for header + continuation static mut HTTP2_MAX_REASS: usize = 102400; static mut HTTP2_MAX_STREAMS: usize = 4096; // 0x1000 @@ -124,6 +128,15 @@ pub struct HTTP2Frame { pub data: HTTP2FrameTypeData, } +#[derive(Debug, Default)] +pub struct DohHttp2Tx { + is_doh_data: [bool; 2], + // dns response buffer + pub data_buf: [Vec; 2], + pub dns_request_tx: Option, + pub dns_response_tx: Option, +} + #[derive(Debug)] pub struct HTTP2Transaction { tx_id: u64, @@ -146,6 +159,8 @@ pub struct HTTP2Transaction { pub escaped: Vec>, pub req_line: Vec, pub resp_line: Vec, + + pub doh: Option, } impl Transaction for HTTP2Transaction { @@ -177,6 +192,7 @@ impl HTTP2Transaction { escaped: Vec::with_capacity(16), req_line: Vec::new(), resp_line: Vec::new(), + doh: None, } } @@ -186,13 +202,13 @@ impl HTTP2Transaction { if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } { //TODO get a file container instead of NULL (c.HTPFileCloseHandleRange)( - sfcm.files_sbcfg, - std::ptr::null_mut(), - 0, - self.file_range, - std::ptr::null_mut(), - 0, - ); + sfcm.files_sbcfg, + std::ptr::null_mut(), + 0, + self.file_range, + std::ptr::null_mut(), + 0, + ); (c.HttpRangeFreeBlock)(self.file_range); self.file_range = std::ptr::null_mut(); } @@ -204,12 +220,33 @@ impl HTTP2Transaction { self.tx_data.set_event(event as u8); } - fn handle_headers(&mut self, blocks: &[parser::HTTP2FrameHeaderBlock], dir: Direction) { + fn handle_headers( + &mut self, blocks: &[parser::HTTP2FrameHeaderBlock], dir: Direction, + ) -> Option> { let mut authority = None; + let mut path = None; + let mut doh = false; let mut host = None; for block in blocks { if block.name.as_ref() == b"content-encoding" { self.decoder.http2_encoding_fromvec(&block.value, dir); + } else if block.name.as_ref() == b"accept" { + //TODO? faster pattern matching + if block.value.as_ref() == b"application/dns-message" { + doh = true; + } + } else if block.name.as_ref() == b"content-type" { + if block.value.as_ref() == b"application/dns-message" { + if let Some(doh) = &mut self.doh { + doh.is_doh_data[dir.index()] = true; + } else { + let mut doh = DohHttp2Tx::default(); + doh.is_doh_data[dir.index()] = true; + self.doh = Some(doh); + } + } + } else if block.name.as_ref() == b":path" { + path = Some(&block.value); } else if block.name.eq_ignore_ascii_case(b":authority") { authority = Some(&block.value); if block.value.iter().any(|&x| x == b'@') { @@ -231,6 +268,14 @@ impl HTTP2Transaction { } } } + if doh && unsafe { ALPROTO_DOH2 } != ALPROTO_UNKNOWN { + if let Some(p) = path { + if let Ok((_, dns_req)) = parser::doh_extract_request(p) { + return Some(dns_req); + } + } + } + return None; } pub fn update_file_flags(&mut self, flow_file_flags: u16) { @@ -239,10 +284,10 @@ impl HTTP2Transaction { } fn decompress<'a>( - &'a mut self, input: &'a [u8], dir: Direction, sfcm: &'static SuricataFileContext, over: bool, flow: *const Flow, + &'a mut self, input: &'a [u8], output: &'a mut Vec, dir: Direction, + sfcm: &'static SuricataFileContext, over: bool, flow: *const Flow, ) -> io::Result<()> { - let mut output = Vec::with_capacity(decompression::HTTP2_DECOMPRESSION_CHUNK_SIZE); - let decompressed = self.decoder.decompress(input, &mut output, dir)?; + let decompressed = self.decoder.decompress(input, output, dir)?; let xid: u32 = self.tx_id as u32; if dir == Direction::ToClient { self.ft_tc.tx_id = self.tx_id - 1; @@ -258,7 +303,14 @@ impl HTTP2Transaction { ) { match range::http2_parse_check_content_range(&value) { Ok((_, v)) => { - range::http2_range_open(self, &v, flow, sfcm, Direction::ToClient, decompressed); + range::http2_range_open( + self, + &v, + flow, + sfcm, + Direction::ToClient, + decompressed, + ); if over && !self.file_range.is_null() { range::http2_range_close(self, Direction::ToClient, &[]) } @@ -301,13 +353,23 @@ impl HTTP2Transaction { &xid, ); }; + if unsafe { ALPROTO_DOH2 } != ALPROTO_UNKNOWN { + // we store DNS response, and process it when complete + if let Some(doh) = &mut self.doh { + if doh.is_doh_data[dir.index()] && doh.data_buf[dir.index()].len() < 0xFFFF { + // a DNS message is U16_MAX + doh.data_buf[dir.index()].extend_from_slice(decompressed); + } + } + } return Ok(()); } fn handle_frame( &mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: Direction, - ) { + ) -> Option> { //handle child_stream_id changes + let mut r = None; match data { HTTP2FrameTypeData::PUSHPROMISE(hs) => { if dir == Direction::ToClient { @@ -317,7 +379,7 @@ impl HTTP2Transaction { } self.state = HTTP2TransactionState::HTTP2StateReserved; } - self.handle_headers(&hs.blocks, dir); + r = self.handle_headers(&hs.blocks, dir); } HTTP2FrameTypeData::CONTINUATION(hs) => { if dir == Direction::ToClient @@ -325,13 +387,13 @@ impl HTTP2Transaction { { self.child_stream_id = 0; } - self.handle_headers(&hs.blocks, dir); + r = self.handle_headers(&hs.blocks, dir); } HTTP2FrameTypeData::HEADERS(hs) => { if dir == Direction::ToClient { self.child_stream_id = 0; } - self.handle_headers(&hs.blocks, dir); + r = self.handle_headers(&hs.blocks, dir); } HTTP2FrameTypeData::RSTSTREAM(_) => { self.child_stream_id = 0; @@ -378,6 +440,29 @@ impl HTTP2Transaction { } _ => {} } + return r; + } + + fn handle_dns_data(&mut self, dir: Direction, flow: *const Flow) { + if let Some(doh) = &mut self.doh { + if !doh.data_buf[dir.index()].is_empty() { + if dir.is_to_client() { + if let Ok(mut dtx) = dns_parse_response(&doh.data_buf[dir.index()]) { + dtx.id = 1; + doh.dns_response_tx = Some(dtx); + unsafe { + AppLayerForceProtocolChange(flow, ALPROTO_DOH2); + } + } + } else if let Ok(mut dtx) = dns_parse_request(&doh.data_buf[dir.index()]) { + dtx.id = 1; + doh.dns_request_tx = Some(dtx); + unsafe { + AppLayerForceProtocolChange(flow, ALPROTO_DOH2); + } + } + } + } } } @@ -603,7 +688,7 @@ impl HTTP2State { tx.state = HTTP2TransactionState::HTTP2StateGlobal; tx.tx_data.update_file_flags(self.state_data.file_flags); // TODO can this tx hold files? - tx.tx_data.file_tx = STREAM_TOSERVER|STREAM_TOCLIENT; // might hold files in both directions + tx.tx_data.file_tx = STREAM_TOSERVER | STREAM_TOCLIENT; // might hold files in both directions tx.update_file_flags(tx.tx_data.file_flags); self.transactions.push_back(tx); return self.transactions.back_mut().unwrap(); @@ -677,7 +762,7 @@ impl HTTP2State { tx.state = HTTP2TransactionState::HTTP2StateOpen; tx.tx_data.update_file_flags(self.state_data.file_flags); tx.update_file_flags(tx.tx_data.file_flags); - tx.tx_data.file_tx = STREAM_TOSERVER|STREAM_TOCLIENT; // might hold files in both directions + tx.tx_data.file_tx = STREAM_TOSERVER | STREAM_TOCLIENT; // might hold files in both directions self.transactions.push_back(tx); return Some(self.transactions.back_mut().unwrap()); } @@ -688,9 +773,7 @@ impl HTTP2State { for block in blocks { if block.error >= parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeError { self.set_event(HTTP2Event::InvalidHeader); - } else if block.error - == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate - { + } else if block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate { update = true; if block.sizeupdate > sizeup { sizeup = block.sizeupdate; @@ -891,13 +974,15 @@ impl HTTP2State { *reass_limit_reached = true; } if head.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS == 0 { - let hs = parser::HTTP2FrameContinuation { - blocks: Vec::new(), - }; + let hs = parser::HTTP2FrameContinuation { blocks: Vec::new() }; return HTTP2FrameTypeData::CONTINUATION(hs); } } // else try to parse anyways - let input_reass = if head.stream_id == buf.stream_id { &buf.data } else { input }; + let input_reass = if head.stream_id == buf.stream_id { + &buf.data + } else { + input + }; let dyn_headers = if dir == Direction::ToClient { &mut self.dynamic_headers_tc @@ -1055,10 +1140,24 @@ impl HTTP2State { return AppLayerResult::err(); } let tx = tx.unwrap(); + if let Some(doh_req_buf) = tx.handle_frame(&head, &txdata, dir) { + if let Ok(mut dtx) = dns_parse_request(&doh_req_buf) { + dtx.id = 1; + unsafe { + AppLayerForceProtocolChange(flow, ALPROTO_DOH2); + } + if let Some(doh) = &mut tx.doh { + doh.dns_request_tx = Some(dtx); + } else { + let doh = DohHttp2Tx { dns_request_tx: Some(dtx), ..Default::default() }; + tx.doh = Some(doh); + } + } + } if reass_limit_reached { - tx.tx_data.set_event(HTTP2Event::ReassemblyLimitReached as u8); + tx.tx_data + .set_event(HTTP2Event::ReassemblyLimitReached as u8); } - tx.handle_frame(&head, &txdata, dir); let over = head.flags & parser::HTTP2_FLAG_HEADER_EOS != 0; let ftype = head.ftype; let sid = head.stream_id; @@ -1087,16 +1186,28 @@ impl HTTP2State { tx_same.ft_ts.tx_id = tx_same.tx_id - 1; }; let mut dinput = &rem[..hlsafe]; - if padded && !rem.is_empty() && usize::from(rem[0]) < hlsafe{ + if padded && !rem.is_empty() && usize::from(rem[0]) < hlsafe { dinput = &rem[1..hlsafe - usize::from(rem[0])]; } - if tx_same.decompress( + let mut output = Vec::with_capacity( + decompression::HTTP2_DECOMPRESSION_CHUNK_SIZE, + ); + match tx_same.decompress( dinput, + &mut output, dir, sfcm, over, - flow).is_err() { - self.set_event(HTTP2Event::FailedDecompression); + flow, + ) { + Ok(_) => { + if over { + tx_same.handle_dns_data(dir, flow); + } + } + _ => { + self.set_event(HTTP2Event::FailedDecompression); + } } } } @@ -1191,6 +1302,24 @@ impl HTTP2State { // C exports. +#[no_mangle] +pub unsafe extern "C" fn SCDoH2GetDnsTx( + tx: &HTTP2Transaction, flags: u8, +) -> *mut std::os::raw::c_void { + if let Some(doh) = &tx.doh { + if flags & Direction::ToServer as u8 != 0 { + if let Some(ref dtx) = &doh.dns_request_tx { + return dtx as *const _ as *mut _; + } + } else if flags & Direction::ToClient as u8 != 0 { + if let Some(ref dtx) = &doh.dns_response_tx { + return dtx as *const _ as *mut _; + } + } + } + std::ptr::null_mut() +} + export_tx_data_get!(rs_http2_get_tx_data, HTTP2Transaction); export_state_data_get!(rs_http2_get_state_data, HTTP2State); @@ -1324,9 +1453,15 @@ pub unsafe extern "C" fn rs_http2_getfiles( let tx = cast_pointer!(tx, HTTP2Transaction); if let Some(sfcm) = { SURICATA_HTTP2_FILE_CONFIG } { if direction & STREAM_TOSERVER != 0 { - return AppLayerGetFileState { fc: &mut tx.ft_ts.file, cfg: sfcm.files_sbcfg } + return AppLayerGetFileState { + fc: &mut tx.ft_ts.file, + cfg: sfcm.files_sbcfg, + }; } else { - return AppLayerGetFileState { fc: &mut tx.ft_tc.file, cfg: sfcm.files_sbcfg } + return AppLayerGetFileState { + fc: &mut tx.ft_tc.file, + cfg: sfcm.files_sbcfg, + }; } } AppLayerGetFileState::err() @@ -1338,7 +1473,7 @@ const PARSER_NAME: &[u8] = b"http2\0"; #[no_mangle] pub unsafe extern "C" fn rs_http2_register_parser() { let default_port = CString::new("[80]").unwrap(); - let parser = RustParser { + let mut parser = RustParser { name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char, default_port: default_port.as_ptr(), ipproto: IPPROTO_TCP, @@ -1405,4 +1540,22 @@ pub unsafe extern "C" fn rs_http2_register_parser() { } else { SCLogNotice!("Protocol detector and parser disabled for HTTP2."); } + + // doh2 is just http2 wrapped in another name + parser.name = b"doh2\0".as_ptr() as *const std::os::raw::c_char; + parser.probe_tc = None; + parser.default_port = std::ptr::null(); + if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let alproto = AppLayerRegisterProtocolDetection(&parser, 1); + ALPROTO_DOH2 = alproto; + if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { + let _ = AppLayerRegisterParser(&parser, alproto); + } else { + SCLogWarning!("DOH2 is not meant to be detection-only."); + } + AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_DOH2); + SCLogDebug!("Rust doh2 parser registered."); + } else { + SCLogNotice!("Protocol detector and parser disabled for DOH2."); + } } diff --git a/rust/src/http2/logger.rs b/rust/src/http2/logger.rs index ca8994e40849..b424e62b8446 100644 --- a/rust/src/http2/logger.rs +++ b/rust/src/http2/logger.rs @@ -17,6 +17,7 @@ use super::http2::{HTTP2Frame, HTTP2FrameTypeData, HTTP2Transaction}; use super::parser; +use crate::dns::log::{SCDnsLogAnswerEnabled, SCDnsLogJsonAnswer, SCDnsLogJsonQuery}; use crate::jsonbuilder::{JsonBuilder, JsonError}; use std; use std::collections::{HashMap, HashSet}; @@ -125,7 +126,10 @@ fn log_http2_frames(frames: &[HTTP2Frame], js: &mut JsonBuilder) -> Result Result( while dyn_headers.current_size > dyn_headers.max_size && toremove < dyn_headers.table.len() { - dyn_headers.current_size -= - 32 + dyn_headers.table[toremove].name.len() + dyn_headers.table[toremove].value.len(); + dyn_headers.current_size -= 32 + + dyn_headers.table[toremove].name.len() + + dyn_headers.table[toremove].value.len(); toremove += 1; } dyn_headers.table.drain(0..toremove); @@ -758,6 +760,19 @@ pub fn http2_parse_frame_settings(i: &[u8]) -> IResult<&[u8], Vec IResult<&[u8], Vec> { + let (i, _) = tag("/dns-query?dns=")(i)?; + match base64::decode(i) { + Ok(dec) => { + // i is unused + return Ok((i, dec)); + } + _ => { + return Err(Err::Error(make_error(i, ErrorKind::MapOpt))); + } + } +} + #[cfg(test)] mod tests { diff --git a/rust/src/http2/range.rs b/rust/src/http2/range.rs index 9c96899443a1..71c22a7b5d01 100644 --- a/rust/src/http2/range.rs +++ b/rust/src/http2/range.rs @@ -131,7 +131,11 @@ pub fn http2_range_open( // whole file in one range return; } - let flags = if dir == Direction::ToServer { tx.ft_ts.file_flags } else { tx.ft_tc.file_flags }; + let flags = if dir == Direction::ToServer { + tx.ft_ts.file_flags + } else { + tx.ft_tc.file_flags + }; if let Ok((key, index)) = http2_range_key_get(tx) { let name = &key[index..]; tx.file_range = unsafe { @@ -151,15 +155,15 @@ pub fn http2_range_open( } } -pub fn http2_range_append(cfg: &'static SuricataFileContext, fr: *mut HttpRangeContainerBlock, data: &[u8]) { +pub fn http2_range_append( + cfg: &'static SuricataFileContext, fr: *mut HttpRangeContainerBlock, data: &[u8], +) { unsafe { HttpRangeAppendData(cfg.files_sbcfg, fr, data.as_ptr(), data.len() as u32); } } -pub fn http2_range_close( - tx: &mut HTTP2Transaction, dir: Direction, data: &[u8], -) { +pub fn http2_range_close(tx: &mut HTTP2Transaction, dir: Direction, data: &[u8]) { let added = if let Some(c) = unsafe { SC } { if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } { let (files, flags) = if dir == Direction::ToServer { @@ -168,13 +172,13 @@ pub fn http2_range_close( (&mut tx.ft_tc.file, tx.ft_tc.file_flags) }; let added = (c.HTPFileCloseHandleRange)( - sfcm.files_sbcfg, - files, - flags, - tx.file_range, - data.as_ptr(), - data.len() as u32, - ); + sfcm.files_sbcfg, + files, + flags, + tx.file_range, + data.as_ptr(), + data.len() as u32, + ); (c.HttpRangeFreeBlock)(tx.file_range); added } else { @@ -197,7 +201,8 @@ extern "C" { data: *const c_uchar, data_len: u32, ) -> *mut HttpRangeContainerBlock; pub fn HttpRangeAppendData( - cfg: *const StreamingBufferConfig, c: *mut HttpRangeContainerBlock, data: *const c_uchar, data_len: u32, + cfg: *const StreamingBufferConfig, c: *mut HttpRangeContainerBlock, data: *const c_uchar, + data_len: u32, ) -> std::os::raw::c_int; } diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index 5748587f74bb..020290165e67 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -1844,6 +1844,16 @@ bool AppLayerRequestProtocolTLSUpgrade(Flow *f) return AppLayerRequestProtocolChange(f, 443, ALPROTO_TLS); } +void AppLayerForceProtocolChange(Flow *f, AppProto new_proto) +{ + if (new_proto != f->alproto) { + f->alproto_orig = f->alproto; + f->alproto = new_proto; + f->alproto_ts = f->alproto; + f->alproto_tc = f->alproto; + } +} + void AppLayerProtoDetectReset(Flow *f) { FLOW_RESET_PM_DONE(f, STREAM_TOSERVER); @@ -2025,6 +2035,9 @@ void AppLayerProtoDetectSupportedIpprotos(AppProto alproto, uint8_t *ipprotos) if (alproto == ALPROTO_HTTP) { AppLayerProtoDetectSupportedIpprotos(ALPROTO_HTTP1, ipprotos); AppLayerProtoDetectSupportedIpprotos(ALPROTO_HTTP2, ipprotos); + } else if (alproto == ALPROTO_DOH2) { + // DOH2 is not detected, just HTTP2 + AppLayerProtoDetectSupportedIpprotos(ALPROTO_HTTP2, ipprotos); } else { AppLayerProtoDetectPMGetIpprotos(alproto, ipprotos); AppLayerProtoDetectPPGetIpprotos(alproto, ipprotos); diff --git a/src/app-layer-detect-proto.h b/src/app-layer-detect-proto.h index 158ad234dd49..adc458ed93f2 100644 --- a/src/app-layer-detect-proto.h +++ b/src/app-layer-detect-proto.h @@ -121,6 +121,8 @@ void AppLayerProtoDetectReset(Flow *); bool AppLayerRequestProtocolChange(Flow *f, uint16_t dp, AppProto expect_proto); bool AppLayerRequestProtocolTLSUpgrade(Flow *f); +void AppLayerForceProtocolChange(Flow *f, AppProto new_proto); + /** * \brief Cleans up the app layer protocol detection phase. */ diff --git a/src/app-layer-protos.c b/src/app-layer-protos.c index babe6ea83adf..283752247f3c 100644 --- a/src/app-layer-protos.c +++ b/src/app-layer-protos.c @@ -61,6 +61,7 @@ const AppProtoStringTuple AppProtoStrings[ALPROTO_MAX] = { { ALPROTO_PGSQL, "pgsql" }, { ALPROTO_TELNET, "telnet" }, { ALPROTO_WEBSOCKET, "websocket" }, + { ALPROTO_DOH2, "doh2" }, { ALPROTO_TEMPLATE, "template" }, { ALPROTO_RDP, "rdp" }, { ALPROTO_HTTP2, "http2" }, diff --git a/src/app-layer-protos.h b/src/app-layer-protos.h index 00a5a54811e8..04b51e27c25a 100644 --- a/src/app-layer-protos.h +++ b/src/app-layer-protos.h @@ -57,6 +57,7 @@ enum AppProtoEnum { ALPROTO_PGSQL, ALPROTO_TELNET, ALPROTO_WEBSOCKET, + ALPROTO_DOH2, ALPROTO_TEMPLATE, ALPROTO_RDP, ALPROTO_HTTP2, @@ -93,6 +94,13 @@ static inline bool AppProtoEquals(AppProto sigproto, AppProto alproto) return true; } switch (sigproto) { + case ALPROTO_DNS: + return (alproto == ALPROTO_DOH2) || (alproto == ALPROTO_DNS); + case ALPROTO_HTTP2: + return (alproto == ALPROTO_DOH2) || (alproto == ALPROTO_HTTP2); + case ALPROTO_DOH2: + return (alproto == ALPROTO_DOH2) || (alproto == ALPROTO_HTTP2) || + (alproto == ALPROTO_DNS) || (alproto == ALPROTO_HTTP); case ALPROTO_HTTP: return (alproto == ALPROTO_HTTP1) || (alproto == ALPROTO_HTTP2); case ALPROTO_DCERPC: @@ -101,6 +109,48 @@ static inline bool AppProtoEquals(AppProto sigproto, AppProto alproto) return false; } +// whether a signature AppProto matches a flow (or signature) AppProto +static inline AppProto AppProtoCommon(AppProto sigproto, AppProto alproto) +{ + switch (sigproto) { + case ALPROTO_SMB: + if (alproto == ALPROTO_DCERPC) { + // ok to have dcerpc keywords in smb sig + return ALPROTO_SMB; + } + break; + case ALPROTO_HTTP: + // we had a generic http sig, now version specific + if (alproto == ALPROTO_HTTP1) { + return ALPROTO_HTTP1; + } else if (alproto == ALPROTO_HTTP2) { + return ALPROTO_HTTP2; + } + break; + case ALPROTO_HTTP1: + // version-specific sig with a generic keyword + if (alproto == ALPROTO_HTTP) { + return ALPROTO_HTTP1; + } + break; + case ALPROTO_HTTP2: + if (alproto == ALPROTO_HTTP) { + return ALPROTO_HTTP2; + } + break; + case ALPROTO_DOH2: + // DOH2 accepts different protocol keywords + if (alproto == ALPROTO_HTTP || alproto == ALPROTO_HTTP2 || alproto == ALPROTO_DNS) { + return ALPROTO_DOH2; + } + break; + } + if (sigproto != alproto) { + return ALPROTO_FAILED; + } + return alproto; +} + /** * \brief Maps the ALPROTO_*, to its string equivalent. * diff --git a/src/app-layer.c b/src/app-layer.c index 0cf8405eab0e..af354168a8c9 100644 --- a/src/app-layer.c +++ b/src/app-layer.c @@ -554,7 +554,7 @@ static int TCPProtoDetect(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, PACKET_PROFILING_APP_START(app_tctx, f->alproto); int r = AppLayerParserParse(tv, app_tctx->alp_tctx, f, f->alproto, flags, data, data_len); - PACKET_PROFILING_APP_END(app_tctx, f->alproto); + PACKET_PROFILING_APP_END(app_tctx); p->app_update_direction = (uint8_t)app_update_dir; if (r != 1) { StreamTcpUpdateAppLayerProgress(ssn, direction, data_len); @@ -642,7 +642,7 @@ static int TCPProtoDetect(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, int r = AppLayerParserParse(tv, app_tctx->alp_tctx, f, f->alproto, flags, data, data_len); - PACKET_PROFILING_APP_END(app_tctx, f->alproto); + PACKET_PROFILING_APP_END(app_tctx); p->app_update_direction = (uint8_t)app_update_dir; if (r != 1) { StreamTcpUpdateAppLayerProgress(ssn, direction, data_len); @@ -751,7 +751,7 @@ int AppLayerHandleTCPData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, Packet PACKET_PROFILING_APP_START(app_tctx, f->alproto); r = AppLayerParserParse(tv, app_tctx->alp_tctx, f, f->alproto, flags, data, data_len); - PACKET_PROFILING_APP_END(app_tctx, f->alproto); + PACKET_PROFILING_APP_END(app_tctx); p->app_update_direction = (uint8_t)app_update_dir; /* ignore parser result for gap */ StreamTcpUpdateAppLayerProgress(ssn, direction, data_len); @@ -837,7 +837,7 @@ int AppLayerHandleTCPData(ThreadVars *tv, TcpReassemblyThreadCtx *ra_ctx, Packet PACKET_PROFILING_APP_START(app_tctx, f->alproto); r = AppLayerParserParse(tv, app_tctx->alp_tctx, f, f->alproto, flags, data, data_len); - PACKET_PROFILING_APP_END(app_tctx, f->alproto); + PACKET_PROFILING_APP_END(app_tctx); p->app_update_direction = (uint8_t)app_update_dir; if (r != 1) { StreamTcpUpdateAppLayerProgress(ssn, direction, data_len); @@ -963,7 +963,7 @@ int AppLayerHandleUdp(ThreadVars *tv, AppLayerThreadCtx *tctx, Packet *p, Flow * PACKET_PROFILING_APP_START(tctx, f->alproto); r = AppLayerParserParse(tv, tctx->alp_tctx, f, f->alproto, flags, p->payload, p->payload_len); - PACKET_PROFILING_APP_END(tctx, f->alproto); + PACKET_PROFILING_APP_END(tctx); p->app_update_direction = (uint8_t)UPDATE_DIR_PACKET; } PACKET_PROFILING_APP_STORE(tctx, p); @@ -979,7 +979,7 @@ int AppLayerHandleUdp(ThreadVars *tv, AppLayerThreadCtx *tctx, Packet *p, Flow * PACKET_PROFILING_APP_START(tctx, f->alproto); r = AppLayerParserParse(tv, tctx->alp_tctx, f, f->alproto, flags, p->payload, p->payload_len); - PACKET_PROFILING_APP_END(tctx, f->alproto); + PACKET_PROFILING_APP_END(tctx); PACKET_PROFILING_APP_STORE(tctx, p); p->app_update_direction = (uint8_t)UPDATE_DIR_PACKET; } diff --git a/src/detect-engine-mpm.c b/src/detect-engine-mpm.c index eca9807df1b8..5e8687e34686 100644 --- a/src/detect-engine-mpm.c +++ b/src/detect-engine-mpm.c @@ -107,6 +107,11 @@ static void RegisterInternal(const char *name, int direction, int priority, FatalError("MPM engine registration for %s failed", name); } + // every HTTP2 can be accessed from DOH2 + if (alproto == ALPROTO_HTTP2 || alproto == ALPROTO_DNS) { + RegisterInternal(name, direction, priority, PrefilterRegister, GetData, GetMultiData, + ALPROTO_DOH2, tx_min_progress); + } DetectBufferMpmRegistry *am = SCCalloc(1, sizeof(*am)); BUG_ON(am == NULL); am->name = name; diff --git a/src/detect-engine-prefilter.c b/src/detect-engine-prefilter.c index 83ccb2afb291..feff1251e4e2 100644 --- a/src/detect-engine-prefilter.c +++ b/src/detect-engine-prefilter.c @@ -107,8 +107,12 @@ void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx, PrefilterEngine *engine = sgh->tx_engines; do { - if (engine->alproto != alproto) + // based on flow alproto, and engine, we get right tx_ptr + void *tx_ptr = DetectGetInnerTx(tx->tx_ptr, alproto, engine->alproto, flow_flags); + if (tx_ptr == NULL) { + // incompatible engine->alproto with flow alproto goto next; + } if (engine->ctx.tx_min_progress > tx->tx_progress) break; if (tx->tx_progress > engine->ctx.tx_min_progress) { @@ -118,8 +122,8 @@ void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx, } PREFILTER_PROFILING_START(det_ctx); - engine->cb.PrefilterTx(det_ctx, engine->pectx, p, p->flow, tx->tx_ptr, tx->tx_id, - tx->tx_data_ptr, flow_flags); + engine->cb.PrefilterTx( + det_ctx, engine->pectx, p, p->flow, tx_ptr, tx->tx_id, tx->tx_data_ptr, flow_flags); PREFILTER_PROFILING_END(det_ctx, engine->gid); if (tx->tx_progress > engine->ctx.tx_min_progress && engine->is_last_for_progress) { diff --git a/src/detect-engine.c b/src/detect-engine.c index 819fa04a39cd..25dba655970c 100644 --- a/src/detect-engine.c +++ b/src/detect-engine.c @@ -200,6 +200,11 @@ static void AppLayerInspectEngineRegisterInternal(const char *name, AppProto alp } else { direction = 1; } + // every DNS or HTTP2 can be accessed from DOH2 + if (alproto == ALPROTO_HTTP2 || alproto == ALPROTO_DNS) { + AppLayerInspectEngineRegisterInternal( + name, ALPROTO_DOH2, dir, progress, Callback, GetData, GetMultiData); + } DetectEngineAppInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEngineAppInspectionEngine)); diff --git a/src/detect-parse.c b/src/detect-parse.c index 61318521f45d..984501c1dd8a 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -1742,20 +1742,9 @@ int DetectSignatureSetAppProto(Signature *s, AppProto alproto) return -1; } - /* since AppProtoEquals is quite permissive wrt dcerpc and smb, make sure - * we refuse `alert dcerpc ... smb.share; content...` explicitly. */ - if (alproto == ALPROTO_SMB && s->alproto == ALPROTO_DCERPC) { - SCLogError("can't set rule app proto to %s: already set to %s", AppProtoToString(alproto), - AppProtoToString(s->alproto)); - return -1; - } - - if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, alproto)) { - if (AppProtoEquals(alproto, s->alproto)) { - // happens if alproto = HTTP_ANY and s->alproto = HTTP1 - // in this case, we must keep the most restrictive HTTP1 - alproto = s->alproto; - } else { + if (s->alproto != ALPROTO_UNKNOWN) { + alproto = AppProtoCommon(s->alproto, alproto); + if (alproto == ALPROTO_FAILED) { SCLogError("can't set rule app proto to %s: already set to %s", AppProtoToString(alproto), AppProtoToString(s->alproto)); return -1; diff --git a/src/detect.c b/src/detect.c index 5d6eb4df3e8a..557e8f805646 100644 --- a/src/detect.c +++ b/src/detect.c @@ -1050,6 +1050,24 @@ DetectRunTxSortHelper(const void *a, const void *b) #define TRACE_SID_TXS(sid,txs,...) #endif +// Get inner transaction for engine +void *DetectGetInnerTx(void *tx_ptr, AppProto alproto, AppProto engine_alproto, uint8_t flow_flags) +{ + if (alproto == ALPROTO_DOH2) { + if (engine_alproto == ALPROTO_DNS) { + // need to get the dns tx pointer + tx_ptr = SCDoH2GetDnsTx(tx_ptr, flow_flags); + } else if (engine_alproto != ALPROTO_HTTP2) { + // incompatible engine->alproto with flow alproto + tx_ptr = NULL; + } + } else if (engine_alproto != alproto) { + // incompatible engine->alproto with flow alproto + tx_ptr = NULL; + } + return tx_ptr; +} + /** \internal * \brief inspect a rule against a transaction * @@ -1110,12 +1128,16 @@ static bool DetectRunTxInspectRule(ThreadVars *tv, if (!(inspect_flags & BIT_U32(engine->id)) && direction == engine->dir) { - const bool skip_engine = (engine->alproto != 0 && engine->alproto != f->alproto); - /* special case: file_data on 'alert tcp' will have engines - * in the list that are not for us. */ - if (unlikely(skip_engine)) { - engine = engine->next; - continue; + void *tx_ptr = DetectGetInnerTx(tx->tx_ptr, f->alproto, engine->alproto, flow_flags); + if (tx_ptr == NULL) { + if (engine->alproto != 0) { + /* special case: file_data on 'alert tcp' will have engines + * in the list that are not for us. */ + engine = engine->next; + continue; + } else { + tx_ptr = tx->tx_ptr; + } } /* engines are sorted per progress, except that the one with @@ -1150,7 +1172,7 @@ static bool DetectRunTxInspectRule(ThreadVars *tv, KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list); DEBUG_VALIDATE_BUG_ON(engine->v2.Callback == NULL); match = engine->v2.Callback( - de_ctx, det_ctx, engine, s, f, flow_flags, alstate, tx->tx_ptr, tx->tx_id); + de_ctx, det_ctx, engine, s, f, flow_flags, alstate, tx_ptr, tx->tx_id); TRACE_SID_TXS(s->id, tx, "engine %p match %d", engine, match); if (engine->stream) { can->stream_stored = true; diff --git a/src/detect.h b/src/detect.h index a4f154523280..3b32cb649ccd 100644 --- a/src/detect.h +++ b/src/detect.h @@ -1598,6 +1598,7 @@ const SigGroupHead *SigMatchSignaturesGetSgh(const DetectEngineCtx *de_ctx, cons int DetectUnregisterThreadCtxFuncs(DetectEngineCtx *, void *data, const char *name); int DetectRegisterThreadCtxFuncs(DetectEngineCtx *, const char *name, void *(*InitFunc)(void *), void *data, void (*FreeFunc)(void *), int); void *DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx *, int); +void *DetectGetInnerTx(void *tx_ptr, AppProto alproto, AppProto engine_alproto, uint8_t flow_flags); void RuleMatchCandidateTxArrayInit(DetectEngineThreadCtx *det_ctx, uint32_t size); void RuleMatchCandidateTxArrayFree(DetectEngineThreadCtx *det_ctx); diff --git a/src/output.c b/src/output.c index 7b13913c831b..0e445defa1b5 100644 --- a/src/output.c +++ b/src/output.c @@ -1092,6 +1092,10 @@ void OutputRegisterLoggers(void) OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonEnipLog", "eve-log.enip", OutputJsonLogInitSub, ALPROTO_ENIP, JsonGenericDirFlowLogger, JsonLogThreadInit, JsonLogThreadDeinit, NULL); + /* DoH2 JSON logger. */ + OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonDoH2Log", "eve-log.doh2", + OutputJsonLogInitSub, ALPROTO_DOH2, JsonGenericDirFlowLogger, JsonLogThreadInit, + JsonLogThreadDeinit, NULL); /* Template JSON logger. */ OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonTemplateLog", "eve-log.template", OutputJsonLogInitSub, ALPROTO_TEMPLATE, JsonGenericDirPacketLogger, JsonLogThreadInit, @@ -1147,6 +1151,7 @@ static EveJsonSimpleAppLayerLogger simple_json_applayer_loggers[ALPROTO_MAX] = { { ALPROTO_PGSQL, JsonPgsqlAddMetadata }, { ALPROTO_TELNET, NULL }, // no logging { ALPROTO_WEBSOCKET, rs_websocket_logger_log }, + { ALPROTO_DOH2, rs_http2_log_json }, // http2 logger knows how to log dns { ALPROTO_TEMPLATE, rs_template_logger_log }, { ALPROTO_RDP, (EveJsonSimpleTxLogFunc)rs_rdp_to_json }, { ALPROTO_HTTP2, rs_http2_log_json }, diff --git a/src/util-profiling.h b/src/util-profiling.h index 564f62b5c78f..094c6de6d209 100644 --- a/src/util-profiling.h +++ b/src/util-profiling.h @@ -170,13 +170,12 @@ PktProfiling *SCProfilePacketStart(void); (dp)->alproto = (id); \ } -#define PACKET_PROFILING_APP_END(dp, id) \ - if (profiling_packets_enabled) { \ - BUG_ON((id) != (dp)->alproto); \ - (dp)->ticks_end = UtilCpuGetTicks(); \ - if ((dp)->ticks_start != 0 && (dp)->ticks_start < ((dp)->ticks_end)) { \ - (dp)->ticks_spent = ((dp)->ticks_end - (dp)->ticks_start); \ - } \ +#define PACKET_PROFILING_APP_END(dp) \ + if (profiling_packets_enabled) { \ + (dp)->ticks_end = UtilCpuGetTicks(); \ + if ((dp)->ticks_start != 0 && (dp)->ticks_start < ((dp)->ticks_end)) { \ + (dp)->ticks_spent = ((dp)->ticks_end - (dp)->ticks_start); \ + } \ } #define PACKET_PROFILING_APP_PD_START(dp) \ @@ -340,7 +339,7 @@ void SCProfilingDump(void); #define PACKET_PROFILING_RESET(p) #define PACKET_PROFILING_APP_START(dp, id) -#define PACKET_PROFILING_APP_END(dp, id) +#define PACKET_PROFILING_APP_END(d) #define PACKET_PROFILING_APP_RESET(dp) #define PACKET_PROFILING_APP_STORE(dp, p) diff --git a/suricata.yaml.in b/suricata.yaml.in index 0ba63086d0d1..0dac28a9fd94 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -315,6 +315,8 @@ outputs: - mqtt: # passwords: yes # enable output of passwords - http2 + # dns over http2 + - doh2 - pgsql: enabled: no # passwords: yes # enable output of passwords. Disabled by default @@ -942,6 +944,8 @@ app-layer: ssh: enabled: yes #hassh: yes + doh2: + enabled: yes http2: enabled: yes # Maximum number of live HTTP2 streams in a flow