doh: implement dns over http2 app-proto

Ticket: 5773
pull/11536/head
Philippe Antoine 2 years ago committed by Victor Julien
parent 46d98ae81c
commit 1e82e20c65

@ -4591,6 +4591,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"
@ -4753,6 +4756,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"
@ -4922,6 +4928,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"

@ -471,6 +471,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);

@ -36,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;
@ -259,7 +260,7 @@ impl HTTP2Transaction {
}
}
}
if doh {
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);
@ -344,10 +345,12 @@ impl HTTP2Transaction {
&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);
if unsafe {ALPROTO_DOH2} != ALPROTO_UNKNOWN {
// 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(());
}
@ -1105,6 +1108,15 @@ 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;
tx.dns_request_tx = Some(dtx);
unsafe {
AppLayerForceProtocolChange(flow, ALPROTO_DOH2);
}
}
}
if reass_limit_reached {
tx.tx_data
.set_event(HTTP2Event::ReassemblyLimitReached as u8);
@ -1153,12 +1165,17 @@ impl HTTP2State {
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);
if !tx_same.doh_response_buf.is_empty() && over {
if let Ok(mut dtx) =
dns_parse_response(&tx_same.doh_response_buf)
{
dtx.id = 1;
tx_same.dns_response_tx = Some(dtx);
unsafe {
AppLayerForceProtocolChange(
flow,
ALPROTO_DOH2,
);
}
}
}
@ -1413,7 +1430,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,
@ -1479,4 +1496,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.");
}
}

@ -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);

@ -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.
*/

@ -62,6 +62,7 @@ const AppProtoStringTuple AppProtoStrings[ALPROTO_MAX] = {
{ ALPROTO_TELNET, "telnet" },
{ ALPROTO_WEBSOCKET, "websocket" },
{ ALPROTO_LDAP, "ldap" },
{ ALPROTO_DOH2, "doh2" },
{ ALPROTO_TEMPLATE, "template" },
{ ALPROTO_RDP, "rdp" },
{ ALPROTO_HTTP2, "http2" },

@ -58,6 +58,7 @@ enum AppProtoEnum {
ALPROTO_TELNET,
ALPROTO_WEBSOCKET,
ALPROTO_LDAP,
ALPROTO_DOH2,
ALPROTO_TEMPLATE,
ALPROTO_RDP,
ALPROTO_HTTP2,

@ -1096,6 +1096,10 @@ void OutputRegisterLoggers(void)
OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonLdapLog", "eve-log.ldap",
OutputJsonLogInitSub, ALPROTO_LDAP, JsonGenericDirPacketLogger, 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,
@ -1152,6 +1156,7 @@ static EveJsonSimpleAppLayerLogger simple_json_applayer_loggers[ALPROTO_MAX] = {
{ ALPROTO_TELNET, NULL }, // no logging
{ ALPROTO_WEBSOCKET, rs_websocket_logger_log },
{ ALPROTO_LDAP, rs_ldap_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 },

@ -321,6 +321,8 @@ outputs:
# the maximum parsed message size (see
# app-layer configuration)
- http2
# dns over http2
- doh2
- pgsql:
enabled: no
# passwords: yes # enable output of passwords. Disabled by default
@ -948,6 +950,8 @@ app-layer:
ssh:
enabled: yes
#hassh: yes
doh2:
enabled: yes
http2:
enabled: yes
# Maximum number of live HTTP2 streams in a flow

Loading…
Cancel
Save