initial parsing for resource record, and stubbed rdata parsers
This commit is contained in:
parent
55dac96d79
commit
8b7facb69e
102
src/rr/domain.rs
Normal file
102
src/rr/domain.rs
Normal file
@ -0,0 +1,102 @@
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
pub struct Name {
|
||||
labels: Vec<String>
|
||||
}
|
||||
|
||||
impl Name {
|
||||
/// parses the chain of labels
|
||||
/// this has a max of 255 octets, with each label being less than 63.
|
||||
/// all names will be stored lowercase internally.
|
||||
/// This will consume the portions of the Vec which it is reading...
|
||||
pub fn parse(slice: &mut Vec<u8>) -> Result<Name, FromUtf8Error> {
|
||||
let mut state: LabelParseState = LabelParseState::LabelLengthOrPointer;
|
||||
let mut labels: Vec<String> = Vec::with_capacity(3); // most labels will be around three, e.g. www.example.com
|
||||
|
||||
// assume all chars are utf-8. We're doing byte-by-byte operations, no endianess issues...
|
||||
// reserved: (1000 0000 aka 0800) && (0100 0000 aka 0400)
|
||||
// pointer: (slice == 1100 0000 aka C0) & C0 == true, then 03FF & slice = offset
|
||||
// label: 03FF & slice = length; slice.next(length) = label
|
||||
// root: 0000
|
||||
loop {
|
||||
state = match state {
|
||||
LabelParseState::LabelLengthOrPointer => {
|
||||
// determine what the next label is
|
||||
match slice.remove(0) {
|
||||
0 => LabelParseState::Root,
|
||||
byte if byte & 0xC0 == 0xC0 => LabelParseState::Pointer(byte & 0x3F),
|
||||
byte if byte <= 0x3F => LabelParseState::Label(byte),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
},
|
||||
LabelParseState::Label(count) => {
|
||||
//let label_slice: &mut [u8] = &mut Vec::with_capacity(count as usize)[..];
|
||||
//let mut label_slice = Vec::with_capacity(count as usize);
|
||||
//let mut label_vec: Vec<u8> = iter::FromIterator::from_iter(iter::repeat(0).take(count as usize));
|
||||
//let mut label_slice: &mut [u8] = &mut label_vec[..];
|
||||
|
||||
// TODO once Drain stabalizes on Vec, this should be replaced...
|
||||
let mut label_slice: Vec<u8> = Vec::with_capacity(count as usize);
|
||||
for i in 0..count as usize {
|
||||
label_slice.push(slice.remove(i-i)); // get rid of the unused i warning...
|
||||
}
|
||||
|
||||
//println!("count: {} slice: {} label_slice: {}", count, slice.len(), label_slice.len());
|
||||
//let label_slice: Vec<u8> = iter::FromIterator::from_iter(iter.take(count as usize).map(|&i| i).collect::<Vec<u8>>());
|
||||
//assert_eq!((&*slice).read(label_slice).ok().unwrap(), count as usize);
|
||||
println!("count: {} slice: {} label_slice: {}", count, slice.len(), label_slice.len());
|
||||
|
||||
// translate bytes to string, then lowercase...
|
||||
//let label_slice = &*label_slice;
|
||||
//let label = try!(String::from_utf8(label_slice.into())).to_lowercase();
|
||||
let label = try!(String::from_utf8(label_slice)).to_lowercase();
|
||||
labels.push(label);
|
||||
|
||||
// reset to collect more data
|
||||
LabelParseState::LabelLengthOrPointer
|
||||
},
|
||||
LabelParseState::Pointer(offset) => {
|
||||
// lookup in the hashmap the label to use
|
||||
unimplemented!()
|
||||
},
|
||||
LabelParseState::Root => {
|
||||
// technically could return here...
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Name { labels: labels })
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the list of states for the label parsing state machine
|
||||
enum LabelParseState {
|
||||
LabelLengthOrPointer, // basically the start of the FSM
|
||||
Label(u8), // storing length of the label, must be < 63
|
||||
Pointer(u8), // location of pointer in slice,
|
||||
Root, // root is the end of the labels list, aka null
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse() {
|
||||
let data: Vec<(Vec<u8>, Vec<String>)> = vec![
|
||||
(vec![0], vec![]), // base case, only the root
|
||||
(vec![1,b'a',0], vec!["a".to_string()]), // a single 'a' label
|
||||
(vec![1,b'a',2,b'b',b'c',0], vec!["a".to_string(), "bc".to_string()]), // two labels, 'a.bc'
|
||||
(vec![1,b'a',3,0xE2,0x99,0xA5,0], vec!["a".to_string(), "♥".to_string()]), // two labels utf8, 'a.♥'
|
||||
(vec![1,b'A',0], vec!["a".to_string()]), // a single 'a' label, lowercased
|
||||
];
|
||||
|
||||
let mut test_num = 0;
|
||||
for (mut binary, expect) in data {
|
||||
test_num += 1;
|
||||
println!("test: {}", test_num);
|
||||
assert_eq!(Name::parse(&mut binary).ok().unwrap().labels, expect);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
pub mod record_type;
|
||||
pub mod dns_class;
|
||||
pub mod resource;
|
||||
pub mod record_data;
|
||||
pub mod domain;
|
||||
|
||||
mod rdata;
|
||||
|
26
src/rr/rdata/a.rs
Normal file
26
src/rr/rdata/a.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use super::super::record_data::RData;
|
||||
|
||||
// 3.4. Internet specific RRs
|
||||
//
|
||||
// 3.4.1. A RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | ADDRESS |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// ADDRESS A 32 bit Internet address.
|
||||
//
|
||||
// Hosts that have multiple Internet addresses will have multiple A
|
||||
// records.
|
||||
//
|
||||
// A records cause no additional section processing. The RDATA section of
|
||||
// an A line in a master file is an Internet address expressed as four
|
||||
// decimal numbers separated by dots without any imbedded spaces (e.g.,
|
||||
// "10.2.0.52" or "192.0.5.6").
|
||||
//
|
||||
// A { address: u32 }
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
unimplemented!()
|
||||
}
|
12
src/rr/rdata/aaaa.rs
Normal file
12
src/rr/rdata/aaaa.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use super::super::record_data::RData;
|
||||
//-- RFC 1886 -- IPv6 DNS Extensions December 1995
|
||||
|
||||
// 2.2 AAAA data format
|
||||
//
|
||||
// A 128 bit IPv6 address is encoded in the data portion of an AAAA
|
||||
// resource record in network byte order (high-order byte first).
|
||||
//
|
||||
// AAAA { high: u64, low: u64 }
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
unimplemented!()
|
||||
}
|
22
src/rr/rdata/cname.rs
Normal file
22
src/rr/rdata/cname.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use super::super::record_data::RData;
|
||||
|
||||
// 3.3.1. CNAME RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / CNAME /
|
||||
// / /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// CNAME A <domain-name> which specifies the canonical or primary
|
||||
// name for the owner. The owner name is an alias.
|
||||
//
|
||||
// CNAME RRs cause no additional section processing, but name servers may
|
||||
// choose to restart the query at the canonical name in certain cases. See
|
||||
// the description of name server logic in [RFC-1034] for details.
|
||||
//
|
||||
// CNAME { cname: Name },
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
unimplemented!()
|
||||
}
|
27
src/rr/rdata/hinfo.rs
Normal file
27
src/rr/rdata/hinfo.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use super::super::record_data::RData;
|
||||
|
||||
// 3.3.2. HINFO RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / CPU /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / OS /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// CPU A <character-string> which specifies the CPU type.
|
||||
//
|
||||
// OS A <character-string> which specifies the operating
|
||||
// system type.
|
||||
//
|
||||
// Standard values for CPU and OS can be found in [RFC-1010].
|
||||
//
|
||||
// HINFO records are used to acquire general information about a host. The
|
||||
// main use is for protocols such as FTP that can use special procedures
|
||||
// when talking between machines or operating systems of the same type.
|
||||
//
|
||||
// HINFO { cpu: String, os: String},
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
unimplemented!()
|
||||
}
|
12
src/rr/rdata/mod.rs
Normal file
12
src/rr/rdata/mod.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// each of these module's has the parser for that rdata embedded, to keep the file sizes down...
|
||||
pub mod cname;
|
||||
pub mod hinfo;
|
||||
pub mod mx;
|
||||
pub mod null;
|
||||
pub mod ns;
|
||||
pub mod ptr;
|
||||
pub mod soa;
|
||||
pub mod txt;
|
||||
pub mod a;
|
||||
pub mod wks;
|
||||
pub mod aaaa;
|
28
src/rr/rdata/mx.rs
Normal file
28
src/rr/rdata/mx.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use super::super::record_data::RData;
|
||||
|
||||
// 3.3.9. MX RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | PREFERENCE |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / EXCHANGE /
|
||||
// / /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// PREFERENCE A 16 bit integer which specifies the preference given to
|
||||
// this RR among others at the same owner. Lower values
|
||||
// are preferred.
|
||||
//
|
||||
// EXCHANGE A <domain-name> which specifies a host willing to act as
|
||||
// a mail exchange for the owner name.
|
||||
//
|
||||
// MX records cause type A additional section processing for the host
|
||||
// specified by EXCHANGE. The use of MX RRs is explained in detail in
|
||||
// [RFC-974].
|
||||
//
|
||||
// MX { preference: u16, exchange: Name },
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
unimplemented!()
|
||||
}
|
29
src/rr/rdata/ns.rs
Normal file
29
src/rr/rdata/ns.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use super::super::record_data::RData;
|
||||
|
||||
// 3.3.11. NS RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / NSDNAME /
|
||||
// / /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// NSDNAME A <domain-name> which specifies a host which should be
|
||||
// authoritative for the specified class and domain.
|
||||
//
|
||||
// NS records cause both the usual additional section processing to locate
|
||||
// a type A record, and, when used in a referral, a special search of the
|
||||
// zone in which they reside for glue information.
|
||||
//
|
||||
// The NS RR states that the named host should be expected to have a zone
|
||||
// starting at owner name of the specified class. Note that the class may
|
||||
// not indicate the protocol family which should be used to communicate
|
||||
// with the host, although it is typically a strong hint. For example,
|
||||
// hosts which are name servers for either Internet (IN) or Hesiod (HS)
|
||||
// class information are normally queried using IN class protocols.
|
||||
//
|
||||
// NS { nsdname: Name },
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
unimplemented!()
|
||||
}
|
20
src/rr/rdata/null.rs
Normal file
20
src/rr/rdata/null.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use super::super::record_data::RData;
|
||||
|
||||
// 3.3.10. NULL RDATA format (EXPERIMENTAL)
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / <anything> /
|
||||
// / /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// Anything at all may be in the RDATA field so long as it is 65535 octets
|
||||
// or less.
|
||||
//
|
||||
// NULL records cause no additional section processing. NULL RRs are not
|
||||
// allowed in master files. NULLs are used as placeholders in some
|
||||
// experimental extensions of the DNS.
|
||||
//
|
||||
// NULL { anything: Vec<u8> },
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
unimplemented!()
|
||||
}
|
23
src/rr/rdata/ptr.rs
Normal file
23
src/rr/rdata/ptr.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use super::super::record_data::RData;
|
||||
|
||||
// 3.3.12. PTR RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / PTRDNAME /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// PTRDNAME A <domain-name> which points to some location in the
|
||||
// domain name space.
|
||||
//
|
||||
// PTR records cause no additional section processing. These RRs are used
|
||||
// in special domains to point to some other location in the domain space.
|
||||
// These records are simple data, and don't imply any special processing
|
||||
// similar to that performed by CNAME, which identifies aliases. See the
|
||||
// description of the IN-ADDR.ARPA domain for an example.
|
||||
//
|
||||
// PTR { ptrdnam: Name },
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
unimplemented!()
|
||||
}
|
71
src/rr/rdata/soa.rs
Normal file
71
src/rr/rdata/soa.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use super::super::record_data::RData;
|
||||
|
||||
// 3.3.13. SOA RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / MNAME /
|
||||
// / /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / RNAME /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | SERIAL |
|
||||
// | |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | REFRESH |
|
||||
// | |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | RETRY |
|
||||
// | |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | EXPIRE |
|
||||
// | |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | MINIMUM |
|
||||
// | |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// MNAME The <domain-name> of the name server that was the
|
||||
// original or primary source of data for this zone.
|
||||
//
|
||||
// RNAME A <domain-name> which specifies the mailbox of the
|
||||
// person responsible for this zone.
|
||||
//
|
||||
// SERIAL The unsigned 32 bit version number of the original copy
|
||||
// of the zone. Zone transfers preserve this value. This
|
||||
// value wraps and should be compared using sequence space
|
||||
// arithmetic.
|
||||
//
|
||||
// REFRESH A 32 bit time interval before the zone should be
|
||||
// refreshed.
|
||||
//
|
||||
// RETRY A 32 bit time interval that should elapse before a
|
||||
// failed refresh should be retried.
|
||||
//
|
||||
// EXPIRE A 32 bit time value that specifies the upper limit on
|
||||
// the time interval that can elapse before the zone is no
|
||||
// longer authoritative.
|
||||
//
|
||||
// MINIMUM The unsigned 32 bit minimum TTL field that should be
|
||||
// exported with any RR from this zone.
|
||||
//
|
||||
// SOA records cause no additional section processing.
|
||||
//
|
||||
// All times are in units of seconds.
|
||||
//
|
||||
// Most of these fields are pertinent only for name server maintenance
|
||||
// operations. However, MINIMUM is used in all query operations that
|
||||
// retrieve RRs from a zone. Whenever a RR is sent in a response to a
|
||||
// query, the TTL field is set to the maximum of the TTL field from the RR
|
||||
// and the MINIMUM field in the appropriate SOA. Thus MINIMUM is a lower
|
||||
// bound on the TTL field for all RRs in a zone. Note that this use of
|
||||
// MINIMUM should occur when the RRs are copied into the response and not
|
||||
// when the zone is loaded from a master file or via a zone transfer. The
|
||||
// reason for this provison is to allow future dynamic update facilities to
|
||||
// change the SOA RR with known semantics.
|
||||
//
|
||||
// SOA { mname: Name, rname: Name, serial: u32, refresh: i32, retry: i32, expire: i32, minimum: u32, },
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
unimplemented!()
|
||||
}
|
19
src/rr/rdata/txt.rs
Normal file
19
src/rr/rdata/txt.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use super::super::record_data::RData;
|
||||
|
||||
// 3.3.14. TXT RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / TXT-DATA /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// TXT-DATA One or more <character-string>s.
|
||||
//
|
||||
// TXT RRs are used to hold descriptive text. The semantics of the text
|
||||
// depends on the domain where it is found.
|
||||
//
|
||||
// TXT { txt_data: Vec<String> }
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
unimplemented!()
|
||||
}
|
49
src/rr/rdata/wks.rs
Normal file
49
src/rr/rdata/wks.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use super::super::record_data::RData;
|
||||
|
||||
// 3.4.2. WKS RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | ADDRESS |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | PROTOCOL | |
|
||||
// +--+--+--+--+--+--+--+--+ |
|
||||
// | |
|
||||
// / <BIT MAP> /
|
||||
// / /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// ADDRESS An 32 bit Internet address
|
||||
//
|
||||
// PROTOCOL An 8 bit IP protocol number
|
||||
//
|
||||
// <BIT MAP> A variable length bit map. The bit map must be a
|
||||
// multiple of 8 bits long.
|
||||
//
|
||||
// The WKS record is used to describe the well known services supported by
|
||||
// a particular protocol on a particular internet address. The PROTOCOL
|
||||
// field specifies an IP protocol number, and the bit map has one bit per
|
||||
// port of the specified protocol. The first bit corresponds to port 0,
|
||||
// the second to port 1, etc. If the bit map does not include a bit for a
|
||||
// protocol of interest, that bit is assumed zero. The appropriate values
|
||||
// and mnemonics for ports and protocols are specified in [RFC-1010].
|
||||
//
|
||||
// For example, if PROTOCOL=TCP (6), the 26th bit corresponds to TCP port
|
||||
// 25 (SMTP). If this bit is set, a SMTP server should be listening on TCP
|
||||
// port 25; if zero, SMTP service is not supported on the specified
|
||||
// address.
|
||||
//
|
||||
// The purpose of WKS RRs is to provide availability information for
|
||||
// servers for TCP and UDP. If a server supports both TCP and UDP, or has
|
||||
// multiple Internet addresses, then multiple WKS RRs are used.
|
||||
//
|
||||
// WKS RRs cause no additional section processing.
|
||||
//
|
||||
// In master files, both ports and protocols are expressed using mnemonics
|
||||
// or decimal numbers.
|
||||
//
|
||||
// WKS { address: u32, protocol: u8, bitmap: Vec<u8> },
|
||||
pub fn parse(data: &mut Vec<u8>) -> RData {
|
||||
unimplemented!()
|
||||
}
|
324
src/rr/record_data.rs
Normal file
324
src/rr/record_data.rs
Normal file
@ -0,0 +1,324 @@
|
||||
use super::domain::Name;
|
||||
use super::record_type::RecordType;
|
||||
|
||||
// 3.3. Standard RRs
|
||||
//
|
||||
// The following RR definitions are expected to occur, at least
|
||||
// potentially, in all classes. In particular, NS, SOA, CNAME, and PTR
|
||||
// will be used in all classes, and have the same format in all classes.
|
||||
// Because their RDATA format is known, all domain names in the RDATA
|
||||
// section of these RRs may be compressed.
|
||||
//
|
||||
// <domain-name> is a domain name represented as a series of labels, and
|
||||
// terminated by a label with zero length. <character-string> is a single
|
||||
// length octet followed by that number of characters. <character-string>
|
||||
// is treated as binary information, and can be up to 256 characters in
|
||||
// length (including the length octet).
|
||||
//
|
||||
pub enum RData {
|
||||
//-- RFC 1035 -- Domain Implementation and Specification November 1987
|
||||
|
||||
// 3.3. Standard RRs
|
||||
//
|
||||
// The following RR definitions are expected to occur, at least
|
||||
// potentially, in all classes. In particular, NS, SOA, CNAME, and PTR
|
||||
// will be used in all classes, and have the same format in all classes.
|
||||
// Because their RDATA format is known, all domain names in the RDATA
|
||||
// section of these RRs may be compressed.
|
||||
//
|
||||
// <domain-name> is a domain name represented as a series of labels, and
|
||||
// terminated by a label with zero length. <character-string> is a single
|
||||
// length octet followed by that number of characters. <character-string>
|
||||
// is treated as binary information, and can be up to 256 characters in
|
||||
// length (including the length octet).
|
||||
//
|
||||
// 3.3.1. CNAME RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / CNAME /
|
||||
// / /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// CNAME A <domain-name> which specifies the canonical or primary
|
||||
// name for the owner. The owner name is an alias.
|
||||
//
|
||||
// CNAME RRs cause no additional section processing, but name servers may
|
||||
// choose to restart the query at the canonical name in certain cases. See
|
||||
// the description of name server logic in [RFC-1034] for details.
|
||||
CNAME { cname: Name },
|
||||
|
||||
// 3.3.2. HINFO RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / CPU /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / OS /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// CPU A <character-string> which specifies the CPU type.
|
||||
//
|
||||
// OS A <character-string> which specifies the operating
|
||||
// system type.
|
||||
//
|
||||
// Standard values for CPU and OS can be found in [RFC-1010].
|
||||
//
|
||||
// HINFO records are used to acquire general information about a host. The
|
||||
// main use is for protocols such as FTP that can use special procedures
|
||||
// when talking between machines or operating systems of the same type.
|
||||
HINFO { cpu: String, os: String},
|
||||
|
||||
// 3.3.9. MX RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | PREFERENCE |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / EXCHANGE /
|
||||
// / /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// PREFERENCE A 16 bit integer which specifies the preference given to
|
||||
// this RR among others at the same owner. Lower values
|
||||
// are preferred.
|
||||
//
|
||||
// EXCHANGE A <domain-name> which specifies a host willing to act as
|
||||
// a mail exchange for the owner name.
|
||||
//
|
||||
// MX records cause type A additional section processing for the host
|
||||
// specified by EXCHANGE. The use of MX RRs is explained in detail in
|
||||
// [RFC-974].
|
||||
MX { preference: u16, exchange: Name },
|
||||
|
||||
// 3.3.10. NULL RDATA format (EXPERIMENTAL)
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / <anything> /
|
||||
// / /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// Anything at all may be in the RDATA field so long as it is 65535 octets
|
||||
// or less.
|
||||
//
|
||||
// NULL records cause no additional section processing. NULL RRs are not
|
||||
// allowed in master files. NULLs are used as placeholders in some
|
||||
// experimental extensions of the DNS.
|
||||
NULL { anything: Vec<u8> },
|
||||
|
||||
// 3.3.11. NS RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / NSDNAME /
|
||||
// / /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// NSDNAME A <domain-name> which specifies a host which should be
|
||||
// authoritative for the specified class and domain.
|
||||
//
|
||||
// NS records cause both the usual additional section processing to locate
|
||||
// a type A record, and, when used in a referral, a special search of the
|
||||
// zone in which they reside for glue information.
|
||||
//
|
||||
// The NS RR states that the named host should be expected to have a zone
|
||||
// starting at owner name of the specified class. Note that the class may
|
||||
// not indicate the protocol family which should be used to communicate
|
||||
// with the host, although it is typically a strong hint. For example,
|
||||
// hosts which are name servers for either Internet (IN) or Hesiod (HS)
|
||||
// class information are normally queried using IN class protocols.
|
||||
NS { nsdname: Name },
|
||||
|
||||
// 3.3.12. PTR RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / PTRDNAME /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// PTRDNAME A <domain-name> which points to some location in the
|
||||
// domain name space.
|
||||
//
|
||||
// PTR records cause no additional section processing. These RRs are used
|
||||
// in special domains to point to some other location in the domain space.
|
||||
// These records are simple data, and don't imply any special processing
|
||||
// similar to that performed by CNAME, which identifies aliases. See the
|
||||
// description of the IN-ADDR.ARPA domain for an example.
|
||||
PTR { ptrdnam: Name },
|
||||
|
||||
// 3.3.13. SOA RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / MNAME /
|
||||
// / /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / RNAME /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | SERIAL |
|
||||
// | |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | REFRESH |
|
||||
// | |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | RETRY |
|
||||
// | |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | EXPIRE |
|
||||
// | |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | MINIMUM |
|
||||
// | |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// MNAME The <domain-name> of the name server that was the
|
||||
// original or primary source of data for this zone.
|
||||
//
|
||||
// RNAME A <domain-name> which specifies the mailbox of the
|
||||
// person responsible for this zone.
|
||||
//
|
||||
// SERIAL The unsigned 32 bit version number of the original copy
|
||||
// of the zone. Zone transfers preserve this value. This
|
||||
// value wraps and should be compared using sequence space
|
||||
// arithmetic.
|
||||
//
|
||||
// REFRESH A 32 bit time interval before the zone should be
|
||||
// refreshed.
|
||||
//
|
||||
// RETRY A 32 bit time interval that should elapse before a
|
||||
// failed refresh should be retried.
|
||||
//
|
||||
// EXPIRE A 32 bit time value that specifies the upper limit on
|
||||
// the time interval that can elapse before the zone is no
|
||||
// longer authoritative.
|
||||
//
|
||||
// MINIMUM The unsigned 32 bit minimum TTL field that should be
|
||||
// exported with any RR from this zone.
|
||||
//
|
||||
// SOA records cause no additional section processing.
|
||||
//
|
||||
// All times are in units of seconds.
|
||||
//
|
||||
// Most of these fields are pertinent only for name server maintenance
|
||||
// operations. However, MINIMUM is used in all query operations that
|
||||
// retrieve RRs from a zone. Whenever a RR is sent in a response to a
|
||||
// query, the TTL field is set to the maximum of the TTL field from the RR
|
||||
// and the MINIMUM field in the appropriate SOA. Thus MINIMUM is a lower
|
||||
// bound on the TTL field for all RRs in a zone. Note that this use of
|
||||
// MINIMUM should occur when the RRs are copied into the response and not
|
||||
// when the zone is loaded from a master file or via a zone transfer. The
|
||||
// reason for this provison is to allow future dynamic update facilities to
|
||||
// change the SOA RR with known semantics.
|
||||
SOA { mname: Name, rname: Name, serial: u32, refresh: i32, retry: i32, expire: i32, minimum: u32, },
|
||||
|
||||
// 3.3.14. TXT RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// / TXT-DATA /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// TXT-DATA One or more <character-string>s.
|
||||
//
|
||||
// TXT RRs are used to hold descriptive text. The semantics of the text
|
||||
// depends on the domain where it is found.
|
||||
TXT { txt_data: Vec<String> },
|
||||
|
||||
// 3.4. Internet specific RRs
|
||||
//
|
||||
// 3.4.1. A RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | ADDRESS |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// ADDRESS A 32 bit Internet address.
|
||||
//
|
||||
// Hosts that have multiple Internet addresses will have multiple A
|
||||
// records.
|
||||
//
|
||||
// A records cause no additional section processing. The RDATA section of
|
||||
// an A line in a master file is an Internet address expressed as four
|
||||
// decimal numbers separated by dots without any imbedded spaces (e.g.,
|
||||
// "10.2.0.52" or "192.0.5.6").
|
||||
A { address: u32 },
|
||||
|
||||
// 3.4.2. WKS RDATA format
|
||||
//
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | ADDRESS |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | PROTOCOL | |
|
||||
// +--+--+--+--+--+--+--+--+ |
|
||||
// | |
|
||||
// / <BIT MAP> /
|
||||
// / /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// ADDRESS An 32 bit Internet address
|
||||
//
|
||||
// PROTOCOL An 8 bit IP protocol number
|
||||
//
|
||||
// <BIT MAP> A variable length bit map. The bit map must be a
|
||||
// multiple of 8 bits long.
|
||||
//
|
||||
// The WKS record is used to describe the well known services supported by
|
||||
// a particular protocol on a particular internet address. The PROTOCOL
|
||||
// field specifies an IP protocol number, and the bit map has one bit per
|
||||
// port of the specified protocol. The first bit corresponds to port 0,
|
||||
// the second to port 1, etc. If the bit map does not include a bit for a
|
||||
// protocol of interest, that bit is assumed zero. The appropriate values
|
||||
// and mnemonics for ports and protocols are specified in [RFC-1010].
|
||||
//
|
||||
// For example, if PROTOCOL=TCP (6), the 26th bit corresponds to TCP port
|
||||
// 25 (SMTP). If this bit is set, a SMTP server should be listening on TCP
|
||||
// port 25; if zero, SMTP service is not supported on the specified
|
||||
// address.
|
||||
//
|
||||
// The purpose of WKS RRs is to provide availability information for
|
||||
// servers for TCP and UDP. If a server supports both TCP and UDP, or has
|
||||
// multiple Internet addresses, then multiple WKS RRs are used.
|
||||
//
|
||||
// WKS RRs cause no additional section processing.
|
||||
//
|
||||
// In master files, both ports and protocols are expressed using mnemonics
|
||||
// or decimal numbers.
|
||||
WKS { address: u32, protocol: u8, bitmap: Vec<u8> },
|
||||
|
||||
//-- RFC 1886 -- IPv6 DNS Extensions December 1995
|
||||
|
||||
// 2.2 AAAA data format
|
||||
//
|
||||
// A 128 bit IPv6 address is encoded in the data portion of an AAAA
|
||||
// resource record in network byte order (high-order byte first).
|
||||
AAAA { high: u64, low: u64 }
|
||||
|
||||
}
|
||||
|
||||
impl RData {
|
||||
pub fn parse(data: &mut Vec<u8>, rtype: &RecordType, rd_length: u16) -> Self {
|
||||
use super::rdata;
|
||||
match rtype {
|
||||
&RecordType::A => rdata::a::parse(data),
|
||||
&RecordType::AAAA => rdata::aaaa::parse(data),
|
||||
&RecordType::CNAME => rdata::cname::parse(data),
|
||||
&RecordType::MX => rdata::mx::parse(data),
|
||||
&RecordType::NS => rdata::ns::parse(data),
|
||||
&RecordType::PTR => rdata::ptr::parse(data),
|
||||
&RecordType::SOA => rdata::soa::parse(data),
|
||||
&RecordType::TXT => rdata::txt::parse(data),
|
||||
_ => unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
@ -97,6 +97,7 @@ impl<'a> From<&'a str> for RecordType {
|
||||
"NS" => RecordType::NS,
|
||||
"SOA" => RecordType::SOA,
|
||||
"ANY" => RecordType::ANY,
|
||||
"*" => RecordType::ANY,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
@ -1,95 +1,85 @@
|
||||
//use std::collections::HashMap;
|
||||
//use std::error::Error;
|
||||
use std::string::FromUtf8Error;
|
||||
use std::io::Read;
|
||||
use std::iter;
|
||||
use std::slice::{IterMut,Iter};
|
||||
|
||||
use super::record_data::RData;
|
||||
use super::record_type::RecordType;
|
||||
use super::dns_class::DNSClass;
|
||||
use super::domain;
|
||||
|
||||
// labels 63 octets or less
|
||||
// names 255 octets or less
|
||||
// TTL positive values of a signed 32 bit number.
|
||||
// UDP messages 512 octets or less
|
||||
pub struct Record {
|
||||
name_labels: domain::Name,
|
||||
rr_type: RecordType,
|
||||
dns_class: DNSClass,
|
||||
name_labels: Vec<String>,
|
||||
ttl: i32,
|
||||
rdata: RData,
|
||||
}
|
||||
|
||||
impl Record {
|
||||
|
||||
/// parse a resource record line example:
|
||||
///
|
||||
///
|
||||
//fn parse(line: &str) -> Record {
|
||||
pub fn parse(record_bytes: Vec<u8>) -> Result<Record, FromUtf8Error> {
|
||||
// TODO: it would be better to pass iter to all of these methods, but String::from_utf8 makes that complicated
|
||||
let mut data: Vec<u8> = record_bytes;
|
||||
|
||||
// NAME an owner name, i.e., the name of the node to which this
|
||||
// resource record pertains.
|
||||
let name_labels: domain::Name = try!(domain::Name::parse(&mut data));
|
||||
|
||||
// tokenize the string
|
||||
//let mut tokens = line.words();
|
||||
//}
|
||||
// TYPE two octets containing one of the RR TYPE codes.
|
||||
let record_type: RecordType = RecordType::from(Self::parse_u16(&mut data));
|
||||
|
||||
/// parses the chain of labels
|
||||
/// this has a max of 255 octets, with each label being less than 63.
|
||||
/// all names will be stored lowercase internally.
|
||||
fn parse_labels(slice: &[u8]) -> Result<Vec<String>, FromUtf8Error> {
|
||||
let mut state: LabelParseState = LabelParseState::LabelLengthOrPointer;
|
||||
let mut labels: Vec<String> = Vec::with_capacity(3); // most labels will be around three, e.g. www.example.com
|
||||
// CLASS two octets containing one of the RR CLASS codes.
|
||||
let class: DNSClass = DNSClass::from(Self::parse_u16(&mut data));
|
||||
|
||||
let mut cur_slice = slice;
|
||||
// TTL a 32 bit signed integer that specifies the time interval
|
||||
// that the resource record may be cached before the source
|
||||
// of the information should again be consulted. Zero
|
||||
// values are interpreted to mean that the RR can only be
|
||||
// used for the transaction in progress, and should not be
|
||||
// cached. For example, SOA records are always distributed
|
||||
// with a zero TTL to prohibit caching. Zero values can
|
||||
// also be used for extremely volatile data.
|
||||
let ttl: i32 = Self::parse_i32(&mut data);
|
||||
|
||||
// assume all chars are utf-8. We're doing byte-by-byte operations, no endianess issues...
|
||||
// reserved: (1000 0000 aka 0800) && (0100 0000 aka 0400)
|
||||
// pointer: (slice == 1100 0000 aka C0) & C0 == true, then 03FF & slice = offset
|
||||
// label: 03FF & slice = length; slice.next(length) = label
|
||||
// root: 0000
|
||||
loop {
|
||||
state = match state {
|
||||
LabelParseState::LabelLengthOrPointer => {
|
||||
//let byte: u8 = *iter.take(1).next().unwrap(); // could default to zero, but it's an error if this doesn't exist, perhaps try! instead
|
||||
let (first, tmp_slice) = cur_slice.split_at(1);
|
||||
cur_slice = tmp_slice;
|
||||
// RDLENGTH an unsigned 16 bit integer that specifies the length in
|
||||
// octets of the RDATA field.
|
||||
let rd_length: u16 = Self::parse_u16(&mut data);
|
||||
|
||||
match first.first() {
|
||||
Some(&0) | None => LabelParseState::Root,
|
||||
Some(&byte) if byte & 0xC0 == 0xC0 => LabelParseState::Pointer(byte & 0x3F),
|
||||
Some(&byte) if byte <= 0x3F => LabelParseState::Label(byte),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
},
|
||||
LabelParseState::Label(count) => {
|
||||
//let label_iter: Take<Iter<u8>> = iter.take(count as usize);
|
||||
//let arr: Vec<&u8> = label_iter.collect();
|
||||
//let arr2: Vec<u8> = arr.to_vec();
|
||||
let (label_slice, tmp_slice) = cur_slice.split_at(count as usize);
|
||||
cur_slice = tmp_slice;
|
||||
// RDATA a variable length string of octets that describes the
|
||||
// resource. The format of this information varies
|
||||
// according to the TYPE and CLASS of the resource record.
|
||||
let rdata = RData::parse(&mut data, &record_type, rd_length);
|
||||
|
||||
// translate bytes to string, then lowercase...
|
||||
let label = try!(String::from_utf8(label_slice.into())).to_lowercase();
|
||||
labels.push(label);
|
||||
|
||||
// reset to collect more data
|
||||
LabelParseState::LabelLengthOrPointer
|
||||
},
|
||||
LabelParseState::Pointer(offset) => {
|
||||
// lookup in the hashmap the label to use
|
||||
unimplemented!()
|
||||
},
|
||||
LabelParseState::Root => {
|
||||
// technically could return here...
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(labels);
|
||||
Ok(Record{ name_labels: name_labels, rr_type: record_type, dns_class: class, ttl: ttl, rdata: rdata })
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the list of states for the state machine
|
||||
enum LabelParseState {
|
||||
LabelLengthOrPointer, // basically the start of the FSM
|
||||
Label(u8), // storing length of the label, must be < 63
|
||||
Pointer(u8), // location of pointer in slice,
|
||||
Root, // root is the end of the labels list, aka null
|
||||
/// parses the next 2 bytes into u16. This performs a byte-by-byte manipulation, there
|
||||
/// which means endianness is implicitly handled (i.e. no network to little endian (intel), issues)
|
||||
fn parse_u16(data: &mut Vec<u8>) -> u16 {
|
||||
// TODO use Drain once it stabalizes...
|
||||
let b1: u8 = data.remove(0);
|
||||
let b2: u8 = data.remove(0);
|
||||
|
||||
// translate from network byte order, i.e. big endian
|
||||
((b1 as u16) << 8) + (b2 as u16)
|
||||
}
|
||||
|
||||
/// parses the next four bytes into i32. This performs a byte-by-byte manipulation, there
|
||||
/// which means endianness is implicitly handled (i.e. no network to little endian (intel), issues)
|
||||
fn parse_i32(data: &mut Vec<u8>) -> i32 {
|
||||
// TODO use Drain once it stabalizes...
|
||||
let b1: u8 = data.remove(0);
|
||||
let b2: u8 = data.remove(0);
|
||||
let b3: u8 = data.remove(0);
|
||||
let b4: u8 = data.remove(0);
|
||||
|
||||
// translate from network byte order, i.e. big endian
|
||||
((b1 as i32) << 24) + ((b2 as i32) << 16) + ((b3 as i32) << 8) + (b4 as i32)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -97,17 +87,40 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_labels() {
|
||||
let data: Vec<(Vec<u8>, Vec<String>)> = vec![
|
||||
(vec![0], vec![]), // base case, only the root
|
||||
(vec![1,b'a',0], vec!["a".to_string()]), // a single 'a' label
|
||||
(vec![1,b'a',2,b'b',b'c',0], vec!["a".to_string(), "bc".to_string()]), // two labels, 'a.bc'
|
||||
(vec![1,b'a',3,0xE2,0x99,0xA5,0], vec!["a".to_string(), "♥".to_string()]), // two labels utf8, 'a.♥'
|
||||
(vec![1,b'A',0], vec!["a".to_string()]), // a single 'a' label, lowercased
|
||||
fn parse_u16() {
|
||||
let data: Vec<(Vec<u8>, u16)> = vec![
|
||||
(vec![0x00,0x00], 0),
|
||||
(vec![0x00,0x01], 1),
|
||||
(vec![0x01,0x00], 256),
|
||||
(vec![0xFF,0xFF], u16::max_value()),
|
||||
];
|
||||
|
||||
for (binary, result) in data {
|
||||
assert_eq!(Record::parse_labels(&binary[..]).ok().unwrap(), result);
|
||||
let mut test_num = 0;
|
||||
for (mut binary, expect) in data {
|
||||
test_num += 1;
|
||||
println!("test: {}", test_num);
|
||||
assert_eq!(Record::parse_u16(&mut binary), expect);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_i32() {
|
||||
let data: Vec<(Vec<u8>, i32)> = vec![
|
||||
(vec![0x00,0x00,0x00,0x00], 0),
|
||||
(vec![0x00,0x00,0x00,0x01], 1),
|
||||
(vec![0x00,0x00,0x01,0x00], 256),
|
||||
(vec![0x00,0x01,0x00,0x00], 256*256),
|
||||
(vec![0x01,0x00,0x00,0x00], 256*256*256),
|
||||
(vec![0xFF,0xFF,0xFF,0xFF], -1),
|
||||
(vec![0x80,0x00,0x00,0x00], i32::min_value()),
|
||||
(vec![0x7F,0xFF,0xFF,0xFF], i32::max_value()),
|
||||
];
|
||||
|
||||
let mut test_num = 0;
|
||||
for (mut binary, expect) in data {
|
||||
test_num += 1;
|
||||
println!("test: {}", test_num);
|
||||
assert_eq!(Record::parse_i32(&mut binary), expect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user