]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
eve/dns/v2: support eve/dns v2 in rust
authorJason Ish <ish@unx.ca>
Fri, 2 Feb 2018 18:46:13 +0000 (12:46 -0600)
committerVictor Julien <victor@inliniac.net>
Thu, 15 Mar 2018 09:00:19 +0000 (10:00 +0100)
rust/src/dns/log.rs
src/output-json-dns.c

index 18359a475ff4100425503d6b49bb1cc844e9610b..c3c5f3a370caab40c00451bca5539a169a6b3ce2 100644 (file)
@@ -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<u8>) -> std::string::String {
 }
 
 ///  Log the SSHPF in an DNSAnswerEntry.
-fn dns_log_sshfp(js: &Json, answer: &DNSAnswerEntry)
+fn dns_log_sshfp(answer: &DNSAnswerEntry) -> Option<Json>
 {
     // 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();
 }
index 8eb6a6dc717b0aac97f59396884577786d6c09cf..021efdc937b72b43fab3f733b5732b59c2900b1b 100644 (file)
@@ -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");