rust: DNS app-layer.

A DNS application layer in Rust. This is different than the
C based one, as it is partially stateless by not matching
up responses to replies.
pull/2746/head
Jason Ish 8 years ago
parent 9449739dd5
commit 73388042b2

@ -15,15 +15,55 @@
* 02110-1301, USA.
*/
extern crate libc;
extern crate nom;
use std;
use std::mem::transmute;
use log::*;
use core;
use dns::parser;
/// DNS record types.
pub const DNS_RTYPE_A: u16 = 1;
pub const DNS_RTYPE_CNAME: u16 = 5;
pub const DNS_RTYPE_SOA: u16 = 6;
pub const DNS_RTYPE_PTR: u16 = 12;
pub const DNS_RTYPE_MX: u16 = 15;
pub const DNS_RTYPE_AAAA: u16 = 28;
pub const DNS_RTYPE_SSHFP: u16 = 44;
pub const DNS_RTYPE_RRSIG: u16 = 46;
/// DNS error codes.
pub const DNS_RCODE_NOERROR: u16 = 0;
pub const DNS_RCODE_FORMERR: u16 = 1;
pub const DNS_RCODE_NXDOMAIN: u16 = 3;
/// The maximum number of transactions to keep in the queue pending
/// processing before they are aggressively purged. Due to the
/// stateless nature of this parser this is rarely needed, especially
/// when one call to parse a request parses and a single request, and
/// likewise for responses.
///
/// Where this matters is when one TCP buffer contains multiple
/// requests are responses and one call into the parser creates
/// multiple transactions. In this case we have to hold onto
/// transactions longer than until handling the next transaction so it
/// gets logged.
const MAX_TRANSACTIONS: usize = 32;
#[repr(u32)]
pub enum DNSEvent {
UnsolicitedResponse = 0,
MalformedData,
NotRequest,
NotResponse,
ZFlagSet,
Flooded,
StateMemCapReached,
}
#[derive(Debug,PartialEq)]
pub struct DNSHeader {
pub tx_id: u16,
@ -41,6 +81,18 @@ pub struct DNSQueryEntry {
pub rrclass: u16,
}
impl DNSQueryEntry {
pub fn name(&self) -> &str {
let r = std::str::from_utf8(&self.name);
if r.is_err() {
return "";
}
return r.unwrap();
}
}
#[derive(Debug,PartialEq)]
pub struct DNSAnswerEntry {
pub name: Vec<u8>,
@ -51,6 +103,26 @@ pub struct DNSAnswerEntry {
pub data: Vec<u8>,
}
impl DNSAnswerEntry {
pub fn name(&self) -> &str {
let r = std::str::from_utf8(&self.name);
if r.is_err() {
return "";
}
return r.unwrap();
}
pub fn data_to_string(&self) -> &str {
let r = std::str::from_utf8(&self.data);
if r.is_err() {
return "";
}
return r.unwrap();
}
}
#[derive(Debug)]
pub struct DNSRequest {
pub header: DNSHeader,
@ -64,3 +136,666 @@ pub struct DNSResponse {
pub answers: Vec<DNSAnswerEntry>,
pub authorities: Vec<DNSAnswerEntry>,
}
#[derive(Debug)]
pub struct DNSTransaction {
pub id: u64,
pub request: Option<DNSRequest>,
pub response: Option<DNSResponse>,
pub logged: u32,
pub de_state: Option<*mut core::DetectEngineState>,
pub events: *mut core::AppLayerDecoderEvents,
}
impl DNSTransaction {
pub fn new() -> DNSTransaction {
return DNSTransaction{
id: 0,
request: None,
response: None,
logged: 0,
de_state: None,
events: std::ptr::null_mut(),
}
}
pub fn free(&mut self) {
if self.events != std::ptr::null_mut() {
core::sc_app_layer_decoder_events_free_events(&mut self.events);
}
}
/// Get the DNS transactions ID (not the internal tracking ID).
pub fn tx_id(&self) -> u16 {
for request in &self.request {
return request.header.tx_id;
}
for response in &self.response {
return response.header.tx_id;
}
// Shouldn't happen.
return 0;
}
/// Get the reply code of the transaction. Note that this will
/// also return 0 if there is no reply.
pub fn rcode(&self) -> u16 {
for response in &self.response {
return response.header.flags & 0x000f;
}
return 0;
}
}
impl Drop for DNSTransaction {
fn drop(&mut self) {
self.free();
}
}
pub struct DNSState {
// Internal transaction ID.
pub tx_id: u64,
// Transactions.
pub transactions: Vec<DNSTransaction>,
pub de_state_count: u64,
pub events: u16,
pub request_buffer: Vec<u8>,
pub response_buffer: Vec<u8>,
}
impl DNSState {
pub fn new() -> DNSState {
return DNSState{
tx_id: 0,
transactions: Vec::new(),
de_state_count: 0,
events: 0,
request_buffer: Vec::new(),
response_buffer: Vec::new(),
};
}
/// Allocate a new state with capacites in the buffers for
/// potentially buffering as might be needed in TCP.
pub fn new_tcp() -> DNSState {
return DNSState{
tx_id: 0,
transactions: Vec::new(),
de_state_count: 0,
events: 0,
request_buffer: Vec::with_capacity(0xffff),
response_buffer: Vec::with_capacity(0xffff),
};
}
pub fn free(&mut self) {
SCLogDebug!("Freeing {} transactions left in state.",
self.transactions.len());
while self.transactions.len() > 0 {
self.free_tx_at_index(0);
}
assert!(self.transactions.len() == 0);
}
pub fn new_tx(&mut self) -> DNSTransaction {
let mut tx = DNSTransaction::new();
self.tx_id += 1;
tx.id = self.tx_id;
return tx;
}
pub fn free_tx(&mut self, tx_id: u64) {
SCLogDebug!("************** Freeing TX with ID {}", tx_id);
let len = self.transactions.len();
let mut found = false;
let mut index = 0;
for i in 0..len {
let tx = &self.transactions[i];
if tx.id == tx_id + 1 {
found = true;
index = i;
break;
}
}
if found {
self.free_tx_at_index(index);
}
}
fn free_tx_at_index(&mut self, index: usize) {
let tx = self.transactions.remove(index);
match tx.de_state {
Some(state) => {
core::sc_detect_engine_state_free(state);
self.de_state_count -= 1;
}
_ => {}
}
}
// Purges all transactions except one. This is a stateless parser
// so we don't need to hang onto old transactions.
//
// This is to actually handle an edge case where a DNS flood
// occurs in a single direction with no response packets. In such
// a case the functions to free a transaction are never called by
// the app-layer as they require bidirectional traffic.
pub fn purge(&mut self, tx_id: u64) {
while self.transactions.len() > MAX_TRANSACTIONS {
if self.transactions[0].id == tx_id + 1 {
return;
}
SCLogDebug!("Purging DNS TX with ID {}", self.transactions[0].id);
self.free_tx_at_index(0);
}
}
pub fn get_tx(&mut self, tx_id: u64) -> Option<&DNSTransaction> {
SCLogDebug!("get_tx: tx_id={}", tx_id);
self.purge(tx_id);
for tx in &mut self.transactions {
if tx.id == tx_id + 1 {
SCLogDebug!("Found DNS TX with ID {}", tx_id);
return Some(tx);
}
}
SCLogDebug!("Failed to find DNS TX with ID {}", tx_id);
return None;
}
/// Set an event. The event is set on the most recent transaction.
pub fn set_event(&mut self, event: DNSEvent) {
let len = self.transactions.len();
if len == 0 {
return;
}
let mut tx = &mut self.transactions[len - 1];
core::sc_app_layer_decoder_events_set_event_raw(&mut tx.events,
event as u8);
self.events += 1;
}
pub fn parse_request(&mut self, input: &[u8]) -> bool {
match parser::dns_parse_request(input) {
nom::IResult::Done(_, request) => {
if request.header.flags & 0x8000 != 0 {
SCLogDebug!("DNS message is not a request");
self.set_event(DNSEvent::NotRequest);
return false;
}
if request.header.flags & 0x0040 != 0 {
SCLogDebug!("Z-flag set on DNS response");
self.set_event(DNSEvent::ZFlagSet);
return false;
}
let mut tx = self.new_tx();
tx.request = Some(request);
self.transactions.push(tx);
return true;
}
nom::IResult::Incomplete(_) => {
// Insufficient data.
SCLogDebug!("Insufficient data while parsing DNS request");
self.set_event(DNSEvent::MalformedData);
return false;
}
nom::IResult::Error(_) => {
// Error, probably malformed data.
SCLogDebug!("An error occurred while parsing DNS request");
self.set_event(DNSEvent::MalformedData);
return false;
}
}
}
pub fn parse_response(&mut self, input: &[u8]) -> bool {
match parser::dns_parse_response(input) {
nom::IResult::Done(_, response) => {
SCLogDebug!("Response header flags: {}", response.header.flags);
if response.header.flags & 0x8000 == 0 {
SCLogDebug!("DNS message is not a response");
self.set_event(DNSEvent::NotResponse);
}
if response.header.flags & 0x0040 != 0 {
SCLogDebug!("Z-flag set on DNS response");
self.set_event(DNSEvent::ZFlagSet);
return false;
}
let mut tx = self.new_tx();
tx.response = Some(response);
self.transactions.push(tx);
return true;
}
nom::IResult::Incomplete(_) => {
// Insufficient data.
SCLogDebug!("Insufficient data while parsing DNS response");
self.set_event(DNSEvent::MalformedData);
return false;
}
nom::IResult::Error(_) => {
// Error, probably malformed data.
SCLogDebug!("An error occurred while parsing DNS response");
self.set_event(DNSEvent::MalformedData);
return false;
}
}
}
/// TCP variation of response request parser to handle the length
/// prefix as well as buffering.
///
/// Always buffer and read from the buffer. Should optimize to skip
/// the buffer if not needed.
pub fn parse_request_tcp(&mut self, input: &[u8]) -> i8 {
self.request_buffer.extend_from_slice(input);
while self.request_buffer.len() > 0 {
let size = match nom::be_u16(&self.request_buffer) {
nom::IResult::Done(_, len) => {
len as usize
}
_ => 0 as usize
};
SCLogDebug!("Have {} bytes, need {} to parse",
self.request_buffer.len(), size);
if size > 0 && self.request_buffer.len() >= size {
let msg: Vec<u8> = self.request_buffer.drain(0..(size + 2))
.collect();
if self.parse_request(&msg[2..]) {
continue;
}
return -1;
}
SCLogDebug!("Not enough DNS traffic to parse.");
return 0;
}
return 0;
}
/// TCP variation of the response parser to handle the length
/// prefix as well as buffering.
///
/// Always buffer and read from the buffer. Should optimize to skip
/// the buffer if not needed.
pub fn parse_response_tcp(&mut self, input: &[u8]) -> i8 {
self.response_buffer.extend_from_slice(input);
let size = match nom::be_u16(&self.response_buffer) {
nom::IResult::Done(_, len) => {
len as usize
}
_ => 0 as usize
};
if size > 0 && self.response_buffer.len() + 2 >= size {
let msg: Vec<u8> = self.response_buffer.drain(0..(size + 2))
.collect();
if self.parse_response(&msg[2..]) {
return 1;
}
return -1;
}
0
}
}
/// Implement Drop for DNSState as transactions need to do some
/// explicit cleanup.
impl Drop for DNSState {
fn drop(&mut self) {
self.free();
}
}
/// Returns *mut DNSState
#[no_mangle]
pub extern "C" fn rs_dns_state_new() -> *mut libc::c_void {
let state = DNSState::new();
let boxed = Box::new(state);
return unsafe{transmute(boxed)};
}
/// Returns *mut DNSState
#[no_mangle]
pub extern "C" fn rs_dns_state_tcp_new() -> *mut libc::c_void {
let state = DNSState::new_tcp();
let boxed = Box::new(state);
return unsafe{transmute(boxed)};
}
/// Params:
/// - state: *mut DNSState as void pointer
#[no_mangle]
pub extern "C" fn rs_dns_state_free(state: *mut libc::c_void) {
// Just unbox...
let _drop: Box<DNSState> = unsafe{transmute(state)};
}
#[no_mangle]
pub extern "C" fn rs_dns_state_tx_free(state: &mut DNSState,
tx_id: libc::uint64_t)
{
state.free_tx(tx_id);
}
/// C binding parse a DNS request. Returns 1 on success, -1 on failure.
#[no_mangle]
pub extern "C" fn rs_dns_parse_request(_flow: *mut core::Flow,
state: &mut DNSState,
_pstate: *mut libc::c_void,
input: *mut libc::uint8_t,
input_len: libc::uint32_t,
_data: *mut libc::c_void)
-> libc::int8_t {
let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
if state.parse_request(buf) {
1
} else {
-1
}
}
#[no_mangle]
pub extern "C" fn rs_dns_parse_response(_flow: *mut core::Flow,
state: &mut DNSState,
_pstate: *mut libc::c_void,
input: *mut libc::uint8_t,
input_len: libc::uint32_t,
_data: *mut libc::c_void)
-> libc::int8_t {
let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
if state.parse_response(buf) {
1
} else {
-1
}
}
/// C binding parse a DNS request. Returns 1 on success, -1 on failure.
#[no_mangle]
pub extern "C" fn rs_dns_parse_request_tcp(_flow: *mut core::Flow,
state: &mut DNSState,
_pstate: *mut libc::c_void,
input: *mut libc::uint8_t,
input_len: libc::uint32_t,
_data: *mut libc::c_void)
-> libc::int8_t {
let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
return state.parse_request_tcp(buf);
}
#[no_mangle]
pub extern "C" fn rs_dns_parse_response_tcp(_flow: *mut core::Flow,
state: &mut DNSState,
_pstate: *mut libc::c_void,
input: *mut libc::uint8_t,
input_len: libc::uint32_t,
_data: *mut libc::c_void)
-> libc::int8_t {
let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
return state.parse_response_tcp(buf);
}
#[no_mangle]
pub extern "C" fn rs_dns_state_progress_completion_status(
_direction: libc::uint8_t)
-> libc::c_int
{
SCLogDebug!("rs_dns_state_progress_completion_status");
return 1;
}
#[no_mangle]
pub extern "C" fn rs_dns_tx_get_alstate_progress(_tx: &mut DNSTransaction,
_direction: libc::uint8_t)
-> libc::uint8_t
{
// This is a stateless parser, just the existence of a transaction
// means its complete.
SCLogDebug!("rs_dns_tx_get_alstate_progress");
return 1;
}
#[no_mangle]
pub extern "C" fn rs_dns_tx_set_logged(_state: &mut DNSState,
tx: &mut DNSTransaction,
logger: libc::uint32_t)
{
tx.logged |= logger;
}
#[no_mangle]
pub extern "C" fn rs_dns_tx_get_logged(_state: &mut DNSState,
tx: &mut DNSTransaction,
logger: libc::uint32_t)
-> i8
{
if tx.logged & logger != 0 {
return 1;
}
return 0;
}
#[no_mangle]
pub extern "C" fn rs_dns_state_get_tx_count(state: &mut DNSState)
-> libc::uint64_t
{
SCLogDebug!("rs_dns_state_get_tx_count: returning {}", state.tx_id);
return state.tx_id;
}
#[no_mangle]
pub extern "C" fn rs_dns_state_get_tx(state: &mut DNSState,
tx_id: libc::uint64_t)
-> *mut DNSTransaction
{
match state.get_tx(tx_id) {
Some(tx) => {
return unsafe{transmute(tx)};
}
None => {
return std::ptr::null_mut();
}
}
}
#[no_mangle]
pub extern "C" fn rs_dns_state_has_detect_state(state: &mut DNSState) -> u8
{
if state.de_state_count > 0 {
return 1;
}
return 0;
}
#[no_mangle]
pub extern "C" fn rs_dns_state_set_tx_detect_state(
state: &mut DNSState,
tx: &mut DNSTransaction,
de_state: &mut core::DetectEngineState)
{
state.de_state_count += 1;
tx.de_state = Some(de_state);
}
#[no_mangle]
pub extern "C" fn rs_dns_state_get_tx_detect_state(
tx: &mut DNSTransaction)
-> *mut core::DetectEngineState
{
match tx.de_state {
Some(ds) => {
return ds;
},
None => {
return std::ptr::null_mut();
}
}
}
#[no_mangle]
pub extern "C" fn rs_dns_state_has_events(state: &mut DNSState) -> u8 {
if state.events > 0 {
return 1;
}
return 0;
}
#[no_mangle]
pub extern "C" fn rs_dns_state_get_events(state: &mut DNSState,
tx_id: libc::uint64_t)
-> *mut core::AppLayerDecoderEvents
{
match state.get_tx(tx_id) {
Some(tx) => {
return tx.events;
}
_ => {
return std::ptr::null_mut();
}
}
}
#[no_mangle]
pub extern "C" fn rs_dns_tx_get_query_name(tx: &mut DNSTransaction,
i: libc::uint16_t,
buf: *mut *const libc::uint8_t,
len: *mut libc::uint32_t)
-> libc::uint8_t
{
for request in &tx.request {
if (i as usize) < request.queries.len() {
let query = &request.queries[i as usize];
if query.name.len() > 0 {
unsafe {
*len = query.name.len() as libc::uint32_t;
*buf = query.name.as_ptr();
}
return 1;
}
}
}
return 0;
}
/// Get the DNS transaction ID of a transaction.
//
/// extern uint16_t rs_dns_tx_get_tx_id(RSDNSTransaction *);
#[no_mangle]
pub extern "C" fn rs_dns_tx_get_tx_id(tx: &mut DNSTransaction) -> libc::uint16_t
{
return tx.tx_id()
}
/// Get the DNS response flags for a transaction.
///
/// extern uint16_t rs_dns_tx_get_response_flags(RSDNSTransaction *);
#[no_mangle]
pub extern "C" fn rs_dns_tx_get_response_flags(tx: &mut DNSTransaction)
-> libc::uint16_t
{
return tx.rcode();
}
#[no_mangle]
pub extern "C" fn rs_dns_tx_get_query_rrtype(tx: &mut DNSTransaction,
i: libc::uint16_t,
rrtype: *mut libc::uint16_t)
-> libc::uint8_t
{
for request in &tx.request {
if (i as usize) < request.queries.len() {
let query = &request.queries[i as usize];
if query.name.len() > 0 {
unsafe {
*rrtype = query.rrtype;
}
return 1;
}
}
}
return 0;
}
#[no_mangle]
pub extern "C" fn rs_dns_probe(input: *const libc::uint8_t, len: libc::uint32_t)
-> libc::uint8_t
{
let slice: &[u8] = unsafe {
std::slice::from_raw_parts(input as *mut u8, len as usize)
};
match parser::dns_parse_request(slice) {
nom::IResult::Done(_, _) => {
return 1;
}
_ => {
return 0;
}
}
}
#[no_mangle]
pub extern "C" fn rs_dns_probe_tcp(input: *const libc::uint8_t,
len: libc::uint32_t)
-> libc::uint8_t
{
let slice: &[u8] = unsafe {
std::slice::from_raw_parts(input as *mut u8, len as usize)
};
match nom::be_u16(slice) {
nom::IResult::Done(rem, len) => {
if rem.len() >= len as usize {
match parser::dns_parse_request(rem) {
nom::IResult::Done(_, _) => {
return 1;
}
_ => {}
}
}
}
_ => {}
}
return 0;
}
#[cfg(test)]
mod tests {
// Playing with vector draining...
#[test]
fn test_drain() {
let buf: &[u8] = &[
0x09, 0x63,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2d, 0x63, 0x66,
0x07, 0x64, 0x72, 0x6f, 0x70, 0x62, 0x6f, 0x78,
0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00,
];
let mut v: Vec<u8> = Vec::new();
v.extend_from_slice(buf);
assert_eq!(v.len(), buf.len());
// Drain one byte.
let drained: Vec<u8> = v.drain(0..1).collect();
assert_eq!(drained.len(), 1);
assert_eq!(v.len(), buf.len() - 1);
assert_eq!(buf[0], drained[0]);
// Drain some more.
v.drain(0..8);
assert_eq!(v.len(), buf.len() - 9);
}
}

@ -0,0 +1,211 @@
/* Copyright (C) 2017 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
extern crate libc;
use std;
use std::string::String;
use json::*;
use dns::dns::*;
use log::*;
fn dns_rrtype_string(rrtype: u16) -> String {
match rrtype {
DNS_RTYPE_A => "A",
DNS_RTYPE_CNAME => "CNAME",
DNS_RTYPE_SOA => "SOA",
DNS_RTYPE_PTR => "PTR",
DNS_RTYPE_MX => "MX",
DNS_RTYPE_SSHFP => "SSHFP",
DNS_RTYPE_RRSIG => "RRSIG",
_ => {
return rrtype.to_string();
}
}.to_string()
}
fn dns_rcode_string(flags: u16) -> String {
match flags & 0x000f {
DNS_RCODE_NOERROR => "NOERROR",
DNS_RCODE_FORMERR => "FORMERR",
DNS_RCODE_NXDOMAIN => "NXDOMAIN",
_ => {
return (flags & 0x000f).to_string();
}
}.to_string()
}
/// Format bytes as an IP address string.
fn dns_print_addr(addr: &Vec<u8>) -> std::string::String {
if addr.len() == 4 {
return format!("{}.{}.{}.{}", addr[0], addr[1], addr[2], addr[3]);
}
else if addr.len() == 16 {
return format!("{}{}:{}{}:{}{}:{}{}:{}{}:{}{}:{}{}:{}{}",
addr[0],
addr[1],
addr[2],
addr[3],
addr[4],
addr[5],
addr[6],
addr[7],
addr[8],
addr[9],
addr[10],
addr[11],
addr[12],
addr[13],
addr[14],
addr[15]);
}
else {
return "".to_string();
}
}
/// Log the SSHPF in an DNSAnswerEntry.
fn dns_log_sshfp(js: &Json, answer: &DNSAnswerEntry)
{
// Need at least 3 bytes - TODO: log something if we don't?
if answer.data_len < 3 {
return;
}
let sshfp = Json::object();
let mut hex = Vec::new();
for byte in &answer.data[2..] {
hex.push(format!("{:02x}", byte));
}
sshfp.set_string("fingerprint", &hex.join(":"));
sshfp.set_integer("algo", answer.data[0] as u64);
sshfp.set_integer("type", answer.data[1] as u64);
js.set("sshfp", sshfp);
}
#[no_mangle]
pub extern "C" fn rs_dns_log_json_query(tx: &mut DNSTransaction,
i: libc::uint16_t)
-> *mut JsonT
{
SCLogDebug!("rs_dns_log_json_query: tx_id={}, i={}", tx.id, i);
let index = i as usize;
for request in &tx.request {
if index < request.queries.len() {
let query = &request.queries[index];
let js = Json::object();
js.set_string("type", "query");
js.set_integer("id", request.header.tx_id as u64);
js.set_string("rrname", query.name());
js.set_string("rrtype", &dns_rrtype_string(query.rrtype));
js.set_integer("tx_id", tx.id - 1);
return js.unwrap();
}
}
return std::ptr::null_mut();
}
fn dns_log_json_answer(header: &DNSHeader, answer: &DNSAnswerEntry)
-> Json
{
let js = Json::object();
js.set_string("type", "answer");
js.set_integer("id", header.tx_id as u64);
js.set_string("rcode", &dns_rcode_string(header.flags));
js.set_string("rrname", answer.name());
js.set_string("rrtype", &dns_rrtype_string(answer.rrtype));
js.set_integer("ttl", answer.ttl as u64);
match answer.rrtype {
DNS_RTYPE_A | DNS_RTYPE_AAAA => {
js.set_string("rdata", &dns_print_addr(&answer.data));
}
DNS_RTYPE_CNAME |
DNS_RTYPE_MX |
DNS_RTYPE_PTR => {
js.set_string("rdata", answer.data_to_string());
},
DNS_RTYPE_SSHFP => {
dns_log_sshfp(&js, &answer);
},
_ => {}
}
return js;
}
fn dns_log_json_failure(r: &DNSResponse, index: usize) -> * mut JsonT {
if index >= r.queries.len() {
return std::ptr::null_mut();
}
let ref query = r.queries[index];
let js = Json::object();
js.set_string("type", "answer");
js.set_integer("id", r.header.tx_id as u64);
js.set_string("rcode", &dns_rcode_string(r.header.flags));
js.set_string("rrname", std::str::from_utf8(&query.name[..]).unwrap());
return js.unwrap();
}
#[no_mangle]
pub extern "C" fn rs_dns_log_json_answer(tx: &mut DNSTransaction,
i: libc::uint16_t)
-> *mut JsonT
{
let index = i as usize;
for response in &tx.response {
if response.header.flags & 0x000f > 0 {
if index == 0 {
return dns_log_json_failure(response, index);
}
break;
}
if index >= response.answers.len() {
break;
}
let answer = &response.answers[index];
let js = dns_log_json_answer(&response.header, answer);
return js.unwrap();
}
return std::ptr::null_mut();
}
#[no_mangle]
pub extern "C" fn rs_dns_log_json_authority(tx: &mut DNSTransaction,
i: libc::uint16_t)
-> *mut JsonT
{
let index = i as usize;
for response in &tx.response {
if index >= response.authorities.len() {
break;
}
let answer = &response.authorities[index];
let js = dns_log_json_answer(&response.header, answer);
return js.unwrap();
}
return std::ptr::null_mut();
}

@ -15,26 +15,6 @@
* 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;

@ -19,7 +19,7 @@
use nom::{be_u8, be_u16, be_u32};
use nom;
use dns::*;
use dns::dns::*;
/// Parse a DNS header.
named!(pub dns_parse_header<DNSHeader>,
@ -147,19 +147,19 @@ pub fn dns_parse_response<'a>(slice: &'a [u8])
));
let response = closure!(&'a [u8], do_parse!(
header: dns_parse_header >>
queries: count!(apply!(dns_parse_query, slice),
header.questions as usize) >>
answers: count!(answer_parser, header.answer_rr as usize) >>
authorities: count!(answer_parser, header.authority_rr as usize) >>
(
DNSResponse{
header: header,
queries: queries,
answers: answers,
authorities: authorities,
}
)
header: dns_parse_header
>> queries: count!(apply!(dns_parse_query, slice),
header.questions as usize)
>> answers: count!(answer_parser, header.answer_rr as usize)
>> authorities: count!(answer_parser, header.authority_rr as usize)
>> (
DNSResponse{
header: header,
queries: queries,
answers: answers,
authorities: authorities,
}
)
))(slice);
return response;

@ -20,7 +20,9 @@ app-layer-dnp3.c app-layer-dnp3.h \
app-layer-dnp3-objects.c app-layer-dnp3-objects.h \
app-layer-dns-common.c app-layer-dns-common.h \
app-layer-dns-tcp.c app-layer-dns-tcp.h \
app-layer-dns-tcp-rust.c app-layer-dns-tcp-rust.h \
app-layer-dns-udp.c app-layer-dns-udp.h \
app-layer-dns-udp-rust.c app-layer-dns-udp-rust.h \
app-layer-enip.c app-layer-enip.h \
app-layer-enip-common.c app-layer-enip-common.h \
app-layer-events.c app-layer-events.h \

@ -124,6 +124,10 @@ enum {
DNS_DECODER_EVENT_STATE_MEMCAP_REACHED,
};
/** Opaque Rust types. */
typedef struct RSDNSState_ RSDNSState;
typedef struct RSDNSTransaction_ RSDNSTransaction;
/** \brief DNS packet header */
typedef struct DNSHeader_ {
uint16_t tx_id;

@ -0,0 +1,204 @@
/* Copyright (C) 2017 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "suricata-common.h"
#include "suricata.h"
#include "app-layer-protos.h"
#include "app-layer-detect-proto.h"
#include "app-layer-parser.h"
#include "app-layer-dns-common.h"
#ifdef HAVE_RUST
#include "app-layer-dns-tcp-rust.h"
#include "rust-dns-dns-gen.h"
static void RustDNSTCPParserRegisterTests(void);
static int RustDNSTCPParseRequest(Flow *f, void *state,
AppLayerParserState *pstate, uint8_t *input, uint32_t input_len,
void *local_data)
{
SCLogDebug("RustDNSTCPParseRequest");
return rs_dns_parse_request_tcp(f, state, pstate, input, input_len,
local_data);
}
static int RustDNSTCPParseResponse(Flow *f, void *state,
AppLayerParserState *pstate, uint8_t *input, uint32_t input_len,
void *local_data)
{
SCLogDebug("RustDNSTCPParseResponse");
return rs_dns_parse_response_tcp(f, state, pstate, input, input_len,
local_data);
}
static uint16_t RustDNSTCPProbe(uint8_t *input, uint32_t len, uint32_t *offset)
{
SCLogDebug("RustDNSTCPProbe");
if (len == 0 || len < sizeof(DNSHeader)) {
return ALPROTO_UNKNOWN;
}
// Validate and return ALPROTO_FAILED if needed.
if (!rs_dns_probe_tcp(input, len)) {
return ALPROTO_FAILED;
}
return ALPROTO_DNS;
}
static int RustDNSGetAlstateProgress(void *tx, uint8_t direction)
{
return rs_dns_tx_get_alstate_progress(tx, direction);
}
static uint64_t RustDNSGetTxCnt(void *alstate)
{
return rs_dns_state_get_tx_count(alstate);
}
static void *RustDNSGetTx(void *alstate, uint64_t tx_id)
{
return rs_dns_state_get_tx(alstate, tx_id);
}
static void RustDNSSetTxLogged(void *alstate, void *tx, uint32_t logger)
{
rs_dns_tx_set_logged(alstate, tx, logger);
}
static int RustDNSGetTxLogged(void *alstate, void *tx, uint32_t logger)
{
return rs_dns_tx_get_logged(alstate, tx, logger);
}
static void RustDNSStateTransactionFree(void *state, uint64_t tx_id)
{
rs_dns_state_tx_free(state, tx_id);
}
static int RustDNSStateHasTxDetectState(void *state)
{
return rs_dns_state_has_detect_state(state);
}
static DetectEngineState *RustDNSGetTxDetectState(void *tx)
{
return rs_dns_state_get_tx_detect_state(tx);
}
static int RustDNSSetTxDetectState(void *state, void *tx,
DetectEngineState *s)
{
rs_dns_state_set_tx_detect_state(state, tx, s);
return 0;
}
static int RustDNSHasEvents(void *state)
{
return rs_dns_state_has_events(state);
}
static AppLayerDecoderEvents *RustDNSGetEvents(void *state, uint64_t id)
{
return rs_dns_state_get_events(state, id);
}
void RegisterRustDNSTCPParsers(void)
{
const char *proto_name = "dns";
/** DNS */
if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
AppLayerProtoDetectRegisterProtocol(ALPROTO_DNS, proto_name);
if (RunmodeIsUnittests()) {
AppLayerProtoDetectPPRegister(IPPROTO_TCP, "53", ALPROTO_DNS, 0,
sizeof(DNSHeader) + 2, STREAM_TOSERVER, RustDNSTCPProbe,
NULL);
} else {
int have_cfg = AppLayerProtoDetectPPParseConfPorts("tcp",
IPPROTO_TCP, proto_name, ALPROTO_DNS, 0,
sizeof(DNSHeader) + 2, RustDNSTCPProbe, RustDNSTCPProbe);
/* if we have no config, we enable the default port 53 */
if (!have_cfg) {
SCLogWarning(SC_ERR_DNS_CONFIG, "no DNS TCP config found, "
"enabling DNS detection on "
"port 53.");
AppLayerProtoDetectPPRegister(IPPROTO_TCP, "53", ALPROTO_DNS, 0,
sizeof(DNSHeader) + 2, STREAM_TOSERVER, RustDNSTCPProbe,
RustDNSTCPProbe);
}
}
} else {
SCLogInfo("Protocol detection and parser disabled for %s protocol.",
proto_name);
return;
}
if (AppLayerParserConfParserEnabled("tcp", proto_name)) {
AppLayerParserRegisterParser(IPPROTO_TCP, ALPROTO_DNS, STREAM_TOSERVER,
RustDNSTCPParseRequest);
AppLayerParserRegisterParser(IPPROTO_TCP , ALPROTO_DNS, STREAM_TOCLIENT,
RustDNSTCPParseResponse);
AppLayerParserRegisterStateFuncs(IPPROTO_TCP, ALPROTO_DNS,
rs_dns_state_tcp_new, rs_dns_state_free);
AppLayerParserRegisterTxFreeFunc(IPPROTO_TCP, ALPROTO_DNS,
RustDNSStateTransactionFree);
AppLayerParserRegisterGetEventsFunc(IPPROTO_TCP, ALPROTO_DNS,
RustDNSGetEvents);
AppLayerParserRegisterHasEventsFunc(IPPROTO_TCP, ALPROTO_DNS,
RustDNSHasEvents);
AppLayerParserRegisterDetectStateFuncs(IPPROTO_TCP, ALPROTO_DNS,
RustDNSStateHasTxDetectState, RustDNSGetTxDetectState,
RustDNSSetTxDetectState);
AppLayerParserRegisterGetTx(IPPROTO_TCP, ALPROTO_DNS, RustDNSGetTx);
AppLayerParserRegisterGetTxCnt(IPPROTO_TCP, ALPROTO_DNS,
RustDNSGetTxCnt);
AppLayerParserRegisterLoggerFuncs(IPPROTO_TCP, ALPROTO_DNS,
RustDNSGetTxLogged, RustDNSSetTxLogged);
AppLayerParserRegisterGetStateProgressFunc(IPPROTO_TCP, ALPROTO_DNS,
RustDNSGetAlstateProgress);
AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_DNS,
rs_dns_state_progress_completion_status);
DNSAppLayerRegisterGetEventInfo(IPPROTO_TCP, ALPROTO_DNS);
} else {
SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
"still on.", proto_name);
}
#ifdef UNITTESTS
AppLayerParserRegisterProtocolUnittests(IPPROTO_TCP, ALPROTO_DNS,
RustDNSTCPParserRegisterTests);
#endif
return;
}
#ifdef UNITTESTS
#endif /* UNITTESTS */
void RustDNSTCPParserRegisterTests(void)
{
#if 0
UtRegisterTest("DNSTCPParserTestMultiRecord", DNSTCPParserTestMultiRecord);
#endif
}
#endif /* HAVE_RUST */

@ -0,0 +1,23 @@
/* Copyright (C) 2017 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#ifndef __APP_LAYER_DNS_TCP_RUST_H__
#define __APP_LAYER_DNS_TCP_RUST_H__
void RegisterRustDNSTCPParsers(void);
#endif /* !__APP_LAYER_DNS_TCP_RUST_H__ */

@ -47,6 +47,10 @@
#include "app-layer-dns-tcp.h"
#ifdef HAVE_RUST
#include "app-layer-dns-tcp-rust.h"
#endif
struct DNSTcpHeader_ {
uint16_t len;
uint16_t tx_id;
@ -693,7 +697,10 @@ static uint16_t DNSTcpProbeResponse(uint8_t *input, uint32_t len,
void RegisterDNSTCPParsers(void)
{
const char *proto_name = "dns";
#ifdef HAVE_RUST
RegisterRustDNSTCPParsers();
return;
#endif
/** DNS */
if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
AppLayerProtoDetectRegisterProtocol(ALPROTO_DNS, proto_name);

@ -0,0 +1,197 @@
/* Copyright (C) 2017 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "suricata-common.h"
#include "suricata.h"
#include "app-layer-protos.h"
#include "app-layer-detect-proto.h"
#include "app-layer-parser.h"
#include "app-layer-dns-common.h"
#ifdef HAVE_RUST
#include "app-layer-dns-udp-rust.h"
#include "rust-dns-dns-gen.h"
static int RustDNSUDPParseRequest(Flow *f, void *state,
AppLayerParserState *pstate, uint8_t *input, uint32_t input_len,
void *local_data)
{
return rs_dns_parse_request(f, state, pstate, input, input_len,
local_data);
}
static int RustDNSUDPParseResponse(Flow *f, void *state,
AppLayerParserState *pstate, uint8_t *input, uint32_t input_len,
void *local_data)
{
return rs_dns_parse_response(f, state, pstate, input, input_len,
local_data);
}
static uint16_t DNSUDPProbe(uint8_t *input, uint32_t len, uint32_t *offset)
{
if (len == 0 || len < sizeof(DNSHeader)) {
return ALPROTO_UNKNOWN;
}
// Validate and return ALPROTO_FAILED if needed.
if (!rs_dns_probe(input, len)) {
return ALPROTO_FAILED;
}
return ALPROTO_DNS;
}
static int RustDNSGetAlstateProgress(void *tx, uint8_t direction)
{
return rs_dns_tx_get_alstate_progress(tx, direction);
}
static uint64_t RustDNSGetTxCnt(void *alstate)
{
return rs_dns_state_get_tx_count(alstate);
}
static void *RustDNSGetTx(void *alstate, uint64_t tx_id)
{
return rs_dns_state_get_tx(alstate, tx_id);
}
static void RustDNSSetTxLogged(void *alstate, void *tx, uint32_t logger)
{
rs_dns_tx_set_logged(alstate, tx, logger);
}
static int RustDNSGetTxLogged(void *alstate, void *tx, uint32_t logger)
{
return rs_dns_tx_get_logged(alstate, tx, logger);
}
static void RustDNSStateTransactionFree(void *state, uint64_t tx_id)
{
rs_dns_state_tx_free(state, tx_id);
}
static int RustDNSStateHasTxDetectState(void *state)
{
return rs_dns_state_has_detect_state(state);
}
static DetectEngineState *RustDNSGetTxDetectState(void *tx)
{
return rs_dns_state_get_tx_detect_state(tx);
}
static int RustDNSSetTxDetectState(void *state, void *tx,
DetectEngineState *s)
{
rs_dns_state_set_tx_detect_state(state, tx, s);
return 0;
}
static int RustDNSHasEvents(void *state)
{
return rs_dns_state_has_events(state);
}
static AppLayerDecoderEvents *RustDNSGetEvents(void *state, uint64_t id)
{
return rs_dns_state_get_events(state, id);
}
void RegisterRustDNSUDPParsers(void)
{
const char *proto_name = "dns";
/** DNS */
if (AppLayerProtoDetectConfProtoDetectionEnabled("udp", proto_name)) {
AppLayerProtoDetectRegisterProtocol(ALPROTO_DNS, proto_name);
if (RunmodeIsUnittests()) {
AppLayerProtoDetectPPRegister(IPPROTO_UDP, "53", ALPROTO_DNS, 0,
sizeof(DNSHeader), STREAM_TOSERVER, DNSUDPProbe,
NULL);
} else {
int have_cfg = AppLayerProtoDetectPPParseConfPorts("udp",
IPPROTO_UDP, proto_name, ALPROTO_DNS, 0, sizeof(DNSHeader),
DNSUDPProbe, NULL);
/* If no config, enable on port 53. */
if (!have_cfg) {
#ifndef AFLFUZZ_APPLAYER
SCLogWarning(SC_ERR_DNS_CONFIG, "no DNS UDP config found, "
"enabling DNS detection on port 53.");
#endif
AppLayerProtoDetectPPRegister(IPPROTO_UDP, "53", ALPROTO_DNS,
0, sizeof(DNSHeader), STREAM_TOSERVER,
DNSUDPProbe, NULL);
}
}
} else {
SCLogInfo("Protocol detection and parser disabled for %s protocol.",
proto_name);
return;
}
if (AppLayerParserConfParserEnabled("udp", proto_name)) {
AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_DNS, STREAM_TOSERVER,
RustDNSUDPParseRequest);
AppLayerParserRegisterParser(IPPROTO_UDP, ALPROTO_DNS, STREAM_TOCLIENT,
RustDNSUDPParseResponse);
AppLayerParserRegisterStateFuncs(IPPROTO_UDP, ALPROTO_DNS,
rs_dns_state_new, rs_dns_state_free);
AppLayerParserRegisterTxFreeFunc(IPPROTO_UDP, ALPROTO_DNS,
RustDNSStateTransactionFree);
AppLayerParserRegisterGetEventsFunc(IPPROTO_UDP, ALPROTO_DNS,
RustDNSGetEvents);
AppLayerParserRegisterHasEventsFunc(IPPROTO_UDP, ALPROTO_DNS,
RustDNSHasEvents);
AppLayerParserRegisterDetectStateFuncs(IPPROTO_UDP, ALPROTO_DNS,
RustDNSStateHasTxDetectState, RustDNSGetTxDetectState,
RustDNSSetTxDetectState);
AppLayerParserRegisterGetTx(IPPROTO_UDP, ALPROTO_DNS, RustDNSGetTx);
AppLayerParserRegisterGetTxCnt(IPPROTO_UDP, ALPROTO_DNS,
RustDNSGetTxCnt);
AppLayerParserRegisterLoggerFuncs(IPPROTO_UDP, ALPROTO_DNS,
RustDNSGetTxLogged, RustDNSSetTxLogged);
AppLayerParserRegisterGetStateProgressFunc(IPPROTO_UDP, ALPROTO_DNS,
RustDNSGetAlstateProgress);
AppLayerParserRegisterGetStateProgressCompletionStatus(ALPROTO_DNS,
rs_dns_state_progress_completion_status);
DNSAppLayerRegisterGetEventInfo(IPPROTO_UDP, ALPROTO_DNS);
#if 0
DNSUDPConfigure();
#endif
} else {
SCLogInfo("Parsed disabled for %s protocol. Protocol detection"
"still on.", proto_name);
}
#if 0
#ifdef UNITTESTS
AppLayerParserRegisterProtocolUnittests(IPPROTO_UDP, ALPROTO_DNS,
DNSUDPParserRegisterTests);
#endif
#endif
}
#endif /* HAVE_RUST */

@ -0,0 +1,23 @@
/* Copyright (C) 2017 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#ifndef __APP_LAYER_DNS_UDP_RUST_H__
#define __APP_LAYER_DNS_UDP_RUST_H__
void RegisterRustDNSUDPParsers(void);
#endif /* !__APP_LAYER_DNS_UDP_RUST_H__ */

@ -51,7 +51,7 @@
#include "app-layer-dns-udp.h"
#ifdef HAVE_RUST
#include "rust.h"
#include "app-layer-dns-udp-rust.h"
#endif
/** \internal
@ -390,9 +390,7 @@ void RegisterDNSUDPParsers(void)
const char *proto_name = "dns";
#ifdef HAVE_RUST
/* If DNS was implemented in Rust, we could call into the rust
* init function here. */
rs_dns_init();
return RegisterRustDNSUDPParsers();
#endif
/** DNS */

@ -48,6 +48,10 @@
#include "util-unittest-helper.h"
#include "util-validate.h"
#ifdef HAVE_RUST
#include "rust-dns-dns-gen.h"
#endif
/** \brief Do the content inspection & validation for a signature
*
* \param de_ctx Detection engine context
@ -67,14 +71,32 @@ int DetectEngineInspectDnsQueryName(ThreadVars *tv,
Flow *f, uint8_t flags, void *alstate,
void *txv, uint64_t tx_id)
{
DNSTransaction *tx = (DNSTransaction *)txv;
DNSQueryEntry *query = NULL;
uint8_t *buffer;
uint16_t buffer_len;
uint32_t buffer_len;
int r = 0;
SCLogDebug("start");
#ifdef HAVE_RUST
for (uint16_t i = 0;; i++) {
det_ctx->discontinue_matching = 0;
det_ctx->buffer_offset = 0;
det_ctx->inspection_recursion_counter = 0;
if (rs_dns_tx_get_query_name(txv, i, &buffer, &buffer_len)) {
r = DetectEngineContentInspection(de_ctx, det_ctx,
s, smd, f, buffer, buffer_len, 0,
DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE, NULL);
if (r == 1) {
break;
}
} else {
break;
}
}
#else
DNSTransaction *tx = (DNSTransaction *)txv;
DNSQueryEntry *query = NULL;
TAILQ_FOREACH(query, &tx->query_list, next) {
SCLogDebug("tx %p query %p", tx, query);
det_ctx->discontinue_matching = 0;
@ -93,6 +115,7 @@ int DetectEngineInspectDnsQueryName(ThreadVars *tv,
if (r == 1)
break;
}
#endif
return r;
}
@ -110,8 +133,23 @@ static void PrefilterTxDnsQuery(DetectEngineThreadCtx *det_ctx,
const uint64_t idx, const uint8_t flags)
{
SCEnter();
const MpmCtx *mpm_ctx = (MpmCtx *)pectx;
#ifdef HAVE_RUST
uint8_t *buffer;
uint32_t buffer_len;
for (uint16_t i = 0; i < 0xffff; i++) {
if (rs_dns_tx_get_query_name(txv, i, &buffer, &buffer_len)) {
if (buffer_len >= mpm_ctx->minlen) {
(void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx,
&det_ctx->mtcu, &det_ctx->pmq,
buffer, buffer_len);
}
} else {
break;
}
}
#else
DNSTransaction *tx = (DNSTransaction *)txv;
DNSQueryEntry *query = NULL;
@ -128,6 +166,7 @@ static void PrefilterTxDnsQuery(DetectEngineThreadCtx *det_ctx,
buffer, buffer_len);
}
}
#endif
}
int PrefilterTxDnsQueryRegister(SigGroupHead *sgh, MpmCtx *mpm_ctx)

@ -164,6 +164,10 @@ static void LogAnswer(LogDnsLogThread *aft, char *timebuf, char *srcip, char *ds
static int LogDnsLogger(ThreadVars *tv, void *data, const Packet *p,
Flow *f, void *state, void *tx, uint64_t tx_id, uint8_t direction)
{
#ifdef HAVE_RUST
SCLogNotice("LogDnsLogger not implemented for Rust DNS.");
return 0;
#endif
LogDnsLogThread *aft = (LogDnsLogThread *)data;
DNSTransaction *dns_tx = (DNSTransaction *)tx;
SCLogDebug("pcap_cnt %"PRIu64, p->pcap_cnt);

@ -53,6 +53,10 @@
#ifdef HAVE_LIBJANSSON
#ifdef HAVE_RUST
#include "rust-dns-log-gen.h"
#endif
/* we can do query logging as well, but it's disabled for now as the
* TX id handling doesn't expect it */
#define QUERY 0
@ -260,6 +264,7 @@ typedef struct LogDnsLogThread_ {
MemBuffer *buffer;
} LogDnsLogThread;
#ifndef HAVE_RUST
static int DNSRRTypeEnabled(uint16_t type, uint64_t flags)
{
if (likely(flags == ~0UL)) {
@ -387,7 +392,9 @@ static int DNSRRTypeEnabled(uint16_t type, uint64_t flags)
return 0;
}
}
#endif
#ifndef HAVE_RUST
static void LogQuery(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx,
uint64_t tx_id, DNSQueryEntry *entry) __attribute__((nonnull));
@ -435,7 +442,9 @@ static void LogQuery(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx,
OutputJSONBuffer(js, aft->dnslog_ctx->file_ctx, &aft->buffer);
json_object_del(js, "dns");
}
#endif
#ifndef HAVE_RUST
static void OutputAnswer(LogDnsLogThread *aft, json_t *djs,
DNSTransaction *tx, DNSAnswerEntry *entry) __attribute__((nonnull));
@ -546,7 +555,9 @@ static void OutputAnswer(LogDnsLogThread *aft, json_t *djs,
return;
}
#endif
#ifndef HAVE_RUST
static void OutputFailure(LogDnsLogThread *aft, json_t *djs,
DNSTransaction *tx, DNSQueryEntry *entry) __attribute__((nonnull));
@ -588,7 +599,9 @@ static void OutputFailure(LogDnsLogThread *aft, json_t *djs,
return;
}
#endif
#ifndef HAVE_RUST
static void LogAnswers(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx, uint64_t tx_id)
{
@ -616,6 +629,7 @@ static void LogAnswers(LogDnsLogThread *aft, json_t *js, DNSTransaction *tx, uin
}
}
#endif
static int JsonDnsLoggerToServer(ThreadVars *tv, void *thread_data,
const Packet *p, Flow *f, void *alstate, void *txptr, uint64_t tx_id)
@ -624,9 +638,30 @@ static int JsonDnsLoggerToServer(ThreadVars *tv, void *thread_data,
LogDnsLogThread *td = (LogDnsLogThread *)thread_data;
LogDnsFileCtx *dnslog_ctx = td->dnslog_ctx;
DNSTransaction *tx = txptr;
json_t *js;
if (unlikely(dnslog_ctx->flags & LOG_QUERIES) == 0) {
return TM_ECODE_OK;
}
#ifdef HAVE_RUST
for (uint16_t i = 0; i < 0xffff; i++) {
js = CreateJSONHeader((Packet *)p, 1, "dns");
if (unlikely(js == NULL)) {
return TM_ECODE_OK;
}
json_t *dns = rs_dns_log_json_query(txptr, i);
if (unlikely(dns == NULL)) {
json_decref(js);
break;
}
json_object_set_new(js, "dns", dns);
MemBufferReset(td->buffer);
OutputJSONBuffer(js, td->dnslog_ctx->file_ctx, &td->buffer);
json_decref(js);
}
#else
DNSTransaction *tx = txptr;
if (likely(dnslog_ctx->flags & LOG_QUERIES) != 0) {
DNSQueryEntry *query = NULL;
TAILQ_FOREACH(query, &tx->query_list, next) {
@ -639,6 +674,7 @@ static int JsonDnsLoggerToServer(ThreadVars *tv, void *thread_data,
json_decref(js);
}
}
#endif
SCReturnInt(TM_ECODE_OK);
}
@ -650,19 +686,48 @@ static int JsonDnsLoggerToClient(ThreadVars *tv, void *thread_data,
LogDnsLogThread *td = (LogDnsLogThread *)thread_data;
LogDnsFileCtx *dnslog_ctx = td->dnslog_ctx;
DNSTransaction *tx = txptr;
json_t *js;
if (likely(dnslog_ctx->flags & LOG_ANSWERS) != 0) {
js = CreateJSONHeader((Packet *)p, 0, "dns");
if (unlikely(js == NULL))
return TM_ECODE_OK;
if (unlikely(dnslog_ctx->flags & LOG_ANSWERS) == 0) {
return TM_ECODE_OK;
}
LogAnswers(td, js, tx, tx_id);
js = CreateJSONHeader((Packet *)p, 0, "dns");
json_decref(js);
#if HAVE_RUST
/* Log answers. */
for (uint16_t i = 0; i < 0xffff; i++) {
json_t *answer = rs_dns_log_json_answer(txptr, i);
if (answer == NULL) {
break;
}
json_object_set_new(js, "dns", answer);
MemBufferReset(td->buffer);
OutputJSONBuffer(js, td->dnslog_ctx->file_ctx, &td->buffer);
json_object_del(js, "dns");
}
/* Log authorities. */
for (uint16_t i = 0; i < 0xffff; i++) {
json_t *answer = rs_dns_log_json_authority(txptr, i);
if (answer == NULL) {
break;
}
json_object_set_new(js, "dns", answer);
MemBufferReset(td->buffer);
OutputJSONBuffer(js, td->dnslog_ctx->file_ctx, &td->buffer);
json_object_del(js, "dns");
}
#else
DNSTransaction *tx = txptr;
if (unlikely(js == NULL))
return TM_ECODE_OK;
LogAnswers(td, js, tx, tx_id);
#endif
json_decref(js);
SCReturnInt(TM_ECODE_OK);
}

@ -27,6 +27,4 @@ typedef struct SuricataContext_ {
void (*AppLayerDecoderEventsFreeEvents)(AppLayerDecoderEvents **);
} SuricataContext;
void rs_dns_init(void);
#endif /* !__RUST_H__ */

@ -175,7 +175,7 @@
#ifdef HAVE_RUST
#include "rust.h"
#include "rust-core.h"
#include "rust-core-gen.h"
#endif
/*

@ -44,7 +44,7 @@
#include "util-syslog.h"
#ifdef HAVE_RUST
#include "rust-log.h"
#include "rust-log-gen.h"
#endif
#include "conf.h"

@ -58,11 +58,42 @@
#include "util-lua-common.h"
#include "util-lua-dns.h"
#ifdef HAVE_RUST
#include "rust-dns-dns-gen.h"
#endif
static int DnsGetDnsRrname(lua_State *luastate)
{
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
#ifdef HAVE_RUST
RSDNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
for (uint16_t i = 0;; i++) {
uint32_t buf_len;
uint8_t *buf;
if (!rs_dns_tx_get_query_name(tx, i, &buf, &buf_len)) {
break;
}
char *rrname = BytesToString(buf, buf_len);
if (rrname != NULL) {
size_t input_len = strlen(rrname);
/* sanity check */
if (input_len > (size_t)(2 * buf_len)) {
SCFree(rrname);
return LuaCallbackError(luastate, "invalid length");
}
int ret = LuaPushStringBuffer(luastate, (uint8_t *)rrname,
input_len);
SCFree(rrname);
return ret;
}
}
#else
DNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
@ -85,7 +116,7 @@ static int DnsGetDnsRrname(lua_State *luastate)
return ret;
}
}
#endif
return LuaCallbackError(luastate, "no query");
}
@ -93,12 +124,19 @@ static int DnsGetTxid(lua_State *luastate)
{
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
#ifdef HAVE_RUST
RSDNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
uint16_t tx_id = rs_dns_tx_get_tx_id(tx);
lua_pushinteger(luastate, tx_id);
#else
DNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
lua_pushinteger(luastate, tx->tx_id);
#endif
return 1;
}
@ -106,15 +144,24 @@ static int DnsGetRcode(lua_State *luastate)
{
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
uint16_t rcode = 0;
#ifdef HAVE_RUST
RSDNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL) {
return LuaCallbackError(luastate, "internal error: no tx");
}
uint16_t flags = rs_dns_tx_get_response_flags(tx);
rcode = flags & 0x000f;
#else
DNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
if (tx->rcode) {
char rcode[16] = "";
DNSCreateRcodeString(tx->rcode, rcode, sizeof(rcode));
return LuaPushStringBuffer(luastate, (const uint8_t *)rcode, strlen(rcode));
rcode = tx->rcode;
#endif
if (rcode) {
char rcode_str[16] = "";
DNSCreateRcodeString(rcode, rcode_str, sizeof(rcode_str));
return LuaPushStringBuffer(luastate, (const uint8_t *)rcode_str, strlen(rcode_str));
} else {
return 0;
}
@ -124,17 +171,77 @@ static int DnsGetRecursionDesired(lua_State *luastate)
{
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
#ifdef HAVE_RUST
RSDNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
uint16_t flags = rs_dns_tx_get_response_flags(tx);
int recursion_desired = flags & 0x0080 ? 1 : 0;
lua_pushboolean(luastate, recursion_desired);
#else
DNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
lua_pushboolean(luastate, tx->recursion_desired);
#endif
return 1;
}
static int DnsGetQueryTable(lua_State *luastate)
{
#ifdef HAVE_RUST
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
RSDNSTransaction *tx = LuaStateGetTX(luastate);
if (tx == NULL)
return LuaCallbackError(luastate, "internal error: no tx");
lua_newtable(luastate);
uint8_t *name;
uint32_t name_len;
for (uint16_t i = 0;; i++) {
if (!rs_dns_tx_get_query_name(tx, i, &name, &name_len)) {
break;
}
lua_pushinteger(luastate, i);
lua_newtable(luastate);
uint16_t rrtype;
if (rs_dns_tx_get_query_rrtype(tx, i, &rrtype)) {
char s_rrtype[16] = "";
DNSCreateTypeString(rrtype, s_rrtype, sizeof(s_rrtype));
lua_pushstring(luastate, "type");
lua_pushstring(luastate, s_rrtype);
lua_settable(luastate, -3);
}
char *s = BytesToString(name, name_len);
if (s != NULL) {
size_t slen = strlen(s);
if (slen > name_len * 2) {
SCFree(s);
return LuaCallbackError(luastate, "invalid length");
}
lua_pushstring(luastate, "rrname");
LuaPushStringBuffer(luastate, (uint8_t *)s, slen);
lua_settable(luastate, -3);
SCFree(s);
}
lua_pushinteger(luastate, i++);
}
return 1;
#else
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
@ -178,10 +285,15 @@ static int DnsGetQueryTable(lua_State *luastate)
}
return 1;
#endif
}
static int DnsGetAnswerTable(lua_State *luastate)
{
#ifdef HAVE_RUST
SCLogNotice("DnsGetAnswerTable not implemented for Rust DNS.");
return 1;
#else
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
@ -242,10 +354,15 @@ static int DnsGetAnswerTable(lua_State *luastate)
}
return 1;
#endif
}
static int DnsGetAuthorityTable(lua_State *luastate)
{
#ifdef HAVE_RUST
SCLogNotice("DnsGetAuthorityTable not implemented for Rust DNS");
return 1;
#else
if (!(LuaStateNeedProto(luastate, ALPROTO_DNS)))
return LuaCallbackError(luastate, "error: protocol not dns");
@ -293,6 +410,7 @@ static int DnsGetAuthorityTable(lua_State *luastate)
}
return 1;
#endif
}

Loading…
Cancel
Save