Beginning of writers for RData.

This commit is contained in:
Benjamin Fry 2015-08-17 00:59:21 -07:00
parent 7adbc23d64
commit 9ad645564d
11 changed files with 224 additions and 83 deletions

View File

@ -1,3 +1,5 @@
use std::convert::From;
use super::op_code::OpCode;
use super::response_code::ResponseCode;
use super::super::rr::util;
@ -116,6 +118,8 @@ impl Header {
let name_server_count = util::parse_u16(data);
let additional_count = util::parse_u16(data);
// TODO: question, should this use the builder pattern instead? might be cleaner code, but
// this guarantees that the Header is
Header { id: id, message_type: message_type, op_code: op_code, authoritative: authoritative,
truncation: truncation, recursion_desired: recursion_desired,
recursion_available: recursion_available, response_code: response_code,
@ -123,6 +127,33 @@ impl Header {
name_server_count: name_server_count, additional_count: additional_count }
}
pub fn write_to(&self, buf: &mut Vec<u8>) {
buf.reserve(12); // the 12 bytes for the following fields;
// Id
util::write_u16_to(buf, self.id);
// IsQuery, OpCode, Authoritative, Truncation, RecursionDesired
let mut q_opcd_a_t_r: u8 = 0;
q_opcd_a_t_r = if let MessageType::Response = self.message_type { 0x80 } else { 0x00 };
q_opcd_a_t_r |= u8::from(self.op_code) << 3;
q_opcd_a_t_r |= if self.authoritative { 0x4 } else { 0x0 };
q_opcd_a_t_r |= if self.truncation { 0x2 } else { 0x0 };
q_opcd_a_t_r |= if self.recursion_desired { 0x1 } else { 0x0 };
buf.push(q_opcd_a_t_r);
// IsRecursionAvailable, Triple 0's, ResponseCode
let mut r_zzz_rcod: u8 = 0;
r_zzz_rcod = if self.recursion_available { 0x80 } else { 0x00 };
r_zzz_rcod |= u8::from(self.response_code);
buf.push(r_zzz_rcod);
util::write_u16_to(buf, self.query_count);
util::write_u16_to(buf, self.answer_count);
util::write_u16_to(buf, self.name_server_count);
util::write_u16_to(buf, self.additional_count);
}
pub fn getId(&self) -> u16 { self.id }
pub fn getMessageType(&self) -> MessageType { self.message_type }
pub fn getOpCode(&self) -> OpCode { self.op_code }
@ -149,11 +180,31 @@ fn test_parse() {
data.reverse();
let expect = Header { id: 0x0110, message_type: MessageType::Response, op_code: OpCode::Update,
authoritative: false, truncation: true, recursion_desired: false,
recursion_available: true, response_code: ResponseCode::NXDomain,
query_count: 0x8877, answer_count: 0x6655, name_server_count: 0x4433, additional_count: 0x2211};
authoritative: false, truncation: true, recursion_desired: false,
recursion_available: true, response_code: ResponseCode::NXDomain,
query_count: 0x8877, answer_count: 0x6655, name_server_count: 0x4433, additional_count: 0x2211};
let got = Header::parse(&mut data);
assert_eq!(got, expect);
}
#[test]
fn test_write() {
let header = Header { id: 0x0110, message_type: MessageType::Response, op_code: OpCode::Update,
authoritative: false, truncation: true, recursion_desired: false,
recursion_available: true, response_code: ResponseCode::NXDomain,
query_count: 0x8877, answer_count: 0x6655, name_server_count: 0x4433, additional_count: 0x2211};
let expect: Vec<u8> = vec![0x01, 0x10,
0xAA, 0x83, // 0b1010 1010 1000 0011
0x88, 0x77,
0x66, 0x55,
0x44, 0x33,
0x22, 0x11];
let mut got: Vec<u8> = Vec::with_capacity(expect.len());
header.write_to(&mut got);
assert_eq!(got, expect);
}

View File

@ -47,13 +47,18 @@ impl Message {
let header = Header::parse(data);
// get the questions
let queryCount: usize = header.getQueryCount() as usize;
let mut queries = Vec::with_capacity(queryCount);
for _ in 0 .. queryCount {
let count: usize = header.getQueryCount() as usize;
let mut queries = Vec::with_capacity(count);
for _ in 0 .. count {
queries.push(Query::parse(data));
}
// get the answers
// let count: usize = header.getAnswerCount() as usize;
// let mut answers = Vec::with_capacity(count);
// for _ in 0 .. count {
// answers.push(Answer)
// }
Message { header: header, queries: queries}
}

View File

@ -40,11 +40,13 @@ use super::super::rr::dns_class::DNSClass;
* QCLASS a two octet code that specifies the class of the query.
* For example, the QCLASS field is IN for the Internet.
*/
#[derive(PartialEq, Debug)]
pub struct Query {
name: Name, query_type: RecordType, query_class: DNSClass
}
impl Query {
// TODO: these functions certainly seem like they could just be rustc::encodable
pub fn parse(data: &mut Vec<u8>) -> Self {
let name = Name::parse(data);
let query_type = RecordType::parse(data);
@ -52,9 +54,23 @@ impl Query {
Query { name: name, query_type: query_type, query_class: query_class}
}
pub fn write_to(&self, buf: &mut Vec<u8>) {
self.name.write_to(buf);
self.query_type.write_to(buf);
self.query_class.write_to(buf);
}
}
#[test]
fn test_parse() {
fn test_parse_and_write() {
let expect = Query { name: Name::with_labels(vec!["www".to_string(),"example".to_string(),"com".to_string()]),
query_type: RecordType::AAAA, query_class: DNSClass::IN };
let mut written = Vec::new();
expect.write_to(&mut written);
written.reverse(); // flip it around to read in...
let got = Query::parse(&mut written);
assert_eq!(got, expect);
}

View File

@ -15,6 +15,10 @@ impl DNSClass {
pub fn parse(data: &mut Vec<u8>) -> Self {
util::parse_u16(data).into()
}
pub fn write_to(&self, buf: &mut Vec<u8>) {
util::write_u16_to(buf, (*self).into());
}
}
// TODO make these a macro or annotation

View File

@ -9,6 +9,19 @@ pub struct Name {
}
impl Name {
pub fn new() -> Self {
Name { labels: Vec::new() }
}
pub fn with_labels(labels: Vec<String>) -> Self {
Name { labels: labels }
}
pub fn add_label(&mut self, label: String) -> &mut Self {
self.labels.push(label);
self
}
/// 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.

View File

@ -24,6 +24,24 @@ pub fn parse(data: &mut Vec<u8>) -> RData {
RData::AAAA{ address: Ipv6Addr::new(a,b,c,d,e,f,g,h)}
}
pub fn write_to(aaaa: &RData, buf: &mut Vec<u8>) {
if let RData::AAAA { address } = *aaaa {
let segments = address.segments();
util::write_u16_to(buf, segments[0]);
util::write_u16_to(buf, segments[1]);
util::write_u16_to(buf, segments[2]);
util::write_u16_to(buf, segments[3]);
util::write_u16_to(buf, segments[4]);
util::write_u16_to(buf, segments[5]);
util::write_u16_to(buf, segments[6]);
util::write_u16_to(buf, segments[7]);
} else {
assert!(false)
}
}
#[test]
fn test_parse() {
use std::net::Ipv6Addr;

View File

@ -8,5 +8,4 @@ pub mod ptr;
pub mod soa;
pub mod txt;
pub mod a;
pub mod wks;
pub mod aaaa;

View File

@ -1,49 +0,0 @@
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!()
}

View File

@ -2,6 +2,7 @@ use std::net::{Ipv4Addr, Ipv6Addr};
use super::domain::Name;
use super::record_type::RecordType;
use super::rdata;
// 3.3. Standard RRs
//
@ -309,17 +310,31 @@ pub enum RData {
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, rd_length),
match *rtype {
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, rd_length),
RecordType::A => rdata::a::parse(data),
RecordType::AAAA => rdata::aaaa::parse(data),
_ => unimplemented!()
}
}
pub fn write_to(&self, buf: &mut Vec<u8>) {
match self {
// CNAME => rdata::cname::write_to(self, buf),
// MX => rdata::mx::write_to(self, buf),
// NULL => rdata::null::write_to(self, buf),
// NS => rdata::null::write_to(self, buf),
// PTR => rdata::ptr::write_to(self, buf),
// SOA => rdata::soa::write_to(self, buf),
// TXT => rdata::txt::write_to(self, buf),
// A => rdata::a::write_to(self, buf),
AAAA => rdata::aaaa::write_to(self, buf),
// _ => unimplemented!()
}
}
}

View File

@ -52,8 +52,8 @@ impl RecordType {
util::parse_u16(data).into()
}
pub fn write_to(buf: &mut Vec<u8>) {
pub fn write_to(&self, buf: &mut Vec<u8>) {
util::write_u16_to(buf, (*self).into());
}
}

View File

@ -11,6 +11,62 @@ use super::dns_class::DNSClass;
use super::domain;
use super::util;
/*
* RFC 1035 Domain Implementation and Specification November 1987
*
* 4.1.3. Resource record format
*
* The answer, authority, and additional sections all share the same
* format: a variable number of resource records, where the number of
* records is specified in the corresponding count field in the header.
* Each resource record has the following format:
* 1 1 1 1 1 1
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | |
* / /
* / NAME /
* | |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | TYPE |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | CLASS |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | TTL |
* | |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | RDLENGTH |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
* / RDATA /
* / /
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*
* where:
*
* NAME a domain name to which this resource record pertains.
*
* TYPE two octets containing one of the RR type codes. This
* field specifies the meaning of the data in the RDATA
* field.
*
* CLASS two octets which specify the class of the data in the
* RDATA field.
*
* TTL a 32 bit unsigned integer that specifies the time
* interval (in seconds) that the resource record may be
* cached before it should be discarded. Zero values are
* interpreted to mean that the RR can only be used for the
* transaction in progress, and should not be cached.
*
* RDLENGTH an unsigned 16 bit integer that specifies the length in
* octets of the RDATA field.
*
* 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.
* For example, the if the TYPE is A and the CLASS is IN,
* the RDATA field is a 4 octet ARPA Internet address.
*/
pub struct Record {
name_labels: domain::Name,
rr_type: RecordType,
@ -23,20 +79,16 @@ impl Record {
/// parse a resource record line example:
/// WARNING: the record_bytes is 100% consumed and destroyed in this parsing process
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;
data.reverse();
pub fn parse(data: &mut Vec<u8>) -> Result<Record, FromUtf8Error> {
// NAME an owner name, i.e., the name of the node to which this
// resource record pertains.
let name_labels: domain::Name = domain::Name::parse(&mut data);
let name_labels: domain::Name = domain::Name::parse(data);
// TYPE two octets containing one of the RR TYPE codes.
let record_type: RecordType = RecordType::parse(&mut data);
let record_type: RecordType = RecordType::parse(data);
// CLASS two octets containing one of the RR CLASS codes.
let class: DNSClass = DNSClass::parse(&mut data);
let class: DNSClass = DNSClass::parse(data);
// TTL a 32 bit signed integer that specifies the time interval
// that the resource record may be cached before the source
@ -46,21 +98,38 @@ impl Record {
// 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 = util::parse_i32(&mut data);
let ttl: i32 = util::parse_i32(data);
// RDLENGTH an unsigned 16 bit integer that specifies the length in
// octets of the RDATA field.
let rd_length: u16 = util::parse_u16(&mut data);
let rd_length: u16 = util::parse_u16(data);
// 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);
let rdata = RData::parse(data, &record_type, rd_length);
Ok(Record{ name_labels: name_labels, rr_type: record_type, dns_class: class, ttl: ttl, rdata: rdata })
}
}
#[cfg(test)]
mod tests {
pub fn write_to(&self, buf: &mut Vec<u8>) {
self.name_labels.write_to(buf);
self.rr_type.write_to(buf);
self.dns_class.write_to(buf);
util::write_i32_to(buf, self.ttl);
// TODO: gah... need to write rdata before we know the size of rdata...
let mut tmp_buf: Vec<u8> = Vec::with_capacity(255); // making random space
self.rdata.write_to(&mut tmp_buf);
assert!(tmp_buf.len() <= u16::max_value() as usize);
util::write_u16_to(buf, tmp_buf.len() as u16);
buf.reserve(tmp_buf.len());
tmp_buf.reverse();
while let Some(byte) = tmp_buf.pop() {
buf.push(byte);
}
}
}