From 0ab006264f72d36702aea9b44b58792dfd56e312 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Mon, 25 Jul 2016 17:21:18 +0200 Subject: [PATCH] Third part of zonefile parsing. --- src/bits/mod.rs | 2 +- src/bits/name.rs | 158 ++++++++++++++++++++++++++--------------- src/bits/nest.rs | 10 ++- src/bits/rdata.rs | 15 ++-- src/bits/record.rs | 2 + src/master/control.rs | 102 -------------------------- src/master/entry.rs | 97 +++++++++++++++++++++++++ src/master/error.rs | 1 + src/master/mod.rs | 93 ++---------------------- src/master/reader.rs | 81 +++++++++++++++++++++ src/master/record.rs | 161 +++++++++++++++++++++++++----------------- src/master/stream.rs | 70 ++++++++++++------ src/rdata/macros.rs | 23 +++--- src/rdata/rfc1035.rs | 68 +++++++++--------- src/rdata/rfc3596.rs | 26 +++---- 15 files changed, 514 insertions(+), 395 deletions(-) delete mode 100644 src/master/control.rs create mode 100644 src/master/entry.rs create mode 100644 src/master/reader.rs diff --git a/src/bits/mod.rs b/src/bits/mod.rs index e3d2866a..20bee31b 100644 --- a/src/bits/mod.rs +++ b/src/bits/mod.rs @@ -45,7 +45,7 @@ pub use self::octets::Octets; pub use self::parse::{ParseBytes}; pub use self::question::Question; pub use self::rdata::{GenericRecordData, RecordData}; -pub use self::record::Record; +pub use self::record::{GenericRecord, Record}; pub mod bytes; pub mod charstr; diff --git a/src/bits/name.rs b/src/bits/name.rs index 1dd73557..aec69ac4 100644 --- a/src/bits/name.rs +++ b/src/bits/name.rs @@ -178,6 +178,15 @@ impl<'a> From> for DName<'a> { } } +impl<'a> From> for DName<'a> { + fn from(name: Cow<'a, DNameSlice>) -> DName<'a> { + match name { + Cow::Borrowed(name) => DName::Slice(name), + Cow::Owned(name) => DName::Owned(name) + } + } +} + //--- FromStr @@ -543,6 +552,29 @@ impl DNameSlice { } +/// # Scanning from Master Format +/// +impl DNameSlice { + pub fn scan<'a, R: io::Read>(stream: &mut master::Stream, + origin: Option<&'a DNameSlice>) + -> master::Result> { + if let Ok(()) = stream.skip_literal(b"@") { + if let Some(origin) = origin { + stream.ok(Cow::Borrowed(origin)) + } + else { + stream.err(master::SyntaxError::NoOrigin) + } + } + else { + let mut res = DNameBuf::new(); + try!(DNameBuf::scan_into(stream, origin, &mut res.inner)); + Ok(Cow::Owned(res)) + } + } +} + + //--- AsRef impl AsRef for DNameSlice { @@ -735,77 +767,91 @@ impl DNameBuf { Ok(try!(try!(PackedDName::parse(parser, context)).to_owned())) } - pub fn scan_absolute(stream: &mut master::Stream) - -> master::Result { - let res = try!(DNameBuf::_scan(stream)); - if res.is_relative() { - stream.err(master::SyntaxError::RelativeName) - } - else { - stream.ok(res) - } - } - - pub fn scan(stream: &mut master::Stream, origin: N) - -> master::Result - where R: io::Read, N: AsRef { - let mut res = try!(DNameBuf::_scan(stream)); - res.append(origin); - if res.is_relative() { - stream.err(master::SyntaxError::RelativeName) - } - else { - stream.ok(res) - } - } - - pub fn _scan(stream: &mut master::Stream) - -> master::Result { - // XXX TODO Rewrite using Stream::scan_word_chars(). - let mut res = DNameBuf::new(); - let mut label = Vec::new(); - loop { - match try!(stream.read_word_char()) { - Some(b'.') => { - if label.len() > 63 { - return stream.err(master::SyntaxError::LongLabel) + /// Scans from `stream` into `target`. + /// + /// Does not progress the stream on success so you can still error out + /// at the beginning of the name. Returns whether the resulting + /// domain name is absolute. + pub fn _scan_into(stream: &mut master::Stream, + target: &mut Vec) + -> master::Result { + let name_start = target.len(); + let mut label_start = name_start; + target.push_u8(0); + try!(stream.scan_word_chars(|ch, escape| { + match (ch, escape) { + (b'.', false) => { + let label_len = target.len() - label_start - 1; + if label_len > 63 { + return Err(master::SyntaxError::LongLabel) } - res.inner.push(label.len() as u8); - res.inner.extend(&label); - label.clear(); + target[label_start] = label_len as u8; + label_start = target.len(); + target.push_u8(0); + } + (ch, _) => { + target.push_u8(ch) } - Some(b'\\') => label.push(try!(stream.scan_escape())), - Some(ch) => label.push(ch), - None => break, } - } - if label.len() > 63 { - return stream.err(master::SyntaxError::LongLabel) - } - res.inner.push(label.len() as u8); - res.inner.extend(&label); - if res.inner.len() > 255 { + Ok(()) + })); + if target.len() - name_start > 255 { stream.err(master::SyntaxError::LongName) } else { - Ok(res) + Ok(label_start == target.len() -1) } } - pub fn scan_into(stream: &mut master::Stream, origin: N, - target: &mut B) -> master::Result<()> - where R: io::Read, N: AsRef, - B: BytesBuf { - // XXX TODO Rewrite without extra allocation. - let name = try!(DNameBuf::scan(stream, origin)); - target.push_bytes(&name.as_bytes()); - Ok(()) + pub fn scan_into(stream: &mut master::Stream, + origin: Option<&DNameSlice>, + target: &mut Vec) + -> master::Result<()> { + if let Ok(()) = stream.skip_literal(b"@") { + if let Some(origin) = origin { + target.push_bytes(&origin.as_bytes()); + } + else { + return stream.err(master::SyntaxError::NoOrigin) + } + } + else if let false = try!(DNameBuf::_scan_into(stream, target)) { + if let Some(origin) = origin { + target.push_bytes(&origin.as_bytes()); + } + else { + return stream.err(master::SyntaxError::RelativeName) + } + } + stream.ok(()) + } + + pub fn scan(stream: &mut master::Stream, + origin: Option<&DNameSlice>) + -> master::Result { + if let Ok(()) = stream.skip_literal(b"@") { + if let Some(origin) = origin { + stream.ok(origin.to_owned()) + } + else { + stream.err(master::SyntaxError::NoOrigin) + } + } + else { + let mut res = DNameBuf::new(); + try!(DNameBuf::scan_into(stream, origin, &mut res.inner)); + Ok(res) + } } /// Returns a reference to a domain name slice of this domain name. pub fn as_slice(&self) -> &DNameSlice { self } + + pub fn into_vec(self) -> Vec { + self.inner + } } diff --git a/src/bits/nest.rs b/src/bits/nest.rs index 9af62a50..daeac6e5 100644 --- a/src/bits/nest.rs +++ b/src/bits/nest.rs @@ -225,11 +225,19 @@ impl NestBuf { NestBuf(Vec::new()) } - /// Creates a nest as a copy of the given bytes slice. + /// Creates an owned nest as a copy of the given bytes slice. pub fn from_bytes(slice: &[u8]) -> Self { NestBuf(Vec::from(slice)) } + /// Creates an owned nest from the given vec. + pub fn from_vec(vec: Vec) -> Self { + NestBuf(vec) + } + + /// Returns a mutable reference to the nest’s content. + pub fn as_mut_vec(&mut self) -> &mut Vec { &mut self.0 } + /// Parses an owned nest. pub fn parse<'a, P>(p: &mut P, len: usize) -> ParseResult where P: ParseBytes<'a> { diff --git a/src/bits/rdata.rs b/src/bits/rdata.rs index 627a1e66..dd146cd1 100644 --- a/src/bits/rdata.rs +++ b/src/bits/rdata.rs @@ -19,7 +19,7 @@ use super::compose::ComposeBytes; use super::error::{ComposeResult, ParseResult}; use super::nest::Nest; use super::parse::ParseBytes; -use ::bits::bytes::BytesBuf; +use bits::bytes::BytesBuf; use ::master; @@ -96,9 +96,9 @@ impl<'a> GenericRecordData<'a> { /// /// [RFC 3597]: https:://tools.ietf.org/html/rfc3597 /// [domain::rdata::scan_into()]: ../../rdata/fn.scan_into.html - pub fn scan_into(stream: &mut master::Stream, target: &mut B) - -> master::Result<()> - where R: io::Read, B: BytesBuf { + pub fn scan_into(stream: &mut master::Stream, + target: &mut Vec) + -> master::Result<()> { try!(stream.skip_literal(b"\\#")); let mut len = try!(stream.scan_u16()); target.reserve(len as usize); @@ -115,6 +115,13 @@ impl<'a> GenericRecordData<'a> { Ok(()) } + pub fn scan(stream: &mut master::Stream) + -> master::Result> { + let mut res = Vec::new(); + try!(GenericRecordData::scan_into(stream, &mut res)); + Ok(res) + } + /// Formats the record data as if it were of concrete type `R`. pub fn fmt<'b: 'a, R>(&'b self, f: &mut fmt::Formatter) -> fmt::Result where R: RecordData<'a> + fmt::Display { diff --git a/src/bits/record.rs b/src/bits/record.rs index a4816365..30d586ea 100644 --- a/src/bits/record.rs +++ b/src/bits/record.rs @@ -11,6 +11,7 @@ use super::nest::Nest; use super::parse::ParseBytes; use super::rdata::{GenericRecordData, RecordData}; + //------------ Record ------------------------------------------------------- /// A resource record. @@ -203,6 +204,7 @@ impl<'a> Record<'a, GenericRecordData<'a>> { } } + //------------ RecordTarget ------------------------------------------------- /// A helper trait to compose records without creating `Record` values first. diff --git a/src/master/control.rs b/src/master/control.rs deleted file mode 100644 index e25fd452..00000000 --- a/src/master/control.rs +++ /dev/null @@ -1,102 +0,0 @@ - -use std::ascii::AsciiExt; -use std::io; -use ::bits::name::DNameBuf; -use ::master::{Pos, Zonefile}; -use ::master::error::{Error, Result}; -use ::master::stream::{Newline, Stream}; - - -pub fn scan_opt_control(stream: &mut Stream, - file: &mut Zonefile) - -> Result> { - Ok(match try!(ControlType::scan_opt(stream)) { - None => None, - Some(ControlType::Origin) => Some(try!(scan_origin(stream, file))), - Some(ControlType::Include) => Some(try!(scan_include(stream, file))), - Some(ControlType::Ttl) => Some(try!(scan_ttl(stream, file))), - Some(ControlType::Other(name, pos)) - => Some(try!(scan_other(stream, name, pos, file))) - }) -} - - -//------------ ControlType --------------------------------------------------- - -enum ControlType { - Origin, - Include, - Ttl, - Other(String, Pos) -} - -impl ControlType { - pub fn scan_opt(stream: &mut Stream) - -> Result> { - let pos = stream.pos(); - match stream.skip_char(b'$') { - Ok(()) => { } - Err(Error::Syntax(..)) => return Ok(None), - Err(err) => return Err(err) - } - stream.scan_word(|word| { - if word.eq_ignore_ascii_case(b"ORIGIN") { - Ok(ControlType::Origin) - } - else if word.eq_ignore_ascii_case(b"INCLUDE") { - Ok(ControlType::Include) - } - else if word.eq_ignore_ascii_case(b"TTL") { - Ok(ControlType::Ttl) - } - else { - // XXX Zonefile-encode non-ASCII characters. - Ok(ControlType::Other(String::from_utf8_lossy(word) - .into_owned(), pos)) - } - }).map(|x| Some(x)) - } -} - - -//------------ Scanner functions for control types --------------------------- - -/// Scans the $ORIGIN control entity. -fn scan_origin(stream: &mut Stream, - file: &mut Zonefile) -> Result { - try!(stream.skip_opt_space()); - let origin = try!(DNameBuf::scan_absolute(stream)); - let res = try!(stream.scan_newline()); - file.set_origin(origin); - Ok(res) -} - -/// Scans the $INCLUDE control entity. -fn scan_include(stream: &mut Stream, - file: &mut Zonefile) -> Result { - try!(stream.skip_opt_space()); - let path = try!(stream.scan_phrase(|path| Ok(Vec::from(path)))); - try!(stream.skip_opt_space()); - let origin = DNameBuf::scan(stream, file.origin()).ok(); - let res = try!(stream.scan_newline()); - file.add_include(path, origin); - Ok(res) -} - -/// Scans the $TTL control entitiy defined in RFC 2308. -fn scan_ttl(stream: &mut Stream, - file: &mut Zonefile) -> Result { - try!(stream.skip_opt_space()); - let ttl = try!(stream.scan_u32()); - let res = try!(stream.scan_newline()); - file.set_ttl(ttl); - Ok(res) -} - -fn scan_other(stream: &mut Stream, name: String, - pos: Pos, file: &mut Zonefile) -> Result { - let res = try!(stream.skip_entry()); - file.add_warning(pos, format!("Unknown control entry '${}'", name)); - Ok(res) -} - diff --git a/src/master/entry.rs b/src/master/entry.rs new file mode 100644 index 00000000..5173dfb3 --- /dev/null +++ b/src/master/entry.rs @@ -0,0 +1,97 @@ + +use std::ascii::AsciiExt; +use std::io; +use std::rc::Rc; +use ::bits::DNameBuf; +use ::iana::Class; +use ::master::{Error, Pos, Result, Stream}; +use ::master::record::{MasterRecord, map_origin}; + + +//------------ Entry --------------------------------------------------------- + +pub enum Entry { + Origin(Rc), + Include { path: Vec, origin: Option> }, + Ttl(u32), + Control { name: Vec, start: Pos }, + Record(MasterRecord) +} + +impl Entry { + pub fn scan(stream: &mut Stream, + last_owner: Option>, + last_class: Option, + origin: &Option>, + default_ttl: Option) + -> Result> { + while let Ok(()) = stream.scan_newline() { } + if let Ok(true) = stream.is_eof() { + return Ok(None) + } + let res = match try!(ControlType::scan_opt(stream)) { + Some(ControlType::Origin) => { + let origin = map_origin(origin); + Entry::Origin(Rc::new(try!(DNameBuf::scan(stream, origin)))) + } + Some(ControlType::Include) => { + Entry::Include { + path: try!(stream.scan_phrase_copy()), + origin: DNameBuf::scan(stream, map_origin(origin)) + .map(|n| Rc::new(n)).ok() + } + } + Some(ControlType::Ttl) => { + Entry::Ttl(try!(stream.scan_u32())) + } + Some(ControlType::Other(name, pos)) => { + Entry::Control { name: name, start: pos } + } + None => { + Entry::Record(try!(MasterRecord::scan(stream, last_owner, + last_class, origin, + default_ttl))) + } + }; + try!(stream.scan_newline()); + Ok(Some(res)) + } +} + + +//------------ ControlType --------------------------------------------------- + +enum ControlType { + Origin, + Include, + Ttl, + Other(Vec, Pos) +} + +impl ControlType { + pub fn scan_opt(stream: &mut Stream) + -> Result> { + let pos = stream.pos(); + match stream.skip_char(b'$') { + Ok(()) => { } + Err(Error::Syntax(..)) => return Ok(None), + Err(err) => return Err(err) + } + stream.scan_word(|word| { + if word.eq_ignore_ascii_case(b"ORIGIN") { + Ok(ControlType::Origin) + } + else if word.eq_ignore_ascii_case(b"INCLUDE") { + Ok(ControlType::Include) + } + else if word.eq_ignore_ascii_case(b"TTL") { + Ok(ControlType::Ttl) + } + else { + // XXX Master-encode non-ASCII characters. + Ok(ControlType::Other(word.to_owned(), pos)) + } + }).map(|x| Some(x)) + } +} + diff --git a/src/master/error.rs b/src/master/error.rs index 33bbde93..99f07d95 100644 --- a/src/master/error.rs +++ b/src/master/error.rs @@ -25,6 +25,7 @@ pub enum SyntaxError { NoDefaultTtl, NoLastClass, NoLastOwner, + NoOrigin, RelativeName, Unexpected(u8), UnexpectedEof, diff --git a/src/master/mod.rs b/src/master/mod.rs index 330c1581..c66754a9 100644 --- a/src/master/mod.rs +++ b/src/master/mod.rs @@ -1,96 +1,11 @@ - -use ::bits::{DNameBuf, DNameSlice}; -use ::bits::record::GenericRecord; -use ::iana::Class; +//! Reading and writing of master files. pub use self::error::{Error, Result, SyntaxError, SyntaxResult}; -pub use self::stream::{Newline, Stream}; +pub use self::stream::{Pos, Stream}; - -pub mod control; +pub mod entry; pub mod error; +pub mod reader; pub mod record; pub mod stream; - -//------------ Zonefile ------------------------------------------------------ - -pub struct Zonefile<'a> { - includes: Vec, - records: Vec>, - origin: DNameBuf, - ttl: Option, - warnings: Vec<(Pos, String)> -} - -impl<'a> Zonefile<'a> { - pub fn origin(&self) -> &DNameSlice { - &self.origin - } - - pub fn set_origin(&mut self, origin: DNameBuf) { - self.origin = origin - } - - pub fn ttl(&self) -> Option { - self.ttl - } - - pub fn set_ttl(&mut self, ttl: u32) { - self.ttl = Some(ttl) - } - - pub fn last_class(&self) -> Option { - unimplemented!() - } - - pub fn last_owner(&self) -> Option { - unimplemented!() - } - - - pub fn add_record(&mut self, record: GenericRecord<'a>) { - self.records.push(record) - } - - pub fn add_include(&mut self, path: Vec, origin: Option) { - self.includes.push(Include { path: path, origin: origin }) - } - - pub fn add_warning(&mut self, pos: Pos, text: String) { - self.warnings.push((pos, text)) - } -} - - -//------------ Include ------------------------------------------------------ - -pub struct Include { - pub path: Vec, - pub origin: Option, -} - - -//------------ Pos ----------------------------------------------------------- - -#[derive(Clone, Copy, Debug)] -pub struct Pos { - line: usize, - col: usize -} - -impl Pos { - pub fn new() -> Pos { - Pos { line: 1, col: 1 } - } - - pub fn update(&mut self, ch: u8) { - match ch { - b'\n' => self.line += 1, - _ => self.col += 1 - } - } -} - - - diff --git a/src/master/reader.rs b/src/master/reader.rs new file mode 100644 index 00000000..03d13b27 --- /dev/null +++ b/src/master/reader.rs @@ -0,0 +1,81 @@ + +use std::io; +use std::rc::Rc; +use ::bits::name::DNameBuf; +use ::iana::Class; +use ::master::entry::Entry; +use ::master::error::Result; +use ::master::record::MasterRecord; +use ::master::stream::Stream; + + +pub struct Reader { + stream: Stream, + origin: Option>, + ttl: Option, + last: Option<(Rc, Class)>, +} + +impl Reader { + pub fn new(reader: R) -> Self { + Reader { + stream: Stream::new(reader), + origin: None, + ttl: None, + last: None + } + } + + fn last_owner(&self) -> Option> { + match &self.last { + &Some((ref name, _)) => Some(name.clone()), + &None => None + } + } + + fn last_class(&self) -> Option { + match &self.last { + &Some((_, class)) => Some(class), + &None => None + } + } + + pub fn next_record(&mut self) -> Result> { + loop { + let last_owner = self.last_owner(); + let last_class = self.last_class(); + match try!(Entry::scan(&mut self.stream, last_owner, + last_class, &self.origin, self.ttl)) { + Some(Entry::Origin(origin)) => self.origin = Some(origin), + Some(Entry::Include{path, origin}) => { + return Ok(Some(ReaderItem::Include { path: path, + origin: origin })) + } + Some(Entry::Ttl(ttl)) => self.ttl = Some(ttl), + Some(Entry::Control{..}) => { }, + Some(Entry::Record(record)) => { + return Ok(Some(ReaderItem::Record(record))) + } + None => return Ok(None) + } + } + } +} + +impl Iterator for Reader { + type Item = Result; + + fn next(&mut self) -> Option> { + match self.next_record() { + Ok(Some(res)) => Some(Ok(res)), + Ok(None) => None, + Err(err) => Some(Err(err)) + } + } +} + + +pub enum ReaderItem { + Record(MasterRecord), + Include { path: Vec, origin: Option> } +} diff --git a/src/master/record.rs b/src/master/record.rs index ad6c097c..40a266bc 100644 --- a/src/master/record.rs +++ b/src/master/record.rs @@ -1,79 +1,114 @@ use std::io; -use ::bits::{DName, DNameBuf}; -use ::bits::nest::{Nest, NestBuf}; -use ::bits::record::GenericRecord; +use std::rc::Rc; +use ::bits::{DNameBuf, DNameSlice}; use ::iana::{Class, RRType}; -use ::master::{Newline, Result, Stream, SyntaxError, Zonefile,}; use ::rdata; +use super::{Result, Stream, SyntaxError}; -pub fn scan_record(stream: &mut Stream, file: &mut Zonefile) - -> Result { - let owner = DName::Owned(try!(scan_owner(stream, file))); - let (ttl, class) = try!(scan_ttl_class(stream, file)); - let rtype = try!(RRType::scan(stream)); - let mut rdata = NestBuf::new(); - try!(rdata::scan_into(rtype, stream, file.origin(), &mut rdata)); - let res = try!(stream.scan_newline()); - file.add_record(GenericRecord::new_generic(owner, class, rtype, ttl, - Nest::Owned(rdata))); - Ok(res) +#[derive(Clone, Debug, PartialEq)] +pub struct MasterRecord { + owner: Rc, + rtype: RRType, + class: Class, + ttl: u32, + rdata: Vec, } -fn scan_owner(stream: &mut Stream, file: &mut Zonefile) - -> Result { - if try!(stream.skip_opt_space()) { - if let Some(name) = file.last_owner() { - stream.ok(name) +impl MasterRecord { + pub fn new(owner: Rc, rtype: RRType, class: Class, ttl: u32, + rdata: Vec) -> Self { + MasterRecord { owner: owner, rtype: rtype, class: class, + ttl: ttl, rdata: rdata } + } +} + +impl MasterRecord { + pub fn scan(stream: &mut Stream, + last_owner: Option>, + last_class: Option, + origin: &Option>, + default_ttl: Option) -> Result { + let owner = try!(MasterRecord::scan_owner(stream, last_owner, + &origin)); + let (ttl, class) = try!(MasterRecord::scan_ttl_class(stream, + default_ttl, + last_class)); + let rtype = try!(RRType::scan(stream)); + let rdata = try!(rdata::scan(rtype, stream, map_origin(origin))); + try!(stream.scan_newline()); + Ok(MasterRecord::new(owner, rtype, class, ttl, rdata)) + } + + /// Scans the owner. + /// + /// Returns new owner and origin. + fn scan_owner(stream: &mut Stream, + last_owner: Option>, + origin: &Option>) + -> Result> { + if try!(stream.skip_opt_space()) { + if let Some(owner) = last_owner { Ok(owner) } + else { stream.err(SyntaxError::NoLastOwner) } + } + else if let Ok(()) = stream.skip_literal(b"@") { + if let &Some(ref origin) = origin { Ok(origin.clone()) } + else { stream.err(SyntaxError::NoOrigin) } } else { - stream.err(SyntaxError::NoLastOwner) + Ok(Rc::new(try!(DNameBuf::scan(stream, map_origin(origin))))) } } - else { - let res = try!(DNameBuf::scan(stream, file.origin())); - try!(stream.skip_opt_space()); - Ok(res) + + fn scan_ttl_class(stream: &mut Stream, + default_ttl: Option, + last_class: Option) + -> Result<(u32, Class)> { + let (ttl, class) = match stream.scan_u32() { + Ok(ttl) => { + try!(stream.skip_opt_space()); + match Class::scan(stream) { + Ok(class) => { + try!(stream.skip_opt_space()); + (Some(ttl), Some(class)) + } + Err(_) => (Some(ttl), None) + } + } + Err(_) => { + match Class::scan(stream) { + Ok(class) => { + try!(stream.skip_opt_space()); + match stream.scan_u32() { + Ok(ttl) => { + try!(stream.skip_opt_space()); + (Some(ttl), Some(class)) + } + Err(_) => (None, Some(class)) + } + } + Err(_) => (None, None) + } + } + }; + let ttl = match ttl.or(default_ttl) { + Some(ttl) => ttl, + None => return stream.err(SyntaxError::NoDefaultTtl) + }; + let class = match class.or(last_class) { + Some(class) => class, + None => return stream.err(SyntaxError::NoLastClass) + }; + Ok((ttl, class)) } } -fn scan_ttl_class(stream: &mut Stream, file: &mut Zonefile) - -> Result<(u32, Class)> { - let (ttl, class) = match stream.scan_u32() { - Ok(ttl) => { - try!(stream.skip_opt_space()); - match Class::scan(stream) { - Ok(class) => { - try!(stream.skip_opt_space()); - (Some(ttl), Some(class)) - } - Err(_) => (Some(ttl), None) - } - } - Err(_) => { - match Class::scan(stream) { - Ok(class) => { - try!(stream.skip_opt_space()); - match stream.scan_u32() { - Ok(ttl) => { - try!(stream.skip_opt_space()); - (Some(ttl), Some(class)) - } - Err(_) => (None, Some(class)) - } - } - Err(_) => (None, None) - } - } - }; - let ttl = match ttl.or(file.ttl()) { - Some(ttl) => ttl, - None => return stream.err(SyntaxError::NoDefaultTtl) - }; - let class = match class.or(file.last_class()) { - Some(class) => class, - None => return stream.err(SyntaxError::NoLastClass) - }; - Ok((ttl, class)) + +pub fn map_origin<'a>(origin: &'a Option>) + -> Option<&'a DNameSlice> { + match origin { + &Some(ref rc) => Some(rc), + &None => None + } } diff --git a/src/master/stream.rs b/src/master/stream.rs index abccf832..e453ffe9 100644 --- a/src/master/stream.rs +++ b/src/master/stream.rs @@ -3,7 +3,6 @@ use std::io; use std::str; use ::master::error::{Error, Result, SyntaxError, SyntaxResult}; -use ::master::Pos; //------------ Stream --------------------------------------------------- @@ -13,6 +12,11 @@ pub struct Stream { paren: bool, } +impl Stream { + pub fn new(reader: R) -> Self { + Stream { buf: Buffer::new(reader), paren: false } + } +} /// Reading. /// @@ -30,6 +34,11 @@ impl Stream { self.buf.peek_char() } + pub fn is_eof(&mut self) -> io::Result { + if let Some(_) = try!(self.peek_char()) { Ok(false) } + else { Ok(true) } + } + pub fn cond_read_char(&mut self, f: F) -> Result> where F: Fn(u8) -> bool { match try!(self.buf.peek_char()) { @@ -332,9 +341,7 @@ impl Stream { self.paren = false } Some(b';') => { - if let Newline::Eof = try!(self.skip_comment()) { - return self.ok(true) - } + try!(self.skip_comment()); } _ => { } } @@ -355,27 +362,27 @@ impl Stream { } } - fn skip_comment(&mut self) -> Result { + fn skip_comment(&mut self) -> Result<()> { match self.skip_until(is_newline) { - Ok(_) => self.ok(Newline::Real), - Err(ref err) if err.is_eof() => self.ok(Newline::Eof), + Ok(_) => self.ok(()), + Err(ref err) if err.is_eof() => self.ok(()), Err(err) => Err(err) } } - pub fn scan_newline(&mut self) -> Result { + pub fn scan_newline(&mut self) -> Result<()> { try!(self.skip_opt_space()); match try!(self.read_char()) { Some(b';') => { self.skip_comment() } - Some(ch) if is_newline(ch) => self.ok(Newline::Real), - None => self.ok(Newline::Eof), + Some(ch) if is_newline(ch) => self.ok(()), + None => self.ok(()), _ => self.err(SyntaxError::ExpectedNewline) } } - pub fn skip_entry(&mut self) -> Result { + pub fn skip_entry(&mut self) -> Result<()> { // We try to skip over space, then break if we find a newline // or try to scan a phrase and start again. // @@ -386,8 +393,8 @@ impl Stream { try!(self.skip_opt_space()); match try!(self.read_char()) { Some(b';') => return self.skip_comment(), - Some(ch) if is_newline(ch) => return self.ok(Newline::Real), - None => return self.ok(Newline::Eof), + Some(ch) if is_newline(ch) => return self.ok(()), + None => return self.ok(()), _ => { } } try!(self.scan_phrase(|_| Ok(()))); @@ -417,14 +424,6 @@ impl Stream { } -//------------ Newline ------------------------------------------------------- - -pub enum Newline { - Real, - Eof -} - - //------------ Buffer -------------------------------------------------------- pub struct Buffer { @@ -436,6 +435,13 @@ pub struct Buffer { curr_pos: Pos } +impl Buffer { + pub fn new(reader: R) -> Self { + Buffer { reader: reader, buf: Vec::new(), start: 0, curr: 0, + start_pos: Pos::new(), curr_pos: Pos::new() } + } +} + impl Buffer { pub fn ok(&mut self) { if self.buf.len() == self.curr { @@ -484,6 +490,28 @@ impl Buffer { } +//------------ Pos ----------------------------------------------------------- + +#[derive(Clone, Copy, Debug)] +pub struct Pos { + line: usize, + col: usize +} + +impl Pos { + pub fn new() -> Pos { + Pos { line: 1, col: 1 } + } + + pub fn update(&mut self, ch: u8) { + match ch { + b'\n' => self.line += 1, + _ => self.col += 1 + } + } +} + + //------------ Tests for character classes ---------------------------------- fn is_digit(ch: u8) -> bool { diff --git a/src/rdata/macros.rs b/src/rdata/macros.rs index 49a16f3d..c0da8bc7 100644 --- a/src/rdata/macros.rs +++ b/src/rdata/macros.rs @@ -6,26 +6,27 @@ macro_rules! master_types { pub use self::$module::{ $( $rtype ),* }; )* - pub fn scan_into(rtype: ::iana::RRType, - stream: &mut ::master::Stream, - origin: &::bits::name::DNameSlice, - target: &mut B) - -> ::master::Result<()> - where R: ::std::io::Read, - B: ::bits::bytes::BytesBuf { + pub fn scan<'a, R>(rtype: ::iana::RRType, + stream: &mut ::master::Stream, + origin: Option<&'a ::bits::name::DNameSlice>) + -> ::master::Result> + where R: ::std::io::Read { use ::bits::rdata::GenericRecordData; // First try the generic format for everything. - let err = match GenericRecordData::scan_into(stream, target) { - Ok(()) => return Ok(()), + let err = match GenericRecordData::scan(stream) { + Ok(some) => return Ok(some), Err(err) => err }; // Now see if we have a master type that can parse this for real. match rtype { $( $( - ::iana::RRType::$rtype => - $rtype::scan_into(stream, origin, target), + ::iana::RRType::$rtype => { + let mut res = Vec::new(); + try!($rtype::scan_into(stream, origin, &mut res)); + Ok(res) + } )* )* // We don’t. Good thing we kept the error. diff --git a/src/rdata/rfc1035.rs b/src/rdata/rfc1035.rs index fbac062b..026e7c5e 100644 --- a/src/rdata/rfc1035.rs +++ b/src/rdata/rfc1035.rs @@ -53,10 +53,10 @@ macro_rules! dname_type { |target| target.push_dname_compressed(value)) } - pub fn scan_into(stream: &mut master::Stream, - origin: &DNameSlice, target: &mut B) - -> master::Result<()> - where R: io::Read, B: BytesBuf { + pub fn scan_into(stream: &mut master::Stream, + origin: Option<&DNameSlice>, + target: &mut Vec) + -> master::Result<()> { DNameBuf::scan_into(stream, origin, target) } } @@ -79,6 +79,7 @@ macro_rules! dname_type { } } + impl<'a> fmt::Display for $target<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.$field.fmt(f) @@ -143,10 +144,10 @@ impl A { try!(parser.parse_u8())))) } - pub fn scan_into(stream: &mut master::Stream, - _origin: &DNameSlice, target: &mut B) - -> master::Result<()> - where R: io::Read, B: BytesBuf { + pub fn scan_into(stream: &mut master::Stream, + _origin: Option<&DNameSlice>, + target: &mut Vec) + -> master::Result<()> { stream.scan_str_phrase(|slice| { let addr = try!(Ipv4Addr::from_str(slice)); target.push_bytes(&addr.octets()[..]); @@ -172,6 +173,7 @@ impl<'a> RecordData<'a> for A { } } + impl fmt::Display for A { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.addr.fmt(f) @@ -230,10 +232,10 @@ impl<'a> Hinfo<'a> { try!(parser.parse_charstr()))) } - pub fn scan_into(stream: &mut master::Stream, - _origin: &DNameSlice, target: &mut B) - -> master::Result<()> - where R: io::Read, B: BytesBuf { + pub fn scan_into(stream: &mut master::Stream, + _origin: Option<&DNameSlice>, + target: &mut Vec) + -> master::Result<()> { try!(CharStr::scan_into(stream, target)); CharStr::scan_into(stream, target) } @@ -255,7 +257,6 @@ impl<'a> RecordData<'a> for Hinfo<'a> { } } - impl<'a> fmt::Display for Hinfo<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} {}", self.cpu, self.os) @@ -381,10 +382,10 @@ impl<'a> Minfo<'a> { try!(parser.parse_dname()))) } - pub fn scan_into(stream: &mut master::Stream, - origin: &DNameSlice, target: &mut B) - -> master::Result<()> - where R: io::Read, B: BytesBuf { + pub fn scan_into(stream: &mut master::Stream, + origin: Option<&DNameSlice>, + target: &mut Vec) + -> master::Result<()> { try!(DNameBuf::scan_into(stream, origin, target)); DNameBuf::scan_into(stream, origin, target) } @@ -470,10 +471,10 @@ impl<'a> Mx<'a> { try!(parser.parse_dname()))) } - pub fn scan_into(stream: &mut master::Stream, - origin: &DNameSlice, target: &mut B) - -> master::Result<()> - where R: io::Read, B: BytesBuf { + pub fn scan_into(stream: &mut master::Stream, + origin: Option<&DNameSlice>, + target: &mut Vec) + -> master::Result<()> { target.push_u16(try!(stream.scan_u16())); DNameBuf::scan_into(stream, origin, target) } @@ -664,10 +665,10 @@ impl<'a> Soa<'a> { try!(parser.parse_u32()))) } - pub fn scan_into(stream: &mut master::Stream, - origin: &DNameSlice, target: &mut B) - -> master::Result<()> - where R: io::Read, B: BytesBuf { + pub fn scan_into(stream: &mut master::Stream, + origin: Option<&DNameSlice>, + target: &mut Vec) + -> master::Result<()> { try!(DNameBuf::scan_into(stream, origin, target)); try!(DNameBuf::scan_into(stream, origin, target)); target.push_u32(try!(stream.scan_u32())); @@ -760,10 +761,10 @@ impl<'a> Txt<'a> { Ok(Txt::new(Cow::Borrowed(try!(parser.parse_bytes(len))))) } - pub fn scan_into(stream: &mut master::Stream, - _origin: &DNameSlice, target: &mut B) - -> master::Result<()> - where R: io::Read, B: BytesBuf { + pub fn scan_into(stream: &mut master::Stream, + _origin: Option<&DNameSlice>, + target: &mut Vec) + -> master::Result<()> { // XXX Try to get rid of the allocation, please. let text = try!(stream.scan_phrase_copy()); let mut text = &text[..]; @@ -900,10 +901,10 @@ impl<'a> Wks<'a> { /// Scan the master file representation of a WKS record into a target. /// - pub fn scan_into(stream: &mut master::Stream, - _origin: &DNameSlice, target: &mut B) - -> master::Result<()> - where R: io::Read, B: BytesBuf { + pub fn scan_into(stream: &mut master::Stream, + _origin: Option<&DNameSlice>, + target: &mut Vec) + -> master::Result<()> { try!(A::scan_into(stream, _origin, target)); try!(stream.scan_str_phrase(|s| { if let Some(ent) = ProtoEnt::by_name(s) { @@ -938,7 +939,6 @@ impl<'a> Wks<'a> { } } - impl<'a> RecordData<'a> for Wks<'a> { fn rtype(&self) -> RRType { RRType::Wks } diff --git a/src/rdata/rfc3596.rs b/src/rdata/rfc3596.rs index 7a22b221..c32537d5 100644 --- a/src/rdata/rfc3596.rs +++ b/src/rdata/rfc3596.rs @@ -45,19 +45,6 @@ impl Aaaa { }) } - pub fn scan_into(stream: &mut master::Stream, - _origin: &DNameSlice, target: &mut B) - -> master::Result<()> - where R: io::Read, B: BytesBuf { - stream.scan_str_phrase(|slice| { - let addr = try!(Ipv6Addr::from_str(slice)); - for i in addr.segments().iter() { - target.push_u16(*i) - } - Ok(()) - }) - } - fn parse_always<'a, P>(parser: &mut P) -> ParseResult where P: ParseBytes<'a> { Ok(Aaaa::new(Ipv6Addr::new(try!(parser.parse_u16()), @@ -69,6 +56,19 @@ impl Aaaa { try!(parser.parse_u16()), try!(parser.parse_u16())))) } + + pub fn scan_into(stream: &mut master::Stream, + _origin: Option<&DNameSlice>, + target: &mut Vec) + -> master::Result<()> { + stream.scan_str_phrase(|slice| { + let addr = try!(Ipv6Addr::from_str(slice)); + for i in addr.segments().iter() { + target.push_u16(*i) + } + Ok(()) + }) + } } impl<'a> RecordData<'a> for Aaaa {