From 03cf3dcd6ddf82e63e5c5024e387cf7d3e7d70f7 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Tue, 5 May 2020 09:14:36 -0600 Subject: [PATCH] dns/eve: convert to jsonbuilder --- rust/src/dns/log.rs | 256 +++++++++++++++++++++++----------------- src/output-json-alert.c | 18 ++- src/output-json-dns.c | 107 ++++++++++------- src/output-json-dns.h | 4 +- 4 files changed, 225 insertions(+), 160 deletions(-) diff --git a/rust/src/dns/log.rs b/rust/src/dns/log.rs index beade5a35c..209ede26b6 100644 --- a/rust/src/dns/log.rs +++ b/rust/src/dns/log.rs @@ -19,7 +19,7 @@ use std; use std::string::String; use std::collections::HashMap; -use crate::json::*; +use crate::jsonbuilder::{JsonBuilder, JsonError}; use crate::dns::dns::*; pub const LOG_QUERIES : u64 = BIT_U64!(0); @@ -396,89 +396,90 @@ pub fn dns_print_addr(addr: &Vec) -> std::string::String { } /// Log the SSHPF in an DNSAnswerEntry. -fn dns_log_sshfp(answer: &DNSAnswerEntry) -> Option +fn dns_log_sshfp(answer: &DNSAnswerEntry) -> Result, JsonError> { // Need at least 3 bytes - TODO: log something if we don't? if answer.data.len() < 3 { - return None + return Ok(None) } - let sshfp = Json::object(); + let mut sshfp = JsonBuilder::new_object(); let mut hex = Vec::new(); for byte in &answer.data[2..] { hex.push(format!("{:02x}", byte)); } - sshfp.set_string("fingerprint", &hex.join(":")); - sshfp.set_integer("algo", answer.data[0] as u64); - sshfp.set_integer("type", answer.data[1] as u64); + sshfp.set_string("fingerprint", &hex.join(":"))?; + sshfp.set_uint("algo", answer.data[0] as u64)?; + sshfp.set_uint("type", answer.data[1] as u64)?; - return Some(sshfp); + return Ok(Some(sshfp)); } -fn dns_log_json_answer_detail(answer: &DNSAnswerEntry) -> Json +fn dns_log_json_answer_detail(answer: &DNSAnswerEntry) -> Result { - let jsa = Json::object(); + let mut jsa = JsonBuilder::new_object(); - jsa.set_string_from_bytes("rrname", &answer.name); - jsa.set_string("rrtype", &dns_rrtype_string(answer.rrtype)); - jsa.set_integer("ttl", answer.ttl as u64); + jsa.set_string_from_bytes("rrname", &answer.name)?; + jsa.set_string("rrtype", &dns_rrtype_string(answer.rrtype))?; + jsa.set_uint("ttl", answer.ttl as u64)?; match answer.rrtype { DNS_RECORD_TYPE_A | DNS_RECORD_TYPE_AAAA => { - jsa.set_string("rdata", &dns_print_addr(&answer.data)); + jsa.set_string("rdata", &dns_print_addr(&answer.data))?; } DNS_RECORD_TYPE_CNAME | DNS_RECORD_TYPE_MX | DNS_RECORD_TYPE_TXT | DNS_RECORD_TYPE_PTR => { - jsa.set_string_from_bytes("rdata", &answer.data); + jsa.set_string_from_bytes("rdata", &answer.data)?; }, DNS_RECORD_TYPE_SSHFP => { - for sshfp in dns_log_sshfp(&answer) { - jsa.set("sshfp", sshfp); + if let Some(sshfp) = dns_log_sshfp(answer)? { + jsa.set_object("sshfp", &sshfp)?; } }, _ => {} } - return jsa; + jsa.close()?; + return Ok(jsa); } -fn dns_log_json_answer(response: &DNSResponse, flags: u64) -> Json +fn dns_log_json_answer(js: &mut JsonBuilder, response: &DNSResponse, flags: u64) + -> Result<(), JsonError> { let header = &response.header; - let js = Json::object(); - js.set_integer("version", 2); - js.set_string("type", "answer"); - js.set_integer("id", header.tx_id as u64); - js.set_string("flags", format!("{:x}", header.flags).as_str()); + js.set_uint("version", 2)?; + js.set_string("type", "answer")?; + js.set_uint("id", header.tx_id as u64)?; + js.set_string("flags", format!("{:x}", header.flags).as_str())?; if header.flags & 0x8000 != 0 { - js.set_boolean("qr", true); + js.set_bool("qr", true)?; } if header.flags & 0x0400 != 0 { - js.set_boolean("aa", true); + js.set_bool("aa", true)?; } if header.flags & 0x0200 != 0 { - js.set_boolean("tc", true); + js.set_bool("tc", true)?; } if header.flags & 0x0100 != 0 { - js.set_boolean("rd", true); + js.set_bool("rd", true)?; } if header.flags & 0x0080 != 0 { - js.set_boolean("ra", true); + js.set_bool("ra", true)?; } for query in &response.queries { - js.set_string_from_bytes("rrname", &query.name); - js.set_string("rrtype", &dns_rrtype_string(query.rrtype)); + js.set_string_from_bytes("rrname", &query.name)?; + js.set_string("rrtype", &dns_rrtype_string(query.rrtype))?; break; } - js.set_string("rcode", &dns_rcode_string(header.flags)); + js.set_string("rcode", &dns_rcode_string(header.flags))?; if response.answers.len() > 0 { - let js_answers = Json::array(); + let mut js_answers = JsonBuilder::new_array(); // For grouped answers we use a HashMap keyed by the rrtype. let mut answer_types = HashMap::new(); @@ -491,11 +492,10 @@ fn dns_log_json_answer(response: &DNSResponse, flags: u64) -> Json DNS_RECORD_TYPE_A | DNS_RECORD_TYPE_AAAA => { if !answer_types.contains_key(&type_string) { answer_types.insert(type_string.to_string(), - Json::array()); + JsonBuilder::new_array()); } - for a in &answer_types.get(&type_string) { - a.array_append( - Json::string(&dns_print_addr(&answer.data))); + if let Some(a) = answer_types.get_mut(&type_string) { + a.append_string(&dns_print_addr(&answer.data))?; } } DNS_RECORD_TYPE_CNAME | @@ -504,21 +504,20 @@ fn dns_log_json_answer(response: &DNSResponse, flags: u64) -> Json DNS_RECORD_TYPE_PTR => { if !answer_types.contains_key(&type_string) { answer_types.insert(type_string.to_string(), - Json::array()); + JsonBuilder::new_array()); } - for a in &answer_types.get(&type_string) { - a.array_append( - Json::string_from_bytes(&answer.data)); + if let Some(a) = answer_types.get_mut(&type_string) { + a.append_string_from_bytes(&answer.data)?; } }, DNS_RECORD_TYPE_SSHFP => { if !answer_types.contains_key(&type_string) { answer_types.insert(type_string.to_string(), - Json::array()); + JsonBuilder::new_array()); } - for a in &answer_types.get(&type_string) { - for sshfp in dns_log_sshfp(&answer) { - a.array_append(sshfp); + if let Some(a) = answer_types.get_mut(&type_string) { + if let Some(sshfp) = dns_log_sshfp(&answer)? { + a.append_object(&sshfp)?; } } }, @@ -527,155 +526,191 @@ fn dns_log_json_answer(response: &DNSResponse, flags: u64) -> Json } if flags & LOG_FORMAT_DETAILED != 0 { - js_answers.array_append(dns_log_json_answer_detail(answer)); + js_answers.append_object(&dns_log_json_answer_detail(answer)?)?; } } + js_answers.close()?; + if flags & LOG_FORMAT_DETAILED != 0 { - js.set("answers", js_answers); + js.set_object("answers", &js_answers)?; } if flags & LOG_FORMAT_GROUPED != 0 { - let grouped = Json::object(); - for (k, v) in answer_types.drain() { - grouped.set(&k, v); + js.open_object("grouped")?; + for (k, mut v) in answer_types.drain() { + v.close()?; + js.set_object(&k, &v)?; } - js.set("grouped", grouped); + js.close()?; } } if response.authorities.len() > 0 { - let js_auth = Json::array(); + js.open_array("authorities")?; for auth in &response.authorities { - js_auth.array_append(dns_log_json_answer_detail(auth)); + let auth_detail = dns_log_json_answer_detail(auth)?; + js.append_object(&auth_detail)?; } - js.set("authorities", js_auth); + js.close()?; } - return js; + Ok(()) } -#[no_mangle] -pub extern "C" fn rs_dns_log_json_query(tx: &mut DNSTransaction, - i: u16, - flags: u64) - -> *mut JsonT +fn dns_log_query(tx: &mut DNSTransaction, + i: u16, + flags: u64, + jb: &mut JsonBuilder) + -> Result { let index = i as usize; if let &Some(ref request) = &tx.request { if index < request.queries.len() { let query = &request.queries[index]; if dns_log_rrtype_enabled(query.rrtype, flags) { - let js = Json::object(); - js.set_string("type", "query"); - js.set_integer("id", request.header.tx_id as u64); - js.set_string_from_bytes("rrname", &query.name); - js.set_string("rrtype", &dns_rrtype_string(query.rrtype)); - js.set_integer("tx_id", tx.id - 1); - return js.unwrap(); + jb.set_string("type", "query")?; + jb.set_uint("id", request.header.tx_id as u64)?; + jb.set_string_from_bytes("rrname", &query.name)?; + jb.set_string("rrtype", &dns_rrtype_string(query.rrtype))?; + jb.set_uint("tx_id", tx.id - 1)?; + return Ok(true); } } } - return std::ptr::null_mut(); + return Ok(false); +} + +#[no_mangle] +pub extern "C" fn rs_dns_log_json_query(tx: &mut DNSTransaction, + i: u16, + flags: u64, + jb: &mut JsonBuilder) + -> bool +{ + match dns_log_query(tx, i, flags, jb) { + Ok(false) | Err(_) => { + return false; + } + Ok(true) => { + return true; + } + } } #[no_mangle] pub extern "C" fn rs_dns_log_json_answer(tx: &mut DNSTransaction, - flags: u64) - -> *mut JsonT + flags: u64, mut js: &mut JsonBuilder) + -> bool { if let &Some(ref response) = &tx.response { for query in &response.queries { if dns_log_rrtype_enabled(query.rrtype, flags) { - let js = dns_log_json_answer(response, flags as u64); - return js.unwrap(); + return dns_log_json_answer(&mut js, response, flags as u64).is_ok(); } } } + return false; +} - return std::ptr::null_mut(); +#[no_mangle] +pub extern "C" fn rs_dns_do_log_answer(tx: &mut DNSTransaction, + flags: u64) -> bool +{ + if let &Some(ref response) = &tx.response { + for query in &response.queries { + if dns_log_rrtype_enabled(query.rrtype, flags) { + return true; + } + } + } + return false; } // Version 1 logging support. fn dns_log_json_answer_v1(header: &DNSHeader, answer: &DNSAnswerEntry) - -> Json + -> Result { - let js = Json::object(); + let mut js = JsonBuilder::new_object(); - js.set_string("type", "answer"); - js.set_integer("id", header.tx_id as u64); - js.set_string("flags", format!("{:x}", header.flags).as_str()); + js.set_string("type", "answer")?; + js.set_uint("id", header.tx_id as u64)?; + js.set_string("flags", format!("{:x}", header.flags).as_str())?; if header.flags & 0x8000 != 0 { - js.set_boolean("qr", true); + js.set_bool("qr", true)?; } if header.flags & 0x0400 != 0 { - js.set_boolean("aa", true); + js.set_bool("aa", true)?; } if header.flags & 0x0200 != 0 { - js.set_boolean("tc", true); + js.set_bool("tc", true)?; } if header.flags & 0x0100 != 0 { - js.set_boolean("rd", true); + js.set_bool("rd", true)?; } if header.flags & 0x0080 != 0 { - js.set_boolean("ra", true); + js.set_bool("ra", true)?; } - js.set_string("rcode", &dns_rcode_string(header.flags)); - js.set_string_from_bytes("rrname", &answer.name); - js.set_string("rrtype", &dns_rrtype_string(answer.rrtype)); - js.set_integer("ttl", answer.ttl as u64); + js.set_string("rcode", &dns_rcode_string(header.flags))?; + js.set_string_from_bytes("rrname", &answer.name)?; + js.set_string("rrtype", &dns_rrtype_string(answer.rrtype))?; + js.set_uint("ttl", answer.ttl as u64)?; match answer.rrtype { DNS_RECORD_TYPE_A | DNS_RECORD_TYPE_AAAA => { - js.set_string("rdata", &dns_print_addr(&answer.data)); + js.set_string("rdata", &dns_print_addr(&answer.data))?; } DNS_RECORD_TYPE_CNAME | DNS_RECORD_TYPE_MX | DNS_RECORD_TYPE_TXT | DNS_RECORD_TYPE_PTR => { - js.set_string_from_bytes("rdata", &answer.data); + js.set_string_from_bytes("rdata", &answer.data)?; }, DNS_RECORD_TYPE_SSHFP => { - if let Some(sshfp) = dns_log_sshfp(&answer) { - js.set("sshfp", sshfp); + if let Some(sshfp) = dns_log_sshfp(&answer)? { + js.set_object("sshfp", &sshfp)?; } }, _ => {} } - return js; + js.close()?; + + return Ok(js); } fn dns_log_json_failure_v1(r: &DNSResponse, index: usize, flags: u64) - -> * mut JsonT { + -> Result, JsonError> { if index >= r.queries.len() { - return std::ptr::null_mut(); + return Ok(None); } let ref query = r.queries[index]; if !dns_log_rrtype_enabled(query.rrtype, flags) { - return std::ptr::null_mut(); + return Ok(None); } - let js = Json::object(); + let mut js = JsonBuilder::new_object(); + + js.set_string("type", "answer")?; + js.set_uint("id", r.header.tx_id as u64)?; + js.set_string("rcode", &dns_rcode_string(r.header.flags))?; + js.set_string_from_bytes("rrname", &query.name)?; - js.set_string("type", "answer"); - js.set_integer("id", r.header.tx_id as u64); - js.set_string("rcode", &dns_rcode_string(r.header.flags)); - js.set_string_from_bytes("rrname", &query.name); + js.close()?; - return js.unwrap(); + return Ok(Some(js)); } #[no_mangle] pub extern "C" fn rs_dns_log_json_answer_v1(tx: &mut DNSTransaction, i: u16, flags: u64) - -> *mut JsonT + -> *mut JsonBuilder { let index = i as usize; // Note for loop over Option for easier break out to default @@ -683,7 +718,9 @@ pub extern "C" fn rs_dns_log_json_answer_v1(tx: &mut DNSTransaction, for response in &tx.response { if response.header.flags & 0x000f > 0 { if index == 0 { - return dns_log_json_failure_v1(response, index, flags); + if let Ok(Some(js)) = dns_log_json_failure_v1(response, index, flags) { + return Box::into_raw(Box::new(js)); + } } break; } @@ -692,8 +729,10 @@ pub extern "C" fn rs_dns_log_json_answer_v1(tx: &mut DNSTransaction, } let answer = &response.answers[index]; if dns_log_rrtype_enabled(answer.rrtype, flags) { - let js = dns_log_json_answer_v1(&response.header, answer); - return js.unwrap(); + if let Ok(js) = dns_log_json_answer_v1(&response.header, answer) { + return Box::into_raw(Box::new(js)); + } + break; } } return std::ptr::null_mut(); @@ -703,15 +742,16 @@ pub extern "C" fn rs_dns_log_json_answer_v1(tx: &mut DNSTransaction, pub extern "C" fn rs_dns_log_json_authority_v1(tx: &mut DNSTransaction, i: u16, flags: u64) - -> *mut JsonT + -> *mut JsonBuilder { let index = i as usize; if let &Some(ref response) = &tx.response { if index < response.authorities.len() { let answer = &response.authorities[index]; if dns_log_rrtype_enabled(answer.rrtype, flags) { - let js = dns_log_json_answer_v1(&response.header, answer); - return js.unwrap(); + if let Ok(js) = dns_log_json_answer_v1(&response.header, answer) { + return Box::into_raw(Box::new(js)); + } } } } diff --git a/src/output-json-alert.c b/src/output-json-alert.c index cbe6fb228e..b9b973f182 100644 --- a/src/output-json-alert.c +++ b/src/output-json-alert.c @@ -198,20 +198,18 @@ static void AlertJsonDns(const Flow *f, const uint64_t tx_id, JsonBuilder *js) void *txptr = AppLayerParserGetTx(f->proto, ALPROTO_DNS, dns_state, tx_id); if (txptr) { - json_t *dnsjs = json_object(); - if (unlikely(dnsjs == NULL)) { - return; - } - json_t *qjs = JsonDNSLogQuery(txptr, tx_id); + jb_open_object(js, "dns"); + JsonBuilder *qjs = JsonDNSLogQuery(txptr, tx_id); if (qjs != NULL) { - json_object_set_new(dnsjs, "query", qjs); + jb_set_object(js, "query", qjs); + jb_free(qjs); } - json_t *ajs = JsonDNSLogAnswer(txptr, tx_id); + JsonBuilder *ajs = JsonDNSLogAnswer(txptr, tx_id); if (ajs != NULL) { - json_object_set_new(dnsjs, "answer", ajs); + jb_set_object(js, "answer", ajs); + jb_free(ajs); } - jb_set_jsont(js, "dns", dnsjs); - json_decref(dnsjs); + jb_close(js); } } return; diff --git a/src/output-json-dns.c b/src/output-json-dns.c index 62c4a915de..78fa8da3ed 100644 --- a/src/output-json-dns.c +++ b/src/output-json-dns.c @@ -271,26 +271,38 @@ typedef struct LogDnsLogThread_ { MemBuffer *buffer; } LogDnsLogThread; -json_t *JsonDNSLogQuery(void *txptr, uint64_t tx_id) +JsonBuilder *JsonDNSLogQuery(void *txptr, uint64_t tx_id) { - json_t *queryjs = json_array(); - if (queryjs == NULL) + JsonBuilder *queryjb = jb_new_array(); + if (queryjb == NULL) { return NULL; + } for (uint16_t i = 0; i < UINT16_MAX; i++) { - json_t *dns = rs_dns_log_json_query((void *)txptr, i, LOG_ALL_RRTYPES); - if (unlikely(dns == NULL)) { + JsonBuilder *js = jb_new_object(); + if (!rs_dns_log_json_query((void *)txptr, i, LOG_ALL_RRTYPES, js)) { + jb_free(js); break; } - json_array_append_new(queryjs, dns); + jb_close(js); + jb_append_object(queryjb, js); + jb_free(js); } - return queryjs; + jb_close(queryjb); + return queryjb; } -json_t *JsonDNSLogAnswer(void *txptr, uint64_t tx_id) +JsonBuilder *JsonDNSLogAnswer(void *txptr, uint64_t tx_id) { - return rs_dns_log_json_answer(txptr, LOG_ALL_RRTYPES); + if (!rs_dns_do_log_answer(txptr, LOG_ALL_RRTYPES)) { + return NULL; + } else { + JsonBuilder *js = jb_new_object(); + rs_dns_log_json_answer(txptr, LOG_ALL_RRTYPES, js); + jb_close(js); + return js; + } } static int JsonDnsLoggerToServer(ThreadVars *tv, void *thread_data, @@ -300,28 +312,28 @@ static int JsonDnsLoggerToServer(ThreadVars *tv, void *thread_data, LogDnsLogThread *td = (LogDnsLogThread *)thread_data; LogDnsFileCtx *dnslog_ctx = td->dnslog_ctx; - json_t *js; if (unlikely(dnslog_ctx->flags & LOG_QUERIES) == 0) { return TM_ECODE_OK; } for (uint16_t i = 0; i < 0xffff; i++) { - js = CreateJSONHeader(p, LOG_DIR_FLOW, "dns", NULL); - if (unlikely(js == NULL)) { + JsonBuilder *jb = CreateEveHeader(p, LOG_DIR_FLOW, "dns", NULL); + if (unlikely(jb == NULL)) { return TM_ECODE_OK; } - JsonAddCommonOptions(&dnslog_ctx->cfg, p, f, js); + EveAddCommonOptions(&dnslog_ctx->cfg, p, f, jb); - json_t *dns = rs_dns_log_json_query(txptr, i, td->dnslog_ctx->flags); - if (unlikely(dns == NULL)) { - json_decref(js); + jb_open_object(jb, "dns"); + if (!rs_dns_log_json_query(txptr, i, td->dnslog_ctx->flags, jb)) { + jb_free(jb); break; } - json_object_set_new(js, "dns", dns); + jb_close(jb); + MemBufferReset(td->buffer); - OutputJSONBuffer(js, td->dnslog_ctx->file_ctx, &td->buffer); - json_decref(js); + OutputJsonBuilderBuffer(jb, td->dnslog_ctx->file_ctx, &td->buffer); + jb_free(jb); } SCReturnInt(TM_ECODE_OK); @@ -339,49 +351,64 @@ static int JsonDnsLoggerToClient(ThreadVars *tv, void *thread_data, return TM_ECODE_OK; } - json_t *js = CreateJSONHeader(p, LOG_DIR_FLOW, "dns", NULL); - if (unlikely(js == NULL)) - return TM_ECODE_OK; - - JsonAddCommonOptions(&dnslog_ctx->cfg, p, f, js); - if (td->dnslog_ctx->version == DNS_VERSION_2) { - json_t *answer = rs_dns_log_json_answer(txptr, - td->dnslog_ctx->flags); - if (answer != NULL) { - json_object_set_new(js, "dns", answer); + if (rs_dns_do_log_answer(txptr, td->dnslog_ctx->flags)) { + JsonBuilder *jb = CreateEveHeader(p, LOG_DIR_FLOW, "dns", NULL); + if (unlikely(jb == NULL)) { + return TM_ECODE_OK; + } + EveAddCommonOptions(&dnslog_ctx->cfg, p, f, jb); + + jb_open_object(jb, "dns"); + rs_dns_log_json_answer(txptr, td->dnslog_ctx->flags, jb); + jb_close(jb); MemBufferReset(td->buffer); - OutputJSONBuffer(js, td->dnslog_ctx->file_ctx, &td->buffer); + OutputJsonBuilderBuffer(jb, td->dnslog_ctx->file_ctx, &td->buffer); + jb_free(jb); } } else { /* Log answers. */ for (uint16_t i = 0; i < UINT16_MAX; i++) { - json_t *answer = rs_dns_log_json_answer_v1(txptr, i, + JsonBuilder *jb = CreateEveHeader(p, LOG_DIR_FLOW, "dns", NULL); + if (unlikely(jb == NULL)) { + return TM_ECODE_OK; + } + EveAddCommonOptions(&dnslog_ctx->cfg, p, f, jb); + + JsonBuilder *answer = rs_dns_log_json_answer_v1(txptr, i, td->dnslog_ctx->flags); if (answer == NULL) { + jb_free(jb); break; } - json_object_set_new(js, "dns", answer); + jb_set_object(jb, "dns", answer); + MemBufferReset(td->buffer); - OutputJSONBuffer(js, td->dnslog_ctx->file_ctx, &td->buffer); - json_object_del(js, "dns"); + OutputJsonBuilderBuffer(jb, td->dnslog_ctx->file_ctx, &td->buffer); + jb_free(jb); } /* Log authorities. */ for (uint16_t i = 0; i < UINT16_MAX; i++) { - json_t *answer = rs_dns_log_json_authority_v1(txptr, i, + JsonBuilder *jb = CreateEveHeader(p, LOG_DIR_FLOW, "dns", NULL); + if (unlikely(jb == NULL)) { + return TM_ECODE_OK; + } + EveAddCommonOptions(&dnslog_ctx->cfg, p, f, jb); + + JsonBuilder *answer = rs_dns_log_json_authority_v1(txptr, i, td->dnslog_ctx->flags); if (answer == NULL) { + jb_free(jb); break; } - json_object_set_new(js, "dns", answer); + jb_set_object(jb, "dns", answer); + MemBufferReset(td->buffer); - OutputJSONBuffer(js, td->dnslog_ctx->file_ctx, &td->buffer); - json_object_del(js, "dns"); + OutputJsonBuilderBuffer(jb, td->dnslog_ctx->file_ctx, &td->buffer); + jb_free(jb); } } - json_decref(js); - SCReturnInt(TM_ECODE_OK); } diff --git a/src/output-json-dns.h b/src/output-json-dns.h index 088ad16f76..9d0e451328 100644 --- a/src/output-json-dns.h +++ b/src/output-json-dns.h @@ -26,7 +26,7 @@ void JsonDnsLogRegister(void); -json_t *JsonDNSLogQuery(void *txptr, uint64_t tx_id) __attribute__((nonnull)); -json_t *JsonDNSLogAnswer(void *txptr, uint64_t tx_id) __attribute__((nonnull)); +JsonBuilder *JsonDNSLogQuery(void *txptr, uint64_t tx_id) __attribute__((nonnull)); +JsonBuilder *JsonDNSLogAnswer(void *txptr, uint64_t tx_id) __attribute__((nonnull)); #endif /* __OUTPUT_JSON_DNS_H__ */