Beginning of writers for RData.
This commit is contained in:
parent
7adbc23d64
commit
9ad645564d
@ -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);
|
||||
}
|
||||
|
@ -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}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -8,5 +8,4 @@ pub mod ptr;
|
||||
pub mod soa;
|
||||
pub mod txt;
|
||||
pub mod a;
|
||||
pub mod wks;
|
||||
pub mod aaaa;
|
||||
|
@ -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!()
|
||||
}
|
@ -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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user