From 2e56c7adff47fc2d14bdd60b91aae4845c2dfc1e Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Thu, 21 Dec 2017 08:55:12 +0100 Subject: [PATCH] Bring back dig. --- Cargo.toml | 1 - examples/dig.rs | 278 +++++++++++++----------- src/bits/compose.rs | 6 + src/bits/message.rs | 2 + src/bits/message_builder.rs | 420 +++++++++++++++++++++++++++++------- src/bits/mod.rs | 15 +- src/bits/name/dname.rs | 16 +- src/bits/name/parsed.rs | 68 ++++-- src/bits/name/traits.rs | 11 + src/bits/opt/mod.rs | 12 +- src/bits/question.rs | 27 ++- src/bits/record.rs | 56 ++--- src/lib.rs | 2 +- src/rdata/rfc1035.rs | 11 +- src/resolv/conf.rs | 12 +- src/resolv/mod.rs | 4 +- 16 files changed, 662 insertions(+), 279 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ca4b0081..8e9ce483 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ futures = "0.1.14" tokio = { git = "https://github.com/tokio-rs/tokio.git", branch="new-crate" } [dev-dependencies] -argparse = "0.2" native-tls = "0.1.2" tokio-io = "0.1.2" tokio-tls = "0.1.2" diff --git a/examples/dig.rs b/examples/dig.rs index bc5b766d..460b4e51 100644 --- a/examples/dig.rs +++ b/examples/dig.rs @@ -1,15 +1,18 @@ -/* -extern crate argparse; +extern crate bytes; extern crate domain; -extern crate tokio_core; +extern crate failure; -use std::error; -use std::result; +use std::{env, io}; +use std::net::{SocketAddr, UdpSocket}; +use std::process::exit; use std::str::FromStr; -use domain::bits::message::{MessageBuf, RecordSection}; -use domain::bits::name::{DNameBuf, DNameSlice}; +use std::time::Instant; +use bytes::BytesMut; +use failure::Error; +use domain::bits::{Dname, Message, MessageBuilder, ParsedDname, RecordSection}; use domain::iana::{Class, Rtype}; -use domain::resolv::{ResolvConf, Resolver}; +use domain::rdata::AllRecordData; +use domain::resolv::ResolvConf; //------------ Options ------------------------------------------------------ @@ -28,9 +31,9 @@ struct Options { // -y [hmac:name:key] // -4 // -6 - name: String, // name - qtype: String, // type Type as string - qclass: String, // class + name: Dname, + qtype: Rtype, + qclass: Class, // queryopt... conf: ResolvConf, @@ -43,151 +46,176 @@ impl Options { conf.finalize(); conf.options.use_vc = true; Options { - name: String::new(), - qtype: String::new(), // default depends on name. - qclass: "IN".to_string(), + name: Dname::root(), + qtype: Rtype::A, + qclass: Class::In, conf: conf, } } - fn from_args() -> Options { + fn from_args() -> Result { let mut res = Options::new(); - res.parse(); - res + res.parse()?; + Ok(res) } - fn parse(&mut self) { - use argparse::{ArgumentParser, Store}; + fn parse(&mut self) -> Result<(), Error> { + let mut args = env::args(); + args.next(); - let mut parser = ArgumentParser::new(); - - parser.refer(&mut self.name) - .add_argument("name", Store, "name of the resource record"); - parser.refer(&mut self.qtype) - .add_argument("type", Store, "query type"); - parser.refer(&mut self.qclass) - .add_argument("class", Store, "query class"); - - parser.parse_args_or_exit(); + match args.next() { + Some(name) => self.name = Dname::from_str(&name)?, + None => { + println!("Usage: dig qname [qtype [qclass]]"); + exit(1); + } + }; + match args.next() { + Some(qtype) => self.qtype = Rtype::from_str(&qtype)?, + None => return Ok(()), + } + if let Some(qclass) = args.next() { + self.qclass = Class::from_str(&qclass)? + } + Ok(()) } } impl Options { - fn name(&self) -> Result { - if self.name.is_empty() { - Ok(DNameSlice::root().to_owned()) - } - else { - let mut res = try!(DNameBuf::from_str(&self.name)); - res.append_root().unwrap(); - Ok(res) - } - } - - fn qtype(&self) -> Result { - if self.qtype.is_empty() { - Ok(if self.name.is_empty() { Rtype::Ns } else { Rtype::A }) - } - else { - Ok(try!(Rtype::from_str(&self.qtype))) - } - } - - fn qclass(&self) -> Result { - Ok(Class::In) - } - - fn conf(&self) -> &ResolvConf { &self.conf } } -//------------ Error and Result --------------------------------------------- +impl Options { + fn create_request(&self) -> Result { + let mut msg = MessageBuilder::new_udp(); + msg.header_mut().set_rd(true); + msg.push((&self.name, self.qtype, self.qclass))?; + let mut msg = msg.opt()?; + msg.set_udp_payload_size(4096); + Ok(Message::from_bytes(msg.freeze())?) + } -type Error = Box; - -type Result = result::Result; - - -//------------ Processing Steps --------------------------------------------- - -fn query(options: Options) -> MessageBuf { - Resolver::run_with_conf(options.conf().clone(), |resolv| { - resolv.query((options.name().unwrap(), options.qtype().unwrap(), - options.qclass().unwrap())) - }).unwrap() -} - -fn print_result(response: MessageBuf) { - println!(";; Got answer:"); - println!(";; ->>HEADER<<- opcode: {}, status: {}, id: {}", - response.header().opcode(), response.header().rcode(), - response.header().id()); - print!(";; flags:"); - if response.header().qr() { print!(" qr"); } - if response.header().aa() { print!(" aa"); } - if response.header().tc() { print!(" tc"); } - if response.header().rd() { print!(" rd"); } - if response.header().ra() { print!(" ra"); } - if response.header().ad() { print!(" ad"); } - if response.header().cd() { print!(" cd"); } - println!("; QUERY: {}, ANSWER: {}, AUTHORITY: {}, ADDITIONAL: {}", - response.counts().qdcount(), response.counts().ancount(), - response.counts().nscount(), response.counts().arcount()); - println!(""); - - let mut question = response.question(); - if response.counts().qdcount() > 0 { - println!(";; QUESTION SECTION"); - for item in &mut question { - let item = item.unwrap(); - println!("; {}\t\t{}\t{}", item.qname(), - item.qclass(), item.qtype()); + fn query(&self, request: Message) -> Result { + for server in &self.conf.servers { + if let Some(res) = self.query_udp(&request, server.addr)? { + return Ok(res) + } } - println!(""); + Err(io::Error::new(io::ErrorKind::TimedOut, + "no servers could be reached").into()) } - let mut answer = question.answer().unwrap(); - if response.counts().ancount() > 0 { - println!(";; ANSWER SECTION"); - print_records(&mut answer); - println!(""); + fn query_udp(&self, request: &Message, addr: SocketAddr) + -> Result, Error> { + let sock = UdpSocket::bind("0.0.0.0:0")?; + sock.send_to(request.as_slice(), addr)?; + let done = Instant::now() + self.conf.timeout; + while Instant::now() < done { + sock.set_read_timeout(Some(done - Instant::now()))?; + let mut buf = BytesMut::with_capacity(4096); + unsafe { buf.set_len(4096) }; + let (size, raddr) = match sock.recv_from(buf.as_mut()) { + Ok(res) => res, + Err(err) => { + if err.kind() == io::ErrorKind::TimedOut { + return Ok(None) + } + else { + return Err(err.into()) + } + } + }; + if raddr != addr { + // XXX This may actually be wrong ... + continue + } + unsafe { buf.set_len(size) }; + if let Ok(res) = Message::from_bytes(buf.freeze()) { + if res.is_answer(request) { + return Ok(Some(res)) + } + } + } + Ok(None) } - let mut authority = answer.next_section().unwrap().unwrap(); - if response.counts().nscount() > 0 { - println!(";; AUTHORITY SECTION"); - print_records(&mut authority); + fn print_result(&self, response: Message) -> Result<(), Error> { + println!(";; Got answer:"); + println!(";; ->>HEADER<<- opcode: {}, status: {}, id: {}", + response.header().opcode(), response.header().rcode(), + response.header().id()); + print!(";; flags:"); + if response.header().qr() { print!(" qr"); } + if response.header().aa() { print!(" aa"); } + if response.header().tc() { print!(" tc"); } + if response.header().rd() { print!(" rd"); } + if response.header().ra() { print!(" ra"); } + if response.header().ad() { print!(" ad"); } + if response.header().cd() { print!(" cd"); } + println!("; QUERY: {}, ANSWER: {}, AUTHORITY: {}, ADDITIONAL: {}", + response.header_counts().qdcount(), + response.header_counts().ancount(), + response.header_counts().nscount(), + response.header_counts().arcount()); println!(""); + + let mut question = response.question(); + if response.header_counts().qdcount() > 0 { + println!(";; QUESTION SECTION"); + for item in &mut question { + let item = item.unwrap(); + println!("; {}", item); + } + println!(""); + } + + let mut answer = question.answer().unwrap(); + if response.header_counts().ancount() > 0 { + println!(";; ANSWER SECTION"); + self.print_records(&mut answer); + println!(""); + } + + let mut authority = answer.next_section().unwrap().unwrap(); + if response.header_counts().nscount() > 0 { + println!(";; AUTHORITY SECTION"); + self.print_records(&mut authority); + println!(""); + } + + let mut additional = authority.next_section().unwrap().unwrap(); + if response.header_counts().arcount() > 0 { + println!(";; ADDITIONAL SECTION"); + self.print_records(&mut additional); + println!(""); + } + + Ok(()) } - let mut additional = authority.next_section().unwrap().unwrap(); - if response.counts().arcount() > 0 { - println!(";; ADDITIONAL SECTION"); - print_records(&mut additional); - println!(""); + fn print_records(&self, section: &mut RecordSection) { + for record in section { + let record = record.unwrap() + .into_record::>() + .unwrap().unwrap(); + println!("{}", record); + } } -} -fn print_records(section: &mut RecordSection) { - for record in section { - println!("{}", record.unwrap()); + fn run() -> Result<(), Error> { + let options = Self::from_args()?; + let request = options.create_request()?; + let response = options.query(request)?; + options.print_result(response) } } //------------ Main Function ------------------------------------------------ -*/ fn main() { -/* - let options = Options::from_args(); - let response = query(options); - let len = response.len(); - print_result(response); - println!(";; Query time: not yet available."); - println!(";; SERVER: we don't currently know."); - println!(";; WHEN: not yet available."); - println!(";; MSG SIZE rcvd: {} bytes", len); - println!(""); -*/ + if let Err(err) = Options::run() { + println!("{}", err); + exit(1); + } } diff --git a/src/bits/compose.rs b/src/bits/compose.rs index 657c9a0f..6669f54e 100644 --- a/src/bits/compose.rs +++ b/src/bits/compose.rs @@ -199,6 +199,12 @@ pub trait Compress { fn compress(&self, buf: &mut Compressor) -> Result<(), ShortBuf>; } +impl<'a, C: Compress + 'a> Compress for &'a C { + fn compress(&self, buf: &mut Compressor) -> Result<(), ShortBuf> { + (*self).compress(buf) + } +} + //------------ Compressor ---------------------------------------------------- diff --git a/src/bits/message.rs b/src/bits/message.rs index 51f6d391..f41bab47 100644 --- a/src/bits/message.rs +++ b/src/bits/message.rs @@ -415,6 +415,7 @@ impl QuestionSection { /// /// [`RecordSection`]: struct.RecordSection.html pub fn answer(mut self) -> Result { + // XXX Use Parser::skip here. for question in &mut self { let _ = question?; } @@ -576,6 +577,7 @@ impl RecordSection { Some(section) => section, None => return Ok(None) }; + // XXX Use Parser::skip here. for record in &mut self { let _ = try!(record); } diff --git a/src/bits/message_builder.rs b/src/bits/message_builder.rs index a4aa7ffe..e7f7be4b 100644 --- a/src/bits/message_builder.rs +++ b/src/bits/message_builder.rs @@ -4,75 +4,125 @@ //! contain, among other things, the number of entries in the following four //! section which then contain these entries without any further //! delimitation. In order to safely build a correct message, it thus needs -//! to be assembled step by step, entry by entry. This module provides four -//! types, each responsible for assembling one of the entry sections. +//! to be assembled step by step, entry by entry. This module provides a +//! number of types that can be used to assembling entries in these sections. //! -//! You start out with a [`MessageBuilder`] which you can either create from -//! an existing [`Composer`] or, as a shortcut, either completely [`new()`] -//! or from an existing bytes vector via [`from_vec()`]. Like all of these -//! type, the [`MessageBuilder`] allows access to the header section. In +//! Message building happens by appending data to a [`BytesMut`] buffer. This +//! buffer is automatically grown to accomodate the data if necessary. It +//! does, however, consider the size limit that all DNS messages have. Thus, +//! when you start building by creating a [`MessageBuilder`], you can pass +//! an initial buffer size, a size limit, and a strategy for growing to its +//! [`with_params`] function. Alternatively, you can create the message atop +//! an existing buffer via [`from_buf`]. In this case you can adjust the +//! limits via methods such as [`set_limit`]. +//! +//! All types allow to change the limit later. This is useful if you know +//! already that your message will have to end with an OPT or TSIG record. +//! Since for these you also know the size in advance, you can reserve space +//! by setting a lower limit and increase it only when finally adding those +//! records. +//! +//! Because domain name compression is somewhat expensive, it needs to be +//! enable explicitely through the [`enable_compression`] method. +//! +//! The inital [`MessageBuilder`] allows access to the two first sections of +//! the new message. The +//! header section can be accessed via [`header`] and [`header_mut`]. In //! addition, it is used for building the *question section* of the message. //! This section contains [`Question`]s to be asked of a name server, //! normally exactly one. You can add questions using the -//! [`push()`](struct.MessageBuilder.html#method.push) method. +//! [`push`] method. +//! +//! [`BytesMut`]: ../../../bytes/struct.BytesMut.html +//! [`with_params`]: struct.MessageBuilder.html#method.with_params +//! [`from_buf`]: struct.MessageBuilder.html#method.from_buf +//! [`enable_compression`]: struct.MessageBuilder.html#method.enable_compression +//! [`header`]: struct.MessageBuilder.html#method.header +//! [`header_mut`]: struct.MessageBuilder.html#method.header_mut +//! [`push`]: struct.MessageBuilder.html#method.push +//! [`set_limit`]: struct.MessageBuilder.html#method.set_limit //! //! Once you are happy with the question section, you can proceed to the //! next section, the *answer section,* by calling the -//! [`answer()`](struct.MessageBuilder.html#method.answer) method. In a -//! response, this section contains those resource records that answer the -//! question. The section is represented by the [`AnswerBuilder`] type. -//! It, too, has a [`push()`](struct.AnswerBuilder.html#method.push) method, -//! but for [`Record`]s. +//! [`answer`] method. +//! In a response, this section contains those resource records that answer +//! the question. The section is represented by the [`AnswerBuilder`] type. +//! It, too, has a [`push`] method, but for adding [`Record`]s. //! -//! A call to [`authority()`](struct.AnswerBuilder.html#method.authority) -//! moves on to the *authority section*. It contains resource records that -//! point to the name servers that serve authoritative for the question. -//! Like with the answer section, -//! [`push()`](struct.AuthorityBuilder.html#method.push) adds records to this -//! section. +//! [`answer`]: struct.MessageBuilder.html#method.answer +//! [`push`]: struct.AnswerBuilder.html#method.push +//! +//! A call to [`authority`] moves on to the *authority section*. It contains +//! resource records that allow to identify the name servers that are +//! authoritative for the records requested in the question. As with the +//! answer section, [`push`] adds records to this section. +//! +//! [`authority`]: struct.AnswerBuilder.html#method.authority +//! [`push`]: struct.AuthorityBuilder.html#method.push //! //! The final section is the *additional section.* Here a name server can add //! information it believes will help the client to get to the answer it //! really wants. Which these are depends on the question and is generally //! given in RFCs that define the record types. Unsurprisingly, you will -//! arrive at a [`AdditionalBuilder`] by calling the -//! [`additional()`](struct.AuthorityBuilder.html#method.additional) method -//! once you are done with the authority section. +//! arrive at an [`AdditionalBuilder`] by calling the [`additional`] method +//! once you are done with the authority section. Adding records, once again, +//! happens via the [`push`] method. +//! +//! [`additional`]: struct.AuthorityBuilder.html#method.additional +//! [`push`]: struct.AdditionalBuilder.html#method.push //! //! Once you are done with the additional section, too, you call -//! [`finish()`](struct.AdditionalBuilder.html#method.finish) to retrieve -//! the bytes vector with the assembled message data. +//! [`finish`] to retrieve the underlying bytes buffer or [`freeze`] to get +//! a bytes value instead. +//! +//! [`finish`]: struct.AuthorityBuilder.html#method.finish +//! [`freeze`]: struct.AuthorityBuilder.html#method.freeze //! //! Since at least some of the sections are empty in many messages, for //! instance, a simple request only contains a single question, there are //! shortcuts in place to skip over sections. Each type can go to any later -//! section through the methods named above. Each type also has a `finish()` -//! method to arrive at the final data quickly. +//! section through the methods named above. Each type also has the `finish` +//! and `freeze` methods to arrive at the final data quickly. //! +//! There is one more type: [`OptBuilder`]. It can be used to assemble an +//! OPT record in the additional section. This is helpful because the OPT +//! record in turn is a sequence of options that need to be assembled one +//! by one. +//! +//! An [`OptBuilder`] can be retrieved from an [`AdditionalBuilder`] via its +//! [`opt`] method. Options can then be added as usually via [`push`]. Once +//! done, you can return to the additional section with [`additional`] or, +//! if your OPT record is the final record, conclude message construction +//! via [`finish`] or [`freeze`]. +//! +//! [`opt`]: struct.AdditionalBuilder.html#method.opt +//! [`push`]: struct.OptBuilder.html#method.push +//! [`additional`]: struct.OptBuilder.html#method.additional +//! [`finish`]: struct.OptBuilder.html#method.finish +//! [`freeze`]: struct.OptBuilder.html#method.freeze //! //! # Example //! //! To summarize all of this, here is an example that builds a //! response to an A query for example.com that contains two A records and -//! nothing else. +//! and empty OPT record setting the UDP payload size. //! //! ``` -//! /* //! use std::str::FromStr; -//! use domain::bits::{ComposeMode, DNameBuf, MessageBuilder, Question}; +//! use domain::bits::{Dname, MessageBuilder}; //! use domain::iana::Rtype; //! use domain::rdata::A; //! -//! let name = DNameBuf::from_str("example.com.").unwrap(); -//! let mut msg = MessageBuilder::new(ComposeMode::Limited(512), -//! true).unwrap(); +//! let name = Dname::from_str("example.com.").unwrap(); +//! let mut msg = MessageBuilder::new_udp(); //! msg.header_mut().set_rd(true); //! msg.push((&name, Rtype::A)); //! let mut msg = msg.answer(); //! msg.push((&name, 86400, A::from_octets(192, 0, 2, 1))).unwrap(); //! msg.push((&name, 86400, A::from_octets(192, 0, 2, 2))).unwrap(); -//! let _ = msg.finish(); // get the Vec -//! */ +//! let mut msg = msg.opt().unwrap(); +//! msg.set_udp_payload_size(4096); +//! let _ = msg.freeze(); // get the Bytes //! ``` //! //! [`AdditionalBuilder`]: struct.AdditionalBuilder.html @@ -80,6 +130,7 @@ //! [`AuthorityBuilder`]: struct.AuthorityBuilder.html //! [`Composer`]: ../compose/Composer.html //! [`MessageBuilder`]: struct.MessageBuilder.html +//! [`OptBuilder`]: struct.OptBuilder.html //! [`Question`]: ../question/struct.Question.html //! [`Record`]: ../record/struct.Record.html //! [`new()`]: struct.MessageBuilder.html#method.new @@ -101,12 +152,22 @@ use super::record::Record; //------------ MessageBuilder ----------------------------------------------- -/// A type for building the question section of a DNS message. +/// Starts building a DNS message. /// /// This type starts building a DNS message and allows adding questions to -/// its question section. See the [module documentation] for details. +/// its question section. See the [module documentation] for an overview of +/// how to build a message. +/// +/// Message builders operate atop a [`BytesMut`] byte buffer. There are a +/// number of functions to create a builder either using an existing +/// buffer or with a newly created buffer. +/// +/// Once created, it is possible to access the message header or append +/// questions to the question section before proceeding to the subsequent +/// parts of the message. /// /// [module documentation]: index.html +/// [`BytesMut`]: ../../../bytes/struct.BytesMut.html #[derive(Clone, Debug)] pub struct MessageBuilder { target: MessageTarget, @@ -116,18 +177,66 @@ pub struct MessageBuilder { /// # Creation and Preparation /// impl MessageBuilder { + /// Creates a new builder for a UDP message. + /// + /// The builder will use a new bytes buffer. The buffer will have a + /// capacity of 512 bytes and will also be limited to that. + /// + /// This will result in a UDP message following the original limit. If you + /// want to create larger messages, you should signal this through the use + /// of EDNS. pub fn new_udp() -> Self { Self::with_params(512, 512, 0) } + /// Creates a new builder for a TCP message. + /// + /// The builder will use a new buffer. It will be limited to 65535 bytes, + /// starting with the capacity given and also growing by that amount. + /// + /// Since DNS messages are preceded on TCP by a two octet length + /// inicator, the function will add two bytes with zero before the + /// message. Once you have completed your message, you can use can set + /// these two bytes to the size of the message. But remember that they + /// are in network byte order. + pub fn new_tcp(capacity: usize) -> Self { + let mut buf = BytesMut::with_capacity(capacity + 2); + buf.put_u16::(0); + let mut res = Self::from_buf(buf); + res.set_limit(::std::u16::MAX as usize); + res.set_page_size(capacity); + res + } + + /// Creates a new message builder using an existing bytes buffer. + /// + /// The builder’s initial limit will be equal to whatever capacity is + /// left in the buffer. As a consequence, the builder will never grow + /// beyond that remaining capacity. pub fn from_buf(buf: BytesMut) -> Self { MessageBuilder { target: MessageTarget::from_buf(buf) } } + /// Creates a message builder with the given capacity. + /// + /// The builder will have its own newly created bytes buffer. Its inital + /// limit will be equal to the capacity of that buffer. This may be larger + /// than `capacity`. If you need finer control over the limit, use + /// [`with_params`] instead. + /// + /// [`with_params`]: #method.with_params pub fn with_capacity(capacity: usize) -> Self { Self::from_buf(BytesMut::with_capacity(capacity)) } + /// Creates a new message builder. + /// + /// A new buffer will be created for this builder. It will initially + /// allocate space for at least `initial` bytes. The message will never + /// exceed a size of `limit` bytes. Whenever the buffer’s capacity is + /// exhausted, the builder will allocate at least another `page_size` + /// bytes. If `page_size` is set to `0`, the builder will allocate at + /// most once and then enough bytes to have room for the limit. pub fn with_params(initial: usize, limit: usize, page_size: usize) -> Self { let mut res = Self::with_capacity(initial); @@ -136,14 +245,40 @@ impl MessageBuilder { res } + /// Enables support for domain name compression. + /// + /// After this method is called, the domain names in questions, the owner + /// domain names of resource records, and domain names appearing in the + /// record data of record types defined in [RFC 1035] will be compressed. + /// + /// [RFC 1035]: ../../rdata/rfc1035.rs pub fn enable_compression(&mut self) { self.target.buf.enable_compression() } + /// Sets the maximum size of the constructed DNS message. + /// + /// After this method was called, additional data will not be added to the + /// message if that would result in the message exceeding a size of + /// `limit` bytes. If the message is already larger than `limit` when the + /// method is called, it will _not_ be truncated. That is, you can never + /// actually set a limit smaller than the current message size. + /// + /// Note also that the limit only regards the message constructed by the + /// builder itself. If a builder was created atop a buffer that already + /// contained some data, this pre-existing data is not considered. pub fn set_limit(&mut self, limit: usize) { self.target.buf.set_limit(limit) } + /// Sets the amount of data by which to grow the underlying buffer. + /// + /// Whenever the buffer runs out of space but the message size limit has + /// not yet been reached, the builder will grow the buffer by at least + /// `page_size` bytes. + /// + /// A special case is a page size of zero, in which case the buffer will + /// be grown only once to have enough space to reach the current limit. pub fn set_page_size(&mut self, page_size: usize) { self.target.buf.set_page_size(page_size) } @@ -179,10 +314,13 @@ impl MessageBuilder { /// fulfill this requirement with the class assumed to be `Class::In` in /// the latter case. /// + /// The method will fail if by appending the question the message would + /// exceed its size limit. + /// /// [`Question`]: ../question/struct.Question.html - pub fn push(&mut self, question: &Question) - -> Result<(), ShortBuf> { - self.target.push(|target| question.compress(target), + pub fn push>>(&mut self, question: Q) + -> Result<(), ShortBuf> { + self.target.push(|target| question.into().compress(target), |counts| counts.inc_qdcount()) } @@ -204,6 +342,10 @@ impl MessageBuilder { } /// Proceeds to building the OPT record. + /// + /// Leaves the answer and additional sections empty. Since the method + /// adds the header of the OPT record already, it can fail if there + /// isn’t enough space left in the message. pub fn opt(self) -> Result { self.additional().opt() } @@ -213,8 +355,9 @@ impl MessageBuilder { /// This method requires a `&mut self` since it may need to update some /// length values to return a valid message. /// - /// In case the builder was created from a vector with previous content, - /// the returned reference is for the full content of this vector. + /// In case the builder was created from a buffer with pre-existing + /// content, the returned reference is for the complete content of this + /// buffer. pub fn preview(&mut self) -> &[u8] { self.target.preview() } @@ -226,6 +369,9 @@ impl MessageBuilder { self.target.unwrap() } + /// Finishes the messages and returns the bytes value of the message. + /// + /// This will result in a message with all three record sections empty. pub fn freeze(self) -> Bytes { self.target.freeze() } @@ -234,13 +380,18 @@ impl MessageBuilder { //------------ AnswerBuilder ------------------------------------------------- -/// A type for building the answer section of a DNS message. +/// Builds the answer section of a DNS message. /// -/// This type is typically constructed by calling [`answer()`] on a -/// [`MessageBuilder`]. See the [module documentation] for details. +/// This type is typically constructed by calling [`answer`] on a +/// [`MessageBuilder`]. See the [module documentation] for an overview of how +/// to build a message. /// -/// [`answer()`]: struct.MessageBuilder.html#method.answer +/// Once acquired, you can access a message’s header or append resource +/// records to the message’s answer section with the [`push`] method. +/// +/// [`answer`]: struct.MessageBuilder.html#method.answer /// [`MessageBuilder`]: struct.MessageBuilder.html +/// [`push`]: #method.push /// [module documentation]: index.html #[derive(Clone, Debug)] pub struct AnswerBuilder { @@ -249,11 +400,22 @@ pub struct AnswerBuilder { impl AnswerBuilder { - /// Creates a new answer builder from a compser. + /// Creates a new answer builder from a message target. fn new(target: MessageTarget) -> Self { AnswerBuilder { target } } + /// Updates the message’s size limit. + /// + /// After this method was called, additional data will not be added to the + /// message if that would result in the message exceeding a size of + /// `limit` bytes. If the message is already larger than `limit` when the + /// method is called, it will _not_ be truncated. That is, you can never + /// actually set a limit smaller than the current message size. + /// + /// Note also that the limit only regards the message constructed by the + /// builder itself. If a builder was created atop a buffer that already + /// contained some data, this pre-existing data is not considered. pub fn set_limit(&mut self, limit: usize) { self.target.buf.set_limit(limit) } @@ -268,10 +430,24 @@ impl AnswerBuilder { self.target.header_mut() } + /// Returns a snapshot indicating the current state of the message. + /// + /// The returned value can be used to later return the message to the + /// state at the time the method was called through the [`rewind`] + /// method. + /// + /// [`rewind`]: #method.rewind pub fn snapshot(&self) -> Snapshot { self.target.snapshot() } + /// Rewinds the message to the state it had at `snapshot`. + /// + /// This will truncate the message to the size it had at the time the + /// [`snapshot`] method was called, making it forget all records added + /// since. + /// + /// [`snapshot`]: #method.snapshot pub fn rewind(&mut self, snapshot: Snapshot) { self.target.rewind(snapshot) } @@ -283,12 +459,13 @@ impl AnswerBuilder { /// a domain name, class, TTL, and record data or triples leaving out /// the class which will then be assumed to be `Class::In`. /// + /// If appending the record would result in the message exceeding its + /// size limit, the method will fail. + /// /// [`Record`]: ../record/struct.Record.html - pub fn push(&mut self, record: &Record) - -> Result<(), ShortBuf> - where N: ToDname, - D: RecordData { - self.target.push(|target| record.compress(target), + pub fn push(&mut self, record: R) -> Result<(), ShortBuf> + where N: ToDname, D: RecordData, R: Into> { + self.target.push(|target| record.into().compress(target), |counts| counts.inc_ancount()) } @@ -303,6 +480,13 @@ impl AnswerBuilder { } /// Proceeds to building the OPT record. + /// + /// The method will start by adding the record header. Since this may + /// exceed the message limit, the method may fail. + /// If you have saved space for the OPT record via [`set_limit`] earlier, + /// remember to increase the limit again before calling `opt`. + /// + /// [`set_limit`]: #method.set_limit pub fn opt(self) -> Result { self.additional().opt() } @@ -320,11 +504,16 @@ impl AnswerBuilder { /// Finishes the message and returns the underlying bytes buffer. /// - /// This will result in a message with all three record sections empty. + /// This will result in a message with empty authority and additional + /// sections. pub fn finish(self) -> BytesMut { self.target.unwrap() } + /// Finishes the message and returns the resulting bytes value. + /// + /// This will result in a message with empty authority and additional + /// sections. pub fn freeze(self) -> Bytes { self.target.freeze() } @@ -333,14 +522,18 @@ impl AnswerBuilder { //------------ AuthorityBuilder --------------------------------------------- -/// A type for building the authority section of a DNS message. +/// Builds the authority section of a DNS message. /// /// This type can be constructed by calling `authority()` on a /// [`MessageBuilder`] or [`AnswerBuilder`]. See the [module documentation] -/// for details. +/// for details on constructing messages. +/// +/// Once acquired, you can use this type to add records to the authority +/// section of a message via the [`push`] method. /// /// [`AnswerBuilder`]: struct.AnswerBuilder.html /// [`MessageBuilder`]: struct.MessageBuilder.html +/// [`push`]: #method.push /// [module documentation]: index.html #[derive(Clone, Debug)] pub struct AuthorityBuilder { @@ -354,6 +547,17 @@ impl AuthorityBuilder { AuthorityBuilder { target } } + /// Updates the message’s size limit. + /// + /// After this method was called, additional data will not be added to the + /// message if that would result in the message exceeding a size of + /// `limit` bytes. If the message is already larger than `limit` when the + /// method is called, it will _not_ be truncated. That is, you can never + /// actually set a limit smaller than the current message size. + /// + /// Note also that the limit only regards the message constructed by the + /// builder itself. If a builder was created atop a buffer that already + /// contained some data, this pre-existing data is not considered. pub fn set_limit(&mut self, limit: usize) { self.target.buf.set_limit(limit) } @@ -368,10 +572,24 @@ impl AuthorityBuilder { self.target.header_mut() } + /// Returns a snapshot indicating the current state of the message. + /// + /// The returned value can be used to later return the message to the + /// state at the time the method was called through the [`rewind`] + /// method. + /// + /// [`rewind`]: #method.rewind pub fn snapshot(&self) -> Snapshot { self.target.snapshot() } + /// Rewinds the message to the state it had at `snapshot`. + /// + /// This will truncate the message to the size it had at the time the + /// [`snapshot`] method was called, making it forget all records added + /// since. + /// + /// [`snapshot`]: #method.snapshot pub fn rewind(&mut self, snapshot: Snapshot) { self.target.rewind(snapshot) } @@ -383,12 +601,13 @@ impl AuthorityBuilder { /// a domain name, class, TTL, and record data or triples leaving out /// the class which will then be assumed to be `Class::In`. /// + /// If appending the record would result in the message exceeding its + /// size limit, the method will fail. + /// /// [`Record`]: ../record/struct.Record.html - pub fn push(&mut self, record: &Record) - -> Result<(), ShortBuf> - where N: ToDname, - D: RecordData { - self.target.push(|target| record.compress(target), + pub fn push(&mut self, record: R) -> Result<(), ShortBuf> + where N: ToDname, D: RecordData, R: Into> { + self.target.push(|target| record.into().compress(target), |counts| counts.inc_nscount()) } @@ -398,6 +617,13 @@ impl AuthorityBuilder { } /// Proceeds to building the OPT record. + /// + /// The method will start by adding the record header. Since this may + /// exceed the message limit, the method may fail. + /// If you have saved space for the OPT record via [`set_limit`] earlier, + /// remember to increase the limit again before calling `opt`. + /// + /// [`set_limit`]: #method.set_limit pub fn opt(self) -> Result { self.additional().opt() } @@ -415,11 +641,14 @@ impl AuthorityBuilder { /// Finishes the message and returns the underlying bytes buffer. /// - /// This will result in a message with all three record sections empty. + /// This will result in a message with an empty additional section. pub fn finish(self) -> BytesMut { self.target.unwrap() } + /// Finishes the message and returns the resulting bytes value. + /// + /// This will result in a message with an empty additional section. pub fn freeze(self) -> Bytes { self.target.freeze() } @@ -428,15 +657,23 @@ impl AuthorityBuilder { //------------ AdditionalBuilder -------------------------------------------- -/// A type for building the additional section of a DNS message. +/// Builds the additional section of a DNS message. /// -/// This type can be constructed by calling `additional()` on a +/// This type can be constructed by calling `additional` on a /// [`MessageBuilder`], [`AnswerBuilder`], or [`AuthorityBuilder`]. See the -/// [module documentation] for details. +/// [module documentation] for on overview on building messages. +/// +/// Once aquired, you can add records to the additional section via the +/// [`push`] method. If the record you want to add is an OPT record, you +/// can also use the [`OptBuilder`] type which you can acquire via the +/// [`opt`] method. /// /// [`AnswerBuilder`]: struct.AnswerBuilder.html /// [`AuthorityBuilder`]: struct.AuthorityBuilder.html /// [`MessageBuilder`]: struct.MessageBuilder.html +/// [`OptBuilder`]: struct.OptBuilder.html +/// [`push`]: #method.push +/// [`opt`]: #method.opt /// [module documentation]: index.html #[derive(Clone, Debug)] pub struct AdditionalBuilder { @@ -455,6 +692,17 @@ impl AdditionalBuilder { self.target.header() } + /// Updates the message’s size limit. + /// + /// After this method was called, additional data will not be added to the + /// message if that would result in the message exceeding a size of + /// `limit` bytes. If the message is already larger than `limit` when the + /// method is called, it will _not_ be truncated. That is, you can never + /// actually set a limit smaller than the current message size. + /// + /// Note also that the limit only regards the message constructed by the + /// builder itself. If a builder was created atop a buffer that already + /// contained some data, this pre-existing data is not considered. pub fn set_limit(&mut self, limit: usize) { self.target.buf.set_limit(limit) } @@ -464,10 +712,24 @@ impl AdditionalBuilder { self.target.header_mut() } + /// Returns a snapshot indicating the current state of the message. + /// + /// The returned value can be used to later return the message to the + /// state at the time the method was called through the [`rewind`] + /// method. + /// + /// [`rewind`]: #method.rewind pub fn snapshot(&self) -> Snapshot { self.target.snapshot() } + /// Rewinds the message to the state it had at `snapshot`. + /// + /// This will truncate the message to the size it had at the time the + /// [`snapshot`] method was called, making it forget all records added + /// since. + /// + /// [`snapshot`]: #method.snapshot pub fn rewind(&mut self, snapshot: Snapshot) { self.target.rewind(snapshot) } @@ -479,16 +741,24 @@ impl AdditionalBuilder { /// a domain name, class, TTL, and record data or triples leaving out /// the class which will then be assumed to be `Class::In`. /// + /// If appending the record would result in the message exceeding its + /// size limit, the method will fail. + /// /// [`Record`]: ../record/struct.Record.html - pub fn push(&mut self, record: &Record) - -> Result<(), ShortBuf> - where N: ToDname, - D: RecordData { - self.target.push(|target| record.compress(target), - |counts| counts.inc_arcount()) + pub fn push(&mut self, record: R) -> Result<(), ShortBuf> + where N: ToDname, D: RecordData, R: Into> { + self.target.push(|target| record.into().compress(target), + |counts| counts.inc_nscount()) } /// Proceeds to building the OPT record. + /// + /// The method will start by adding the record header. Since this may + /// exceed the message limit, the method may fail. + /// If you have saved space for the OPT record via [`set_limit`] earlier, + /// remember to increase the limit again before calling `opt`. + /// + /// [`set_limit`]: #method.set_limit pub fn opt(self) -> Result { OptBuilder::new(self.target) } @@ -505,12 +775,11 @@ impl AdditionalBuilder { } /// Finishes the message and returns the underlying bytes buffer. - /// - /// This will result in a message with all three record sections empty. pub fn finish(self) -> BytesMut { self.target.unwrap() } + /// Finishes the message and returns the resulting bytes value. pub fn freeze(self) -> Bytes { self.target.freeze() } @@ -519,6 +788,13 @@ impl AdditionalBuilder { //------------ OptBuilder ---------------------------------------------------- +/// Builds an OPT record as part of the additional section of a DNS message, +/// +/// This type can be constructed by calling the `opt` method on a +/// [`MessageBuilder`], [`AnswerBuilder`], [`AuthorityBuilder`], or +/// [`AdditionalBuilder`]. See the [module documentation] for on overview +/// on building messages. +/// #[derive(Clone, Debug)] pub struct OptBuilder { target: MessageTarget, @@ -571,7 +847,8 @@ impl OptBuilder { let len = self.target.len() - (self.pos + mem::size_of::() + 2); assert!(len <= ::std::u16::MAX as usize); - BigEndian::write_u16(&mut self.target.as_slice_mut()[self.pos..], + let count_pos = self.pos + mem::size_of::(); + BigEndian::write_u16(&mut self.target.as_slice_mut()[count_pos..], len as u16); self.target.counts_mut().inc_arcount(); self.target @@ -614,7 +891,6 @@ impl MessageTarget { - buf.remaining_mut(); buf.reserve(additional) } - 0u16.compose(&mut buf); let mut buf = Compressor::from_buf(buf); HeaderSection::default().compose(&mut buf); MessageTarget { buf, start } diff --git a/src/bits/mod.rs b/src/bits/mod.rs index 02c3101f..90418b7d 100644 --- a/src/bits/mod.rs +++ b/src/bits/mod.rs @@ -88,16 +88,17 @@ pub use self::charstr::{CharStr, CharStrMut}; pub use self::compose::{Compose, Compress, Compressor}; pub use self::header::{Header, HeaderCounts, HeaderSection}; +pub use self::message::{Message, RecordSection}; +pub use self::message_builder::MessageBuilder; +pub use self::name::{Dname, ParsedDname, RelativeDname}; /* -pub use self::message::{Message, MessageBuf}; -pub use self::message_builder::{MessageBuilder, AnswerBuilder, - AuthorityBuilder, AdditionalBuilder}; -pub use self::name::{DName, DNameBuf, DNameSlice, ParsedDName}; pub use self::parse::{Parser, ParseError, ParseResult}; -pub use self::question::Question; -pub use self::rdata::{GenericRecordData, ParsedRecordData, RecordData}; -pub use self::record::{GenericRecord, Record}; */ +pub use self::question::Question; +/* +pub use self::rdata::{GenericRecordData, ParsedRecordData, RecordData}; +*/ +pub use self::record::{Record}; //--- Modules diff --git a/src/bits/name/dname.rs b/src/bits/name/dname.rs index 2d4c1f7a..43def818 100644 --- a/src/bits/name/dname.rs +++ b/src/bits/name/dname.rs @@ -485,9 +485,17 @@ impl hash::Hash for Dname { //--- Display and Debug impl fmt::Display for Dname { + /// Formats the domain name. + /// + /// This will produce the domain name in common display format without + /// the trailing dot. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for label in self.iter() { - write!(f, ".{}", label)? + let mut iter = self.iter(); + write!(f, "{}", iter.next().unwrap())?; + for label in iter { + if !label.is_root() { + write!(f, ".{}", label)? + } } Ok(()) } @@ -495,7 +503,7 @@ impl fmt::Display for Dname { impl fmt::Debug for Dname { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Dname({})", self) + write!(f, "Dname({}.)", self) } } @@ -514,7 +522,7 @@ impl Scan for Dname { impl Print for Dname { fn print(&self, printer: &mut Printer) -> Result<(), io::Error> { - write!(printer.item()?, "{}", self) + write!(printer.item()?, "{}.", self) } } diff --git a/src/bits/name/parsed.rs b/src/bits/name/parsed.rs index 2ba398f2..b8ccca6b 100644 --- a/src/bits/name/parsed.rs +++ b/src/bits/name/parsed.rs @@ -84,12 +84,11 @@ impl Parse for ParsedDname { // Phase 1: Take labels from the parser until the root label or the // first compressed label. In the latter case, remember where // the actual name ended. - let end = loop { + let mut parser = loop { match LabelType::parse(parser) { Ok(LabelType::Normal(0)) => { len += 1; if len > 255 { - parser.seek(start).unwrap(); return Err(BadParsedDname::LongName.into()) } let mut res = parser.clone(); @@ -99,24 +98,21 @@ impl Parse for ParsedDname { } Ok(LabelType::Normal(label_len)) => { if let Err(err) = parser.advance(label_len) { - parser.seek(start).unwrap(); return Err(err.into()) } len += label_len + 1; if len > 255 { - parser.seek(start).unwrap(); return Err(BadParsedDname::LongName.into()) } } Ok(LabelType::Compressed(pos)) => { + let mut parser = parser.clone(); if let Err(err) = parser.seek(pos) { - parser.seek(start).unwrap(); return Err(err.into()) } - break parser.pos() + break parser } Err(err) => { - parser.seek(start).unwrap(); return Err(err) } } @@ -124,29 +120,25 @@ impl Parse for ParsedDname { // Phase 2: Follow offsets so we can get the length. loop { - match LabelType::parse(parser)? { + match LabelType::parse(&mut parser)? { LabelType::Normal(0) => { len += 1; if len > 255 { - parser.seek(start).unwrap(); return Err(BadParsedDname::LongName.into()) } break; } LabelType::Normal(label_len) => { if let Err(err) = parser.advance(label_len) { - parser.seek(start).unwrap(); return Err(err.into()) } len += label_len + 1; if len > 255 { - parser.seek(start).unwrap(); return Err(BadParsedDname::LongName.into()) } } LabelType::Compressed(pos) => { if let Err(err) = parser.seek(pos) { - parser.seek(start).unwrap(); return Err(err.into()) } } @@ -154,10 +146,38 @@ impl Parse for ParsedDname { } // Phase 3: Profit - parser.seek(end).unwrap(); - let mut res = parser.clone(); - res.seek(start).unwrap(); - Ok(ParsedDname { parser: res, len, compressed: true }) + parser.seek(start).unwrap(); + Ok(ParsedDname { parser, len, compressed: true }) + } + + fn skip(parser: &mut Parser) -> Result<(), Self::Err> { + let mut len = 0; + loop { + match LabelType::parse(parser) { + Ok(LabelType::Normal(0)) => { + len += 1; + if len > 255 { + return Err(BadParsedDname::LongName.into()) + } + return Ok(()) + } + Ok(LabelType::Normal(label_len)) => { + if let Err(err) = parser.advance(label_len) { + return Err(err.into()) + } + len += label_len + 1; + if len > 255 { + return Err(BadParsedDname::LongName.into()) + } + } + Ok(LabelType::Compressed(_)) => { + return Ok(()) + } + Err(err) => { + return Err(err) + } + } + } } } @@ -268,9 +288,17 @@ impl hash::Hash for ParsedDname { //--- Display and Debug impl fmt::Display for ParsedDname { + /// Formats the domain name. + /// + /// This will produce the domain name in common display format without + /// the trailing dot. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for label in self.iter() { - write!(f, ".{}", label)? + let mut iter = self.iter(); + write!(f, "{}", iter.next().unwrap())?; + for label in iter { + if !label.is_root() { + write!(f, ".{}", label)? + } } Ok(()) } @@ -278,7 +306,7 @@ impl fmt::Display for ParsedDname { impl fmt::Debug for ParsedDname { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ParsedDname({})", self) + write!(f, "ParsedDname({}.)", self) } } @@ -288,7 +316,7 @@ impl fmt::Debug for ParsedDname { impl Print for ParsedDname { fn print(&self, printer: &mut Printer) -> Result<(), io::Error> { - write!(printer.item()?, "{}", self) + write!(printer.item()?, "{}.", self) } } diff --git a/src/bits/name/traits.rs b/src/bits/name/traits.rs index 3728fff9..093b6440 100644 --- a/src/bits/name/traits.rs +++ b/src/bits/name/traits.rs @@ -56,6 +56,14 @@ pub trait ToLabelIter<'a> { } } +impl<'a, 'b, N: ToLabelIter<'b>> ToLabelIter<'b> for &'a N { + type LabelIter = N::LabelIter; + + fn iter_labels(&'b self) -> Self::LabelIter { + (*self).iter_labels() + } +} + //------------ ToRelativeDname ----------------------------------------------- @@ -88,6 +96,8 @@ pub trait ToRelativeDname: Compose + for<'a> ToLabelIter<'a> { } } +impl<'a, N: ToRelativeDname + 'a> ToRelativeDname for &'a N { } + //------------ ToDname ------------------------------------------------------- @@ -121,3 +131,4 @@ pub trait ToDname: Compose + Compress + for<'a> ToLabelIter<'a> { } } +impl<'a, N: ToDname + 'a> ToDname for &'a N { } diff --git a/src/bits/opt/mod.rs b/src/bits/opt/mod.rs index 14612403..0e551e55 100644 --- a/src/bits/opt/mod.rs +++ b/src/bits/opt/mod.rs @@ -26,6 +26,16 @@ pub mod rfc7873; pub mod rfc7901; pub mod rfc8145; +pub use self::rfc5001::Nsid; +pub use self::rfc6975::{Dau, Dhu, N3u}; +pub use self::rfc7314::Expire; +pub use self::rfc7828::TcpKeepalive; +pub use self::rfc7830::Padding; +pub use self::rfc7871::ClientSubnet; +pub use self::rfc7873::Cookie; +pub use self::rfc7901::Chain; +pub use self::rfc8145::KeyTag; + //------------ Opt ----------------------------------------------------------- @@ -155,7 +165,7 @@ impl OptHeader { impl Default for OptHeader { fn default() -> Self { - OptHeader { inner: [0, 41, 0, 0, 0, 0, 0, 0, 0] } + OptHeader { inner: [0, 0, 41, 0, 0, 0, 0, 0, 0] } } } diff --git a/src/bits/question.rs b/src/bits/question.rs index eaceaa5c..5ca2e360 100644 --- a/src/bits/question.rs +++ b/src/bits/question.rs @@ -1,13 +1,15 @@ +use std::fmt; use bytes::BufMut; use ::iana::{Class, Rtype}; use super::compose::{Compose, Compress, Compressor}; +use super::name::ToDname; use super::parse::{Parse, Parser, ShortBuf}; //------------ Question ------------------------------------------------------ #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Question { +pub struct Question { qname: N, qtype: Rtype, qclass: Class, @@ -15,7 +17,7 @@ pub struct Question { /// # Creation and Conversion /// -impl Question { +impl Question { pub fn new(qname: N, qtype: Rtype, qclass: Class) -> Self { Question { qname, qtype, qclass } } @@ -28,7 +30,7 @@ impl Question { /// # Field Access /// -impl Question { +impl Question { pub fn qname(&self) -> &N { &self.qname } @@ -45,13 +47,13 @@ impl Question { //--- From -impl From<(N, Rtype, Class)> for Question { +impl From<(N, Rtype, Class)> for Question { fn from((name, rtype, class): (N, Rtype, Class)) -> Self { Question::new(name, rtype, class) } } -impl From<(N, Rtype)> for Question { +impl From<(N, Rtype)> for Question { fn from((name, rtype): (N, Rtype)) -> Self { Question::new(name, rtype, Class::In) } @@ -60,7 +62,7 @@ impl From<(N, Rtype)> for Question { //--- Parse, Compose, and Compress -impl Parse for Question { +impl Parse for Question { type Err = ::Err; fn parse(parser: &mut Parser) -> Result { @@ -72,7 +74,7 @@ impl Parse for Question { } } -impl Compose for Question { +impl Compose for Question { fn compose_len(&self) -> usize { self.qname.compose_len() + self.qtype.compose_len() + self.qclass.compose_len() @@ -85,7 +87,7 @@ impl Compose for Question { } } -impl Compress for Question { +impl Compress for Question { fn compress(&self, buf: &mut Compressor) -> Result<(), ShortBuf> { self.qname.compress(buf)?; buf.compose(&self.qtype)?; @@ -93,3 +95,12 @@ impl Compress for Question { } } + +//--- Display + +impl fmt::Display for Question { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}.\t{}\t{}", self.qname, self.qtype, self.qclass) + } +} + diff --git a/src/bits/record.rs b/src/bits/record.rs index c882d862..d45bd204 100644 --- a/src/bits/record.rs +++ b/src/bits/record.rs @@ -14,7 +14,7 @@ use ::iana::{Class, Rtype}; use ::master::print::{Print, Printer}; //use ::master::scan::{CharSource, Scannable, Scanner}; use super::compose::{Compose, Compress, Compressor}; -use super::name::{ParsedDname, ParsedDnameError}; +use super::name::{ParsedDname, ParsedDnameError, ToDname}; use super::parse::{Parse, Parser, ShortBuf}; use super::rdata::{ParseRecordData, RecordData}; @@ -76,7 +76,7 @@ use super::rdata::{ParseRecordData, RecordData}; /// [`Rtype`]: ../../iana/enum.Rtype.html /// [`domain::master`]: ../../master/index.html #[derive(Clone, Debug)] -pub struct Record { +pub struct Record { name: N, class: Class, ttl: u32, @@ -86,7 +86,7 @@ pub struct Record { /// # Creation and Element Access /// -impl Record { +impl Record { /// Creates a new record from its parts. pub fn new(name: N, class: Class, ttl: u32, data: D) -> Self { Record { name, class, ttl, data } @@ -142,6 +142,21 @@ impl Record { } +//--- From + +impl From<(N, Class, u32, D)> for Record { + fn from((name, class, ttl, data): (N, Class, u32, D)) -> Self { + Self::new(name, class, ttl, data) + } +} + +impl From<(N, u32, D)> for Record { + fn from((name, ttl, data): (N, u32, D)) -> Self { + Self::new(name, Class::In, ttl, data) + } +} + + //--- Parsable, Compose, and Compressor impl Parse for Option> { @@ -167,7 +182,7 @@ impl Parse for Option> { } } -impl Compose for Record { +impl Compose for Record { fn compose_len(&self) -> usize { self.name.compose_len() + self.data.compose_len() + 10 } @@ -180,7 +195,7 @@ impl Compose for Record { } } -impl Compress +impl Compress for Record { fn compress(&self, buf: &mut Compressor) -> Result<(), ShortBuf> { self.name.compress(buf)?; @@ -208,7 +223,7 @@ impl Scannable for Record { } */ -impl Print for Record +impl Print for Record where N: Print, D: RecordData + Print { fn print(&self, printer: &mut Printer) -> Result<(), io::Error> { @@ -221,27 +236,12 @@ impl Print for Record } -//--- From - -impl From<(N, Class, u32, D)> for Record { - fn from(x: (N, Class, u32, D)) -> Self { - Record::new(x.0, x.1, x.2, x.3) - } -} - -impl From<(N, u32, D)> for Record { - fn from(x: (N, u32, D)) -> Self { - Record::new(x.0, Class::In, x.1, x.2) - } -} - - -//--- Display and Print +//--- Display impl fmt::Display for Record - where N: fmt::Display, D: RecordData + fmt::Display { + where N: ToDname + fmt::Display, D: RecordData + fmt::Display { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}\t{}\t{}\t{}\t{}", + write!(f, "{}.\t{}\t{}\t{}\t{}", self.name, self.ttl, self.class, self.data.rtype(), self.data) } @@ -299,7 +299,7 @@ impl RecordHeader { } } -impl RecordHeader { +impl RecordHeader { /// Returns a reference to the owner of the record. pub fn name(&self) -> &N { &self.name @@ -326,7 +326,7 @@ impl RecordHeader { } /// Converts the header into an actual record. - pub fn into_record(self, data: D) -> Record { + pub fn into_record(self, data: D) -> Record { Record::new(self.name, self.class, self.ttl, data) } } @@ -455,7 +455,9 @@ impl Parse for ParsedRecord { fn parse(parser: &mut Parser) -> Result { let header = RecordHeader::parse(parser)?; - Ok(Self::new(header, parser.clone())) + let data = parser.clone(); + parser.advance(header.rdlen() as usize)?; + Ok(Self::new(header, data)) } } diff --git a/src/lib.rs b/src/lib.rs index fe64fb20..6860628b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,7 @@ pub mod bits; pub mod iana; pub mod master; pub mod rdata; +pub mod resolv; /* pub mod utils; -pub mod resolv; */ diff --git a/src/rdata/rfc1035.rs b/src/rdata/rfc1035.rs index d01145a4..703d866e 100644 --- a/src/rdata/rfc1035.rs +++ b/src/rdata/rfc1035.rs @@ -136,7 +136,7 @@ macro_rules! dname_type { impl fmt::Display for $target { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.$field.fmt(f) + write!(f, "{}.", self.$field) } } } @@ -590,7 +590,7 @@ impl RtypeRecordData for Minfo { impl fmt::Display for Minfo { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {}", self.rmailbx, self.emailbx) + write!(f, "{}. {}.", self.rmailbx, self.emailbx) } } @@ -714,7 +714,7 @@ impl RtypeRecordData for Mx { impl fmt::Display for Mx { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {}", self.preference, self.exchange) + write!(f, "{} {}.", self.preference, self.exchange) } } @@ -1013,8 +1013,9 @@ impl RtypeRecordData for Soa { impl fmt::Display for Soa { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {} {} {} {} {} {}", self.mname, self.rname, self.serial, - self.refresh, self.retry, self.expire, self.minimum) + write!(f, "{}. {}. {} {} {} {} {}", + self.mname, self.rname, self.serial, self.refresh, self.retry, + self.expire, self.minimum) } } diff --git a/src/resolv/conf.rs b/src/resolv/conf.rs index 878a2e2c..dd880e92 100644 --- a/src/resolv/conf.rs +++ b/src/resolv/conf.rs @@ -22,7 +22,7 @@ use std::path::Path; use std::str::{self, FromStr, SplitWhitespace}; use std::result; use std::time::Duration; -use ::bits::name::{self, DNameBuf, DNameSlice}; +use ::bits::name::{self, Dname}; //------------ ResolvOptions ------------------------------------------------ @@ -269,7 +269,7 @@ pub struct ResolvConf { pub servers: Vec, /// Search list for host-name lookup. - pub search: Vec, + pub search: Vec, /// TODO Sortlist /// sortlist: ?? @@ -328,7 +328,7 @@ impl ResolvConf { self.servers.push(ServerConf::new(addr)) } if self.search.is_empty() { - self.search.push(DNameSlice::root().to_owned()) + self.search.push(Dname::root()) } for server in &mut self.servers { server.request_timeout = self.timeout @@ -396,8 +396,7 @@ impl ResolvConf { } fn parse_domain(&mut self, mut words: SplitWhitespace) -> Result<()> { - let mut domain = try!(DNameBuf::from_str(try!(next_word(&mut words)))); - domain.append(&DNameSlice::root()).unwrap(); // XXX + let domain = try!(Dname::from_str(try!(next_word(&mut words)))); self.search = Vec::new(); self.search.push(domain); no_more_words(words) @@ -406,8 +405,7 @@ impl ResolvConf { fn parse_search(&mut self, words: SplitWhitespace) -> Result<()> { let mut search = Vec::new(); for word in words { - let mut name = try!(DNameBuf::from_str(word)); - name.append(&DNameSlice::root()).unwrap(); // XXX + let name = try!(Dname::from_str(word)); search.push(name) } self.search = search; diff --git a/src/resolv/mod.rs b/src/resolv/mod.rs index 00819eba..c906b7f2 100644 --- a/src/resolv/mod.rs +++ b/src/resolv/mod.rs @@ -213,12 +213,13 @@ //------------ Re-exports ---------------------------------------------------- pub use self::conf::ResolvConf; -pub use self::public::{Query, Resolver}; +//pub use self::public::{Query, Resolver}; //------------ Public Modules ------------------------------------------------ pub mod conf; +/* pub mod error; pub mod lookup; @@ -236,3 +237,4 @@ mod request; mod tcp; mod transport; mod udp; +*/