compiling changes for parser logic! woohoo!
This commit is contained in:
parent
179be5618e
commit
376273f2f2
@ -16,6 +16,7 @@
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::num;
|
||||
use std::io;
|
||||
|
||||
use super::DecodeError;
|
||||
use super::LexerError;
|
||||
@ -26,6 +27,14 @@ pub enum ParseError {
|
||||
LexerError(LexerError),
|
||||
DecodeError(DecodeError),
|
||||
UnrecognizedToken(Token),
|
||||
OriginIsUndefined,
|
||||
RecordTypeNotSpecified,
|
||||
RecordNameNotSpecified,
|
||||
RecordClassNotSpecified,
|
||||
RecordTTLNotSpecified,
|
||||
RecordDataNotSpecified,
|
||||
SoaAlreadySpecified,
|
||||
IoError(io::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseError {
|
||||
@ -34,6 +43,14 @@ impl fmt::Display for ParseError {
|
||||
ParseError::LexerError(ref err) => err.fmt(f),
|
||||
ParseError::DecodeError(ref err) => err.fmt(f),
|
||||
ParseError::UnrecognizedToken(ref t) => write!(f, "Unrecognized Token in stream: {:?}", t),
|
||||
ParseError::OriginIsUndefined => write!(f, "$ORIGIN was not specified"),
|
||||
ParseError::RecordTypeNotSpecified => write!(f, "Record type not specified"),
|
||||
ParseError::RecordNameNotSpecified => write!(f, "Record name not specified"),
|
||||
ParseError::RecordClassNotSpecified => write!(f, "Record class not specified"),
|
||||
ParseError::RecordTTLNotSpecified => write!(f, "Record ttl not specified"),
|
||||
ParseError::RecordDataNotSpecified => write!(f, "Record data not specified"),
|
||||
ParseError::SoaAlreadySpecified => write!(f, "SOA is already specified"),
|
||||
ParseError::IoError(ref err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -43,13 +60,23 @@ impl Error for ParseError {
|
||||
match *self {
|
||||
ParseError::LexerError(ref err) => err.description(),
|
||||
ParseError::DecodeError(ref err) => err.description(),
|
||||
ParseError::UnrecognizedToken(..) => "Unrecognized Token"
|
||||
ParseError::UnrecognizedToken(..) => "Unrecognized Token",
|
||||
ParseError::OriginIsUndefined => "$ORIGIN was not specified",
|
||||
ParseError::RecordTypeNotSpecified => "Record type not specified",
|
||||
ParseError::RecordNameNotSpecified => "Record name not specified",
|
||||
ParseError::RecordClassNotSpecified => "Record class not specified",
|
||||
ParseError::RecordTTLNotSpecified => "Record ttl not specified",
|
||||
ParseError::RecordDataNotSpecified => "Record data not specified",
|
||||
ParseError::SoaAlreadySpecified => "SOA is already specified",
|
||||
ParseError::IoError(ref err) => err.description(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
match *self {
|
||||
ParseError::LexerError(ref err) => Some(err),
|
||||
ParseError::DecodeError(ref err) => Some(err),
|
||||
ParseError::IoError(ref err) => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -66,3 +93,9 @@ impl From<DecodeError> for ParseError {
|
||||
ParseError::DecodeError(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for ParseError {
|
||||
fn from(err: io::Error) -> ParseError {
|
||||
ParseError::IoError(err)
|
||||
}
|
||||
}
|
||||
|
33
src/rr/authority.rs
Normal file
33
src/rr/authority.rs
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Benjamin Fry <benjaminfry@me.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ::serialize::txt::*;
|
||||
use ::error::*;
|
||||
use ::rr::{RecordType, Record, Name};
|
||||
|
||||
/// Authority is the storage method for all
|
||||
///
|
||||
pub struct Authority {
|
||||
origin: Name,
|
||||
records: HashMap<(Name, RecordType), Vec<Record>>,
|
||||
}
|
||||
|
||||
impl Authority {
|
||||
pub fn new(origin: Name, records: HashMap<(Name, RecordType), Vec<Record>>) -> Authority {
|
||||
Authority{ origin: origin, records: records }
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
use std::convert::From;
|
||||
use std::convert::From;
|
||||
use ::serialize::binary::*;
|
||||
use ::error::*;
|
||||
|
||||
|
@ -18,7 +18,7 @@ use std::ops::Index;
|
||||
use ::serialize::binary::*;
|
||||
use ::error::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
pub struct Name {
|
||||
labels: Vec<String>
|
||||
}
|
||||
@ -36,6 +36,32 @@ impl Name {
|
||||
self.labels.push(label);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn append(&mut self, other: &Self) -> &mut Self {
|
||||
for s in &other.labels {
|
||||
self.add_label(s.to_string());
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn parse(local: String, origin: &Option<Self>) -> ParseResult<Self> {
|
||||
let mut build = Name::new();
|
||||
// split the local part
|
||||
|
||||
// TODO: this should be a real lexer, to varify all data is legal name...
|
||||
for s in local.split('.') {
|
||||
if s.len() > 0 {
|
||||
build.add_label(s.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
if !local.ends_with('.') {
|
||||
build.append(try!(origin.as_ref().ok_or(ParseError::OriginIsUndefined)));
|
||||
}
|
||||
|
||||
Ok(build)
|
||||
}
|
||||
}
|
||||
|
||||
impl BinSerializable for Name {
|
||||
|
@ -18,11 +18,13 @@ pub mod dns_class;
|
||||
pub mod resource;
|
||||
pub mod record_data;
|
||||
pub mod domain;
|
||||
mod authority;
|
||||
|
||||
pub use self::record_type::RecordType;
|
||||
pub use self::resource::Record;
|
||||
pub use self::domain::Name;
|
||||
pub use self::dns_class::DNSClass;
|
||||
pub use self::record_data::RData;
|
||||
pub use self::authority::Authority;
|
||||
|
||||
mod rdata;
|
||||
|
@ -65,7 +65,7 @@ pub fn emit(encoder: &mut BinEncoder, a: &RData) -> EncodeResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(tokens: Vec<Token>) -> ParseResult<RData> {
|
||||
pub fn parse(tokens: &Vec<Token>) -> ParseResult<RData> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ pub fn emit(encoder: &mut BinEncoder, aaaa: &RData) -> EncodeResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(tokens: Vec<Token>) -> ParseResult<RData> {
|
||||
pub fn parse(tokens: &Vec<Token>) -> ParseResult<RData> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ pub fn emit(encoder: &mut BinEncoder, cname_data: &RData) -> EncodeResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(tokens: Vec<Token>) -> ParseResult<RData> {
|
||||
pub fn parse(tokens: &Vec<Token>) -> ParseResult<RData> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ pub fn emit(encoder: &mut BinEncoder, mx: &RData) -> EncodeResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(tokens: Vec<Token>) -> ParseResult<RData> {
|
||||
pub fn parse(tokens: &Vec<Token>) -> ParseResult<RData> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,6 @@ pub fn emit(encoder: &mut BinEncoder, ns: &RData) -> EncodeResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(tokens: Vec<Token>) -> ParseResult<RData> {
|
||||
pub fn parse(tokens: &Vec<Token>) -> ParseResult<RData> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
@ -60,6 +60,6 @@ pub fn emit(encoder: &mut BinEncoder, nil: &RData) -> EncodeResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(tokens: Vec<Token>) -> ParseResult<RData> {
|
||||
pub fn parse(tokens: &Vec<Token>) -> ParseResult<RData> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
@ -50,6 +50,6 @@ pub fn emit(encoder: &mut BinEncoder, ptr: &RData) -> EncodeResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(tokens: Vec<Token>) -> ParseResult<RData> {
|
||||
pub fn parse(tokens: &Vec<Token>) -> ParseResult<RData> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
@ -112,6 +112,6 @@ pub fn emit(encoder: &mut BinEncoder, soa: &RData) -> EncodeResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(tokens: Vec<Token>) -> ParseResult<RData> {
|
||||
pub fn parse(tokens: &Vec<Token>) -> ParseResult<RData> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
@ -54,6 +54,6 @@ pub fn emit(encoder: &mut BinEncoder, txt: &RData) -> EncodeResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(tokens: Vec<Token>) -> ParseResult<RData> {
|
||||
pub fn parse(tokens: &Vec<Token>) -> ParseResult<RData> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
@ -329,7 +329,7 @@ pub enum RData {
|
||||
}
|
||||
|
||||
impl RData {
|
||||
fn parse(record_type: RecordType, tokens: Vec<Token>) -> ParseResult<Self> {
|
||||
pub fn parse(record_type: RecordType, tokens: &Vec<Token>) -> ParseResult<Self> {
|
||||
match record_type {
|
||||
RecordType::CNAME => rdata::cname::parse(tokens),
|
||||
RecordType::MX => rdata::mx::parse(tokens),
|
||||
|
@ -103,6 +103,7 @@ impl Record {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&mut self, name: domain::Name) -> &mut Self { self.name_labels = name; self }
|
||||
pub fn add_name(&mut self, label: String) -> &mut Self { self.name_labels.add_label(label); self }
|
||||
pub fn rr_type(&mut self, rr_type: RecordType) -> &mut Self { self.rr_type = rr_type; self }
|
||||
pub fn dns_class(&mut self, dns_class: DNSClass) -> &mut Self { self.dns_class = dns_class; self }
|
||||
|
@ -24,6 +24,7 @@ use ::rr::RecordType;
|
||||
use ::rr::Record;
|
||||
use ::rr::DNSClass;
|
||||
use ::rr::RData;
|
||||
use ::rr::Authority;
|
||||
|
||||
use super::master_lex::{Lexer, Token};
|
||||
|
||||
@ -125,47 +126,74 @@ use super::master_lex::{Lexer, Token};
|
||||
//
|
||||
// ; Semicolon is used to start a comment; the remainder of
|
||||
// the line is ignored.
|
||||
pub struct Parser {
|
||||
records: HashMap<RecordType, Record>,
|
||||
origin: Option<Name>,
|
||||
}
|
||||
pub struct Parser;
|
||||
|
||||
impl Parser {
|
||||
pub fn new() -> Self {
|
||||
Parser { records: HashMap::new(), origin: None }
|
||||
Parser
|
||||
}
|
||||
|
||||
pub fn parse(&mut self, file: File) {
|
||||
let mut lexer = Lexer::with_chars(file.chars());
|
||||
let mut previous_name: Option<Name> = None;
|
||||
pub fn parse(&mut self, file: File, origin: Option<Name>) -> ParseResult<Authority> {
|
||||
let mut records: HashMap<(Name, RecordType), Vec<Record>> = HashMap::new();
|
||||
let mut buf = String::new();
|
||||
let mut file = file;
|
||||
|
||||
// TODO, this should really use something to read line by line or some other method to
|
||||
// keep the usage down.
|
||||
try!(file.read_to_string(&mut buf));
|
||||
let mut lexer = Lexer::new(&buf);
|
||||
|
||||
let mut origin: Option<Name> = origin;
|
||||
let mut current_name: Option<Name> = None;
|
||||
let mut rtype: Option<RecordType> = None;
|
||||
let mut ttl: Option<i32> = None;
|
||||
let mut class: Option<DNSClass> = None;
|
||||
let mut state = State::StartLine;
|
||||
let mut tokens: Vec<Token> = Vec::new();
|
||||
|
||||
while let Some(t) = lexer.next_token() {
|
||||
while let Some(t) = try!(lexer.next_token()) {
|
||||
state = match state {
|
||||
State::StartLine => {
|
||||
// current_name is not reset on the next line b/c it might be needed from the previous
|
||||
rtype = None;
|
||||
ttl = None;
|
||||
class = None;
|
||||
tokens.clear();
|
||||
|
||||
match t {
|
||||
// if Dollar, then $INCLUDE or $ORIGIN
|
||||
Token::Dollar("INCLUDE") => unimplemented!(),
|
||||
Token::Dollar("ORIGIN") => unimplemented!(),
|
||||
Token::Include => unimplemented!(),
|
||||
Token::Origin => State::Origin,
|
||||
|
||||
// if CharData, then Name then ttl_class_type
|
||||
Token::CharData(ref data) => unimplemented!(),
|
||||
Token::CharData(data) => {
|
||||
current_name = Some(try!(Name::parse(data, &origin)));
|
||||
State::Ttl_Class_Type
|
||||
},
|
||||
|
||||
// @ is a placeholder for specifying the current origin
|
||||
Token::At => {
|
||||
current_name = origin.clone(); // TODO a COW or RC would reduce copies...
|
||||
State::Ttl_Class_Type
|
||||
}
|
||||
|
||||
// if blank, then nothing or ttl_class_type... grr...
|
||||
Token::Blank => unimplemented!(),
|
||||
Token::Blank => {
|
||||
State::Ttl_Class_Type
|
||||
},
|
||||
Token::EOL => State::StartLine, // probably a comment
|
||||
// _ => return Err(ParseError::UnrecognizedToken(t)),
|
||||
_ => return Err(ParseError::UnrecognizedToken(t)),
|
||||
}
|
||||
},
|
||||
State::Origin => {
|
||||
match t {
|
||||
Token::CharData(data) => {
|
||||
// TODO an origin was specified, should this be legal? definitely confusing...
|
||||
origin = Some(try!(Name::parse(data, &None)));
|
||||
State::StartLine
|
||||
}
|
||||
_ => return Err(ParseError::UnrecognizedToken(t)),
|
||||
}
|
||||
}
|
||||
State::Include => unimplemented!(),
|
||||
State::Ttl_Class_Type => {
|
||||
match t {
|
||||
// if number, TTL
|
||||
@ -173,27 +201,29 @@ impl Parser {
|
||||
// One of Class or Type (these cannot be overlapping!)
|
||||
Token::CharData(ref data) => {
|
||||
// if it's a number it's a ttl
|
||||
let result = i32::from_str(data);
|
||||
let result = data.parse();
|
||||
if result.is_ok() {
|
||||
if ttl.is_some() { return Err(ParseError::UnrecognizedToken(t.clone())) } // ideally there is no copy in normal usage
|
||||
ttl = result.ok();
|
||||
return State::Ttl_Class_Type
|
||||
State::Ttl_Class_Type
|
||||
} else {
|
||||
// if can parse DNSClass, then class
|
||||
let result = DNSClass::from_str(data);
|
||||
if result.is_ok() {
|
||||
class = result.ok();
|
||||
State::Ttl_Class_Type
|
||||
} else {
|
||||
// if can parse RecordType, then RecordType
|
||||
rtype = Some(try!(RecordType::from_str(data)));
|
||||
State::Record
|
||||
}
|
||||
}
|
||||
|
||||
// if can parse DNSClass, then class
|
||||
let result = DNSClass::from_str(data);
|
||||
if result.is_ok() {
|
||||
class = Some(result);
|
||||
return State::Ttl_Class_Type
|
||||
}
|
||||
|
||||
// if can parse RecordType, then RecordType
|
||||
rtype = try!(RecordType::from_str(data));
|
||||
State::Record
|
||||
}
|
||||
// could be nothing if started with blank and is a comment, i.e. EOL
|
||||
Token::EOL => {
|
||||
State::StartLine // next line
|
||||
}
|
||||
},
|
||||
_ => return Err(ParseError::UnrecognizedToken(t)),
|
||||
}
|
||||
},
|
||||
State::Record => {
|
||||
@ -206,14 +236,60 @@ impl Parser {
|
||||
},
|
||||
State::EndRecord => {
|
||||
// call out to parsers for difference record types
|
||||
let RData = try!(RData::parse(tokens));
|
||||
let rdata = try!(RData::parse(try!(rtype.ok_or(ParseError::RecordTypeNotSpecified)), &tokens));
|
||||
|
||||
// verify that we have everything we need for the record
|
||||
let mut record = Record::new();
|
||||
// TODO COW or RC would reduce mem usage, perhaps Name should have an intern()...
|
||||
// might want to wait until RC.weak() stabilizes, as that would be needed for global
|
||||
// memory where you want
|
||||
record.name(try!(current_name.clone().ok_or(ParseError::RecordNameNotSpecified)));
|
||||
record.rr_type(rtype.unwrap());
|
||||
record.dns_class(try!(class.ok_or(ParseError::RecordClassNotSpecified)));
|
||||
|
||||
// slightly annoying, need to grab the TTL, then move rdata into the record,
|
||||
// then check the Type again and have custom add logic.
|
||||
match rtype.unwrap() {
|
||||
RecordType::SOA => {
|
||||
// TTL for the SOA is set internally...
|
||||
// expire is for the SOA, minimum is default for records
|
||||
if let RData::SOA { ref expire, ref minimum, ..} = rdata {
|
||||
record.ttl(*expire);
|
||||
ttl = Some(*minimum as i32);
|
||||
} else { assert!(false, "Invalid RData here, expected SOA: {:?}", rdata); }
|
||||
},
|
||||
_ => {
|
||||
record.ttl(try!(ttl.ok_or(ParseError::RecordTTLNotSpecified)));
|
||||
},
|
||||
}
|
||||
|
||||
// move the rdata into record...
|
||||
record.rdata(rdata);
|
||||
|
||||
// add to the map
|
||||
let key = (record.get_name().clone(), record.get_rr_type());
|
||||
|
||||
match rtype.unwrap() {
|
||||
RecordType::SOA => {
|
||||
if records.insert(key, vec![record]).is_some() {
|
||||
return Err(ParseError::SoaAlreadySpecified);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// add a Vec if it's not there, then add the record to the list
|
||||
let mut records = records.entry(key).or_insert(Vec::with_capacity(1));
|
||||
records.push(record);
|
||||
},
|
||||
}
|
||||
|
||||
State::StartLine
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// get the last one...
|
||||
//
|
||||
// build the Authority and return.
|
||||
Ok(Authority::new(try!(origin.ok_or(ParseError::OriginIsUndefined)), records))
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,5 +297,7 @@ enum State {
|
||||
StartLine, // start of line, @, $<WORD>, Name, Blank
|
||||
Ttl_Class_Type, // [<TTL>] [<class>] <type>,
|
||||
Record,
|
||||
Include, // $INCLUDE <filename>
|
||||
Origin,
|
||||
EndRecord,
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use std::cell::{Cell,RefCell};
|
||||
use std::iter::Peekable;
|
||||
use std::str::Chars;
|
||||
use std::char;
|
||||
use std::fs::File;
|
||||
|
||||
use ::error::{LexerResult,LexerError};
|
||||
|
||||
@ -13,11 +14,7 @@ pub struct Lexer<'a> {
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
pub fn new(txt: &str) -> Lexer {
|
||||
Self::with_chars(txt.chars())
|
||||
}
|
||||
|
||||
pub fn with_chars(chars: Chars) -> Lexer {
|
||||
Lexer { txt: chars.peekable(), is_first_line: true, in_list: false }
|
||||
Lexer { txt: txt.chars().peekable(), is_first_line: true, in_list: false }
|
||||
}
|
||||
|
||||
pub fn next_token(&mut self) -> LexerResult<Option<Token>> {
|
||||
@ -198,7 +195,7 @@ pub enum State {
|
||||
EOL, // \n or \r\n
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Token {
|
||||
Blank, // only if the first part of the line
|
||||
StartList, // (
|
||||
|
@ -13,8 +13,15 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
use ::error::ParseResult;
|
||||
|
||||
mod master_lex;
|
||||
// mod master;
|
||||
//
|
||||
// pub use self::master::Parser;
|
||||
mod master;
|
||||
|
||||
pub use self::master::Parser;
|
||||
pub use self::master_lex::Lexer;
|
||||
pub use self::master_lex::Token;
|
||||
|
||||
pub trait TxtSerializable {
|
||||
fn parse(lexer: &mut Lexer) -> ParseResult<Self>;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user