use alloc::string::ToString;
use tracing::warn;
use crate::rr::rdata::CAA;
use crate::rr::rdata::caa::{Property, read_value};
use crate::serialize::binary::{BinDecoder, Restrict};
use crate::serialize::txt::errors::{ParseError, ParseErrorKind, ParseResult};
pub(crate) fn parse<'i, I: Iterator<Item = &'i str>>(mut tokens: I) -> ParseResult<CAA> {
let flags_str: &str = tokens
.next()
.ok_or_else(|| ParseError::from(ParseErrorKind::Message("caa flags not present")))?;
let tag_str: &str = tokens
.next()
.ok_or_else(|| ParseError::from(ParseErrorKind::Message("caa tag not present")))?;
let value_str: &str = tokens
.next()
.ok_or_else(|| ParseError::from(ParseErrorKind::Message("caa value not present")))?;
let flags = flags_str.parse::<u8>()?;
let issuer_critical = (flags & 0b1000_0000) != 0;
let reserved_flags = flags & 0b0111_1111;
if reserved_flags != 0 {
warn!("unexpected flag values in caa (0 or 128): {}", flags);
}
let tag = {
let tag = Property::from(tag_str.to_string());
if tag.is_unknown() {
warn!("unknown tag found for caa: {:?}", tag);
}
tag
};
let raw_tag = tag_str.as_bytes().to_vec();
let raw_value = value_str.as_bytes().to_vec();
let mut value_decoder = BinDecoder::new(&raw_value);
let value = read_value(
&tag,
&mut value_decoder,
Restrict::new(raw_value.len() as u16),
)?;
Ok(CAA {
issuer_critical,
reserved_flags,
tag,
raw_tag,
value,
raw_value,
})
}
#[cfg(test)]
mod tests {
use alloc::string::ToString;
use crate::rr::{Name, RData, RecordType, rdata::caa::KeyValue};
use crate::serialize::txt::parse_rdata::RDataParser;
use super::*;
fn test_to_string_parse_is_reversible(expected_rdata: CAA, input_string: &str) {
let expected_rdata_string = expected_rdata.to_string();
assert_eq!(
input_string, expected_rdata_string,
"input string does not match expected_rdata.to_string()"
);
match RData::try_from_str(RecordType::CAA, input_string).expect("CAA rdata parse failed") {
RData::CAA(parsed_rdata) => assert_eq!(
expected_rdata, parsed_rdata,
"CAA rdata was not parsed as expected. input={input_string:?} expected_rdata={expected_rdata:?} parsed_rdata={parsed_rdata:?}",
),
parsed_rdata => panic!("Parsed RData is not CAA: {:?}", parsed_rdata),
}
}
#[test]
fn test_parsing() {
assert!(parse(vec!["0", "issue", ";"].into_iter()).is_ok());
assert!(parse(vec!["0", "issue", "example.net"].into_iter()).is_ok());
test_to_string_parse_is_reversible(CAA::new_issue(true, None, vec![]), "128 issue \";\"");
test_to_string_parse_is_reversible(CAA::new_issue(false, None, vec![]), "0 issue \";\"");
test_to_string_parse_is_reversible(
CAA::new_issue(
false,
Some(Name::parse("example.com", None).unwrap()),
vec![],
),
"0 issue \"example.com\"",
);
test_to_string_parse_is_reversible(
CAA::new_issue(
false,
Some(Name::parse("example.com", None).unwrap()),
vec![KeyValue::new("one", "1")],
),
"0 issue \"example.com; one=1\"",
);
test_to_string_parse_is_reversible(
CAA::new_issue(
false,
Some(Name::parse("example.com", None).unwrap()),
vec![KeyValue::new("one", "1"), KeyValue::new("two", "2")],
),
"0 issue \"example.com; one=1; two=2\"",
);
test_to_string_parse_is_reversible(
CAA::new_issue(false, None, vec![KeyValue::new("one", "1")]),
"0 issue \"; one=1\"",
);
test_to_string_parse_is_reversible(
CAA::new_issue(
false,
None,
vec![KeyValue::new("one", "1"), KeyValue::new("two", "2")],
),
"0 issue \"; one=1; two=2\"",
);
}
}