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", "description": "Errors encountered parsing DNS/UDP protocol",
"$ref": "#/$defs/stats_applayer_error" "$ref": "#/$defs/stats_applayer_error"
}, },
"doh2": {
"$ref": "#/$defs/stats_applayer_error"
},
"enip_tcp": { "enip_tcp": {
"description": "Errors encounterd parsing ENIP/TCP", "description": "Errors encounterd parsing ENIP/TCP",
"$ref": "#/$defs/stats_applayer_error" "$ref": "#/$defs/stats_applayer_error"
@ -4753,6 +4756,9 @@
"description": "Number of flows for DNS/UDP protocol", "description": "Number of flows for DNS/UDP protocol",
"type": "integer" "type": "integer"
}, },
"doh2": {
"type": "integer"
},
"enip_tcp": { "enip_tcp": {
"description": "Number of flows for ENIP/TCP", "description": "Number of flows for ENIP/TCP",
"type": "integer" "type": "integer"
@ -4922,6 +4928,9 @@
"description": "Number of transactions for DNS/UDP protocol", "description": "Number of transactions for DNS/UDP protocol",
"type": "integer" "type": "integer"
}, },
"doh2": {
"type": "integer"
},
"enip_tcp": { "enip_tcp": {
"description": "Number of transactions for ENIP/TCP", "description": "Number of transactions for ENIP/TCP",
"type": "integer" "type": "integer"

@ -471,6 +471,7 @@ pub unsafe fn AppLayerRegisterParser(parser: *const RustParser, alproto: AppProt
// Defined in app-layer-detect-proto.h // Defined in app-layer-detect-proto.h
extern { extern {
pub fn AppLayerForceProtocolChange(f: *const Flow, new_proto: AppProto);
pub fn AppLayerProtoDetectPPRegister(ipproto: u8, portstr: *const c_char, alproto: AppProto, pub fn AppLayerProtoDetectPPRegister(ipproto: u8, portstr: *const c_char, alproto: AppProto,
min_depth: u16, max_depth: u16, dir: u8, min_depth: u16, max_depth: u16, dir: u8,
pparser1: ProbeFn, pparser2: ProbeFn); pparser1: ProbeFn, pparser2: ProbeFn);

@ -36,6 +36,7 @@ use std::fmt;
use std::io; use std::io;
static mut ALPROTO_HTTP2: AppProto = ALPROTO_UNKNOWN; static mut ALPROTO_HTTP2: AppProto = ALPROTO_UNKNOWN;
static mut ALPROTO_DOH2: AppProto = ALPROTO_UNKNOWN;
const HTTP2_DEFAULT_MAX_FRAME_SIZE: u32 = 16384; const HTTP2_DEFAULT_MAX_FRAME_SIZE: u32 = 16384;
const HTTP2_MAX_HANDLED_FRAME_SIZE: usize = 65536; 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 Some(p) = path {
if let Ok((_, dns_req)) = parser::doh_extract_request(p) { if let Ok((_, dns_req)) = parser::doh_extract_request(p) {
return Some(dns_req); return Some(dns_req);
@ -344,10 +345,12 @@ impl HTTP2Transaction {
&xid, &xid,
); );
}; };
// we store DNS response, and process it when complete if unsafe {ALPROTO_DOH2} != ALPROTO_UNKNOWN {
if self.is_doh_response && self.doh_response_buf.len() < 0xFFFF { // we store DNS response, and process it when complete
// a DNS message is U16_MAX if self.is_doh_response && self.doh_response_buf.len() < 0xFFFF {
self.doh_response_buf.extend_from_slice(decompressed); // a DNS message is U16_MAX
self.doh_response_buf.extend_from_slice(decompressed);
}
} }
return Ok(()); return Ok(());
} }
@ -1105,6 +1108,15 @@ impl HTTP2State {
return AppLayerResult::err(); return AppLayerResult::err();
} }
let tx = tx.unwrap(); 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 { if reass_limit_reached {
tx.tx_data tx.tx_data
.set_event(HTTP2Event::ReassemblyLimitReached as u8); .set_event(HTTP2Event::ReassemblyLimitReached as u8);
@ -1153,12 +1165,17 @@ impl HTTP2State {
flow, flow,
) { ) {
Ok(_) => { Ok(_) => {
if !tx_same.doh_response_buf.is_empty() { if !tx_same.doh_response_buf.is_empty() && over {
if over { if let Ok(mut dtx) =
if let Ok(dtx) = dns_parse_response( dns_parse_response(&tx_same.doh_response_buf)
&tx_same.doh_response_buf, {
) { dtx.id = 1;
tx_same.dns_response_tx = Some(dtx); 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] #[no_mangle]
pub unsafe extern "C" fn rs_http2_register_parser() { pub unsafe extern "C" fn rs_http2_register_parser() {
let default_port = CString::new("[80]").unwrap(); 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, name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
default_port: default_port.as_ptr(), default_port: default_port.as_ptr(),
ipproto: IPPROTO_TCP, ipproto: IPPROTO_TCP,
@ -1479,4 +1496,22 @@ pub unsafe extern "C" fn rs_http2_register_parser() {
} else { } else {
SCLogNotice!("Protocol detector and parser disabled for HTTP2."); 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); 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) void AppLayerProtoDetectReset(Flow *f)
{ {
FLOW_RESET_PM_DONE(f, STREAM_TOSERVER); 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 AppLayerRequestProtocolChange(Flow *f, uint16_t dp, AppProto expect_proto);
bool AppLayerRequestProtocolTLSUpgrade(Flow *f); bool AppLayerRequestProtocolTLSUpgrade(Flow *f);
void AppLayerForceProtocolChange(Flow *f, AppProto new_proto);
/** /**
* \brief Cleans up the app layer protocol detection phase. * \brief Cleans up the app layer protocol detection phase.
*/ */

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

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

@ -1096,6 +1096,10 @@ void OutputRegisterLoggers(void)
OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonLdapLog", "eve-log.ldap", OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonLdapLog", "eve-log.ldap",
OutputJsonLogInitSub, ALPROTO_LDAP, JsonGenericDirPacketLogger, JsonLogThreadInit, OutputJsonLogInitSub, ALPROTO_LDAP, JsonGenericDirPacketLogger, JsonLogThreadInit,
JsonLogThreadDeinit, NULL); 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. */ /* Template JSON logger. */
OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonTemplateLog", "eve-log.template", OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonTemplateLog", "eve-log.template",
OutputJsonLogInitSub, ALPROTO_TEMPLATE, JsonGenericDirPacketLogger, JsonLogThreadInit, OutputJsonLogInitSub, ALPROTO_TEMPLATE, JsonGenericDirPacketLogger, JsonLogThreadInit,
@ -1152,6 +1156,7 @@ static EveJsonSimpleAppLayerLogger simple_json_applayer_loggers[ALPROTO_MAX] = {
{ ALPROTO_TELNET, NULL }, // no logging { ALPROTO_TELNET, NULL }, // no logging
{ ALPROTO_WEBSOCKET, rs_websocket_logger_log }, { ALPROTO_WEBSOCKET, rs_websocket_logger_log },
{ ALPROTO_LDAP, rs_ldap_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_TEMPLATE, rs_template_logger_log },
{ ALPROTO_RDP, (EveJsonSimpleTxLogFunc)rs_rdp_to_json }, { ALPROTO_RDP, (EveJsonSimpleTxLogFunc)rs_rdp_to_json },
{ ALPROTO_HTTP2, rs_http2_log_json }, { ALPROTO_HTTP2, rs_http2_log_json },

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

Loading…
Cancel
Save