hickory_proto/serialize/txt/
trust_anchor.rs1use alloc::{borrow::Cow, string::String, vec::Vec};
7use core::str::FromStr;
8
9use crate::{
10 dnssec::rdata::DNSKEY,
11 rr::{DNSClass, Name, RecordData, RecordType},
12 serialize::txt::{
13 ParseError, ParseErrorKind, ParseResult,
14 rdata_parsers::dnskey,
15 zone,
16 zone_lex::{Lexer, Token as LexToken},
17 },
18};
19
20pub struct Parser<'a> {
22 lexer: Lexer<'a>,
23}
24
25impl<'a> Parser<'a> {
26 pub fn new(input: impl Into<Cow<'a, str>>) -> Self {
28 Self {
29 lexer: Lexer::new(input),
30 }
31 }
32
33 pub fn parse(mut self) -> ParseResult<Vec<Entry>> {
37 let mut state = State::StartLine;
38 let mut records = vec![];
39
40 while let Some(token) = self.lexer.next_token()? {
41 let token: Token = token.try_into()?;
42 state = match state {
43 State::StartLine => match token {
44 Token::Blank | Token::EOL => State::StartLine,
45 Token::CharData(data) => {
46 let name = Name::parse(&data, None)?;
47 State::Ttl { name }
48 }
49 },
50
51 State::Ttl { name } => {
52 if let Token::CharData(data) = token {
53 if let Ok(class) = DNSClass::from_str(&data) {
54 State::Type {
55 name,
56 ttl: None,
57 class,
58 }
59 } else {
60 let ttl = zone::Parser::parse_time(&data)?;
61 State::Class { name, ttl }
62 }
63 } else {
64 return Err(ParseErrorKind::UnexpectedToken(token.into()).into());
65 }
66 }
67
68 State::Class { name, ttl } => {
69 if let Token::CharData(mut data) = token {
70 data.make_ascii_uppercase();
71 let class = DNSClass::from_str(&data)?;
72 State::Type {
73 name,
74 ttl: Some(ttl),
75 class,
76 }
77 } else {
78 return Err(ParseErrorKind::UnexpectedToken(token.into()).into());
79 }
80 }
81
82 State::Type { name, ttl, class } => {
83 if let Token::CharData(data) = token {
84 let rtype = RecordType::from_str(&data)?;
85
86 if !matches!(rtype, RecordType::DNSKEY) {
87 return Err(ParseErrorKind::UnsupportedRecordType(rtype).into());
88 }
89
90 State::RData {
91 name,
92 ttl,
93 class,
94 parts: vec![],
95 }
96 } else {
97 return Err(ParseErrorKind::UnexpectedToken(token.into()).into());
98 }
99 }
100
101 State::RData {
102 name,
103 ttl,
104 class,
105 parts,
106 } => match token {
107 Token::EOL => {
108 Self::flush_record(parts, name, ttl, class, &mut records)?;
109 State::StartLine
110 }
111
112 Token::CharData(data) => {
113 let mut parts = parts;
114 parts.push(data);
115 State::RData {
116 name,
117 ttl,
118 class,
119 parts,
120 }
121 }
122
123 _ => return Err(ParseErrorKind::UnexpectedToken(token.into()).into()),
124 },
125 };
126 }
127
128 if let State::RData {
129 name,
130 ttl,
131 class,
132 parts,
133 } = state
134 {
135 Self::flush_record(parts, name, ttl, class, &mut records)?;
136 }
137
138 Ok(records)
139 }
140
141 fn flush_record(
142 rdata_parts: Vec<String>,
143 name: Name,
144 ttl: Option<u32>,
145 class: DNSClass,
146 records: &mut Vec<Entry>,
147 ) -> ParseResult<()> {
148 let dnskey = dnskey::parse(rdata_parts.iter().map(AsRef::as_ref))?;
149
150 let record = Record {
151 name_labels: name,
152 dns_class: class,
153 ttl,
154 rdata: dnskey,
155 };
156
157 records.push(Entry::DNSKEY(record));
158
159 Ok(())
160 }
161}
162
163#[derive(Debug)]
165#[non_exhaustive]
166pub enum Entry {
167 DNSKEY(Record<DNSKEY>),
169}
170
171#[derive(Debug)]
174pub struct Record<R> {
175 name_labels: Name,
176 dns_class: DNSClass,
177 ttl: Option<u32>,
178 rdata: R,
179}
180
181impl<R> Record<R> {
182 pub fn data(&self) -> &R {
184 &self.rdata
185 }
186
187 pub fn dns_class(&self) -> DNSClass {
189 self.dns_class
190 }
191
192 pub fn ttl(&self) -> Option<u32> {
194 self.ttl
195 }
196
197 pub fn name(&self) -> &Name {
199 &self.name_labels
200 }
201}
202
203impl<R: RecordData> Record<R> {
204 pub fn record_type(&self) -> RecordType {
206 self.data().record_type()
207 }
208}
209
210enum State {
211 StartLine,
213 Ttl { name: Name },
215 Class { name: Name, ttl: u32 },
217 Type {
219 name: Name,
220 ttl: Option<u32>,
221 class: DNSClass,
222 },
223 RData {
225 name: Name,
226 ttl: Option<u32>,
227 class: DNSClass,
228 parts: Vec<String>,
229 },
230}
231
232enum Token {
233 Blank,
234 CharData(String),
235 EOL,
236}
237
238impl TryFrom<LexToken> for Token {
239 type Error = ParseError;
240
241 fn try_from(token: LexToken) -> Result<Self, Self::Error> {
242 let token = match token {
243 LexToken::At
244 | LexToken::Include
245 | LexToken::Origin
246 | LexToken::Ttl
247 | LexToken::List(_) => return Err(ParseErrorKind::UnexpectedToken(token).into()),
248 LexToken::Blank => Self::Blank,
249 LexToken::CharData(data) => Self::CharData(data),
250 LexToken::EOL => Self::EOL,
251 };
252 Ok(token)
253 }
254}
255
256impl From<Token> for LexToken {
257 fn from(token: Token) -> Self {
258 match token {
259 Token::Blank => Self::Blank,
260 Token::CharData(data) => Self::CharData(data),
261 Token::EOL => Self::EOL,
262 }
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269 #[cfg(feature = "__dnssec")]
270 use crate::dnssec::crypto::EcdsaSigningKey;
271 use crate::dnssec::{Algorithm, PublicKey, PublicKeyBuf, SigningKey, rdata::DNSKEY};
272
273 const ENCODED: &str = "aGVsbG8=";
274
275 #[test]
276 fn empty() {
277 let records = parse_ok("");
278 assert!(records.is_empty());
279 }
280
281 #[cfg(feature = "__dnssec")]
282 #[test]
283 fn it_works() {
284 let algorithm = Algorithm::ECDSAP256SHA256;
285 let pkcs8 = EcdsaSigningKey::generate_pkcs8(algorithm).unwrap();
286 let signing_key = EcdsaSigningKey::from_pkcs8(&pkcs8, algorithm).unwrap();
287 let public_key = signing_key.to_public_key().unwrap();
288 let encoded = data_encoding::BASE64.encode(public_key.public_bytes());
289 let input = format!(". 34076 IN DNSKEY 256 3 13 {encoded}");
290
291 let records = parse_ok(&input);
292 let [record] = records.try_into().unwrap();
293 assert_eq!(&Name::root(), record.name());
294 assert_eq!(Some(34076), record.ttl());
295 assert_eq!(DNSClass::IN, record.dns_class());
296 assert_eq!(RecordType::DNSKEY, record.record_type());
297
298 let expected = DNSKEY::new(
299 true,
300 false,
301 false,
302 PublicKeyBuf::new(
303 signing_key.to_public_key().unwrap().public_bytes().to_vec(),
304 algorithm,
305 ),
306 );
307 let actual = record.data();
308 assert_eq!(&expected, actual);
309 }
310
311 #[cfg(feature = "__dnssec")]
312 #[test]
313 fn no_ttl_field() {
314 let algorithm = Algorithm::ECDSAP256SHA256;
315 let pkcs8 = EcdsaSigningKey::generate_pkcs8(algorithm).unwrap();
316 let signing_key = EcdsaSigningKey::from_pkcs8(&pkcs8, algorithm).unwrap();
317 let public_key = signing_key.to_public_key().unwrap();
318 let encoded = data_encoding::BASE64.encode(public_key.public_bytes());
319 let input = format!(". IN DNSKEY 256 3 13 {encoded}");
320
321 let records = parse_ok(&input);
322 let [record] = records.try_into().unwrap();
323 assert_eq!(&Name::root(), record.name());
324 assert_eq!(None, record.ttl());
325 assert_eq!(DNSClass::IN, record.dns_class());
326 assert_eq!(RecordType::DNSKEY, record.record_type());
327
328 let expected = DNSKEY::new(
329 true,
330 false,
331 false,
332 PublicKeyBuf::new(
333 signing_key.to_public_key().unwrap().public_bytes().to_vec(),
334 algorithm,
335 ),
336 );
337 let actual = record.data();
338 assert_eq!(&expected, actual);
339 }
340
341 #[test]
342 fn accepts_real_world_data() {
343 let records = parse_ok(include_str!("../../../tests/test-data/root.key"));
344 assert_eq!(3, records.len());
345 }
346
347 #[test]
348 fn origin() {
349 let err = parse_err("$ORIGIN example.com.");
350 assert!(matches!(err.kind(), ParseErrorKind::UnexpectedToken(_)));
351 }
352
353 #[test]
354 fn at_sign() {
355 let input = format!("@ 34076 IN DNSKEY 256 3 8 {ENCODED}");
356 let err = parse_err(&input);
357 assert!(matches!(err.kind(), ParseErrorKind::UnexpectedToken(_)));
358 }
359
360 #[test]
361 fn wrong_record_type() {
362 let input = "example.com. 657 IN A 93.184.215.14";
364 let err = parse_err(input);
365 assert!(matches!(
366 err.kind(),
367 ParseErrorKind::UnsupportedRecordType(_)
368 ));
369 }
370
371 fn parse_ok(input: &str) -> Vec<Record<DNSKEY>> {
372 let parser = Parser::new(input);
373 let res = parser.parse();
374 let entries = res.expect("parsing failed");
375 entries
376 .into_iter()
377 .map(|Entry::DNSKEY(dnskey)| dnskey)
378 .collect()
379 }
380
381 fn parse_err(input: &str) -> ParseError {
382 let parser = Parser::new(input);
383 let res = parser.parse();
384 res.expect_err("parsing did not fail")
385 }
386}