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;
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
pub escaped: Vec<Vec<u8>>,
pub req_line: Vec<u8>,
pub resp_line: Vec<u8>,
+
+ is_doh_response: bool,
+ // dns response buffer
+ pub doh_response_buf: Vec<u8>,
+ pub dns_request_tx: Option<DNSTransaction>,
+ pub dns_response_tx: Option<DNSTransaction>,
}
impl Transaction for HTTP2Transaction {
escaped: Vec::with_capacity(16),
req_line: Vec::new(),
resp_line: Vec::new(),
+ is_doh_response: false,
+ doh_response_buf: Vec::new(),
+ dns_request_tx: None,
+ dns_response_tx: None,
}
}
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<Vec<u8>> {
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" {
+ self.is_doh_response = true;
+ }
+ } 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'@') {
}
}
}
+ if doh {
+ 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) {
}
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<u8>, 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;
&xid,
);
};
+ // we store DNS response, and process it when complete
+ if self.is_doh_response && self.doh_response_buf.len() < 0xFFFF {
+ // a DNS message is U16_MAX
+ self.doh_response_buf.extend_from_slice(decompressed);
+ }
return Ok(());
}
fn handle_frame(
&mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: Direction,
- ) {
+ ) -> Option<Vec<u8>> {
//handle child_stream_id changes
+ let mut r = None;
match data {
HTTP2FrameTypeData::PUSHPROMISE(hs) => {
if dir == Direction::ToClient {
}
self.state = HTTP2TransactionState::HTTP2StateReserved;
}
- self.handle_headers(&hs.blocks, dir);
+ r = self.handle_headers(&hs.blocks, dir);
}
HTTP2FrameTypeData::CONTINUATION(hs) => {
if dir == Direction::ToClient
{
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;
}
_ => {}
}
+ return r;
}
}
*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
}
let tx = tx.unwrap();
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;
if padded && !rem.is_empty() && usize::from(rem[0]) < hlsafe {
dinput = &rem[1..hlsafe - usize::from(rem[0])];
}
- if tx_same.decompress(dinput, dir, sfcm, over, flow).is_err() {
- self.set_event(HTTP2Event::FailedDecompression);
+ let mut output = Vec::with_capacity(
+ decompression::HTTP2_DECOMPRESSION_CHUNK_SIZE,
+ );
+ match tx_same.decompress(
+ dinput,
+ &mut output,
+ dir,
+ sfcm,
+ over,
+ flow,
+ ) {
+ Ok(_) => {
+ if !tx_same.doh_response_buf.is_empty() {
+ if over {
+ if let Ok(dtx) = dns_parse_response(
+ &tx_same.doh_response_buf,
+ ) {
+ tx_same.dns_response_tx = Some(dtx);
+ }
+ }
+ }
+ }
+ _ => {
+ self.set_event(HTTP2Event::FailedDecompression);
+ }
}
}
}
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};
js.close()?; // http2
js.close()?; // http
+ if tx.dns_request_tx.is_some() || tx.dns_response_tx.is_some() {
+ js.open_object("dns")?;
+ if let Some(dtx) = &tx.dns_request_tx {
+ let mark = js.get_mark();
+ let mut has_dns_query = false;
+ js.open_array("query")?;
+ for i in 0..0xFFFF {
+ let mut jsa = JsonBuilder::try_new_object()?;
+ if !SCDnsLogJsonQuery(dtx, i, 0xFFFFFFFFFFFFFFFF, &mut jsa) {
+ break;
+ }
+ jsa.close()?;
+ js.append_object(&jsa)?;
+ has_dns_query = true;
+ }
+ if has_dns_query {
+ js.close()?; // query
+ } else {
+ js.restore_mark(&mark)?;
+ }
+ }
+ if let Some(dtx) = &tx.dns_response_tx {
+ if SCDnsLogAnswerEnabled(dtx, 0xFFFFFFFFFFFFFFFF) {
+ // logging at root of dns object
+ SCDnsLogJsonAnswer(dtx, 0xFFFFFFFFFFFFFFFF, js);
+ }
+ }
+ js.close()?; // dns
+ }
return Ok(has_request || has_response || has_headers);
}