dns: parse and log fields for SOA record type

Added `dns_parse_rdata_soa` to parse SOA fields into an `DNSRDataSOA`
struct.

Added logging for answer and authority SOA records in both version
1 & 2, as well as grouped formats.
pull/5361/head
Simon Dugas 5 years ago committed by Victor Julien
parent af498fd840
commit 7f26246ce1

@ -233,7 +233,20 @@ pub struct DNSQueryEntry {
#[derive(Debug,PartialEq)]
pub struct DNSRDataSOA {
pub data: Vec<u8>,
/// Primary name server for this zone
pub mname: Vec<u8>,
/// Authority's mailbox
pub rname: Vec<u8>,
/// Serial version number
pub serial: u32,
/// Refresh interval (seconds)
pub refresh: u32,
/// Retry interval (seconds)
pub retry: u32,
/// Upper time limit until zone is no longer authoritative (seconds)
pub expire: u32,
/// Minimum ttl for records in this zone (seconds)
pub minimum: u32,
}
#[derive(Debug,PartialEq)]

@ -395,7 +395,23 @@ pub fn dns_print_addr(addr: &Vec<u8>) -> std::string::String {
}
}
/// Log the SSHPF in an DNSAnswerEntry.
/// Log SOA section fields.
fn dns_log_soa(soa: &DNSRDataSOA) -> Result<JsonBuilder, JsonError> {
let mut js = JsonBuilder::new_object();
js.set_string_from_bytes("mname", &soa.mname)?;
js.set_string_from_bytes("rname", &soa.rname)?;
js.set_uint("serial", soa.serial as u64)?;
js.set_uint("refresh", soa.refresh as u64)?;
js.set_uint("retry", soa.retry as u64)?;
js.set_uint("expire", soa.expire as u64)?;
js.set_uint("minimum", soa.minimum as u64)?;
js.close()?;
return Ok(js);
}
/// Log SSHFP section fields.
fn dns_log_sshfp(sshfp: &DNSRDataSSHFP) -> Result<JsonBuilder, JsonError>
{
let mut js = JsonBuilder::new_object();
@ -431,6 +447,9 @@ fn dns_log_json_answer_detail(answer: &DNSAnswerEntry) -> Result<JsonBuilder, Js
DNSRData::PTR(bytes) => {
jsa.set_string_from_bytes("rdata", &bytes)?;
}
DNSRData::SOA(soa) => {
jsa.set_object("soa", &dns_log_soa(&soa)?)?;
}
DNSRData::SSHFP(sshfp) => {
jsa.set_object("sshfp", &dns_log_sshfp(&sshfp)?)?;
}
@ -505,6 +524,15 @@ fn dns_log_json_answer(js: &mut JsonBuilder, response: &DNSResponse, flags: u64)
a.append_string_from_bytes(&bytes)?;
}
},
DNSRData::SOA(soa) => {
if !answer_types.contains_key(&type_string) {
answer_types.insert(type_string.to_string(),
JsonBuilder::new_array());
}
if let Some(a) = answer_types.get_mut(&type_string) {
a.append_object(&dns_log_soa(&soa)?)?;
}
},
DNSRData::SSHFP(sshfp) => {
if !answer_types.contains_key(&type_string) {
answer_types.insert(type_string.to_string(),
@ -662,6 +690,9 @@ fn dns_log_json_answer_v1(header: &DNSHeader, answer: &DNSAnswerEntry)
DNSRData::PTR(bytes) => {
js.set_string_from_bytes("rdata", &bytes)?;
}
DNSRData::SOA(soa) => {
js.set_object("soa", &dns_log_soa(&soa)?)?;
}
DNSRData::SSHFP(sshfp) => {
js.set_object("sshfp", &dns_log_sshfp(&sshfp)?)?;
}

@ -186,9 +186,9 @@ pub extern "C" fn rs_dns_lua_get_answer_table(clua: &mut CLuaState,
}
},
DNSRData::SOA(ref soa) => {
if soa.data.len() > 0 {
if soa.mname.len() > 0 {
lua.pushstring("addr");
lua.pushstring(&String::from_utf8_lossy(&soa.data));
lua.pushstring(&String::from_utf8_lossy(&soa.mname));
lua.settable(-3);
}
},

@ -280,8 +280,25 @@ fn dns_parse_rdata_ptr<'a>(input: &'a [u8], message: &'a [u8])
fn dns_parse_rdata_soa<'a>(input: &'a [u8], message: &'a [u8])
-> IResult<&'a [u8], DNSRData> {
dns_parse_name(input, message).map(|(input, name)|
(input, DNSRData::SOA(DNSRDataSOA{data: name})))
do_parse!(
input,
mname: call!(dns_parse_name, message) >>
rname: call!(dns_parse_name, message) >>
serial: be_u32 >>
refresh: be_u32 >>
retry: be_u32 >>
expire: be_u32 >>
minimum: be_u32 >>
(DNSRData::SOA(DNSRDataSOA{
mname,
rname,
serial,
refresh,
retry,
expire,
minimum,
}))
)
}
fn dns_parse_rdata_mx<'a>(input: &'a [u8], message: &'a [u8])
@ -587,6 +604,71 @@ mod tests {
}
}
#[test]
fn test_dns_parse_response_nxdomain_soa() {
// DNS response with an SOA authority record from
// dns-udp-nxdomain-soa.pcap.
let pkt: &[u8] = &[
0x82, 0x95, 0x81, 0x83, 0x00, 0x01, /* j....... */
0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x03, 0x64, /* .......d */
0x6e, 0x65, 0x04, 0x6f, 0x69, 0x73, 0x66, 0x03, /* ne.oisf. */
0x6e, 0x65, 0x74, 0x00, 0x00, 0x01, 0x00, 0x01, /* net..... */
0xc0, 0x10, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, /* ........ */
0x03, 0x83, 0x00, 0x45, 0x06, 0x6e, 0x73, 0x2d, /* ...E.ns- */
0x31, 0x31, 0x30, 0x09, 0x61, 0x77, 0x73, 0x64, /* 110.awsd */
0x6e, 0x73, 0x2d, 0x31, 0x33, 0x03, 0x63, 0x6f, /* ns-13.co */
0x6d, 0x00, 0x11, 0x61, 0x77, 0x73, 0x64, 0x6e, /* m..awsdn */
0x73, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x6d, 0x61, /* s-hostma */
0x73, 0x74, 0x65, 0x72, 0x06, 0x61, 0x6d, 0x61, /* ster.ama */
0x7a, 0x6f, 0x6e, 0xc0, 0x3b, 0x00, 0x00, 0x00, /* zon.;... */
0x01, 0x00, 0x00, 0x1c, 0x20, 0x00, 0x00, 0x03, /* .... ... */
0x84, 0x00, 0x12, 0x75, 0x00, 0x00, 0x01, 0x51, /* ...u...Q */
0x80, 0x00, 0x00, 0x29, 0x02, 0x00, 0x00, 0x00, /* ...).... */
0x00, 0x00, 0x00, 0x00 /* .... */
];
let res = dns_parse_response(pkt);
match res {
Ok((rem, response)) => {
// For now we have some remainder data as there is an
// additional record type we don't parse yet.
assert!(rem.len() > 0);
assert_eq!(response.header, DNSHeader{
tx_id: 0x8295,
flags: 0x8183,
questions: 1,
answer_rr: 0,
authority_rr: 1,
additional_rr: 1,
});
assert_eq!(response.authorities.len(), 1);
let authority = &response.authorities[0];
assert_eq!(authority.name,
"oisf.net".as_bytes().to_vec());
assert_eq!(authority.rrtype, 6);
assert_eq!(authority.rrclass, 1);
assert_eq!(authority.ttl, 899);
assert_eq!(authority.data,
DNSRData::SOA(DNSRDataSOA{
mname: "ns-110.awsdns-13.com".as_bytes().to_vec(),
rname: "awsdns-hostmaster.amazon.com".as_bytes().to_vec(),
serial: 1,
refresh: 7200,
retry: 900,
expire: 1209600,
minimum: 86400,
}));
},
_ => {
assert!(false);
}
}
}
#[test]
fn test_dns_parse_rdata_sshfp() {
// Dummy data since we don't have a pcap sample.

Loading…
Cancel
Save