From: Jason Ish Date: Fri, 2 Feb 2018 18:46:13 +0000 (-0600) Subject: eve/dns/v2: support eve/dns v2 in rust X-Git-Tag: suricata-4.1.0-beta1~46 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=27fd5214204fa5e6d891b27e223635670bbd2ad2;p=thirdparty%2Fsuricata.git eve/dns/v2: support eve/dns v2 in rust --- diff --git a/rust/src/dns/log.rs b/rust/src/dns/log.rs index 18359a475f..c3c5f3a370 100644 --- a/rust/src/dns/log.rs +++ b/rust/src/dns/log.rs @@ -19,10 +19,14 @@ extern crate libc; use std; use std::string::String; +use std::collections::HashMap; use json::*; use dns::dns::*; +pub const LOG_QUERIES : u64 = BIT_U64!(0); +pub const LOG_ANSWER : u64 = BIT_U64!(1); + pub const LOG_A : u64 = BIT_U64!(2); pub const LOG_NS : u64 = BIT_U64!(3); pub const LOG_MD : u64 = BIT_U64!(4); @@ -82,6 +86,9 @@ pub const LOG_MAILA : u64 = BIT_U64!(57); pub const LOG_ANY : u64 = BIT_U64!(58); pub const LOG_URI : u64 = BIT_U64!(59); +pub const LOG_FORMAT_GROUPED : u64 = BIT_U64!(60); +pub const LOG_FORMAT_DETAILED : u64 = BIT_U64!(61); + fn dns_log_rrtype_enabled(rtype: u16, flags: u64) -> bool { if flags == !0 { @@ -391,11 +398,11 @@ pub fn dns_print_addr(addr: &Vec) -> std::string::String { } /// Log the SSHPF in an DNSAnswerEntry. -fn dns_log_sshfp(js: &Json, answer: &DNSAnswerEntry) +fn dns_log_sshfp(answer: &DNSAnswerEntry) -> Option { // Need at least 3 bytes - TODO: log something if we don't? if answer.data.len() < 3 { - return; + return None } let sshfp = Json::object(); @@ -408,39 +415,44 @@ fn dns_log_sshfp(js: &Json, answer: &DNSAnswerEntry) sshfp.set_integer("algo", answer.data[0] as u64); sshfp.set_integer("type", answer.data[1] as u64); - js.set("sshfp", sshfp); + return Some(sshfp); } -#[no_mangle] -pub extern "C" fn rs_dns_log_json_query(tx: &mut DNSTransaction, - i: libc::uint16_t, - flags: libc::uint64_t) - -> *mut JsonT +fn dns_log_json_answer_detail(answer: &DNSAnswerEntry) -> Json { - let index = i as usize; - for request in &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(); - } + let jsa = Json::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); + + match answer.rrtype { + DNS_RECORD_TYPE_A | DNS_RECORD_TYPE_AAAA => { + 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); + }, + DNS_RECORD_TYPE_SSHFP => { + for sshfp in dns_log_sshfp(&answer) { + jsa.set("sshfp", sshfp); + } + }, + _ => {} } - return std::ptr::null_mut(); + return jsa; } -fn dns_log_json_answer(header: &DNSHeader, answer: &DNSAnswerEntry) - -> Json +fn dns_log_json_answer(response: &DNSResponse, flags: u64) -> Json { + 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()); @@ -459,94 +471,130 @@ fn dns_log_json_answer(header: &DNSHeader, answer: &DNSAnswerEntry) if header.flags & 0x0080 != 0 { js.set_boolean("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); - match answer.rrtype { - DNS_RECORD_TYPE_A | DNS_RECORD_TYPE_AAAA => { - 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); - }, - DNS_RECORD_TYPE_SSHFP => { - dns_log_sshfp(&js, &answer); - }, - _ => {} + for query in &response.queries { + js.set_string_from_bytes("rrname", &query.name); + break; } + js.set_string("rcode", &dns_rcode_string(header.flags)); - return js; -} + if response.answers.len() > 0 { + let js_answers = Json::array(); -fn dns_log_json_failure(r: &DNSResponse, index: usize, flags: u64) - -> * mut JsonT { - if index >= r.queries.len() { - return std::ptr::null_mut(); - } + // For grouped answers we use a HashMap keyed by the rrtype. + let mut answer_types = HashMap::new(); - let ref query = r.queries[index]; + for answer in &response.answers { - if !dns_log_rrtype_enabled(query.rrtype, flags) { - return std::ptr::null_mut(); - } + if flags & LOG_FORMAT_GROUPED != 0 { + let type_string = dns_rrtype_string(answer.rrtype); + match answer.rrtype { + DNS_RECORD_TYPE_A | DNS_RECORD_TYPE_AAAA => { + if !answer_types.contains_key(&type_string) { + answer_types.insert(type_string.to_string(), + Json::array()); + } + for a in &answer_types.get(&type_string) { + a.array_append( + Json::string(&dns_print_addr(&answer.data))); + } + } + DNS_RECORD_TYPE_CNAME | + DNS_RECORD_TYPE_MX | + DNS_RECORD_TYPE_TXT | + DNS_RECORD_TYPE_PTR => { + if !answer_types.contains_key(&type_string) { + answer_types.insert(type_string.to_string(), + Json::array()); + } + for a in &answer_types.get(&type_string) { + a.array_append( + Json::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()); + } + for a in &answer_types.get(&type_string) { + for sshfp in dns_log_sshfp(&answer) { + a.array_append(sshfp); + } + } + }, + _ => {} + } + } - let js = Json::object(); + if flags & LOG_FORMAT_DETAILED != 0 { + js_answers.array_append(dns_log_json_answer_detail(answer)); + } + } - 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); + if flags & LOG_FORMAT_DETAILED != 0 { + js.set("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.set("grouped", grouped); + } + + } + + if response.authorities.len() > 0 { + let js_auth = Json::array(); + for auth in &response.authorities { + js_auth.array_append(dns_log_json_answer_detail(auth)); + } + js.set("authorities", js_auth); + } - return js.unwrap(); + return js; } #[no_mangle] -pub extern "C" fn rs_dns_log_json_answer(tx: &mut DNSTransaction, - i: libc::uint16_t, - flags: libc::uint64_t) - -> *mut JsonT +pub extern "C" fn rs_dns_log_json_query(tx: &mut DNSTransaction, + i: libc::uint16_t, + flags: libc::uint64_t) + -> *mut JsonT { let index = i as usize; - for response in &tx.response { - if response.header.flags & 0x000f > 0 { - if index == 0 { - return dns_log_json_failure(response, index, flags); + for request in &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(); } - break; - } - if index >= response.answers.len() { - break; - } - let answer = &response.answers[index]; - if dns_log_rrtype_enabled(answer.rrtype, flags) { - let js = dns_log_json_answer(&response.header, answer); - return js.unwrap(); } } + return std::ptr::null_mut(); } #[no_mangle] -pub extern "C" fn rs_dns_log_json_authority(tx: &mut DNSTransaction, - i: libc::uint16_t, - flags: libc::uint64_t) - -> *mut JsonT +pub extern "C" fn rs_dns_log_json_answer(tx: &mut DNSTransaction, + flags: libc::uint64_t) + -> *mut JsonT { - let index = i as usize; for response in &tx.response { - if index >= response.authorities.len() { - break; - } - let answer = &response.authorities[index]; - if dns_log_rrtype_enabled(answer.rrtype, flags) { - let js = dns_log_json_answer(&response.header, answer); - return js.unwrap(); + 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 std::ptr::null_mut(); } diff --git a/src/output-json-dns.c b/src/output-json-dns.c index 8eb6a6dc71..021efdc937 100644 --- a/src/output-json-dns.c +++ b/src/output-json-dns.c @@ -1077,30 +1077,14 @@ static int JsonDnsLoggerToClient(ThreadVars *tv, void *thread_data, } #if HAVE_RUST - /* Log answers. */ - for (uint16_t i = 0; i < 0xffff; i++) { - json_t *answer = rs_dns_log_json_answer(txptr, i, + if (td->dnslog_ctx->version == DNS_VERSION_2) { + json_t *answer = rs_dns_log_json_answer(txptr, td->dnslog_ctx->flags); - if (answer == NULL) { - break; + if (answer != NULL) { + json_object_set_new(js, "dns", answer); + MemBufferReset(td->buffer); + OutputJSONBuffer(js, td->dnslog_ctx->file_ctx, &td->buffer); } - json_object_set_new(js, "dns", answer); - MemBufferReset(td->buffer); - OutputJSONBuffer(js, td->dnslog_ctx->file_ctx, &td->buffer); - json_object_del(js, "dns"); - } - - /* Log authorities. */ - for (uint16_t i = 0; i < 0xffff; i++) { - json_t *answer = rs_dns_log_json_authority(txptr, i, - td->dnslog_ctx->flags); - if (answer == NULL) { - break; - } - json_object_set_new(js, "dns", answer); - MemBufferReset(td->buffer); - OutputJSONBuffer(js, td->dnslog_ctx->file_ctx, &td->buffer); - json_object_del(js, "dns"); } #else DNSTransaction *tx = txptr; @@ -1229,34 +1213,43 @@ static void JsonDnsLogParseConfig(LogDnsFileCtx *dnslog_ctx, ConfNode *conf, } } +static DnsVersion JsonDnsParseVersion(ConfNode *conf) +{ + if (conf == NULL) { + return DNS_VERSION_1; + } + + DnsVersion version = DNS_VERSION_1; + intmax_t config_version; + if (ConfGetChildValueInt(conf, "version", &config_version)) { + switch(config_version) { + case 1: + version = DNS_VERSION_1; + break; + case 2: + version = DNS_VERSION_2; + break; + default: + SCLogWarning(SC_ERR_INVALID_ARGUMENT, + "Invalid version option: %ji, " + "forcing it to version 1", config_version); + version = DNS_VERSION_1; + break; + } + } else { + SCLogWarning(SC_ERR_INVALID_ARGUMENT, + "Version not found, forcing it to version 1"); + version = DNS_VERSION_1; + } + + return version; +} + static void JsonDnsLogInitFilters(LogDnsFileCtx *dnslog_ctx, ConfNode *conf) { dnslog_ctx->flags = ~0UL; if (conf) { - intmax_t version; - int ret = ConfGetChildValueInt(conf, "version", &version); - if (ret) { - switch(version) { - case 1: - dnslog_ctx->version = DNS_VERSION_1; - break; - case 2: - dnslog_ctx->version = DNS_VERSION_2; - break; - default: - SCLogWarning(SC_ERR_INVALID_ARGUMENT, - "Invalid version option: %ji, " - "forcing it to version 1", version); - dnslog_ctx->version = DNS_VERSION_1; - break; - } - } else { - SCLogWarning(SC_ERR_INVALID_ARGUMENT, - "Version not found, forcing it to version 1"); - dnslog_ctx->version = DNS_VERSION_1; - } - if (dnslog_ctx->version == DNS_VERSION_1) { JsonDnsLogParseConfig(dnslog_ctx, conf, "query", "answer", "custom"); } else { @@ -1287,9 +1280,19 @@ static OutputInitResult JsonDnsLogInitCtxSub(ConfNode *conf, OutputCtx *parent_c OutputInitResult result = { NULL, false }; const char *enabled = ConfNodeLookupChildValue(conf, "enabled"); if (enabled != NULL && !ConfValIsTrue(enabled)) { + result.ok = true; return result; } + DnsVersion version = JsonDnsParseVersion(conf); +#ifdef HAVE_RUST + if (version != 2) { + SCLogError(SC_ERR_NOT_SUPPORTED, "EVE/DNS version %d not support with " + "by Rust builds.", version); + exit(1); + } +#endif + OutputJsonCtx *ojc = parent_ctx->data; LogDnsFileCtx *dnslog_ctx = SCMalloc(sizeof(LogDnsFileCtx)); @@ -1310,6 +1313,7 @@ static OutputInitResult JsonDnsLogInitCtxSub(ConfNode *conf, OutputCtx *parent_c output_ctx->data = dnslog_ctx; output_ctx->DeInit = LogDnsLogDeInitCtxSub; + dnslog_ctx->version = version; JsonDnsLogInitFilters(dnslog_ctx, conf); SCLogDebug("DNS log sub-module initialized"); @@ -1335,6 +1339,15 @@ static OutputInitResult JsonDnsLogInitCtx(ConfNode *conf) return result; } + DnsVersion version = JsonDnsParseVersion(conf); +#ifdef HAVE_RUST + if (version != 2) { + SCLogError(SC_ERR_NOT_SUPPORTED, "EVE/DNS version %d not support with " + "by Rust builds.", version); + exit(1); + } +#endif + LogFileCtx *file_ctx = LogFileNewCtx(); if(file_ctx == NULL) { @@ -1366,6 +1379,7 @@ static OutputInitResult JsonDnsLogInitCtx(ConfNode *conf) output_ctx->data = dnslog_ctx; output_ctx->DeInit = LogDnsLogDeInitCtx; + dnslog_ctx->version = version; JsonDnsLogInitFilters(dnslog_ctx, conf); SCLogDebug("DNS log output initialized");