]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
rust: DNS app-layer.
authorJason Ish <ish@unx.ca>
Wed, 26 Apr 2017 22:35:10 +0000 (16:35 -0600)
committerJason Ish <ish@unx.ca>
Mon, 5 Jun 2017 20:57:21 +0000 (14:57 -0600)
A DNS application layer in Rust. This is different than the
C based one, as it is partially stateless by not matching
up responses to replies.

19 files changed:
rust/src/dns/dns.rs
rust/src/dns/log.rs [new file with mode: 0644]
rust/src/dns/mod.rs
rust/src/dns/parser.rs
src/Makefile.am
src/app-layer-dns-common.h
src/app-layer-dns-tcp-rust.c [new file with mode: 0644]
src/app-layer-dns-tcp-rust.h [new file with mode: 0644]
src/app-layer-dns-tcp.c
src/app-layer-dns-udp-rust.c [new file with mode: 0644]
src/app-layer-dns-udp-rust.h [new file with mode: 0644]
src/app-layer-dns-udp.c
src/detect-engine-dns.c
src/log-dnslog.c
src/output-json-dns.c
src/rust.h
src/suricata.c
src/util-debug.c
src/util-lua-dns.c

index 967bfa1e6aa9fdd5e95d09bf12413e66ccada5da..f08ad4f0e811cb878386de6a61a7cbc141fe6600 100644 (file)
  * 02110-1301, USA.
  */
 
+extern crate libc;
+extern crate nom;
+
+use std;
+use std::mem::transmute;
+
+use log::*;
+use core;
+use dns::parser;
+
 /// DNS record types.
 pub const DNS_RTYPE_A:     u16 = 1;
 pub const DNS_RTYPE_CNAME: u16 = 5;
 pub const DNS_RTYPE_SOA:   u16 = 6;
 pub const DNS_RTYPE_PTR:   u16 = 12;
 pub const DNS_RTYPE_MX:    u16 = 15;
+pub const DNS_RTYPE_AAAA:  u16 = 28;
 pub const DNS_RTYPE_SSHFP: u16 = 44;
 pub const DNS_RTYPE_RRSIG: u16 = 46;
 
+/// DNS error codes.
+pub const DNS_RCODE_NOERROR:  u16 = 0;
+pub const DNS_RCODE_FORMERR:  u16 = 1;
+pub const DNS_RCODE_NXDOMAIN: u16 = 3;
+
+/// The maximum number of transactions to keep in the queue pending
+/// processing before they are aggressively purged. Due to the
+/// stateless nature of this parser this is rarely needed, especially
+/// when one call to parse a request parses and a single request, and
+/// likewise for responses.
+///
+/// Where this matters is when one TCP buffer contains multiple
+/// requests are responses and one call into the parser creates
+/// multiple transactions. In this case we have to hold onto
+/// transactions longer than until handling the next transaction so it
+/// gets logged.
+const MAX_TRANSACTIONS: usize = 32;
+
+#[repr(u32)]
+pub enum DNSEvent {
+    UnsolicitedResponse = 0,
+    MalformedData,
+    NotRequest,
+    NotResponse,
+    ZFlagSet,
+    Flooded,
+    StateMemCapReached,
+}
+
 #[derive(Debug,PartialEq)]
 pub struct DNSHeader {
     pub tx_id: u16,
@@ -41,6 +81,18 @@ pub struct DNSQueryEntry {
     pub rrclass: u16,
 }
 
+impl DNSQueryEntry {
+
+    pub fn name(&self) -> &str {
+        let r = std::str::from_utf8(&self.name);
+        if r.is_err() {
+            return "";
+        }
+        return r.unwrap();
+    }
+
+}
+
 #[derive(Debug,PartialEq)]
 pub struct DNSAnswerEntry {
     pub name: Vec<u8>,
@@ -51,6 +103,26 @@ pub struct DNSAnswerEntry {
     pub data: Vec<u8>,
 }
 
+impl DNSAnswerEntry {
+
+    pub fn name(&self) -> &str {
+        let r = std::str::from_utf8(&self.name);
+        if r.is_err() {
+            return "";
+        }
+        return r.unwrap();
+    }
+
+    pub fn data_to_string(&self) -> &str {
+        let r = std::str::from_utf8(&self.data);
+        if r.is_err() {
+            return "";
+        }
+        return r.unwrap();
+    }
+
+}
+
 #[derive(Debug)]
 pub struct DNSRequest {
     pub header: DNSHeader,
@@ -64,3 +136,666 @@ pub struct DNSResponse {
     pub answers: Vec<DNSAnswerEntry>,
     pub authorities: Vec<DNSAnswerEntry>,
 }
+
+#[derive(Debug)]
+pub struct DNSTransaction {
+    pub id: u64,
+    pub request: Option<DNSRequest>,
+    pub response: Option<DNSResponse>,
+    pub logged: u32,
+    pub de_state: Option<*mut core::DetectEngineState>,
+    pub events: *mut core::AppLayerDecoderEvents,
+}
+
+impl DNSTransaction {
+
+    pub fn new() -> DNSTransaction {
+        return DNSTransaction{
+            id: 0,
+            request: None,
+            response: None,
+            logged: 0,
+            de_state: None,
+            events: std::ptr::null_mut(),
+        }
+    }
+
+    pub fn free(&mut self) {
+        if self.events != std::ptr::null_mut() {
+            core::sc_app_layer_decoder_events_free_events(&mut self.events);
+        }
+    }
+
+    /// Get the DNS transactions ID (not the internal tracking ID).
+    pub fn tx_id(&self) -> u16 {
+        for request in &self.request {
+            return request.header.tx_id;
+        }
+        for response in &self.response {
+            return response.header.tx_id;
+        }
+
+        // Shouldn't happen.
+        return 0;
+    }
+
+    /// Get the reply code of the transaction. Note that this will
+    /// also return 0 if there is no reply.
+    pub fn rcode(&self) -> u16 {
+        for response in &self.response {
+            return response.header.flags & 0x000f;
+        }
+        return 0;
+    }
+
+}
+
+impl Drop for DNSTransaction {
+    fn drop(&mut self) {
+        self.free();
+    }
+}
+
+pub struct DNSState {
+    // Internal transaction ID.
+    pub tx_id: u64,
+
+    // Transactions.
+    pub transactions: Vec<DNSTransaction>,
+
+    pub de_state_count: u64,
+
+    pub events: u16,
+
+    pub request_buffer: Vec<u8>,
+    pub response_buffer: Vec<u8>,
+}
+
+impl DNSState {
+
+    pub fn new() -> DNSState {
+        return DNSState{
+            tx_id: 0,
+            transactions: Vec::new(),
+            de_state_count: 0,
+            events: 0,
+            request_buffer: Vec::new(),
+            response_buffer: Vec::new(),
+        };
+    }
+
+    /// Allocate a new state with capacites in the buffers for
+    /// potentially buffering as might be needed in TCP.
+    pub fn new_tcp() -> DNSState {
+        return DNSState{
+            tx_id: 0,
+            transactions: Vec::new(),
+            de_state_count: 0,
+            events: 0,
+            request_buffer: Vec::with_capacity(0xffff),
+            response_buffer: Vec::with_capacity(0xffff),
+        };
+    }
+
+    pub fn free(&mut self) {
+        SCLogDebug!("Freeing {} transactions left in state.",
+                    self.transactions.len());
+        while self.transactions.len() > 0 {
+            self.free_tx_at_index(0);
+        }
+        assert!(self.transactions.len() == 0);
+    }
+
+    pub fn new_tx(&mut self) -> DNSTransaction {
+        let mut tx = DNSTransaction::new();
+        self.tx_id += 1;
+        tx.id = self.tx_id;
+        return tx;
+    }
+
+    pub fn free_tx(&mut self, tx_id: u64) {
+        SCLogDebug!("************** Freeing TX with ID {}", tx_id);
+        let len = self.transactions.len();
+        let mut found = false;
+        let mut index = 0;
+        for i in 0..len {
+            let tx = &self.transactions[i];
+            if tx.id == tx_id + 1 {
+                found = true;
+                index = i;
+                break;
+            }
+        }
+        if found {
+            self.free_tx_at_index(index);
+        }
+    }
+
+    fn free_tx_at_index(&mut self, index: usize) {
+        let tx = self.transactions.remove(index);
+        match tx.de_state {
+            Some(state) => {
+                core::sc_detect_engine_state_free(state);
+                self.de_state_count -= 1;
+            }
+            _ => {}
+        }
+    }
+
+    // Purges all transactions except one. This is a stateless parser
+    // so we don't need to hang onto old transactions.
+    //
+    // This is to actually handle an edge case where a DNS flood
+    // occurs in a single direction with no response packets. In such
+    // a case the functions to free a transaction are never called by
+    // the app-layer as they require bidirectional traffic.
+    pub fn purge(&mut self, tx_id: u64) {
+        while self.transactions.len() > MAX_TRANSACTIONS {
+            if self.transactions[0].id == tx_id + 1 {
+                return;
+            }
+            SCLogDebug!("Purging DNS TX with ID {}", self.transactions[0].id);
+            self.free_tx_at_index(0);
+        }
+    }
+
+    pub fn get_tx(&mut self, tx_id: u64) -> Option<&DNSTransaction> {
+        SCLogDebug!("get_tx: tx_id={}", tx_id);
+        self.purge(tx_id);
+        for tx in &mut self.transactions {
+            if tx.id == tx_id + 1 {
+                SCLogDebug!("Found DNS TX with ID {}", tx_id);
+                return Some(tx);
+            }
+        }
+        SCLogDebug!("Failed to find DNS TX with ID {}", tx_id);
+        return None;
+    }
+
+    /// Set an event. The event is set on the most recent transaction.
+    pub fn set_event(&mut self, event: DNSEvent) {
+        let len = self.transactions.len();
+        if len == 0 {
+            return;
+        }
+
+        let mut tx = &mut self.transactions[len - 1];
+        core::sc_app_layer_decoder_events_set_event_raw(&mut tx.events,
+                                                        event as u8);
+        self.events += 1;
+    }
+
+    pub fn parse_request(&mut self, input: &[u8]) -> bool {
+        match parser::dns_parse_request(input) {
+            nom::IResult::Done(_, request) => {
+                if request.header.flags & 0x8000 != 0 {
+                    SCLogDebug!("DNS message is not a request");
+                    self.set_event(DNSEvent::NotRequest);
+                    return false;
+                }
+
+                if request.header.flags & 0x0040 != 0 {
+                    SCLogDebug!("Z-flag set on DNS response");
+                    self.set_event(DNSEvent::ZFlagSet);
+                    return false;
+                }
+
+                let mut tx = self.new_tx();
+                tx.request = Some(request);
+                self.transactions.push(tx);
+                return true;
+            }
+            nom::IResult::Incomplete(_) => {
+                // Insufficient data.
+                SCLogDebug!("Insufficient data while parsing DNS request");
+                self.set_event(DNSEvent::MalformedData);
+                return false;
+            }
+            nom::IResult::Error(_) => {
+                // Error, probably malformed data.
+                SCLogDebug!("An error occurred while parsing DNS request");
+                self.set_event(DNSEvent::MalformedData);
+                return false;
+            }
+        }
+    }
+
+    pub fn parse_response(&mut self, input: &[u8]) -> bool {
+        match parser::dns_parse_response(input) {
+            nom::IResult::Done(_, response) => {
+
+                SCLogDebug!("Response header flags: {}", response.header.flags);
+
+                if response.header.flags & 0x8000 == 0 {
+                    SCLogDebug!("DNS message is not a response");
+                    self.set_event(DNSEvent::NotResponse);
+                }
+
+                if response.header.flags & 0x0040 != 0 {
+                    SCLogDebug!("Z-flag set on DNS response");
+                    self.set_event(DNSEvent::ZFlagSet);
+                    return false;
+                }
+
+                let mut tx = self.new_tx();
+                tx.response = Some(response);
+                self.transactions.push(tx);
+                return true;
+            }
+            nom::IResult::Incomplete(_) => {
+                // Insufficient data.
+                SCLogDebug!("Insufficient data while parsing DNS response");
+                self.set_event(DNSEvent::MalformedData);
+                return false;
+            }
+            nom::IResult::Error(_) => {
+                // Error, probably malformed data.
+                SCLogDebug!("An error occurred while parsing DNS response");
+                self.set_event(DNSEvent::MalformedData);
+                return false;
+            }
+        }
+    }
+
+    /// TCP variation of response request parser to handle the length
+    /// prefix as well as buffering.
+    ///
+    /// Always buffer and read from the buffer. Should optimize to skip
+    /// the buffer if not needed.
+    pub fn parse_request_tcp(&mut self, input: &[u8]) -> i8 {
+        self.request_buffer.extend_from_slice(input);
+
+        while self.request_buffer.len() > 0 {
+            let size = match nom::be_u16(&self.request_buffer) {
+                nom::IResult::Done(_, len) => {
+                    len as usize
+                }
+                _ => 0 as usize
+            };
+            SCLogDebug!("Have {} bytes, need {} to parse",
+                        self.request_buffer.len(), size);
+            if size > 0 && self.request_buffer.len() >= size {
+                let msg: Vec<u8> = self.request_buffer.drain(0..(size + 2))
+                    .collect();
+                if self.parse_request(&msg[2..]) {
+                    continue;
+                }
+                return -1;
+            }
+            SCLogDebug!("Not enough DNS traffic to parse.");
+            return 0;
+        }
+        return 0;
+    }
+
+    /// TCP variation of the response parser to handle the length
+    /// prefix as well as buffering.
+    ///
+    /// Always buffer and read from the buffer. Should optimize to skip
+    /// the buffer if not needed.
+    pub fn parse_response_tcp(&mut self, input: &[u8]) -> i8 {
+        self.response_buffer.extend_from_slice(input);
+        let size = match nom::be_u16(&self.response_buffer) {
+            nom::IResult::Done(_, len) => {
+                len as usize
+            }
+            _ => 0 as usize
+        };
+        if size > 0 && self.response_buffer.len() + 2 >= size {
+            let msg: Vec<u8> = self.response_buffer.drain(0..(size + 2))
+                .collect();
+            if self.parse_response(&msg[2..]) {
+                return 1;
+            }
+            return -1;
+        }
+        0
+    }
+}
+
+/// Implement Drop for DNSState as transactions need to do some
+/// explicit cleanup.
+impl Drop for DNSState {
+    fn drop(&mut self) {
+        self.free();
+    }
+}
+
+/// Returns *mut DNSState
+#[no_mangle]
+pub extern "C" fn rs_dns_state_new() -> *mut libc::c_void {
+    let state = DNSState::new();
+    let boxed = Box::new(state);
+    return unsafe{transmute(boxed)};
+}
+
+/// Returns *mut DNSState
+#[no_mangle]
+pub extern "C" fn rs_dns_state_tcp_new() -> *mut libc::c_void {
+    let state = DNSState::new_tcp();
+    let boxed = Box::new(state);
+    return unsafe{transmute(boxed)};
+}
+
+/// Params:
+/// - state: *mut DNSState as void pointer
+#[no_mangle]
+pub extern "C" fn rs_dns_state_free(state: *mut libc::c_void) {
+    // Just unbox...
+    let _drop: Box<DNSState> = unsafe{transmute(state)};
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_state_tx_free(state: &mut DNSState,
+                                       tx_id: libc::uint64_t)
+{
+    state.free_tx(tx_id);
+}
+
+/// C binding parse a DNS request. Returns 1 on success, -1 on failure.
+#[no_mangle]
+pub extern "C" fn rs_dns_parse_request(_flow: *mut core::Flow,
+                                       state: &mut DNSState,
+                                       _pstate: *mut libc::c_void,
+                                       input: *mut libc::uint8_t,
+                                       input_len: libc::uint32_t,
+                                       _data: *mut libc::c_void)
+                                       -> libc::int8_t {
+    let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
+    if state.parse_request(buf) {
+        1
+    } else {
+        -1
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_parse_response(_flow: *mut core::Flow,
+                                        state: &mut DNSState,
+                                        _pstate: *mut libc::c_void,
+                                        input: *mut libc::uint8_t,
+                                        input_len: libc::uint32_t,
+                                        _data: *mut libc::c_void)
+                                        -> libc::int8_t {
+    let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
+    if state.parse_response(buf) {
+        1
+    } else {
+        -1
+    }
+}
+
+/// C binding parse a DNS request. Returns 1 on success, -1 on failure.
+#[no_mangle]
+pub extern "C" fn rs_dns_parse_request_tcp(_flow: *mut core::Flow,
+                                           state: &mut DNSState,
+                                           _pstate: *mut libc::c_void,
+                                           input: *mut libc::uint8_t,
+                                           input_len: libc::uint32_t,
+                                           _data: *mut libc::c_void)
+                                           -> libc::int8_t {
+    let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
+    return state.parse_request_tcp(buf);
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_parse_response_tcp(_flow: *mut core::Flow,
+                                            state: &mut DNSState,
+                                            _pstate: *mut libc::c_void,
+                                            input: *mut libc::uint8_t,
+                                            input_len: libc::uint32_t,
+                                            _data: *mut libc::c_void)
+                                            -> libc::int8_t {
+    let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
+    return state.parse_response_tcp(buf);
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_state_progress_completion_status(
+    _direction: libc::uint8_t)
+    -> libc::c_int
+{
+    SCLogDebug!("rs_dns_state_progress_completion_status");
+    return 1;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_tx_get_alstate_progress(_tx: &mut DNSTransaction,
+                                                 _direction: libc::uint8_t)
+                                                 -> libc::uint8_t
+{
+    // This is a stateless parser, just the existence of a transaction
+    // means its complete.
+    SCLogDebug!("rs_dns_tx_get_alstate_progress");
+    return 1;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_tx_set_logged(_state: &mut DNSState,
+                                       tx: &mut DNSTransaction,
+                                       logger: libc::uint32_t)
+{
+    tx.logged |= logger;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_tx_get_logged(_state: &mut DNSState,
+                                       tx: &mut DNSTransaction,
+                                       logger: libc::uint32_t)
+                                       -> i8
+{
+    if tx.logged & logger != 0 {
+        return 1;
+    }
+    return 0;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_state_get_tx_count(state: &mut DNSState)
+                                            -> libc::uint64_t
+{
+    SCLogDebug!("rs_dns_state_get_tx_count: returning {}", state.tx_id);
+    return state.tx_id;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_state_get_tx(state: &mut DNSState,
+                                      tx_id: libc::uint64_t)
+                                      -> *mut DNSTransaction
+{
+    match state.get_tx(tx_id) {
+        Some(tx) => {
+            return unsafe{transmute(tx)};
+        }
+        None => {
+            return std::ptr::null_mut();
+        }
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_state_has_detect_state(state: &mut DNSState) -> u8
+{
+    if state.de_state_count > 0 {
+        return 1;
+    }
+    return 0;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_state_set_tx_detect_state(
+    state: &mut DNSState,
+    tx: &mut DNSTransaction,
+    de_state: &mut core::DetectEngineState)
+{
+    state.de_state_count += 1;
+    tx.de_state = Some(de_state);
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_state_get_tx_detect_state(
+    tx: &mut DNSTransaction)
+    -> *mut core::DetectEngineState
+{
+    match tx.de_state {
+        Some(ds) => {
+            return ds;
+        },
+        None => {
+            return std::ptr::null_mut();
+        }
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_state_has_events(state: &mut DNSState) -> u8 {
+    if state.events > 0 {
+        return 1;
+    }
+    return 0;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_state_get_events(state: &mut DNSState,
+                                          tx_id: libc::uint64_t)
+                                          -> *mut core::AppLayerDecoderEvents
+{
+    match state.get_tx(tx_id) {
+        Some(tx) => {
+            return tx.events;
+        }
+        _ => {
+            return std::ptr::null_mut();
+        }
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_tx_get_query_name(tx: &mut DNSTransaction,
+                                       i: libc::uint16_t,
+                                       buf: *mut *const libc::uint8_t,
+                                       len: *mut libc::uint32_t)
+                                       -> libc::uint8_t
+{
+    for request in &tx.request {
+        if (i as usize) < request.queries.len() {
+            let query = &request.queries[i as usize];
+            if query.name.len() > 0 {
+                unsafe {
+                    *len = query.name.len() as libc::uint32_t;
+                    *buf = query.name.as_ptr();
+                }
+                return 1;
+            }
+        }
+    }
+    return 0;
+}
+
+/// Get the DNS transaction ID of a transaction.
+//
+/// extern uint16_t rs_dns_tx_get_tx_id(RSDNSTransaction *);
+#[no_mangle]
+pub extern "C" fn rs_dns_tx_get_tx_id(tx: &mut DNSTransaction) -> libc::uint16_t
+{
+    return tx.tx_id()
+}
+
+/// Get the DNS response flags for a transaction.
+///
+/// extern uint16_t rs_dns_tx_get_response_flags(RSDNSTransaction *);
+#[no_mangle]
+pub extern "C" fn rs_dns_tx_get_response_flags(tx: &mut DNSTransaction)
+                                           -> libc::uint16_t
+{
+    return tx.rcode();
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_tx_get_query_rrtype(tx: &mut DNSTransaction,
+                                         i: libc::uint16_t,
+                                         rrtype: *mut libc::uint16_t)
+                                         -> libc::uint8_t
+{
+    for request in &tx.request {
+        if (i as usize) < request.queries.len() {
+            let query = &request.queries[i as usize];
+            if query.name.len() > 0 {
+                unsafe {
+                    *rrtype = query.rrtype;
+                }
+                return 1;
+            }
+        }
+    }
+    return 0;
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_probe(input: *const libc::uint8_t, len: libc::uint32_t)
+                               -> libc::uint8_t
+{
+    let slice: &[u8] = unsafe {
+        std::slice::from_raw_parts(input as *mut u8, len as usize)
+    };
+    match parser::dns_parse_request(slice) {
+        nom::IResult::Done(_, _) => {
+            return 1;
+        }
+        _ => {
+            return 0;
+        }
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_probe_tcp(input: *const libc::uint8_t,
+                                   len: libc::uint32_t)
+                                   -> libc::uint8_t
+{
+    let slice: &[u8] = unsafe {
+        std::slice::from_raw_parts(input as *mut u8, len as usize)
+    };
+    match nom::be_u16(slice) {
+        nom::IResult::Done(rem, len) => {
+            if rem.len() >= len as usize {
+                match parser::dns_parse_request(rem) {
+                    nom::IResult::Done(_, _) => {
+                        return 1;
+                    }
+                    _ => {}
+                }
+            }
+        }
+        _ => {}
+    }
+    return 0;
+}
+
+#[cfg(test)]
+mod tests {
+
+    // Playing with vector draining...
+    #[test]
+    fn test_drain() {
+        let buf: &[u8] = &[
+            0x09, 0x63,
+            0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2d, 0x63, 0x66,
+            0x07, 0x64, 0x72, 0x6f, 0x70, 0x62, 0x6f, 0x78,
+            0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00,
+        ];
+        let mut v: Vec<u8> = Vec::new();
+        v.extend_from_slice(buf);
+        assert_eq!(v.len(), buf.len());
+
+        // Drain one byte.
+        let drained: Vec<u8> = v.drain(0..1).collect();
+        assert_eq!(drained.len(), 1);
+        assert_eq!(v.len(), buf.len() - 1);
+        assert_eq!(buf[0], drained[0]);
+
+        // Drain some more.
+        v.drain(0..8);
+        assert_eq!(v.len(), buf.len() - 9);
+    }
+}
diff --git a/rust/src/dns/log.rs b/rust/src/dns/log.rs
new file mode 100644 (file)
index 0000000..3fe4133
--- /dev/null
@@ -0,0 +1,211 @@
+/* Copyright (C) 2017 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.
+ */
+
+extern crate libc;
+
+use std;
+use std::string::String;
+
+use json::*;
+use dns::dns::*;
+use log::*;
+
+fn dns_rrtype_string(rrtype: u16) -> String {
+    match rrtype {
+        DNS_RTYPE_A => "A",
+        DNS_RTYPE_CNAME => "CNAME",
+        DNS_RTYPE_SOA => "SOA",
+        DNS_RTYPE_PTR => "PTR",
+        DNS_RTYPE_MX => "MX",
+        DNS_RTYPE_SSHFP => "SSHFP",
+        DNS_RTYPE_RRSIG => "RRSIG",
+        _ => {
+            return rrtype.to_string();
+        }
+    }.to_string()
+}
+
+fn dns_rcode_string(flags: u16) -> String {
+    match flags & 0x000f {
+        DNS_RCODE_NOERROR => "NOERROR",
+        DNS_RCODE_FORMERR => "FORMERR",
+        DNS_RCODE_NXDOMAIN => "NXDOMAIN",
+        _ => {
+            return (flags & 0x000f).to_string();
+        }
+    }.to_string()
+}
+
+/// Format bytes as an IP address string.
+fn dns_print_addr(addr: &Vec<u8>) -> std::string::String {
+    if addr.len() == 4 {
+        return format!("{}.{}.{}.{}", addr[0], addr[1], addr[2], addr[3]);
+    }
+    else if addr.len() == 16 {
+        return format!("{}{}:{}{}:{}{}:{}{}:{}{}:{}{}:{}{}:{}{}",
+                       addr[0],
+                       addr[1],
+                       addr[2],
+                       addr[3],
+                       addr[4],
+                       addr[5],
+                       addr[6],
+                       addr[7],
+                       addr[8],
+                       addr[9],
+                       addr[10],
+                       addr[11],
+                       addr[12],
+                       addr[13],
+                       addr[14],
+                       addr[15]);
+    }
+    else {
+        return "".to_string();
+    }
+}
+
+///  Log the SSHPF in an DNSAnswerEntry.
+fn dns_log_sshfp(js: &Json, answer: &DNSAnswerEntry)
+{
+    // Need at least 3 bytes - TODO: log something if we don't?
+    if answer.data_len < 3 {
+        return;
+    }
+
+    let sshfp = Json::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);
+
+    js.set("sshfp", sshfp);
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_log_json_query(tx: &mut DNSTransaction,
+                                        i: libc::uint16_t)
+                                        -> *mut JsonT
+{
+    SCLogDebug!("rs_dns_log_json_query: tx_id={}, i={}", tx.id, i);
+    let index = i as usize;
+    for request in &tx.request {
+        if index < request.queries.len() {
+            let query = &request.queries[index];
+            let js = Json::object();
+            js.set_string("type", "query");
+            js.set_integer("id", request.header.tx_id as u64);
+            js.set_string("rrname", query.name());
+            js.set_string("rrtype", &dns_rrtype_string(query.rrtype));
+            js.set_integer("tx_id", tx.id - 1);
+            return js.unwrap();
+        }
+    }
+
+    return std::ptr::null_mut();
+}
+
+fn dns_log_json_answer(header: &DNSHeader, answer: &DNSAnswerEntry)
+                       -> Json
+{
+    let js = Json::object();
+
+    js.set_string("type", "answer");
+    js.set_integer("id", header.tx_id as u64);
+    js.set_string("rcode", &dns_rcode_string(header.flags));
+    js.set_string("rrname", answer.name());
+    js.set_string("rrtype", &dns_rrtype_string(answer.rrtype));
+    js.set_integer("ttl", answer.ttl as u64);
+
+    match answer.rrtype {
+        DNS_RTYPE_A | DNS_RTYPE_AAAA => {
+            js.set_string("rdata", &dns_print_addr(&answer.data));
+        }
+        DNS_RTYPE_CNAME |
+        DNS_RTYPE_MX |
+        DNS_RTYPE_PTR => {
+            js.set_string("rdata", answer.data_to_string());
+        },
+        DNS_RTYPE_SSHFP => {
+            dns_log_sshfp(&js, &answer);
+        },
+        _ => {}
+    }
+
+    return js;
+}
+
+fn dns_log_json_failure(r: &DNSResponse, index: usize) -> * mut JsonT {
+    if index >= r.queries.len() {
+        return std::ptr::null_mut();
+    }
+
+    let ref query = r.queries[index];
+
+    let js = Json::object();
+
+    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("rrname", std::str::from_utf8(&query.name[..]).unwrap());
+
+    return js.unwrap();
+}
+
+#[no_mangle]
+pub extern "C" fn rs_dns_log_json_answer(tx: &mut DNSTransaction,
+                                         i: libc::uint16_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);
+            }
+            break;
+        }
+        if index >= response.answers.len() {
+            break;
+        }
+        let answer = &response.answers[index];
+        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)
+                                            -> *mut JsonT
+{
+    let index = i as usize;
+    for response in &tx.response {
+        if index >= response.authorities.len() {
+            break;
+        }
+        let answer = &response.authorities[index];
+        let js = dns_log_json_answer(&response.header, answer);
+        return js.unwrap();
+    }
+    return std::ptr::null_mut();
+}
index d1821c3c4fcc63ed9428625dd76c4638c489b210..d6693b0a9ad2aad1fbe75718775d603c060ad60a 100644 (file)
  * 02110-1301, USA.
  */
 
-use log::*;
-use conf;
-
 pub mod parser;
-pub use self::parser::*;
-
 pub mod dns;
-pub use self::dns::*;
-
-#[no_mangle]
-pub extern "C" fn rs_dns_init() {
-    SCLogNotice!("Initializing DNS analyzer");
-
-    match conf::conf_get("app-layer.protocols.dns.tcp.enabled") {
-        Some(val) => SCLogNotice!("- TCP is enabled: {}", val),
-        None => SCLogNotice!("- TCP is not enabled."),
-    }
-
-    match conf::conf_get("app-layer.protocols.dns.udp.enabled") {
-        Some(val) => SCLogNotice!("- UDP is enabled: {}", val),
-        None => SCLogNotice!("- UDP is not enabled."),
-    }
-}
+pub mod log;
index c72ac97b40b42bc4a7c6917474348e1ce7bdc7ae..8794b311db5a3b6f35d79b4495482f62757e300d 100644 (file)
@@ -19,7 +19,7 @@
 
 use nom::{be_u8, be_u16, be_u32};
 use nom;
-use dns::*;
+use dns::dns::*;
 
 /// Parse a DNS header.
 named!(pub dns_parse_header<DNSHeader>,
@@ -147,19 +147,19 @@ pub fn dns_parse_response<'a>(slice: &'a [u8])
     ));
 
     let response = closure!(&'a [u8], do_parse!(
-        header: dns_parse_header >>
-        queries: count!(apply!(dns_parse_query, slice),
-                        header.questions as usize) >>
-        answers: count!(answer_parser, header.answer_rr as usize) >>
-        authorities: count!(answer_parser, header.authority_rr as usize) >>
-        (
-            DNSResponse{
-                header: header,
-                queries: queries,
-                answers: answers,
-                authorities: authorities,
-            }
-        )
+        header: dns_parse_header
+            >> queries: count!(apply!(dns_parse_query, slice),
+                               header.questions as usize)
+            >> answers: count!(answer_parser, header.answer_rr as usize)
+            >> authorities: count!(answer_parser, header.authority_rr as usize)
+            >> (
+                DNSResponse{
+                    header: header,
+                    queries: queries,
+                    answers: answers,
+                    authorities: authorities,
+                }
+            )
     ))(slice);
 
     return response;
index 09325831c009d4fe6921d0504cc996b912ae0466..a8bc12607f2bc16ccc7e9c2b2437251bf590310e 100644 (file)
@@ -20,7 +20,9 @@ app-layer-dnp3.c app-layer-dnp3.h \
 app-layer-dnp3-objects.c app-layer-dnp3-objects.h \
 app-layer-dns-common.c app-layer-dns-common.h \
 app-layer-dns-tcp.c app-layer-dns-tcp.h \
+app-layer-dns-tcp-rust.c app-layer-dns-tcp-rust.h \
 app-layer-dns-udp.c app-layer-dns-udp.h \
+app-layer-dns-udp-rust.c app-layer-dns-udp-rust.h \
 app-layer-enip.c app-layer-enip.h \
 app-layer-enip-common.c app-layer-enip-common.h \
 app-layer-events.c app-layer-events.h \
index 2e069d0f4b9d098c4de91099baaf067e43d4008a..51f3763de07038a254569fc33116996a56f32ccc 100644 (file)
@@ -124,6 +124,10 @@ enum {
     DNS_DECODER_EVENT_STATE_MEMCAP_REACHED,
 };
 
+/** Opaque Rust types. */
+typedef struct RSDNSState_ RSDNSState;
+typedef struct RSDNSTransaction_ RSDNSTransaction;
+
 /** \brief DNS packet header */
 typedef struct DNSHeader_ {
     uint16_t tx_id;
diff --git a/src/app-layer-dns-tcp-rust.c b/src/app-layer-dns-tcp-rust.c
new file mode 100644 (file)
index 0000000..8c94f7d
--- /dev/null
@@ -0,0 +1,204 @@
+/* Copyright (C) 2017 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 "suricata.h"
+
+#include "app-layer-protos.h"
+#include "app-layer-detect-proto.h"
+#include "app-layer-parser.h"
+#include "app-layer-dns-common.h"
+
+#ifdef HAVE_RUST
+
+#include "app-layer-dns-tcp-rust.h"
+#include "rust-dns-dns-gen.h"
+
+static void RustDNSTCPParserRegisterTests(void);
+
+static int RustDNSTCPParseRequest(Flow *f, void *state,
+        AppLayerParserState *pstate, uint8_t *input, uint32_t input_len,
+        void *local_data)
+{
+    SCLogDebug("RustDNSTCPParseRequest");
+    return rs_dns_parse_request_tcp(f, state, pstate, input, input_len,
+            local_data);
+}
+
+static int RustDNSTCPParseResponse(Flow *f, void *state,
+        AppLayerParserState *pstate, uint8_t *input, uint32_t input_len,
+        void *local_data)
+{
+    SCLogDebug("RustDNSTCPParseResponse");
+    return rs_dns_parse_response_tcp(f, state, pstate, input, input_len,
+            local_data);
+}
+
+static uint16_t RustDNSTCPProbe(uint8_t *input, uint32_t len, uint32_t *offset)
+{
+    SCLogDebug("RustDNSTCPProbe");
+    if (len == 0 || len < sizeof(DNSHeader)) {
+        return ALPROTO_UNKNOWN;
+    }
+
+    // Validate and return ALPROTO_FAILED if needed.
+    if (!rs_dns_probe_tcp(input, len)) {
+        return ALPROTO_FAILED;
+    }
+
+    return ALPROTO_DNS;
+}
+
+static int RustDNSGetAlstateProgress(void *tx, uint8_t direction)
+{
+    return rs_dns_tx_get_alstate_progress(tx, direction);
+}
+
+static uint64_t RustDNSGetTxCnt(void *alstate)
+{
+    return rs_dns_state_get_tx_count(alstate);
+}
+
+static void *RustDNSGetTx(void *alstate, uint64_t tx_id)
+{
+    return rs_dns_state_get_tx(alstate, tx_id);
+}
+
+static void RustDNSSetTxLogged(void *alstate, void *tx, uint32_t logger)
+{
+    rs_dns_tx_set_logged(alstate, tx, logger);
+}
+
+static int RustDNSGetTxLogged(void *alstate, void *tx, uint32_t logger)
+{
+    return rs_dns_tx_get_logged(alstate, tx, logger);
+}
+
+static void RustDNSStateTransactionFree(void *state, uint64_t tx_id)
+{
+    rs_dns_state_tx_free(state, tx_id);
+}
+
+static int RustDNSStateHasTxDetectState(void *state)
+{
+    return rs_dns_state_has_detect_state(state);
+}
+
+static DetectEngineState *RustDNSGetTxDetectState(void *tx)
+{
+    return rs_dns_state_get_tx_detect_state(tx);
+}
+
+static int RustDNSSetTxDetectState(void *state, void *tx,
+        DetectEngineState *s)
+{
+    rs_dns_state_set_tx_detect_state(state, tx, s);
+    return 0;
+}
+
+static int RustDNSHasEvents(void *state)
+{
+    return rs_dns_state_has_events(state);
+}
+
+static AppLayerDecoderEvents *RustDNSGetEvents(void *state, uint64_t id)
+{
+    return rs_dns_state_get_events(state, id);
+}
+
+void RegisterRustDNSTCPParsers(void)
+{
+    const char *proto_name = "dns";
+
+    /** DNS */
+    if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
+        AppLayerProtoDetectRegisterProtocol(ALPROTO_DNS, proto_name);
+
+        if (RunmodeIsUnittests()) {
+            AppLayerProtoDetectPPRegister(IPPROTO_TCP, "53", ALPROTO_DNS, 0,
+                    sizeof(DNSHeader) + 2, STREAM_TOSERVER, RustDNSTCPProbe,
+                    NULL);
+        } else {
+            int have_cfg = AppLayerProtoDetectPPParseConfPorts("tcp",
+                    IPPROTO_TCP, proto_name, ALPROTO_DNS, 0,
+                    sizeof(DNSHeader) + 2, RustDNSTCPProbe, RustDNSTCPProbe);
+            /* if we have no config, we enable the default port 53 */
+            if (!have_cfg) {
+                SCLogWarning(SC_ERR_DNS_CONFIG, "no DNS TCP config found, "
+                                                "enabling DNS detection on "
+                                                "port 53.");
+                AppLayerProtoDetectPPRegister(IPPROTO_TCP, "53", ALPROTO_DNS, 0,
+                        sizeof(DNSHeader) + 2, STREAM_TOSERVER, RustDNSTCPProbe,
+                        RustDNSTCPProbe);
+            }
+        }
+    } else {
+        SCLogInfo("Protocol detection and parser disabled for %s protocol.",
+                  proto_name);
+        return;
+    }
+
+    if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
+        AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_DNS, STREAM_TOSERVER,
+                RustDNSTCPParseRequest);
+        AppLayerParserRegisterParser(IPPROTO_TCP , ALPROTO_DNS, STREAM_TOCLIENT,
+                RustDNSTCPParseResponse);
+        AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_DNS,
+                rs_dns_state_tcp_new, rs_dns_state_free);
+        AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_DNS,
+                RustDNSStateTransactionFree);
+        AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_DNS,
+                RustDNSGetEvents);
+        AppLayerParserRegisterHasEventsFunc(IPPROTO_TCP, ALPROTO_DNS,
+                RustDNSHasEvents);
+        AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_DNS,
+                RustDNSStateHasTxDetectState, RustDNSGetTxDetectState,
+                RustDNSSetTxDetectState);
+        AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_DNS, RustDNSGetTx);
+        AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_DNS,
+                RustDNSGetTxCnt);
+        AppLayerParserRegisterLoggerFuncs(IPPROTO_TCP, ALPROTO_DNS,
+                RustDNSGetTxLogged, RustDNSSetTxLogged);
+        AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_DNS,
+                RustDNSGetAlstateProgress);
+        AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_DNS,
+                rs_dns_state_progress_completion_status);
+        DNSAppLayerRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_DNS);
+    } else {
+        SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
+                  "still on.", proto_name);
+    }
+
+#ifdef UNITTESTS
+    AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_DNS,
+            RustDNSTCPParserRegisterTests);
+#endif
+
+    return;
+}
+
+#ifdef UNITTESTS
+#endif /* UNITTESTS */
+
+void RustDNSTCPParserRegisterTests(void)
+{
+#if 0
+    UtRegisterTest("DNSTCPParserTestMultiRecord", DNSTCPParserTestMultiRecord);
+#endif
+}
+
+#endif /* HAVE_RUST */
diff --git a/src/app-layer-dns-tcp-rust.h b/src/app-layer-dns-tcp-rust.h
new file mode 100644 (file)
index 0000000..7409496
--- /dev/null
@@ -0,0 +1,23 @@
+/* Copyright (C) 2017 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 __APP_LAYER_DNS_TCP_RUST_H__
+#define __APP_LAYER_DNS_TCP_RUST_H__
+
+void RegisterRustDNSTCPParsers(void);
+
+#endif /* !__APP_LAYER_DNS_TCP_RUST_H__ */
index cb88232e201570ede4fec69a98f80fcc4ac573c6..785ef5415087f8f3d30db13aac59f0df519c3a34 100644 (file)
 
 #include "app-layer-dns-tcp.h"
 
+#ifdef HAVE_RUST
+#include "app-layer-dns-tcp-rust.h"
+#endif
+
 struct DNSTcpHeader_ {
     uint16_t len;
     uint16_t tx_id;
@@ -693,7 +697,10 @@ static uint16_t DNSTcpProbeResponse(uint8_t *input, uint32_t len,
 void RegisterDNSTCPParsers(void)
 {
     const char *proto_name = "dns";
-
+#ifdef HAVE_RUST
+    RegisterRustDNSTCPParsers();
+    return;
+#endif
     /** DNS */
     if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
         AppLayerProtoDetectRegisterProtocol(ALPROTO_DNS, proto_name);
diff --git a/src/app-layer-dns-udp-rust.c b/src/app-layer-dns-udp-rust.c
new file mode 100644 (file)
index 0000000..cc189a1
--- /dev/null
@@ -0,0 +1,197 @@
+/* Copyright (C) 2017 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 "suricata.h"
+
+#include "app-layer-protos.h"
+#include "app-layer-detect-proto.h"
+#include "app-layer-parser.h"
+#include "app-layer-dns-common.h"
+
+#ifdef HAVE_RUST
+
+#include "app-layer-dns-udp-rust.h"
+#include "rust-dns-dns-gen.h"
+
+static int RustDNSUDPParseRequest(Flow *f, void *state,
+        AppLayerParserState *pstate, uint8_t *input, uint32_t input_len,
+        void *local_data)
+{
+    return rs_dns_parse_request(f, state, pstate, input, input_len,
+            local_data);
+}
+
+static int RustDNSUDPParseResponse(Flow *f, void *state,
+        AppLayerParserState *pstate, uint8_t *input, uint32_t input_len,
+        void *local_data)
+{
+    return rs_dns_parse_response(f, state, pstate, input, input_len,
+            local_data);
+}
+
+static uint16_t DNSUDPProbe(uint8_t *input, uint32_t len, uint32_t *offset)
+{
+    if (len == 0 || len < sizeof(DNSHeader)) {
+        return ALPROTO_UNKNOWN;
+    }
+
+    // Validate and return ALPROTO_FAILED if needed.
+    if (!rs_dns_probe(input, len)) {
+        return ALPROTO_FAILED;
+    }
+
+    return ALPROTO_DNS;
+}
+
+static int RustDNSGetAlstateProgress(void *tx, uint8_t direction)
+{
+    return rs_dns_tx_get_alstate_progress(tx, direction);
+}
+
+static uint64_t RustDNSGetTxCnt(void *alstate)
+{
+    return rs_dns_state_get_tx_count(alstate);
+}
+
+static void *RustDNSGetTx(void *alstate, uint64_t tx_id)
+{
+    return rs_dns_state_get_tx(alstate, tx_id);
+}
+
+static void RustDNSSetTxLogged(void *alstate, void *tx, uint32_t logger)
+{
+    rs_dns_tx_set_logged(alstate, tx, logger);
+}
+
+static int RustDNSGetTxLogged(void *alstate, void *tx, uint32_t logger)
+{
+    return rs_dns_tx_get_logged(alstate, tx, logger);
+}
+
+static void RustDNSStateTransactionFree(void *state, uint64_t tx_id)
+{
+    rs_dns_state_tx_free(state, tx_id);
+}
+
+static int RustDNSStateHasTxDetectState(void *state)
+{
+    return rs_dns_state_has_detect_state(state);
+}
+
+static DetectEngineState *RustDNSGetTxDetectState(void *tx)
+{
+    return rs_dns_state_get_tx_detect_state(tx);
+}
+
+static int RustDNSSetTxDetectState(void *state, void *tx,
+        DetectEngineState *s)
+{
+    rs_dns_state_set_tx_detect_state(state, tx, s);
+    return 0;
+}
+
+static int RustDNSHasEvents(void *state)
+{
+    return rs_dns_state_has_events(state);
+}
+
+static AppLayerDecoderEvents *RustDNSGetEvents(void *state, uint64_t id)
+{
+    return rs_dns_state_get_events(state, id);
+}
+
+void RegisterRustDNSUDPParsers(void)
+{
+    const char *proto_name = "dns";
+
+    /** DNS */
+    if (AppLayerProtoDetectConfProtoDetectionEnabled("udp", proto_name)) {
+        AppLayerProtoDetectRegisterProtocol(ALPROTO_DNS, proto_name);
+
+        if (RunmodeIsUnittests()) {
+            AppLayerProtoDetectPPRegister(IPPROTO_UDP, "53", ALPROTO_DNS, 0,
+                    sizeof(DNSHeader), STREAM_TOSERVER, DNSUDPProbe,
+                    NULL);
+        } else {
+            int have_cfg = AppLayerProtoDetectPPParseConfPorts("udp",
+                    IPPROTO_UDP, proto_name, ALPROTO_DNS, 0, sizeof(DNSHeader),
+                    DNSUDPProbe, NULL);
+
+            /* If no config, enable on port 53. */
+            if (!have_cfg) {
+#ifndef AFLFUZZ_APPLAYER
+                SCLogWarning(SC_ERR_DNS_CONFIG, "no DNS UDP config found, "
+                        "enabling DNS detection on port 53.");
+#endif
+                AppLayerProtoDetectPPRegister(IPPROTO_UDP, "53", ALPROTO_DNS,
+                        0, sizeof(DNSHeader), STREAM_TOSERVER,
+                        DNSUDPProbe, NULL);
+            }
+        }
+    } else {
+        SCLogInfo("Protocol detection and parser disabled for %s protocol.",
+                  proto_name);
+        return;
+    }
+
+    if (AppLayerParserConfParserEnabled("udp", proto_name)) {
+        AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_DNS, STREAM_TOSERVER,
+                RustDNSUDPParseRequest);
+        AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_DNS, STREAM_TOCLIENT,
+                RustDNSUDPParseResponse);
+        AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_DNS,
+                rs_dns_state_new, rs_dns_state_free);
+        AppLayerParserRegisterTxFreeFunc(IPPROTO_UDP, ALPROTO_DNS,
+                RustDNSStateTransactionFree);
+        AppLayerParserRegisterGetEventsFunc(IPPROTO_UDP, ALPROTO_DNS,
+                RustDNSGetEvents);
+        AppLayerParserRegisterHasEventsFunc(IPPROTO_UDP, ALPROTO_DNS,
+                RustDNSHasEvents);
+        AppLayerParserRegisterDetectStateFuncs(IPPROTO_UDP, ALPROTO_DNS,
+                RustDNSStateHasTxDetectState, RustDNSGetTxDetectState,
+                RustDNSSetTxDetectState);
+
+        AppLayerParserRegisterGetTx(IPPROTO_UDP, ALPROTO_DNS, RustDNSGetTx);
+        AppLayerParserRegisterGetTxCnt(IPPROTO_UDP, ALPROTO_DNS,
+                RustDNSGetTxCnt);
+        AppLayerParserRegisterLoggerFuncs(IPPROTO_UDP, ALPROTO_DNS,
+                RustDNSGetTxLogged, RustDNSSetTxLogged);
+        AppLayerParserRegisterGetStateProgressFunc(IPPROTO_UDP, ALPROTO_DNS,
+                RustDNSGetAlstateProgress);
+
+        AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_DNS,
+                rs_dns_state_progress_completion_status);
+
+        DNSAppLayerRegisterGetEventInfo(IPPROTO_UDP, ALPROTO_DNS);
+
+#if 0
+        DNSUDPConfigure();
+#endif
+    } else {
+        SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
+                "still on.", proto_name);
+    }
+#if 0
+#ifdef UNITTESTS
+    AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_DNS,
+            DNSUDPParserRegisterTests);
+#endif
+#endif
+}
+
+#endif /* HAVE_RUST */
diff --git a/src/app-layer-dns-udp-rust.h b/src/app-layer-dns-udp-rust.h
new file mode 100644 (file)
index 0000000..e457375
--- /dev/null
@@ -0,0 +1,23 @@
+/* Copyright (C) 2017 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 __APP_LAYER_DNS_UDP_RUST_H__
+#define __APP_LAYER_DNS_UDP_RUST_H__
+
+void RegisterRustDNSUDPParsers(void);
+
+#endif /* !__APP_LAYER_DNS_UDP_RUST_H__ */
index 8d6ae4d2a6cbb44cb964af8651c2750090360bc7..29a89b4955d31040cceee437c894a681c91773c0 100644 (file)
@@ -51,7 +51,7 @@
 #include "app-layer-dns-udp.h"
 
 #ifdef HAVE_RUST
-#include "rust.h"
+#include "app-layer-dns-udp-rust.h"
 #endif
 
 /** \internal
@@ -390,9 +390,7 @@ void RegisterDNSUDPParsers(void)
     const char *proto_name = "dns";
 
 #ifdef HAVE_RUST
-    /* If DNS was implemented in Rust, we could call into the rust
-     * init function here. */
-    rs_dns_init();
+    return RegisterRustDNSUDPParsers();
 #endif
 
     /** DNS */
index 2891d088310e3ff7e69dbfbbd3c71759c2882fa2..8226920191d2dbad5ef5745120e5d9e9deada00a 100644 (file)
 #include "util-unittest-helper.h"
 #include "util-validate.h"
 
+#ifdef HAVE_RUST
+#include "rust-dns-dns-gen.h"
+#endif
+
 /** \brief Do the content inspection & validation for a signature
  *
  *  \param de_ctx Detection engine context
@@ -67,14 +71,32 @@ int DetectEngineInspectDnsQueryName(ThreadVars *tv,
         Flow *f, uint8_t flags, void *alstate,
         void *txv, uint64_t tx_id)
 {
-    DNSTransaction *tx = (DNSTransaction *)txv;
-    DNSQueryEntry *query = NULL;
     uint8_t *buffer;
-    uint16_t buffer_len;
+    uint32_t buffer_len;
     int r = 0;
 
     SCLogDebug("start");
 
+#ifdef HAVE_RUST
+    for (uint16_t i = 0;; i++) {
+        det_ctx->discontinue_matching = 0;
+        det_ctx->buffer_offset = 0;
+        det_ctx->inspection_recursion_counter = 0;
+
+        if (rs_dns_tx_get_query_name(txv, i, &buffer, &buffer_len)) {
+            r = DetectEngineContentInspection(de_ctx, det_ctx,
+                s, smd, f, buffer, buffer_len, 0,
+                DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE, NULL);
+            if (r == 1) {
+                break;
+            }
+        } else {
+            break;
+        }
+    }
+#else
+    DNSTransaction *tx = (DNSTransaction *)txv;
+    DNSQueryEntry *query = NULL;
     TAILQ_FOREACH(query, &tx->query_list, next) {
         SCLogDebug("tx %p query %p", tx, query);
         det_ctx->discontinue_matching = 0;
@@ -93,6 +115,7 @@ int DetectEngineInspectDnsQueryName(ThreadVars *tv,
         if (r == 1)
             break;
     }
+#endif
     return r;
 }
 
@@ -110,8 +133,23 @@ static void PrefilterTxDnsQuery(DetectEngineThreadCtx *det_ctx,
         const uint64_t idx, const uint8_t flags)
 {
     SCEnter();
-
     const MpmCtx *mpm_ctx = (MpmCtx *)pectx;
+
+#ifdef HAVE_RUST
+    uint8_t *buffer;
+    uint32_t buffer_len;
+    for (uint16_t i = 0; i < 0xffff; i++) {
+        if (rs_dns_tx_get_query_name(txv, i, &buffer, &buffer_len)) {
+            if (buffer_len >= mpm_ctx->minlen) {
+                (void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx,
+                    &det_ctx->mtcu, &det_ctx->pmq,
+                    buffer, buffer_len);
+            }
+        } else {
+            break;
+        }
+    }
+#else
     DNSTransaction *tx = (DNSTransaction *)txv;
     DNSQueryEntry *query = NULL;
 
@@ -128,6 +166,7 @@ static void PrefilterTxDnsQuery(DetectEngineThreadCtx *det_ctx,
                     buffer, buffer_len);
         }
     }
+#endif
 }
 
 int PrefilterTxDnsQueryRegister(SigGroupHead *sgh, MpmCtx *mpm_ctx)
index df3e19c39d5e50614dd0f45a39c35980339350d8..7b1ce7bc44b4c4922a3ad2e61382bc1ae2701fb2 100644 (file)
@@ -164,6 +164,10 @@ static void LogAnswer(LogDnsLogThread *aft, char *timebuf, char *srcip, char *ds
 static int LogDnsLogger(ThreadVars *tv, void *data, const Packet *p,
     Flow *f, void *state, void *tx, uint64_t tx_id, uint8_t direction)
 {
+#ifdef HAVE_RUST
+    SCLogNotice("LogDnsLogger not implemented for Rust DNS.");
+    return 0;
+#endif
     LogDnsLogThread *aft = (LogDnsLogThread *)data;
     DNSTransaction *dns_tx = (DNSTransaction *)tx;
     SCLogDebug("pcap_cnt %"PRIu64, p->pcap_cnt);
index a438fb0164008cd63d2e40907c12d2ac204b5ecc..98b10129ca92941f01733b8fd35b5bcdcf6d7b9e 100644 (file)
 
 #ifdef HAVE_LIBJANSSON
 
+#ifdef HAVE_RUST
+#include "rust-dns-log-gen.h"
+#endif
+
 /* we can do query logging as well, but it's disabled for now as the
  * TX id handling doesn't expect it */
 #define QUERY 0
@@ -260,6 +264,7 @@ typedef struct LogDnsLogThread_ {
     MemBuffer *buffer;
 } LogDnsLogThread;
 
+#ifndef HAVE_RUST
 static int DNSRRTypeEnabled(uint16_t type, uint64_t flags)
 {
     if (likely(flags == ~0UL)) {
@@ -387,7 +392,9 @@ static int DNSRRTypeEnabled(uint16_t type, uint64_t flags)
             return 0;
     }
 }
+#endif
 
+#ifndef HAVE_RUST
 static void LogQuery(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx,
         uint64_t tx_id, DNSQueryEntry *entry) __attribute__((nonnull));
 
@@ -435,7 +442,9 @@ static void LogQuery(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx,
     OutputJSONBuffer(js, aft->dnslog_ctx->file_ctx, &aft->buffer);
     json_object_del(js, "dns");
 }
+#endif
 
+#ifndef HAVE_RUST
 static void OutputAnswer(LogDnsLogThread *aft, json_t *djs,
         DNSTransaction *tx, DNSAnswerEntry *entry) __attribute__((nonnull));
 
@@ -546,7 +555,9 @@ static void OutputAnswer(LogDnsLogThread *aft, json_t *djs,
 
     return;
 }
+#endif
 
+#ifndef HAVE_RUST
 static void OutputFailure(LogDnsLogThread *aft, json_t *djs,
         DNSTransaction *tx, DNSQueryEntry *entry) __attribute__((nonnull));
 
@@ -588,7 +599,9 @@ static void OutputFailure(LogDnsLogThread *aft, json_t *djs,
 
     return;
 }
+#endif
 
+#ifndef HAVE_RUST
 static void LogAnswers(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx, uint64_t tx_id)
 {
 
@@ -616,6 +629,7 @@ static void LogAnswers(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx, uin
     }
 
 }
+#endif
 
 static int JsonDnsLoggerToServer(ThreadVars *tv, void *thread_data,
     const Packet *p, Flow *f, void *alstate, void *txptr, uint64_t tx_id)
@@ -624,9 +638,30 @@ static int JsonDnsLoggerToServer(ThreadVars *tv, void *thread_data,
 
     LogDnsLogThread *td = (LogDnsLogThread *)thread_data;
     LogDnsFileCtx *dnslog_ctx = td->dnslog_ctx;
-    DNSTransaction *tx = txptr;
     json_t *js;
 
+    if (unlikely(dnslog_ctx->flags & LOG_QUERIES) == 0) {
+        return TM_ECODE_OK;
+    }
+
+#ifdef HAVE_RUST
+    for (uint16_t i = 0; i < 0xffff; i++) {
+        js = CreateJSONHeader((Packet *)p, 1, "dns");
+        if (unlikely(js == NULL)) {
+            return TM_ECODE_OK;
+        }
+        json_t *dns = rs_dns_log_json_query(txptr, i);
+        if (unlikely(dns == NULL)) {
+            json_decref(js);
+            break;
+        }
+        json_object_set_new(js, "dns", dns);
+        MemBufferReset(td->buffer);
+        OutputJSONBuffer(js, td->dnslog_ctx->file_ctx, &td->buffer);
+        json_decref(js);
+    }
+#else
+    DNSTransaction *tx = txptr;
     if (likely(dnslog_ctx->flags & LOG_QUERIES) != 0) {
         DNSQueryEntry *query = NULL;
         TAILQ_FOREACH(query, &tx->query_list, next) {
@@ -639,6 +674,7 @@ static int JsonDnsLoggerToServer(ThreadVars *tv, void *thread_data,
             json_decref(js);
         }
     }
+#endif
 
     SCReturnInt(TM_ECODE_OK);
 }
@@ -650,19 +686,48 @@ static int JsonDnsLoggerToClient(ThreadVars *tv, void *thread_data,
 
     LogDnsLogThread *td = (LogDnsLogThread *)thread_data;
     LogDnsFileCtx *dnslog_ctx = td->dnslog_ctx;
-    DNSTransaction *tx = txptr;
     json_t *js;
 
-    if (likely(dnslog_ctx->flags & LOG_ANSWERS) != 0) {
-        js = CreateJSONHeader((Packet *)p, 0, "dns");
-        if (unlikely(js == NULL))
-            return TM_ECODE_OK;
+    if (unlikely(dnslog_ctx->flags & LOG_ANSWERS) == 0) {
+        return TM_ECODE_OK;
+    }
 
-        LogAnswers(td, js, tx, tx_id);
+    js = CreateJSONHeader((Packet *)p, 0, "dns");
 
-        json_decref(js);
+#if HAVE_RUST
+    /* Log answers. */
+    for (uint16_t i = 0; i < 0xffff; i++) {
+        json_t *answer = rs_dns_log_json_answer(txptr, i);
+        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");
     }
 
+    /* Log authorities. */
+    for (uint16_t i = 0; i < 0xffff; i++) {
+        json_t *answer = rs_dns_log_json_authority(txptr, i);
+        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;
+    if (unlikely(js == NULL))
+        return TM_ECODE_OK;
+
+    LogAnswers(td, js, tx, tx_id);
+#endif
+
+    json_decref(js);
+
     SCReturnInt(TM_ECODE_OK);
 }
 
index 68314cbd771fc2e538f19bef4d2e80fec4b23955..b5ee1b50383386f81518a68b64da8d738b833786 100644 (file)
@@ -27,6 +27,4 @@ typedef struct SuricataContext_ {
     void (*AppLayerDecoderEventsFreeEvents)(AppLayerDecoderEvents **);
 } SuricataContext;
 
-void rs_dns_init(void);
-
 #endif /* !__RUST_H__ */
index 7ab3ec3af7b4567d3db97ccaddf92deac283461a..afda25d67ea6070c481a6d8e5d263da4859b1956 100644 (file)
 
 #ifdef HAVE_RUST
 #include "rust.h"
-#include "rust-core.h"
+#include "rust-core-gen.h"
 #endif
 
 /*
index 027538a1f121c857cd29f2c8ea5584460cbac81e..97b1626b2bb6d0ea1686b83dc3b2e63dd35b5cc0 100644 (file)
@@ -44,7 +44,7 @@
 #include "util-syslog.h"
 
 #ifdef HAVE_RUST
-#include "rust-log.h"
+#include "rust-log-gen.h"
 #endif
 
 #include "conf.h"
index da5c99deb37f74742de3849661ed8ea730e32d02..761b1f7c9a8f7e109c0956de98dbe0746a8173dd 100644 (file)
 #include "util-lua-common.h"
 #include "util-lua-dns.h"
 
+#ifdef HAVE_RUST
+#include "rust-dns-dns-gen.h"
+#endif
+
 static int DnsGetDnsRrname(lua_State *luastate)
 {
     if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
         return LuaCallbackError(luastate, "error: protocol not dns");
+#ifdef HAVE_RUST
+    RSDNSTransaction *tx = LuaStateGetTX(luastate);
+    if (tx == NULL)
+        return LuaCallbackError(luastate, "internal error: no tx");
+
+    for (uint16_t i = 0;; i++) {
+        uint32_t buf_len;
+        uint8_t *buf;
 
+        if (!rs_dns_tx_get_query_name(tx, i, &buf, &buf_len)) {
+            break;
+        }
+
+        char *rrname = BytesToString(buf, buf_len);
+        if (rrname != NULL) {
+            size_t input_len = strlen(rrname);
+            /* sanity check */
+            if (input_len > (size_t)(2 * buf_len)) {
+                SCFree(rrname);
+                return LuaCallbackError(luastate, "invalid length");
+            }
+            int ret = LuaPushStringBuffer(luastate, (uint8_t *)rrname,
+                input_len);
+            SCFree(rrname);
+            return ret;
+        }
+    }
+#else
     DNSTransaction *tx = LuaStateGetTX(luastate);
     if (tx == NULL)
         return LuaCallbackError(luastate, "internal error: no tx");
@@ -85,7 +116,7 @@ static int DnsGetDnsRrname(lua_State *luastate)
             return ret;
         }
     }
-
+#endif
     return LuaCallbackError(luastate, "no query");
 }
 
@@ -93,12 +124,19 @@ static int DnsGetTxid(lua_State *luastate)
 {
     if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
         return LuaCallbackError(luastate, "error: protocol not dns");
-
+#ifdef HAVE_RUST
+    RSDNSTransaction *tx = LuaStateGetTX(luastate);
+    if (tx == NULL)
+        return LuaCallbackError(luastate, "internal error: no tx");
+    uint16_t tx_id = rs_dns_tx_get_tx_id(tx);
+    lua_pushinteger(luastate, tx_id);
+#else
     DNSTransaction *tx = LuaStateGetTX(luastate);
     if (tx == NULL)
         return LuaCallbackError(luastate, "internal error: no tx");
 
     lua_pushinteger(luastate, tx->tx_id);
+#endif
     return 1;
 }
 
@@ -106,15 +144,24 @@ static int DnsGetRcode(lua_State *luastate)
 {
     if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
         return LuaCallbackError(luastate, "error: protocol not dns");
-
+    uint16_t rcode = 0;
+#ifdef HAVE_RUST
+    RSDNSTransaction *tx = LuaStateGetTX(luastate);
+    if (tx == NULL) {
+        return LuaCallbackError(luastate, "internal error: no tx");
+    }
+    uint16_t flags = rs_dns_tx_get_response_flags(tx);
+    rcode = flags & 0x000f;
+#else
     DNSTransaction *tx = LuaStateGetTX(luastate);
     if (tx == NULL)
         return LuaCallbackError(luastate, "internal error: no tx");
-
-    if (tx->rcode) {
-        char rcode[16] = "";
-        DNSCreateRcodeString(tx->rcode, rcode, sizeof(rcode));
-        return LuaPushStringBuffer(luastate, (const uint8_t *)rcode, strlen(rcode));
+    rcode = tx->rcode;
+#endif
+    if (rcode) {
+        char rcode_str[16] = "";
+        DNSCreateRcodeString(rcode, rcode_str, sizeof(rcode_str));
+        return LuaPushStringBuffer(luastate, (const uint8_t *)rcode_str, strlen(rcode_str));
     } else {
         return 0;
     }
@@ -124,17 +171,77 @@ static int DnsGetRecursionDesired(lua_State *luastate)
 {
     if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
         return LuaCallbackError(luastate, "error: protocol not dns");
+#ifdef HAVE_RUST
+    RSDNSTransaction *tx = LuaStateGetTX(luastate);
+    if (tx == NULL)
+        return LuaCallbackError(luastate, "internal error: no tx");
+
+    uint16_t flags = rs_dns_tx_get_response_flags(tx);
+    int recursion_desired = flags & 0x0080 ? 1 : 0;
 
+    lua_pushboolean(luastate, recursion_desired);
+#else
     DNSTransaction *tx = LuaStateGetTX(luastate);
     if (tx == NULL)
         return LuaCallbackError(luastate, "internal error: no tx");
 
     lua_pushboolean(luastate, tx->recursion_desired);
+#endif
     return 1;
 }
 
 static int DnsGetQueryTable(lua_State *luastate)
 {
+#ifdef HAVE_RUST
+    if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
+        return LuaCallbackError(luastate, "error: protocol not dns");
+
+    RSDNSTransaction *tx = LuaStateGetTX(luastate);
+    if (tx == NULL)
+        return LuaCallbackError(luastate, "internal error: no tx");
+
+    lua_newtable(luastate);
+
+    uint8_t *name;
+    uint32_t name_len;
+
+    for (uint16_t i = 0;; i++) {
+
+        if (!rs_dns_tx_get_query_name(tx, i, &name, &name_len)) {
+            break;
+        }
+
+        lua_pushinteger(luastate, i);
+        lua_newtable(luastate);
+
+        uint16_t rrtype;
+        if (rs_dns_tx_get_query_rrtype(tx, i, &rrtype)) {
+            char s_rrtype[16] = "";
+            DNSCreateTypeString(rrtype, s_rrtype, sizeof(s_rrtype));
+            lua_pushstring(luastate, "type");
+            lua_pushstring(luastate, s_rrtype);
+            lua_settable(luastate, -3);
+        }
+
+        char *s = BytesToString(name, name_len);
+        if (s != NULL) {
+            size_t slen = strlen(s);
+            if (slen > name_len * 2) {
+                SCFree(s);
+                return LuaCallbackError(luastate, "invalid length");
+            }
+            lua_pushstring(luastate, "rrname");
+            LuaPushStringBuffer(luastate, (uint8_t *)s, slen);
+            lua_settable(luastate, -3);
+            SCFree(s);
+        }
+
+        lua_pushinteger(luastate, i++);
+
+    }
+
+    return 1;
+#else
     if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
         return LuaCallbackError(luastate, "error: protocol not dns");
 
@@ -178,10 +285,15 @@ static int DnsGetQueryTable(lua_State *luastate)
     }
 
     return 1;
+#endif
 }
 
 static int DnsGetAnswerTable(lua_State *luastate)
 {
+#ifdef HAVE_RUST
+    SCLogNotice("DnsGetAnswerTable not implemented for Rust DNS.");
+    return 1;
+#else
     if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
         return LuaCallbackError(luastate, "error: protocol not dns");
 
@@ -242,10 +354,15 @@ static int DnsGetAnswerTable(lua_State *luastate)
     }
 
     return 1;
+#endif
 }
 
 static int DnsGetAuthorityTable(lua_State *luastate)
 {
+#ifdef HAVE_RUST
+    SCLogNotice("DnsGetAuthorityTable not implemented for Rust DNS");
+    return 1;
+#else
     if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
         return LuaCallbackError(luastate, "error: protocol not dns");
 
@@ -293,6 +410,7 @@ static int DnsGetAuthorityTable(lua_State *luastate)
     }
 
     return 1;
+#endif
 }