]> git.ipfire.org Git - people/ms/suricata.git/blobdiff - rust/src/dns/dns.rs
rust/app-layer: provide generic implementation of iterator
[people/ms/suricata.git] / rust / src / dns / dns.rs
index f08ad4f0e811cb878386de6a61a7cbc141fe6600..16b67cfa9c1e357ab8a27a7194353aa03ce90863 100644 (file)
  * 02110-1301, USA.
  */
 
-extern crate libc;
 extern crate nom;
 
 use std;
-use std::mem::transmute;
+use std::ffi::CString;
+use std::collections::HashMap;
+use std::collections::VecDeque;
 
-use log::*;
-use core;
-use dns::parser;
+use crate::applayer::*;
+use crate::core::{self, *};
+use crate::dns::parser;
+
+use nom7::{Err, IResult};
+use nom7::number::streaming::be_u16;
 
 /// 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;
+pub const DNS_RECORD_TYPE_A           : u16 = 1;
+pub const DNS_RECORD_TYPE_NS          : u16 = 2;
+pub const DNS_RECORD_TYPE_MD          : u16 = 3;   // Obsolete
+pub const DNS_RECORD_TYPE_MF          : u16 = 4;   // Obsolete
+pub const DNS_RECORD_TYPE_CNAME       : u16 = 5;
+pub const DNS_RECORD_TYPE_SOA         : u16 = 6;
+pub const DNS_RECORD_TYPE_MB          : u16 = 7;   // Experimental
+pub const DNS_RECORD_TYPE_MG          : u16 = 8;   // Experimental
+pub const DNS_RECORD_TYPE_MR          : u16 = 9;   // Experimental
+pub const DNS_RECORD_TYPE_NULL        : u16 = 10;  // Experimental
+pub const DNS_RECORD_TYPE_WKS         : u16 = 11;
+pub const DNS_RECORD_TYPE_PTR         : u16 = 12;
+pub const DNS_RECORD_TYPE_HINFO       : u16 = 13;
+pub const DNS_RECORD_TYPE_MINFO       : u16 = 14;
+pub const DNS_RECORD_TYPE_MX          : u16 = 15;
+pub const DNS_RECORD_TYPE_TXT         : u16 = 16;
+pub const DNS_RECORD_TYPE_RP          : u16 = 17;
+pub const DNS_RECORD_TYPE_AFSDB       : u16 = 18;
+pub const DNS_RECORD_TYPE_X25         : u16 = 19;
+pub const DNS_RECORD_TYPE_ISDN        : u16 = 20;
+pub const DNS_RECORD_TYPE_RT          : u16 = 21;
+pub const DNS_RECORD_TYPE_NSAP        : u16 = 22;
+pub const DNS_RECORD_TYPE_NSAPPTR     : u16 = 23;
+pub const DNS_RECORD_TYPE_SIG         : u16 = 24;
+pub const DNS_RECORD_TYPE_KEY         : u16 = 25;
+pub const DNS_RECORD_TYPE_PX          : u16 = 26;
+pub const DNS_RECORD_TYPE_GPOS        : u16 = 27;
+pub const DNS_RECORD_TYPE_AAAA        : u16 = 28;
+pub const DNS_RECORD_TYPE_LOC         : u16 = 29;
+pub const DNS_RECORD_TYPE_NXT         : u16 = 30;  // Obsolete
+pub const DNS_RECORD_TYPE_SRV         : u16 = 33;
+pub const DNS_RECORD_TYPE_ATMA        : u16 = 34;
+pub const DNS_RECORD_TYPE_NAPTR       : u16 = 35;
+pub const DNS_RECORD_TYPE_KX          : u16 = 36;
+pub const DNS_RECORD_TYPE_CERT        : u16 = 37;
+pub const DNS_RECORD_TYPE_A6          : u16 = 38;  // Obsolete
+pub const DNS_RECORD_TYPE_DNAME       : u16 = 39;
+pub const DNS_RECORD_TYPE_OPT         : u16 = 41;
+pub const DNS_RECORD_TYPE_APL         : u16 = 42;
+pub const DNS_RECORD_TYPE_DS          : u16 = 43;
+pub const DNS_RECORD_TYPE_SSHFP       : u16 = 44;
+pub const DNS_RECORD_TYPE_IPSECKEY    : u16 = 45;
+pub const DNS_RECORD_TYPE_RRSIG       : u16 = 46;
+pub const DNS_RECORD_TYPE_NSEC        : u16 = 47;
+pub const DNS_RECORD_TYPE_DNSKEY      : u16 = 48;
+pub const DNS_RECORD_TYPE_DHCID       : u16 = 49;
+pub const DNS_RECORD_TYPE_NSEC3       : u16 = 50;
+pub const DNS_RECORD_TYPE_NSEC3PARAM  : u16 = 51;
+pub const DNS_RECORD_TYPE_TLSA        : u16 = 52;
+pub const DNS_RECORD_TYPE_HIP         : u16 = 55;
+pub const DNS_RECORD_TYPE_CDS         : u16 = 59;
+pub const DNS_RECORD_TYPE_CDNSKEY     : u16 = 60;
+pub const DNS_RECORD_TYPE_SPF         : u16 = 99;  // Obsolete
+pub const DNS_RECORD_TYPE_TKEY        : u16 = 249;
+pub const DNS_RECORD_TYPE_TSIG        : u16 = 250;
+pub const DNS_RECORD_TYPE_MAILA       : u16 = 254; // Obsolete
+pub const DNS_RECORD_TYPE_ANY         : u16 = 255;
+pub const DNS_RECORD_TYPE_URI         : u16 = 256;
 
 /// DNS error codes.
 pub const DNS_RCODE_NOERROR:  u16 = 0;
 pub const DNS_RCODE_FORMERR:  u16 = 1;
+pub const DNS_RCODE_SERVFAIL: u16 = 2;
 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 const DNS_RCODE_NOTIMP:   u16 = 4;
+pub const DNS_RCODE_REFUSED:  u16 = 5;
+pub const DNS_RCODE_YXDOMAIN: u16 = 6;
+pub const DNS_RCODE_YXRRSET:  u16 = 7;
+pub const DNS_RCODE_NXRRSET:  u16 = 8;
+pub const DNS_RCODE_NOTAUTH:  u16 = 9;
+pub const DNS_RCODE_NOTZONE:  u16 = 10;
+// Support for OPT RR from RFC6891 will be needed to
+// parse RCODE values over 15
+pub const DNS_RCODE_BADVERS:  u16 = 16;
+pub const DNS_RCODE_BADSIG:   u16 = 16;
+pub const DNS_RCODE_BADKEY:   u16 = 17;
+pub const DNS_RCODE_BADTIME:  u16 = 18;
+pub const DNS_RCODE_BADMODE:  u16 = 19;
+pub const DNS_RCODE_BADNAME:  u16 = 20;
+pub const DNS_RCODE_BADALG:   u16 = 21;
+pub const DNS_RCODE_BADTRUNC: u16 = 22;
+
+
+static mut ALPROTO_DNS: AppProto = ALPROTO_UNKNOWN;
+
+#[derive(Debug, PartialEq, AppLayerEvent)]
 pub enum DNSEvent {
-    UnsolicitedResponse = 0,
     MalformedData,
     NotRequest,
     NotResponse,
     ZFlagSet,
-    Flooded,
-    StateMemCapReached,
 }
 
 #[derive(Debug,PartialEq)]
+#[repr(C)]
 pub struct DNSHeader {
     pub tx_id: u16,
     pub flags: u16,
@@ -81,16 +141,66 @@ pub struct DNSQueryEntry {
     pub rrclass: u16,
 }
 
-impl DNSQueryEntry {
+#[derive(Debug,PartialEq)]
+pub struct DNSRDataSOA {
+    /// Primary name server for this zone
+    pub mname: Vec<u8>,
+    /// Authority's mailbox
+    pub rname: Vec<u8>,
+    /// Serial version number
+    pub serial: u32,
+    /// Refresh interval (seconds)
+    pub refresh: u32,
+    /// Retry interval (seconds)
+    pub retry: u32,
+    /// Upper time limit until zone is no longer authoritative (seconds)
+    pub expire: u32,
+    /// Minimum ttl for records in this zone (seconds)
+    pub minimum: u32,
+}
 
-    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 DNSRDataSSHFP {
+    /// Algorithm number
+    pub algo: u8,
+    /// Fingerprint type
+    pub fp_type: u8,
+    /// Fingerprint
+    pub fingerprint: Vec<u8>,
+}
+
+#[derive(Debug,PartialEq)]
+pub struct DNSRDataSRV {
+    /// Priority
+    pub priority: u16,
+    /// Weight
+    pub weight: u16,
+    /// Port
+    pub port: u16,
+    /// Target
+    pub target: Vec<u8>,
+}
 
+/// Represents RData of various formats
+#[derive(Debug,PartialEq)]
+pub enum DNSRData {
+    // RData is an address
+    A(Vec<u8>),
+    AAAA(Vec<u8>),
+    // RData is a domain name
+    CNAME(Vec<u8>),
+    PTR(Vec<u8>),
+    MX(Vec<u8>),
+    NS(Vec<u8>),
+    // RData is text
+    TXT(Vec<u8>),
+    NULL(Vec<u8>),
+    // RData has several fields
+    SOA(DNSRDataSOA),
+    SRV(DNSRDataSRV),
+    SSHFP(DNSRDataSSHFP),
+    // RData for remaining types is sometimes ignored
+    Unknown(Vec<u8>),
 }
 
 #[derive(Debug,PartialEq)]
@@ -99,28 +209,7 @@ pub struct DNSAnswerEntry {
     pub rrtype: u16,
     pub rrclass: u16,
     pub ttl: u32,
-    pub data_len: u16,
-    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();
-    }
-
+    pub data: DNSRData,
 }
 
 #[derive(Debug)]
@@ -142,36 +231,48 @@ 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,
+    pub tx_data: AppLayerTxData,
+}
+
+impl Transaction for DNSTransaction {
+    fn id(&self) -> u64 {
+        self.id
+    }
 }
 
 impl DNSTransaction {
 
-    pub fn new() -> DNSTransaction {
-        return DNSTransaction{
+    pub fn new() -> Self {
+        return Self {
             id: 0,
             request: None,
             response: None,
-            logged: 0,
             de_state: None,
             events: std::ptr::null_mut(),
+            tx_data: AppLayerTxData::new(),
         }
     }
 
     pub fn free(&mut self) {
-        if self.events != std::ptr::null_mut() {
+        if !self.events.is_null() {
             core::sc_app_layer_decoder_events_free_events(&mut self.events);
         }
+        match self.de_state {
+            Some(state) => {
+                core::sc_detect_engine_state_free(state);
+            }
+            None => { },
+        }
     }
 
     /// Get the DNS transactions ID (not the internal tracking ID).
     pub fn tx_id(&self) -> u16 {
-        for request in &self.request {
+        if let &Some(ref request) = &self.request {
             return request.header.tx_id;
         }
-        for response in &self.response {
+        if let &Some(ref response) = &self.response {
             return response.header.tx_id;
         }
 
@@ -182,7 +283,7 @@ impl DNSTransaction {
     /// 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 {
+        if let &Some(ref response) = &self.response {
             return response.header.flags & 0x000f;
         }
         return 0;
@@ -196,6 +297,37 @@ impl Drop for DNSTransaction {
     }
 }
 
+struct ConfigTracker {
+    map: HashMap<u16, AppLayerTxConfig>,
+    queue: VecDeque<u16>,
+}
+
+impl ConfigTracker {
+    fn new() -> ConfigTracker {
+        ConfigTracker {
+            map: HashMap::new(),
+            queue: VecDeque::new(),
+        }
+    }
+
+    fn add(&mut self, id: u16, config: AppLayerTxConfig) {
+        // If at size limit, remove the oldest entry.
+        if self.queue.len() > 499 {
+            if let Some(id) = self.queue.pop_front() {
+                self.map.remove(&id);
+            }
+        }
+
+        self.map.insert(id, config);
+        self.queue.push_back(id);
+    }
+
+    fn remove(&mut self, id: &u16) -> Option<AppLayerTxConfig> {
+        self.map.remove(id)
+    }
+}
+
+#[derive(Default)]
 pub struct DNSState {
     // Internal transaction ID.
     pub tx_id: u64,
@@ -203,47 +335,27 @@ pub struct DNSState {
     // Transactions.
     pub transactions: Vec<DNSTransaction>,
 
-    pub de_state_count: u64,
-
     pub events: u16,
 
-    pub request_buffer: Vec<u8>,
-    pub response_buffer: Vec<u8>,
-}
+    config: Option<ConfigTracker>,
 
-impl DNSState {
+    gap: bool,
+}
 
-    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(),
-        };
+impl State<DNSTransaction> for DNSState {
+    fn get_transactions(&self) -> &[DNSTransaction] {
+        &self.transactions
     }
+}
 
-    /// 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),
-        };
+impl DNSState {
+
+    pub fn new() -> Self {
+            Default::default()
     }
 
-    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_tcp() -> Self {
+            Default::default()
     }
 
     pub fn new_tx(&mut self) -> DNSTransaction {
@@ -254,7 +366,6 @@ impl DNSState {
     }
 
     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;
@@ -267,41 +378,12 @@ impl DNSState {
             }
         }
         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);
+            self.transactions.remove(index);
         }
     }
 
     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);
@@ -319,7 +401,7 @@ impl DNSState {
             return;
         }
 
-        let mut tx = &mut self.transactions[len - 1];
+        let tx = &mut self.transactions[len - 1];
         core::sc_app_layer_decoder_events_set_event_raw(&mut tx.events,
                                                         event as u8);
         self.events += 1;
@@ -327,7 +409,7 @@ impl DNSState {
 
     pub fn parse_request(&mut self, input: &[u8]) -> bool {
         match parser::dns_parse_request(input) {
-            nom::IResult::Done(_, request) => {
+            Ok((_, request)) => {
                 if request.header.flags & 0x8000 != 0 {
                     SCLogDebug!("DNS message is not a request");
                     self.set_event(DNSEvent::NotRequest);
@@ -345,13 +427,13 @@ impl DNSState {
                 self.transactions.push(tx);
                 return true;
             }
-            nom::IResult::Incomplete(_) => {
+            Err(Err::Incomplete(_)) => {
                 // Insufficient data.
                 SCLogDebug!("Insufficient data while parsing DNS request");
                 self.set_event(DNSEvent::MalformedData);
                 return false;
             }
-            nom::IResult::Error(_) => {
+            Err(_) => {
                 // Error, probably malformed data.
                 SCLogDebug!("An error occurred while parsing DNS request");
                 self.set_event(DNSEvent::MalformedData);
@@ -362,7 +444,7 @@ impl DNSState {
 
     pub fn parse_response(&mut self, input: &[u8]) -> bool {
         match parser::dns_parse_response(input) {
-            nom::IResult::Done(_, response) => {
+            Ok((_, response)) => {
 
                 SCLogDebug!("Response header flags: {}", response.header.flags);
 
@@ -378,17 +460,22 @@ impl DNSState {
                 }
 
                 let mut tx = self.new_tx();
+                if let Some(ref mut config) = &mut self.config {
+                    if let Some(config) = config.remove(&response.header.tx_id) {
+                        tx.tx_data.config = config;
+                    }
+                }
                 tx.response = Some(response);
                 self.transactions.push(tx);
                 return true;
             }
-            nom::IResult::Incomplete(_) => {
+            Err(Err::Incomplete(_)) => {
                 // Insufficient data.
                 SCLogDebug!("Insufficient data while parsing DNS response");
                 self.set_event(DNSEvent::MalformedData);
                 return false;
             }
-            nom::IResult::Error(_) => {
+            Err(_) => {
                 // Error, probably malformed data.
                 SCLogDebug!("An error occurred while parsing DNS response");
                 self.set_event(DNSEvent::MalformedData);
@@ -398,171 +485,285 @@ impl DNSState {
     }
 
     /// TCP variation of response request parser to handle the length
-    /// prefix as well as buffering.
+    /// prefix.
     ///
-    /// 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();
+    /// Returns the number of messages parsed.
+    pub fn parse_request_tcp(&mut self, input: &[u8]) -> AppLayerResult {
+        if self.gap {
+            let (is_dns, _, is_incomplete) = probe_tcp(input);
+            if is_dns || is_incomplete {
+                self.gap = false;
+            } else {
+                AppLayerResult::ok();
+            }
+        }
+
+        let mut cur_i = input;
+        let mut consumed = 0;
+        while cur_i.len() > 0 {
+            if cur_i.len() == 1 {
+                return AppLayerResult::incomplete(consumed as u32, 2 as u32);
+            }
+            let size = match be_u16(cur_i) as IResult<&[u8],u16> {
+                Ok((_, len)) => len,
+                _ => 0
+            } as usize;
+            SCLogDebug!("[request] Have {} bytes, need {} to parse",
+                        cur_i.len(), size + 2);
+            if size > 0 && cur_i.len() >= size + 2 {
+                let msg = &cur_i[0..(size + 2)];
                 if self.parse_request(&msg[2..]) {
-                    continue;
+                    cur_i = &cur_i[(size + 2)..];
+                    consumed += size  + 2;
+                } else {
+                    return AppLayerResult::err();
                 }
-                return -1;
+            } else if size == 0 {
+                cur_i = &cur_i[2..];
+                consumed += 2;
+            } else {
+                SCLogDebug!("[request]Not enough DNS traffic to parse. Returning {}/{}",
+                            consumed as u32, (size + 2) as u32);
+                return AppLayerResult::incomplete(consumed as u32,
+                    (size  + 2) as u32);
             }
-            SCLogDebug!("Not enough DNS traffic to parse.");
-            return 0;
         }
-        return 0;
+        AppLayerResult::ok()
     }
 
     /// TCP variation of the response parser to handle the length
-    /// prefix as well as buffering.
+    /// prefix.
     ///
-    /// 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
+    /// Returns the number of messages parsed.
+    pub fn parse_response_tcp(&mut self, input: &[u8]) -> AppLayerResult {
+        if self.gap {
+            let (is_dns, _, is_incomplete) = probe_tcp(input);
+            if is_dns || is_incomplete {
+                self.gap = false;
+            } else {
+                return AppLayerResult::ok();
             }
-            _ => 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;
+        }
+
+        let mut cur_i = input;
+        let mut consumed = 0;
+        while cur_i.len() > 0 {
+            if cur_i.len() == 1 {
+                return AppLayerResult::incomplete(consumed as u32, 2 as u32);
+            }
+            let size = match be_u16(cur_i) as IResult<&[u8],u16> {
+                Ok((_, len)) => len,
+                _ => 0
+            } as usize;
+            SCLogDebug!("[response] Have {} bytes, need {} to parse",
+                        cur_i.len(), size + 2);
+            if size > 0 && cur_i.len() >= size + 2 {
+                let msg = &cur_i[0..(size + 2)];
+                if self.parse_response(&msg[2..]) {
+                    cur_i = &cur_i[(size + 2)..];
+                    consumed += size + 2;
+                } else {
+                    return AppLayerResult::err();
+                }
+            } else if size == 0 {
+                cur_i = &cur_i[2..];
+                consumed += 2;
+            } else  {
+                SCLogDebug!("[response]Not enough DNS traffic to parse. Returning {}/{}",
+                    consumed as u32, (cur_i.len() - consumed) as u32);
+                return AppLayerResult::incomplete(consumed as u32,
+                    (size + 2) as u32);
             }
-            return -1;
         }
-        0
+        AppLayerResult::ok()
+    }
+
+    /// A gap has been seen in the request direction. Set the gap flag.
+    pub fn request_gap(&mut self, gap: u32) {
+        if gap > 0 {
+            self.gap = true;
+        }
+    }
+
+    /// A gap has been seen in the response direction. Set the gap
+    /// flag.
+    pub fn response_gap(&mut self, gap: u32) {
+        if gap > 0 {
+            self.gap = true;
+        }
     }
 }
 
-/// Implement Drop for DNSState as transactions need to do some
-/// explicit cleanup.
-impl Drop for DNSState {
-    fn drop(&mut self) {
-        self.free();
+const DNS_HEADER_SIZE: usize = 12;
+
+fn probe_header_validity(header: DNSHeader, rlen: usize) -> (bool, bool, bool) {
+    let opcode = ((header.flags >> 11) & 0xf) as u8;
+    if opcode >= 7 {
+        //unassigned opcode
+        return (false, false, false);
+    }
+    if 2 * (header.additional_rr as usize
+        + header.answer_rr as usize
+        + header.authority_rr as usize
+        + header.questions as usize)
+        + DNS_HEADER_SIZE
+        > rlen
+    {
+        //not enough data for such a DNS record
+        return (false, false, false);
     }
+    let is_request = header.flags & 0x8000 == 0;
+    return (true, is_request, false);
+}
+
+/// Probe input to see if it looks like DNS.
+fn probe(input: &[u8], dlen: usize) -> (bool, bool, bool) {
+    let i2 = if input.len() <= dlen { input } else { &input[..dlen] };
+    match parser::dns_parse_request(i2) {
+        Ok((_, request)) => {
+            return probe_header_validity(request.header, dlen);
+        },
+        Err(Err::Incomplete(_)) => {
+            match parser::dns_parse_header(input) {
+                Ok((_, header)) => {
+                    return probe_header_validity(header, dlen);
+                }
+                Err(Err::Incomplete(_)) => (false, false, true),
+                Err(_) => (false, false, false),
+            }
+        }
+        Err(_) => (false, false, false),
+    }
+}
+
+/// Probe TCP input to see if it looks like DNS.
+pub fn probe_tcp(input: &[u8]) -> (bool, bool, bool) {
+    match be_u16(input) as IResult<&[u8],u16> {
+        Ok((rem, dlen)) => {
+            return probe(rem, dlen as usize);
+        },
+        Err(Err::Incomplete(_)) => {
+            return (false, false, true);
+        }
+        _ => {}
+    }
+    return (false, false, false);
 }
 
 /// Returns *mut DNSState
 #[no_mangle]
-pub extern "C" fn rs_dns_state_new() -> *mut libc::c_void {
+pub extern "C" fn rs_dns_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void {
     let state = DNSState::new();
     let boxed = Box::new(state);
-    return unsafe{transmute(boxed)};
+    return Box::into_raw(boxed) as *mut _;
 }
 
 /// Returns *mut DNSState
 #[no_mangle]
-pub extern "C" fn rs_dns_state_tcp_new() -> *mut libc::c_void {
+pub extern "C" fn rs_dns_state_tcp_new() -> *mut std::os::raw::c_void {
     let state = DNSState::new_tcp();
     let boxed = Box::new(state);
-    return unsafe{transmute(boxed)};
+    return Box::into_raw(boxed) as *mut _;
 }
 
 /// Params:
 /// - state: *mut DNSState as void pointer
 #[no_mangle]
-pub extern "C" fn rs_dns_state_free(state: *mut libc::c_void) {
+pub extern "C" fn rs_dns_state_free(state: *mut std::os::raw::c_void) {
     // Just unbox...
-    let _drop: Box<DNSState> = unsafe{transmute(state)};
+    std::mem::drop(unsafe { Box::from_raw(state as *mut DNSState) });
 }
 
 #[no_mangle]
-pub extern "C" fn rs_dns_state_tx_free(state: &mut DNSState,
-                                       tx_id: libc::uint64_t)
+pub unsafe extern "C" fn rs_dns_state_tx_free(state: *mut std::os::raw::c_void,
+                                       tx_id: u64)
 {
+    let state = cast_pointer!(state, DNSState);
     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)};
+pub unsafe extern "C" fn rs_dns_parse_request(_flow: *const core::Flow,
+                                        state: *mut std::os::raw::c_void,
+                                       _pstate: *mut std::os::raw::c_void,
+                                       input: *const u8,
+                                       input_len: u32,
+                                       _data: *const std::os::raw::c_void,
+                                       _flags: u8)
+                                       -> AppLayerResult {
+    let state = cast_pointer!(state, DNSState);
+    let buf = std::slice::from_raw_parts(input, input_len as usize);
     if state.parse_request(buf) {
-        1
+        AppLayerResult::ok()
     } else {
-        -1
+        AppLayerResult::err()
     }
 }
 
 #[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)};
+pub unsafe extern "C" fn rs_dns_parse_response(_flow: *const core::Flow,
+                                        state: *mut std::os::raw::c_void,
+                                        _pstate: *mut std::os::raw::c_void,
+                                        input: *const u8,
+                                        input_len: u32,
+                                        _data: *const std::os::raw::c_void,
+                                        _flags: u8)
+                                        -> AppLayerResult {
+    let state = cast_pointer!(state, DNSState);
+    let buf = std::slice::from_raw_parts(input, input_len as usize);
     if state.parse_response(buf) {
-        1
+        AppLayerResult::ok()
     } else {
-        -1
+        AppLayerResult::err()
     }
 }
 
 /// 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);
+pub unsafe extern "C" fn rs_dns_parse_request_tcp(_flow: *const core::Flow,
+                                           state: *mut std::os::raw::c_void,
+                                           _pstate: *mut std::os::raw::c_void,
+                                           input: *const u8,
+                                           input_len: u32,
+                                           _data: *const std::os::raw::c_void,
+                                           _flags: u8)
+                                           -> AppLayerResult {
+    let state = cast_pointer!(state, DNSState);
+    if input_len > 0 {
+        if !input.is_null() {
+            let buf = std::slice::from_raw_parts(input, input_len as usize);
+            return state.parse_request_tcp(buf);
+        }
+        state.request_gap(input_len);
+    }
+    AppLayerResult::ok()
 }
 
 #[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;
+pub unsafe extern "C" fn rs_dns_parse_response_tcp(_flow: *const core::Flow,
+                                            state: *mut std::os::raw::c_void,
+                                            _pstate: *mut std::os::raw::c_void,
+                                            input: *const u8,
+                                            input_len: u32,
+                                            _data: *const std::os::raw::c_void,
+                                            _flags: u8)
+                                            -> AppLayerResult {
+    let state = cast_pointer!(state, DNSState);
+    if input_len > 0 {
+        if !input.is_null() {
+            let buf = std::slice::from_raw_parts(input, input_len as usize);
+            return state.parse_response_tcp(buf);
+        }
+        state.response_gap(input_len);
+    }
+    AppLayerResult::ok()
 }
 
 #[no_mangle]
-pub extern "C" fn rs_dns_tx_get_alstate_progress(_tx: &mut DNSTransaction,
-                                                 _direction: libc::uint8_t)
-                                                 -> libc::uint8_t
+pub extern "C" fn rs_dns_tx_get_alstate_progress(_tx: *mut std::os::raw::c_void,
+                                                 _direction: u8)
+                                                 -> std::os::raw::c_int
 {
     // This is a stateless parser, just the existence of a transaction
     // means its complete.
@@ -571,41 +772,23 @@ pub extern "C" fn rs_dns_tx_get_alstate_progress(_tx: &mut DNSTransaction,
 }
 
 #[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
+pub unsafe extern "C" fn rs_dns_state_get_tx_count(state: *mut std::os::raw::c_void)
+                                            -> u64
 {
+    let state = cast_pointer!(state, DNSState);
     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
+pub unsafe extern "C" fn rs_dns_state_get_tx(state: *mut std::os::raw::c_void,
+                                      tx_id: u64)
+                                      -> *mut std::os::raw::c_void
 {
+    let state = cast_pointer!(state, DNSState);
     match state.get_tx(tx_id) {
         Some(tx) => {
-            return unsafe{transmute(tx)};
+            return tx as *const _ as *mut _;
         }
         None => {
             return std::ptr::null_mut();
@@ -614,29 +797,31 @@ pub extern "C" fn rs_dns_state_get_tx(state: &mut DNSState,
 }
 
 #[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;
+pub extern "C" fn rs_dns_tx_is_request(tx: &mut DNSTransaction) -> bool {
+    tx.request.is_some()
 }
 
 #[no_mangle]
-pub extern "C" fn rs_dns_state_set_tx_detect_state(
-    state: &mut DNSState,
-    tx: &mut DNSTransaction,
-    de_state: &mut core::DetectEngineState)
+pub extern "C" fn rs_dns_tx_is_response(tx: &mut DNSTransaction) -> bool {
+    tx.response.is_some()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_dns_state_set_tx_detect_state(
+    tx: *mut std::os::raw::c_void,
+    de_state: &mut core::DetectEngineState) -> std::os::raw::c_int
 {
-    state.de_state_count += 1;
+    let tx = cast_pointer!(tx, DNSTransaction);
     tx.de_state = Some(de_state);
+    return 0;
 }
 
 #[no_mangle]
-pub extern "C" fn rs_dns_state_get_tx_detect_state(
-    tx: &mut DNSTransaction)
+pub unsafe extern "C" fn rs_dns_state_get_tx_detect_state(
+    tx: *mut std::os::raw::c_void)
     -> *mut core::DetectEngineState
 {
+    let tx = cast_pointer!(tx, DNSTransaction);
     match tx.de_state {
         Some(ds) => {
             return ds;
@@ -648,43 +833,35 @@ pub extern "C" fn rs_dns_state_get_tx_detect_state(
 }
 
 #[no_mangle]
-pub extern "C" fn rs_dns_state_has_events(state: &mut DNSState) -> u8 {
-    if state.events > 0 {
-        return 1;
-    }
-    return 0;
+pub unsafe extern "C" fn rs_dns_state_get_events(tx: *mut std::os::raw::c_void)
+                                          -> *mut core::AppLayerDecoderEvents
+{
+    let tx = cast_pointer!(tx, DNSTransaction);
+    return tx.events;
 }
 
 #[no_mangle]
-pub extern "C" fn rs_dns_state_get_events(state: &mut DNSState,
-                                          tx_id: libc::uint64_t)
-                                          -> *mut core::AppLayerDecoderEvents
+pub unsafe extern "C" fn rs_dns_state_get_tx_data(
+    tx: *mut std::os::raw::c_void)
+    -> *mut AppLayerTxData
 {
-    match state.get_tx(tx_id) {
-        Some(tx) => {
-            return tx.events;
-        }
-        _ => {
-            return std::ptr::null_mut();
-        }
-    }
+    let tx = cast_pointer!(tx, DNSTransaction);
+    return &mut tx.tx_data;
 }
 
 #[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
+pub unsafe extern "C" fn rs_dns_tx_get_query_name(tx: &mut DNSTransaction,
+                                       i: u32,
+                                       buf: *mut *const u8,
+                                       len: *mut u32)
+                                       -> u8
 {
-    for request in &tx.request {
+    if let &Some(ref request) = &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();
-                }
+                *len = query.name.len() as u32;
+                *buf = query.name.as_ptr();
                 return 1;
             }
         }
@@ -696,7 +873,7 @@ pub extern "C" fn rs_dns_tx_get_query_name(tx: &mut DNSTransaction,
 //
 /// 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
+pub extern "C" fn rs_dns_tx_get_tx_id(tx: &mut DNSTransaction) -> u16
 {
     return tx.tx_id()
 }
@@ -706,24 +883,22 @@ pub extern "C" fn rs_dns_tx_get_tx_id(tx: &mut DNSTransaction) -> libc::uint16_t
 /// 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
+                                           -> u16
 {
     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
+pub unsafe extern "C" fn rs_dns_tx_get_query_rrtype(tx: &mut DNSTransaction,
+                                         i: u16,
+                                         rrtype: *mut u16)
+                                         -> u8
 {
-    for request in &tx.request {
+    if let &Some(ref request) = &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;
-                }
+                *rrtype = query.rrtype;
                 return 1;
             }
         }
@@ -732,70 +907,589 @@ pub extern "C" fn rs_dns_tx_get_query_rrtype(tx: &mut DNSTransaction,
 }
 
 #[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;
+pub unsafe extern "C" fn rs_dns_probe(
+    _flow: *const core::Flow,
+    _dir: u8,
+    input: *const u8,
+    len: u32,
+    rdir: *mut u8,
+) -> AppProto {
+    if len == 0 || len < std::mem::size_of::<DNSHeader>() as u32 {
+        return core::ALPROTO_UNKNOWN;
+    }
+    let slice: &[u8] = std::slice::from_raw_parts(input as *mut u8, len as usize);
+    let (is_dns, is_request, _) = probe(slice, slice.len());
+    if is_dns {
+        let dir = if is_request {
+            Direction::ToServer
+        } else {
+            Direction::ToClient
+        };
+        *rdir = dir as u8;
+        return ALPROTO_DNS;
+    }
+    return 0;
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_dns_probe_tcp(
+    _flow: *const core::Flow,
+    direction: u8,
+    input: *const u8,
+    len: u32,
+    rdir: *mut u8
+) -> AppProto {
+    if len == 0 || len < std::mem::size_of::<DNSHeader>() as u32 + 2 {
+        return core::ALPROTO_UNKNOWN;
+    }
+    let slice: &[u8] = std::slice::from_raw_parts(input as *mut u8, len as usize);
+    //is_incomplete is checked by caller
+    let (is_dns, is_request, _) = probe_tcp(slice);
+    if is_dns {
+        let dir = if is_request {
+            Direction::ToServer
+        } else {
+            Direction::ToClient
+        };
+        if (direction & DIR_BOTH) != dir.into() {
+            *rdir = dir as u8;
         }
-        _ => {
-            return 0;
+        return ALPROTO_DNS;
+    }
+    return 0;
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_dns_apply_tx_config(
+    _state: *mut std::os::raw::c_void, _tx: *mut std::os::raw::c_void,
+    _mode: std::os::raw::c_int, config: AppLayerTxConfig
+) {
+    let tx = cast_pointer!(_tx, DNSTransaction);
+    let state = cast_pointer!(_state, DNSState);
+    if let Some(request) = &tx.request {
+        if state.config.is_none() {
+            state.config = Some(ConfigTracker::new());
+        }
+        if let Some(ref mut tracker) = &mut state.config {
+            tracker.add(request.header.tx_id, config);
         }
     }
 }
 
 #[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)
+pub unsafe extern "C" fn rs_dns_udp_register_parser() {
+    let default_port = std::ffi::CString::new("[53]").unwrap();
+    let parser = RustParser{
+        name: b"dns\0".as_ptr() as *const std::os::raw::c_char,
+        default_port: default_port.as_ptr(),
+        ipproto: IPPROTO_UDP,
+        probe_ts: Some(rs_dns_probe),
+        probe_tc: Some(rs_dns_probe),
+        min_depth: 0,
+        max_depth: std::mem::size_of::<DNSHeader>() as u16,
+        state_new: rs_dns_state_new,
+        state_free: rs_dns_state_free,
+        tx_free: rs_dns_state_tx_free,
+        parse_ts: rs_dns_parse_request,
+        parse_tc: rs_dns_parse_response,
+        get_tx_count: rs_dns_state_get_tx_count,
+        get_tx: rs_dns_state_get_tx,
+        tx_comp_st_ts: 1,
+        tx_comp_st_tc: 1,
+        tx_get_progress: rs_dns_tx_get_alstate_progress,
+        get_events: Some(rs_dns_state_get_events),
+        get_eventinfo: Some(DNSEvent::get_event_info),
+        get_eventinfo_byid: Some(DNSEvent::get_event_info_by_id),
+        localstorage_new: None,
+        localstorage_free: None,
+        get_files: None,
+        get_tx_iterator: Some(crate::applayer::state_get_tx_iterator::<DNSState, DNSTransaction>),
+        get_de_state: rs_dns_state_get_tx_detect_state,
+        set_de_state: rs_dns_state_set_tx_detect_state,
+        get_tx_data: rs_dns_state_get_tx_data,
+        apply_tx_config: Some(rs_dns_apply_tx_config),
+        flags: APP_LAYER_PARSER_OPT_UNIDIR_TXS,
+        truncate: None,
     };
-    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;
-                    }
-                    _ => {}
-                }
-            }
+
+    let ip_proto_str = CString::new("udp").unwrap();
+    if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
+        let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
+        ALPROTO_DNS = alproto;
+        if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
+            let _ = AppLayerRegisterParser(&parser, alproto);
+        }
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_dns_tcp_register_parser() {
+    let default_port = std::ffi::CString::new("53").unwrap();
+    let parser = RustParser{
+        name: b"dns\0".as_ptr() as *const std::os::raw::c_char,
+        default_port: default_port.as_ptr(),
+        ipproto: IPPROTO_TCP,
+        probe_ts: Some(rs_dns_probe_tcp),
+        probe_tc: Some(rs_dns_probe_tcp),
+        min_depth: 0,
+        max_depth: std::mem::size_of::<DNSHeader>() as u16 + 2,
+        state_new: rs_dns_state_new,
+        state_free: rs_dns_state_free,
+        tx_free: rs_dns_state_tx_free,
+        parse_ts: rs_dns_parse_request_tcp,
+        parse_tc: rs_dns_parse_response_tcp,
+        get_tx_count: rs_dns_state_get_tx_count,
+        get_tx: rs_dns_state_get_tx,
+        tx_comp_st_ts: 1,
+        tx_comp_st_tc: 1,
+        tx_get_progress: rs_dns_tx_get_alstate_progress,
+        get_events: Some(rs_dns_state_get_events),
+        get_eventinfo: Some(DNSEvent::get_event_info),
+        get_eventinfo_byid: Some(DNSEvent::get_event_info_by_id),
+        localstorage_new: None,
+        localstorage_free: None,
+        get_files: None,
+        get_tx_iterator: Some(crate::applayer::state_get_tx_iterator::<DNSState, DNSTransaction>),
+        get_de_state: rs_dns_state_get_tx_detect_state,
+        set_de_state: rs_dns_state_set_tx_detect_state,
+        get_tx_data: rs_dns_state_get_tx_data,
+        apply_tx_config: Some(rs_dns_apply_tx_config),
+        flags: APP_LAYER_PARSER_OPT_ACCEPT_GAPS | APP_LAYER_PARSER_OPT_UNIDIR_TXS,
+        truncate: None,
+    };
+
+    let ip_proto_str = CString::new("tcp").unwrap();
+    if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
+        let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
+        ALPROTO_DNS = alproto;
+        if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
+            let _ = AppLayerRegisterParser(&parser, alproto);
         }
-        _ => {}
     }
-    return 0;
 }
 
 #[cfg(test)]
 mod tests {
 
-    // Playing with vector draining...
+    use super::*;
+
+    #[test]
+    fn test_dns_parse_request_tcp_valid() {
+        // A UDP DNS request with the DNS payload starting at byte 42.
+        // From pcap: https://github.com/jasonish/suricata-verify/blob/7cc0e1bd0a5249b52e6e87d82d57c0b6aaf75fce/dns-udp-dig-a-www-suricata-ids-org/dig-a-www.suricata-ids.org.pcap
+        let buf: &[u8] = &[
+            0x00, 0x15, 0x17, 0x0d, 0x06, 0xf7, 0xd8, 0xcb, /* ........ */
+            0x8a, 0xed, 0xa1, 0x46, 0x08, 0x00, 0x45, 0x00, /* ...F..E. */
+            0x00, 0x4d, 0x23, 0x11, 0x00, 0x00, 0x40, 0x11, /* .M#...@. */
+            0x41, 0x64, 0x0a, 0x10, 0x01, 0x0b, 0x0a, 0x10, /* Ad...... */
+            0x01, 0x01, 0xa3, 0x4d, 0x00, 0x35, 0x00, 0x39, /* ...M.5.9 */
+            0xb2, 0xb3, 0x8d, 0x32, 0x01, 0x20, 0x00, 0x01, /* ...2. .. */
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x77, /* .......w */
+            0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63, /* ww.suric */
+            0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03, /* ata-ids. */
+            0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, /* org..... */
+            0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x00, /* ..)..... */
+            0x00, 0x00, 0x00                                /* ... */
+        ];
+
+        // The DNS payload starts at offset 42.
+        let dns_payload = &buf[42..];
+
+        // Make a TCP DNS request payload.
+        let mut request = Vec::new();
+        request.push(((dns_payload.len() as u16) >> 8) as u8);
+        request.push(((dns_payload.len() as u16) & 0xff) as u8);
+        request.extend(dns_payload);
+
+        let mut state = DNSState::new();
+        assert_eq!(
+            AppLayerResult::ok(),
+            state.parse_request_tcp(&request)
+        );
+    }
+
+    #[test]
+    fn test_dns_parse_request_tcp_short_payload() {
+        // A UDP DNS request with the DNS payload starting at byte 42.
+        // From pcap: https://github.com/jasonish/suricata-verify/blob/7cc0e1bd0a5249b52e6e87d82d57c0b6aaf75fce/dns-udp-dig-a-www-suricata-ids-org/dig-a-www.suricata-ids.org.pcap
+        let buf: &[u8] = &[
+            0x00, 0x15, 0x17, 0x0d, 0x06, 0xf7, 0xd8, 0xcb, /* ........ */
+            0x8a, 0xed, 0xa1, 0x46, 0x08, 0x00, 0x45, 0x00, /* ...F..E. */
+            0x00, 0x4d, 0x23, 0x11, 0x00, 0x00, 0x40, 0x11, /* .M#...@. */
+            0x41, 0x64, 0x0a, 0x10, 0x01, 0x0b, 0x0a, 0x10, /* Ad...... */
+            0x01, 0x01, 0xa3, 0x4d, 0x00, 0x35, 0x00, 0x39, /* ...M.5.9 */
+            0xb2, 0xb3, 0x8d, 0x32, 0x01, 0x20, 0x00, 0x01, /* ...2. .. */
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x77, /* .......w */
+            0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63, /* ww.suric */
+            0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03, /* ata-ids. */
+            0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, /* org..... */
+            0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x00, /* ..)..... */
+            0x00, 0x00, 0x00                                /* ... */
+        ];
+
+        // The DNS payload starts at offset 42.
+        let dns_payload = &buf[42..];
+
+        // Make a TCP DNS request payload but with the length 1 larger
+        // than the available data.
+        let mut request = Vec::new();
+        request.push(((dns_payload.len() as u16) >> 8) as u8);
+        request.push(((dns_payload.len() as u16) & 0xff) as u8 + 1);
+        request.extend(dns_payload);
+
+        let mut state = DNSState::new();
+        assert_eq!(
+            AppLayerResult::incomplete(0, 52),
+            state.parse_request_tcp(&request)
+        );
+    }
+
+    #[test]
+    fn test_dns_parse_response_tcp_valid() {
+        // A UDP DNS response with the DNS payload starting at byte 42.
+        // From pcap: https://github.com/jasonish/suricata-verify/blob/7cc0e1bd0a5249b52e6e87d82d57c0b6aaf75fce/dns-udp-dig-a-www-suricata-ids-org/dig-a-www.suricata-ids.org.pcap
+        let buf: &[u8] = &[
+            0xd8, 0xcb, 0x8a, 0xed, 0xa1, 0x46, 0x00, 0x15, /* .....F.. */
+            0x17, 0x0d, 0x06, 0xf7, 0x08, 0x00, 0x45, 0x00, /* ......E. */
+            0x00, 0x80, 0x65, 0x4e, 0x40, 0x00, 0x40, 0x11, /* ..eN@.@. */
+            0xbe, 0xf3, 0x0a, 0x10, 0x01, 0x01, 0x0a, 0x10, /* ........ */
+            0x01, 0x0b, 0x00, 0x35, 0xa3, 0x4d, 0x00, 0x6c, /* ...5.M.l */
+            0x8d, 0x8c, 0x8d, 0x32, 0x81, 0xa0, 0x00, 0x01, /* ...2.... */
+            0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, /* .......w */
+            0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63, /* ww.suric */
+            0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03, /* ata-ids. */
+            0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, /* org..... */
+            0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, /* ........ */
+            0x0d, 0xd8, 0x00, 0x12, 0x0c, 0x73, 0x75, 0x72, /* .....sur */
+            0x69, 0x63, 0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, /* icata-id */
+            0x73, 0x03, 0x6f, 0x72, 0x67, 0x00, 0xc0, 0x32, /* s.org..2 */
+            0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf4, /* ........ */
+            0x00, 0x04, 0xc0, 0x00, 0x4e, 0x18, 0xc0, 0x32, /* ....N..2 */
+            0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf4, /* ........ */
+            0x00, 0x04, 0xc0, 0x00, 0x4e, 0x19              /* ....N. */
+        ];
+
+        // The DNS payload starts at offset 42.
+        let dns_payload = &buf[42..];
+
+        // Make a TCP DNS response payload.
+        let mut request = Vec::new();
+        request.push(((dns_payload.len() as u16) >> 8) as u8);
+        request.push(((dns_payload.len() as u16) & 0xff) as u8);
+        request.extend(dns_payload);
+
+        let mut state = DNSState::new();
+        assert_eq!(
+            AppLayerResult::ok(),
+            state.parse_response_tcp(&request)
+        );
+    }
+
+    // Test that a TCP DNS payload won't be parsed if there is not
+    // enough data.
+    #[test]
+    fn test_dns_parse_response_tcp_short_payload() {
+        // A UDP DNS response with the DNS payload starting at byte 42.
+        // From pcap: https://github.com/jasonish/suricata-verify/blob/7cc0e1bd0a5249b52e6e87d82d57c0b6aaf75fce/dns-udp-dig-a-www-suricata-ids-org/dig-a-www.suricata-ids.org.pcap
+        let buf: &[u8] = &[
+            0xd8, 0xcb, 0x8a, 0xed, 0xa1, 0x46, 0x00, 0x15, /* .....F.. */
+            0x17, 0x0d, 0x06, 0xf7, 0x08, 0x00, 0x45, 0x00, /* ......E. */
+            0x00, 0x80, 0x65, 0x4e, 0x40, 0x00, 0x40, 0x11, /* ..eN@.@. */
+            0xbe, 0xf3, 0x0a, 0x10, 0x01, 0x01, 0x0a, 0x10, /* ........ */
+            0x01, 0x0b, 0x00, 0x35, 0xa3, 0x4d, 0x00, 0x6c, /* ...5.M.l */
+            0x8d, 0x8c, 0x8d, 0x32, 0x81, 0xa0, 0x00, 0x01, /* ...2.... */
+            0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77, /* .......w */
+            0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63, /* ww.suric */
+            0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03, /* ata-ids. */
+            0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01, /* org..... */
+            0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, /* ........ */
+            0x0d, 0xd8, 0x00, 0x12, 0x0c, 0x73, 0x75, 0x72, /* .....sur */
+            0x69, 0x63, 0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, /* icata-id */
+            0x73, 0x03, 0x6f, 0x72, 0x67, 0x00, 0xc0, 0x32, /* s.org..2 */
+            0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf4, /* ........ */
+            0x00, 0x04, 0xc0, 0x00, 0x4e, 0x18, 0xc0, 0x32, /* ....N..2 */
+            0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf4, /* ........ */
+            0x00, 0x04, 0xc0, 0x00, 0x4e, 0x19              /* ....N. */
+        ];
+
+        // The DNS payload starts at offset 42.
+        let dns_payload = &buf[42..];
+
+        // Make a TCP DNS response payload, but make the length 1 byte
+        // larger than the actual size.
+        let mut request = Vec::new();
+        request.push(((dns_payload.len() as u16) >> 8) as u8);
+        request.push((((dns_payload.len() as u16) & 0xff) + 1) as u8);
+        request.extend(dns_payload);
+
+        let mut state = DNSState::new();
+        assert_eq!(
+            AppLayerResult::incomplete(0, 103),
+            state.parse_response_tcp(&request)
+        );
+    }
+
+    // Port of the C RustDNSUDPParserTest02 unit test.
     #[test]
-    fn test_drain() {
+    fn test_dns_udp_parser_test_01() {
+        /* query: abcdefghijk.com
+         * TTL: 86400
+         * serial 20130422 refresh 28800 retry 7200 exp 604800 min ttl 86400
+         * ns, hostmaster */
         let buf: &[u8] = &[
-            0x09, 0x63,
-            0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2d, 0x63, 0x66,
-            0x07, 0x64, 0x72, 0x6f, 0x70, 0x62, 0x6f, 0x78,
+            0x00, 0x3c, 0x85, 0x00, 0x00, 0x01, 0x00, 0x00,
+            0x00, 0x01, 0x00, 0x00, 0x0b, 0x61, 0x62, 0x63,
+            0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+            0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x0f, 0x00,
+            0x01, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x01,
+            0x51, 0x80, 0x00, 0x25, 0x02, 0x6e, 0x73, 0x00,
+            0x0a, 0x68, 0x6f, 0x73, 0x74, 0x6d, 0x61, 0x73,
+            0x74, 0x65, 0x72, 0xc0, 0x2f, 0x01, 0x33, 0x2a,
+            0x76, 0x00, 0x00, 0x70, 0x80, 0x00, 0x00, 0x1c,
+            0x20, 0x00, 0x09, 0x3a, 0x80, 0x00, 0x01, 0x51,
+            0x80,
+        ];
+        let mut state = DNSState::new();
+        assert!(state.parse_response(buf));
+    }
+
+    // Port of the C RustDNSUDPParserTest02 unit test.
+    #[test]
+    fn test_dns_udp_parser_test_02() {
+        let buf: &[u8] = &[
+            0x6D,0x08,0x84,0x80,0x00,0x01,0x00,0x08,0x00,0x00,0x00,0x01,0x03,0x57,0x57,0x57,
+            0x04,0x54,0x54,0x54,0x54,0x03,0x56,0x56,0x56,0x03,0x63,0x6F,0x6D,0x02,0x79,0x79,
+            0x00,0x00,0x01,0x00,0x01,0xC0,0x0C,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,
+            0x02,0xC0,0x0C,0xC0,0x31,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,
+            0x31,0xC0,0x3F,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x3F,0xC0,
+            0x4D,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x4D,0xC0,0x5B,0x00,
+            0x05,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x5B,0xC0,0x69,0x00,0x05,0x00,
+            0x01,0x00,0x00,0x0E,0x10,0x00,0x02,0xC0,0x69,0xC0,0x77,0x00,0x05,0x00,0x01,0x00,
+            0x00,0x0E,0x10,0x00,0x02,0xC0,0x77,0xC0,0x85,0x00,0x05,0x00,0x01,0x00,0x00,0x0E,
+            0x10,0x00,0x02,0xC0,0x85,0x00,0x00,0x29,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+        ];
+        let mut state = DNSState::new();
+        assert!(state.parse_response(buf));
+    }
+
+    // Port of the C RustDNSUDPParserTest03 unit test.
+    #[test]
+    fn test_dns_udp_parser_test_03() {
+        let buf: &[u8] = &[
+            0x6F,0xB4,0x84,0x80,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x03,0x03,0x57,0x57,0x77,
+            0x0B,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x56,0x03,0x55,0x55,0x55,
+            0x02,0x79,0x79,0x00,0x00,0x01,0x00,0x01,0xC0,0x0C,0x00,0x05,0x00,0x01,0x00,0x00,
+            0x0E,0x10,0x00,0x02,0xC0,0x10,0xC0,0x34,0x00,0x01,0x00,0x01,0x00,0x00,0x0E,0x10,
+            0x00,0x04,0xC3,0xEA,0x04,0x19,0xC0,0x34,0x00,0x02,0x00,0x01,0x00,0x00,0x0E,0x10,
+            0x00,0x0A,0x03,0x6E,0x73,0x31,0x03,0x61,0x67,0x62,0xC0,0x20,0xC0,0x46,0x00,0x02,
+            0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x06,0x03,0x6E,0x73,0x32,0xC0,0x56,0xC0,0x52,
+            0x00,0x01,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x04,0xC3,0xEA,0x04,0x0A,0xC0,0x68,
+            0x00,0x01,0x00,0x01,0x00,0x00,0x0E,0x10,0x00,0x04,0xC3,0xEA,0x05,0x14,0x00,0x00,
+            0x29,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+        ];
+        let mut state = DNSState::new();
+        assert!(state.parse_response(buf));
+    }
+
+    // Port of the C RustDNSUDPParserTest04 unit test.
+    //
+    // Test the TXT records in an answer.
+    #[test]
+    fn test_dns_udp_parser_test_04() {
+        let buf: &[u8] = &[
+            0xc2,0x2f,0x81,0x80,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x0a,0x41,0x41,0x41,
+            0x41,0x41,0x4f,0x31,0x6b,0x51,0x41,0x05,0x3d,0x61,0x75,0x74,0x68,0x03,0x73,0x72,
+            0x76,0x06,0x74,0x75,0x6e,0x6e,0x65,0x6c,0x03,0x63,0x6f,0x6d,0x00,0x00,0x10,0x00,
+            0x01,
+            /* answer record start */
+            0xc0,0x0c,0x00,0x10,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x22,
+            /* txt record starts: */
+            0x20, /* <txt len 32 */  0x41,0x68,0x76,0x4d,0x41,0x41,0x4f,0x31,0x6b,0x41,0x46,
+            0x45,0x35,0x54,0x45,0x39,0x51,0x54,0x6a,0x46,0x46,0x4e,0x30,0x39,0x52,0x4e,0x31,
+            0x6c,0x59,0x53,0x44,0x6b,0x00, /* <txt len 0 */   0xc0,0x1d,0x00,0x02,0x00,0x01,
+            0x00,0x09,0x3a,0x80,0x00,0x09,0x06,0x69,0x6f,0x64,0x69,0x6e,0x65,0xc0,0x21,0xc0,
+            0x6b,0x00,0x01,0x00,0x01,0x00,0x09,0x3a,0x80,0x00,0x04,0x0a,0x1e,0x1c,0x5f
+        ];
+        let mut state = DNSState::new();
+        assert!(state.parse_response(buf));
+    }
+
+    // Port of the C RustDNSUDPParserTest05 unit test.
+    //
+    // Test TXT records in answer with a bad length.
+    #[test]
+    fn test_dns_udp_parser_test_05() {
+        let buf: &[u8] = &[
+            0xc2,0x2f,0x81,0x80,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x0a,0x41,0x41,0x41,
+            0x41,0x41,0x4f,0x31,0x6b,0x51,0x41,0x05,0x3d,0x61,0x75,0x74,0x68,0x03,0x73,0x72,
+            0x76,0x06,0x74,0x75,0x6e,0x6e,0x65,0x6c,0x03,0x63,0x6f,0x6d,0x00,0x00,0x10,0x00,
+            0x01,
+            /* answer record start */
+            0xc0,0x0c,0x00,0x10,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x22,
+            /* txt record starts: */
+            0x40, /* <txt len 64 */  0x41,0x68,0x76,0x4d,0x41,0x41,0x4f,0x31,0x6b,0x41,0x46,
+            0x45,0x35,0x54,0x45,0x39,0x51,0x54,0x6a,0x46,0x46,0x4e,0x30,0x39,0x52,0x4e,0x31,
+            0x6c,0x59,0x53,0x44,0x6b,0x00, /* <txt len 0 */   0xc0,0x1d,0x00,0x02,0x00,0x01,
+            0x00,0x09,0x3a,0x80,0x00,0x09,0x06,0x69,0x6f,0x64,0x69,0x6e,0x65,0xc0,0x21,0xc0,
+            0x6b,0x00,0x01,0x00,0x01,0x00,0x09,0x3a,0x80,0x00,0x04,0x0a,0x1e,0x1c,0x5f
+        ];
+        let mut state = DNSState::new();
+        assert!(!state.parse_response(buf));
+    }
+
+    // Port of the C RustDNSTCPParserTestMultiRecord unit test.
+    #[test]
+    fn test_dns_tcp_parser_multi_record() {
+        let buf: &[u8] = &[
+            0x00, 0x1e, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x30,
+            0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+            0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+            0x00, 0x1e, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x31,
+            0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+            0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+            0x00, 0x1e, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x32,
+            0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+            0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+            0x00, 0x1e, 0x00, 0x03, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x33,
+            0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+            0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+            0x00, 0x1e, 0x00, 0x04, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x34,
+            0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+            0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+            0x00, 0x1e, 0x00, 0x05, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x35,
+            0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+            0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+            0x00, 0x1e, 0x00, 0x06, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x36,
+            0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+            0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+            0x00, 0x1e, 0x00, 0x07, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x37,
+            0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+            0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+            0x00, 0x1e, 0x00, 0x08, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38,
+            0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+            0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+            0x00, 0x1e, 0x00, 0x09, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x39,
+            0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+            0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+            0x00, 0x1f, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x31,
+            0x30, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
             0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00,
+            0x01, 0x00, 0x1f, 0x00, 0x0b, 0x01, 0x00, 0x00,
+            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+            0x31, 0x31, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+            0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01,
+            0x00, 0x01, 0x00, 0x1f, 0x00, 0x0c, 0x01, 0x00,
+            0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x02, 0x31, 0x32, 0x06, 0x67, 0x6f, 0x6f, 0x67,
+            0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00,
+            0x01, 0x00, 0x01, 0x00, 0x1f, 0x00, 0x0d, 0x01,
+            0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x02, 0x31, 0x33, 0x06, 0x67, 0x6f, 0x6f,
+            0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00,
+            0x00, 0x01, 0x00, 0x01, 0x00, 0x1f, 0x00, 0x0e,
+            0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x02, 0x31, 0x34, 0x06, 0x67, 0x6f,
+            0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d,
+            0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x1f, 0x00,
+            0x0f, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x02, 0x31, 0x35, 0x06, 0x67,
+            0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f,
+            0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x1f,
+            0x00, 0x10, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x02, 0x31, 0x36, 0x06,
+            0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63,
+            0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
+            0x1f, 0x00, 0x11, 0x01, 0x00, 0x00, 0x01, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x31, 0x37,
+            0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03,
+            0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01,
+            0x00, 0x1f, 0x00, 0x12, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x31,
+            0x38, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+            0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00,
+            0x01, 0x00, 0x1f, 0x00, 0x13, 0x01, 0x00, 0x00,
+            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+            0x31, 0x39, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+            0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01,
+            0x00, 0x01
+        ];
+        let mut state = DNSState::new();
+        assert_eq!(
+            AppLayerResult::ok(),
+            state.parse_request_tcp(buf)
+        );
+    }
+
+    #[test]
+    fn test_dns_tcp_parser_split_payload() {
+        /* incomplete payload */
+        let buf1: &[u8] = &[
+            0x00, 0x1c, 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+        ];
+        /* complete payload plus the start of a new payload */
+        let buf2: &[u8] = &[
+            0x00, 0x1c, 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x03,
+            0x63, 0x6F, 0x6D, 0x00, 0x00, 0x10, 0x00, 0x01,
+
+            // next.
+            0x00, 0x1c, 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        ];
+
+        /* and the complete payload again with no trailing data. */
+        let buf3: &[u8] = &[
+            0x00, 0x1c, 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x03,
+            0x63, 0x6F, 0x6D, 0x00, 0x00, 0x10, 0x00, 0x01,
         ];
-        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);
+
+        let mut state = DNSState::new();
+        assert_eq!(
+            AppLayerResult::incomplete(0, 30),
+            state.parse_request_tcp(buf1)
+        );
+        assert_eq!(
+            AppLayerResult::incomplete(30, 30),
+            state.parse_request_tcp(buf2)
+        );
+        assert_eq!(
+            AppLayerResult::ok(),
+            state.parse_request_tcp(buf3)
+        );
+    }
+
+    #[test]
+    fn test_dns_event_from_id() {
+        assert_eq!(DNSEvent::from_id(0), Some(DNSEvent::MalformedData));
+        assert_eq!(DNSEvent::from_id(3), Some(DNSEvent::ZFlagSet));
+        assert_eq!(DNSEvent::from_id(9), None);
+    }
+
+    #[test]
+    fn test_dns_event_to_cstring() {
+        assert_eq!(DNSEvent::MalformedData.to_cstring(), "malformed_data\0");
+    }
+
+    #[test]
+    fn test_dns_event_from_string() {
+        let name = "malformed_data";
+        let event = DNSEvent::from_string(&name).unwrap();
+        assert_eq!(event, DNSEvent::MalformedData);
+        assert_eq!(event.to_cstring(), format!("{}\0", name));
     }
 }