mirror of https://github.com/OISF/suricata
mdns: add mdns parser, logger and detection
The mDNS support is based heavily on the DNS support, reusing the
existing DNS parser where possible. This meant adding variations on
DNS, as mDNS is a little different. Mainly being that *all* mDNS
traffic is to_server, yet there is still the concept of request and
responses.
Keywords added are:
- mdns.queries.rrname
- mdns.answers.rrname
- mdns.additionals.rrname
- mdns.authorities.rrname
- mdns.response.rrname
They are mostly in-line with the DNS keywords, except
mdns.answers.rdata which is a better than that mdns.response.rrname,
as its actually looking at the rdata, and not rrnames.
mDNS has its own logger that differs from the DNS logger:
- No grouped logging
- In answers/additionals/authorities, the rdata is logged in a field
that is named after the rdata type. For example, "txt" data is no
longer logged in the "rdata" field, but instead a "txt" field. We
currently already did this in DNS for fields that were not a single
buffer, like SOA, SRV, etc. So this makes things more consistent. And
gives query like semantics that the "grouped" object was trying to
provide.
- Types are logged in lower case ("txt" instead of "TXT")
- Flags are logged as an array: "flags": ["aa", "z"]
Ticket: #3952
pull/13334/head
parent
de88d8ec48
commit
4a655053e8
@ -0,0 +1,93 @@
|
||||
mDNS Keywords
|
||||
=============
|
||||
|
||||
Suricata supports sticky buffers for efficiently matching on specific
|
||||
fields in mDNS (Multicast DNS) messages.
|
||||
|
||||
Note that sticky buffers are expected to be followed by one or more
|
||||
:doc:`payload-keywords`.
|
||||
|
||||
mdns.queries.rrname
|
||||
-------------------
|
||||
|
||||
``mdns.queries.rrname`` is a sticky buffer that is used to look at the
|
||||
name field in mDNS query resource records.
|
||||
|
||||
The buffer being matched on contains the complete re-assembled
|
||||
resource name, for example "host.local".
|
||||
|
||||
``mdns.queries.rrname`` supports :doc:`multi-buffer-matching`.
|
||||
|
||||
Example::
|
||||
|
||||
alert udp any any -> any 5353 (msg:"mDNS query for .local domain"; \
|
||||
mdns.queries.rrname; content:".local"; sid:1;)
|
||||
|
||||
mdns.answers.rrname
|
||||
-------------------
|
||||
|
||||
``mdns.answers.rrname`` is a sticky buffer that is used to look at the
|
||||
name field in mDNS answer resource records.
|
||||
|
||||
The buffer being matched on contains the complete re-assembled
|
||||
resource name, for example "printer.local".
|
||||
|
||||
``mdns.answers.rrname`` supports :doc:`multi-buffer-matching`.
|
||||
|
||||
Example::
|
||||
|
||||
alert udp any 5353 -> any any (msg:"mDNS answer for printer.local"; \
|
||||
mdns.answers.rrname; content:"printer.local"; sid:2;)
|
||||
|
||||
mdns.authorities.rrname
|
||||
-----------------------
|
||||
|
||||
``mdns.authorities.rrname`` is a sticky buffer that is used to look at the
|
||||
rrname field in mDNS authority resource records.
|
||||
|
||||
The buffer being matched on contains the complete re-assembled
|
||||
resource name, for example "device.local".
|
||||
|
||||
``mdns.authorities.rrname`` supports :doc:`multi-buffer-matching`.
|
||||
|
||||
Example::
|
||||
|
||||
alert udp any 5353 -> any any (msg:"mDNS authority record check"; \
|
||||
mdns.authorities.rrname; content:"auth.local"; sid:3;)
|
||||
|
||||
mdns.additionals.rrname
|
||||
-----------------------
|
||||
|
||||
``mdns.additionals.rrname`` is a sticky buffer that is used to look at
|
||||
the rrname field in mDNS additional resource records.
|
||||
|
||||
The buffer being matched on contains the complete re-assembled
|
||||
resource name, for example "service.local".
|
||||
|
||||
``mdns.additionals.rrname`` supports :doc:`multi-buffer-matching`.
|
||||
|
||||
Example::
|
||||
|
||||
alert udp any any -> any 5353 (msg:"mDNS additional record check"; \
|
||||
mdns.additionals.rrname; content:"_companion-link._tcp.local"; nocase; sid:4;)
|
||||
|
||||
mdns.response.rrname
|
||||
--------------------
|
||||
|
||||
``mdns.response.rrname`` is a sticky buffer that is used to inspect
|
||||
all the rrname fields in a response, in the queries, answers,
|
||||
additionals and authorities. Additionally it will also inspect rdata
|
||||
fields that have the same format as an rrname (hostname).
|
||||
|
||||
``rdata`` types that will be inspected are:
|
||||
|
||||
* CNAME
|
||||
* PTR
|
||||
* MX
|
||||
* NS
|
||||
* SOA
|
||||
|
||||
Example::
|
||||
|
||||
alert udp any 5353 -> any any (msg:"mDNS answer data match"; \
|
||||
mdns.response.rrname; content:"Apple TV"; sid:5;)
|
||||
@ -0,0 +1,180 @@
|
||||
/* Copyright (C) 2025 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use crate::dns::dns::*;
|
||||
use crate::dns::log::{
|
||||
dns_log_opt, dns_log_soa, dns_log_srv, dns_log_sshfp, dns_print_addr, dns_rrtype_string,
|
||||
};
|
||||
use crate::jsonbuilder::{JsonBuilder, JsonError};
|
||||
|
||||
fn mdns_log_json_answer_detail(answer: &DNSAnswerEntry) -> Result<JsonBuilder, JsonError> {
|
||||
let mut jsa = JsonBuilder::try_new_object()?;
|
||||
|
||||
jsa.set_string_from_bytes("rrname", &answer.name.value)?;
|
||||
if answer.name.flags.contains(DNSNameFlags::TRUNCATED) {
|
||||
jsa.set_bool("rrname_truncated", true)?;
|
||||
}
|
||||
let rrtype = dns_rrtype_string(answer.rrtype).to_lowercase();
|
||||
|
||||
match &answer.data {
|
||||
DNSRData::A(addr) | DNSRData::AAAA(addr) => {
|
||||
jsa.set_string(&rrtype, &dns_print_addr(addr))?;
|
||||
}
|
||||
DNSRData::CNAME(name) | DNSRData::MX(name) | DNSRData::NS(name) | DNSRData::PTR(name) => {
|
||||
jsa.set_string_from_bytes(&rrtype, &name.value)?;
|
||||
if name.flags.contains(DNSNameFlags::TRUNCATED) {
|
||||
jsa.set_bool("rdata_truncated", true)?;
|
||||
}
|
||||
}
|
||||
DNSRData::TXT(txt) => {
|
||||
jsa.open_array(&rrtype)?;
|
||||
for txt in txt {
|
||||
jsa.append_string_from_bytes(txt)?;
|
||||
}
|
||||
jsa.close()?;
|
||||
}
|
||||
DNSRData::NULL(bytes) | DNSRData::Unknown(bytes) => {
|
||||
jsa.set_string_from_bytes(&rrtype, bytes)?;
|
||||
}
|
||||
DNSRData::SOA(soa) => {
|
||||
jsa.set_object(&rrtype, &dns_log_soa(soa)?)?;
|
||||
}
|
||||
DNSRData::SSHFP(sshfp) => {
|
||||
jsa.set_object(&rrtype, &dns_log_sshfp(sshfp)?)?;
|
||||
}
|
||||
DNSRData::SRV(srv) => {
|
||||
jsa.set_object(&rrtype, &dns_log_srv(srv)?)?;
|
||||
}
|
||||
DNSRData::OPT(opt) => {
|
||||
jsa.open_array(&rrtype)?;
|
||||
for val in opt {
|
||||
jsa.append_object(&dns_log_opt(val)?)?;
|
||||
}
|
||||
jsa.close()?;
|
||||
}
|
||||
}
|
||||
|
||||
jsa.close()?;
|
||||
return Ok(jsa);
|
||||
}
|
||||
|
||||
fn log_json(tx: &DNSTransaction, jb: &mut JsonBuilder) -> Result<(), JsonError> {
|
||||
jb.open_object("mdns")?;
|
||||
|
||||
let message = if let Some(request) = &tx.request {
|
||||
jb.set_string("type", "request")?;
|
||||
request
|
||||
} else if let Some(response) = &tx.response {
|
||||
jb.set_string("type", "response")?;
|
||||
response
|
||||
} else {
|
||||
debug_validate_fail!("unreachable");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// The on the wire mDNS transaction ID.
|
||||
jb.set_uint("id", tx.tx_id() as u64)?;
|
||||
|
||||
let header = &message.header;
|
||||
if header.flags & (0x0400 | 0x0200 | 0x0100 | 0x0080 | 0x0040 | 0x0020 | 0x0010) != 0 {
|
||||
jb.open_array("flags")?;
|
||||
if header.flags & 0x0400 != 0 {
|
||||
jb.append_string("aa")?;
|
||||
}
|
||||
if header.flags & 0x0200 != 0 {
|
||||
jb.append_string("tc")?;
|
||||
}
|
||||
if header.flags & 0x0100 != 0 {
|
||||
jb.append_string("rd")?;
|
||||
}
|
||||
if header.flags & 0x0080 != 0 {
|
||||
jb.append_string("ra")?;
|
||||
}
|
||||
if header.flags & 0x0040 != 0 {
|
||||
jb.append_string("z")?;
|
||||
}
|
||||
if header.flags & 0x0020 != 0 {
|
||||
jb.append_string("ad")?;
|
||||
}
|
||||
if header.flags & 0x0010 != 0 {
|
||||
jb.append_string("cd")?;
|
||||
}
|
||||
jb.close()?;
|
||||
}
|
||||
|
||||
let opcode = ((header.flags >> 11) & 0xf) as u8;
|
||||
jb.set_uint("opcode", opcode as u64)?;
|
||||
jb.set_uint("rcode", header.flags & 0x000f)?;
|
||||
|
||||
if !message.queries.is_empty() {
|
||||
jb.open_array("queries")?;
|
||||
for query in &message.queries {
|
||||
jb.start_object()?
|
||||
.set_string_from_bytes("rrname", &query.name.value)?
|
||||
.set_string("rrtype", &dns_rrtype_string(query.rrtype).to_lowercase())?;
|
||||
if query.name.flags.contains(DNSNameFlags::TRUNCATED) {
|
||||
jb.set_bool("rrname_truncated", true)?;
|
||||
}
|
||||
jb.close()?;
|
||||
}
|
||||
jb.close()?;
|
||||
}
|
||||
|
||||
if !message.answers.is_empty() {
|
||||
jb.open_array("answers")?;
|
||||
for entry in &message.answers {
|
||||
jb.append_object(&mdns_log_json_answer_detail(entry)?)?;
|
||||
}
|
||||
jb.close()?;
|
||||
}
|
||||
|
||||
if !message.authorities.is_empty() {
|
||||
jb.open_array("authorities")?;
|
||||
for entry in &message.authorities {
|
||||
jb.append_object(&mdns_log_json_answer_detail(entry)?)?;
|
||||
}
|
||||
jb.close()?;
|
||||
}
|
||||
|
||||
if !message.additionals.is_empty() {
|
||||
let mut is_jb_open = false;
|
||||
for entry in &message.additionals {
|
||||
if let DNSRData::OPT(rdata) = &entry.data {
|
||||
if rdata.is_empty() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if !is_jb_open {
|
||||
jb.open_array("additionals")?;
|
||||
is_jb_open = true;
|
||||
}
|
||||
jb.append_object(&mdns_log_json_answer_detail(entry)?)?;
|
||||
}
|
||||
if is_jb_open {
|
||||
jb.close()?;
|
||||
}
|
||||
}
|
||||
|
||||
jb.close()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// FFI wrapper around the common V3 style mDNS logger.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn SCMdnsLogJson(tx: &DNSTransaction, jb: &mut JsonBuilder) -> bool {
|
||||
log_json(tx, jb).is_ok()
|
||||
}
|
||||
@ -0,0 +1,131 @@
|
||||
/* Copyright (C) 2025 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use std;
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::applayer::*;
|
||||
use crate::core::*;
|
||||
use crate::direction::Direction;
|
||||
use crate::dns::dns;
|
||||
use crate::flow::Flow;
|
||||
|
||||
use suricata_sys::sys::DetectEngineThreadCtx;
|
||||
use suricata_sys::sys::{AppProto, AppProtoEnum};
|
||||
|
||||
pub(super) static mut ALPROTO_MDNS: AppProto = ALPROTO_UNKNOWN;
|
||||
|
||||
unsafe extern "C" fn probe(
|
||||
_flow: *const Flow, _dir: u8, input: *const u8, len: u32, _rdir: *mut u8,
|
||||
) -> AppProto {
|
||||
if crate::dns::dns::probe_udp(_flow, _dir, input, len, _rdir)
|
||||
== AppProtoEnum::ALPROTO_DNS as u16
|
||||
{
|
||||
let dir = Direction::ToServer;
|
||||
*_rdir = dir as u8;
|
||||
return ALPROTO_MDNS;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Returns *mut DNSState
|
||||
pub(crate) extern "C" fn state_new(
|
||||
_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto,
|
||||
) -> *mut std::os::raw::c_void {
|
||||
let state = dns::DNSState::new_variant(dns::DnsVariant::MulticastDns);
|
||||
let boxed = Box::new(state);
|
||||
return Box::into_raw(boxed) as *mut _;
|
||||
}
|
||||
|
||||
/// Get the mDNS response answer name and index i.
|
||||
///
|
||||
/// Very similar to the DNS version, but mDNS is always to_server.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn SCMdnsTxGetAnswerName(
|
||||
_de: *mut DetectEngineThreadCtx, tx: *const c_void, _flow_flags: u8, i: u32,
|
||||
buf: *mut *const u8, len: *mut u32,
|
||||
) -> bool {
|
||||
let tx = cast_pointer!(tx, dns::DNSTransaction);
|
||||
let answers = if tx.request.is_some() {
|
||||
tx.request.as_ref().map(|request| &request.answers)
|
||||
} else {
|
||||
tx.response.as_ref().map(|response| &response.answers)
|
||||
};
|
||||
let index = i as usize;
|
||||
|
||||
if let Some(answers) = answers {
|
||||
if let Some(answer) = answers.get(index) {
|
||||
if !answer.name.value.is_empty() {
|
||||
*buf = answer.name.value.as_ptr();
|
||||
*len = answer.name.value.len() as u32;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn SCRegisterMdnsParser() {
|
||||
let default_port = std::ffi::CString::new("[5353]").unwrap();
|
||||
let parser = RustParser {
|
||||
name: b"mdns\0".as_ptr() as *const std::os::raw::c_char,
|
||||
default_port: default_port.as_ptr(),
|
||||
ipproto: IPPROTO_UDP,
|
||||
probe_ts: Some(probe),
|
||||
probe_tc: Some(probe),
|
||||
min_depth: 0,
|
||||
max_depth: std::mem::size_of::<dns::DNSHeader>() as u16,
|
||||
state_new,
|
||||
state_free: dns::state_free,
|
||||
tx_free: dns::state_tx_free,
|
||||
parse_ts: dns::parse_request,
|
||||
parse_tc: dns::parse_request,
|
||||
get_tx_count: dns::state_get_tx_count,
|
||||
get_tx: dns::state_get_tx,
|
||||
tx_comp_st_ts: 1,
|
||||
tx_comp_st_tc: 1,
|
||||
tx_get_progress: dns::tx_get_alstate_progress,
|
||||
get_eventinfo: Some(dns::DNSEvent::get_event_info),
|
||||
get_eventinfo_byid: Some(dns::DNSEvent::get_event_info_by_id),
|
||||
localstorage_new: None,
|
||||
localstorage_free: None,
|
||||
get_tx_files: None,
|
||||
get_tx_iterator: Some(
|
||||
crate::applayer::state_get_tx_iterator::<dns::DNSState, dns::DNSTransaction>,
|
||||
),
|
||||
get_tx_data: dns::state_get_tx_data,
|
||||
get_state_data: dns::dns_get_state_data,
|
||||
apply_tx_config: None,
|
||||
flags: 0,
|
||||
get_frame_id_by_name: Some(dns::DnsFrameType::ffi_id_from_name),
|
||||
get_frame_name_by_id: Some(dns::DnsFrameType::ffi_name_from_id),
|
||||
get_state_id_by_name: None,
|
||||
get_state_name_by_id: None,
|
||||
};
|
||||
|
||||
let ip_proto_str = CString::new("udp").unwrap();
|
||||
if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
|
||||
let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
|
||||
ALPROTO_MDNS = alproto;
|
||||
if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
|
||||
let _ = AppLayerRegisterParser(&parser, alproto);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
/* Copyright (C) 2025 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
//! mDNS parser, detection, logger and application layer module.
|
||||
|
||||
pub mod log;
|
||||
pub mod mdns;
|
||||
@ -0,0 +1,160 @@
|
||||
/* Copyright (C) 2025 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "conf.h"
|
||||
|
||||
#include "threadvars.h"
|
||||
|
||||
#include "util-debug.h"
|
||||
#include "app-layer-parser.h"
|
||||
#include "output.h"
|
||||
|
||||
#include "output-json.h"
|
||||
#include "output-json-mdns.h"
|
||||
#include "rust.h"
|
||||
|
||||
typedef struct SCDnsLogFileCtx_ {
|
||||
uint64_t flags; /** Store mode */
|
||||
OutputJsonCtx *eve_ctx;
|
||||
uint8_t version;
|
||||
} SCDnsLogFileCtx;
|
||||
|
||||
typedef struct SCDnsLogThread_ {
|
||||
SCDnsLogFileCtx *dnslog_ctx;
|
||||
OutputJsonThreadCtx *ctx;
|
||||
} SCDnsLogThread;
|
||||
|
||||
bool AlertJsonMdns(void *txptr, SCJsonBuilder *js)
|
||||
{
|
||||
return SCMdnsLogJson(txptr, js);
|
||||
}
|
||||
|
||||
static int JsonMdnsLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f,
|
||||
void *alstate, void *txptr, uint64_t tx_id)
|
||||
{
|
||||
SCDnsLogThread *td = (SCDnsLogThread *)thread_data;
|
||||
SCDnsLogFileCtx *dnslog_ctx = td->dnslog_ctx;
|
||||
|
||||
SCJsonBuilder *jb = CreateEveHeader(p, LOG_DIR_FLOW, "mdns", NULL, dnslog_ctx->eve_ctx);
|
||||
if (unlikely(jb == NULL)) {
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
if (SCMdnsLogJson(txptr, jb)) {
|
||||
OutputJsonBuilderBuffer(tv, p, p->flow, jb, td->ctx);
|
||||
}
|
||||
SCJbFree(jb);
|
||||
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
static TmEcode SCDnsLogThreadInit(ThreadVars *t, const void *initdata, void **data)
|
||||
{
|
||||
SCDnsLogThread *aft = SCCalloc(1, sizeof(SCDnsLogThread));
|
||||
if (unlikely(aft == NULL))
|
||||
return TM_ECODE_FAILED;
|
||||
|
||||
if (initdata == NULL) {
|
||||
SCLogDebug("Error getting log context for eve-log.mdns. \"initdata\" argument NULL");
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
/* Use the Output Context (file pointer and mutex) */
|
||||
aft->dnslog_ctx = ((OutputCtx *)initdata)->data;
|
||||
aft->ctx = CreateEveThreadCtx(t, aft->dnslog_ctx->eve_ctx);
|
||||
if (!aft->ctx) {
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
*data = (void *)aft;
|
||||
return TM_ECODE_OK;
|
||||
|
||||
error_exit:
|
||||
SCFree(aft);
|
||||
return TM_ECODE_FAILED;
|
||||
}
|
||||
|
||||
static TmEcode SCDnsLogThreadDeinit(ThreadVars *t, void *data)
|
||||
{
|
||||
SCDnsLogThread *aft = (SCDnsLogThread *)data;
|
||||
if (aft == NULL) {
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
FreeEveThreadCtx(aft->ctx);
|
||||
|
||||
/* clear memory */
|
||||
memset(aft, 0, sizeof(SCDnsLogThread));
|
||||
|
||||
SCFree(aft);
|
||||
return TM_ECODE_OK;
|
||||
}
|
||||
|
||||
static void DnsLogDeInitCtxSub(OutputCtx *output_ctx)
|
||||
{
|
||||
SCDnsLogFileCtx *dnslog_ctx = (SCDnsLogFileCtx *)output_ctx->data;
|
||||
SCFree(dnslog_ctx);
|
||||
SCFree(output_ctx);
|
||||
}
|
||||
|
||||
static OutputInitResult DnsLogInitCtxSub(SCConfNode *conf, OutputCtx *parent_ctx)
|
||||
{
|
||||
OutputInitResult result = { NULL, false };
|
||||
const char *enabled = SCConfNodeLookupChildValue(conf, "enabled");
|
||||
if (enabled != NULL && !SCConfValIsTrue(enabled)) {
|
||||
result.ok = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
OutputJsonCtx *ojc = parent_ctx->data;
|
||||
|
||||
SCDnsLogFileCtx *dnslog_ctx = SCCalloc(1, sizeof(SCDnsLogFileCtx));
|
||||
if (unlikely(dnslog_ctx == NULL)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
dnslog_ctx->eve_ctx = ojc;
|
||||
dnslog_ctx->version = DNS_LOG_VERSION_3;
|
||||
|
||||
/* For mDNS, log everything.
|
||||
*
|
||||
* TODO: Maybe add flags for request and/or response only.
|
||||
*/
|
||||
dnslog_ctx->flags = ~0ULL;
|
||||
|
||||
OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
|
||||
if (unlikely(output_ctx == NULL)) {
|
||||
SCFree(dnslog_ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
output_ctx->data = dnslog_ctx;
|
||||
output_ctx->DeInit = DnsLogDeInitCtxSub;
|
||||
|
||||
AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_MDNS);
|
||||
|
||||
result.ctx = output_ctx;
|
||||
result.ok = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void JsonMdnsLogRegister(void)
|
||||
{
|
||||
OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonMdnsLog", "eve-log.mdns",
|
||||
DnsLogInitCtxSub, ALPROTO_MDNS, JsonMdnsLogger, SCDnsLogThreadInit,
|
||||
SCDnsLogThreadDeinit);
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
/* Copyright (C) 2025 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef SURICATA_OUTPUT_JSON_MDNS_H
|
||||
#define SURICATA_OUTPUT_JSON_MDNS_H
|
||||
|
||||
void JsonMdnsLogRegister(void);
|
||||
bool AlertJsonMdns(void *txptr, SCJsonBuilder *js);
|
||||
|
||||
#endif /* SURICATA_OUTPUT_JSON_MDNS_H */
|
||||
Loading…
Reference in New Issue