domain_core/master/
entry.rs

1/// A master file entry.
2
3use std::path::PathBuf;
4use ::bits::name::Dname;
5use ::bits::record::Record;
6use ::iana::{Class, Rtype};
7use ::rdata::MasterRecordData;
8use ::master::scan::{CharSource, Pos, Scan, ScanError, Scanner,
9                     SyntaxError};
10
11
12//------------ Entry ---------------------------------------------------------
13
14/// A master file entry.
15///
16/// Master files consist of a sequence of entries. An entry contains data for
17/// a resource record or instructions on how to build resource records from
18/// the data.
19///
20/// This enum has variants for each type of master file entries currently
21/// defined. It also knows how to scan itself from a scanner via the
22/// `scan()` function.
23///
24/// The variants are defined in seciton 5 of [RFC 1035] except where
25/// otherwise stated below.
26///
27/// [RFC 1035]: https://tools.ietf.org/html/rfc1035
28#[derive(Clone, Debug)]
29pub enum Entry {
30    /// An `$ORIGIN` control entry.
31    ///
32    /// This entry contains the origin for relative domain names encountered
33    /// in subsequent entries.
34    Origin(Dname),
35    
36    /// An `$INCLUDE` control entry.
37    ///
38    /// This entry instructs the parser to insert the content of the given
39    /// file at this position. The `path` attribute specifies the path to
40    /// the file. The interpretation of the contents of this attribute is
41    /// system dependent. The optional `origin` attribute contains the
42    /// initial value of the origin of relative domain names when including
43    /// the file.
44    Include { path: PathBuf, origin: Option<Dname> },
45
46    /// A `$TTL` control entry.
47    ///
48    /// This entry specifies the value of the TTL field for all subsequent
49    /// records that do not have it explicitely stated.
50    ///
51    /// This entry is defined in section 4 of [RFC 2308].
52    ///
53    /// [RFC 2308]: https://tools.ietf.org/html/rfc2308
54    Ttl(u32),
55
56    /// Some other control entry.
57    ///
58    /// Any other entry starting with a dollar sign is a control entry we
59    /// do not understand. This variant contains the name of the entry in
60    /// the `name` attribute and its starting position in `start`. This can
61    /// be used to produce a meaningful warning or error message.
62    Control { name: String, start: Pos },
63
64    /// A resource record.
65    Record(MasterRecord),
66
67    /// A blank entry.
68    Blank
69}
70
71impl Entry {
72    /// Scans an entry from a scanner.
73    ///
74    /// The four additional arguments contain the state of scanning for
75    /// entries.
76    ///
77    /// The `last_owner` contains the domain name of the last
78    /// record entry unless this is the first entry. This is used for the
79    /// `owner` field if a record entry starts with blanks.
80    /// The `last_class` is the class of the last resource record and is
81    /// used if a class value is missing from a record entry.
82    /// The `origin`
83    /// argument is used for any relative names given in a record entry.
84    /// The `default_ttl` value is used if a TTL value is missing from a
85    /// record entry.
86    ///
87    /// If successful, the function returns some entry or `None` if it
88    /// encountered an end of file before an entry even started.
89    pub fn scan<C: CharSource>(scanner: &mut Scanner<C>,
90                               last_owner: Option<&Dname>,
91                               last_class: Option<Class>,
92                               default_ttl: Option<u32>)
93                               -> Result<Option<Self>, ScanError> {
94        if scanner.is_eof() {
95            Ok(None)
96        }
97        else if let Ok(entry) = Self::scan_control(scanner) {
98            Ok(Some(entry))
99        }
100        else if let Ok(()) = Self::scan_blank(scanner) {
101            Ok(Some(Entry::Blank))
102        }
103        else {
104            let record = Self::scan_record(scanner, last_owner,
105                                           last_class, default_ttl)?;
106            Ok(Some(Entry::Record(record)))
107        }
108    }
109
110    fn scan_blank<C: CharSource>(scanner: &mut Scanner<C>)
111                                 -> Result<(), ScanError> {
112        scanner.scan_opt_space()?;
113        scanner.scan_newline()?;
114        Ok(())
115    }
116
117    /// Tries to scan a control entry.
118    fn scan_control<C: CharSource>(scanner: &mut Scanner<C>)
119                                   -> Result<Self, ScanError> {
120        match ControlType::scan(scanner)? {
121            ControlType::Origin => {
122                let name = Dname::scan(scanner)?;
123                scanner.scan_newline()?;
124                Ok(Entry::Origin(name))
125            }
126            ControlType::Include => {
127                let path = scanner.scan_string_phrase(|x| Ok(x.into()))?;
128                let origin = Dname::scan(scanner).ok();
129                scanner.scan_newline()?;
130                Ok(Entry::Include { path, origin })
131            }
132            ControlType::Ttl => {
133                let ttl = u32::scan(scanner)?;
134                scanner.scan_newline()?;
135                Ok(Entry::Ttl(ttl))
136            }
137            ControlType::Other(name, pos) => {
138                scanner.skip_entry()?;
139                Ok(Entry::Control { name, start: pos })
140            }
141        }
142    }
143
144    fn scan_record<C: CharSource>(scanner: &mut Scanner<C>,
145                               last_owner: Option<&Dname>,
146                               last_class: Option<Class>,
147                               default_ttl: Option<u32>)
148                               -> Result<MasterRecord, ScanError> {
149        let owner = Self::scan_owner(scanner, last_owner)?;
150        let (ttl, class) = Self::scan_ttl_class(scanner, last_class,
151                                                default_ttl)?;
152        let rtype = Rtype::scan(scanner)?;
153        let rdata = MasterRecordData::scan(rtype, scanner)?;
154        scanner.scan_newline()?;
155        Ok(Record::new(owner, class, ttl, rdata))
156    }
157
158    fn scan_owner<C: CharSource>(scanner: &mut Scanner<C>,
159                                 last_owner: Option<&Dname>)
160                                 -> Result<Dname, ScanError> {
161        let pos = scanner.pos();
162        if let Ok(()) = scanner.scan_space() {
163            if let Some(owner) = last_owner { Ok(owner.clone()) }
164            else { Err(ScanError::Syntax(SyntaxError::NoLastOwner, pos)) }
165        }
166        else if let Ok(()) = scanner.skip_literal("@") {
167            if let Some(ref origin) = *scanner.origin() { Ok(origin.clone()) }
168            else { Err(ScanError::Syntax(SyntaxError::NoOrigin, pos)) }
169        }
170        else {
171            Dname::scan(scanner)
172        }
173
174    }
175
176    fn scan_ttl_class<C: CharSource>(scanner: &mut Scanner<C>,
177                                     last_class: Option<Class>,
178                                     default_ttl: Option<u32>)
179                                     -> Result<(u32, Class), ScanError> {
180        let pos = scanner.pos();
181        let (ttl, class) = match u32::scan(scanner) {
182            Ok(ttl) => {
183                match Class::scan(scanner) {
184                    Ok(class) => {
185                        (Some(ttl), Some(class))
186                    }
187                    Err(_) => (Some(ttl), None)
188                }
189            }
190            Err(_) => {
191                match Class::scan(scanner) {
192                    Ok(class) => {
193                        match u32::scan(scanner) {
194                            Ok(ttl) => {
195                                (Some(ttl), Some(class))
196                            }
197                            Err(_) => (None, Some(class))
198                        }
199                    }
200                    Err(_) => (None, None)
201                }
202            }
203        };
204        let ttl = match ttl.or(default_ttl) {
205            Some(ttl) => ttl,
206            None => {
207                return Err(ScanError::Syntax(SyntaxError::NoDefaultTtl, pos))
208            }
209        };
210        let class = match class.or(last_class) {
211            Some(class) => class,
212            None => {
213                return Err(ScanError::Syntax(SyntaxError::NoLastClass, pos))
214            }
215        };
216        Ok((ttl, class))
217    }
218}
219
220
221//------------ ControlType ---------------------------------------------------
222
223/// The type of a control entry.
224#[derive(Clone, Debug)]
225enum ControlType {
226    Origin,
227    Include,
228    Ttl,
229    Other(String, Pos)
230}
231
232impl Scan for ControlType {
233    fn scan<C: CharSource>(scanner: &mut Scanner<C>)
234                           -> Result<Self, ScanError> {
235        let pos = scanner.pos();
236        scanner.scan_string_word(|word| {
237            if word.eq_ignore_ascii_case("$ORIGIN") {
238                Ok(ControlType::Origin)
239            }
240            else if word.eq_ignore_ascii_case("$INCLUDE") {
241                Ok(ControlType::Include)
242            }
243            else if word.eq_ignore_ascii_case("$TTL") {
244                Ok(ControlType::Ttl)
245            }
246            else if let Some('$') = word.chars().next() {
247                Ok(ControlType::Other(word.to_owned(), pos))
248            }
249            else {
250                Err(SyntaxError::Expected(String::from("$")))
251            }
252        })
253    }
254}
255
256
257//------------ MasterRecord --------------------------------------------------
258
259pub type MasterRecord = Record<Dname, MasterRecordData<Dname>>;
260