]> git.ipfire.org Git - people/ms/suricata.git/commitdiff
rust/dns: support gaps in TCP DNS
authorJason Ish <ish@unx.ca>
Fri, 9 Jun 2017 17:12:19 +0000 (11:12 -0600)
committerVictor Julien <victor@inliniac.net>
Mon, 12 Jun 2017 17:27:36 +0000 (19:27 +0200)
rust/src/dns/dns.rs
src/app-layer-dns-tcp-rust.c

index 4c3b3f9a1ff0ee39bf352e1f7d610a864ca51ad0..0023a472b9fe1621644ffdb70274573c4b9bea08 100644 (file)
@@ -271,6 +271,8 @@ pub struct DNSState {
 
     pub request_buffer: Vec<u8>,
     pub response_buffer: Vec<u8>,
+
+    gap: bool,
 }
 
 impl DNSState {
@@ -283,6 +285,7 @@ impl DNSState {
             events: 0,
             request_buffer: Vec::new(),
             response_buffer: Vec::new(),
+            gap: false,
         };
     }
 
@@ -296,6 +299,7 @@ impl DNSState {
             events: 0,
             request_buffer: Vec::with_capacity(0xffff),
             response_buffer: Vec::with_capacity(0xffff),
+            gap: false,
         };
     }
 
@@ -465,6 +469,14 @@ impl DNSState {
     /// 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 {
+        if self.gap {
+            if probe_tcp(input) {
+                self.gap = false;
+            } else {
+                return 0
+            }
+        }
+
         self.request_buffer.extend_from_slice(input);
 
         while self.request_buffer.len() > 0 {
@@ -496,6 +508,14 @@ impl DNSState {
     /// 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 {
+        if self.gap {
+            if probe_tcp(input) {
+                self.gap = false;
+            } else {
+                return 0
+            }
+        }
+
         self.response_buffer.extend_from_slice(input);
         let size = match nom::be_u16(&self.response_buffer) {
             nom::IResult::Done(_, len) => {
@@ -511,7 +531,25 @@ impl DNSState {
             }
             return -1;
         }
-        0
+        return 0
+    }
+
+    /// A gap has been seen in the request direction. Set the gap flag
+    /// to clear any buffered data.
+    pub fn request_gap(&mut self, gap: u32) {
+        if gap > 0 {
+            self.request_buffer.clear();
+            self.gap = true;
+        }
+    }
+
+    /// A gap has been seen in the response direction. Set the gap
+    /// flag to clear any buffered data.
+    pub fn response_gap(&mut self, gap: u32) {
+        if gap > 0 {
+            self.response_buffer.clear();
+            self.gap = true;
+        }
     }
 }
 
@@ -523,6 +561,27 @@ impl Drop for DNSState {
     }
 }
 
+/// Probe input to see if it looks like DNS.
+fn probe(input: &[u8]) -> bool {
+    match parser::dns_parse_request(input) {
+        nom::IResult::Done(_, _) => true,
+        _ => false
+    }
+}
+
+/// Probe TCP input to see if it looks like DNS.
+pub fn probe_tcp(input: &[u8]) -> bool {
+    match nom::be_u16(input) {
+        nom::IResult::Done(rem, len) => {
+            if rem.len() >= len as usize {
+                return probe(rem);
+            }
+        },
+        _ => {}
+    }
+    return false;
+}
+
 /// Returns *mut DNSState
 #[no_mangle]
 pub extern "C" fn rs_dns_state_new() -> *mut libc::c_void {
@@ -596,8 +655,15 @@ pub extern "C" fn rs_dns_parse_request_tcp(_flow: *mut core::Flow,
                                            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);
+    if input_len > 0 {
+        if input != std::ptr::null_mut() {
+            let buf = unsafe{
+                std::slice::from_raw_parts(input, input_len as usize)};
+            return state.parse_request_tcp(buf);
+        }
+        state.request_gap(input_len);
+    }
+    return 0;
 }
 
 #[no_mangle]
@@ -608,8 +674,15 @@ pub extern "C" fn rs_dns_parse_response_tcp(_flow: *mut core::Flow,
                                             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);
+    if input_len > 0 {
+        if input != std::ptr::null_mut() {
+            let buf = unsafe{
+                std::slice::from_raw_parts(input, input_len as usize)};
+            return state.parse_response_tcp(buf);
+        }
+        state.response_gap(input_len);
+    }
+    return 0;
 }
 
 #[no_mangle]
@@ -800,14 +873,10 @@ pub extern "C" fn rs_dns_probe(input: *const libc::uint8_t, len: libc::uint32_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;
-        }
+    if probe(slice) {
+        return 1;
     }
+    return 0;
 }
 
 #[no_mangle]
@@ -818,18 +887,8 @@ pub extern "C" fn rs_dns_probe_tcp(input: *const 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;
-                    }
-                    _ => {}
-                }
-            }
-        }
-        _ => {}
+    if probe_tcp(slice) {
+        return 1;
     }
     return 0;
 }
index 4ca1490d36a7952b0f68fb8fb68ec1b876446bd2..70198fb0c680ecca0438764eea50388fdbc2cce2 100644 (file)
@@ -180,6 +180,11 @@ void RegisterRustDNSTCPParsers(void)
         AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_DNS,
                 rs_dns_state_progress_completion_status);
         DNSAppLayerRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_DNS);
+
+        /* This parser accepts gaps. */
+        AppLayerParserRegisterOptionFlags(IPPROTO_TCP, ALPROTO_DNS,
+                APP_LAYER_PARSER_OPT_ACCEPT_GAPS);
+
     } else {
         SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
                   "still on.", proto_name);