Skip to content

Commit

Permalink
doh: move fields into dedicated Optional struct
Browse files Browse the repository at this point in the history
So as to consume less memory for HTTP2Transaction
  • Loading branch information
catenacyber authored and victorjulien committed Jul 20, 2024
1 parent 6e12475 commit 8aa2964
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 37 deletions.
92 changes: 58 additions & 34 deletions rust/src/http2/http2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,19 @@ pub struct HTTP2Frame {
pub data: HTTP2FrameTypeData,
}

#[derive(Debug, Default)]
/// Dns Over HTTP2 Data inside a HTTP2 transaction
pub struct DohHttp2Tx {
/// wether the HTTP2 data is DNS, for both directions
is_doh_data: [bool; 2],
/// http2 data buffer to parse as DNS on completion
pub data_buf: [Vec<u8>; 2],
/// dns request transation
pub dns_request_tx: Option<DNSTransaction>,
/// dns response transation
pub dns_response_tx: Option<DNSTransaction>,
}

#[derive(Debug)]
pub struct HTTP2Transaction {
tx_id: u64,
Expand All @@ -151,11 +164,7 @@ pub struct HTTP2Transaction {
pub req_line: Vec<u8>,
pub resp_line: Vec<u8>,

is_doh_data: [bool; 2],
// dns response buffer
pub doh_data_buf: [Vec<u8>; 2],
pub dns_request_tx: Option<DNSTransaction>,
pub dns_response_tx: Option<DNSTransaction>,
pub doh: Option<DohHttp2Tx>,
}

impl Transaction for HTTP2Transaction {
Expand Down Expand Up @@ -187,10 +196,7 @@ impl HTTP2Transaction {
escaped: Vec::with_capacity(16),
req_line: Vec::new(),
resp_line: Vec::new(),
is_doh_data: [false; 2],
doh_data_buf: Default::default(),
dns_request_tx: None,
dns_response_tx: None,
doh: None,
}
}

Expand Down Expand Up @@ -235,7 +241,13 @@ impl HTTP2Transaction {
}
} else if block.name.as_ref() == b"content-type" {
if block.value.as_ref() == b"application/dns-message" {
self.is_doh_data[dir.index()] = true;
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);
Expand Down Expand Up @@ -347,9 +359,11 @@ impl HTTP2Transaction {
};
if unsafe { ALPROTO_DOH2 } != ALPROTO_UNKNOWN {
// we store DNS response, and process it when complete
if self.is_doh_data[dir.index()] && self.doh_data_buf[dir.index()].len() < 0xFFFF {
// a DNS message is U16_MAX
self.doh_data_buf[dir.index()].extend_from_slice(decompressed);
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(());
Expand Down Expand Up @@ -433,22 +447,24 @@ impl HTTP2Transaction {
return r;
}

fn handle_dns_data(&mut self, over: bool, dir: Direction, flow: *const Flow) {
if !self.doh_data_buf[dir.index()].is_empty() && over {
if dir.is_to_client() {
if let Ok(mut dtx) = dns_parse_response(&self.doh_data_buf[dir.index()]) {
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;
self.dns_response_tx = Some(dtx);
doh.dns_request_tx = Some(dtx);
unsafe {
AppLayerForceProtocolChange(flow, ALPROTO_DOH2);
}
}
} else if let Ok(mut dtx) = dns_parse_request(&self.doh_data_buf[dir.index()]) {
dtx.id = 1;
self.dns_request_tx = Some(dtx);
unsafe {
AppLayerForceProtocolChange(flow, ALPROTO_DOH2);
}
}
}
}
Expand Down Expand Up @@ -1131,17 +1147,21 @@ impl HTTP2State {
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;
tx.dns_request_tx = Some(dtx);
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.handle_frame(&head, &txdata, dir);
let over = head.flags & parser::HTTP2_FLAG_HEADER_EOS != 0;
let ftype = head.ftype;
let sid = head.stream_id;
Expand Down Expand Up @@ -1185,7 +1205,9 @@ impl HTTP2State {
flow,
) {
Ok(_) => {
tx_same.handle_dns_data(over, dir, flow);
if over {
tx_same.handle_dns_data(dir, flow);
}
}
_ => {
self.set_event(HTTP2Event::FailedDecompression);
Expand Down Expand Up @@ -1288,13 +1310,15 @@ impl HTTP2State {
pub unsafe extern "C" fn SCDoH2GetDnsTx(
tx: &HTTP2Transaction, flags: u8,
) -> *mut std::os::raw::c_void {
if flags & Direction::ToServer as u8 != 0 {
if let Some(ref dtx) = &tx.dns_request_tx {
return dtx as *const _ as *mut _;
}
} else if flags & Direction::ToClient as u8 != 0 {
if let Some(ref dtx) = &tx.dns_response_tx {
return dtx as *const _ as *mut _;
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()
Expand Down
6 changes: 3 additions & 3 deletions rust/src/http2/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,9 @@ fn log_http2(tx: &HTTP2Transaction, js: &mut JsonBuilder) -> Result<bool, JsonEr
js.close()?; // http2
js.close()?; // http

if tx.dns_request_tx.is_some() || tx.dns_response_tx.is_some() {
if let Some(doh) = &tx.doh {
js.open_object("dns")?;
if let Some(dtx) = &tx.dns_request_tx {
if let Some(dtx) = &doh.dns_request_tx {
let mark = js.get_mark();
let mut has_dns_query = false;
js.open_array("query")?;
Expand All @@ -303,7 +303,7 @@ fn log_http2(tx: &HTTP2Transaction, js: &mut JsonBuilder) -> Result<bool, JsonEr
js.restore_mark(&mark)?;
}
}
if let Some(dtx) = &tx.dns_response_tx {
if let Some(dtx) = &doh.dns_response_tx {
if SCDnsLogAnswerEnabled(dtx, 0xFFFFFFFFFFFFFFFF) {
// logging at root of dns object
SCDnsLogJsonAnswer(dtx, 0xFFFFFFFFFFFFFFFF, js);
Expand Down

0 comments on commit 8aa2964

Please sign in to comment.