return Ok(js);
}
-fn dns_log_json_answer_detail(answer: &DNSAnswerEntry) -> Result<JsonBuilder, JsonError> {
+/// Log a single DNS answer entry.
+///
+/// For items that may be array, such as TXT records, i will designate
+/// which entry to log.
+fn dns_log_json_answer_detail(answer: &DNSAnswerEntry, i: usize) -> Result<JsonBuilder, JsonError> {
let mut jsa = JsonBuilder::try_new_object()?;
jsa.set_string_from_bytes("rrname", &answer.name.value)?;
jsa.set_bool("rdata_truncated", true)?;
}
}
- DNSRData::TXT(bytes) | DNSRData::NULL(bytes) => {
+ DNSRData::TXT(txt) => {
+ if let Some(txt) = txt.get(i) {
+ jsa.set_string_from_bytes("rdata", txt)?;
+ } else {
+ debug_validate_fail!("txt entry does not exist");
+ }
+ }
+ DNSRData::NULL(bytes) => {
jsa.set_string_from_bytes("rdata", bytes)?;
}
DNSRData::SOA(soa) => {
a.append_string_from_bytes(&name.value)?;
}
}
- DNSRData::TXT(bytes) | DNSRData::NULL(bytes) => {
+ DNSRData::TXT(txt_strings) => {
+ if !answer_types.contains_key(&type_string) {
+ answer_types
+ .insert(type_string.to_string(), JsonBuilder::try_new_array()?);
+ }
+ if let Some(a) = answer_types.get_mut(&type_string) {
+ for txt in txt_strings {
+ a.append_string_from_bytes(txt)?;
+ }
+ }
+ }
+ DNSRData::NULL(bytes) => {
if !answer_types.contains_key(&type_string) {
answer_types
.insert(type_string.to_string(), JsonBuilder::try_new_array()?);
}
if flags & LOG_FORMAT_DETAILED != 0 {
- js_answers.append_object(&dns_log_json_answer_detail(answer)?)?;
+ match &answer.data {
+ DNSRData::TXT(asdf) => {
+ for i in 0..asdf.len() {
+ js_answers.append_object(&dns_log_json_answer_detail(answer, i)?)?;
+ }
+ }
+ _ => {
+ js_answers.append_object(&dns_log_json_answer_detail(answer, 0)?)?;
+ }
+ }
}
}
if !response.authorities.is_empty() {
js.open_array("authorities")?;
for auth in &response.authorities {
- let auth_detail = dns_log_json_answer_detail(auth)?;
- js.append_object(&auth_detail)?;
+ match &auth.data {
+ DNSRData::TXT(txt) => {
+ for i in 0..txt.len() {
+ let auth_detail = dns_log_json_answer_detail(auth, i)?;
+ js.append_object(&auth_detail)?;
+ }
+ }
+ _ => {
+ let auth_detail = dns_log_json_answer_detail(auth, 0)?;
+ js.append_object(&auth_detail)?;
+ }
+ }
}
js.close()?;
}
js.open_array("additionals")?;
is_js_open = true;
}
- let add_detail = dns_log_json_answer_detail(add)?;
- js.append_object(&add_detail)?;
+ match &add.data {
+ DNSRData::TXT(txt) => {
+ for i in 0..txt.len() {
+ let add_detail = dns_log_json_answer_detail(add, i)?;
+ js.append_object(&add_detail)?;
+ }
+ }
+ _ => {
+ let add_detail = dns_log_json_answer_detail(add, 0)?;
+ js.append_object(&add_detail)?;
+ }
+ }
}
if is_js_open {
js.close()?;
a.append_string_from_bytes(&name.value)?;
}
}
- DNSRData::TXT(bytes) | DNSRData::NULL(bytes) => {
+ DNSRData::TXT(txt) => {
+ if !answer_types.contains_key(&type_string) {
+ answer_types
+ .insert(type_string.to_string(), JsonBuilder::try_new_array()?);
+ }
+ if let Some(a) = answer_types.get_mut(&type_string) {
+ for txt in txt {
+ a.append_string_from_bytes(txt)?;
+ }
+ }
+ }
+ DNSRData::NULL(bytes) => {
if !answer_types.contains_key(&type_string) {
answer_types
.insert(type_string.to_string(), JsonBuilder::try_new_array()?);
}
if flags & LOG_FORMAT_DETAILED != 0 {
- js_answers.append_object(&dns_log_json_answer_detail(answer)?)?;
+ match &answer.data {
+ DNSRData::TXT(txt) => {
+ for i in 0..txt.len() {
+ js_answers.append_object(&dns_log_json_answer_detail(answer, i)?)?;
+ }
+ }
+ _ => {
+ js_answers.append_object(&dns_log_json_answer_detail(answer, 0)?)?;
+ }
+ }
}
}
if !message.authorities.is_empty() {
jb.open_array("authorities")?;
for auth in &message.authorities {
- let auth_detail = dns_log_json_answer_detail(auth)?;
- jb.append_object(&auth_detail)?;
+ match &auth.data {
+ DNSRData::TXT(txt) => {
+ for i in 0..txt.len() {
+ let auth_detail = dns_log_json_answer_detail(auth, i)?;
+ jb.append_object(&auth_detail)?;
+ }
+ }
+ _ => {
+ let auth_detail = dns_log_json_answer_detail(auth, 0)?;
+ jb.append_object(&auth_detail)?;
+ }
+ }
}
jb.close()?;
}
jb.open_array("additionals")?;
is_jb_open = true;
}
- let add_detail = dns_log_json_answer_detail(add)?;
- jb.append_object(&add_detail)?;
+ match &add.data {
+ DNSRData::TXT(txt) => {
+ for i in 0..txt.len() {
+ let add_detail = dns_log_json_answer_detail(add, i)?;
+ jb.append_object(&add_detail)?;
+ }
+ }
+ _ => {
+ let add_detail = dns_log_json_answer_detail(add, 0)?;
+ jb.append_object(&add_detail)?;
+ }
+ }
}
if is_jb_open {
jb.close()?;
//! Nom parsers for DNS.
-use crate::dns::dns::*;
use crate::detect::EnumString;
-use nom7::combinator::{complete, rest};
+use crate::dns::dns::*;
+use nom7::combinator::rest;
use nom7::error::ErrorKind;
-use nom7::multi::{count, length_data, many_m_n};
+use nom7::multi::{count, length_data};
use nom7::number::streaming::{be_u16, be_u32, be_u8};
use nom7::{error_position, Err, IResult};
for _ in 0..count {
match subparser(input, message, flags) {
Ok((rem, val)) => {
- let n = if val.rrtype == DNSRecordType::TXT as u16 {
- // For TXT records we need to run the parser
- // multiple times. Set n high, to the maximum
- // value based on a max txt side of 65535, but
- // taking into considering that strings need
- // to be quoted, so half that.
- 32767
- } else {
- // For all other types we only want to run the
- // parser once, so set n to 1.
- 1
- };
// edge case for additional section of type=OPT
// with empty data (data length = 0x0000)
if val.data.is_empty() && val.rrtype == DNSRecordType::OPT as u16 {
input = rem;
continue;
}
- let result: IResult<&'a [u8], Vec<DNSRData>> = many_m_n(
- 1,
- n,
- complete(|b| dns_parse_rdata(b, message, val.rrtype, flags)),
- )(val.data);
- match result {
- Ok((_, rdatas)) => {
- for rdata in rdatas {
- answers.push(DNSAnswerEntry {
- name: val.name.clone(),
- rrtype: val.rrtype,
- rrclass: val.rrclass,
- ttl: val.ttl,
- data: rdata,
- });
- }
- }
- Err(e) => {
- return Err(e);
- }
- }
+ let (_, rdata) = dns_parse_rdata(val.data, message, val.rrtype, flags)?;
+ answers.push(DNSAnswerEntry {
+ name: val.name.clone(),
+ rrtype: val.rrtype,
+ rrclass: val.rrclass,
+ ttl: val.ttl,
+ data: rdata,
+ });
input = rem;
}
Err(e) => {
}
fn dns_parse_rdata_txt(input: &[u8]) -> IResult<&[u8], DNSRData> {
- let (i, txt) = length_data(be_u8)(input)?;
- Ok((i, DNSRData::TXT(txt.to_vec())))
+ let mut txt_strings = Vec::new();
+ let mut i = input;
+
+ while !i.is_empty() {
+ let (j, txt) = length_data(be_u8)(i)?;
+ txt_strings.push(txt.to_vec());
+ i = j;
+ }
+
+ Ok((i, DNSRData::TXT(txt_strings)))
}
fn dns_parse_rdata_null(input: &[u8]) -> IResult<&[u8], DNSRData> {
let mut invalid_additionals = false;
let mut additionals = Vec::new();
if !invalid_authorities {
- let additionals_parsed = dns_parse_answer(i_next, message, header.additional_rr as usize, &mut flags);
+ let additionals_parsed =
+ dns_parse_answer(i_next, message, header.additional_rr as usize, &mut flags);
if let Ok((i, additionals_ok)) = additionals_parsed {
- additionals = additionals_ok;
- i_next = i;
- } else {
- invalid_additionals = true;
- }
+ additionals = additionals_ok;
+ i_next = i;
+ } else {
+ invalid_additionals = true;
+ }
}
Ok((
i_next,