Third part of zonefile parsing.

This commit is contained in:
Martin Hoffmann
2016-07-25 17:21:18 +02:00
parent 0b08f07d94
commit 0ab006264f
15 changed files with 514 additions and 395 deletions
+1 -1
View File
@@ -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;
+102 -56
View File
@@ -178,6 +178,15 @@ impl<'a> From<PackedDName<'a>> for DName<'a> {
}
}
impl<'a> From<Cow<'a, DNameSlice>> 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<R>,
origin: Option<&'a DNameSlice>)
-> master::Result<Cow<'a, DNameSlice>> {
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<DNameSlice> for DNameSlice {
@@ -735,77 +767,91 @@ impl DNameBuf {
Ok(try!(try!(PackedDName::parse(parser, context)).to_owned()))
}
pub fn scan_absolute<R: io::Read>(stream: &mut master::Stream<R>)
-> master::Result<Self> {
let res = try!(DNameBuf::_scan(stream));
if res.is_relative() {
stream.err(master::SyntaxError::RelativeName)
}
else {
stream.ok(res)
}
}
pub fn scan<R, N>(stream: &mut master::Stream<R>, origin: N)
-> master::Result<Self>
where R: io::Read, N: AsRef<DNameSlice> {
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<R: io::Read>(stream: &mut master::Stream<R>)
-> master::Result<Self> {
// 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<R: io::Read>(stream: &mut master::Stream<R>,
target: &mut Vec<u8>)
-> master::Result<bool> {
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<R, N, B>(stream: &mut master::Stream<R>, origin: N,
target: &mut B) -> master::Result<()>
where R: io::Read, N: AsRef<DNameSlice>,
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<R: io::Read>(stream: &mut master::Stream<R>,
origin: Option<&DNameSlice>,
target: &mut Vec<u8>)
-> 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<R: io::Read>(stream: &mut master::Stream<R>,
origin: Option<&DNameSlice>)
-> master::Result<DNameBuf> {
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<u8> {
self.inner
}
}
+9 -1
View File
@@ -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<u8>) -> Self {
NestBuf(vec)
}
/// Returns a mutable reference to the nest’s content.
pub fn as_mut_vec(&mut self) -> &mut Vec<u8> { &mut self.0 }
/// Parses an owned nest.
pub fn parse<'a, P>(p: &mut P, len: usize) -> ParseResult<Self>
where P: ParseBytes<'a> {
+11 -4
View File
@@ -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<R, B>(stream: &mut master::Stream<R>, target: &mut B)
-> master::Result<()>
where R: io::Read, B: BytesBuf {
pub fn scan_into<R: io::Read>(stream: &mut master::Stream<R>,
target: &mut Vec<u8>)
-> 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<R: io::Read>(stream: &mut master::Stream<R>)
-> master::Result<Vec<u8>> {
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 {
+2
View File
@@ -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.
-102
View File
@@ -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<R: io::Read>(stream: &mut Stream<R>,
file: &mut Zonefile)
-> Result<Option<Newline>> {
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<R: io::Read>(stream: &mut Stream<R>)
-> Result<Option<Self>> {
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<R: io::Read>(stream: &mut Stream<R>,
file: &mut Zonefile) -> Result<Newline> {
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<R: io::Read>(stream: &mut Stream<R>,
file: &mut Zonefile) -> Result<Newline> {
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<R: io::Read>(stream: &mut Stream<R>,
file: &mut Zonefile) -> Result<Newline> {
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<R: io::Read>(stream: &mut Stream<R>, name: String,
pos: Pos, file: &mut Zonefile) -> Result<Newline> {
let res = try!(stream.skip_entry());
file.add_warning(pos, format!("Unknown control entry '${}'", name));
Ok(res)
}
+97
View File
@@ -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<DNameBuf>),
Include { path: Vec<u8>, origin: Option<Rc<DNameBuf>> },
Ttl(u32),
Control { name: Vec<u8>, start: Pos },
Record(MasterRecord)
}
impl Entry {
pub fn scan<R: io::Read>(stream: &mut Stream<R>,
last_owner: Option<Rc<DNameBuf>>,
last_class: Option<Class>,
origin: &Option<Rc<DNameBuf>>,
default_ttl: Option<u32>)
-> Result<Option<Self>> {
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<u8>, Pos)
}
impl ControlType {
pub fn scan_opt<R: io::Read>(stream: &mut Stream<R>)
-> Result<Option<Self>> {
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))
}
}
+1
View File
@@ -25,6 +25,7 @@ pub enum SyntaxError {
NoDefaultTtl,
NoLastClass,
NoLastOwner,
NoOrigin,
RelativeName,
Unexpected(u8),
UnexpectedEof,
+4 -89
View File
@@ -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<Include>,
records: Vec<GenericRecord<'a>>,
origin: DNameBuf,
ttl: Option<u32>,
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<u32> {
self.ttl
}
pub fn set_ttl(&mut self, ttl: u32) {
self.ttl = Some(ttl)
}
pub fn last_class(&self) -> Option<Class> {
unimplemented!()
}
pub fn last_owner(&self) -> Option<DNameBuf> {
unimplemented!()
}
pub fn add_record(&mut self, record: GenericRecord<'a>) {
self.records.push(record)
}
pub fn add_include(&mut self, path: Vec<u8>, origin: Option<DNameBuf>) {
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<u8>,
pub origin: Option<DNameBuf>,
}
//------------ 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
}
}
}
+81
View File
@@ -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<R: io::Read> {
stream: Stream<R>,
origin: Option<Rc<DNameBuf>>,
ttl: Option<u32>,
last: Option<(Rc<DNameBuf>, Class)>,
}
impl<R: io::Read> Reader<R> {
pub fn new(reader: R) -> Self {
Reader {
stream: Stream::new(reader),
origin: None,
ttl: None,
last: None
}
}
fn last_owner(&self) -> Option<Rc<DNameBuf>> {
match &self.last {
&Some((ref name, _)) => Some(name.clone()),
&None => None
}
}
fn last_class(&self) -> Option<Class> {
match &self.last {
&Some((_, class)) => Some(class),
&None => None
}
}
pub fn next_record(&mut self) -> Result<Option<ReaderItem>> {
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<R: io::Read> Iterator for Reader<R> {
type Item = Result<ReaderItem>;
fn next(&mut self) -> Option<Result<ReaderItem>> {
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<u8>, origin: Option<Rc<DNameBuf>> }
}
+98 -63
View File
@@ -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<R: io::Read>(stream: &mut Stream<R>, file: &mut Zonefile)
-> Result<Newline> {
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<DNameBuf>,
rtype: RRType,
class: Class,
ttl: u32,
rdata: Vec<u8>,
}
fn scan_owner<R: io::Read>(stream: &mut Stream<R>, file: &mut Zonefile)
-> Result<DNameBuf> {
if try!(stream.skip_opt_space()) {
if let Some(name) = file.last_owner() {
stream.ok(name)
impl MasterRecord {
pub fn new(owner: Rc<DNameBuf>, rtype: RRType, class: Class, ttl: u32,
rdata: Vec<u8>) -> Self {
MasterRecord { owner: owner, rtype: rtype, class: class,
ttl: ttl, rdata: rdata }
}
}
impl MasterRecord {
pub fn scan<R: io::Read>(stream: &mut Stream<R>,
last_owner: Option<Rc<DNameBuf>>,
last_class: Option<Class>,
origin: &Option<Rc<DNameBuf>>,
default_ttl: Option<u32>) -> Result<Self> {
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<R: io::Read>(stream: &mut Stream<R>,
last_owner: Option<Rc<DNameBuf>>,
origin: &Option<Rc<DNameBuf>>)
-> Result<Rc<DNameBuf>> {
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<R: io::Read>(stream: &mut Stream<R>,
default_ttl: Option<u32>,
last_class: Option<Class>)
-> 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<R: io::Read>(stream: &mut Stream<R>, 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<Rc<DNameBuf>>)
-> Option<&'a DNameSlice> {
match origin {
&Some(ref rc) => Some(rc),
&None => None
}
}
+49 -21
View File
@@ -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<R: io::Read> {
paren: bool,
}
impl<R: io::Read> Stream<R> {
pub fn new(reader: R) -> Self {
Stream { buf: Buffer::new(reader), paren: false }
}
}
/// Reading.
///
@@ -30,6 +34,11 @@ impl<R: io::Read> Stream<R> {
self.buf.peek_char()
}
pub fn is_eof(&mut self) -> io::Result<bool> {
if let Some(_) = try!(self.peek_char()) { Ok(false) }
else { Ok(true) }
}
pub fn cond_read_char<F>(&mut self, f: F) -> Result<Option<u8>>
where F: Fn(u8) -> bool {
match try!(self.buf.peek_char()) {
@@ -332,9 +341,7 @@ impl<R: io::Read> Stream<R> {
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<R: io::Read> Stream<R> {
}
}
fn skip_comment(&mut self) -> Result<Newline> {
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<Newline> {
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<Newline> {
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<R: io::Read> Stream<R> {
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<R: io::Read> Stream<R> {
}
//------------ Newline -------------------------------------------------------
pub enum Newline {
Real,
Eof
}
//------------ Buffer --------------------------------------------------------
pub struct Buffer<R: io::Read> {
@@ -436,6 +435,13 @@ pub struct Buffer<R: io::Read> {
curr_pos: Pos
}
impl<R: io::Read> Buffer<R> {
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<R: io::Read> Buffer<R> {
pub fn ok(&mut self) {
if self.buf.len() == self.curr {
@@ -484,6 +490,28 @@ impl<R: io::Read> Buffer<R> {
}
//------------ 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 {
+12 -11
View File
@@ -6,26 +6,27 @@ macro_rules! master_types {
pub use self::$module::{ $( $rtype ),* };
)*
pub fn scan_into<R, B>(rtype: ::iana::RRType,
stream: &mut ::master::Stream<R>,
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<R>,
origin: Option<&'a ::bits::name::DNameSlice>)
-> ::master::Result<Vec<u8>>
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.
+34 -34
View File
@@ -53,10 +53,10 @@ macro_rules! dname_type {
|target| target.push_dname_compressed(value))
}
pub fn scan_into<R, B>(stream: &mut master::Stream<R>,
origin: &DNameSlice, target: &mut B)
-> master::Result<()>
where R: io::Read, B: BytesBuf {
pub fn scan_into<R: io::Read>(stream: &mut master::Stream<R>,
origin: Option<&DNameSlice>,
target: &mut Vec<u8>)
-> 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<R, B>(stream: &mut master::Stream<R>,
_origin: &DNameSlice, target: &mut B)
-> master::Result<()>
where R: io::Read, B: BytesBuf {
pub fn scan_into<R: io::Read>(stream: &mut master::Stream<R>,
_origin: Option<&DNameSlice>,
target: &mut Vec<u8>)
-> 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<R, B>(stream: &mut master::Stream<R>,
_origin: &DNameSlice, target: &mut B)
-> master::Result<()>
where R: io::Read, B: BytesBuf {
pub fn scan_into<R: io::Read>(stream: &mut master::Stream<R>,
_origin: Option<&DNameSlice>,
target: &mut Vec<u8>)
-> 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<R, B>(stream: &mut master::Stream<R>,
origin: &DNameSlice, target: &mut B)
-> master::Result<()>
where R: io::Read, B: BytesBuf {
pub fn scan_into<R: io::Read>(stream: &mut master::Stream<R>,
origin: Option<&DNameSlice>,
target: &mut Vec<u8>)
-> 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<R, B>(stream: &mut master::Stream<R>,
origin: &DNameSlice, target: &mut B)
-> master::Result<()>
where R: io::Read, B: BytesBuf {
pub fn scan_into<R: io::Read>(stream: &mut master::Stream<R>,
origin: Option<&DNameSlice>,
target: &mut Vec<u8>)
-> 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<R, B>(stream: &mut master::Stream<R>,
origin: &DNameSlice, target: &mut B)
-> master::Result<()>
where R: io::Read, B: BytesBuf {
pub fn scan_into<R: io::Read>(stream: &mut master::Stream<R>,
origin: Option<&DNameSlice>,
target: &mut Vec<u8>)
-> 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<R, B>(stream: &mut master::Stream<R>,
_origin: &DNameSlice, target: &mut B)
-> master::Result<()>
where R: io::Read, B: BytesBuf {
pub fn scan_into<R: io::Read>(stream: &mut master::Stream<R>,
_origin: Option<&DNameSlice>,
target: &mut Vec<u8>)
-> 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<R, B>(stream: &mut master::Stream<R>,
_origin: &DNameSlice, target: &mut B)
-> master::Result<()>
where R: io::Read, B: BytesBuf {
pub fn scan_into<R: io::Read>(stream: &mut master::Stream<R>,
_origin: Option<&DNameSlice>,
target: &mut Vec<u8>)
-> 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 }
+13 -13
View File
@@ -45,19 +45,6 @@ impl Aaaa {
})
}
pub fn scan_into<R, B>(stream: &mut master::Stream<R>,
_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<Self>
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<R: io::Read>(stream: &mut master::Stream<R>,
_origin: Option<&DNameSlice>,
target: &mut Vec<u8>)
-> 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 {