From 5ba8f8db13918cdc0160f3d64efae569e3dda1d0 Mon Sep 17 00:00:00 2001 From: Ximon Eighteen <3304436+ximon18@users.noreply.github.com> Date: Mon, 25 Nov 2024 14:26:08 +0100 Subject: [PATCH 1/3] Rough hack to support reporting zonefile parsing progress to the caller. --- examples/read-zone.rs | 2 +- src/rdata/zonemd.rs | 2 +- src/sign/records.rs | 41 +++++++++++++++++++++++++++++++++++++++-- src/stelline/client.rs | 4 ++-- src/stelline/matches.rs | 2 +- src/stelline/server.rs | 6 +++--- src/validator/anchor.rs | 6 +++--- src/zonefile/inplace.rs | 40 ++++++++++++++++++++++++++++++++++------ src/zonetree/parsed.rs | 2 +- 9 files changed, 85 insertions(+), 20 deletions(-) diff --git a/examples/read-zone.rs b/examples/read-zone.rs index 110aaa804..7b8393ec8 100644 --- a/examples/read-zone.rs +++ b/examples/read-zone.rs @@ -42,7 +42,7 @@ fn main() { ); eprintln!(" Error: {err}"); if let Some(entry) = &last_entry { - if let Entry::Record(record) = &entry { + if let Entry::Record(record, _) = &entry { eprintln!( "\nThe last record read was:\n{record}." ); diff --git a/src/rdata/zonemd.rs b/src/rdata/zonemd.rs index 66f41d400..630a8dc79 100644 --- a/src/rdata/zonemd.rs +++ b/src/rdata/zonemd.rs @@ -462,7 +462,7 @@ ns2 3600 IN AAAA 2001:db8::63 zone.set_origin(Name::root()); while let Some(entry) = zone.next_entry().unwrap() { match entry { - Entry::Record(record) => { + Entry::Record(record, _) => { if record.rtype() != Rtype::ZONEMD { continue; } diff --git a/src/sign/records.rs b/src/sign/records.rs index bb46975c3..56385f172 100644 --- a/src/sign/records.rs +++ b/src/sign/records.rs @@ -141,6 +141,14 @@ where pub fn iter(&self) -> Iter<'_, Record> { self.records.iter() } + + pub fn len(&self) -> usize { + self.records.len() + } + + pub fn is_empty(&self) -> bool { + self.records.is_empty() + } } impl SortedRecords { @@ -178,13 +186,14 @@ where /// AND has the SEP flag set, it will be used as a CSK (i.e. both KSK and /// ZSK). #[allow(clippy::type_complexity)] - pub fn sign( + pub fn sign( &self, apex: &FamilyName, expiration: Timestamp, inception: Timestamp, keys: &[SigningKey], add_used_dnskeys: bool, + progress_cb: CB, ) -> Result< Vec>>, ErrorTypeToBeDetermined, @@ -200,6 +209,7 @@ where + Clone + From> + octseq::OctetsFrom>, + CB: Fn(usize, usize, Option<&'static str>), { let (mut ksks, mut zsks): (Vec<_>, Vec<_>) = keys .iter() @@ -228,6 +238,9 @@ where debug!("# ZSKs: {}", zsks.len()); } + let num_families = self.families().count(); + (progress_cb)(0, num_families, Some("Creating DNSKEYs")); + let mut res: Vec>> = Vec::new(); let mut buf = Vec::new(); let mut cut: Option> = None; @@ -265,6 +278,8 @@ where ZoneRecordData::Dnskey(dnskey), )); } + + (progress_cb)(1, 1, None); } let dummy_dnskey_rrs = SortedRecords::::new(); @@ -274,16 +289,19 @@ where dummy_dnskey_rrs.families().chain(families) }; + (progress_cb)(0, 0, Some("Signing RRs")); for family in families_iter { // If the owner is out of zone, we have moved out of our zone and // are done. if !family.is_in_zone(apex) { + (progress_cb)(1, 0, None); break; } // If the family is below a zone cut, we must ignore it. if let Some(ref cut) = cut { if family.owner().ends_with(cut.owner()) { + (progress_cb)(1, 0, None); continue; } } @@ -357,6 +375,8 @@ where )); } } + + (progress_cb)(1, 0, None); } Ok(res) @@ -465,7 +485,8 @@ where /// [RFC 5155]: https://www.rfc-editor.org/rfc/rfc5155.html /// [RFC 9077]: https://www.rfc-editor.org/rfc/rfc9077.html /// [RFC 9276]: https://www.rfc-editor.org/rfc/rfc9276.html - pub fn nsec3s( + #[allow(clippy::too_many_arguments)] + pub fn nsec3s( &self, apex: &FamilyName, ttl: Ttl, @@ -473,6 +494,7 @@ where opt_out: Nsec3OptOut, assume_dnskeys_will_be_added: bool, capture_hash_to_owner_mappings: bool, + progress_cb: CB, ) -> Result, Nsec3HashError> where N: ToName + Clone + From> + Display + Ord + Hash, @@ -487,6 +509,7 @@ where + EmptyBuilder + FreezeBuilder, ::Octets: AsRef<[u8]>, + CB: Fn(usize, usize, Option<&'static str>), { // TODO: // - Handle name collisions? (see RFC 5155 7.1 Zone Signing) @@ -514,6 +537,9 @@ where // The owner name of a zone cut if we currently are at or below one. let mut cut: Option> = None; + let num_families = self.families().count(); + (progress_cb)(0, num_families, Some("Creating NSEC3 RRs")); + let mut families = self.families(); // Since the records are ordered, the first family is the apex -- @@ -535,12 +561,14 @@ where // If the owner is out of zone, we have moved out of our zone and // are done. if !family.is_in_zone(apex) { + (progress_cb)(1, 0, None); break; } // If the family is below a zone cut, we must ignore it. if let Some(ref cut) = cut { if family.owner().ends_with(cut.owner()) { + (progress_cb)(1, 0, None); continue; } } @@ -562,6 +590,7 @@ where // delegations MAY be excluded." let has_ds = family.records().any(|rec| rec.rtype() == Rtype::DS); if cut.is_some() && !has_ds && opt_out == Nsec3OptOut::OptOut { + (progress_cb)(1, 0, None); continue; } @@ -617,6 +646,7 @@ where builder.append_origin(&apex_owner).unwrap().into(); if let Err(pos) = ents.binary_search(&name) { + (progress_cb)(n, 1, None); ents.insert(pos, name); } } @@ -668,8 +698,10 @@ where last_nent_stack.push(last_nent); } last_nent_stack.push(name.owner().clone()); + (progress_cb)(1, 1, None); } + (progress_cb)(0, 0, Some("Creating ENTs NSEC3 RRs")); for name in ents { // Create the type bitmap, empty for an ENT NSEC3. let bitmap = RtypeBitmap::::builder(); @@ -691,6 +723,8 @@ where // Store the record by order of its owner name. nsec3s.push(rec); + + (progress_cb)(1, 1, None); } // RFC 5155 7.1 step 7: @@ -698,7 +732,9 @@ where // value of the next NSEC3 RR in hash order. The next hashed owner // name of the last NSEC3 RR in the zone contains the value of the // hashed owner name of the first NSEC3 RR in the hash order." + (progress_cb)(0, 0, Some("Sorting")); let mut nsec3s = SortedRecords::, S>::from(nsec3s); + (progress_cb)(0, 0, Some("Hashing NSEC3 owner names")); for i in 1..=nsec3s.records.len() { // TODO: Detect duplicate hashes. let next_i = if i == nsec3s.records.len() { 0 } else { i }; @@ -716,6 +752,7 @@ where let last_rec = &mut nsec3s.records[i - 1]; let last_nsec3: &mut Nsec3 = last_rec.data_mut(); last_nsec3.set_next_owner(owner_hash.clone()); + (progress_cb)(1, 0, None); } // RFC 5155 7.1 step 8: diff --git a/src/stelline/client.rs b/src/stelline/client.rs index 79fad00ac..db09cf9d7 100644 --- a/src/stelline/client.rs +++ b/src/stelline/client.rs @@ -778,13 +778,13 @@ fn entry2msg(entry: &Entry) -> (&Sections, Reply, Message>) { } let mut msg = msg.authority(); for zone_file_entry in §ions.authority { - if let Record(rec) = zone_file_entry { + if let Record(rec, _) = zone_file_entry { msg.push(rec).unwrap(); } } let mut msg = msg.additional(); for zone_file_entry in §ions.additional.zone_entries { - if let Record(rec) = zone_file_entry { + if let Record(rec, _) = zone_file_entry { msg.push(rec).unwrap(); } } diff --git a/src/stelline/matches.rs b/src/stelline/matches.rs index 3aa073f70..c47da1e50 100644 --- a/src/stelline/matches.rs +++ b/src/stelline/matches.rs @@ -376,7 +376,7 @@ fn match_section< } for (index, mat_rr) in match_section.iter().enumerate() { // Remove outer Record - let mat_rr = if let ZonefileEntry::Record(record) = mat_rr { + let mat_rr = if let ZonefileEntry::Record(record, _) = mat_rr { record } else { panic!("include not expected"); diff --git a/src/stelline/server.rs b/src/stelline/server.rs index b9d06da9d..8ff0874ab 100644 --- a/src/stelline/server.rs +++ b/src/stelline/server.rs @@ -95,7 +95,7 @@ where } let mut msg = msg.answer(); for a in §ions.answer[0] { - let rec = if let ZonefileEntry::Record(record) = a { + let rec = if let ZonefileEntry::Record(record, _) = a { record } else { panic!("include not expected") @@ -104,7 +104,7 @@ where } let mut msg = msg.authority(); for a in §ions.authority { - let rec = if let ZonefileEntry::Record(record) = a { + let rec = if let ZonefileEntry::Record(record, _) = a { record } else { panic!("include not expected") @@ -113,7 +113,7 @@ where } let mut msg = msg.additional(); for a in §ions.additional.zone_entries { - let rec = if let ZonefileEntry::Record(record) = a { + let rec = if let ZonefileEntry::Record(record, _) = a { record } else { panic!("include not expected") diff --git a/src/validator/anchor.rs b/src/validator/anchor.rs index ef6a70042..37a6eb68e 100644 --- a/src/validator/anchor.rs +++ b/src/validator/anchor.rs @@ -103,7 +103,7 @@ impl TrustAnchors { for e in zonefile { let e = e?; match e { - Entry::Record(r) => { + Entry::Record(r, _) => { new_self.add(r); } Entry::Include { path: _, origin: _ } => continue, // Just ignore include @@ -123,7 +123,7 @@ impl TrustAnchors { for e in zonefile { let e = e?; match e { - Entry::Record(r) => { + Entry::Record(r, _) => { new_self.add(r); } Entry::Include { path: _, origin: _ } => continue, // Just ignore include @@ -142,7 +142,7 @@ impl TrustAnchors { for e in zonefile { let e = e?; match e { - Entry::Record(r) => { + Entry::Record(r, _) => { self.add(r); } Entry::Include { path: _, origin: _ } => continue, // Just ignore include diff --git a/src/zonefile/inplace.rs b/src/zonefile/inplace.rs index ba679bfd7..d1066f8c2 100644 --- a/src/zonefile/inplace.rs +++ b/src/zonefile/inplace.rs @@ -110,6 +110,18 @@ impl Zonefile { std::io::copy(read, &mut buf)?; Ok(buf.into_inner()) } + + pub fn pos(&self) -> usize { + self.buf.pos() + } + + pub fn len(&self) -> usize { + self.buf.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } } impl Default for Zonefile { @@ -213,8 +225,9 @@ impl Iterator for Zonefile { /// An entry of a zonefile. #[derive(Clone, Debug)] pub enum Entry { - /// A DNS record. - Record(ScannedRecord), + /// A DNS record and the approximate index into the source buffer after + /// the record was read from it. + Record(ScannedRecord, usize), /// An include directive. /// @@ -362,9 +375,10 @@ impl<'a> EntryScanner<'a> { self.zonefile.buf.require_line_feed()?; - Ok(ScannedEntry::Entry(Entry::Record(Record::new( - owner, class, ttl, data, - )))) + Ok(ScannedEntry::Entry(Entry::Record( + Record::new(owner, class, ttl, data), + self.zonefile.pos(), + ))) } /// Scans the TTL, class, and type portions of a regular record. @@ -969,6 +983,9 @@ struct SourceBuf { /// /// This may be negative if we cut off bits of the current line. line_start: isize, + + /// The number of bytes read so far from the start of the data given to Zonefile. + pos: usize, } impl SourceBuf { @@ -986,9 +1003,18 @@ impl SourceBuf { parens: 0, line_num: 1, line_start: 1, + pos: 0, } } + fn pos(&self) -> usize { + self.pos + } + + fn len(&self) -> usize { + self.buf.len() + } + /// Enriches an entry error with position information. fn error(&self, err: EntryError) -> Error { Error { @@ -1363,6 +1389,7 @@ impl SourceBuf { fn split_to(&mut self, at: usize) -> BytesMut { assert!(at <= self.start); let res = self.buf.split_to(at); + self.pos += at; self.start -= at; self.line_start -= at as isize; res @@ -1376,6 +1403,7 @@ impl SourceBuf { fn trim_to(&mut self, at: usize) { assert!(at <= self.start); self.buf.advance(at); + self.pos += at; self.start -= at; self.line_start -= at as isize; } @@ -1576,7 +1604,7 @@ mod test { let mut result = case.result.as_slice(); while let Some(entry) = zone.next_entry().unwrap() { match entry { - Entry::Record(record) => { + Entry::Record(record, _) => { let (first, tail) = result.split_first().unwrap(); assert_eq!(first, &record); result = tail; diff --git a/src/zonetree/parsed.rs b/src/zonetree/parsed.rs index dc4cab13f..296ad5bb0 100644 --- a/src/zonetree/parsed.rs +++ b/src/zonetree/parsed.rs @@ -309,7 +309,7 @@ impl TryFrom for Zonefile { for res in source { match res.map_err(RecordError::MalformedRecord) { - Ok(Entry::Record(r)) => { + Ok(Entry::Record(r, _)) => { let stored_rec = r.flatten_into(); let name = stored_rec.owner().clone(); if let Err(err) = zonefile.insert(stored_rec) { From ccfae8c6ed748945da609bf14418294cf0c2b1d4 Mon Sep 17 00:00:00 2001 From: Ximon Eighteen <3304436+ximon18@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:22:03 +0100 Subject: [PATCH 2/3] Add a tab before the RDATA as well as within it, to match LDNS tabbed output format. (#463) --- src/base/zonefile_fmt.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/base/zonefile_fmt.rs b/src/base/zonefile_fmt.rs index b48e48e1f..8902d7cf3 100644 --- a/src/base/zonefile_fmt.rs +++ b/src/base/zonefile_fmt.rs @@ -145,6 +145,7 @@ impl FormatWriter for SimpleWriter { /// A single line writer that puts tabs between ungrouped tokens struct TabbedWriter { first: bool, + first_block: bool, blocks: usize, writer: W, } @@ -153,6 +154,7 @@ impl TabbedWriter { fn new(writer: W) -> Self { Self { first: true, + first_block: true, blocks: 0, writer, } @@ -162,7 +164,14 @@ impl TabbedWriter { impl FormatWriter for TabbedWriter { fn fmt_token(&mut self, args: fmt::Arguments<'_>) -> Result { if !self.first { - let c = if self.blocks == 0 { '\t' } else { ' ' }; + let c = if self.blocks == 0 { + '\t' + } else if self.first_block { + self.first_block = false; + '\t' + } else { + ' ' + }; self.writer.write_char(c)?; } self.first = false; @@ -439,7 +448,7 @@ mod test { } #[test] - fn aligned() { + fn tabbed() { let record = create_record( Cds::new( 5414, @@ -453,7 +462,7 @@ mod test { // The name, ttl, class and rtype should be separated by \t, but the // rdata shouldn't. assert_eq!( - "example.com.\t3600\tIN\tCDS 5414 15 2 DEADBEEF", + "example.com.\t3600\tIN\tCDS\t5414 15 2 DEADBEEF", record.display_zonefile(DisplayKind::Tabbed).to_string() ); } From 41c78f2d4d8c2bf47c8fb443fe28167cf2d639df Mon Sep 17 00:00:00 2001 From: Ximon Eighteen <3304436+ximon18@users.noreply.github.com> Date: Fri, 6 Dec 2024 15:27:19 +0100 Subject: [PATCH 3/3] Update changelog. --- Changelog.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 0845dde68..aabf4b9a0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,7 +17,8 @@ New running resolver. In combination with `ResolvConf::new()` this can also be used to control the connections made when testing code that uses the stub resolver. ([#440]) -* Add `ZonefileFmt` trait for printing records as zonefiles. ([#379], [#446]) +* Add `ZonefileFmt` trait for printing records as zonefiles. ([#379], [#446], + [#463]) Bug fixes @@ -48,6 +49,7 @@ Other changes [#440]: https://github.com/NLnetLabs/domain/pull/440 [#441]: https://github.com/NLnetLabs/domain/pull/441 [#446]: https://github.com/NLnetLabs/domain/pull/446 +[#463]: https://github.com/NLnetLabs/domain/pull/463 [@weilence]: https://github.com/weilence ## 0.10.3