@@ -21,6 +21,9 @@ use crate::{
2121 utils:: dst:: { UnsizedCopy , UnsizedCopyFrom } ,
2222} ;
2323
24+ #[ cfg( feature = "zonefile" ) ]
25+ use crate :: new:: zonefile:: scanner:: { Scan , ScanError , Scanner } ;
26+
2427use super :: {
2528 CanonicalName , Label , LabelBuf , LabelIter , LabelParseError ,
2629 NameCompressor ,
@@ -190,6 +193,35 @@ impl BuildInMessage for Name {
190193 }
191194}
192195
196+ //--- Parsing from the zonefile format
197+
198+ #[ cfg( feature = "zonefile" ) ]
199+ impl < ' a > Scan < ' a > for & ' a Name {
200+ /// Scan a domain name token.
201+ ///
202+ /// This parses a domain name, following the [specification].
203+ ///
204+ /// [specification]: crate::new::zonefile#specification
205+ fn scan (
206+ scanner : & mut Scanner < ' _ > ,
207+ alloc : & ' a bumpalo:: Bump ,
208+ buffer : & mut std:: vec:: Vec < u8 > ,
209+ ) -> Result < Self , ScanError > {
210+ let name = NameBuf :: scan ( scanner, alloc, buffer) ?;
211+ let bytes = alloc. alloc_slice_copy ( name. as_bytes ( ) ) ;
212+ Ok ( unsafe { Name :: from_bytes_unchecked ( bytes) } )
213+ }
214+ }
215+
216+ //--- Cloning
217+
218+ #[ cfg( feature = "alloc" ) ]
219+ impl Clone for alloc:: boxed:: Box < Name > {
220+ fn clone ( & self ) -> Self {
221+ ( * self ) . unsized_copy_into ( )
222+ }
223+ }
224+
193225//--- Equality
194226
195227impl PartialEq for Name {
@@ -575,6 +607,102 @@ impl FromStr for NameBuf {
575607 }
576608}
577609
610+ //--- Parsing from the zonefile format
611+
612+ #[ cfg( feature = "zonefile" ) ]
613+ impl Scan < ' _ > for NameBuf {
614+ /// Scan a domain name token.
615+ ///
616+ /// This parses a domain name, following the [specification].
617+ ///
618+ /// [specification]: crate::new::zonefile#specification
619+ fn scan (
620+ scanner : & mut Scanner < ' _ > ,
621+ alloc : & ' _ bumpalo:: Bump ,
622+ buffer : & mut std:: vec:: Vec < u8 > ,
623+ ) -> Result < Self , ScanError > {
624+ // Build up a 'Name'.
625+ let mut this = Self :: empty ( ) ;
626+
627+ // Try parsing '@', indicating the origin name.
628+ if let [ b'@' , b' ' | b'\t' | b'\r' | b'\n' , ..] | [ b'@' ] =
629+ scanner. remaining ( )
630+ {
631+ scanner. consume ( 1 ) ;
632+ let origin = scanner
633+ . origin ( )
634+ . ok_or ( ScanError :: Custom ( "unknown origin name" ) ) ?;
635+
636+ origin
637+ . build_bytes ( & mut this. buffer )
638+ . expect ( "Valid 'RevName's are at most 255 bytes" ) ;
639+ this. size = origin. len ( ) as u8 ;
640+ return Ok ( this) ;
641+ }
642+
643+ while let Some ( & c) = scanner. remaining ( ) . first ( ) {
644+ if c. is_ascii_whitespace ( ) {
645+ break ;
646+ }
647+
648+ if !c. is_ascii_alphanumeric ( ) && !b"\\ -_" . contains ( & c) {
649+ return Err ( ScanError :: Custom (
650+ "irregular character in domain name" ,
651+ ) ) ;
652+ }
653+
654+ // Parse a label and prepend it to the buffer.
655+ let label = LabelBuf :: scan ( scanner, alloc, buffer) ?;
656+ if 255 - this. size < 1 + label. as_bytes ( ) . len ( ) as u8 {
657+ return Err ( ScanError :: Custom (
658+ "domain name exceeds 255 bytes" ,
659+ ) ) ;
660+ }
661+ this. append_label ( & label) ;
662+
663+ // Check if this is the end of the domain name.
664+ match scanner. remaining ( ) {
665+ & [ b' ' | b'\t' | b'\r' | b'\n' , ..] | & [ ] => {
666+ // This is a relative domain name.
667+ let origin = scanner
668+ . origin ( )
669+ . ok_or ( ScanError :: Custom ( "unknown origin name" ) ) ?;
670+
671+ // Append the origin to this name.
672+ origin
673+ . build_bytes ( & mut this. buffer [ this. size as usize ..] )
674+ . map_err ( |_| {
675+ ScanError :: Custom (
676+ "relative domain name exceeds 255 bytes" ,
677+ )
678+ } ) ?;
679+ // We exclude the root label, which gets added manually.
680+ this. size += origin. len ( ) as u8 - 1 ;
681+ break ;
682+ }
683+
684+ & [ b'.' , ..] => {
685+ scanner. consume ( 1 ) ;
686+ }
687+
688+ _ => {
689+ return Err ( ScanError :: Custom (
690+ "irregular character in domain name" ,
691+ ) ) ;
692+ }
693+ }
694+ }
695+
696+ if this. size == 0 {
697+ return Err ( ScanError :: Incomplete ) ;
698+ }
699+
700+ // Add a root label and stop.
701+ this. append_label ( Label :: ROOT ) ;
702+ Ok ( this)
703+ }
704+ }
705+
578706//--- Access to the underlying 'Name'
579707
580708impl Deref for NameBuf {
@@ -692,3 +820,61 @@ impl fmt::Display for NameParseError {
692820 } )
693821 }
694822}
823+
824+ //============ Unit tests ====================================================
825+
826+ #[ cfg( test) ]
827+ mod test {
828+ #[ cfg( feature = "zonefile" ) ]
829+ #[ test]
830+ fn scan ( ) {
831+ use std:: vec:: Vec ;
832+
833+ use crate :: {
834+ new:: base:: name:: RevNameBuf ,
835+ new:: zonefile:: scanner:: { Scan , ScanError , Scanner } ,
836+ } ;
837+
838+ use super :: NameBuf ;
839+
840+ let cases = [
841+ ( b"" . as_slice ( ) , Err ( ScanError :: Incomplete ) ) ,
842+ ( b" " . as_slice ( ) , Err ( ScanError :: Incomplete ) ) ,
843+ ( b"a" , Ok ( & [ b"a" as & [ u8 ] , b"org" , b"" ] as & [ & [ u8 ] ] ) ) ,
844+ ( b"xn--hello." , Ok ( & [ b"xn--hello" , b"" ] ) ) ,
845+ (
846+ b"hello\\ .world.sld" ,
847+ Ok ( & [ b"hello.world" , b"sld" , b"org" , b"" ] ) ,
848+ ) ,
849+ ( b"a\\ 046b.c." , Ok ( & [ b"a.b" , b"c" , b"" ] ) ) ,
850+ ( b"a.b\\ c.d" , Ok ( & [ b"a" , b"b c" , b"d" , b"org" , b"" ] ) ) ,
851+ ] ;
852+
853+ let alloc = bumpalo:: Bump :: new ( ) ;
854+ let mut buffer = Vec :: new ( ) ;
855+ for ( input, expected) in cases {
856+ let origin = "org" . parse :: < RevNameBuf > ( ) . unwrap ( ) ;
857+ let mut scanner = Scanner :: new ( input, Some ( & origin) ) ;
858+ let mut name_buf = None ;
859+ let actual = NameBuf :: scan ( & mut scanner, & alloc, & mut buffer)
860+ . map ( |name| name_buf. insert ( name) . labels ( ) ) ;
861+ match expected {
862+ Ok ( labels) => {
863+ assert ! (
864+ actual. clone( ) . is_ok_and( |actual| actual
865+ . map( |l| & l. as_bytes( ) [ 1 ..] )
866+ . eq( labels. iter( ) . copied( ) ) ) ,
867+ "{actual:?} == Ok({labels:?})"
868+ ) ;
869+ }
870+
871+ Err ( err) => {
872+ assert ! (
873+ actual. clone( ) . is_err_and( |e| e == err) ,
874+ "{actual:?} == Err({err:?})"
875+ ) ;
876+ }
877+ }
878+ }
879+ }
880+ }
0 commit comments