use nom::IResult;
use nom::combinator::rest;
use nom::error::ErrorKind;
use nom::multi::length_data;
use nom::number::streaming::{be_u8, be_u16, be_u32};
use nom;
use crate::dns::dns::*;
named!(pub dns_parse_header<DNSHeader>,
do_parse!(
tx_id: be_u16 >>
flags: be_u16 >>
questions: be_u16 >>
answer_rr: be_u16 >>
authority_rr: be_u16 >>
additional_rr: be_u16 >>
(
DNSHeader{
tx_id: tx_id,
flags: flags,
questions: questions,
answer_rr: answer_rr,
authority_rr: authority_rr,
additional_rr: additional_rr,
}
)
)
);
pub fn dns_parse_name<'a, 'b>(start: &'b [u8],
message: &'b [u8])
-> IResult<&'b [u8], Vec<u8>> {
let mut pos = start;
let mut pivot = start;
let mut name: Vec<u8> = Vec::with_capacity(32);
let mut count = 0;
loop {
if pos.len() == 0 {
break;
}
let len = pos[0];
if len == 0x00 {
pos = &pos[1..];
break;
} else if len & 0b1100_0000 == 0 {
match length_data(be_u8)(pos) as IResult<&[u8],_> {
Ok((rem, label)) => {
if name.len() > 0 {
name.push('.' as u8);
}
name.extend(label);
pos = rem;
}
_ => {
return Err(nom::Err::Error(
error_position!(pos, ErrorKind::OctDigit)));
}
}
} else if len & 0b1100_0000 == 0b1100_0000 {
match be_u16(pos) as IResult<&[u8],_> {
Ok((rem, leader)) => {
let offset = usize::from(leader) & 0x3fff;
if offset > message.len() {
return Err(nom::Err::Error(
error_position!(pos, ErrorKind::OctDigit)));
}
pos = &message[offset..];
if pivot == start {
pivot = rem;
}
}
_ => {
return Err(nom::Err::Error(
error_position!(pos, ErrorKind::OctDigit)));
}
}
} else {
return Err(nom::Err::Error(
error_position!(pos, ErrorKind::OctDigit)));
}
count += 1;
if count > 255 {
return Err(nom::Err::Error(
error_position!(pos, ErrorKind::OctDigit)));
}
}
if pivot.len() != start.len() {
return Ok((pivot, name));
}
return Ok((pos, name));
}
fn dns_parse_answer<'a>(slice: &'a [u8], message: &'a [u8], count: usize)
-> IResult<&'a [u8], Vec<DNSAnswerEntry>> {
let mut answers = Vec::new();
let mut input = slice;
for _ in 0..count {
match do_parse!(
input,
name: call!(dns_parse_name, message) >>
rrtype: be_u16 >>
rrclass: be_u16 >>
ttl: be_u32 >>
data_len: be_u16 >>
data: take!(data_len) >>
(
name,
rrtype,
rrclass,
ttl,
data
)
) {
Ok((rem, val)) => {
let name = val.0;
let rrtype = val.1;
let rrclass = val.2;
let ttl = val.3;
let data = val.4;
let n = match rrtype {
DNS_RECORD_TYPE_TXT => {
32767
}
_ => {
1
}
};
let result: IResult<&'a [u8], Vec<DNSRData>> =
do_parse!(
data,
rdata: many_m_n!(1, n,
complete!(call!(dns_parse_rdata, message, rrtype)))
>> (rdata)
);
match result {
Ok((_, rdatas)) => {
for rdata in rdatas {
answers.push(DNSAnswerEntry{
name: name.clone(),
rrtype: rrtype,
rrclass: rrclass,
ttl: ttl,
data: rdata,
});
}
}
Err(e) => { return Err(e); }
}
input = rem;
}
Err(e) => { return Err(e); }
}
}
return Ok((input, answers));
}
pub fn dns_parse_response<'a>(slice: &'a [u8])
-> IResult<&[u8], DNSResponse> {
do_parse!(
slice,
header: dns_parse_header
>> queries: count!(
call!(dns_parse_query, slice), header.questions as usize)
>> answers: call!(
dns_parse_answer, slice, header.answer_rr as usize)
>> authorities: call!(
dns_parse_answer, slice, header.authority_rr as usize)
>> (
DNSResponse{
header: header,
queries: queries,
answers: answers,
authorities: authorities,
}
)
)
}
pub fn dns_parse_query<'a>(input: &'a [u8],
message: &'a [u8])
-> IResult<&'a [u8], DNSQueryEntry> {
do_parse!(
input,
name: call!(dns_parse_name, message) >>
rrtype: be_u16 >>
rrclass: be_u16 >>
(
DNSQueryEntry{
name: name,
rrtype: rrtype,
rrclass: rrclass,
}
)
)
}
fn dns_parse_rdata_a<'a>(input: &'a [u8]) -> IResult<&'a [u8], DNSRData> {
rest(input).map(|(input, data)| (input, DNSRData::A(data.to_vec())))
}
fn dns_parse_rdata_aaaa<'a>(input: &'a [u8]) -> IResult<&'a [u8], DNSRData> {
rest(input).map(|(input, data)| (input, DNSRData::AAAA(data.to_vec())))
}
fn dns_parse_rdata_cname<'a>(input: &'a [u8], message: &'a [u8])
-> IResult<&'a [u8], DNSRData> {
dns_parse_name(input, message).map(|(input, name)|
(input, DNSRData::CNAME(name)))
}
fn dns_parse_rdata_ptr<'a>(input: &'a [u8], message: &'a [u8])
-> IResult<&'a [u8], DNSRData> {
dns_parse_name(input, message).map(|(input, name)|
(input, DNSRData::PTR(name)))
}
fn dns_parse_rdata_soa<'a>(input: &'a [u8], message: &'a [u8])
-> IResult<&'a [u8], DNSRData> {
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])
-> IResult<&'a [u8], DNSRData> {
do_parse!(
input,
be_u16 >>
name: call!(dns_parse_name, message) >>
(DNSRData::MX(name))
)
}
fn dns_parse_rdata_txt<'a>(input: &'a [u8])
-> IResult<&'a [u8], DNSRData> {
do_parse!(
input,
len: be_u8 >>
txt: take!(len) >>
(DNSRData::TXT(txt.to_vec()))
)
}
fn dns_parse_rdata_sshfp<'a>(input: &'a [u8])
-> IResult<&'a [u8], DNSRData> {
do_parse!(
input,
algo: be_u8 >>
fp_type: be_u8 >>
fingerprint: call!(rest) >>
(DNSRData::SSHFP(DNSRDataSSHFP{
algo,
fp_type,
fingerprint: fingerprint.to_vec()
}))
)
}
fn dns_parse_rdata_unknown<'a>(input: &'a [u8])
-> IResult<&'a [u8], DNSRData> {
rest(input).map(|(input, data)| (input, DNSRData::Unknown(data.to_vec())))
}
pub fn dns_parse_rdata<'a>(input: &'a [u8], message: &'a [u8], rrtype: u16)
-> IResult<&'a [u8], DNSRData>
{
match rrtype {
DNS_RECORD_TYPE_A => dns_parse_rdata_a(input),
DNS_RECORD_TYPE_AAAA => dns_parse_rdata_aaaa(input),
DNS_RECORD_TYPE_CNAME => dns_parse_rdata_cname(input, message),
DNS_RECORD_TYPE_PTR => dns_parse_rdata_ptr(input, message),
DNS_RECORD_TYPE_SOA => dns_parse_rdata_soa(input, message),
DNS_RECORD_TYPE_MX => dns_parse_rdata_mx(input, message),
DNS_RECORD_TYPE_TXT => dns_parse_rdata_txt(input),
DNS_RECORD_TYPE_SSHFP => dns_parse_rdata_sshfp(input),
_ => dns_parse_rdata_unknown(input),
}
}
pub fn dns_parse_request<'a>(input: &'a [u8]) -> IResult<&[u8], DNSRequest> {
do_parse!(
input,
header: dns_parse_header >>
queries: count!(call!(dns_parse_query, input),
header.questions as usize) >>
(
DNSRequest{
header: header,
queries: queries,
}
)
)
}
#[cfg(test)]
mod tests {
use crate::dns::dns::{DNSHeader,DNSAnswerEntry};
use crate::dns::parser::*;
#[test]
fn test_dns_parse_name() {
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 expected_remainder: &[u8] = &[0x00, 0x01, 0x00];
let (remainder,name) = dns_parse_name(buf, buf).unwrap();
assert_eq!("client-cf.dropbox.com".as_bytes(), &name[..]);
assert_eq!(remainder, expected_remainder);
}
#[test]
fn test_dns_parse_name_with_pointer() {
let buf: &[u8] = &[
0xd8, 0xcb, 0x8a, 0xed, 0xa1, 0x46, 0x00, 0x15 ,
0x17, 0x0d, 0x06, 0xf7, 0x08, 0x00, 0x45, 0x00 ,
0x00, 0x7b, 0x71, 0x6e, 0x00, 0x00, 0x39, 0x11 ,
0xf4, 0xd9, 0x08, 0x08, 0x08, 0x08, 0x0a, 0x10 ,
0x01, 0x0b, 0x00, 0x35, 0xe1, 0x8e, 0x00, 0x67 ,
0x60, 0x00, 0xef, 0x08, 0x81, 0x80, 0x00, 0x01 ,
0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x03, 0x77 ,
0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63 ,
0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03 ,
0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01 ,
0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00 ,
0x0e, 0x0f, 0x00, 0x02, 0xc0, 0x10, 0xc0, 0x10 ,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x2b ,
0x00, 0x04, 0xc0, 0x00, 0x4e, 0x19, 0xc0, 0x10 ,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x2b ,
0x00, 0x04, 0xc0, 0x00, 0x4e, 0x18, 0x00, 0x00 ,
0x29, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ,
0x00,
];
let message = &buf[42..];
let start1 = &buf[54..];
let res1 = dns_parse_name(start1, message);
assert_eq!(res1,
Ok((&start1[22..],
"www.suricata-ids.org".as_bytes().to_vec())));
let start2 = &buf[80..];
let res2 = dns_parse_name(start2, message);
assert_eq!(res2,
Ok((&start2[2..],
"www.suricata-ids.org".as_bytes().to_vec())));
let start3 = &buf[94..];
let res3 = dns_parse_name(start3, message);
assert_eq!(res3,
Ok((&start3[2..],
"suricata-ids.org".as_bytes().to_vec())));
let start4 = &buf[110..];
let res4 = dns_parse_name(start4, message);
assert_eq!(res4,
Ok((&start4[2..],
"suricata-ids.org".as_bytes().to_vec())));
}
#[test]
fn test_dns_parse_name_double_pointer() {
let buf: &[u8] = &[
0xd8, 0xcb, 0x8a, 0xed, 0xa1, 0x46, 0x00, 0x15 ,
0x17, 0x0d, 0x06, 0xf7, 0x08, 0x00, 0x45, 0x00 ,
0x00, 0x66, 0x5e, 0x20, 0x40, 0x00, 0x40, 0x11 ,
0xc6, 0x3b, 0x0a, 0x10, 0x01, 0x01, 0x0a, 0x10 ,
0x01, 0x0b, 0x00, 0x35, 0xc2, 0x21, 0x00, 0x52 ,
0x35, 0xc5, 0x0d, 0x4f, 0x81, 0x80, 0x00, 0x01 ,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x05, 0x62 ,
0x6c, 0x6f, 0x63, 0x6b, 0x07, 0x64, 0x72, 0x6f ,
0x70, 0x62, 0x6f, 0x78, 0x03, 0x63, 0x6f, 0x6d ,
0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00 ,
0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00 ,
0x0b, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x02 ,
0x67, 0x31, 0xc0, 0x12, 0xc0, 0x2f, 0x00, 0x01 ,
0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04 ,
0x2d, 0x3a, 0x46, 0x21
];
let message: &[u8] = &buf[42..];
let start: &[u8] = &buf[100..];
let res = dns_parse_name(start, message);
assert_eq!(res,
Ok((&start[2..],
"block.g1.dropbox.com".as_bytes().to_vec())));
}
#[test]
fn test_dns_parse_request() {
let pkt: &[u8] = &[
0x8d, 0x32, 0x01, 0x20, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x77,
0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63,
0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03,
0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00
];
let res = dns_parse_request(pkt);
match res {
Ok((rem, request)) => {
assert!(rem.len() > 0);
assert_eq!(request.header, DNSHeader {
tx_id: 0x8d32,
flags: 0x0120,
questions: 1,
answer_rr: 0,
authority_rr: 0,
additional_rr: 1,
});
assert_eq!(request.queries.len(), 1);
let query = &request.queries[0];
assert_eq!(query.name,
"www.suricata-ids.org".as_bytes().to_vec());
assert_eq!(query.rrtype, 1);
assert_eq!(query.rrclass, 1);
}
_ => {
assert!(false);
}
}
}
#[test]
fn test_dns_parse_response() {
let pkt: &[u8] = &[
0x8d, 0x32, 0x81, 0xa0, 0x00, 0x01,
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x77,
0x77, 0x77, 0x0c, 0x73, 0x75, 0x72, 0x69, 0x63,
0x61, 0x74, 0x61, 0x2d, 0x69, 0x64, 0x73, 0x03,
0x6f, 0x72, 0x67, 0x00, 0x00, 0x01, 0x00, 0x01,
0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00,
0x0d, 0xd8, 0x00, 0x12, 0x0c, 0x73, 0x75, 0x72,
0x69, 0x63, 0x61, 0x74, 0x61, 0x2d, 0x69, 0x64,
0x73, 0x03, 0x6f, 0x72, 0x67, 0x00, 0xc0, 0x32,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf4,
0x00, 0x04, 0xc0, 0x00, 0x4e, 0x18, 0xc0, 0x32,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf4,
0x00, 0x04, 0xc0, 0x00, 0x4e, 0x19
];
let res = dns_parse_response(pkt);
match res {
Ok((rem, response)) => {
assert_eq!(rem.len(), 0);
assert_eq!(response.header, DNSHeader{
tx_id: 0x8d32,
flags: 0x81a0,
questions: 1,
answer_rr: 3,
authority_rr: 0,
additional_rr: 0,
});
assert_eq!(response.answers.len(), 3);
let answer1 = &response.answers[0];
assert_eq!(answer1.name,
"www.suricata-ids.org".as_bytes().to_vec());
assert_eq!(answer1.rrtype, 5);
assert_eq!(answer1.rrclass, 1);
assert_eq!(answer1.ttl, 3544);
assert_eq!(answer1.data,
DNSRData::CNAME("suricata-ids.org".as_bytes().to_vec()));
let answer2 = &response.answers[1];
assert_eq!(answer2, &DNSAnswerEntry{
name: "suricata-ids.org".as_bytes().to_vec(),
rrtype: 1,
rrclass: 1,
ttl: 244,
data: DNSRData::A([192, 0, 78, 24].to_vec()),
});
let answer3 = &response.answers[2];
assert_eq!(answer3, &DNSAnswerEntry{
name: "suricata-ids.org".as_bytes().to_vec(),
rrtype: 1,
rrclass: 1,
ttl: 244,
data: DNSRData::A([192, 0, 78, 25].to_vec()),
})
},
_ => {
assert!(false);
}
}
}
#[test]
fn test_dns_parse_response_nxdomain_soa() {
let pkt: &[u8] = &[
0x82, 0x95, 0x81, 0x83, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x03, 0x64,
0x6e, 0x65, 0x04, 0x6f, 0x69, 0x73, 0x66, 0x03,
0x6e, 0x65, 0x74, 0x00, 0x00, 0x01, 0x00, 0x01,
0xc0, 0x10, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00,
0x03, 0x83, 0x00, 0x45, 0x06, 0x6e, 0x73, 0x2d,
0x31, 0x31, 0x30, 0x09, 0x61, 0x77, 0x73, 0x64,
0x6e, 0x73, 0x2d, 0x31, 0x33, 0x03, 0x63, 0x6f,
0x6d, 0x00, 0x11, 0x61, 0x77, 0x73, 0x64, 0x6e,
0x73, 0x2d, 0x68, 0x6f, 0x73, 0x74, 0x6d, 0x61,
0x73, 0x74, 0x65, 0x72, 0x06, 0x61, 0x6d, 0x61,
0x7a, 0x6f, 0x6e, 0xc0, 0x3b, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x1c, 0x20, 0x00, 0x00, 0x03,
0x84, 0x00, 0x12, 0x75, 0x00, 0x00, 0x01, 0x51,
0x80, 0x00, 0x00, 0x29, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
];
let res = dns_parse_response(pkt);
match res {
Ok((rem, response)) => {
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() {
let data: &[u8] = &[
0x02,
0x01,
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf6, 0x78, 0x90,
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf6, 0x78, 0x90
];
let res = dns_parse_rdata_sshfp(data);
match res {
Ok((rem, rdata)) => {
assert_eq!(rem.len(), 0);
match rdata {
DNSRData::SSHFP(sshfp) => {
assert_eq!(sshfp.algo, 2);
assert_eq!(sshfp.fp_type, 1);
assert_eq!(sshfp.fingerprint, &data[2..]);
},
_ => {
assert!(false);
}
}
},
_ => {
assert!(false);
}
}
}
}