* 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,
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>,
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,
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);
+ }
+}
--- /dev/null
+/* 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();
+}
* 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;
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>,
));
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;
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 \
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;
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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__ */
#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;
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);
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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__ */
#include "app-layer-dns-udp.h"
#ifdef HAVE_RUST
-#include "rust.h"
+#include "app-layer-dns-udp-rust.h"
#endif
/** \internal
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 */
#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
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;
if (r == 1)
break;
}
+#endif
return r;
}
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;
buffer, buffer_len);
}
}
+#endif
}
int PrefilterTxDnsQueryRegister(SigGroupHead *sgh, MpmCtx *mpm_ctx)
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);
#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
MemBuffer *buffer;
} LogDnsLogThread;
+#ifndef HAVE_RUST
static int DNSRRTypeEnabled(uint16_t type, uint64_t flags)
{
if (likely(flags == ~0UL)) {
return 0;
}
}
+#endif
+#ifndef HAVE_RUST
static void LogQuery(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx,
uint64_t tx_id, DNSQueryEntry *entry) __attribute__((nonnull));
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));
return;
}
+#endif
+#ifndef HAVE_RUST
static void OutputFailure(LogDnsLogThread *aft, json_t *djs,
DNSTransaction *tx, DNSQueryEntry *entry) __attribute__((nonnull));
return;
}
+#endif
+#ifndef HAVE_RUST
static void LogAnswers(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx, uint64_t tx_id)
{
}
}
+#endif
static int JsonDnsLoggerToServer(ThreadVars *tv, void *thread_data,
const Packet *p, Flow *f, void *alstate, void *txptr, uint64_t tx_id)
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) {
json_decref(js);
}
}
+#endif
SCReturnInt(TM_ECODE_OK);
}
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);
}
void (*AppLayerDecoderEventsFreeEvents)(AppLayerDecoderEvents **);
} SuricataContext;
-void rs_dns_init(void);
-
#endif /* !__RUST_H__ */
#ifdef HAVE_RUST
#include "rust.h"
-#include "rust-core.h"
+#include "rust-core-gen.h"
#endif
/*
#include "util-syslog.h"
#ifdef HAVE_RUST
-#include "rust-log.h"
+#include "rust-log-gen.h"
#endif
#include "conf.h"
#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");
return ret;
}
}
-
+#endif
return LuaCallbackError(luastate, "no query");
}
{
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;
}
{
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;
}
{
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");
}
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");
}
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");
}
return 1;
+#endif
}