require docs (#116)
* partial docs * documentation for message.rs * more docs * all rdata documented * all client interfaces documented * get all Server public interfaces documented * add missed docs for Server * fix renamed method
This commit is contained in:
parent
2843420bd5
commit
e8cf649c4f
@ -5,9 +5,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
## 0.10.1 (unreleased)
|
||||
### Added
|
||||
- Added `From<IpAddr>` for Name (reverse DNS) #105
|
||||
- AppVeyor support #103
|
||||
|
||||
### Changed
|
||||
- Fixed TLS documentation, and add more elsewhere; fixes #102
|
||||
- Upgraded tokio-core and moved to tokio-io
|
||||
- *Important* Some `Server` types have been migrated to [RFC#344](https://github.com/aturon/rfcs/blob/conventions-galore/active/0000-conventions-galore.md#gettersetter-apis) style. `get_field()` -> `field()`; `field()` -> `set_field()`
|
||||
|
||||
## 0.10.0
|
||||
### Changed
|
||||
|
@ -1,4 +1,5 @@
|
||||
[![Build Status](https://travis-ci.org/bluejekyll/trust-dns.svg?branch=master)](https://travis-ci.org/bluejekyll/trust-dns)
|
||||
[![Build status](https://ci.appveyor.com/api/projects/status/tmlih8wdt7628vyl/branch/master?svg=true)](https://ci.appveyor.com/project/bluejekyll/trust-dns/branch/master)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/bluejekyll/trust-dns/badge.svg?branch=master)](https://coveralls.io/github/bluejekyll/trust-dns?branch=master)
|
||||
[![](http://meritbadge.herokuapp.com/trust-dns)](https://crates.io/crates/trust-dns)
|
||||
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE-MIT)
|
||||
|
@ -19,14 +19,14 @@ use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
// write out a version file to link against for version information
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let version = env::var("CARGO_PKG_VERSION").unwrap();
|
||||
let dest_path = Path::new(&out_dir).join("version.rs");
|
||||
let mut f = File::create(&dest_path).unwrap();
|
||||
// write out a version file to link against for version information
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let version = env::var("CARGO_PKG_VERSION").unwrap();
|
||||
let dest_path = Path::new(&out_dir).join("version.rs");
|
||||
let mut f = File::create(&dest_path).unwrap();
|
||||
|
||||
|
||||
f.write_all(b"pub fn version() -> &'static str {").unwrap();
|
||||
write!(f, " \"{}\" ", version).unwrap();
|
||||
f.write_all(b" }").unwrap();
|
||||
f.write_all(b"/// Get the version of this application as specified in the Cargo.toml\n").unwrap();
|
||||
f.write_all(b"pub fn version() -> &'static str {").unwrap();
|
||||
write!(f, " \"{}\" ", version).unwrap();
|
||||
f.write_all(b" }").unwrap();
|
||||
}
|
||||
|
@ -40,7 +40,10 @@ use op::Message;
|
||||
/// signer which can be optionally associated to the Client. This replaces the previous per-function
|
||||
/// parameter, and it will sign all update requests (this matches the `ClientFuture` API).
|
||||
pub trait Client<C: ClientHandle> {
|
||||
/// get a mutable reference to the tokio Core associated to the Client
|
||||
fn get_io_loop(&self) -> RefMut<Core>;
|
||||
|
||||
/// Get a mutable handle reference tot the Core assiated to the Client
|
||||
fn get_client_handle(&self) -> RefMut<C>;
|
||||
|
||||
/// A *classic* DNS query, i.e. does not perform and DNSSec operations
|
||||
|
@ -23,7 +23,11 @@ use client::ClientStreamHandle;
|
||||
|
||||
/// Trait for client connections
|
||||
pub trait ClientConnection: Sized {
|
||||
/// The associated DNS Message stream type.
|
||||
type MessageStream;
|
||||
|
||||
/// Return the inner Futures items
|
||||
///
|
||||
/// Consumes the connection and allows for future based operations afterward.
|
||||
fn unwrap(self) -> (Core, Box<Future<Item=Self::MessageStream, Error=io::Error>>, Box<ClientStreamHandle>);
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ pub type StreamHandle = UnboundedSender<Vec<u8>>;
|
||||
|
||||
/// Implementations of Sinks for sending DNS messages
|
||||
pub trait ClientStreamHandle {
|
||||
/// Sends a message to the Handle for delivery to the server.
|
||||
fn send(&mut self, buffer: Vec<u8>) -> io::Result<()>;
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
use futures::{Future, Poll};
|
||||
|
||||
use client::ClientHandle;
|
||||
use ::error::*;
|
||||
use error::*;
|
||||
use op::Message;
|
||||
|
||||
/// Can be used to reattempt a queries if they fail
|
||||
@ -24,6 +24,12 @@ pub struct RetryClientHandle<H: ClientHandle> {
|
||||
impl<H> RetryClientHandle<H>
|
||||
where H: ClientHandle
|
||||
{
|
||||
/// Creates a new Client handler for reattempting requests on failures.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `client` - handle to the client connection
|
||||
/// * `attempts` - number of attempts before failing
|
||||
pub fn new(client: H, attempts: usize) -> RetryClientHandle<H> {
|
||||
RetryClientHandle {
|
||||
client: client,
|
||||
@ -41,11 +47,11 @@ impl<H> ClientHandle for RetryClientHandle<H>
|
||||
let future = self.client.send(message.clone());
|
||||
|
||||
return Box::new(RetrySendFuture {
|
||||
message: message,
|
||||
client: self.client.clone(),
|
||||
future: future,
|
||||
remaining_attempts: self.attempts,
|
||||
});
|
||||
message: message,
|
||||
client: self.client.clone(),
|
||||
future: future,
|
||||
remaining_attempts: self.attempts,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,9 +93,9 @@ impl<H> Future for RetrySendFuture<H>
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::cell::Cell;
|
||||
use ::client::*;
|
||||
use ::error::*;
|
||||
use ::op::*;
|
||||
use client::*;
|
||||
use error::*;
|
||||
use op::*;
|
||||
use futures::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -125,7 +131,10 @@ mod test {
|
||||
},
|
||||
2);
|
||||
let test1 = Message::new();
|
||||
let result = client.send(test1).wait().ok().expect("should have succeeded");
|
||||
let result = client.send(test1)
|
||||
.wait()
|
||||
.ok()
|
||||
.expect("should have succeeded");
|
||||
assert_eq!(result.id(), 1); // this is checking the number of iterations the TestCient ran
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ use error::{DnsSecError, DnsSecErrorKind};
|
||||
|
||||
use rr::Name;
|
||||
|
||||
/// Errors while decoding DNS messages
|
||||
error_chain! {
|
||||
// The type defined for this error. These are the conventional
|
||||
// and recommended names, but they can be arbitrarily chosen.
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#![allow(missing_docs)]
|
||||
|
||||
//! All defined errors for Trust-DNS
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#![deny(missing_docs)]
|
||||
#![recursion_limit = "1024"]
|
||||
|
||||
//! Trust-DNS is intended to be a fully compliant domain name server and client library.
|
||||
@ -91,12 +92,19 @@ pub type MessageStream = Stream<Item = Message, Error = io::Error>;
|
||||
/// A sender to which a Message can be sent
|
||||
pub type MessageStreamHandle = UnboundedSender<Message>;
|
||||
|
||||
/// A buffering stream bound to a `SocketAddr`
|
||||
pub struct BufClientStreamHandle {
|
||||
name_server: SocketAddr,
|
||||
sender: BufStreamHandle,
|
||||
}
|
||||
|
||||
impl BufClientStreamHandle {
|
||||
/// Constructs a new Buffered Stream Handle, used for sending data to the DNS peer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name_server` - the address of the DNS server
|
||||
/// * `sender` - the handle being used to send data to the server
|
||||
pub fn new(name_server: SocketAddr, sender: BufStreamHandle) -> Self {
|
||||
BufClientStreamHandle {
|
||||
name_server: name_server,
|
||||
|
@ -1,28 +1,34 @@
|
||||
//! Logging configuration
|
||||
|
||||
use log;
|
||||
use log::{LogLevel, SetLoggerError, LogMetadata, LogRecord};
|
||||
use chrono::*;
|
||||
|
||||
/// The logging manager for the system
|
||||
#[allow(unused)]
|
||||
pub struct TrustDnsLogger {
|
||||
level: LogLevel,
|
||||
}
|
||||
|
||||
impl TrustDnsLogger {
|
||||
/// Configure a logger with the given log level
|
||||
pub fn new(level: LogLevel) -> TrustDnsLogger {
|
||||
TrustDnsLogger { level: level }
|
||||
}
|
||||
|
||||
/// Initializes the logger.
|
||||
pub fn init(self) -> Result<(), SetLoggerError> {
|
||||
let result = log::set_logger(|max_log_level| {
|
||||
max_log_level.set(self.level.to_log_level_filter());
|
||||
Box::new(self)
|
||||
});
|
||||
max_log_level.set(self.level.to_log_level_filter());
|
||||
Box::new(self)
|
||||
});
|
||||
|
||||
info!("logging initialized");
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Enables the logger with the given `LogLevel`
|
||||
pub fn enable_logging(log_level: LogLevel) {
|
||||
Self::new(log_level).init().is_ok();
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use rr::{DNSClass, Name, Record, RecordType, RData};
|
||||
use rr::rdata::OPT;
|
||||
use rr::rdata::opt::{EdnsCode, EdnsOption};
|
||||
|
||||
/// Edns implements the higher level concepts for working with Edns as it is used to create or be
|
||||
/// Edns implements the higher level concepts for working with extended dns as it is used to create or be
|
||||
/// created from OPT record data.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Edns {
|
||||
@ -38,6 +38,7 @@ pub struct Edns {
|
||||
}
|
||||
|
||||
impl Edns {
|
||||
/// Creates a new extended DNS object.
|
||||
pub fn new() -> Self {
|
||||
Edns {
|
||||
rcode_high: 0,
|
||||
@ -48,37 +49,57 @@ impl Edns {
|
||||
}
|
||||
}
|
||||
|
||||
/// The high order bytes for the response code in the DNS Message
|
||||
pub fn rcode_high(&self) -> u8 {
|
||||
self.rcode_high
|
||||
}
|
||||
|
||||
/// Returns the EDNS version
|
||||
pub fn version(&self) -> u8 {
|
||||
self.version
|
||||
}
|
||||
|
||||
/// Specifies that DNSSec is supported for this Client or Server
|
||||
pub fn dnssec_ok(&self) -> bool {
|
||||
self.dnssec_ok
|
||||
}
|
||||
|
||||
/// Maximum supported size of the DNS payload
|
||||
pub fn max_payload(&self) -> u16 {
|
||||
self.max_payload
|
||||
}
|
||||
|
||||
/// Returns the Option associated with the code
|
||||
pub fn option(&self, code: &EdnsCode) -> Option<&EdnsOption> {
|
||||
self.options.get(code)
|
||||
}
|
||||
|
||||
/// Returns the options portion of EDNS
|
||||
pub fn options(&self) -> &OPT {
|
||||
&self.options
|
||||
}
|
||||
|
||||
/// Set the high order bits for the result code.
|
||||
pub fn set_rcode_high(&mut self, rcode_high: u8) {
|
||||
self.rcode_high = rcode_high
|
||||
}
|
||||
|
||||
/// Set the EDNS version
|
||||
pub fn set_version(&mut self, version: u8) {
|
||||
self.version = version
|
||||
}
|
||||
|
||||
/// Set to true if DNSSec is supported
|
||||
pub fn set_dnssec_ok(&mut self, dnssec_ok: bool) {
|
||||
self.dnssec_ok = dnssec_ok
|
||||
}
|
||||
|
||||
/// Set the maximum payload which can be supported
|
||||
pub fn set_max_payload(&mut self, max_payload: u16) {
|
||||
self.max_payload = max_payload
|
||||
}
|
||||
|
||||
/// Set the specified EDNS option
|
||||
pub fn set_option(&mut self, option: EdnsOption) {
|
||||
self.options.insert(option);
|
||||
}
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
use std::convert::From;
|
||||
|
||||
use ::serialize::binary::*;
|
||||
use ::error::*;
|
||||
use serialize::binary::*;
|
||||
use error::*;
|
||||
use super::op_code::OpCode;
|
||||
use super::response_code::ResponseCode;
|
||||
|
||||
@ -73,9 +73,12 @@ pub struct Header {
|
||||
additional_count: u16,
|
||||
}
|
||||
|
||||
/// Message types are either Query (also Update) or Response
|
||||
#[derive(Debug, PartialEq, PartialOrd, Copy, Clone)]
|
||||
pub enum MessageType {
|
||||
/// Queries are Client requests, these are either Queries or Updates
|
||||
Query,
|
||||
/// Response message from the Server or upstream Resolver
|
||||
Response,
|
||||
}
|
||||
|
||||
@ -101,63 +104,93 @@ impl Header {
|
||||
}
|
||||
}
|
||||
|
||||
/// Length of the header, always 12 bytes
|
||||
#[inline(always)]
|
||||
pub fn len() -> usize {
|
||||
12 /* this is always 12 bytes */
|
||||
}
|
||||
|
||||
/// Sets the id of the message, for queries this shoudl be random.
|
||||
pub fn set_id(&mut self, id: u16) -> &mut Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the message type, Queries and Updates both use Query.
|
||||
pub fn set_message_type(&mut self, message_type: MessageType) -> &mut Self {
|
||||
self.message_type = message_type;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the operation code for the message
|
||||
pub fn set_op_code(&mut self, op_code: OpCode) -> &mut Self {
|
||||
self.op_code = op_code;
|
||||
self
|
||||
}
|
||||
|
||||
/// From the server is specifies that it is an authoritative reqponse.
|
||||
pub fn set_authoritative(&mut self, authoritative: bool) -> &mut Self {
|
||||
self.authoritative = authoritative;
|
||||
self
|
||||
}
|
||||
|
||||
/// Specifies that the records were too large for the payload.
|
||||
///
|
||||
/// See EDNS or TCP for resolutions to truncation.
|
||||
pub fn set_truncated(&mut self, truncated: bool) -> &mut Self {
|
||||
self.truncation = truncated;
|
||||
self
|
||||
}
|
||||
|
||||
/// Specify that the resolver should recursiviley request data from upstream DNS nodes
|
||||
pub fn set_recursion_desired(&mut self, recursion_desired: bool) -> &mut Self {
|
||||
self.recursion_desired = recursion_desired;
|
||||
self
|
||||
}
|
||||
|
||||
/// Specifies that recursion is available from this or the remote resolver
|
||||
pub fn set_recursion_available(&mut self, recursion_available: bool) -> &mut Self {
|
||||
self.recursion_available = recursion_available;
|
||||
self
|
||||
}
|
||||
|
||||
/// Specifies that the data is authnetic, i.e. the resolver believes all data to be valid through DNSSec
|
||||
pub fn set_authentic_data(&mut self, authentic_data: bool) -> &mut Self {
|
||||
self.authentic_data = authentic_data;
|
||||
self
|
||||
}
|
||||
|
||||
/// Used during recursive resolution to specified if a resolver should or should not validate DNSSec signatures
|
||||
pub fn set_checking_disabled(&mut self, checking_disabled: bool) -> &mut Self {
|
||||
self.checking_disabled = checking_disabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// The low responsed code (original response codes before EDNS extensions)
|
||||
pub fn set_response_code(&mut self, response_code: ResponseCode) -> &mut Self {
|
||||
self.response_code = response_code.low();
|
||||
self
|
||||
}
|
||||
|
||||
/// Number or query records in the message
|
||||
pub fn set_query_count(&mut self, query_count: u16) -> &mut Self {
|
||||
self.query_count = query_count;
|
||||
self
|
||||
}
|
||||
|
||||
/// Number of answer records in the message
|
||||
pub fn set_answer_count(&mut self, answer_count: u16) -> &mut Self {
|
||||
self.answer_count = answer_count;
|
||||
self
|
||||
}
|
||||
|
||||
/// Number of name server records in the message
|
||||
pub fn set_name_server_count(&mut self, name_server_count: u16) -> &mut Self {
|
||||
self.name_server_count = name_server_count;
|
||||
self
|
||||
}
|
||||
|
||||
/// Number of additional records in the message
|
||||
pub fn set_additional_count(&mut self, additional_count: u16) -> &mut Self {
|
||||
self.additional_count = additional_count;
|
||||
self
|
||||
@ -383,21 +416,21 @@ impl BinSerializable<Header> for Header {
|
||||
// TODO: question, should this use the builder pattern instead? might be cleaner code, but
|
||||
// this guarantees that the Header is fully instantiated with all values...
|
||||
Ok(Header {
|
||||
id: id,
|
||||
message_type: message_type,
|
||||
op_code: op_code,
|
||||
authoritative: authoritative,
|
||||
truncation: truncation,
|
||||
recursion_desired: recursion_desired,
|
||||
recursion_available: recursion_available,
|
||||
authentic_data: authentic_data,
|
||||
checking_disabled: checking_disabled,
|
||||
response_code: response_code,
|
||||
query_count: query_count,
|
||||
answer_count: answer_count,
|
||||
name_server_count: name_server_count,
|
||||
additional_count: additional_count,
|
||||
})
|
||||
id: id,
|
||||
message_type: message_type,
|
||||
op_code: op_code,
|
||||
authoritative: authoritative,
|
||||
truncation: truncation,
|
||||
recursion_desired: recursion_desired,
|
||||
recursion_available: recursion_available,
|
||||
authentic_data: authentic_data,
|
||||
checking_disabled: checking_disabled,
|
||||
response_code: response_code,
|
||||
query_count: query_count,
|
||||
answer_count: answer_count,
|
||||
name_server_count: name_server_count,
|
||||
additional_count: additional_count,
|
||||
})
|
||||
}
|
||||
|
||||
fn emit(&self, encoder: &mut BinEncoder) -> EncodeResult {
|
||||
|
@ -19,7 +19,7 @@
|
||||
use std::fmt::Debug;
|
||||
use std::mem;
|
||||
|
||||
use ::error::*;
|
||||
use error::*;
|
||||
use rr::{Record, RecordType};
|
||||
#[cfg(feature = "openssl")]
|
||||
use rr::{DNSClass, Name, RData};
|
||||
@ -83,6 +83,7 @@ pub struct Message {
|
||||
}
|
||||
|
||||
impl Message {
|
||||
/// Returns a new "empty" Message
|
||||
pub fn new() -> Self {
|
||||
Message {
|
||||
header: Header::new(),
|
||||
@ -95,6 +96,13 @@ impl Message {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a Message constructed with error details to return to a client
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `id` - message id should match the request message id
|
||||
/// * `op_code` - operation of the request
|
||||
/// * `response_code` - the error code for the response
|
||||
pub fn error_msg(id: u16, op_code: OpCode, response_code: ResponseCode) -> Message {
|
||||
let mut message: Message = Message::new();
|
||||
message.set_message_type(MessageType::Response);
|
||||
@ -105,6 +113,7 @@ impl Message {
|
||||
message
|
||||
}
|
||||
|
||||
/// Truncates a Message, this blindly removes all response fields and sets trucation to `true`
|
||||
pub fn truncate(&self) -> Self {
|
||||
let mut truncated: Message = Message::new();
|
||||
truncated.set_id(self.id());
|
||||
@ -123,54 +132,73 @@ impl Message {
|
||||
truncated
|
||||
}
|
||||
|
||||
/// see `Header::set_id`
|
||||
pub fn set_id(&mut self, id: u16) -> &mut Self {
|
||||
self.header.set_id(id);
|
||||
self
|
||||
}
|
||||
|
||||
/// see `Header::set_message_type`
|
||||
pub fn set_message_type(&mut self, message_type: MessageType) -> &mut Self {
|
||||
self.header.set_message_type(message_type);
|
||||
self
|
||||
}
|
||||
|
||||
/// see `Header::set_op_code`
|
||||
pub fn set_op_code(&mut self, op_code: OpCode) -> &mut Self {
|
||||
self.header.set_op_code(op_code);
|
||||
self
|
||||
}
|
||||
|
||||
/// see `Header::set_authoritative`
|
||||
pub fn set_authoritative(&mut self, authoritative: bool) -> &mut Self {
|
||||
self.header.set_authoritative(authoritative);
|
||||
self
|
||||
}
|
||||
|
||||
/// see `Header::set_truncated`
|
||||
pub fn set_truncated(&mut self, truncated: bool) -> &mut Self {
|
||||
self.header.set_truncated(truncated);
|
||||
self
|
||||
}
|
||||
|
||||
/// see `Header::set_recursion_desired`
|
||||
pub fn set_recursion_desired(&mut self, recursion_desired: bool) -> &mut Self {
|
||||
self.header.set_recursion_desired(recursion_desired);
|
||||
self
|
||||
}
|
||||
|
||||
/// see `Header::set_recursion_available`
|
||||
pub fn set_recursion_available(&mut self, recursion_available: bool) -> &mut Self {
|
||||
self.header.set_recursion_available(recursion_available);
|
||||
self
|
||||
}
|
||||
|
||||
/// see `Header::set_authentic_data`
|
||||
pub fn set_authentic_data(&mut self, authentic_data: bool) -> &mut Self {
|
||||
self.header.set_authentic_data(authentic_data);
|
||||
self
|
||||
}
|
||||
|
||||
/// see `Header::set_checking_disabled`
|
||||
pub fn set_checking_disabled(&mut self, checking_disabled: bool) -> &mut Self {
|
||||
self.header.set_checking_disabled(checking_disabled);
|
||||
self
|
||||
}
|
||||
|
||||
/// see `Header::set_response_code`
|
||||
pub fn set_response_code(&mut self, response_code: ResponseCode) -> &mut Self {
|
||||
self.header.set_response_code(response_code);
|
||||
self
|
||||
}
|
||||
|
||||
/// TODO: given that only a single query is ever accepted by almost all DNS servers,
|
||||
/// it's unclear how it could be useful to have more than one here? change this to set
|
||||
/// a single query?
|
||||
/// Add a query to the Message, either the query response from the server, or the request Query.
|
||||
pub fn add_query(&mut self, query: Query) -> &mut Self {
|
||||
self.queries.push(query);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a slice of Queries to the message
|
||||
#[deprecated = "will be removed post 0.9.x"]
|
||||
pub fn add_all_queries(&mut self, queries: &[Query]) -> &mut Self {
|
||||
for q in queries {
|
||||
@ -179,6 +207,8 @@ impl Message {
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds an iterator over a set of Queries to be added to the message
|
||||
pub fn add_queries<Q, I>(&mut self, queries: Q) -> &mut Self
|
||||
where Q: IntoIterator<Item = Query, IntoIter = I>,
|
||||
I: Iterator<Item = Query>
|
||||
@ -190,10 +220,13 @@ impl Message {
|
||||
self
|
||||
}
|
||||
|
||||
/// Add an answer to the Message
|
||||
pub fn add_answer(&mut self, record: Record) -> &mut Self {
|
||||
self.answers.push(record);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add an entire set of Answers
|
||||
#[deprecated = "will be removed post 0.9.x"]
|
||||
pub fn add_all_answers(&mut self, vector: &[&Record]) -> &mut Self {
|
||||
for &r in vector {
|
||||
@ -203,6 +236,8 @@ impl Message {
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Add all the records from the iterator to the answers section of the Message
|
||||
pub fn add_answers<R, I>(&mut self, records: R) -> &mut Self
|
||||
where R: IntoIterator<Item = Record, IntoIter = I>,
|
||||
I: Iterator<Item = Record>
|
||||
@ -224,10 +259,13 @@ impl Message {
|
||||
self.answers = records;
|
||||
}
|
||||
|
||||
/// Add a name server record to the Message
|
||||
pub fn add_name_server(&mut self, record: Record) -> &mut Self {
|
||||
self.name_servers.push(record);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a set of name server records to the message
|
||||
#[deprecated = "will be removed post 0.9.x"]
|
||||
pub fn add_all_name_servers(&mut self, vector: &[&Record]) -> &mut Self {
|
||||
for &r in vector {
|
||||
@ -237,6 +275,8 @@ impl Message {
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Add all the records in the Iterator to the name server section of the message
|
||||
pub fn add_name_servers<R, I>(&mut self, records: R) -> &mut Self
|
||||
where R: IntoIterator<Item = Record, IntoIter = I>,
|
||||
I: Iterator<Item = Record>
|
||||
@ -258,6 +298,7 @@ impl Message {
|
||||
self.name_servers = records;
|
||||
}
|
||||
|
||||
/// A an addtional Record to the message
|
||||
pub fn add_additional(&mut self, record: Record) -> &mut Self {
|
||||
self.additionals.push(record);
|
||||
self
|
||||
@ -273,11 +314,15 @@ impl Message {
|
||||
self.additionals = records;
|
||||
}
|
||||
|
||||
/// Add the EDNS section the the Message
|
||||
pub fn set_edns(&mut self, edns: Edns) -> &mut Self {
|
||||
self.edns = Some(edns);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a SIG0 record, i.e. sign this message
|
||||
///
|
||||
/// This must be don't only after all records have been associated. Generally this will be handled by the client and not need to be used directly
|
||||
pub fn add_sig0(&mut self, record: Record) -> &mut Self {
|
||||
assert_eq!(RecordType::SIG, record.rr_type());
|
||||
self.sig0.push(record);
|
||||
@ -352,6 +397,7 @@ impl Message {
|
||||
&self.answers
|
||||
}
|
||||
|
||||
/// Removes all the answers from the Message
|
||||
pub fn take_answers(&mut self) -> Vec<Record> {
|
||||
mem::replace(&mut self.answers, vec![])
|
||||
}
|
||||
@ -365,6 +411,7 @@ impl Message {
|
||||
&self.name_servers
|
||||
}
|
||||
|
||||
/// Remove the name servers from the Message
|
||||
pub fn take_name_servers(&mut self) -> Vec<Record> {
|
||||
mem::replace(&mut self.name_servers, vec![])
|
||||
}
|
||||
@ -377,6 +424,7 @@ impl Message {
|
||||
&self.additionals
|
||||
}
|
||||
|
||||
/// Remove the additional Records from the Message
|
||||
pub fn take_additionals(&mut self) -> Vec<Record> {
|
||||
mem::replace(&mut self.additionals, vec![])
|
||||
}
|
||||
@ -480,10 +528,11 @@ impl Message {
|
||||
additional_count += self.sig0.len()
|
||||
};
|
||||
|
||||
self.header.clone(self.queries.len() as u16,
|
||||
self.answers.len() as u16,
|
||||
self.name_servers.len() as u16,
|
||||
additional_count as u16)
|
||||
self.header
|
||||
.clone(self.queries.len() as u16,
|
||||
self.answers.len() as u16,
|
||||
self.name_servers.len() as u16,
|
||||
additional_count as u16)
|
||||
}
|
||||
|
||||
fn read_records(decoder: &mut BinDecoder,
|
||||
@ -502,7 +551,7 @@ impl Message {
|
||||
if !is_additional {
|
||||
if saw_sig0 {
|
||||
return Err(DecodeErrorKind::Message("sig0 must be final resource record")
|
||||
.into());
|
||||
.into());
|
||||
} // SIG0 must be last
|
||||
records.push(record)
|
||||
} else {
|
||||
@ -515,12 +564,12 @@ impl Message {
|
||||
if saw_sig0 {
|
||||
return Err(DecodeErrorKind::Message("sig0 must be final resource \
|
||||
record")
|
||||
.into());
|
||||
.into());
|
||||
} // SIG0 must be last
|
||||
if edns.is_some() {
|
||||
return Err(DecodeErrorKind::Message("more than one edns record \
|
||||
present")
|
||||
.into());
|
||||
.into());
|
||||
}
|
||||
edns = Some((&record).into());
|
||||
}
|
||||
@ -528,7 +577,7 @@ impl Message {
|
||||
if saw_sig0 {
|
||||
return Err(DecodeErrorKind::Message("sig0 must be final resource \
|
||||
record")
|
||||
.into());
|
||||
.into());
|
||||
} // SIG0 must be last
|
||||
records.push(record);
|
||||
}
|
||||
@ -546,11 +595,13 @@ impl Message {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decodes a message from the buffer.
|
||||
pub fn from_vec(buffer: &[u8]) -> DecodeResult<Message> {
|
||||
let mut decoder = BinDecoder::new(buffer);
|
||||
Message::read(&mut decoder)
|
||||
}
|
||||
|
||||
/// Encodes the Message into a buffer
|
||||
pub fn to_vec(&self) -> Result<Vec<u8>, EncodeError> {
|
||||
let mut buffer = Vec::with_capacity(512);
|
||||
{
|
||||
@ -561,7 +612,9 @@ impl Message {
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
// TODO: where's the 'right' spot for this function
|
||||
/// Sign the message, i.e. add a SIG0 record to this Message.
|
||||
///
|
||||
/// Subsequent to calling this, the Message should not change.
|
||||
#[cfg(feature = "openssl")]
|
||||
pub fn sign(&mut self, signer: &Signer, inception_time: u32) -> DnsSecResult<()> {
|
||||
debug!("signing message: {:?}", self);
|
||||
@ -588,24 +641,24 @@ impl Message {
|
||||
|
||||
sig0.set_rr_type(RecordType::SIG);
|
||||
sig0.set_rdata(
|
||||
RData::SIG(SIG::new(
|
||||
// type covered in SIG(0) is 0 which is what makes this SIG0 vs a standard SIG
|
||||
RecordType::NULL,
|
||||
signer.algorithm(),
|
||||
num_labels,
|
||||
// see above, original_ttl is meaningless, The TTL fields SHOULD be zero
|
||||
0,
|
||||
// recommended time is +5 minutes from now, to prevent timing attacks, 2 is probably good
|
||||
expiration_time,
|
||||
// current time, this should be UTC
|
||||
// unsigned numbers of seconds since the start of 1 January 1970, GMT
|
||||
inception_time,
|
||||
key_tag,
|
||||
// can probably get rid of this clone if the owndership is correct
|
||||
signer.signer_name().clone(),
|
||||
signature,
|
||||
)
|
||||
));
|
||||
RData::SIG(SIG::new(
|
||||
// type covered in SIG(0) is 0 which is what makes this SIG0 vs a standard SIG
|
||||
RecordType::NULL,
|
||||
signer.algorithm(),
|
||||
num_labels,
|
||||
// see above, original_ttl is meaningless, The TTL fields SHOULD be zero
|
||||
0,
|
||||
// recommended time is +5 minutes from now, to prevent timing attacks, 2 is probably good
|
||||
expiration_time,
|
||||
// current time, this should be UTC
|
||||
// unsigned numbers of seconds since the start of 1 January 1970, GMT
|
||||
inception_time,
|
||||
key_tag,
|
||||
// can probably get rid of this clone if the owndership is correct
|
||||
signer.signer_name().clone(),
|
||||
signature,
|
||||
)
|
||||
));
|
||||
|
||||
debug!("sig0: {:?}", sig0);
|
||||
|
||||
@ -613,34 +666,63 @@ impl Message {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Always returns an error; enable OpenSSL for signing support
|
||||
#[cfg(not(feature = "openssl"))]
|
||||
pub fn sign(&mut self, _: &Signer, _: u32) -> DnsSecResult<()> {
|
||||
Err(DnsSecErrorKind::Message("openssl feature not enabled").into())
|
||||
}
|
||||
}
|
||||
|
||||
/// to reduce errors in using the Message struct as an Update, this will do the call throughs
|
||||
/// To reduce errors in using the Message struct as an Update, this will do the call throughs
|
||||
/// to properly do that.
|
||||
///
|
||||
/// Generally rather than constructin this by hand, see the update methods on `Client`
|
||||
pub trait UpdateMessage: Debug {
|
||||
/// see `Header::id`
|
||||
fn id(&self) -> u16;
|
||||
|
||||
/// Adds the zone section, i.e. name.example.com would be example.com
|
||||
fn add_zone(&mut self, query: Query);
|
||||
|
||||
/// Add the pre-requisite records
|
||||
///
|
||||
/// These must exist, or not, for the Update request to go through.
|
||||
fn add_pre_requisite(&mut self, record: Record);
|
||||
|
||||
/// Add all pre-requisites to the UpdateMessage
|
||||
#[deprecated = "will be removed post 0.9.x"]
|
||||
fn add_all_pre_requisites(&mut self, vector: &[&Record]);
|
||||
|
||||
/// Add all the Records from the Iterator to the pre-reqisites section
|
||||
fn add_pre_requisites<R, I>(&mut self, records: R)
|
||||
where R: IntoIterator<Item = Record, IntoIter = I>,
|
||||
I: Iterator<Item = Record>;
|
||||
|
||||
/// Add the Record to be updated
|
||||
fn add_update(&mut self, record: Record);
|
||||
|
||||
/// Add the set of Records to be updated
|
||||
#[deprecated = "will be removed post 0.9.x"]
|
||||
fn add_all_updates(&mut self, vector: &[&Record]);
|
||||
|
||||
/// Add the Records from the Iterator to the updates section
|
||||
fn add_updates<R, I>(&mut self, records: R)
|
||||
where R: IntoIterator<Item = Record, IntoIter = I>,
|
||||
I: Iterator<Item = Record>;
|
||||
|
||||
/// Add Records to the additional Section of hte UpdateMessage
|
||||
fn add_additional(&mut self, record: Record);
|
||||
|
||||
/// Returns the Zones to be updated, generally should only be one.
|
||||
fn zones(&self) -> &[Query];
|
||||
|
||||
/// Returns the pre-requisites
|
||||
fn prerequisites(&self) -> &[Record];
|
||||
|
||||
/// Returns the records to be updated
|
||||
fn updates(&self) -> &[Record];
|
||||
|
||||
/// Returns the additonal records
|
||||
fn additionals(&self) -> &[Record];
|
||||
|
||||
/// This is used to authenticate update messages.
|
||||
@ -648,6 +730,7 @@ pub trait UpdateMessage: Debug {
|
||||
/// see `Message::sig0()` for more information.
|
||||
fn sig0(&self) -> &[Record];
|
||||
|
||||
/// Signs the UpdateMessage, used to validate the authenticity and authorization of UpdateMessage
|
||||
fn sign(&mut self, signer: &Signer, inception_time: u32) -> DnsSecResult<()>;
|
||||
}
|
||||
|
||||
@ -736,14 +819,14 @@ impl BinSerializable<Message> for Message {
|
||||
let (additionals, edns, sig0) = try!(Self::read_records(decoder, additional_count, true));
|
||||
|
||||
Ok(Message {
|
||||
header: header,
|
||||
queries: queries,
|
||||
answers: answers,
|
||||
name_servers: name_servers,
|
||||
additionals: additionals,
|
||||
sig0: sig0,
|
||||
edns: edns,
|
||||
})
|
||||
header: header,
|
||||
queries: queries,
|
||||
answers: answers,
|
||||
name_servers: name_servers,
|
||||
additionals: additionals,
|
||||
sig0: sig0,
|
||||
edns: edns,
|
||||
})
|
||||
}
|
||||
|
||||
fn emit(&self, encoder: &mut BinEncoder) -> EncodeResult {
|
||||
@ -780,7 +863,8 @@ impl BinSerializable<Message> for Message {
|
||||
#[test]
|
||||
fn test_emit_and_read_header() {
|
||||
let mut message = Message::new();
|
||||
message.set_id(10)
|
||||
message
|
||||
.set_id(10)
|
||||
.set_message_type(MessageType::Response)
|
||||
.set_op_code(OpCode::Update)
|
||||
.set_authoritative(true)
|
||||
@ -795,7 +879,8 @@ fn test_emit_and_read_header() {
|
||||
#[test]
|
||||
fn test_emit_and_read_query() {
|
||||
let mut message = Message::new();
|
||||
message.set_id(10)
|
||||
message
|
||||
.set_id(10)
|
||||
.set_message_type(MessageType::Response)
|
||||
.set_op_code(OpCode::Update)
|
||||
.set_authoritative(true)
|
||||
@ -812,7 +897,8 @@ fn test_emit_and_read_query() {
|
||||
#[test]
|
||||
fn test_emit_and_read_records() {
|
||||
let mut message = Message::new();
|
||||
message.set_id(10)
|
||||
message
|
||||
.set_id(10)
|
||||
.set_message_type(MessageType::Response)
|
||||
.set_op_code(OpCode::Update)
|
||||
.set_authoritative(true)
|
||||
|
@ -89,6 +89,7 @@ impl From<OpCode> for u8 {
|
||||
/// assert_eq!(OpCode::Query, var);
|
||||
/// ```
|
||||
impl OpCode {
|
||||
/// Decodes the binary value of the OpCode
|
||||
pub fn from_u8(value: u8) -> DecodeResult<Self> {
|
||||
match value {
|
||||
0 => Ok(OpCode::Query),
|
||||
|
@ -68,10 +68,14 @@ impl Query {
|
||||
self.name = name;
|
||||
self
|
||||
}
|
||||
|
||||
/// Specify the RecordType being queried
|
||||
pub fn set_query_type(&mut self, query_type: RecordType) -> &mut Self {
|
||||
self.query_type = query_type;
|
||||
self
|
||||
}
|
||||
|
||||
/// Specify÷ the DNS class of the Query, almost always IN
|
||||
pub fn set_query_class(&mut self, query_class: DNSClass) -> &mut Self {
|
||||
self.query_class = query_class;
|
||||
self
|
||||
|
@ -1,3 +1,12 @@
|
||||
// Copyright 2015-2017 Benjamin Fry <benjaminfry@me.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
//! Request Handler for incoming requests
|
||||
|
||||
use op::Message;
|
||||
|
||||
/// Trait for handling incoming requests, and providing a message response.
|
||||
|
@ -117,7 +117,7 @@ pub enum ResponseCode {
|
||||
BADTRUNC,
|
||||
|
||||
/// Bad/missing server cookie [draft-ietf-dnsop-cookies](https://tools.ietf.org/html/draft-ietf-dnsop-cookies-10)
|
||||
BADCOOKIE,
|
||||
BADCOOKIE,
|
||||
// 24-3840 Unassigned
|
||||
// 3841-4095 Reserved for Private Use [RFC6895]
|
||||
// 4096-65534 Unassigned
|
||||
@ -135,10 +135,12 @@ impl ResponseCode {
|
||||
(u16::from(*self) & 0x0FF0) >> 4;
|
||||
}
|
||||
|
||||
/// Combines the EDNS high and low from the Header to produce the Extended ResponseCode
|
||||
pub fn from(high: u8, low: u8) -> ResponseCode {
|
||||
(((high as u16) << 4) | ((low as u16) & 0x000F)).into()
|
||||
}
|
||||
|
||||
/// Transforms the response code into the human message
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match *self {
|
||||
ResponseCode::NoError => "No Error",
|
||||
@ -165,20 +167,18 @@ impl ResponseCode {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from ResponseCode to u16
|
||||
*
|
||||
* ```
|
||||
* use std::convert::From;
|
||||
* use trust_dns::op::response_code::ResponseCode;
|
||||
*
|
||||
* let var: ResponseCode = From::from(0);
|
||||
* assert_eq!(ResponseCode::NoError, var);
|
||||
*
|
||||
* let var: ResponseCode = 0.into();
|
||||
* assert_eq!(ResponseCode::NoError, var);
|
||||
* ```
|
||||
*/
|
||||
/// Convert from ResponseCode to u16
|
||||
///
|
||||
/// ```
|
||||
/// use std::convert::From;
|
||||
/// use trust_dns::op::response_code::ResponseCode;
|
||||
///
|
||||
/// let var: ResponseCode = From::from(0);
|
||||
/// assert_eq!(ResponseCode::NoError, var);
|
||||
///
|
||||
/// let var: ResponseCode = 0.into();
|
||||
/// assert_eq!(ResponseCode::NoError, var);
|
||||
/// ```
|
||||
impl From<ResponseCode> for u16 {
|
||||
fn from(rt: ResponseCode) -> Self {
|
||||
match rt {
|
||||
@ -207,20 +207,18 @@ impl From<ResponseCode> for u16 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from u16 to ResponseCode
|
||||
*
|
||||
* ```
|
||||
* use std::convert::From;
|
||||
* use trust_dns::op::response_code::ResponseCode;
|
||||
*
|
||||
* let var: u16 = From::from(ResponseCode::NoError);
|
||||
* assert_eq!(0, var);
|
||||
*
|
||||
* let var: u16 = ResponseCode::NoError.into();
|
||||
* assert_eq!(0, var);
|
||||
* ```
|
||||
*/
|
||||
/// Convert from u16 to ResponseCode
|
||||
///
|
||||
/// ```
|
||||
/// use std::convert::From;
|
||||
/// use trust_dns::op::response_code::ResponseCode;
|
||||
///
|
||||
/// let var: u16 = From::from(ResponseCode::NoError);
|
||||
/// assert_eq!(0, var);
|
||||
///
|
||||
/// let var: u16 = ResponseCode::NoError.into();
|
||||
/// assert_eq!(0, var);
|
||||
/// ```
|
||||
impl From<u16> for ResponseCode {
|
||||
fn from(value: u16) -> Self {
|
||||
match value {
|
||||
|
@ -19,18 +19,25 @@
|
||||
use std::convert::From;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use ::serialize::binary::*;
|
||||
use ::error::*;
|
||||
use serialize::binary::*;
|
||||
use error::*;
|
||||
|
||||
/// The DNS Record class
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub enum DNSClass {
|
||||
IN, // 1 RFC 1035 Internet (IN)
|
||||
CH, // 3 Chaos (CH)
|
||||
HS, // 4 Hesiod (HS)
|
||||
NONE, // 254 QCLASS NONE
|
||||
ANY, // 255 QCLASS * (ANY)
|
||||
OPT(u16), // Special class for OPT Version, it was overloaded for EDNS - RFC 6891
|
||||
/// Internet
|
||||
IN,
|
||||
/// Chaos
|
||||
CH,
|
||||
/// Hesiod
|
||||
HS,
|
||||
/// QCLASS NONE
|
||||
NONE,
|
||||
/// QCLASS * (ANY)
|
||||
ANY,
|
||||
/// Special class for OPT Version, it was overloaded for EDNS - RFC 6891
|
||||
OPT(u16),
|
||||
}
|
||||
|
||||
impl DNSClass {
|
||||
@ -73,6 +80,7 @@ impl DNSClass {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the OPT version from value
|
||||
pub fn for_opt(value: u16) -> Self {
|
||||
DNSClass::OPT(value)
|
||||
}
|
||||
@ -147,9 +155,16 @@ impl Ord for DNSClass {
|
||||
|
||||
#[test]
|
||||
fn test_order() {
|
||||
let ordered = vec![DNSClass::IN, DNSClass::CH, DNSClass::HS, DNSClass::NONE, DNSClass::ANY];
|
||||
let mut unordered =
|
||||
vec![DNSClass::NONE, DNSClass::HS, DNSClass::CH, DNSClass::IN, DNSClass::ANY];
|
||||
let ordered = vec![DNSClass::IN,
|
||||
DNSClass::CH,
|
||||
DNSClass::HS,
|
||||
DNSClass::NONE,
|
||||
DNSClass::ANY];
|
||||
let mut unordered = vec![DNSClass::NONE,
|
||||
DNSClass::HS,
|
||||
DNSClass::CH,
|
||||
DNSClass::IN,
|
||||
DNSClass::ANY];
|
||||
|
||||
unordered.sort();
|
||||
|
||||
|
@ -15,8 +15,8 @@
|
||||
*/
|
||||
use std::str::FromStr;
|
||||
|
||||
use ::serialize::binary::*;
|
||||
use ::error::*;
|
||||
use serialize::binary::*;
|
||||
use error::*;
|
||||
|
||||
/// DNSSec signing and validation algorithms.
|
||||
///
|
||||
@ -105,7 +105,9 @@ pub enum Algorithm {
|
||||
RSASHA1,
|
||||
/// DO NOT USE, SHA1 is a compromised hashing function, it is here for backward compatability
|
||||
RSASHA1NSEC3SHA1,
|
||||
/// RSA public key with SHA256 hash
|
||||
RSASHA256,
|
||||
/// RSA public key with SHA512 hash
|
||||
RSASHA512,
|
||||
/// [rfc6605](https://tools.ietf.org/html/rfc6605)
|
||||
ECDSAP256SHA256,
|
||||
@ -143,6 +145,7 @@ impl Algorithm {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert to string form
|
||||
pub fn to_str(&self) -> &'static str {
|
||||
match *self {
|
||||
Algorithm::RSASHA1 => "RSASHA1",
|
||||
@ -231,14 +234,16 @@ fn test_order() {
|
||||
|
||||
algorithms.sort();
|
||||
|
||||
for (got, expect) in algorithms.iter().zip([Algorithm::RSASHA1,
|
||||
Algorithm::RSASHA1NSEC3SHA1,
|
||||
Algorithm::RSASHA256,
|
||||
Algorithm::RSASHA512,
|
||||
Algorithm::ECDSAP256SHA256,
|
||||
Algorithm::ECDSAP384SHA384,
|
||||
Algorithm::ED25519]
|
||||
.iter()) {
|
||||
for (got, expect) in algorithms
|
||||
.iter()
|
||||
.zip([Algorithm::RSASHA1,
|
||||
Algorithm::RSASHA1NSEC3SHA1,
|
||||
Algorithm::RSASHA256,
|
||||
Algorithm::RSASHA512,
|
||||
Algorithm::ECDSAP256SHA256,
|
||||
Algorithm::ECDSAP384SHA384,
|
||||
Algorithm::ED25519]
|
||||
.iter()) {
|
||||
assert_eq!(got, expect);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use openssl::hash;
|
||||
use openssl::hash::MessageDigest;
|
||||
|
||||
use rr::dnssec::Algorithm;
|
||||
use ::error::*;
|
||||
use error::*;
|
||||
|
||||
/// This is the digest format for the
|
||||
///
|
||||
@ -34,12 +34,17 @@ use ::error::*;
|
||||
/// ```
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||
pub enum DigestType {
|
||||
SHA1, // [RFC3658]
|
||||
/// [RFC3658]
|
||||
SHA1,
|
||||
/// [RFC4509]
|
||||
SHA256, // [RFC4509]
|
||||
// GOSTR34_11_94, // [RFC5933]
|
||||
SHA384, // [RFC6605]
|
||||
/// [RFC6605]
|
||||
SHA384,
|
||||
/// Undefined
|
||||
SHA512,
|
||||
ED25519, // this is a passthrough digest as ED25519 is self-packaged
|
||||
/// This is a passthrough digest as ED25519 is self-packaged
|
||||
ED25519,
|
||||
}
|
||||
|
||||
impl DigestType {
|
||||
@ -55,6 +60,7 @@ impl DigestType {
|
||||
}
|
||||
}
|
||||
|
||||
/// The OpenSSL counterpart for the digest
|
||||
#[cfg(feature = "openssl")]
|
||||
pub fn to_openssl_digest(&self) -> DnsSecResult<MessageDigest> {
|
||||
match *self {
|
||||
@ -64,16 +70,18 @@ impl DigestType {
|
||||
DigestType::SHA512 => Ok(MessageDigest::sha512()),
|
||||
_ => {
|
||||
Err(DnsSecErrorKind::Msg(format!("digest not supported by openssl: {:?}", self))
|
||||
.into())
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Hash the data
|
||||
#[cfg(feature = "openssl")]
|
||||
pub fn hash(&self, data: &[u8]) -> DnsSecResult<Vec<u8>> {
|
||||
hash::hash(try!(self.to_openssl_digest()), data).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// This will always error, enable openssl feature at compile time
|
||||
#[cfg(not(feature = "openssl"))]
|
||||
pub fn hash(&self, _: &[u8]) -> DnsSecResult<Vec<u8>> {
|
||||
Err(DnsSecErrorKind::Message("openssl feature not enabled").into())
|
||||
|
@ -9,10 +9,14 @@ use ::error::*;
|
||||
use rr::dnssec::Algorithm;
|
||||
use rr::dnssec::KeyPair;
|
||||
|
||||
/// The format of the binary key
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum KeyFormat {
|
||||
/// A der encoded key
|
||||
Der,
|
||||
/// A pem encoded key, the default of OpenSSL
|
||||
Pem,
|
||||
/// Raw bytes unformatted
|
||||
Raw,
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ use ring::signature::{Ed25519KeyPair, Ed25519KeyPairBytes, EdDSAParameters, Veri
|
||||
#[cfg(feature = "ring")]
|
||||
use untrusted::Input;
|
||||
|
||||
use ::error::*;
|
||||
use error::*;
|
||||
use rr::Name;
|
||||
use rr::dnssec::{Algorithm, DigestType};
|
||||
use rr::rdata::{DNSKEY, DS};
|
||||
@ -36,10 +36,13 @@ use rr::rdata::{DNSKEY, DS};
|
||||
/// differing features, some key types may not be available. The `openssl` will enable RSA and EC
|
||||
/// (P256 and P384). `ring` enables ED25519.
|
||||
pub enum KeyPair {
|
||||
/// RSA keypair, supported by OpenSSL
|
||||
#[cfg(feature = "openssl")]
|
||||
RSA(PKey),
|
||||
/// Ellyptic curve keypair, supported by OpenSSL
|
||||
#[cfg(feature = "openssl")]
|
||||
EC(PKey),
|
||||
/// ED25519 ecryption and hash defined keypair
|
||||
#[cfg(feature = "ring")]
|
||||
ED25519(Ed25519KeyPairBytes),
|
||||
}
|
||||
@ -49,9 +52,12 @@ impl KeyPair {
|
||||
/// Creates an RSA type keypair.
|
||||
#[cfg(feature = "openssl")]
|
||||
pub fn from_rsa(rsa: OpenSslRsa) -> DnsSecResult<Self> {
|
||||
PKey::from_rsa(rsa).map(|pkey| KeyPair::RSA(pkey)).map_err(|e| e.into())
|
||||
PKey::from_rsa(rsa)
|
||||
.map(|pkey| KeyPair::RSA(pkey))
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// Given a know pkey of an RSA key, return the wrapped keypair
|
||||
#[cfg(feature = "openssl")]
|
||||
pub fn from_rsa_pkey(pkey: PKey) -> Self {
|
||||
KeyPair::RSA(pkey)
|
||||
@ -60,9 +66,12 @@ impl KeyPair {
|
||||
/// Creates an EC, elliptic curve, type keypair, only P256 or P384 are supported.
|
||||
#[cfg(feature = "openssl")]
|
||||
pub fn from_ec_key(ec_key: EcKey) -> DnsSecResult<Self> {
|
||||
PKey::from_ec_key(ec_key).map(|pkey| KeyPair::EC(pkey)).map_err(|e| e.into())
|
||||
PKey::from_ec_key(ec_key)
|
||||
.map(|pkey| KeyPair::EC(pkey))
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// Given a know pkey of an EC key, return the wrapped keypair
|
||||
#[cfg(feature = "openssl")]
|
||||
pub fn from_ec_pkey(pkey: PKey) -> Self {
|
||||
KeyPair::EC(pkey)
|
||||
@ -207,7 +216,7 @@ impl KeyPair {
|
||||
if public_key.len() != 32 {
|
||||
return Err(DnsSecErrorKind::Msg(format!("expected 32 byte public_key: {}",
|
||||
public_key.len()))
|
||||
.into());
|
||||
.into());
|
||||
}
|
||||
|
||||
// these are LittleEndian encoded bytes... we need to special case
|
||||
@ -244,9 +253,13 @@ impl KeyPair {
|
||||
|
||||
// this is to get us access to the exponent and the modulus
|
||||
// TODO: make these expects a try! and Err()
|
||||
let e: Vec<u8> = rsa.e().expect("RSA should have been initialized").to_vec();
|
||||
let e: Vec<u8> = rsa.e()
|
||||
.expect("RSA should have been initialized")
|
||||
.to_vec();
|
||||
// TODO: make these expects a try! and Err()
|
||||
let n: Vec<u8> = rsa.n().expect("RSA should have been initialized").to_vec();
|
||||
let n: Vec<u8> = rsa.n()
|
||||
.expect("RSA should have been initialized")
|
||||
.to_vec();
|
||||
|
||||
if e.len() > 255 {
|
||||
bytes.push(0);
|
||||
@ -267,14 +280,17 @@ impl KeyPair {
|
||||
// TODO: make these expects a try! and Err()
|
||||
let ec_key: EcKey = pkey.ec_key()
|
||||
.expect("pkey should have been initialized with EC");
|
||||
ec_key.group()
|
||||
ec_key
|
||||
.group()
|
||||
.and_then(|group| ec_key.public_key().map(|point| (group, point)))
|
||||
.ok_or(DnsSecErrorKind::Message("missing group or point on ec_key").into())
|
||||
.and_then(|(group, point)| {
|
||||
BigNumContext::new()
|
||||
.and_then(|mut ctx| {
|
||||
point.to_bytes(group, POINT_CONVERSION_UNCOMPRESSED, &mut ctx)
|
||||
})
|
||||
point.to_bytes(group,
|
||||
POINT_CONVERSION_UNCOMPRESSED,
|
||||
&mut ctx)
|
||||
})
|
||||
.map_err(|e| e.into())
|
||||
})
|
||||
}
|
||||
@ -390,8 +406,10 @@ impl KeyPair {
|
||||
self.to_dnskey(algorithm)
|
||||
.and_then(|dnskey| self.key_tag().map(|key_tag| (key_tag, dnskey)))
|
||||
.and_then(|(key_tag, dnskey)| {
|
||||
dnskey.to_digest(name, digest_type).map(|digest| (key_tag, digest))
|
||||
})
|
||||
dnskey
|
||||
.to_digest(name, digest_type)
|
||||
.map(|digest| (key_tag, digest))
|
||||
})
|
||||
.map(|(key_tag, digest)| DS::new(key_tag, algorithm, digest_type, digest))
|
||||
}
|
||||
|
||||
@ -420,8 +438,8 @@ impl KeyPair {
|
||||
KeyPair::ED25519(ref ed_key) => {
|
||||
Ed25519KeyPair::from_bytes(&ed_key.private_key, &ed_key.public_key)
|
||||
.map_err(|_| {
|
||||
DnsSecErrorKind::Message("something is wrong with the keys").into()
|
||||
})
|
||||
DnsSecErrorKind::Message("something is wrong with the keys").into()
|
||||
})
|
||||
.map(|ed_key| ed_key.sign(message).as_slice().to_vec())
|
||||
}
|
||||
#[cfg(not(any(feature = "openssl", feature = "ring")))]
|
||||
@ -453,20 +471,23 @@ impl KeyPair {
|
||||
let digest_type = try!(DigestType::from(algorithm).to_openssl_digest());
|
||||
let mut verifier = Verifier::new(digest_type, &pkey).unwrap();
|
||||
try!(verifier.update(message));
|
||||
verifier.finish(signature)
|
||||
verifier
|
||||
.finish(signature)
|
||||
.map_err(|e| e.into())
|
||||
.and_then(|b| if b {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(DnsSecErrorKind::Message("could not verify").into())
|
||||
})
|
||||
Ok(())
|
||||
} else {
|
||||
Err(DnsSecErrorKind::Message("could not verify").into())
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "ring")]
|
||||
KeyPair::ED25519(ref ed_key) => {
|
||||
let public_key = Input::from(&ed_key.public_key);
|
||||
let message = Input::from(message);
|
||||
let signature = Input::from(signature);
|
||||
EdDSAParameters {}.verify(public_key, message, signature).map_err(|e| e.into())
|
||||
EdDSAParameters {}
|
||||
.verify(public_key, message, signature)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
#[cfg(not(any(feature = "openssl", feature = "ring")))]
|
||||
_ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
|
||||
@ -523,16 +544,16 @@ impl KeyPair {
|
||||
|
||||
if bytes.len() != 64 {
|
||||
return Err(DnsSecErrorKind::Msg(format!("expected 64 bytes: {}", bytes.len()))
|
||||
.into());
|
||||
.into());
|
||||
}
|
||||
|
||||
private_key.copy_from_slice(&bytes[..32]);
|
||||
public_key.copy_from_slice(&bytes[32..]);
|
||||
|
||||
Ok(KeyPair::from_ed25519(Ed25519KeyPairBytes {
|
||||
private_key: private_key,
|
||||
public_key: public_key,
|
||||
}))
|
||||
private_key: private_key,
|
||||
public_key: public_key,
|
||||
}))
|
||||
}
|
||||
#[cfg(not(all(feature = "openssl", feature = "ring")))]
|
||||
_ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
|
||||
@ -651,8 +672,8 @@ fn to_from_public_key_test(algorithm: Algorithm) {
|
||||
let key = KeyPair::generate(algorithm).unwrap();
|
||||
|
||||
assert!(key.to_public_bytes()
|
||||
.and_then(|bytes| KeyPair::from_public_bytes(&bytes, algorithm))
|
||||
.is_ok());
|
||||
.and_then(|bytes| KeyPair::from_public_bytes(&bytes, algorithm))
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
|
@ -18,7 +18,7 @@ use std::io::Write;
|
||||
#[cfg(feature = "openssl")]
|
||||
use openssl::hash;
|
||||
|
||||
use ::error::*;
|
||||
use error::*;
|
||||
#[cfg(feature = "openssl")]
|
||||
use rr::dnssec::DigestType;
|
||||
#[cfg(feature = "openssl")]
|
||||
@ -26,79 +26,82 @@ use rr::Name;
|
||||
#[cfg(feature = "openssl")]
|
||||
use serialize::binary::{BinEncoder, BinSerializable};
|
||||
|
||||
// RFC 5155 NSEC3 March 2008
|
||||
//
|
||||
// 11. IANA Considerations
|
||||
//
|
||||
// Although the NSEC3 and NSEC3PARAM RR formats include a hash algorithm
|
||||
// parameter, this document does not define a particular mechanism for
|
||||
// safely transitioning from one NSEC3 hash algorithm to another. When
|
||||
// specifying a new hash algorithm for use with NSEC3, a transition
|
||||
// mechanism MUST also be defined.
|
||||
//
|
||||
// This document updates the IANA registry "DOMAIN NAME SYSTEM
|
||||
// PARAMETERS" (http://www.iana.org/assignments/dns-parameters) in sub-
|
||||
// registry "TYPES", by defining two new types. Section 3 defines the
|
||||
// NSEC3 RR type 50. Section 4 defines the NSEC3PARAM RR type 51.
|
||||
//
|
||||
// This document updates the IANA registry "DNS SECURITY ALGORITHM
|
||||
// NUMBERS -- per [RFC4035]"
|
||||
// (http://www.iana.org/assignments/dns-sec-alg-numbers). Section 2
|
||||
// defines the aliases DSA-NSEC3-SHA1 (6) and RSASHA1-NSEC3-SHA1 (7) for
|
||||
// respectively existing registrations DSA and RSASHA1 in combination
|
||||
// with NSEC3 hash algorithm SHA1.
|
||||
//
|
||||
// Since these algorithm numbers are aliases for existing DNSKEY
|
||||
// algorithm numbers, the flags that exist for the original algorithm
|
||||
// are valid for the alias algorithm.
|
||||
//
|
||||
// This document creates a new IANA registry for NSEC3 flags. This
|
||||
// registry is named "DNSSEC NSEC3 Flags". The initial contents of this
|
||||
// registry are:
|
||||
//
|
||||
// 0 1 2 3 4 5 6 7
|
||||
// +---+---+---+---+---+---+---+---+
|
||||
// | | | | | | | |Opt|
|
||||
// | | | | | | | |Out|
|
||||
// +---+---+---+---+---+---+---+---+
|
||||
//
|
||||
// bit 7 is the Opt-Out flag.
|
||||
//
|
||||
// bits 0 - 6 are available for assignment.
|
||||
//
|
||||
// Assignment of additional NSEC3 Flags in this registry requires IETF
|
||||
// Standards Action [RFC2434].
|
||||
//
|
||||
// This document creates a new IANA registry for NSEC3PARAM flags. This
|
||||
// registry is named "DNSSEC NSEC3PARAM Flags". The initial contents of
|
||||
// this registry are:
|
||||
//
|
||||
// 0 1 2 3 4 5 6 7
|
||||
// +---+---+---+---+---+---+---+---+
|
||||
// | | | | | | | | 0 |
|
||||
// +---+---+---+---+---+---+---+---+
|
||||
//
|
||||
// bit 7 is reserved and must be 0.
|
||||
//
|
||||
// bits 0 - 6 are available for assignment.
|
||||
//
|
||||
// Assignment of additional NSEC3PARAM Flags in this registry requires
|
||||
// IETF Standards Action [RFC2434].
|
||||
//
|
||||
// Finally, this document creates a new IANA registry for NSEC3 hash
|
||||
// algorithms. This registry is named "DNSSEC NSEC3 Hash Algorithms".
|
||||
// The initial contents of this registry are:
|
||||
//
|
||||
// 0 is Reserved.
|
||||
//
|
||||
// 1 is SHA-1.
|
||||
//
|
||||
// 2-255 Available for assignment.
|
||||
//
|
||||
// Assignment of additional NSEC3 hash algorithms in this registry
|
||||
// requires IETF Standards Action [RFC2434].
|
||||
/// ```text
|
||||
/// RFC 5155 NSEC3 March 2008
|
||||
///
|
||||
/// 11. IANA Considerations
|
||||
///
|
||||
/// Although the NSEC3 and NSEC3PARAM RR formats include a hash algorithm
|
||||
/// parameter, this document does not define a particular mechanism for
|
||||
/// safely transitioning from one NSEC3 hash algorithm to another. When
|
||||
/// specifying a new hash algorithm for use with NSEC3, a transition
|
||||
/// mechanism MUST also be defined.
|
||||
///
|
||||
/// This document updates the IANA registry "DOMAIN NAME SYSTEM
|
||||
/// PARAMETERS" (http://www.iana.org/assignments/dns-parameters) in sub-
|
||||
/// registry "TYPES", by defining two new types. Section 3 defines the
|
||||
/// NSEC3 RR type 50. Section 4 defines the NSEC3PARAM RR type 51.
|
||||
///
|
||||
/// This document updates the IANA registry "DNS SECURITY ALGORITHM
|
||||
/// NUMBERS -- per [RFC4035]"
|
||||
/// (http://www.iana.org/assignments/dns-sec-alg-numbers). Section 2
|
||||
/// defines the aliases DSA-NSEC3-SHA1 (6) and RSASHA1-NSEC3-SHA1 (7) for
|
||||
/// respectively existing registrations DSA and RSASHA1 in combination
|
||||
/// with NSEC3 hash algorithm SHA1.
|
||||
///
|
||||
/// Since these algorithm numbers are aliases for existing DNSKEY
|
||||
/// algorithm numbers, the flags that exist for the original algorithm
|
||||
/// are valid for the alias algorithm.
|
||||
///
|
||||
/// This document creates a new IANA registry for NSEC3 flags. This
|
||||
/// registry is named "DNSSEC NSEC3 Flags". The initial contents of this
|
||||
/// registry are:
|
||||
///
|
||||
/// 0 1 2 3 4 5 6 7
|
||||
/// +---+---+---+---+---+---+---+---+
|
||||
/// | | | | | | | |Opt|
|
||||
/// | | | | | | | |Out|
|
||||
/// +---+---+---+---+---+---+---+---+
|
||||
///
|
||||
/// bit 7 is the Opt-Out flag.
|
||||
///
|
||||
/// bits 0 - 6 are available for assignment.
|
||||
///
|
||||
/// Assignment of additional NSEC3 Flags in this registry requires IETF
|
||||
/// Standards Action [RFC2434].
|
||||
///
|
||||
/// This document creates a new IANA registry for NSEC3PARAM flags. This
|
||||
/// registry is named "DNSSEC NSEC3PARAM Flags". The initial contents of
|
||||
/// this registry are:
|
||||
///
|
||||
/// 0 1 2 3 4 5 6 7
|
||||
/// +---+---+---+---+---+---+---+---+
|
||||
/// | | | | | | | | 0 |
|
||||
/// +---+---+---+---+---+---+---+---+
|
||||
///
|
||||
/// bit 7 is reserved and must be 0.
|
||||
///
|
||||
/// bits 0 - 6 are available for assignment.
|
||||
///
|
||||
/// Assignment of additional NSEC3PARAM Flags in this registry requires
|
||||
/// IETF Standards Action [RFC2434].
|
||||
///
|
||||
/// Finally, this document creates a new IANA registry for NSEC3 hash
|
||||
/// algorithms. This registry is named "DNSSEC NSEC3 Hash Algorithms".
|
||||
/// The initial contents of this registry are:
|
||||
///
|
||||
/// 0 is Reserved.
|
||||
///
|
||||
/// 1 is SHA-1.
|
||||
///
|
||||
/// 2-255 Available for assignment.
|
||||
///
|
||||
/// Assignment of additional NSEC3 hash algorithms in this registry
|
||||
/// requires IETF Standards Action [RFC2434].
|
||||
/// ```
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub enum Nsec3HashAlgorithm {
|
||||
/// Hash for the Nsec3 records
|
||||
SHA1,
|
||||
}
|
||||
|
||||
@ -112,35 +115,37 @@ impl Nsec3HashAlgorithm {
|
||||
}
|
||||
}
|
||||
|
||||
// Laurie, et al. Standards Track [Page 14]
|
||||
//
|
||||
// RFC 5155 NSEC3 March 2008
|
||||
//
|
||||
// Define H(x) to be the hash of x using the Hash Algorithm selected by
|
||||
// the NSEC3 RR, k to be the number of Iterations, and || to indicate
|
||||
// concatenation. Then define:
|
||||
//
|
||||
// IH(salt, x, 0) = H(x || salt), and
|
||||
//
|
||||
// IH(salt, x, k) = H(IH(salt, x, k-1) || salt), if k > 0
|
||||
//
|
||||
// Then the calculated hash of an owner name is
|
||||
//
|
||||
// IH(salt, owner name, iterations),
|
||||
//
|
||||
// where the owner name is in the canonical form, defined as:
|
||||
//
|
||||
// The wire format of the owner name where:
|
||||
//
|
||||
// 1. The owner name is fully expanded (no DNS name compression) and
|
||||
// fully qualified;
|
||||
//
|
||||
// 2. All uppercase US-ASCII letters are replaced by the corresponding
|
||||
// lowercase US-ASCII letters;
|
||||
//
|
||||
// 3. If the owner name is a wildcard name, the owner name is in its
|
||||
// original unexpanded form, including the "*" label (no wildcard
|
||||
// substitution);
|
||||
/// ```text
|
||||
/// Laurie, et al. Standards Track [Page 14]
|
||||
///
|
||||
/// RFC 5155 NSEC3 March 2008
|
||||
///
|
||||
/// Define H(x) to be the hash of x using the Hash Algorithm selected by
|
||||
/// the NSEC3 RR, k to be the number of Iterations, and || to indicate
|
||||
/// concatenation. Then define:
|
||||
///
|
||||
/// IH(salt, x, 0) = H(x || salt), and
|
||||
///
|
||||
/// IH(salt, x, k) = H(IH(salt, x, k-1) || salt), if k > 0
|
||||
///
|
||||
/// Then the calculated hash of an owner name is
|
||||
///
|
||||
/// IH(salt, owner name, iterations),
|
||||
///
|
||||
/// where the owner name is in the canonical form, defined as:
|
||||
///
|
||||
/// The wire format of the owner name where:
|
||||
///
|
||||
/// 1. The owner name is fully expanded (no DNS name compression) and
|
||||
/// fully qualified;
|
||||
///
|
||||
/// 2. All uppercase US-ASCII letters are replaced by the corresponding
|
||||
/// lowercase US-ASCII letters;
|
||||
///
|
||||
/// 3. If the owner name is a wildcard name, the owner name is in its
|
||||
/// original unexpanded form, including the "*" label (no wildcard
|
||||
/// substitution);
|
||||
/// ```
|
||||
#[cfg(feature = "openssl")]
|
||||
pub fn hash(&self, salt: &[u8], name: &Name, iterations: u16) -> DnsSecResult<Vec<u8>> {
|
||||
match *self {
|
||||
@ -158,7 +163,7 @@ impl Nsec3HashAlgorithm {
|
||||
}
|
||||
}
|
||||
|
||||
// until there is another supported algorithm, just hardcoded to this.
|
||||
/// until there is another supported algorithm, just hardcoded to this.
|
||||
#[cfg(feature = "openssl")]
|
||||
fn sha1_recursive_hash(salt: &[u8], bytes: Vec<u8>, iterations: u16) -> DnsSecResult<Vec<u8>> {
|
||||
let digest_type = try!(DigestType::SHA1.to_openssl_digest());
|
||||
@ -192,11 +197,20 @@ fn test_hash() {
|
||||
let name = Name::new().label("www").label("example").label("com");
|
||||
let salt: Vec<u8> = vec![1, 2, 3, 4];
|
||||
|
||||
assert_eq!(Nsec3HashAlgorithm::SHA1.hash(&salt, &name, 0).unwrap().len(),
|
||||
assert_eq!(Nsec3HashAlgorithm::SHA1
|
||||
.hash(&salt, &name, 0)
|
||||
.unwrap()
|
||||
.len(),
|
||||
20);
|
||||
assert_eq!(Nsec3HashAlgorithm::SHA1.hash(&salt, &name, 1).unwrap().len(),
|
||||
assert_eq!(Nsec3HashAlgorithm::SHA1
|
||||
.hash(&salt, &name, 1)
|
||||
.unwrap()
|
||||
.len(),
|
||||
20);
|
||||
assert_eq!(Nsec3HashAlgorithm::SHA1.hash(&salt, &name, 3).unwrap().len(),
|
||||
assert_eq!(Nsec3HashAlgorithm::SHA1
|
||||
.hash(&salt, &name, 3)
|
||||
.unwrap()
|
||||
.len(),
|
||||
20);
|
||||
}
|
||||
|
||||
@ -256,6 +270,8 @@ fn hash_with_base32(name: &str) -> String {
|
||||
// NSEC3PARAM 1 0 12 aabbccdd
|
||||
let known_name = Name::parse(name, Some(&Name::new())).unwrap();
|
||||
let known_salt = [0xAAu8, 0xBBu8, 0xCCu8, 0xDDu8];
|
||||
let hash = Nsec3HashAlgorithm::SHA1.hash(&known_salt, &known_name, 12).unwrap();
|
||||
let hash = Nsec3HashAlgorithm::SHA1
|
||||
.hash(&known_salt, &known_name, 12)
|
||||
.unwrap();
|
||||
base32hex::encode(&hash).to_lowercase()
|
||||
}
|
||||
|
@ -239,6 +239,7 @@ pub struct Signer {
|
||||
is_zone_update_auth: bool,
|
||||
}
|
||||
|
||||
/// Placeholder type for when OpenSSL and *ring* are disabled; enable OpenSSL and Ring for support
|
||||
#[cfg(not(any(feature = "openssl", feature = "ring")))]
|
||||
pub struct Signer;
|
||||
|
||||
@ -279,21 +280,34 @@ impl Signer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the algorithm this Signer will use to either sign or validate a signature
|
||||
pub fn algorithm(&self) -> Algorithm {
|
||||
self.algorithm
|
||||
}
|
||||
|
||||
/// Return the key used for validateion/signing
|
||||
pub fn key(&self) -> &KeyPair {
|
||||
&self.key
|
||||
}
|
||||
|
||||
/// Returns the duration that this signature is valid for
|
||||
pub fn sig_duration(&self) -> Duration {
|
||||
self.sig_duration
|
||||
}
|
||||
|
||||
/// The name of the signing entity, e.g. the DNS server name.
|
||||
///
|
||||
/// This should match the name on key in the zone.
|
||||
pub fn signer_name(&self) -> &Name {
|
||||
&self.signer_name
|
||||
}
|
||||
|
||||
/// A hint to the DNSKey associated with this Signer can be used to sign/validate records in the zone
|
||||
pub fn is_zone_signing_key(&self) -> bool {
|
||||
self.is_zone_signing_key
|
||||
}
|
||||
|
||||
/// The associated key can be used for dynamic updates
|
||||
pub fn is_zone_update_auth(&self) -> bool {
|
||||
self.is_zone_update_auth
|
||||
}
|
||||
@ -504,7 +518,7 @@ impl Signer {
|
||||
name
|
||||
} else {
|
||||
return Err(DnsSecErrorKind::Msg(format!("could not determine name from {}", name))
|
||||
.into());
|
||||
.into());
|
||||
};
|
||||
|
||||
// TODO: rather than buffering here, use the Signer/Verifier? might mean fewer allocations...
|
||||
@ -530,14 +544,16 @@ impl Signer {
|
||||
sig_inception,
|
||||
key_tag,
|
||||
&signer_name)
|
||||
.is_ok());
|
||||
.is_ok());
|
||||
|
||||
// construct the rrset signing data
|
||||
for record in rrset {
|
||||
// RR(i) = name | type | class | OrigTTL | RDATA length | RDATA
|
||||
//
|
||||
// name is calculated according to the function in the RFC 4035
|
||||
assert!(name.to_lowercase().emit_as_canonical(&mut encoder, true).is_ok());
|
||||
assert!(name.to_lowercase()
|
||||
.emit_as_canonical(&mut encoder, true)
|
||||
.is_ok());
|
||||
//
|
||||
// type is the RRset type and all RRs in the class
|
||||
assert!(type_covered.emit(&mut encoder).is_ok());
|
||||
@ -587,7 +603,7 @@ impl Signer {
|
||||
} else {
|
||||
return Err(DnsSecErrorKind::Msg(format!("could not determine name from {}",
|
||||
rrsig.name()))
|
||||
.into());
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
@ -670,7 +686,9 @@ impl Signer {
|
||||
///
|
||||
/// The signature, ready to be stored in an `RData::RRSIG`.
|
||||
pub fn sign(&self, hash: &[u8]) -> DnsSecResult<Vec<u8>> {
|
||||
self.key.sign(self.algorithm, &hash).map_err(|e| e.into())
|
||||
self.key
|
||||
.sign(self.algorithm, &hash)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// Verifies the hash matches the signature with the current `key`.
|
||||
@ -686,7 +704,9 @@ impl Signer {
|
||||
/// True if and only if the signature is valid for the hash. This will always return
|
||||
/// false if the `key`.
|
||||
pub fn verify(&self, hash: &[u8], signature: &[u8]) -> DnsSecResult<()> {
|
||||
self.key.verify(self.algorithm, hash, signature).map_err(|e| e.into())
|
||||
self.key
|
||||
.verify(self.algorithm, hash, signature)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
@ -790,7 +810,8 @@ mod tests {
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::CNAME)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::CNAME(Name::parse("a.iana-servers.net.", None).unwrap()))
|
||||
.set_rdata(RData::CNAME(Name::parse("a.iana-servers.net.", None)
|
||||
.unwrap()))
|
||||
.clone(), // different type
|
||||
Record::new()
|
||||
.set_name(Name::parse("www.example.com.", None).unwrap())
|
||||
|
@ -20,6 +20,7 @@ use std::convert::From;
|
||||
|
||||
use rr::dnssec::Algorithm;
|
||||
|
||||
/// Used to specify the set of SupportedAlgorithms between a client and server
|
||||
#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
pub struct SupportedAlgorithms {
|
||||
// right now the number of Algorithms supported are fewer than 16..
|
||||
@ -27,14 +28,17 @@ pub struct SupportedAlgorithms {
|
||||
}
|
||||
|
||||
impl SupportedAlgorithms {
|
||||
/// Return a new set of Supported algorithms
|
||||
pub fn new() -> Self {
|
||||
SupportedAlgorithms { bit_map: 0 }
|
||||
}
|
||||
|
||||
/// Specify the entire set is supported
|
||||
pub fn all() -> Self {
|
||||
SupportedAlgorithms { bit_map: 0b01111111 }
|
||||
}
|
||||
|
||||
/// Based on the set of Algorithms, return the supported set
|
||||
pub fn from_vec(algorithms: &[Algorithm]) -> Self {
|
||||
let mut supported = SupportedAlgorithms::new();
|
||||
|
||||
@ -75,20 +79,24 @@ impl SupportedAlgorithms {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the specified algorithm as supported
|
||||
pub fn set(&mut self, algorithm: Algorithm) {
|
||||
let bit_pos: u8 = Self::pos(algorithm);
|
||||
self.bit_map |= bit_pos;
|
||||
}
|
||||
|
||||
/// Returns true if the algorithm is supported
|
||||
pub fn has(&self, algorithm: Algorithm) -> bool {
|
||||
let bit_pos: u8 = Self::pos(algorithm);
|
||||
(bit_pos & self.bit_map) == bit_pos
|
||||
}
|
||||
|
||||
/// Return an Iterator over the supported set.
|
||||
pub fn iter(&self) -> SupportedAlgorithmsIter {
|
||||
SupportedAlgorithmsIter::new(self)
|
||||
}
|
||||
|
||||
/// Return the count of supported algorithms
|
||||
pub fn len(&self) -> u16 {
|
||||
// this is pretty much guaranteed to be less that u16::max_value()
|
||||
self.iter().count() as u16
|
||||
|
@ -26,20 +26,24 @@ use rr::dnssec::KeyPair;
|
||||
#[cfg(feature = "openssl")]
|
||||
const ROOT_ANCHOR: &'static str = include_str!("Kjqmt7v.pem");
|
||||
|
||||
// TODO: these should also store some information, or more specifically, metadata from the signed
|
||||
// public certificate.
|
||||
/// The root set of trust anchors for validating DNSSec, anything in this set will be trusted
|
||||
pub struct TrustAnchor {
|
||||
// TODO: these should also store some information, or more specifically, metadata from the signed
|
||||
// public certificate.
|
||||
pkeys: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Default for TrustAnchor {
|
||||
#[cfg(feature = "openssl")]
|
||||
fn default() -> TrustAnchor {
|
||||
let rsa = Rsa::public_key_from_pem(ROOT_ANCHOR.as_bytes())
|
||||
.expect("Error parsing Kjqmt7v.pem");
|
||||
let rsa =
|
||||
Rsa::public_key_from_pem(ROOT_ANCHOR.as_bytes()).expect("Error parsing Kjqmt7v.pem");
|
||||
let key = KeyPair::from_rsa(rsa).expect("Error creating KeyPair from RSA key");
|
||||
|
||||
TrustAnchor { pkeys: vec![key.to_public_bytes().expect("could not convert key to bytes")] }
|
||||
TrustAnchor {
|
||||
pkeys: vec![key.to_public_bytes()
|
||||
.expect("could not convert key to bytes")],
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "openssl"))]
|
||||
@ -49,10 +53,12 @@ impl Default for TrustAnchor {
|
||||
}
|
||||
|
||||
impl TrustAnchor {
|
||||
/// Creates a new empty trust anchor set
|
||||
pub fn new() -> TrustAnchor {
|
||||
TrustAnchor { pkeys: vec![] }
|
||||
}
|
||||
|
||||
/// determines if the key is in the trust anchor set
|
||||
pub fn contains(&self, other_key: &[u8]) -> bool {
|
||||
self.pkeys.iter().any(|k| other_key == k as &[u8])
|
||||
}
|
||||
@ -64,6 +70,7 @@ impl TrustAnchor {
|
||||
}
|
||||
}
|
||||
|
||||
/// get the trust anchor at the specified index
|
||||
pub fn get(&self, idx: usize) -> &[u8] {
|
||||
&self.pkeys[idx]
|
||||
}
|
||||
|
@ -36,11 +36,12 @@ pub struct Name {
|
||||
}
|
||||
|
||||
impl Name {
|
||||
/// Create a new domain::Name, i.e. label
|
||||
pub fn new() -> Self {
|
||||
Name { labels: Rc::new(Vec::new()) }
|
||||
}
|
||||
|
||||
// this is the root label, i.e. no labels, can probably make this better in the future.
|
||||
/// Returns the root label, i.e. no labels, can probably make this better in the future.
|
||||
pub fn root() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
@ -224,7 +225,9 @@ impl Name {
|
||||
} else {
|
||||
1
|
||||
};
|
||||
self.labels.iter().fold(dots, |acc, item| acc + item.len())
|
||||
self.labels
|
||||
.iter()
|
||||
.fold(dots, |acc, item| acc + item.len())
|
||||
}
|
||||
|
||||
/// attempts to parse a name such as `"example.com."` or `"subdomain.example.com."`
|
||||
@ -372,10 +375,12 @@ impl Name {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes the labels, as lower case, to the encoder
|
||||
pub fn emit_with_lowercase(&self, encoder: &mut BinEncoder, lowercase: bool) -> EncodeResult {
|
||||
let is_canonical_names = encoder.is_canonical_names();
|
||||
if lowercase {
|
||||
self.to_lowercase().emit_as_canonical(encoder, is_canonical_names)
|
||||
self.to_lowercase()
|
||||
.emit_as_canonical(encoder, is_canonical_names)
|
||||
} else {
|
||||
self.emit_as_canonical(encoder, is_canonical_names)
|
||||
}
|
||||
@ -435,10 +440,13 @@ impl From<Ipv4Addr> for Name {
|
||||
fn from(addr: Ipv4Addr) -> Name {
|
||||
let octets = addr.octets();
|
||||
|
||||
let mut labels = octets.iter().rev().fold(Vec::with_capacity(6), |mut labels, o| {
|
||||
labels.push(format!("{}", o));
|
||||
labels
|
||||
});
|
||||
let mut labels = octets
|
||||
.iter()
|
||||
.rev()
|
||||
.fold(Vec::with_capacity(6), |mut labels, o| {
|
||||
labels.push(format!("{}", o));
|
||||
labels
|
||||
});
|
||||
|
||||
labels.push("in-addr".to_string());
|
||||
labels.push("arpa".to_string());
|
||||
@ -451,13 +459,16 @@ impl From<Ipv6Addr> for Name {
|
||||
fn from(addr: Ipv6Addr) -> Name {
|
||||
let segments = addr.segments();
|
||||
|
||||
let mut labels = segments.iter().rev().fold(Vec::with_capacity(34), |mut labels, o| {
|
||||
labels.push(format!("{:x}", (*o & 0x000F) as u8));
|
||||
labels.push(format!("{:x}", (*o >> 4 & 0x000F) as u8));
|
||||
labels.push(format!("{:x}", (*o >> 8 & 0x000F) as u8));
|
||||
labels.push(format!("{:x}", (*o >> 12 & 0x000F) as u8));
|
||||
labels
|
||||
});
|
||||
let mut labels = segments
|
||||
.iter()
|
||||
.rev()
|
||||
.fold(Vec::with_capacity(34), |mut labels, o| {
|
||||
labels.push(format!("{:x}", (*o & 0x000F) as u8));
|
||||
labels.push(format!("{:x}", (*o >> 4 & 0x000F) as u8));
|
||||
labels.push(format!("{:x}", (*o >> 8 & 0x000F) as u8));
|
||||
labels.push(format!("{:x}", (*o >> 12 & 0x000F) as u8));
|
||||
labels
|
||||
});
|
||||
|
||||
labels.push("ip6".to_string());
|
||||
labels.push("arpa".to_string());
|
||||
@ -748,10 +759,7 @@ mod tests {
|
||||
|
||||
assert_eq!(zone.base_name(), Name::new().label("com"));
|
||||
assert!(zone.base_name().base_name().is_root());
|
||||
assert!(zone.base_name()
|
||||
.base_name()
|
||||
.base_name()
|
||||
.is_root());
|
||||
assert!(zone.base_name().base_name().base_name().is_root());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -35,5 +35,6 @@ pub use self::rr_key::RrKey;
|
||||
pub use self::rr_set::IntoRecordSet;
|
||||
pub use self::rr_set::RecordSet;
|
||||
|
||||
/// A RecordSet is a set of Records whose types all match, but data do not
|
||||
#[deprecated = "will be removed post 0.9.x, use RecordSet"]
|
||||
pub type RrSet = RecordSet;
|
||||
|
@ -46,6 +46,7 @@ use ::serialize::txt::*;
|
||||
use ::serialize::binary::*;
|
||||
use ::error::*;
|
||||
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder) -> DecodeResult<Ipv4Addr> {
|
||||
Ok(Ipv4Addr::new(try!(decoder.pop()),
|
||||
try!(decoder.pop()),
|
||||
@ -53,6 +54,7 @@ pub fn read(decoder: &mut BinDecoder) -> DecodeResult<Ipv4Addr> {
|
||||
try!(decoder.pop())))
|
||||
}
|
||||
|
||||
/// Write the RData from the given Decoder
|
||||
pub fn emit(encoder: &mut BinEncoder, address: &Ipv4Addr) -> EncodeResult {
|
||||
let segments = address.octets();
|
||||
|
||||
@ -63,6 +65,7 @@ pub fn emit(encoder: &mut BinEncoder, address: &Ipv4Addr) -> EncodeResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parse the RData from a set of Tokens
|
||||
pub fn parse(tokens: &Vec<Token>) -> ParseResult<Ipv4Addr> {
|
||||
let mut token = tokens.iter();
|
||||
|
||||
|
@ -38,8 +38,7 @@ use ::serialize::txt::*;
|
||||
use ::serialize::binary::*;
|
||||
use ::error::*;
|
||||
|
||||
//
|
||||
// AAAA { address: Ipv6Addr }
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder) -> DecodeResult<Ipv6Addr> {
|
||||
let a: u16 = try!(decoder.read_u16());
|
||||
let b: u16 = try!(decoder.read_u16());
|
||||
@ -53,6 +52,7 @@ pub fn read(decoder: &mut BinDecoder) -> DecodeResult<Ipv6Addr> {
|
||||
Ok(Ipv6Addr::new(a, b, c, d, e, f, g, h))
|
||||
}
|
||||
|
||||
/// Write the RData from the given Decoder
|
||||
pub fn emit(encoder: &mut BinEncoder, address: &Ipv6Addr) -> EncodeResult {
|
||||
let segments = address.segments();
|
||||
|
||||
@ -67,6 +67,7 @@ pub fn emit(encoder: &mut BinEncoder, address: &Ipv6Addr) -> EncodeResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parse the RData from a set of Tokens
|
||||
pub fn parse(tokens: &Vec<Token>) -> ParseResult<Ipv6Addr> {
|
||||
let mut token = tokens.iter();
|
||||
|
||||
|
@ -77,6 +77,19 @@ pub struct DNSKEY {
|
||||
}
|
||||
|
||||
impl DNSKEY {
|
||||
/// Construct a new DNSKey RData
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `zone_key` - this key is used to sign Zone resource records
|
||||
/// * `secure_entry_point` - this key is used to sign DNSKeys that sign the Zone records
|
||||
/// * `revoke` - this key has been revoked
|
||||
/// * `algorithm` - specifies the algorithm which this Key uses to sign records
|
||||
/// * `public_key` - the public key material, in native endian, the emitter will perform any necessary conversion
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// A new DNSKEY RData for use in a Resource Record
|
||||
pub fn new(zone_key: bool,
|
||||
secure_entry_point: bool,
|
||||
revoke: bool,
|
||||
@ -216,6 +229,7 @@ impl DNSKEY {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<DNSKEY> {
|
||||
let flags: u16 = try!(decoder.read_u16());
|
||||
|
||||
@ -248,6 +262,7 @@ pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<DNSKEY>
|
||||
Ok(DNSKEY::new(zone_key, secure_entry_point, revoke, algorithm, public_key))
|
||||
}
|
||||
|
||||
/// Write the RData from the given Decoder
|
||||
pub fn emit(encoder: &mut BinEncoder, rdata: &DNSKEY) -> EncodeResult {
|
||||
let mut flags: u16 = 0;
|
||||
if rdata.zone_key() {
|
||||
|
@ -74,6 +74,18 @@ pub struct DS {
|
||||
}
|
||||
|
||||
impl DS {
|
||||
/// Constructs a new DS RData
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `key_tag` - the key_tag associated to the DNSKEY
|
||||
/// * `algorithm` - algorithm as specified in the DNSKEY
|
||||
/// * `digest_type` - hash algorithm used to validate the DNSKEY
|
||||
/// * `digest` - hash of the DNSKEY
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// the DS RDATA for use in a Resource Record
|
||||
pub fn new(key_tag: u16, algorithm: Algorithm, digest_type: DigestType, digest: Vec<u8>) -> DS {
|
||||
DS {
|
||||
key_tag: key_tag,
|
||||
@ -169,6 +181,7 @@ impl DS {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<DS> {
|
||||
let start_idx = decoder.index();
|
||||
|
||||
@ -182,6 +195,7 @@ pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<DS> {
|
||||
Ok(DS::new(key_tag, algorithm, digest_type, digest))
|
||||
}
|
||||
|
||||
/// Write the RData from the given Decoder
|
||||
pub fn emit(encoder: &mut BinEncoder, rdata: &DS) -> EncodeResult {
|
||||
try!(encoder.emit_u16(rdata.key_tag()));
|
||||
try!(rdata.algorithm().emit(encoder)); // always 3 for now
|
||||
|
@ -45,6 +45,16 @@ pub struct MX {
|
||||
}
|
||||
|
||||
impl MX {
|
||||
/// Constructs a new MX RData
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `preference` - weight of this MX record as opposed to others, lower values have the higher preference
|
||||
/// * `exchange` - Name labels for the mail server
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A new MX RData for use in a Resource Record
|
||||
pub fn new(preference: u16, exchange: Name) -> MX {
|
||||
MX {
|
||||
preference: preference,
|
||||
@ -74,6 +84,7 @@ impl MX {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder) -> DecodeResult<MX> {
|
||||
Ok(MX::new(try!(decoder.read_u16()), try!(Name::read(decoder))))
|
||||
}
|
||||
@ -101,6 +112,7 @@ pub fn emit(encoder: &mut BinEncoder, mx: &MX) -> EncodeResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parse the RData from a set of Tokens
|
||||
pub fn parse(tokens: &Vec<Token>, origin: Option<&Name>) -> ParseResult<MX> {
|
||||
let mut token = tokens.iter();
|
||||
|
||||
|
@ -44,7 +44,7 @@ use ::serialize::binary::*;
|
||||
use ::error::*;
|
||||
use rr::domain::Name;
|
||||
|
||||
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder) -> DecodeResult<Name> {
|
||||
Name::read(decoder)
|
||||
}
|
||||
@ -73,6 +73,7 @@ pub fn emit(encoder: &mut BinEncoder, name_data: &Name) -> EncodeResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parse the RData from a set of Tokens
|
||||
pub fn parse(tokens: &Vec<Token>, origin: Option<&Name>) -> ParseResult<Name> {
|
||||
let mut token = tokens.iter();
|
||||
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
//! negative cache proof for non-existence
|
||||
|
||||
use ::serialize::binary::*;
|
||||
use ::error::*;
|
||||
use serialize::binary::*;
|
||||
use error::*;
|
||||
use rr::{Name, RecordType};
|
||||
use rr::rdata::nsec3;
|
||||
|
||||
@ -52,6 +52,16 @@ pub struct NSEC {
|
||||
}
|
||||
|
||||
impl NSEC {
|
||||
/// Constructs a new NSET RData
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `next_domain_name` - the name labels of the next ordered name in the zone
|
||||
/// * `type_bit_maps` - a bit map of the types that don't exist at this name
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// An NSEC RData for use in a Resource Record
|
||||
pub fn new(next_domain_name: Name, type_bit_maps: Vec<RecordType>) -> NSEC {
|
||||
NSEC {
|
||||
next_domain_name: next_domain_name,
|
||||
@ -100,6 +110,7 @@ impl NSEC {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<NSEC> {
|
||||
let start_idx = decoder.index();
|
||||
|
||||
@ -136,7 +147,10 @@ pub fn test() {
|
||||
use rr::RecordType;
|
||||
|
||||
let rdata = NSEC::new(Name::new().label("www").label("example").label("com"),
|
||||
vec![RecordType::A, RecordType::AAAA, RecordType::DS, RecordType::RRSIG]);
|
||||
vec![RecordType::A,
|
||||
RecordType::AAAA,
|
||||
RecordType::DS,
|
||||
RecordType::RRSIG]);
|
||||
|
||||
let mut bytes = Vec::new();
|
||||
let mut encoder: BinEncoder = BinEncoder::new(&mut bytes);
|
||||
|
@ -119,6 +119,7 @@ pub struct NSEC3 {
|
||||
}
|
||||
|
||||
impl NSEC3 {
|
||||
/// Constructs a new NSEC3 record
|
||||
pub fn new(hash_algorithm: Nsec3HashAlgorithm,
|
||||
opt_out: bool,
|
||||
iterations: u16,
|
||||
@ -237,6 +238,7 @@ impl NSEC3 {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<NSEC3> {
|
||||
let start_idx = decoder.index();
|
||||
|
||||
@ -264,6 +266,16 @@ pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<NSEC3>
|
||||
record_types))
|
||||
}
|
||||
|
||||
/// Decodes the array of RecordTypes covered by this NSEC record
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `decoder` - decoder to read from
|
||||
/// * `bit_map_len` - the number bytes in the bit map
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The Array of covered types
|
||||
pub fn decode_type_bit_maps(decoder: &mut BinDecoder,
|
||||
bit_map_len: usize)
|
||||
-> DecodeResult<Vec<RecordType>> {
|
||||
@ -373,6 +385,7 @@ enum BitMapState {
|
||||
ReadType { window: u8, len: u8, left: u8 },
|
||||
}
|
||||
|
||||
/// Write the RData from the given Decoder
|
||||
pub fn emit(encoder: &mut BinEncoder, rdata: &NSEC3) -> EncodeResult {
|
||||
try!(encoder.emit(rdata.hash_algorithm().into()));
|
||||
let mut flags: u8 = 0;
|
||||
@ -390,6 +403,12 @@ pub fn emit(encoder: &mut BinEncoder, rdata: &NSEC3) -> EncodeResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Encode the bit map
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `encoder` - the encoder to write to
|
||||
/// * `type_bit_maps` - types to encode into the bitmap
|
||||
pub fn encode_bit_maps(encoder: &mut BinEncoder, type_bit_maps: &[RecordType]) -> EncodeResult {
|
||||
let mut hash: HashMap<u8, Vec<u8>> = HashMap::new();
|
||||
|
||||
|
@ -85,6 +85,7 @@ pub struct NSEC3PARAM {
|
||||
}
|
||||
|
||||
impl NSEC3PARAM {
|
||||
/// Constructs a new NSEC3PARAM RData for use in a Resource Record
|
||||
pub fn new(hash_algorithm: Nsec3HashAlgorithm,
|
||||
opt_out: bool,
|
||||
iterations: u16,
|
||||
@ -156,6 +157,7 @@ impl NSEC3PARAM {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder) -> DecodeResult<NSEC3PARAM> {
|
||||
let hash_algorithm = try!(Nsec3HashAlgorithm::from_u8(try!(decoder.read_u8())));
|
||||
let flags: u8 = try!(decoder.read_u8());
|
||||
@ -171,6 +173,7 @@ pub fn read(decoder: &mut BinDecoder) -> DecodeResult<NSEC3PARAM> {
|
||||
Ok(NSEC3PARAM::new(hash_algorithm, opt_out, iterations, salt))
|
||||
}
|
||||
|
||||
/// Write the RData from the given Decoder
|
||||
pub fn emit(encoder: &mut BinEncoder, rdata: &NSEC3PARAM) -> EncodeResult {
|
||||
try!(encoder.emit(rdata.hash_algorithm().into()));
|
||||
let mut flags: u8 = 0;
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
//! null record type, generally not used except as an internal tool for representing null data
|
||||
|
||||
use ::serialize::txt::*;
|
||||
use ::serialize::binary::*;
|
||||
use ::error::*;
|
||||
use serialize::txt::*;
|
||||
use serialize::binary::*;
|
||||
use error::*;
|
||||
|
||||
/// [RFC 1035, DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987](https://tools.ietf.org/html/rfc1035)
|
||||
///
|
||||
@ -43,21 +43,23 @@ pub struct NULL {
|
||||
}
|
||||
|
||||
impl NULL {
|
||||
/// Construct a new NULL RData
|
||||
pub fn new() -> NULL {
|
||||
NULL { anything: None }
|
||||
}
|
||||
|
||||
/// Constructs a new NULL RData with the associated data
|
||||
pub fn with(anything: Vec<u8>) -> NULL {
|
||||
NULL { anything: Some(anything) }
|
||||
}
|
||||
|
||||
/// Returns the buffer stored in the NULL
|
||||
pub fn anything(&self) -> Option<&Vec<u8>> {
|
||||
self.anything.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: length should be stored in the decoder, and guaranteed everywhere, right?
|
||||
// TODO: use this for unknown record types in caching...
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<NULL> {
|
||||
if rdata_length > 0 {
|
||||
let mut anything: Vec<u8> = Vec::with_capacity(rdata_length as usize);
|
||||
@ -75,6 +77,7 @@ pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<NULL> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the RData from the given Decoder
|
||||
pub fn emit(encoder: &mut BinEncoder, nil: &NULL) -> EncodeResult {
|
||||
if let Some(ref anything) = nil.anything() {
|
||||
for b in anything.iter() {
|
||||
@ -85,6 +88,7 @@ pub fn emit(encoder: &mut BinEncoder, nil: &NULL) -> EncodeResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parse the RData from a set of Tokens
|
||||
#[allow(unused)]
|
||||
pub fn parse(tokens: &Vec<Token>) -> ParseResult<NULL> {
|
||||
unimplemented!()
|
||||
|
@ -193,7 +193,7 @@ impl OPT {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<OPT> {
|
||||
let mut state: OptReadState = OptReadState::ReadCode;
|
||||
let mut options: HashMap<EdnsCode, EdnsOption> = HashMap::new();
|
||||
@ -240,6 +240,7 @@ pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<OPT> {
|
||||
Ok(OPT::new(options))
|
||||
}
|
||||
|
||||
/// Write the RData from the given Decoder
|
||||
pub fn emit(encoder: &mut BinEncoder, opt: &OPT) -> EncodeResult {
|
||||
for (ref edns_code, ref edns_option) in opt.options().iter() {
|
||||
try!(encoder.emit_u16(u16::from(**edns_code)));
|
||||
@ -262,6 +263,7 @@ enum OptReadState {
|
||||
}, // expect the data for the option
|
||||
}
|
||||
|
||||
/// The code of the EDNS data option
|
||||
#[derive(Hash, Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum EdnsCode {
|
||||
/// [RFC 6891, Reserved](https://tools.ietf.org/html/rfc6891)
|
||||
@ -373,6 +375,7 @@ pub enum EdnsOption {
|
||||
}
|
||||
|
||||
impl EdnsOption {
|
||||
/// Returns the length in bytes of the EdnsOption
|
||||
pub fn len(&self) -> u16 {
|
||||
match *self {
|
||||
EdnsOption::DAU(ref algorithms) |
|
||||
|
@ -424,6 +424,7 @@ impl SIG {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<SIG> {
|
||||
let start_idx = decoder.index();
|
||||
|
||||
|
@ -209,7 +209,7 @@ impl SOA {
|
||||
}
|
||||
}
|
||||
|
||||
// SOA { mname: Name, rname: Name, serial: u32, refresh: i32, retry: i32, expire: i32, minimum: u32, },
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder) -> DecodeResult<SOA> {
|
||||
Ok(SOA {
|
||||
mname: try!(Name::read(decoder)),
|
||||
@ -253,12 +253,7 @@ pub fn emit(encoder: &mut BinEncoder, soa: &SOA) -> EncodeResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// VENERA Action\.domains (
|
||||
// 20 ; SERIAL
|
||||
// 7200 ; REFRESH
|
||||
// 600 ; RETRY
|
||||
// 3600000; EXPIRE
|
||||
// 60) ; MINIMUM
|
||||
/// Parse the RData from a set of Tokens
|
||||
pub fn parse(tokens: &Vec<Token>, origin: Option<&Name>) -> ParseResult<SOA> {
|
||||
let mut token = tokens.iter();
|
||||
|
||||
|
@ -192,6 +192,7 @@ impl SRV {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder) -> DecodeResult<SRV> {
|
||||
// SRV { priority: u16, weight: u16, port: u16, target: Name, },
|
||||
Ok(SRV::new(try!(decoder.read_u16()),
|
||||
@ -228,7 +229,7 @@ pub fn emit(encoder: &mut BinEncoder, srv: &SRV) -> EncodeResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// _foobar._tcp SRV 0 1 9 old-slow-box.example.com.
|
||||
/// Parse the RData from a set of Tokens
|
||||
pub fn parse(tokens: &Vec<Token>, origin: Option<&Name>) -> ParseResult<SRV> {
|
||||
let mut token = tokens.iter();
|
||||
|
||||
|
@ -60,6 +60,7 @@ impl TXT {
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the RData from the given Decoder
|
||||
pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<TXT> {
|
||||
let data_len = decoder.len();
|
||||
let mut strings = Vec::with_capacity(1);
|
||||
@ -70,6 +71,7 @@ pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<TXT> {
|
||||
Ok(TXT::new(strings))
|
||||
}
|
||||
|
||||
/// Write the RData from the given Decoder
|
||||
pub fn emit(encoder: &mut BinEncoder, txt: &TXT) -> EncodeResult {
|
||||
for s in txt.txt_data() {
|
||||
try!(encoder.emit_character_data(s));
|
||||
@ -78,6 +80,7 @@ pub fn emit(encoder: &mut BinEncoder, txt: &TXT) -> EncodeResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parse the RData from a set of Tokens
|
||||
pub fn parse(tokens: &Vec<Token>) -> ParseResult<TXT> {
|
||||
let mut txt_data: Vec<String> = Vec::with_capacity(tokens.len());
|
||||
for t in tokens {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,51 +22,76 @@ use std::cmp::Ordering;
|
||||
use ::serialize::binary::*;
|
||||
use ::error::*;
|
||||
|
||||
/// The type of the resource record.
|
||||
///
|
||||
/// This specifies the type of data in the RData field of the Resource Record
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub enum RecordType {
|
||||
A, // 1 RFC 1035[1] IPv4 Address record
|
||||
AAAA, // 28 RFC 3596[2] IPv6 address record
|
||||
/// RFC 1035[1] IPv4 Address record
|
||||
A,
|
||||
/// RFC 3596[2] IPv6 address record
|
||||
AAAA,
|
||||
// AFSDB, // 18 RFC 1183 AFS database record
|
||||
ANY, // * 255 RFC 1035[1] All cached records, aka ANY
|
||||
/// RFC 1035[1] All cached records, aka ANY
|
||||
ANY,
|
||||
// APL, // 42 RFC 3123 Address Prefix List
|
||||
AXFR, // 252 RFC 1035[1] Authoritative Zone Transfer
|
||||
/// RFC 1035[1] Authoritative Zone Transfer
|
||||
AXFR,
|
||||
// CAA, // 257 RFC 6844 Certification Authority Authorization
|
||||
// CDNSKEY, // 60 RFC 7344 Child DNSKEY
|
||||
// CDS, // 59 RFC 7344 Child DS
|
||||
// CERT, // 37 RFC 4398 Certificate record
|
||||
CNAME, // 5 RFC 1035[1] Canonical name record
|
||||
/// RFC 1035[1] Canonical name record
|
||||
CNAME,
|
||||
// DHCID, // 49 RFC 4701 DHCP identifier
|
||||
// DLV, // 32769 RFC 4431 DNSSEC Lookaside Validation record
|
||||
// DNAME, // 39 RFC 2672 Delegation Name
|
||||
DNSKEY, // 48 RFC 4034 DNS Key record: RSASHA256 and RSASHA512, RFC5702
|
||||
DS, // 43 RFC 4034 Delegation signer: RSASHA256 and RSASHA512, RFC5702
|
||||
/// RFC 4034 DNS Key record: RSASHA256 and RSASHA512, RFC5702
|
||||
DNSKEY,
|
||||
/// RFC 4034 Delegation signer: RSASHA256 and RSASHA512, RFC5702
|
||||
DS,
|
||||
// HIP, // 55 RFC 5205 Host Identity Protocol
|
||||
// IPSECKEY, // 45 RFC 4025 IPsec Key
|
||||
IXFR, // 251 RFC 1996 Incremental Zone Transfer
|
||||
KEY, // 25 RFC 2535[3] and RFC 2930[4] Key record
|
||||
/// RFC 1996 Incremental Zone Transfer
|
||||
IXFR,
|
||||
/// RFC 2535[3] and RFC 2930[4] Key record
|
||||
KEY,
|
||||
// KX, // 36 RFC 2230 Key eXchanger record
|
||||
// LOC, // 29 RFC 1876 Location record
|
||||
MX, // 15 RFC 1035[1] Mail exchange record
|
||||
/// RFC 1035[1] Mail exchange record
|
||||
MX,
|
||||
// NAPTR, // 35 RFC 3403 Naming Authority Pointer
|
||||
NS, // 2 RFC 1035[1] Name server record
|
||||
NULL, // 0 RFC 1035[1] Null server record, for testing
|
||||
NSEC, // 47 RFC 4034 Next-Secure record
|
||||
NSEC3, // 50 RFC 5155 NSEC record version 3
|
||||
NSEC3PARAM, // 51 RFC 5155 NSEC3 parameters
|
||||
OPT, // 41 RFC 6891 Option
|
||||
PTR, // 12 RFC 1035[1] Pointer record
|
||||
RRSIG, // 46 RFC 4034 DNSSEC signature: RSASHA256 and RSASHA512, RFC5702
|
||||
/// RFC 1035[1] Name server record
|
||||
NS,
|
||||
/// RFC 1035[1] Null server record, for testing
|
||||
NULL,
|
||||
/// RFC 4034 Next-Secure record
|
||||
NSEC,
|
||||
/// RFC 5155 NSEC record version 3
|
||||
NSEC3,
|
||||
/// RFC 5155 NSEC3 parameters
|
||||
NSEC3PARAM,
|
||||
/// RFC 6891 Option
|
||||
OPT,
|
||||
/// RFC 1035[1] Pointer record
|
||||
PTR,
|
||||
/// RFC 4034 DNSSEC signature: RSASHA256 and RSASHA512, RFC5702
|
||||
RRSIG,
|
||||
// RP, // 17 RFC 1183 Responsible person
|
||||
SIG, // 24 RFC 2535 (2931) Signature, to support 2137 Update
|
||||
SOA, // 6 RFC 1035[1] and RFC 2308[9] Start of [a zone of] authority record
|
||||
SRV, // 33 RFC 2782 Service locator
|
||||
/// RFC 2535 (2931) Signature, to support 2137 Update
|
||||
SIG,
|
||||
/// RFC 1035[1] and RFC 2308[9] Start of [a zone of] authority record
|
||||
SOA,
|
||||
/// RFC 2782 Service locator
|
||||
SRV,
|
||||
// SSHFP, // 44 RFC 4255 SSH Public Key Fingerprint
|
||||
// TA, // 32768 N/A DNSSEC Trust Authorities
|
||||
// TKEY, // 249 RFC 2930 Secret key record
|
||||
// TLSA, // 52 RFC 6698 TLSA certificate association
|
||||
// TSIG, // 250 RFC 2845 Transaction Signature
|
||||
TXT, // 16 RFC 1035[1] Text record
|
||||
/// RFC 1035[1] Text record
|
||||
TXT,
|
||||
}
|
||||
|
||||
impl RecordType {
|
||||
|
@ -19,8 +19,8 @@
|
||||
use std::sync::Arc as Rc;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use ::serialize::binary::*;
|
||||
use ::error::*;
|
||||
use serialize::binary::*;
|
||||
use error::*;
|
||||
use rr::dns_class::DNSClass;
|
||||
use rr::domain;
|
||||
use rr::IntoRecordSet;
|
||||
@ -133,6 +133,8 @@ impl Record {
|
||||
self.name_labels = name;
|
||||
self
|
||||
}
|
||||
|
||||
/// Appends a label to a name
|
||||
pub fn add_name(&mut self, label: String) -> &mut Self {
|
||||
self.name_labels.add_label(Rc::new(label));
|
||||
self
|
||||
@ -181,24 +183,37 @@ impl Record {
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the name of the record
|
||||
pub fn name(&self) -> &domain::Name {
|
||||
&self.name_labels
|
||||
}
|
||||
|
||||
/// Returns the type of the RData in the record
|
||||
pub fn rr_type(&self) -> RecordType {
|
||||
self.rr_type
|
||||
}
|
||||
|
||||
/// Returns the DNSClass of the Record, generally IN fro internet
|
||||
pub fn dns_class(&self) -> DNSClass {
|
||||
self.dns_class
|
||||
}
|
||||
|
||||
/// Returns the time-to-live of the record, for caching purposes
|
||||
pub fn ttl(&self) -> u32 {
|
||||
self.ttl
|
||||
}
|
||||
|
||||
/// Returns the Record Data, i.e. the record information
|
||||
pub fn rdata(&self) -> &RData {
|
||||
&self.rdata
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the Record Data
|
||||
pub fn rdata_mut(&mut self) -> &mut RData {
|
||||
&mut self.rdata
|
||||
}
|
||||
|
||||
/// Returns the RData consuming the Record
|
||||
pub fn unwrap_rdata(self) -> RData {
|
||||
self.rdata
|
||||
}
|
||||
@ -262,12 +277,12 @@ impl BinSerializable<Record> for Record {
|
||||
};
|
||||
|
||||
Ok(Record {
|
||||
name_labels: name_labels,
|
||||
rr_type: record_type,
|
||||
dns_class: class,
|
||||
ttl: ttl,
|
||||
rdata: rdata,
|
||||
})
|
||||
name_labels: name_labels,
|
||||
rr_type: record_type,
|
||||
dns_class: class,
|
||||
ttl: ttl,
|
||||
rdata: rdata,
|
||||
})
|
||||
}
|
||||
|
||||
fn emit(&self, encoder: &mut BinEncoder) -> EncodeResult {
|
||||
@ -389,7 +404,7 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
#[allow(unused)]
|
||||
use ::serialize::binary::*;
|
||||
use serialize::binary::*;
|
||||
use rr::record_data::RData;
|
||||
use rr::record_type::RecordType;
|
||||
use rr::dns_class::DNSClass;
|
||||
@ -399,7 +414,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_emit_and_read() {
|
||||
let mut record = Record::new();
|
||||
record.add_name("www".to_string())
|
||||
record
|
||||
.add_name("www".to_string())
|
||||
.add_name("example".to_string())
|
||||
.add_name("com".to_string())
|
||||
.set_rr_type(RecordType::A)
|
||||
@ -423,7 +439,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_order() {
|
||||
let mut record = Record::new();
|
||||
record.add_name("www".to_string())
|
||||
record
|
||||
.add_name("www".to_string())
|
||||
.add_name("example".to_string())
|
||||
.add_name("com".to_string())
|
||||
.set_rr_type(RecordType::A)
|
||||
|
@ -5,7 +5,9 @@ use rr::{Name, RecordType};
|
||||
/// Accessor key for RRSets in the Authority.
|
||||
#[derive(Eq, PartialEq, Debug, Hash, Clone)]
|
||||
pub struct RrKey {
|
||||
/// Matches the name in the Record of this key
|
||||
pub name: Name,
|
||||
/// Matches the type of the Record of this key
|
||||
pub record_type: RecordType,
|
||||
}
|
||||
|
||||
|
@ -160,15 +160,15 @@ impl RecordSet {
|
||||
let rrsigs = self.rrsigs
|
||||
.iter()
|
||||
.filter(|record| if let &RData::SIG(ref rrsig) = record.rdata() {
|
||||
supported_algorithms.has(rrsig.algorithm())
|
||||
} else {
|
||||
false
|
||||
})
|
||||
supported_algorithms.has(rrsig.algorithm())
|
||||
} else {
|
||||
false
|
||||
})
|
||||
.max_by_key(|record| if let &RData::SIG(ref rrsig) = record.rdata() {
|
||||
rrsig.algorithm()
|
||||
} else {
|
||||
Algorithm::RSASHA1
|
||||
});
|
||||
rrsig.algorithm()
|
||||
} else {
|
||||
Algorithm::RSASHA1
|
||||
});
|
||||
self.records.iter().chain(rrsigs).collect()
|
||||
} else {
|
||||
self.records.iter().collect()
|
||||
@ -190,14 +190,23 @@ impl RecordSet {
|
||||
self.serial
|
||||
}
|
||||
|
||||
/// Returns a slice of all the Records signatures in the RecordSet
|
||||
pub fn rrsigs(&self) -> &[Record] {
|
||||
&self.rrsigs
|
||||
}
|
||||
|
||||
/// Inserts a Signature for the Record set
|
||||
///
|
||||
/// Many can be associated with the RecordSet. Once added, the RecordSet should not be changed
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `rrsig` - A signature which covers the RecordSet.
|
||||
pub fn insert_rrsig(&mut self, rrsig: Record) {
|
||||
self.rrsigs.push(rrsig)
|
||||
}
|
||||
|
||||
/// Useful for clearing all signatures when the RecordSet is updated, or keys are rotated.
|
||||
pub fn clear_rrsigs(&mut self) {
|
||||
self.rrsigs.clear()
|
||||
}
|
||||
@ -215,7 +224,10 @@ impl RecordSet {
|
||||
record.set_rdata(rdata.clone()); // TODO: remove clone()? this is only needed for the record return
|
||||
self.insert(record, 0);
|
||||
|
||||
self.records.iter().find(|r| *r.rdata() == rdata).expect("insert failed? 172")
|
||||
self.records
|
||||
.iter()
|
||||
.find(|r| *r.rdata() == rdata)
|
||||
.expect("insert failed? 172")
|
||||
}
|
||||
|
||||
/// Inserts a new Resource Record into the Set.
|
||||
@ -348,8 +360,7 @@ impl RecordSet {
|
||||
/// True if a record was removed.
|
||||
pub fn remove(&mut self, record: &Record, serial: u32) -> bool {
|
||||
assert_eq!(record.name(), &self.name);
|
||||
assert!(record.rr_type() == self.record_type ||
|
||||
record.rr_type() == RecordType::ANY);
|
||||
assert!(record.rr_type() == self.record_type || record.rr_type() == RecordType::ANY);
|
||||
|
||||
match record.rr_type() {
|
||||
// never delete the last NS record
|
||||
@ -386,7 +397,9 @@ impl RecordSet {
|
||||
}
|
||||
}
|
||||
|
||||
/// Types which implement this can be converted into a RecordSet
|
||||
pub trait IntoRecordSet: Sized {
|
||||
/// Performs the conversion to a RecordSet
|
||||
fn into_record_set(self) -> RecordSet;
|
||||
}
|
||||
|
||||
@ -408,7 +421,7 @@ impl IntoIterator for RecordSet {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::net::Ipv4Addr;
|
||||
use ::rr::*;
|
||||
use rr::*;
|
||||
use rr::rdata::SOA;
|
||||
|
||||
#[test]
|
||||
@ -427,12 +440,16 @@ mod test {
|
||||
|
||||
assert!(rr_set.insert(insert.clone(), 0));
|
||||
assert_eq!(rr_set.records(false, Default::default()).len(), 1);
|
||||
assert!(rr_set.records(false, Default::default()).contains(&&insert));
|
||||
assert!(rr_set
|
||||
.records(false, Default::default())
|
||||
.contains(&&insert));
|
||||
|
||||
// dups ignored
|
||||
assert!(!rr_set.insert(insert.clone(), 0));
|
||||
assert_eq!(rr_set.records(false, Default::default()).len(), 1);
|
||||
assert!(rr_set.records(false, Default::default()).contains(&&insert));
|
||||
assert!(rr_set
|
||||
.records(false, Default::default())
|
||||
.contains(&&insert));
|
||||
|
||||
// add one
|
||||
let insert1 = Record::new()
|
||||
@ -444,8 +461,12 @@ mod test {
|
||||
.clone();
|
||||
assert!(rr_set.insert(insert1.clone(), 0));
|
||||
assert_eq!(rr_set.records(false, Default::default()).len(), 2);
|
||||
assert!(rr_set.records(false, Default::default()).contains(&&insert));
|
||||
assert!(rr_set.records(false, Default::default()).contains(&&insert1));
|
||||
assert!(rr_set
|
||||
.records(false, Default::default())
|
||||
.contains(&&insert));
|
||||
assert!(rr_set
|
||||
.records(false, Default::default())
|
||||
.contains(&&insert1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -495,19 +516,31 @@ mod test {
|
||||
.clone();
|
||||
|
||||
assert!(rr_set.insert(insert.clone(), 0));
|
||||
assert!(rr_set.records(false, Default::default()).contains(&&insert));
|
||||
assert!(rr_set
|
||||
.records(false, Default::default())
|
||||
.contains(&&insert));
|
||||
// same serial number
|
||||
assert!(!rr_set.insert(same_serial.clone(), 0));
|
||||
assert!(rr_set.records(false, Default::default()).contains(&&insert));
|
||||
assert!(!rr_set.records(false, Default::default()).contains(&&same_serial));
|
||||
assert!(rr_set
|
||||
.records(false, Default::default())
|
||||
.contains(&&insert));
|
||||
assert!(!rr_set
|
||||
.records(false, Default::default())
|
||||
.contains(&&same_serial));
|
||||
|
||||
assert!(rr_set.insert(new_serial.clone(), 0));
|
||||
assert!(!rr_set.insert(same_serial.clone(), 0));
|
||||
assert!(!rr_set.insert(insert.clone(), 0));
|
||||
|
||||
assert!(rr_set.records(false, Default::default()).contains(&&new_serial));
|
||||
assert!(!rr_set.records(false, Default::default()).contains(&&insert));
|
||||
assert!(!rr_set.records(false, Default::default()).contains(&&same_serial));
|
||||
assert!(rr_set
|
||||
.records(false, Default::default())
|
||||
.contains(&&new_serial));
|
||||
assert!(!rr_set
|
||||
.records(false, Default::default())
|
||||
.contains(&&insert));
|
||||
assert!(!rr_set
|
||||
.records(false, Default::default())
|
||||
.contains(&&same_serial));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -535,12 +568,18 @@ mod test {
|
||||
.clone();
|
||||
|
||||
assert!(rr_set.insert(insert.clone(), 0));
|
||||
assert!(rr_set.records(false, Default::default()).contains(&&insert));
|
||||
assert!(rr_set
|
||||
.records(false, Default::default())
|
||||
.contains(&&insert));
|
||||
|
||||
// update the record
|
||||
assert!(rr_set.insert(new_record.clone(), 0));
|
||||
assert!(!rr_set.records(false, Default::default()).contains(&&insert));
|
||||
assert!(rr_set.records(false, Default::default()).contains(&&new_record));
|
||||
assert!(!rr_set
|
||||
.records(false, Default::default())
|
||||
.contains(&&insert));
|
||||
assert!(rr_set
|
||||
.records(false, Default::default())
|
||||
.contains(&&new_record));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -595,7 +634,9 @@ mod test {
|
||||
|
||||
assert!(rr_set.insert(insert.clone(), 0));
|
||||
assert!(!rr_set.remove(&insert, 0));
|
||||
assert!(rr_set.records(false, Default::default()).contains(&&insert));
|
||||
assert!(rr_set
|
||||
.records(false, Default::default())
|
||||
.contains(&&insert));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -719,22 +760,24 @@ mod test {
|
||||
rrset.insert_rrsig(rrsig_ecp384);
|
||||
rrset.insert_rrsig(rrsig_ed25519);
|
||||
|
||||
assert!(rrset.records(true, SupportedAlgorithms::all())
|
||||
.iter()
|
||||
.any(|r| if let &RData::SIG(ref sig) = r.rdata() {
|
||||
sig.algorithm() == Algorithm::ED25519
|
||||
} else {
|
||||
false
|
||||
}));
|
||||
assert!(rrset
|
||||
.records(true, SupportedAlgorithms::all())
|
||||
.iter()
|
||||
.any(|r| if let &RData::SIG(ref sig) = r.rdata() {
|
||||
sig.algorithm() == Algorithm::ED25519
|
||||
} else {
|
||||
false
|
||||
}));
|
||||
|
||||
let mut supported_algorithms = SupportedAlgorithms::new();
|
||||
supported_algorithms.set(Algorithm::ECDSAP384SHA384);
|
||||
assert!(rrset.records(true, supported_algorithms)
|
||||
.iter()
|
||||
.any(|r| if let &RData::SIG(ref sig) = r.rdata() {
|
||||
sig.algorithm() == Algorithm::ECDSAP384SHA384
|
||||
} else {
|
||||
false
|
||||
}));
|
||||
assert!(rrset
|
||||
.records(true, supported_algorithms)
|
||||
.iter()
|
||||
.any(|r| if let &RData::SIG(ref sig) = r.rdata() {
|
||||
sig.algorithm() == Algorithm::ECDSAP384SHA384
|
||||
} else {
|
||||
false
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -18,16 +18,21 @@ use error::{DecodeErrorKind, DecodeResult};
|
||||
/// This is non-destructive to the inner buffer, b/c for pointer types we need to perform a reverse
|
||||
/// seek to lookup names
|
||||
///
|
||||
/// A note on serialization, there was a thought to have this implement the rustc-serialization,
|
||||
/// A note on serialization, there was a thought to have this implement the Serde deserializer,
|
||||
/// but given that this is such a small subset of all the serialization which that performs
|
||||
/// this is a simpler implementation without the cruft, at least for serializing to/from the
|
||||
/// binary DNS protocols. rustc-serialization will be used for other coms, e.g. json over http
|
||||
/// binary DNS protocols.
|
||||
pub struct BinDecoder<'a> {
|
||||
buffer: &'a [u8],
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'a> BinDecoder<'a> {
|
||||
/// Creates a new BinDecoder
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `buffer` - buffer from which all data will be read
|
||||
pub fn new(buffer: &'a [u8]) -> Self {
|
||||
BinDecoder {
|
||||
buffer: buffer,
|
||||
@ -35,6 +40,7 @@ impl<'a> BinDecoder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pop one byte from the buffer
|
||||
pub fn pop(&mut self) -> DecodeResult<u8> {
|
||||
if self.index < self.buffer.len() {
|
||||
let byte = self.buffer[self.index];
|
||||
@ -45,10 +51,12 @@ impl<'a> BinDecoder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of bytes in the buffer
|
||||
pub fn len(&self) -> usize {
|
||||
self.buffer.len() - self.index
|
||||
}
|
||||
|
||||
/// Peed one byte forward, without moving the current index forward
|
||||
pub fn peek(&self) -> Option<u8> {
|
||||
if self.index < self.buffer.len() {
|
||||
Some(self.buffer[self.index])
|
||||
@ -57,6 +65,7 @@ impl<'a> BinDecoder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current index in the buffer
|
||||
pub fn index(&self) -> usize {
|
||||
return self.index;
|
||||
}
|
||||
@ -70,12 +79,18 @@ impl<'a> BinDecoder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
///<character-string> is a single
|
||||
/// Reads a String from the buffer
|
||||
///
|
||||
/// ```text
|
||||
/// <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).
|
||||
/// ```
|
||||
///
|
||||
/// the vector should be reversed before calling.
|
||||
/// # Returns
|
||||
///
|
||||
/// A String version of the character data
|
||||
pub fn read_character_data(&mut self) -> DecodeResult<String> {
|
||||
let length: u8 = try!(self.pop());
|
||||
|
||||
@ -88,6 +103,15 @@ impl<'a> BinDecoder<'a> {
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
/// Reads a Vec out of the buffer
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `len` - number of bytes to read from the buffer
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The Vec of the specified length, otherwise an error
|
||||
pub fn read_vec(&mut self, len: usize) -> DecodeResult<Vec<u8>> {
|
||||
// TODO once Drain stabalizes on Vec, this should be replaced...
|
||||
let mut vec: Vec<u8> = Vec::with_capacity(len);
|
||||
@ -98,14 +122,19 @@ impl<'a> BinDecoder<'a> {
|
||||
Ok(vec)
|
||||
}
|
||||
|
||||
/// Reads a byte from the buffer, equivalent to `Self::pop()`
|
||||
pub fn read_u8(&mut self) -> DecodeResult<u8> {
|
||||
self.pop()
|
||||
}
|
||||
|
||||
/// parses the next 2 bytes into u16. This performs a byte-by-byte manipulation, there
|
||||
/// Reads 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)
|
||||
///
|
||||
/// the vector should be reversed before calling.
|
||||
/// # Return
|
||||
///
|
||||
/// Return the u16 from the buffer
|
||||
pub fn read_u16(&mut self) -> DecodeResult<u16> {
|
||||
let b1: u8 = try!(self.pop());
|
||||
let b2: u8 = try!(self.pop());
|
||||
@ -114,10 +143,14 @@ impl<'a> BinDecoder<'a> {
|
||||
Ok(((b1 as u16) << 8) + (b2 as u16))
|
||||
}
|
||||
|
||||
/// parses the next four bytes into i32. This performs a byte-by-byte manipulation, there
|
||||
/// Reads 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)
|
||||
///
|
||||
/// the vector should be reversed before calling.
|
||||
/// # Return
|
||||
///
|
||||
/// Return the i32 from the buffer
|
||||
pub fn read_i32(&mut self) -> DecodeResult<i32> {
|
||||
// TODO should this use a default rather than the panic! that will happen in the None case?
|
||||
let b1: u8 = try!(self.pop());
|
||||
@ -129,10 +162,14 @@ impl<'a> BinDecoder<'a> {
|
||||
Ok(((b1 as i32) << 24) + ((b2 as i32) << 16) + ((b3 as i32) << 8) + (b4 as i32))
|
||||
}
|
||||
|
||||
/// parses the next four bytes into u32. This performs a byte-by-byte manipulation, there
|
||||
/// Reads the next four bytes into u32.
|
||||
///
|
||||
/// This performs a byte-by-byte manipulation, there
|
||||
/// which means endianness is implicitly handled (i.e. no network to little endian (intel), issues)
|
||||
///
|
||||
/// the vector should be reversed before calling.
|
||||
/// # Return
|
||||
///
|
||||
/// Return the u32 from the buffer
|
||||
pub fn read_u32(&mut self) -> DecodeResult<u32> {
|
||||
// TODO should this use a default rather than the panic! that will happen in the None case?
|
||||
let b1: u8 = try!(self.pop());
|
||||
|
@ -30,17 +30,29 @@ pub struct BinEncoder<'a> {
|
||||
}
|
||||
|
||||
impl<'a> BinEncoder<'a> {
|
||||
/// Create a new encoder with the Vec to fill
|
||||
pub fn new(buf: &'a mut Vec<u8>) -> Self {
|
||||
Self::with_offset(buf, 0, EncodeMode::Normal)
|
||||
}
|
||||
|
||||
/// Specify the mode for encoding
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `mode` - In Signing mode, it canonical forms of all data are encoded, otherwise format matches the source form
|
||||
pub fn with_mode(buf: &'a mut Vec<u8>, mode: EncodeMode) -> Self {
|
||||
Self::with_offset(buf, 0, mode)
|
||||
}
|
||||
|
||||
/// offset is used mainly for pointers. If this encoder is starting at some point further in
|
||||
/// Begins the encoder at the given offset
|
||||
///
|
||||
/// This is used for pointers. If this encoder is starting at some point further in
|
||||
/// the sequence of bytes, for the proper offset of the pointer, the offset accounts for that
|
||||
/// by using the offset to add to the pointer location being written.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `offset` - index at which to start writing into the buffer
|
||||
pub fn with_offset(buf: &'a mut Vec<u8>, offset: u32, mode: EncodeMode) -> Self {
|
||||
BinEncoder {
|
||||
offset: offset,
|
||||
@ -51,48 +63,59 @@ impl<'a> BinEncoder<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the internal buffer
|
||||
pub fn as_bytes(self) -> &'a Vec<u8> {
|
||||
self.buffer
|
||||
}
|
||||
|
||||
/// Returns the length of the buffer
|
||||
pub fn len(&self) -> usize {
|
||||
self.buffer.len()
|
||||
}
|
||||
|
||||
/// Returns the current offset into the buffer
|
||||
pub fn offset(&self) -> u32 {
|
||||
self.offset
|
||||
}
|
||||
|
||||
/// Returns the current Encoding mode
|
||||
pub fn mode(&self) -> EncodeMode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
/// If set to true, then names will be written into the buffer in canonical form
|
||||
pub fn set_canonical_names(&mut self, canonical_names: bool) {
|
||||
self.canonical_names = canonical_names;
|
||||
}
|
||||
|
||||
/// Returns true if then encoder is writing in canonical form
|
||||
pub fn is_canonical_names(&self) -> bool {
|
||||
self.canonical_names
|
||||
}
|
||||
|
||||
/// Reserve specified length in the internal buffer
|
||||
pub fn reserve(&mut self, extra: usize) {
|
||||
self.buffer.reserve(extra);
|
||||
}
|
||||
|
||||
/// Emit one byte into the buffer
|
||||
pub fn emit(&mut self, b: u8) -> EncodeResult {
|
||||
self.offset += 1;
|
||||
self.buffer.push(b);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// store the label pointer, the location is the current position in the buffer
|
||||
/// implicitly, it is expected that the name will be written to the stream after this.
|
||||
/// Stores a label pointer to an already written label
|
||||
///
|
||||
/// The location is the current position in the buffer
|
||||
/// implicitly, it is expected that the name will be written to the stream after the current index.
|
||||
pub fn store_label_pointer(&mut self, labels: Vec<Rc<String>>) {
|
||||
if self.offset < 0x3FFFu32 {
|
||||
self.name_pointers.insert(labels, self.offset as u16); // the next char will be at the len() location
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks up the index of an already written label
|
||||
pub fn get_label_pointer(&self, labels: &[Rc<String>]) -> Option<u16> {
|
||||
self.name_pointers.get(labels).map(|i| *i)
|
||||
}
|
||||
@ -128,6 +151,7 @@ impl<'a> BinEncoder<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a u16 in network byte order to the buffer
|
||||
pub fn emit_u16(&mut self, data: u16) -> EncodeResult {
|
||||
self.buffer.reserve(2); // two bytes coming
|
||||
|
||||
@ -140,7 +164,7 @@ impl<'a> BinEncoder<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Writes an i32 in network byte order to the buffer
|
||||
pub fn emit_i32(&mut self, data: i32) -> EncodeResult {
|
||||
self.buffer.reserve(4); // four bytes coming...
|
||||
|
||||
@ -157,7 +181,7 @@ impl<'a> BinEncoder<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Writes an u32 in network byte order to the buffer
|
||||
pub fn emit_u32(&mut self, data: u32) -> EncodeResult {
|
||||
self.buffer.reserve(4); // four bytes coming...
|
||||
|
||||
@ -174,6 +198,7 @@ impl<'a> BinEncoder<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes the byte slice to the stream
|
||||
pub fn emit_vec(&mut self, data: &[u8]) -> EncodeResult {
|
||||
self.buffer.reserve(data.len());
|
||||
|
||||
@ -189,6 +214,8 @@ impl<'a> BinEncoder<'a> {
|
||||
/// should not be included in the additional count and not in the encoded data when in Verify
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum EncodeMode {
|
||||
/// In signing mode records are written in canonical form
|
||||
Signing,
|
||||
/// Write records in standard format
|
||||
Normal,
|
||||
}
|
||||
|
@ -13,6 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//! Binary serialization types
|
||||
|
||||
mod decoder;
|
||||
mod encoder;
|
||||
|
||||
@ -25,8 +28,12 @@ pub mod bin_tests;
|
||||
|
||||
use ::error::*;
|
||||
|
||||
/// A trait for types which are serializable
|
||||
pub trait BinSerializable<S: Sized> {
|
||||
/// Read the type from the stream
|
||||
fn read(decoder: &mut BinDecoder) -> DecodeResult<S>;
|
||||
|
||||
/// Write the type to the stream
|
||||
fn emit(&self, encoder: &mut BinEncoder) -> EncodeResult;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use ::error::*;
|
||||
use error::*;
|
||||
use rr::{Name, IntoRecordSet, RecordType, Record, DNSClass, RData, RrKey, RecordSet};
|
||||
|
||||
use super::master_lex::{Lexer, Token};
|
||||
@ -123,11 +123,16 @@ use super::master_lex::{Lexer, Token};
|
||||
pub struct Parser;
|
||||
|
||||
impl Parser {
|
||||
/// Returns a new Zone file parser
|
||||
pub fn new() -> Self {
|
||||
Parser
|
||||
}
|
||||
|
||||
// TODO: change this function to load into an Authority, using the update_records() method
|
||||
/// Parse a file from the Lexer
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// A pair of the Zone origin name and a map of all Keys to RecordSets
|
||||
pub fn parse(&mut self,
|
||||
lexer: Lexer,
|
||||
origin: Option<Name>)
|
||||
@ -280,12 +285,13 @@ impl Parser {
|
||||
if records.insert(key, set).is_some() {
|
||||
return Err(ParseErrorKind::Message("SOA is already \
|
||||
specified")
|
||||
.into());
|
||||
.into());
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// add a Vec if it's not there, then add the record to the list
|
||||
let mut set = records.entry(key)
|
||||
let mut set = records
|
||||
.entry(key)
|
||||
.or_insert(RecordSet::new(record.name(),
|
||||
record.rr_type(),
|
||||
0));
|
||||
|
@ -11,12 +11,14 @@ use std::char;
|
||||
|
||||
use error::{LexerResult, LexerError, LexerErrorKind};
|
||||
|
||||
/// A Lexer for Zone files
|
||||
pub struct Lexer<'a> {
|
||||
txt: Peekable<Chars<'a>>,
|
||||
state: State,
|
||||
}
|
||||
|
||||
impl<'a> Lexer<'a> {
|
||||
/// Creates a new lexer with the given data to parse
|
||||
pub fn new(txt: &str) -> Lexer {
|
||||
Lexer {
|
||||
txt: txt.chars().peekable(),
|
||||
@ -24,6 +26,7 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the next Token in the string
|
||||
pub fn next_token(&mut self) -> LexerResult<Option<Token>> {
|
||||
let mut char_data_vec: Option<Vec<String>> = None;
|
||||
let mut char_data: Option<String> = None;
|
||||
@ -122,7 +125,9 @@ impl<'a> Lexer<'a> {
|
||||
Some('"') => {
|
||||
self.state = State::RestOfLine;
|
||||
self.txt.next();
|
||||
return Ok(Some(Token::CharData(char_data.take().unwrap_or("".into()))));
|
||||
return Ok(Some(Token::CharData(char_data
|
||||
.take()
|
||||
.unwrap_or("".into()))));
|
||||
}
|
||||
Some('\\') => {
|
||||
try!(Self::push_to_str(&mut char_data, try!(self.escape_seq())));
|
||||
@ -144,7 +149,8 @@ impl<'a> Lexer<'a> {
|
||||
// finishes the Dollar...
|
||||
Some(_) | None => {
|
||||
self.state = State::RestOfLine;
|
||||
let dollar: String = try!(char_data.take()
|
||||
let dollar: String =
|
||||
try!(char_data.take()
|
||||
.ok_or(LexerError::from(LexerErrorKind::IllegalState("char_data \
|
||||
is None"))));
|
||||
|
||||
@ -171,10 +177,11 @@ impl<'a> Lexer<'a> {
|
||||
Some(')') => {
|
||||
self.txt.next();
|
||||
self.state = State::RestOfLine;
|
||||
return char_data_vec.take()
|
||||
.ok_or(LexerErrorKind::IllegalState("char_data_vec is None")
|
||||
.into())
|
||||
.map(|v| Some(Token::List(v)));
|
||||
return char_data_vec
|
||||
.take()
|
||||
.ok_or(LexerErrorKind::IllegalState("char_data_vec is None")
|
||||
.into())
|
||||
.map(|v| Some(Token::List(v)));
|
||||
}
|
||||
Some(ch) if ch.is_whitespace() => {
|
||||
self.txt.next();
|
||||
@ -198,9 +205,10 @@ impl<'a> Lexer<'a> {
|
||||
self.state = State::List;
|
||||
} else {
|
||||
self.state = State::RestOfLine;
|
||||
let result = char_data.take()
|
||||
let result = char_data
|
||||
.take()
|
||||
.ok_or(LexerErrorKind::IllegalState("char_data is None")
|
||||
.into());
|
||||
.into());
|
||||
let opt = result.map(|s| Some(Token::CharData(s)));
|
||||
return opt;
|
||||
}
|
||||
@ -214,9 +222,11 @@ impl<'a> Lexer<'a> {
|
||||
Some(ch) => return Err(LexerErrorKind::UnrecognizedChar(ch).into()),
|
||||
None => {
|
||||
self.state = State::EOF;
|
||||
return char_data.take()
|
||||
.ok_or(LexerErrorKind::IllegalState("char_data is None").into())
|
||||
.map(|s| Some(Token::CharData(s)));
|
||||
return char_data
|
||||
.take()
|
||||
.ok_or(LexerErrorKind::IllegalState("char_data is None")
|
||||
.into())
|
||||
.map(|s| Some(Token::CharData(s)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -252,7 +262,8 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
|
||||
fn push_to_str(collect: &mut Option<String>, ch: char) -> LexerResult<()> {
|
||||
collect.as_mut()
|
||||
collect
|
||||
.as_mut()
|
||||
.ok_or(LexerErrorKind::IllegalState("collect is None").into())
|
||||
.and_then(|s| Ok(s.push(ch)))
|
||||
}
|
||||
@ -285,8 +296,9 @@ impl<'a> Lexer<'a> {
|
||||
}))); // gobble
|
||||
|
||||
let val: u32 = (d1 << 16) + (d2 << 8) + d3;
|
||||
let ch: char = try!(char::from_u32(val)
|
||||
.ok_or(LexerError::from(LexerErrorKind::UnrecognizedOctet(val))));
|
||||
let ch: char =
|
||||
try!(char::from_u32(val)
|
||||
.ok_or(LexerError::from(LexerErrorKind::UnrecognizedOctet(val))));
|
||||
|
||||
return Ok(ch);
|
||||
} else {
|
||||
@ -321,16 +333,25 @@ pub enum State {
|
||||
EOF,
|
||||
}
|
||||
|
||||
/// Tokens emited from each Lexer pass
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Token {
|
||||
Blank, // only if the first part of the line
|
||||
List(Vec<String>), // (..) TODO, this is probably wrong, List maybe should just skip line endings
|
||||
CharData(String), // [a-zA-Z, non-control utf8, ., -, 0-9]+, ".*"
|
||||
At, // @
|
||||
Include, // $INCLUDE
|
||||
Origin, // $ORIGIN
|
||||
Ttl, // $TTL
|
||||
EOL, // \n or \r\n
|
||||
/// only if the first part of the line
|
||||
Blank,
|
||||
/// (..) TODO, this is probably wrong, List maybe should just skip line endings
|
||||
List(Vec<String>),
|
||||
/// [a-zA-Z, non-control utf8, ., -, 0-9]+, ".*"
|
||||
CharData(String),
|
||||
/// @
|
||||
At,
|
||||
/// $INCLUDE
|
||||
Include,
|
||||
/// $ORIGIN
|
||||
Origin,
|
||||
/// $TTL
|
||||
Ttl,
|
||||
/// \n or \r\n
|
||||
EOL,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -401,7 +422,10 @@ mod lex_test {
|
||||
Token::CharData("Quoted".to_string()));
|
||||
assert_eq!(Lexer::new("\";@$\"").next_token().unwrap().unwrap(),
|
||||
Token::CharData(";@$".to_string()));
|
||||
assert_eq!(Lexer::new("\"some \\A\"").next_token().unwrap().unwrap(),
|
||||
assert_eq!(Lexer::new("\"some \\A\"")
|
||||
.next_token()
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Token::CharData("some A".to_string()));
|
||||
assert_eq!(Lexer::new("\"a\\Aa\"").next_token().unwrap().unwrap(),
|
||||
Token::CharData("aAa".to_string()));
|
||||
@ -495,8 +519,7 @@ mod lex_test {
|
||||
|
||||
#[test]
|
||||
fn soa() {
|
||||
let mut lexer =
|
||||
Lexer::new("@ IN SOA VENERA Action\\.domains (
|
||||
let mut lexer = Lexer::new("@ IN SOA VENERA Action\\.domains (
|
||||
\
|
||||
20 ; SERIAL
|
||||
7200 ; REFRESH
|
||||
|
@ -13,6 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//! Text serialization types
|
||||
|
||||
mod master_lex;
|
||||
mod master;
|
||||
|
||||
|
@ -50,6 +50,7 @@ impl TcpClientStream<TokioTcpStream> {
|
||||
}
|
||||
|
||||
impl<S> TcpClientStream<S> {
|
||||
/// Wraps the TcpStream in TcpClientStream
|
||||
pub fn from_stream(tcp_stream: TcpStream<S>) -> Self {
|
||||
TcpClientStream { tcp_stream: tcp_stream }
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
//! This module contains all the TCP structures for demuxing TCP into streams of DNS packets.
|
||||
|
||||
use std::mem;
|
||||
use std::net::SocketAddr;
|
||||
use std::io;
|
||||
@ -18,21 +20,48 @@ use tokio_core::reactor::Handle;
|
||||
|
||||
use BufStreamHandle;
|
||||
|
||||
/// Current state while writing to the remote of the TCP connection
|
||||
enum WriteTcpState {
|
||||
/// Currently writing the length of bytes to of the buffer.
|
||||
LenBytes {
|
||||
/// Current position in the length buffer being written
|
||||
pos: usize,
|
||||
/// Length of the buffer
|
||||
length: [u8; 2],
|
||||
/// Buffer to write after the length
|
||||
bytes: Vec<u8>,
|
||||
},
|
||||
Bytes { pos: usize, bytes: Vec<u8> },
|
||||
/// Currently writing the buffer to the remote
|
||||
Bytes {
|
||||
/// Current position in the buffer written
|
||||
pos: usize,
|
||||
/// Buffer to write to the remote
|
||||
bytes: Vec<u8>,
|
||||
},
|
||||
/// Currently flushing the bytes to the remote
|
||||
Flushing,
|
||||
}
|
||||
|
||||
/// Current state of a TCP stream as it's being read.
|
||||
pub enum ReadTcpState {
|
||||
LenBytes { pos: usize, bytes: [u8; 2] },
|
||||
Bytes { pos: usize, bytes: Vec<u8> },
|
||||
/// Currently reading the length of the TCP packet
|
||||
LenBytes {
|
||||
/// Current position in the buffer
|
||||
pos: usize,
|
||||
/// Buffer of the length to read
|
||||
bytes: [u8; 2],
|
||||
},
|
||||
/// Currently reading the byts of the DNS packet
|
||||
Bytes {
|
||||
/// Current position while reading the buffer
|
||||
pos: usize,
|
||||
/// buffer being read into
|
||||
bytes: Vec<u8>,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
/// A Stream used for sending data to and from a remote DNS endpoint (client or server).
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
pub struct TcpStream<S> {
|
||||
socket: S,
|
||||
@ -43,6 +72,7 @@ pub struct TcpStream<S> {
|
||||
}
|
||||
|
||||
impl<S> TcpStream<S> {
|
||||
/// Returns the address of the peer connection.
|
||||
pub fn peer_addr(&self) -> SocketAddr {
|
||||
self.peer_addr
|
||||
}
|
||||
@ -130,11 +160,18 @@ impl<S: AsyncRead + AsyncWrite> Stream for TcpStream<S> {
|
||||
if self.send_state.is_some() {
|
||||
// sending...
|
||||
match self.send_state {
|
||||
Some(WriteTcpState::LenBytes { ref mut pos, ref length, .. }) => {
|
||||
Some(WriteTcpState::LenBytes {
|
||||
ref mut pos,
|
||||
ref length,
|
||||
..
|
||||
}) => {
|
||||
let wrote = try_nb!(self.socket.write(&length[*pos..]));
|
||||
*pos += wrote;
|
||||
}
|
||||
Some(WriteTcpState::Bytes { ref mut pos, ref bytes }) => {
|
||||
Some(WriteTcpState::Bytes {
|
||||
ref mut pos,
|
||||
ref bytes,
|
||||
}) => {
|
||||
let wrote = try_nb!(self.socket.write(&bytes[*pos..]));
|
||||
*pos += wrote;
|
||||
}
|
||||
@ -153,25 +190,25 @@ impl<S: AsyncRead + AsyncWrite> Stream for TcpStream<S> {
|
||||
if pos < length.len() {
|
||||
mem::replace(&mut self.send_state,
|
||||
Some(WriteTcpState::LenBytes {
|
||||
pos: pos,
|
||||
length: length,
|
||||
bytes: bytes,
|
||||
}));
|
||||
pos: pos,
|
||||
length: length,
|
||||
bytes: bytes,
|
||||
}));
|
||||
} else {
|
||||
mem::replace(&mut self.send_state,
|
||||
Some(WriteTcpState::Bytes {
|
||||
pos: 0,
|
||||
bytes: bytes,
|
||||
}));
|
||||
pos: 0,
|
||||
bytes: bytes,
|
||||
}));
|
||||
}
|
||||
}
|
||||
Some(WriteTcpState::Bytes { pos, bytes }) => {
|
||||
if pos < bytes.len() {
|
||||
mem::replace(&mut self.send_state,
|
||||
Some(WriteTcpState::Bytes {
|
||||
pos: pos,
|
||||
bytes: bytes,
|
||||
}));
|
||||
pos: pos,
|
||||
bytes: bytes,
|
||||
}));
|
||||
} else {
|
||||
// At this point we successfully delivered the entire message.
|
||||
// flush
|
||||
@ -220,7 +257,10 @@ impl<S: AsyncRead + AsyncWrite> Stream for TcpStream<S> {
|
||||
// Evaluates the next state. If None is the result, then no state change occurs,
|
||||
// if Some(_) is returned, then that will be used as the next state.
|
||||
let new_state: Option<ReadTcpState> = match self.read_state {
|
||||
ReadTcpState::LenBytes { ref mut pos, ref mut bytes } => {
|
||||
ReadTcpState::LenBytes {
|
||||
ref mut pos,
|
||||
ref mut bytes,
|
||||
} => {
|
||||
// debug!("reading length {}", bytes.len());
|
||||
let read = try_nb!(self.socket.read(&mut bytes[*pos..]));
|
||||
if read == 0 {
|
||||
@ -250,12 +290,15 @@ impl<S: AsyncRead + AsyncWrite> Stream for TcpStream<S> {
|
||||
|
||||
debug!("move ReadTcpState::Bytes: {}", bytes.len());
|
||||
Some(ReadTcpState::Bytes {
|
||||
pos: 0,
|
||||
bytes: bytes,
|
||||
})
|
||||
pos: 0,
|
||||
bytes: bytes,
|
||||
})
|
||||
}
|
||||
}
|
||||
ReadTcpState::Bytes { ref mut pos, ref mut bytes } => {
|
||||
ReadTcpState::Bytes {
|
||||
ref mut pos,
|
||||
ref mut bytes,
|
||||
} => {
|
||||
let read = try_nb!(self.socket.read(&mut bytes[*pos..]));
|
||||
if read == 0 {
|
||||
// the Stream was closed!
|
||||
@ -276,9 +319,9 @@ impl<S: AsyncRead + AsyncWrite> Stream for TcpStream<S> {
|
||||
} else {
|
||||
debug!("reset ReadTcpState::LenBytes: {}", 0);
|
||||
Some(ReadTcpState::LenBytes {
|
||||
pos: 0,
|
||||
bytes: [0u8; 2],
|
||||
})
|
||||
pos: 0,
|
||||
bytes: [0u8; 2],
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -367,32 +410,45 @@ fn tcp_client_stream_test(server_addr: IpAddr) {
|
||||
let send_recv_times = 4;
|
||||
|
||||
// an in and out server
|
||||
let server_handle = std::thread::Builder::new().name("test_tcp_client_stream:server".to_string()).spawn(move || {
|
||||
let (mut socket, _) = server.accept().expect("accept failed");
|
||||
let server_handle = std::thread::Builder::new()
|
||||
.name("test_tcp_client_stream:server".to_string())
|
||||
.spawn(move || {
|
||||
let (mut socket, _) = server.accept().expect("accept failed");
|
||||
|
||||
socket.set_read_timeout(Some(std::time::Duration::from_secs(5))).unwrap(); // should recieve something within 5 seconds...
|
||||
socket.set_write_timeout(Some(std::time::Duration::from_secs(5))).unwrap(); // should recieve something within 5 seconds...
|
||||
socket
|
||||
.set_read_timeout(Some(std::time::Duration::from_secs(5)))
|
||||
.unwrap(); // should recieve something within 5 seconds...
|
||||
socket
|
||||
.set_write_timeout(Some(std::time::Duration::from_secs(5)))
|
||||
.unwrap(); // should recieve something within 5 seconds...
|
||||
|
||||
for _ in 0..send_recv_times {
|
||||
// wait for some bytes...
|
||||
let mut len_bytes = [0_u8; 2];
|
||||
socket.read_exact(&mut len_bytes).expect("SERVER: receive failed");
|
||||
let length = (len_bytes[0] as u16) << 8 & 0xFF00 | len_bytes[1] as u16 & 0x00FF;
|
||||
assert_eq!(length as usize, TEST_BYTES_LEN);
|
||||
for _ in 0..send_recv_times {
|
||||
// wait for some bytes...
|
||||
let mut len_bytes = [0_u8; 2];
|
||||
socket
|
||||
.read_exact(&mut len_bytes)
|
||||
.expect("SERVER: receive failed");
|
||||
let length = (len_bytes[0] as u16) << 8 & 0xFF00 | len_bytes[1] as u16 & 0x00FF;
|
||||
assert_eq!(length as usize, TEST_BYTES_LEN);
|
||||
|
||||
let mut buffer = [0_u8; TEST_BYTES_LEN];
|
||||
socket.read_exact(&mut buffer).unwrap();
|
||||
let mut buffer = [0_u8; TEST_BYTES_LEN];
|
||||
socket.read_exact(&mut buffer).unwrap();
|
||||
|
||||
// println!("read bytes iter: {}", i);
|
||||
assert_eq!(&buffer, TEST_BYTES);
|
||||
// println!("read bytes iter: {}", i);
|
||||
assert_eq!(&buffer, TEST_BYTES);
|
||||
|
||||
// bounce them right back...
|
||||
socket.write_all(&len_bytes).expect("SERVER: send length failed");
|
||||
socket.write_all(&buffer).expect("SERVER: send buffer failed");
|
||||
// println!("wrote bytes iter: {}", i);
|
||||
std::thread::yield_now();
|
||||
}
|
||||
}).unwrap();
|
||||
// bounce them right back...
|
||||
socket
|
||||
.write_all(&len_bytes)
|
||||
.expect("SERVER: send length failed");
|
||||
socket
|
||||
.write_all(&buffer)
|
||||
.expect("SERVER: send buffer failed");
|
||||
// println!("wrote bytes iter: {}", i);
|
||||
std::thread::yield_now();
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// setup the client, which is going to run on the testing thread...
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
@ -402,13 +458,20 @@ fn tcp_client_stream_test(server_addr: IpAddr) {
|
||||
// let timeout = Timeout::new(Duration::from_secs(5), &io_loop.handle());
|
||||
let (stream, sender) = TcpStream::new(server_addr, io_loop.handle());
|
||||
|
||||
let mut stream = io_loop.run(stream).ok().expect("run failed to get stream");
|
||||
let mut stream = io_loop
|
||||
.run(stream)
|
||||
.ok()
|
||||
.expect("run failed to get stream");
|
||||
|
||||
for _ in 0..send_recv_times {
|
||||
// test once
|
||||
sender.send((TEST_BYTES.to_vec(), server_addr)).expect("send failed");
|
||||
let (buffer, stream_tmp) =
|
||||
io_loop.run(stream.into_future()).ok().expect("future iteration run failed");
|
||||
sender
|
||||
.send((TEST_BYTES.to_vec(), server_addr))
|
||||
.expect("send failed");
|
||||
let (buffer, stream_tmp) = io_loop
|
||||
.run(stream.into_future())
|
||||
.ok()
|
||||
.expect("future iteration run failed");
|
||||
stream = stream_tmp;
|
||||
let (buffer, _) = buffer.expect("no buffer received");
|
||||
assert_eq!(&buffer, TEST_BYTES);
|
||||
|
@ -35,6 +35,7 @@ pub struct TlsClientConnection {
|
||||
}
|
||||
|
||||
impl TlsClientConnection {
|
||||
/// Creates a new builder for the construction of a TlsClientConnection.
|
||||
pub fn builder() -> TlsClientConnectionBuilder {
|
||||
TlsClientConnectionBuilder(TlsClientStream::builder())
|
||||
}
|
||||
@ -43,11 +44,16 @@ impl TlsClientConnection {
|
||||
impl ClientConnection for TlsClientConnection {
|
||||
type MessageStream = TlsClientStream;
|
||||
|
||||
fn unwrap(self) -> (Core, Box<Future<Item=Self::MessageStream, Error=io::Error>>, Box<ClientStreamHandle>){
|
||||
fn unwrap
|
||||
(self)
|
||||
-> (Core,
|
||||
Box<Future<Item = Self::MessageStream, Error = io::Error>>,
|
||||
Box<ClientStreamHandle>) {
|
||||
(self.io_loop, self.tls_client_stream, self.client_stream_handle)
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder for the TlsClientStream.
|
||||
pub struct TlsClientConnectionBuilder(TlsClientStreamBuilder);
|
||||
|
||||
impl TlsClientConnectionBuilder {
|
||||
|
@ -21,14 +21,17 @@ use tcp::TcpClientStream;
|
||||
use tls::{TlsStream, TlsStreamBuilder};
|
||||
use client::ClientStreamHandle;
|
||||
|
||||
/// A Type definition for the TLS stream
|
||||
pub type TlsClientStream = TcpClientStream<TokioTlsStream<TokioTcpStream>>;
|
||||
|
||||
impl TlsClientStream {
|
||||
/// Creates a builder fo the construction of a TlsClientStream
|
||||
pub fn builder() -> TlsClientStreamBuilder {
|
||||
TlsClientStreamBuilder(TlsStream::builder())
|
||||
}
|
||||
}
|
||||
|
||||
/// A Builder for the TlsClientStream
|
||||
pub struct TlsClientStreamBuilder(TlsStreamBuilder);
|
||||
|
||||
impl TlsClientStreamBuilder {
|
||||
|
@ -52,6 +52,7 @@ impl TlsIdentityExt for SslContextBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// A TlsStream counterpart to the TcpStream which embeds a secure TlsStream
|
||||
pub type TlsStream = TcpStream<TokioTlsStream<TokioTcpStream>>;
|
||||
|
||||
impl TlsStream {
|
||||
@ -121,6 +122,7 @@ impl TlsStream {
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder for the TlsStream
|
||||
pub struct TlsStreamBuilder {
|
||||
ca_chain: Vec<X509>,
|
||||
identity: Option<ParsedPkcs12>,
|
||||
|
@ -15,6 +15,7 @@ use BufClientStreamHandle;
|
||||
use client::ClientStreamHandle;
|
||||
use udp::UdpStream;
|
||||
|
||||
/// A UDP client stream of DNS binary packets
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
pub struct UdpClientStream {
|
||||
name_server: SocketAddr,
|
||||
|
@ -25,6 +25,7 @@ lazy_static!{
|
||||
static ref IPV6_ZERO: Ipv6Addr = Ipv6Addr::new(0,0,0,0,0,0,0,0);
|
||||
}
|
||||
|
||||
/// A UDP stream of DNS binary packets
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
pub struct UdpStream {
|
||||
socket: tokio_core::net::UdpSocket,
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2015-2016 Benjamin Fry <benjaminfry@me.com>
|
||||
// Copyright 2015-2017 Benjamin Fry <benjaminfry@me.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
//! TLS protocol related components for DNS over TLS
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
extern crate futures;
|
||||
extern crate native_tls;
|
||||
extern crate tokio_core;
|
||||
|
@ -37,6 +37,7 @@ pub struct TlsClientConnection {
|
||||
}
|
||||
|
||||
impl TlsClientConnection {
|
||||
/// Creates a new builder for the construction of a TlsClientConnection.
|
||||
pub fn builder() -> TlsClientConnectionBuilder {
|
||||
TlsClientConnectionBuilder(TlsClientStreamBuilder::new())
|
||||
}
|
||||
@ -45,11 +46,16 @@ impl TlsClientConnection {
|
||||
impl ClientConnection for TlsClientConnection {
|
||||
type MessageStream = TlsClientStream;
|
||||
|
||||
fn unwrap(self) -> (Core, Box<Future<Item=Self::MessageStream, Error=io::Error>>, Box<ClientStreamHandle>){
|
||||
fn unwrap
|
||||
(self)
|
||||
-> (Core,
|
||||
Box<Future<Item = Self::MessageStream, Error = io::Error>>,
|
||||
Box<ClientStreamHandle>) {
|
||||
(self.io_loop, self.tls_client_stream, self.client_stream_handle)
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder for the TlsClientStream.
|
||||
pub struct TlsClientConnectionBuilder(TlsClientStreamBuilder);
|
||||
|
||||
impl TlsClientConnectionBuilder {
|
||||
|
@ -5,6 +5,8 @@
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
//! TlsClientStream for DNS over TLS
|
||||
|
||||
use std::net::SocketAddr;
|
||||
use std::io;
|
||||
|
||||
@ -22,11 +24,16 @@ use trust_dns::client::ClientStreamHandle;
|
||||
|
||||
use TlsStreamBuilder;
|
||||
|
||||
/// TlsClientStream secure DNS over TCP stream
|
||||
///
|
||||
/// See TlsClientStreamBuilder::new()
|
||||
pub type TlsClientStream = TcpClientStream<TokioTlsStream<TokioTcpStream>>;
|
||||
|
||||
/// Builder for TlsClientStream
|
||||
pub struct TlsClientStreamBuilder(TlsStreamBuilder);
|
||||
|
||||
impl TlsClientStreamBuilder {
|
||||
/// Creates a builder fo the construction of a TlsClientStream
|
||||
pub fn new() -> TlsClientStreamBuilder {
|
||||
TlsClientStreamBuilder(TlsStreamBuilder::new())
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
//! Base TlsStream
|
||||
|
||||
use std::net::SocketAddr;
|
||||
use std::io;
|
||||
|
||||
@ -19,6 +21,7 @@ use tokio_tls::{TlsConnectorExt, TlsStream as TokioTlsStream};
|
||||
use trust_dns::BufStreamHandle;
|
||||
use trust_dns::tcp::TcpStream;
|
||||
|
||||
/// A TlsStream counterpart to the TcpStream which embeds a secure TlsStream
|
||||
pub type TlsStream = TcpStream<TokioTlsStream<TokioTcpStream>>;
|
||||
|
||||
fn tls_new(certs: Vec<Certificate>, pkcs12: Option<Pkcs12>) -> io::Result<TlsConnector> {
|
||||
@ -71,6 +74,7 @@ pub fn tls_from_stream(stream: TokioTlsStream<TokioTcpStream>,
|
||||
(stream, message_sender)
|
||||
}
|
||||
|
||||
/// A builder for the TlsStream
|
||||
pub struct TlsStreamBuilder {
|
||||
ca_chain: Vec<Certificate>,
|
||||
identity: Option<Pkcs12>,
|
||||
|
@ -26,6 +26,7 @@ fn main() {
|
||||
let mut f = File::create(&dest_path).unwrap();
|
||||
|
||||
|
||||
f.write_all(b"/// Returns the current version of TRust-DNS\n").unwrap();
|
||||
f.write_all(b"pub fn version() -> &'static str {").unwrap();
|
||||
write!(f, " \"{}\" ", version).unwrap();
|
||||
f.write_all(b" }").unwrap();
|
||||
|
@ -13,6 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
//! All authority related types
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use chrono::UTC;
|
||||
@ -88,7 +90,7 @@ impl Authority {
|
||||
/// * `signer` - Signer with associated private key
|
||||
pub fn add_secure_key(&mut self, signer: Signer) -> DnsSecResult<()> {
|
||||
// also add the key to the zone
|
||||
let zone_ttl = self.get_minimum_ttl();
|
||||
let zone_ttl = self.minimum_ttl();
|
||||
let dnskey = try!(signer.key().to_dnskey(signer.algorithm()));
|
||||
let dnskey = Record::from_rdata(self.origin.clone(),
|
||||
zone_ttl,
|
||||
@ -96,7 +98,7 @@ impl Authority {
|
||||
RData::DNSKEY(dnskey));
|
||||
|
||||
// TODO: also generate the CDS and CDNSKEY
|
||||
let serial = self.get_serial();
|
||||
let serial = self.serial();
|
||||
self.upsert(dnskey, serial);
|
||||
self.secure_keys.push(signer);
|
||||
Ok(())
|
||||
@ -142,7 +144,7 @@ impl Authority {
|
||||
/// Returns an error if there was an issue writing to the persistence layer.
|
||||
pub fn persist_to_journal(&self) -> PersistenceResult<()> {
|
||||
if let Some(journal) = self.journal.as_ref() {
|
||||
let serial = self.get_serial();
|
||||
let serial = self.serial();
|
||||
|
||||
info!("persisting zone to journal at SOA.serial: {}", serial);
|
||||
|
||||
@ -162,11 +164,13 @@ impl Authority {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn journal(&mut self, journal: Journal) {
|
||||
/// Associate a backing Journal with this Authority for Updatable zones
|
||||
pub fn set_journal(&mut self, journal: Journal) {
|
||||
self.journal = Some(journal);
|
||||
}
|
||||
|
||||
pub fn get_journal(&self) -> Option<&Journal> {
|
||||
/// Returns the associated Journal
|
||||
pub fn journal(&self) -> Option<&Journal> {
|
||||
self.journal.as_ref()
|
||||
}
|
||||
|
||||
@ -176,30 +180,30 @@ impl Authority {
|
||||
}
|
||||
|
||||
/// Retrieve the Signer, which contains the private keys, for this zone
|
||||
pub fn get_secure_keys(&self) -> &[Signer] {
|
||||
pub fn secure_keys(&self) -> &[Signer] {
|
||||
&self.secure_keys
|
||||
}
|
||||
|
||||
/// Get the origin of this zone, i.e. example.com is the origin for www.example.com
|
||||
pub fn get_origin(&self) -> &Name {
|
||||
pub fn origin(&self) -> &Name {
|
||||
&self.origin
|
||||
}
|
||||
|
||||
/// What type is this zone
|
||||
pub fn get_zone_type(&self) -> ZoneType {
|
||||
pub fn zone_type(&self) -> ZoneType {
|
||||
self.zone_type
|
||||
}
|
||||
|
||||
/// Get all the
|
||||
pub fn get_records(&self) -> &BTreeMap<RrKey, RecordSet> {
|
||||
pub fn records(&self) -> &BTreeMap<RrKey, RecordSet> {
|
||||
&self.records
|
||||
}
|
||||
|
||||
/// Returns the SOA of the authority.
|
||||
///
|
||||
/// *Note*: This will only return the SOA, if this is fullfilling a request, a standard lookup
|
||||
/// should be used, see `get_soa_secure()`, which will optionally return RRSIGs.
|
||||
pub fn get_soa(&self) -> Option<&Record> {
|
||||
/// should be used, see `soa_secure()`, which will optionally return RRSIGs.
|
||||
pub fn soa(&self) -> Option<&Record> {
|
||||
// SOA should be origin|SOA
|
||||
self.lookup(&self.origin,
|
||||
RecordType::SOA,
|
||||
@ -210,10 +214,10 @@ impl Authority {
|
||||
}
|
||||
|
||||
/// Returns the SOA record for the zone
|
||||
pub fn get_soa_secure(&self,
|
||||
is_secure: bool,
|
||||
supported_algorithms: SupportedAlgorithms)
|
||||
-> Vec<&Record> {
|
||||
pub fn soa_secure(&self,
|
||||
is_secure: bool,
|
||||
supported_algorithms: SupportedAlgorithms)
|
||||
-> Vec<&Record> {
|
||||
self.lookup(&self.origin,
|
||||
RecordType::SOA,
|
||||
is_secure,
|
||||
@ -221,17 +225,18 @@ impl Authority {
|
||||
}
|
||||
|
||||
/// Returns the minimum ttl (as used in the SOA record)
|
||||
pub fn get_minimum_ttl(&self) -> u32 {
|
||||
self.get_soa().map_or(0, |soa| if let &RData::SOA(ref rdata) = soa.rdata() {
|
||||
rdata.minimum()
|
||||
} else {
|
||||
0
|
||||
})
|
||||
pub fn minimum_ttl(&self) -> u32 {
|
||||
self.soa()
|
||||
.map_or(0, |soa| if let &RData::SOA(ref rdata) = soa.rdata() {
|
||||
rdata.minimum()
|
||||
} else {
|
||||
0
|
||||
})
|
||||
}
|
||||
|
||||
/// get the current serial number for the zone.
|
||||
pub fn get_serial(&self) -> u32 {
|
||||
let soa = if let Some(ref soa_record) = self.get_soa() {
|
||||
pub fn serial(&self) -> u32 {
|
||||
let soa = if let Some(ref soa_record) = self.soa() {
|
||||
soa_record.clone()
|
||||
} else {
|
||||
warn!("no soa record found for zone: {}", self.origin);
|
||||
@ -246,7 +251,7 @@ impl Authority {
|
||||
}
|
||||
|
||||
fn increment_soa_serial(&mut self) -> u32 {
|
||||
let mut soa = if let Some(ref mut soa_record) = self.get_soa() {
|
||||
let mut soa = if let Some(ref mut soa_record) = self.soa() {
|
||||
soa_record.clone()
|
||||
} else {
|
||||
warn!("no soa record found for zone: {}", self.origin);
|
||||
@ -264,10 +269,8 @@ impl Authority {
|
||||
return serial;
|
||||
}
|
||||
|
||||
pub fn get_ns(&self,
|
||||
is_secure: bool,
|
||||
supported_algorithms: SupportedAlgorithms)
|
||||
-> Vec<&Record> {
|
||||
/// Get the NS, NameServer, record for the zone
|
||||
pub fn ns(&self, is_secure: bool, supported_algorithms: SupportedAlgorithms) -> Vec<&Record> {
|
||||
self.lookup(&self.origin,
|
||||
RecordType::NS,
|
||||
is_secure,
|
||||
@ -480,51 +483,54 @@ impl Authority {
|
||||
let sig0s: &[Record] = update_message.sig0();
|
||||
debug!("authorizing with: {:?}", sig0s);
|
||||
if !sig0s.is_empty() &&
|
||||
sig0s.iter()
|
||||
.filter_map(|sig0| if let &RData::SIG(ref sig) = sig0.rdata() {
|
||||
Some(sig)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.any(|sig| {
|
||||
let name = sig.signer_name();
|
||||
let keys = self.lookup(name, RecordType::KEY, false, SupportedAlgorithms::new());
|
||||
debug!("found keys {:?}", keys);
|
||||
keys.iter()
|
||||
.filter_map(|rr_set| if let &RData::KEY(ref key) = rr_set.rdata() {
|
||||
Some(key)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.any(|key| {
|
||||
let pkey = KeyPair::from_public_bytes(key.public_key(),
|
||||
*key.algorithm());
|
||||
if let Err(error) = pkey {
|
||||
warn!("public key {:?} of {} could not be used: {}",
|
||||
key,
|
||||
name,
|
||||
error);
|
||||
return false;
|
||||
}
|
||||
|
||||
let pkey = pkey.unwrap();
|
||||
let signer: Signer = Signer::new_verifier(*key.algorithm(),
|
||||
pkey,
|
||||
sig.signer_name().clone(),
|
||||
false,
|
||||
true);
|
||||
|
||||
signer.verify_message(update_message, sig.sig())
|
||||
.map(|_| {
|
||||
info!("verified sig: {:?} with key: {:?}", sig, key);
|
||||
true
|
||||
sig0s
|
||||
.iter()
|
||||
.filter_map(|sig0| if let &RData::SIG(ref sig) = sig0.rdata() {
|
||||
Some(sig)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.any(|sig| {
|
||||
let name = sig.signer_name();
|
||||
let keys = self.lookup(name, RecordType::KEY, false, SupportedAlgorithms::new());
|
||||
debug!("found keys {:?}", keys);
|
||||
keys.iter()
|
||||
.filter_map(|rr_set| if let &RData::KEY(ref key) = rr_set.rdata() {
|
||||
Some(key)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.unwrap_or_else(|_| {
|
||||
debug!("did not verify sig: {:?} with key: {:?}", sig, key);
|
||||
false
|
||||
})
|
||||
})
|
||||
}) {
|
||||
.any(|key| {
|
||||
let pkey = KeyPair::from_public_bytes(key.public_key(), *key.algorithm());
|
||||
if let Err(error) = pkey {
|
||||
warn!("public key {:?} of {} could not be used: {}",
|
||||
key,
|
||||
name,
|
||||
error);
|
||||
return false;
|
||||
}
|
||||
|
||||
let pkey = pkey.unwrap();
|
||||
let signer: Signer = Signer::new_verifier(*key.algorithm(),
|
||||
pkey,
|
||||
sig.signer_name().clone(),
|
||||
false,
|
||||
true);
|
||||
|
||||
signer
|
||||
.verify_message(update_message, sig.sig())
|
||||
.map(|_| {
|
||||
info!("verified sig: {:?} with key: {:?}", sig, key);
|
||||
true
|
||||
})
|
||||
.unwrap_or_else(|_| {
|
||||
debug!("did not verify sig: {:?} with key: {:?}",
|
||||
sig,
|
||||
key);
|
||||
false
|
||||
})
|
||||
})
|
||||
}) {
|
||||
return Ok(());
|
||||
} else {
|
||||
warn!("no sig0 matched registered records: id {}",
|
||||
@ -581,7 +587,7 @@ impl Authority {
|
||||
// else
|
||||
// return (FORMERR)
|
||||
for rr in records {
|
||||
if !self.get_origin().zone_of(rr.name()) {
|
||||
if !self.origin().zone_of(rr.name()) {
|
||||
return Err(ResponseCode::NotZone);
|
||||
}
|
||||
|
||||
@ -656,7 +662,7 @@ impl Authority {
|
||||
auto_signing_and_increment: bool)
|
||||
-> UpdateResult<bool> {
|
||||
let mut updated = false;
|
||||
let serial: u32 = self.get_serial();
|
||||
let serial: u32 = self.serial();
|
||||
|
||||
// the persistence act as a write-ahead log. The WAL will also be used for recovery of a zone
|
||||
// subsequent to a failure of the server.
|
||||
@ -747,10 +753,10 @@ impl Authority {
|
||||
let to_delete = self.records
|
||||
.keys()
|
||||
.filter(|k| {
|
||||
!((k.record_type == RecordType::SOA ||
|
||||
k.record_type == RecordType::NS) &&
|
||||
k.name != self.origin)
|
||||
})
|
||||
!((k.record_type == RecordType::SOA ||
|
||||
k.record_type == RecordType::NS) &&
|
||||
k.name != self.origin)
|
||||
})
|
||||
.filter(|k| &k.name == rr.name())
|
||||
.cloned()
|
||||
.collect::<Vec<RrKey>>();
|
||||
@ -796,10 +802,11 @@ impl Authority {
|
||||
// update the serial...
|
||||
if updated && auto_signing_and_increment {
|
||||
if self.is_dnssec_enabled {
|
||||
try!(self.secure_zone().map_err(|e| {
|
||||
error!("failure securing zone: {}", e);
|
||||
ResponseCode::ServFail
|
||||
}))
|
||||
try!(self.secure_zone()
|
||||
.map_err(|e| {
|
||||
error!("failure securing zone: {}", e);
|
||||
ResponseCode::ServFail
|
||||
}))
|
||||
} else {
|
||||
// the secure_zone() function increments the SOA during it's operation, if we're not
|
||||
// dnssec, then we need to do it here...
|
||||
@ -826,9 +833,10 @@ impl Authority {
|
||||
assert_eq!(self.class, record.dns_class());
|
||||
|
||||
let rr_key = RrKey::new(record.name(), record.rr_type());
|
||||
let records: &mut RecordSet = self.records
|
||||
.entry(rr_key)
|
||||
.or_insert(RecordSet::new(record.name(), record.rr_type(), serial));
|
||||
let records: &mut RecordSet =
|
||||
self.records
|
||||
.entry(rr_key)
|
||||
.or_insert(RecordSet::new(record.name(), record.rr_type(), serial));
|
||||
|
||||
records.insert(record, serial)
|
||||
}
|
||||
@ -920,7 +928,7 @@ impl Authority {
|
||||
// if this is an AXFR zone transfer, verify that this is either the slave or master
|
||||
// for AXFR the first and last record must be the SOA
|
||||
if RecordType::AXFR == record_type {
|
||||
match self.get_zone_type() {
|
||||
match self.zone_type() {
|
||||
ZoneType::Master | ZoneType::Slave => (),
|
||||
// TODO Forward?
|
||||
_ => return vec![], // TODO this sould be an error.
|
||||
@ -929,13 +937,11 @@ impl Authority {
|
||||
|
||||
// it would be better to stream this back, rather than packaging everything up in an array
|
||||
// though for UDP it would still need to be bundled
|
||||
let mut query_result: Vec<_> = self.lookup(query.name(),
|
||||
record_type,
|
||||
is_secure,
|
||||
supported_algorithms);
|
||||
let mut query_result: Vec<_> =
|
||||
self.lookup(query.name(), record_type, is_secure, supported_algorithms);
|
||||
|
||||
if RecordType::AXFR == record_type {
|
||||
if let Some(soa) = self.get_soa() {
|
||||
if let Some(soa) = self.soa() {
|
||||
let mut xfr: Vec<&Record> = query_result;
|
||||
// TODO: probably make Records Rc or Arc, to remove the clone
|
||||
xfr.insert(0, soa);
|
||||
@ -984,8 +990,8 @@ impl Authority {
|
||||
self.records
|
||||
.values()
|
||||
.filter(|rr_set| {
|
||||
rtype == RecordType::ANY || rr_set.record_type() != RecordType::SOA
|
||||
})
|
||||
rtype == RecordType::ANY || rr_set.record_type() != RecordType::SOA
|
||||
})
|
||||
.filter(|rr_set| rtype == RecordType::AXFR || rr_set.name() == name)
|
||||
.fold(Vec::<&Record>::new(), |mut vec, rr_set| {
|
||||
vec.append(&mut rr_set.records(is_secure, supported_algorithms));
|
||||
@ -993,9 +999,14 @@ impl Authority {
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
self.records.get(&rr_key).map_or(vec![], |rr_set| {
|
||||
rr_set.records(is_secure, supported_algorithms).into_iter().collect()
|
||||
})
|
||||
self.records
|
||||
.get(&rr_key)
|
||||
.map_or(vec![], |rr_set| {
|
||||
rr_set
|
||||
.records(is_secure, supported_algorithms)
|
||||
.into_iter()
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
@ -1020,7 +1031,10 @@ impl Authority {
|
||||
.skip_while(|rr_set| name < rr_set.name())
|
||||
.next()
|
||||
.map_or(vec![], |rr_set| {
|
||||
rr_set.records(is_secure, supported_algorithms).into_iter().collect()
|
||||
rr_set
|
||||
.records(is_secure, supported_algorithms)
|
||||
.into_iter()
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
@ -1058,8 +1072,8 @@ impl Authority {
|
||||
}
|
||||
|
||||
// now go through and generate the nsec records
|
||||
let ttl = self.get_minimum_ttl();
|
||||
let serial = self.get_serial();
|
||||
let ttl = self.minimum_ttl();
|
||||
let serial = self.serial();
|
||||
let mut records: Vec<Record> = vec![];
|
||||
|
||||
{
|
||||
@ -1085,7 +1099,7 @@ impl Authority {
|
||||
if let Some((name, vec)) = nsec_info {
|
||||
// names aren't equal, create the NSEC record
|
||||
let mut record = Record::with(name.clone(), RecordType::NSEC, ttl);
|
||||
let rdata = NSEC::new(self.get_origin().clone(), vec);
|
||||
let rdata = NSEC::new(self.origin().clone(), vec);
|
||||
record.set_rdata(RData::NSEC(rdata));
|
||||
records.push(record);
|
||||
}
|
||||
@ -1101,21 +1115,23 @@ impl Authority {
|
||||
fn sign_zone(&mut self) -> DnsSecResult<()> {
|
||||
debug!("signing zone: {}", self.origin);
|
||||
let inception = UTC::now();
|
||||
let zone_ttl = self.get_minimum_ttl();
|
||||
let zone_ttl = self.minimum_ttl();
|
||||
|
||||
// TODO: should this be an error?
|
||||
if self.secure_keys.is_empty() {
|
||||
warn!("attempt to sign_zone for dnssec, but no keys available!")
|
||||
}
|
||||
|
||||
for rr_set in self.records.iter_mut().filter_map(|(_, rr_set)| {
|
||||
// do not sign zone DNSKEY's that's the job of the parent zone
|
||||
if rr_set.record_type() == RecordType::DNSKEY {
|
||||
return None;
|
||||
}
|
||||
rr_set.rrsigs().is_empty();
|
||||
Some(rr_set)
|
||||
}) {
|
||||
for rr_set in self.records
|
||||
.iter_mut()
|
||||
.filter_map(|(_, rr_set)| {
|
||||
// do not sign zone DNSKEY's that's the job of the parent zone
|
||||
if rr_set.record_type() == RecordType::DNSKEY {
|
||||
return None;
|
||||
}
|
||||
rr_set.rrsigs().is_empty();
|
||||
Some(rr_set)
|
||||
}) {
|
||||
|
||||
debug!("signing rr_set: {}", rr_set.name());
|
||||
rr_set.clear_rrsigs();
|
||||
@ -1136,11 +1152,12 @@ impl Authority {
|
||||
try!(signer.calculate_key_tag()),
|
||||
signer.signer_name(),
|
||||
// TODO: this is a nasty clone... the issue is that the vec
|
||||
// from get_records is of Vec<&R>, but we really want &[R]
|
||||
&rr_set.records(false, SupportedAlgorithms::new())
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect::<Vec<Record>>());
|
||||
// from records is of Vec<&R>, but we really want &[R]
|
||||
&rr_set
|
||||
.records(false, SupportedAlgorithms::new())
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect::<Vec<Record>>());
|
||||
|
||||
// TODO, maybe chain these with some ETL operations instead?
|
||||
if hash.is_err() {
|
||||
@ -1159,23 +1176,23 @@ impl Authority {
|
||||
|
||||
let mut rrsig = rrsig_temp.clone();
|
||||
rrsig.set_rdata(RData::SIG(SIG::new(// type_covered: RecordType,
|
||||
rr_set.record_type(),
|
||||
// algorithm: Algorithm,
|
||||
signer.algorithm(),
|
||||
// num_labels: u8,
|
||||
rr_set.name().num_labels(),
|
||||
// original_ttl: u32,
|
||||
rr_set.ttl(),
|
||||
// sig_expiration: u32,
|
||||
expiration.timestamp() as u32,
|
||||
// sig_inception: u32,
|
||||
inception.timestamp() as u32,
|
||||
// key_tag: u16,
|
||||
try!(signer.calculate_key_tag()),
|
||||
// signer_name: Name,
|
||||
signer.signer_name().clone(),
|
||||
// sig: Vec<u8>
|
||||
signature)));
|
||||
rr_set.record_type(),
|
||||
// algorithm: Algorithm,
|
||||
signer.algorithm(),
|
||||
// num_labels: u8,
|
||||
rr_set.name().num_labels(),
|
||||
// original_ttl: u32,
|
||||
rr_set.ttl(),
|
||||
// sig_expiration: u32,
|
||||
expiration.timestamp() as u32,
|
||||
// sig_inception: u32,
|
||||
inception.timestamp() as u32,
|
||||
// key_tag: u16,
|
||||
try!(signer.calculate_key_tag()),
|
||||
// signer_name: Name,
|
||||
signer.signer_name().clone(),
|
||||
// sig: Vec<u8>
|
||||
signature)));
|
||||
|
||||
rr_set.insert_rrsig(rrsig);
|
||||
debug!("signed rr_set: {}", rr_set.name());
|
||||
|
@ -59,10 +59,10 @@ impl RequestHandler for Catalog {
|
||||
let our_version = 0;
|
||||
resp_edns.set_dnssec_ok(true);
|
||||
resp_edns.set_max_payload(if req_edns.max_payload() < 512 {
|
||||
512
|
||||
} else {
|
||||
req_edns.max_payload()
|
||||
});
|
||||
512
|
||||
} else {
|
||||
req_edns.max_payload()
|
||||
});
|
||||
resp_edns.set_version(our_version);
|
||||
|
||||
if req_edns.version() > our_version {
|
||||
@ -100,17 +100,13 @@ impl RequestHandler for Catalog {
|
||||
}
|
||||
c @ _ => {
|
||||
error!("unimplemented op_code: {:?}", c);
|
||||
Message::error_msg(request.id(),
|
||||
request.op_code(),
|
||||
ResponseCode::NotImp)
|
||||
Message::error_msg(request.id(), request.op_code(), ResponseCode::NotImp)
|
||||
}
|
||||
}
|
||||
}
|
||||
MessageType::Response => {
|
||||
warn!("got a response as a request from id: {}", request.id());
|
||||
Message::error_msg(request.id(),
|
||||
request.op_code(),
|
||||
ResponseCode::NotImp)
|
||||
Message::error_msg(request.id(), request.op_code(), ResponseCode::NotImp)
|
||||
}
|
||||
};
|
||||
|
||||
@ -140,10 +136,17 @@ impl RequestHandler for Catalog {
|
||||
}
|
||||
|
||||
impl Catalog {
|
||||
/// Constructs a new Catalog
|
||||
pub fn new() -> Self {
|
||||
Catalog { authorities: HashMap::new() }
|
||||
}
|
||||
|
||||
/// Insert or update a zone authority
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - zone name, e.g. example.com.
|
||||
/// * `authority` - the zone data
|
||||
pub fn upsert(&mut self, name: Name, authority: Authority) {
|
||||
self.authorities.insert(name, RwLock::new(authority));
|
||||
}
|
||||
@ -217,7 +220,7 @@ impl Catalog {
|
||||
|
||||
if let Some(authority) = self.find_auth_recurse(zones[0].name()) {
|
||||
let mut authority = authority.write().unwrap(); // poison errors should panic...
|
||||
match authority.get_zone_type() {
|
||||
match authority.zone_type() {
|
||||
ZoneType::Slave => {
|
||||
error!("slave forwarding for update not yet implemented");
|
||||
response.set_response_code(ResponseCode::NotImp);
|
||||
@ -264,8 +267,9 @@ impl Catalog {
|
||||
for query in request.queries() {
|
||||
if let Some(ref_authority) = self.find_auth_recurse(query.name()) {
|
||||
let authority = &ref_authority.read().unwrap(); // poison errors should panic
|
||||
debug!("found authority: {:?}", authority.get_origin());
|
||||
let (is_dnssec, supported_algorithms) = request.edns()
|
||||
debug!("found authority: {:?}", authority.origin());
|
||||
let (is_dnssec, supported_algorithms) = request
|
||||
.edns()
|
||||
.map_or((false, SupportedAlgorithms::new()), |edns| {
|
||||
let supported_algorithms = if let Some(&EdnsOption::DAU(algs)) =
|
||||
edns.option(&EdnsCode::DAU) {
|
||||
@ -284,28 +288,27 @@ impl Catalog {
|
||||
response.add_answers(records.into_iter().cloned());
|
||||
|
||||
// get the NS records
|
||||
let ns = authority.get_ns(is_dnssec, supported_algorithms);
|
||||
let ns = authority.ns(is_dnssec, supported_algorithms);
|
||||
if ns.is_empty() {
|
||||
warn!("there are no NS records for: {:?}", authority.get_origin());
|
||||
warn!("there are no NS records for: {:?}", authority.origin());
|
||||
} else {
|
||||
response.add_name_servers(ns.into_iter().cloned());
|
||||
}
|
||||
} else {
|
||||
if is_dnssec {
|
||||
// get NSEC records
|
||||
let nsecs =
|
||||
authority.get_nsec_records(query.name(),
|
||||
is_dnssec,
|
||||
supported_algorithms);
|
||||
let nsecs = authority.get_nsec_records(query.name(),
|
||||
is_dnssec,
|
||||
supported_algorithms);
|
||||
response.add_name_servers(nsecs.into_iter().cloned());
|
||||
}
|
||||
|
||||
// in the not found case it's standard to return the SOA in the authority section
|
||||
response.set_response_code(ResponseCode::NXDomain);
|
||||
|
||||
let soa = authority.get_soa_secure(is_dnssec, supported_algorithms);
|
||||
let soa = authority.soa_secure(is_dnssec, supported_algorithms);
|
||||
if soa.is_empty() {
|
||||
warn!("there is no SOA record for: {:?}", authority.get_origin());
|
||||
warn!("there is no SOA record for: {:?}", authority.origin());
|
||||
} else {
|
||||
response.add_name_servers(soa.into_iter().cloned());
|
||||
}
|
||||
|
@ -18,13 +18,19 @@
|
||||
|
||||
use trust_dns::op::ResponseCode;
|
||||
|
||||
/// Result of an Update operation
|
||||
pub type UpdateResult<T> = Result<T, ResponseCode>;
|
||||
|
||||
/// The type of zone stored in a Catalog
|
||||
#[derive(RustcDecodable, PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub enum ZoneType {
|
||||
/// This authority for a zone, i.e. the Primary
|
||||
Master,
|
||||
/// A secondary, i.e. replicated from the Master
|
||||
Slave,
|
||||
/// A cached zone with recursive resolver abilities
|
||||
Hint,
|
||||
/// A cached zone where all requests are forwarded to another Resolver
|
||||
Forward,
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,9 @@
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
//! All zone persistence related types
|
||||
|
||||
use std::iter::Iterator;
|
||||
use std::path::Path;
|
||||
|
||||
@ -16,6 +19,7 @@ use trust_dns::serialize::binary::{BinDecoder, BinEncoder, BinSerializable};
|
||||
|
||||
use error::{PersistenceErrorKind, PersistenceResult};
|
||||
|
||||
/// The current Journal version of the application
|
||||
pub const CURRENT_VERSION: i64 = 1;
|
||||
|
||||
/// The Journal is the audit log of all changes to a zone after initial creation.
|
||||
@ -25,14 +29,16 @@ pub struct Journal {
|
||||
}
|
||||
|
||||
impl Journal {
|
||||
/// Constructs a new Journal, attaching to the specified Sqlite Connection
|
||||
pub fn new(conn: Connection) -> PersistenceResult<Journal> {
|
||||
let version = Self::select_schema_version(&conn);
|
||||
Ok(Journal {
|
||||
conn: conn,
|
||||
version: try!(version),
|
||||
})
|
||||
conn: conn,
|
||||
version: try!(version),
|
||||
})
|
||||
}
|
||||
|
||||
/// Constructs a new Journal opening a Sqlite connection to the file at the specified path
|
||||
pub fn from_file(journal_file: &Path) -> PersistenceResult<Journal> {
|
||||
let result = Self::new(try!(Connection::open(journal_file)));
|
||||
match result {
|
||||
@ -44,12 +50,13 @@ impl Journal {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_conn(&self) -> &Connection {
|
||||
/// Returns a reference to the Sqlite Connection
|
||||
pub fn conn(&self) -> &Connection {
|
||||
&self.conn
|
||||
}
|
||||
|
||||
/// gets the current schema version of the journal
|
||||
pub fn get_schema_version(&self) -> i64 {
|
||||
/// Returns the current schema version of the journal
|
||||
pub fn schema_version(&self) -> i64 {
|
||||
self.version
|
||||
}
|
||||
|
||||
@ -81,13 +88,14 @@ impl Journal {
|
||||
let client_id: i64 = 0; // TODO: we need better id information about the client, like pub_key
|
||||
let soa_serial: i64 = soa_serial as i64;
|
||||
|
||||
let count = try!(self.conn.execute("INSERT
|
||||
let count = try!(self.conn
|
||||
.execute("INSERT
|
||||
\
|
||||
INTO records (client_id, soa_serial, timestamp, \
|
||||
record)
|
||||
\
|
||||
VALUES ($1, $2, $3, $4)",
|
||||
&[&client_id, &soa_serial, ×tamp, &serial_record]));
|
||||
&[&client_id, &soa_serial, ×tamp, &serial_record]));
|
||||
//
|
||||
if count != 1 {
|
||||
return Err(PersistenceErrorKind::WrongInsertCount(count, 1).into());
|
||||
@ -119,7 +127,8 @@ impl Journal {
|
||||
assert!(self.version == CURRENT_VERSION,
|
||||
"schema version mismatch, schema_up() resolves this");
|
||||
|
||||
let mut stmt = try!(self.conn.prepare("SELECT _rowid_, record
|
||||
let mut stmt = try!(self.conn
|
||||
.prepare("SELECT _rowid_, record
|
||||
\
|
||||
FROM records
|
||||
\
|
||||
@ -127,8 +136,7 @@ impl Journal {
|
||||
\
|
||||
LIMIT 1"));
|
||||
|
||||
let record_opt: Option<Result<(i64, Record), rusqlite::Error>> =
|
||||
try!(stmt.query_and_then(&[&row_id],
|
||||
let record_opt: Option<Result<(i64, Record), rusqlite::Error>> = try!(stmt.query_and_then(&[&row_id],
|
||||
|row| -> Result<(i64, Record), rusqlite::Error> {
|
||||
let row_id: i64 = try!(row.get_checked(0));
|
||||
let record_bytes: Vec<u8> = try!(row.get_checked(1));
|
||||
@ -194,7 +202,8 @@ impl Journal {
|
||||
// validate the versions of all the schemas...
|
||||
assert!(new_version <= CURRENT_VERSION);
|
||||
|
||||
let count = try!(self.conn.execute("UPDATE tdns_schema SET version = $1", &[&new_version]));
|
||||
let count = try!(self.conn
|
||||
.execute("UPDATE tdns_schema SET version = $1", &[&new_version]));
|
||||
|
||||
//
|
||||
assert_eq!(count, 1);
|
||||
@ -218,16 +227,18 @@ impl Journal {
|
||||
|
||||
/// initial schema, include the tdns_schema table for tracking the Journal version
|
||||
fn init_up(&self) -> PersistenceResult<i64> {
|
||||
let count = try!(self.conn.execute("CREATE TABLE tdns_schema (
|
||||
let count = try!(self.conn
|
||||
.execute("CREATE TABLE tdns_schema (
|
||||
\
|
||||
version INTEGER NOT NULL
|
||||
\
|
||||
)",
|
||||
&[]));
|
||||
&[]));
|
||||
//
|
||||
assert_eq!(count, 0);
|
||||
|
||||
let count = try!(self.conn.execute("INSERT INTO tdns_schema (version) VALUES (0)", &[]));
|
||||
let count = try!(self.conn
|
||||
.execute("INSERT INTO tdns_schema (version) VALUES (0)", &[]));
|
||||
//
|
||||
assert_eq!(count, 1);
|
||||
|
||||
@ -238,7 +249,8 @@ impl Journal {
|
||||
/// authority. Each record is expected to be in the format of an update record
|
||||
fn records_up(&self) -> PersistenceResult<i64> {
|
||||
// we'll be using rowid for our primary key, basically: `rowid INTEGER PRIMARY KEY ASC`
|
||||
let count = try!(self.conn.execute("CREATE TABLE records (
|
||||
let count = try!(self.conn
|
||||
.execute("CREATE TABLE records (
|
||||
\
|
||||
client_id INTEGER NOT NULL,
|
||||
\
|
||||
@ -249,7 +261,7 @@ impl Journal {
|
||||
record BLOB NOT NULL
|
||||
\
|
||||
)",
|
||||
&[]));
|
||||
&[]));
|
||||
//
|
||||
assert_eq!(count, 1);
|
||||
|
||||
@ -257,6 +269,9 @@ impl Journal {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over all items in a Journal
|
||||
///
|
||||
/// Useful for replaying an entire journal into memory to reconstruct a zone from disk
|
||||
pub struct JournalIter<'j> {
|
||||
current_row_id: i64,
|
||||
journal: &'j Journal,
|
||||
@ -275,8 +290,8 @@ impl<'j> Iterator for JournalIter<'j> {
|
||||
type Item = Record;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next: PersistenceResult<Option<(i64, Record)>> = self.journal
|
||||
.select_record(self.current_row_id + 1);
|
||||
let next: PersistenceResult<Option<(i64, Record)>> =
|
||||
self.journal.select_record(self.current_row_id + 1);
|
||||
|
||||
match next {
|
||||
Ok(Some((row_id, record))) => {
|
||||
|
@ -39,16 +39,26 @@ static DEFAULT_PORT: u16 = 53;
|
||||
static DEFAULT_TLS_PORT: u16 = 853;
|
||||
static DEFAULT_TCP_REQUEST_TIMEOUT: u64 = 5;
|
||||
|
||||
/// Server configuration
|
||||
#[derive(RustcDecodable, Debug)]
|
||||
pub struct Config {
|
||||
/// The list of IPv4 addresses to listen on
|
||||
listen_addrs_ipv4: Vec<String>,
|
||||
/// This list of IPv6 addresses to listen on
|
||||
listen_addrs_ipv6: Vec<String>,
|
||||
/// Port on which to listen (associated to all IPs)
|
||||
listen_port: Option<u16>,
|
||||
/// Secure port to listen on
|
||||
tls_listen_port: Option<u16>,
|
||||
/// Timeout associated to a request before it is closed.
|
||||
tcp_request_timeout: Option<u64>,
|
||||
/// Level at which to log, default is INFO
|
||||
log_level: Option<String>,
|
||||
/// Base configuration directory, i.e. root path for zones
|
||||
directory: Option<String>,
|
||||
/// List of configurations for zones
|
||||
zones: Vec<ZoneConfig>,
|
||||
/// Certificate to associate to TLS connections
|
||||
tls_cert: Option<TlsCertConfig>,
|
||||
}
|
||||
|
||||
@ -63,11 +73,17 @@ impl Config {
|
||||
|
||||
/// set of listening ipv4 addresses (for TCP and UDP)
|
||||
pub fn get_listen_addrs_ipv4(&self) -> Vec<Ipv4Addr> {
|
||||
self.listen_addrs_ipv4.iter().map(|s| s.parse().unwrap()).collect()
|
||||
self.listen_addrs_ipv4
|
||||
.iter()
|
||||
.map(|s| s.parse().unwrap())
|
||||
.collect()
|
||||
}
|
||||
/// set of listening ipv6 addresses (for TCP and UDP)
|
||||
pub fn get_listen_addrs_ipv6(&self) -> Vec<Ipv6Addr> {
|
||||
self.listen_addrs_ipv6.iter().map(|s| s.parse().unwrap()).collect()
|
||||
self.listen_addrs_ipv6
|
||||
.iter()
|
||||
.map(|s| s.parse().unwrap())
|
||||
.collect()
|
||||
}
|
||||
/// port on which to listen for connections on specified addresses
|
||||
pub fn get_listen_port(&self) -> u16 {
|
||||
@ -79,7 +95,8 @@ impl Config {
|
||||
}
|
||||
/// default timeout for all TCP connections before forceably shutdown
|
||||
pub fn get_tcp_request_timeout(&self) -> Duration {
|
||||
Duration::from_secs(self.tcp_request_timeout.unwrap_or(DEFAULT_TCP_REQUEST_TIMEOUT))
|
||||
Duration::from_secs(self.tcp_request_timeout
|
||||
.unwrap_or(DEFAULT_TCP_REQUEST_TIMEOUT))
|
||||
}
|
||||
|
||||
// TODO: also support env_logger
|
||||
@ -100,7 +117,9 @@ impl Config {
|
||||
}
|
||||
/// the path for all zone configurations, defaults to `/var/named`
|
||||
pub fn get_directory(&self) -> &Path {
|
||||
self.directory.as_ref().map_or(Path::new(DEFAULT_PATH), |s| Path::new(s))
|
||||
self.directory
|
||||
.as_ref()
|
||||
.map_or(Path::new(DEFAULT_PATH), |s| Path::new(s))
|
||||
}
|
||||
/// the set of zones which should be loaded
|
||||
pub fn get_zones(&self) -> &[ZoneConfig] {
|
||||
@ -116,12 +135,14 @@ impl FromStr for Config {
|
||||
type Err = ConfigError;
|
||||
|
||||
fn from_str(toml: &str) -> ConfigResult<Config> {
|
||||
let value: Value = try!(toml.parse().map_err(|vec| ConfigErrorKind::VecParserError(vec)));
|
||||
let value: Value = try!(toml.parse()
|
||||
.map_err(|vec| ConfigErrorKind::VecParserError(vec)));
|
||||
let mut decoder: Decoder = Decoder::new(value);
|
||||
Ok(try!(Self::decode(&mut decoder)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for a zone
|
||||
#[derive(RustcDecodable, PartialEq, Debug)]
|
||||
pub struct ZoneConfig {
|
||||
zone: String, // TODO: make Domain::Name decodable
|
||||
@ -133,6 +154,16 @@ pub struct ZoneConfig {
|
||||
}
|
||||
|
||||
impl ZoneConfig {
|
||||
/// Return a new zone configuration
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `zone` - name of a zone, e.g. example.com
|
||||
/// * `zone_type` - Type of zone, e.g. Master
|
||||
/// * `file` - relative to Config base path, to the zone file
|
||||
/// * `allow_update` - enable dynamic updates
|
||||
/// * `enable_dnssec` - enable signing of the zone for DNSSec
|
||||
/// * `keys` - list of private and public keys used to sign a zone
|
||||
pub fn new(zone: String,
|
||||
zone_type: ZoneType,
|
||||
file: String,
|
||||
@ -185,6 +216,7 @@ impl ZoneConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Key pair configuration for DNSSec keys for signing a zone
|
||||
#[derive(RustcDecodable, PartialEq, Debug)]
|
||||
pub struct KeyConfig {
|
||||
key_path: String,
|
||||
@ -197,6 +229,17 @@ pub struct KeyConfig {
|
||||
}
|
||||
|
||||
impl KeyConfig {
|
||||
/// Return a new KeyConfig
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `key_path` - file path to the key
|
||||
/// * `password` - password to use to read the key
|
||||
/// * `algorithm` - the type of key stored, see `Algorithm`
|
||||
/// * `signer_name` - the name to use when signing records, e.g. ns.example.com
|
||||
/// * `is_zone_signing_key` - specify that this key should be used for signing a zone
|
||||
/// * `is_zone_update_auth` - specifies that this key can be used for dynamic updates in the zone
|
||||
/// * `do_auto_generate` - if the key does not exist, generate a new one (it will need to be signed)
|
||||
pub fn new(key_path: String,
|
||||
password: Option<String>,
|
||||
algorithm: Algorithm,
|
||||
@ -217,16 +260,16 @@ impl KeyConfig {
|
||||
}
|
||||
|
||||
/// path to the key file, either relative to the zone file, or a explicit from the root.
|
||||
pub fn get_key_path(&self) -> &Path {
|
||||
pub fn key_path(&self) -> &Path {
|
||||
Path::new(&self.key_path)
|
||||
}
|
||||
|
||||
/// Converts key into
|
||||
pub fn get_format(&self) -> ParseResult<KeyFormat> {
|
||||
let extension = try!(self.get_key_path()
|
||||
pub fn format(&self) -> ParseResult<KeyFormat> {
|
||||
let extension = try!(self.key_path()
|
||||
.extension()
|
||||
.ok_or(ParseErrorKind::Msg(format!("file lacks extension, e.g. '.p12': {:?}",
|
||||
self.get_key_path())
|
||||
self.key_path())
|
||||
.into())));
|
||||
|
||||
match extension.to_str() {
|
||||
@ -237,23 +280,24 @@ impl KeyConfig {
|
||||
e @ _ => {
|
||||
Err(ParseErrorKind::Msg(format!("extension not understood, '{:?}': {:?}",
|
||||
e,
|
||||
self.get_key_path()))
|
||||
.into())
|
||||
self.key_path()))
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_password(&self) -> Option<&str> {
|
||||
/// Returns the password used to read the key
|
||||
pub fn password(&self) -> Option<&str> {
|
||||
self.password.as_ref().map(|s| s.as_str())
|
||||
}
|
||||
|
||||
/// algorithm for for the key, see `Algorithm` for supported algorithms.
|
||||
pub fn get_algorithm(&self) -> ParseResult<Algorithm> {
|
||||
pub fn algorithm(&self) -> ParseResult<Algorithm> {
|
||||
Algorithm::from_str(&self.algorithm).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// the signer name for the key, this defaults to the $ORIGIN aka zone name.
|
||||
pub fn get_signer_name(&self) -> ParseResult<Option<Name>> {
|
||||
pub fn signer_name(&self) -> ParseResult<Option<Name>> {
|
||||
if let Some(ref signer_name) = self.signer_name.as_ref() {
|
||||
let name = try!(Name::parse(signer_name, None));
|
||||
return Ok(Some(name));
|
||||
|
@ -16,6 +16,9 @@
|
||||
|
||||
//! All defined errors for Trust-DNS
|
||||
|
||||
// TODO figure out how to doc error_chain!
|
||||
#![allow(missing_docs)]
|
||||
|
||||
mod config_error;
|
||||
mod persistence_error;
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#![deny(missing_docs)]
|
||||
#![recursion_limit = "1024"]
|
||||
|
||||
//! Trust-DNS is intended to be a fully compliant domain name server and client library.
|
||||
|
@ -139,7 +139,7 @@ fn load_zone(zone_dir: &Path, zone_config: &ZoneConfig) -> Result<Authority, Str
|
||||
.recover_with_journal(&journal)
|
||||
.map_err(|e| format!("error recovering from journal: {}", e)));
|
||||
|
||||
authority.journal(journal);
|
||||
authority.set_journal(journal);
|
||||
info!("recovered zone: {}", zone_name);
|
||||
|
||||
authority
|
||||
@ -169,7 +169,7 @@ fn load_zone(zone_dir: &Path, zone_config: &ZoneConfig) -> Result<Authority, Str
|
||||
try!(Journal::from_file(&journal_path)
|
||||
.map_err(|e| format!("error creating journal {:?}: {}", journal_path, e)));
|
||||
|
||||
authority.journal(journal);
|
||||
authority.set_journal(journal);
|
||||
|
||||
// preserve to the new journal, i.e. we just loaded the zone from disk, start the journal
|
||||
try!(authority
|
||||
@ -199,11 +199,11 @@ fn load_zone(zone_dir: &Path, zone_config: &ZoneConfig) -> Result<Authority, Str
|
||||
true);
|
||||
let signer = try!(load_key(zone_name, &key_config).map_err(|e| {
|
||||
format!("failed to load key: {:?} msg: {}",
|
||||
key_config.get_key_path(),
|
||||
key_config.key_path(),
|
||||
e)
|
||||
}));
|
||||
info!("adding key to zone: {:?}, is_zsk: {}, is_auth: {}",
|
||||
key_config.get_key_path(),
|
||||
key_config.key_path(),
|
||||
key_config.is_zone_signing_key(),
|
||||
key_config.is_zone_update_auth());
|
||||
authority
|
||||
@ -213,11 +213,11 @@ fn load_zone(zone_dir: &Path, zone_config: &ZoneConfig) -> Result<Authority, Str
|
||||
for key_config in zone_config.get_keys() {
|
||||
let signer = try!(load_key(zone_name.clone(), &key_config).map_err(|e| {
|
||||
format!("failed to load key: {:?} msg: {}",
|
||||
key_config.get_key_path(),
|
||||
key_config.key_path(),
|
||||
e)
|
||||
}));
|
||||
info!("adding key to zone: {:?}, is_zsk: {}, is_auth: {}",
|
||||
key_config.get_key_path(),
|
||||
key_config.key_path(),
|
||||
key_config.is_zone_signing_key(),
|
||||
key_config.is_zone_update_auth());
|
||||
authority
|
||||
@ -245,12 +245,12 @@ fn load_zone(zone_dir: &Path, zone_config: &ZoneConfig) -> Result<Authority, Str
|
||||
/// same directory has the zone $file:
|
||||
/// keys = [ "my_rsa_2048|RSASHA256", "/path/to/my_ed25519|ED25519" ]
|
||||
fn load_key(zone_name: Name, key_config: &KeyConfig) -> Result<Signer, String> {
|
||||
let key_path = key_config.get_key_path();
|
||||
let key_path = key_config.key_path();
|
||||
let algorithm = try!(key_config
|
||||
.get_algorithm()
|
||||
.algorithm()
|
||||
.map_err(|e| format!("bad algorithm: {}", e)));
|
||||
let format = try!(key_config
|
||||
.get_format()
|
||||
.format()
|
||||
.map_err(|e| format!("bad key format: {}", e)));
|
||||
|
||||
let key: KeyPair = if key_path.exists() {
|
||||
@ -265,7 +265,7 @@ fn load_key(zone_name: Name, key_config: &KeyConfig) -> Result<Signer, String> {
|
||||
.map_err(|e| format!("could not read key from: {:?}: {}", key_path, e)));
|
||||
|
||||
try!(format
|
||||
.decode_key(&key_bytes, key_config.get_password(), algorithm)
|
||||
.decode_key(&key_bytes, key_config.password(), algorithm)
|
||||
.map_err(|e| format!("could not decode key: {}", e)))
|
||||
} else if key_config.create_if_absent() {
|
||||
info!("creating key: {:?}", key_path);
|
||||
@ -279,7 +279,7 @@ fn load_key(zone_name: Name, key_config: &KeyConfig) -> Result<Signer, String> {
|
||||
.map_err(|e| format!("could not generate key: {}", e)));
|
||||
let key_bytes: Vec<u8> =
|
||||
try!(format
|
||||
.encode_key(&key, key_config.get_password())
|
||||
.encode_key(&key, key_config.password())
|
||||
.map_err(|e| format!("could not get key bytes: {}", e)));
|
||||
|
||||
try!(file.write_all(&key_bytes)
|
||||
@ -294,7 +294,7 @@ fn load_key(zone_name: Name, key_config: &KeyConfig) -> Result<Signer, String> {
|
||||
};
|
||||
|
||||
let name = try!(key_config
|
||||
.get_signer_name()
|
||||
.signer_name()
|
||||
.map_err(|e| format!("error reading name: {}", e)))
|
||||
.unwrap_or(zone_name);
|
||||
|
||||
|
@ -7,8 +7,11 @@ use trust_dns::BufStreamHandle;
|
||||
use trust_dns::op::Message;
|
||||
use trust_dns::serialize::binary::{BinDecoder, BinEncoder, BinSerializable};
|
||||
|
||||
/// An incoming request to the DNS catalog
|
||||
pub struct Request {
|
||||
/// Message with the associated query or update data
|
||||
pub message: Message,
|
||||
/// Source address of the Client
|
||||
pub src: SocketAddr,
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,8 @@ use server::{Request, RequestStream, ResponseHandle, TimeoutStream};
|
||||
use authority::Catalog;
|
||||
|
||||
// TODO, would be nice to have a Slab for buffers here...
|
||||
|
||||
/// A Futures based implementation of a DNS catalog server
|
||||
pub struct ServerFuture {
|
||||
io_loop: Core,
|
||||
catalog: Arc<Catalog>, // should the catalog just be static?
|
||||
@ -207,6 +209,7 @@ impl ServerFuture {
|
||||
"Server stopping due to interruption"))
|
||||
}
|
||||
|
||||
/// Returns a reference to the tokio core loop driving this Server instance
|
||||
pub fn tokio_core(&mut self) -> &mut Core {
|
||||
&mut self.io_loop
|
||||
}
|
||||
|
@ -16,6 +16,13 @@ pub struct TimeoutStream<S> {
|
||||
}
|
||||
|
||||
impl<S> TimeoutStream<S> {
|
||||
/// Returns a new TimeoutStream
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `stream` - stream to wrap
|
||||
/// * `timeout_duration` - timeout between each request, once exceed the connection is killed
|
||||
/// * `reactor_handle` - reactor used for registering new timeouts
|
||||
pub fn new(stream: S, timeout_duration: Duration, reactor_handle: Handle) -> io::Result<Self> {
|
||||
// store a Timeout for this message before sending
|
||||
|
||||
|
@ -23,7 +23,7 @@ use common::authority::{create_example, create_secure_example};
|
||||
#[test]
|
||||
fn test_search() {
|
||||
let example = create_example();
|
||||
let origin = example.get_origin().clone();
|
||||
let origin = example.origin().clone();
|
||||
|
||||
let mut query: Query = Query::new();
|
||||
query.set_name(origin.clone());
|
||||
@ -63,21 +63,21 @@ fn test_search_www() {
|
||||
fn test_authority() {
|
||||
let authority: Authority = create_example();
|
||||
|
||||
assert!(authority.get_soa().is_some());
|
||||
assert_eq!(authority.get_soa().unwrap().dns_class(), DNSClass::IN);
|
||||
assert!(authority.soa().is_some());
|
||||
assert_eq!(authority.soa().unwrap().dns_class(), DNSClass::IN);
|
||||
|
||||
assert!(!authority.lookup(authority.get_origin(),
|
||||
assert!(!authority.lookup(authority.origin(),
|
||||
RecordType::NS,
|
||||
false,
|
||||
SupportedAlgorithms::new())
|
||||
.is_empty());
|
||||
|
||||
let mut lookup: Vec<_> = authority.get_ns(false, SupportedAlgorithms::new());
|
||||
let mut lookup: Vec<_> = authority.ns(false, SupportedAlgorithms::new());
|
||||
lookup.sort();
|
||||
|
||||
assert_eq!(**lookup.first().unwrap(),
|
||||
Record::new()
|
||||
.set_name(authority.get_origin().clone())
|
||||
.set_name(authority.origin().clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
@ -85,20 +85,20 @@ fn test_authority() {
|
||||
.clone());
|
||||
assert_eq!(**lookup.last().unwrap(),
|
||||
Record::new()
|
||||
.set_name(authority.get_origin().clone())
|
||||
.set_name(authority.origin().clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()))
|
||||
.clone());
|
||||
|
||||
assert!(!authority.lookup(authority.get_origin(),
|
||||
assert!(!authority.lookup(authority.origin(),
|
||||
RecordType::TXT,
|
||||
false,
|
||||
SupportedAlgorithms::new())
|
||||
.is_empty());
|
||||
|
||||
let mut lookup: Vec<_> = authority.lookup(authority.get_origin(),
|
||||
let mut lookup: Vec<_> = authority.lookup(authority.origin(),
|
||||
RecordType::TXT,
|
||||
false,
|
||||
SupportedAlgorithms::new());
|
||||
@ -106,7 +106,7 @@ fn test_authority() {
|
||||
|
||||
assert_eq!(**lookup.first().unwrap(),
|
||||
Record::new()
|
||||
.set_name(authority.get_origin().clone())
|
||||
.set_name(authority.origin().clone())
|
||||
.set_ttl(60)
|
||||
.set_rr_type(RecordType::TXT)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
@ -115,14 +115,14 @@ fn test_authority() {
|
||||
.to_string()])))
|
||||
.clone());
|
||||
|
||||
assert_eq!(**authority.lookup(authority.get_origin(),
|
||||
assert_eq!(**authority.lookup(authority.origin(),
|
||||
RecordType::A,
|
||||
false,
|
||||
SupportedAlgorithms::new())
|
||||
.first()
|
||||
.unwrap(),
|
||||
Record::new()
|
||||
.set_name(authority.get_origin().clone())
|
||||
.set_name(authority.origin().clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::A)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
@ -172,7 +172,7 @@ fn test_prerequisites() {
|
||||
|
||||
// * ANY ANY empty Name is in use
|
||||
assert!(authority.verify_prerequisites(&[Record::new()
|
||||
.set_name(authority.get_origin().clone())
|
||||
.set_name(authority.origin().clone())
|
||||
.set_ttl(0)
|
||||
.set_dns_class(DNSClass::ANY)
|
||||
.set_rr_type(RecordType::ANY)
|
||||
@ -190,7 +190,7 @@ fn test_prerequisites() {
|
||||
|
||||
// * ANY rrset empty RRset exists (value independent)
|
||||
assert!(authority.verify_prerequisites(&[Record::new()
|
||||
.set_name(authority.get_origin().clone())
|
||||
.set_name(authority.origin().clone())
|
||||
.set_ttl(0)
|
||||
.set_dns_class(DNSClass::ANY)
|
||||
.set_rr_type(RecordType::A)
|
||||
@ -216,7 +216,7 @@ fn test_prerequisites() {
|
||||
.clone()])
|
||||
.is_ok());
|
||||
assert_eq!(authority.verify_prerequisites(&[Record::new()
|
||||
.set_name(authority.get_origin().clone())
|
||||
.set_name(authority.origin().clone())
|
||||
.set_ttl(0)
|
||||
.set_dns_class(DNSClass::NONE)
|
||||
.set_rr_type(RecordType::ANY)
|
||||
@ -234,7 +234,7 @@ fn test_prerequisites() {
|
||||
.clone()])
|
||||
.is_ok());
|
||||
assert_eq!(authority.verify_prerequisites(&[Record::new()
|
||||
.set_name(authority.get_origin().clone())
|
||||
.set_name(authority.origin().clone())
|
||||
.set_ttl(0)
|
||||
.set_dns_class(DNSClass::NONE)
|
||||
.set_rr_type(RecordType::A)
|
||||
@ -244,7 +244,7 @@ fn test_prerequisites() {
|
||||
|
||||
// * zone rrset rr RRset exists (value dependent)
|
||||
assert!(authority.verify_prerequisites(&[Record::new()
|
||||
.set_name(authority.get_origin().clone())
|
||||
.set_name(authority.origin().clone())
|
||||
.set_ttl(0)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rr_type(RecordType::A)
|
||||
@ -253,7 +253,7 @@ fn test_prerequisites() {
|
||||
.is_ok());
|
||||
// wrong class
|
||||
assert_eq!(authority.verify_prerequisites(&[Record::new()
|
||||
.set_name(authority.get_origin().clone())
|
||||
.set_name(authority.origin().clone())
|
||||
.set_ttl(0)
|
||||
.set_dns_class(DNSClass::CH)
|
||||
.set_rr_type(RecordType::A)
|
||||
@ -277,7 +277,7 @@ fn test_prerequisites() {
|
||||
Err(ResponseCode::NXRRSet));
|
||||
// wrong IP
|
||||
assert_eq!(authority.verify_prerequisites(&[Record::new()
|
||||
.set_name(authority.get_origin().clone())
|
||||
.set_name(authority.origin().clone())
|
||||
.set_ttl(0)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rr_type(RecordType::A)
|
||||
@ -459,7 +459,7 @@ fn test_update() {
|
||||
let new_name = Name::new().label("new").label("example").label("com");
|
||||
let www_name = Name::new().label("www").label("example").label("com");
|
||||
let mut authority: Authority = create_example();
|
||||
let serial = authority.get_serial();
|
||||
let serial = authority.serial();
|
||||
|
||||
authority.set_allow_update(true);
|
||||
|
||||
@ -527,7 +527,7 @@ fn test_update() {
|
||||
false,
|
||||
SupportedAlgorithms::new()),
|
||||
add_record.iter().collect::<Vec<&Record>>());
|
||||
assert_eq!(serial + 1, authority.get_serial());
|
||||
assert_eq!(serial + 1, authority.serial());
|
||||
|
||||
let add_www_record = &[Record::new()
|
||||
.set_name(www_name.clone())
|
||||
@ -537,7 +537,7 @@ fn test_update() {
|
||||
.set_rdata(RData::A(Ipv4Addr::new(10, 0, 0, 1)))
|
||||
.clone()];
|
||||
assert!(authority.update_records(add_www_record, true).expect("update failed"));
|
||||
assert_eq!(serial + 2, authority.get_serial());
|
||||
assert_eq!(serial + 2, authority.serial());
|
||||
|
||||
{
|
||||
let mut www_rrset = authority.lookup(&www_name,
|
||||
@ -562,7 +562,7 @@ fn test_update() {
|
||||
.set_rdata(RData::A(Ipv4Addr::new(93, 184, 216, 24)))
|
||||
.clone()];
|
||||
assert!(authority.update_records(del_record, true).expect("update failed"));
|
||||
assert_eq!(serial + 3, authority.get_serial());
|
||||
assert_eq!(serial + 3, authority.serial());
|
||||
{
|
||||
println!("after delete of specific record: {:?}",
|
||||
authority.lookup(&new_name,
|
||||
@ -585,7 +585,7 @@ fn test_update() {
|
||||
.set_rdata(RData::A(Ipv4Addr::new(10, 0, 0, 1)))
|
||||
.clone()];
|
||||
assert!(authority.update_records(del_record, true).expect("update failed"));
|
||||
assert_eq!(serial + 4, authority.get_serial());
|
||||
assert_eq!(serial + 4, authority.serial());
|
||||
{
|
||||
let mut www_rrset = authority.lookup(&www_name,
|
||||
RecordType::ANY,
|
||||
@ -606,7 +606,7 @@ fn test_update() {
|
||||
.set_rdata(RData::NULL(NULL::new()))
|
||||
.clone()];
|
||||
assert!(authority.update_records(del_record, true).expect("update failed"));
|
||||
assert_eq!(serial + 5, authority.get_serial());
|
||||
assert_eq!(serial + 5, authority.serial());
|
||||
let mut removed_a_vec: Vec<_> = vec![Record::new()
|
||||
.set_name(www_name.clone())
|
||||
.set_ttl(86400)
|
||||
@ -657,14 +657,14 @@ fn test_update() {
|
||||
false,
|
||||
SupportedAlgorithms::new())
|
||||
.is_empty());
|
||||
assert_eq!(serial + 6, authority.get_serial());
|
||||
assert_eq!(serial + 6, authority.serial());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zone_signing() {
|
||||
let authority: Authority = create_secure_example();
|
||||
|
||||
let results = authority.lookup(&authority.get_origin(),
|
||||
let results = authority.lookup(&authority.origin(),
|
||||
RecordType::AXFR,
|
||||
true,
|
||||
SupportedAlgorithms::all());
|
||||
@ -714,7 +714,7 @@ fn test_journal() {
|
||||
journal.schema_up().unwrap();
|
||||
|
||||
let mut authority = create_example();
|
||||
authority.journal(journal);
|
||||
authority.set_journal(journal);
|
||||
authority.persist_to_journal().unwrap();
|
||||
|
||||
let new_name = Name::new().label("new").label("example").label("com");
|
||||
@ -740,12 +740,12 @@ fn test_journal() {
|
||||
assert!(delete_rrset.is_empty());
|
||||
|
||||
// that record should have been recorded... let's reload the journal and see if we get it.
|
||||
let mut recovered_authority = Authority::new(authority.get_origin().clone(),
|
||||
let mut recovered_authority = Authority::new(authority.origin().clone(),
|
||||
BTreeMap::new(),
|
||||
ZoneType::Master,
|
||||
false,
|
||||
false);
|
||||
recovered_authority.recover_with_journal(authority.get_journal().expect("journal not Some"))
|
||||
recovered_authority.recover_with_journal(authority.journal().expect("journal not Some"))
|
||||
.expect("recovery");
|
||||
|
||||
// assert that the correct set of records is there.
|
||||
@ -768,11 +768,11 @@ fn test_recovery() {
|
||||
journal.schema_up().unwrap();
|
||||
|
||||
let mut authority = create_example();
|
||||
authority.journal(journal);
|
||||
authority.set_journal(journal);
|
||||
authority.persist_to_journal().unwrap();
|
||||
|
||||
let journal = authority.get_journal().unwrap();
|
||||
let mut recovered_authority = Authority::new(authority.get_origin().clone(),
|
||||
let journal = authority.journal().unwrap();
|
||||
let mut recovered_authority = Authority::new(authority.origin().clone(),
|
||||
BTreeMap::new(),
|
||||
ZoneType::Master,
|
||||
false,
|
||||
@ -780,20 +780,20 @@ fn test_recovery() {
|
||||
|
||||
recovered_authority.recover_with_journal(journal).expect("recovery");
|
||||
|
||||
assert_eq!(recovered_authority.get_records().len(),
|
||||
authority.get_records().len());
|
||||
assert_eq!(recovered_authority.get_soa(), authority.get_soa());
|
||||
assert!(recovered_authority.get_records().iter().all(|(rr_key, rr_set)| {
|
||||
assert_eq!(recovered_authority.records().len(),
|
||||
authority.records().len());
|
||||
assert_eq!(recovered_authority.soa(), authority.soa());
|
||||
assert!(recovered_authority.records().iter().all(|(rr_key, rr_set)| {
|
||||
let other_rr_set =
|
||||
authority.get_records().get(rr_key).expect(&format!("key doesn't exist: {:?}", rr_key));
|
||||
authority.records().get(rr_key).expect(&format!("key doesn't exist: {:?}", rr_key));
|
||||
rr_set.iter().zip(other_rr_set.iter()).all(|(record, other_record)| {
|
||||
record.ttl() == other_record.ttl() &&
|
||||
record.rdata() == other_record.rdata()
|
||||
})
|
||||
}));
|
||||
|
||||
assert!(authority.get_records().iter().all(|(rr_key, rr_set)| {
|
||||
let other_rr_set = recovered_authority.get_records()
|
||||
assert!(authority.records().iter().all(|(rr_key, rr_set)| {
|
||||
let other_rr_set = recovered_authority.records()
|
||||
.get(rr_key)
|
||||
.expect(&format!("key doesn't exist: {:?}", rr_key));
|
||||
rr_set.iter().zip(other_rr_set.iter()).all(|(record, other_record)| {
|
||||
|
@ -114,8 +114,8 @@ pub fn create_test() -> Authority {
|
||||
fn test_catalog_lookup() {
|
||||
let example = create_example();
|
||||
let test = create_test();
|
||||
let origin = example.get_origin().clone();
|
||||
let test_origin = test.get_origin().clone();
|
||||
let origin = example.origin().clone();
|
||||
let test_origin = test.origin().clone();
|
||||
|
||||
let mut catalog: Catalog = Catalog::new();
|
||||
catalog.upsert(origin.clone(), example);
|
||||
@ -173,7 +173,7 @@ fn test_catalog_lookup() {
|
||||
#[test]
|
||||
fn test_catalog_nx_soa() {
|
||||
let example = create_example();
|
||||
let origin = example.get_origin().clone();
|
||||
let origin = example.origin().clone();
|
||||
|
||||
let mut catalog: Catalog = Catalog::new();
|
||||
catalog.upsert(origin.clone(), example);
|
||||
@ -207,7 +207,7 @@ fn test_catalog_nx_soa() {
|
||||
#[test]
|
||||
fn test_axfr() {
|
||||
let test = create_test();
|
||||
let origin = test.get_origin().clone();
|
||||
let origin = test.origin().clone();
|
||||
let soa = Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(3600)
|
||||
|
@ -39,7 +39,7 @@ use common::authority::create_example;
|
||||
fn test_query_nonet() {
|
||||
let authority = create_example();
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
catalog.upsert(authority.origin().clone(), authority);
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (stream, sender) = TestClientStream::new(catalog);
|
||||
@ -149,7 +149,7 @@ fn test_query(client: &mut BasicClientHandle) -> Box<Future<Item = (), Error = (
|
||||
fn test_notify() {
|
||||
let authority = create_example();
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
catalog.upsert(authority.origin().clone(), authority);
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (stream, sender) = TestClientStream::new(catalog);
|
||||
@ -175,7 +175,7 @@ fn test_notify() {
|
||||
fn create_sig0_ready_client(io_loop: &Core) -> (BasicClientHandle, domain::Name) {
|
||||
let mut authority = create_example();
|
||||
authority.set_allow_update(true);
|
||||
let origin = authority.get_origin().clone();
|
||||
let origin = authority.origin().clone();
|
||||
|
||||
let rsa = Rsa::generate(512).unwrap();
|
||||
let key = KeyPair::from_rsa(rsa).unwrap();
|
||||
@ -206,7 +206,7 @@ fn create_sig0_ready_client(io_loop: &Core) -> (BasicClientHandle, domain::Name)
|
||||
|
||||
// setup the catalog
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
catalog.upsert(authority.origin().clone(), authority);
|
||||
|
||||
let (stream, sender) = TestClientStream::new(catalog);
|
||||
let client = ClientFuture::new(stream, sender, io_loop.handle(), Some(signer));
|
||||
@ -791,7 +791,7 @@ impl fmt::Debug for NeverReturnsClientStream {
|
||||
fn test_timeout_query_nonet() {
|
||||
let authority = create_example();
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
catalog.upsert(authority.origin().clone(), authority);
|
||||
|
||||
let mut io_loop = Core::new().unwrap();
|
||||
let (stream, sender) = NeverReturnsClientStream::new();
|
||||
|
@ -53,7 +53,7 @@ impl ClientConnection for TestClientConnection {
|
||||
fn test_query_nonet() {
|
||||
let authority = create_example();
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
catalog.upsert(authority.origin().clone(), authority);
|
||||
|
||||
let client = SyncClient::new(TestClientConnection::new(catalog));
|
||||
|
||||
@ -120,7 +120,7 @@ fn test_secure_query_example_nonet() {
|
||||
let authority = create_secure_example();
|
||||
|
||||
let trust_anchor = {
|
||||
let signers = authority.get_secure_keys();
|
||||
let signers = authority.secure_keys();
|
||||
let public_key = signers.first().expect("expected a key in the authority").key();
|
||||
|
||||
let mut trust_anchor = TrustAnchor::new();
|
||||
@ -130,7 +130,7 @@ fn test_secure_query_example_nonet() {
|
||||
};
|
||||
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
catalog.upsert(authority.origin().clone(), authority);
|
||||
|
||||
let client = SecureSyncClient::new(TestClientConnection::new(catalog))
|
||||
.trust_anchor(trust_anchor)
|
||||
@ -232,7 +232,7 @@ fn test_nsec_query_example_nonet() {
|
||||
let authority = create_secure_example();
|
||||
|
||||
let trust_anchor = {
|
||||
let signers = authority.get_secure_keys();
|
||||
let signers = authority.secure_keys();
|
||||
let public_key = signers.first().expect("expected a key in the authority").key();
|
||||
|
||||
let mut trust_anchor = TrustAnchor::new();
|
||||
@ -242,7 +242,7 @@ fn test_nsec_query_example_nonet() {
|
||||
};
|
||||
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
catalog.upsert(authority.origin().clone(), authority);
|
||||
|
||||
let client = SecureSyncClient::new(TestClientConnection::new(catalog))
|
||||
.trust_anchor(trust_anchor)
|
||||
@ -344,7 +344,7 @@ fn test_nsec_query_type() {
|
||||
fn create_sig0_ready_client(mut catalog: Catalog) -> (SyncClient, domain::Name) {
|
||||
let mut authority = create_example();
|
||||
authority.set_allow_update(true);
|
||||
let origin = authority.get_origin().clone();
|
||||
let origin = authority.origin().clone();
|
||||
|
||||
let rsa = Rsa::generate(512).unwrap();
|
||||
let key = KeyPair::from_rsa(rsa).unwrap();
|
||||
@ -373,7 +373,7 @@ fn create_sig0_ready_client(mut catalog: Catalog) -> (SyncClient, domain::Name)
|
||||
.expect("to_vec failed"))));
|
||||
authority.upsert(auth_key, 0);
|
||||
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
catalog.upsert(authority.origin().clone(), authority);
|
||||
let client = SyncClient::with_signer(TestClientConnection::new(catalog), signer);
|
||||
|
||||
(client, origin)
|
||||
|
@ -170,7 +170,7 @@ pub fn create_secure_example() -> Authority {
|
||||
let key = KeyPair::from_rsa(rsa).unwrap();
|
||||
let signer = Signer::new(Algorithm::RSASHA256,
|
||||
key,
|
||||
authority.get_origin().clone(),
|
||||
authority.origin().clone(),
|
||||
Duration::weeks(1),
|
||||
true,
|
||||
true);
|
||||
|
@ -65,7 +65,7 @@ fn test_read_config() {
|
||||
vec![]),
|
||||
ZoneConfig::new("0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.\
|
||||
ip6.arpa"
|
||||
.into(),
|
||||
.into(),
|
||||
ZoneType::Master,
|
||||
"default/ipv6_1.zone".into(),
|
||||
None,
|
||||
@ -100,7 +100,9 @@ fn test_parse_toml() {
|
||||
assert_eq!(config.get_listen_addrs_ipv4(),
|
||||
vec![Ipv4Addr::new(0, 0, 0, 0)]);
|
||||
|
||||
let config: Config = "listen_addrs_ipv4 = [\"0.0.0.0\", \"127.0.0.1\"]".parse().unwrap();
|
||||
let config: Config = "listen_addrs_ipv4 = [\"0.0.0.0\", \"127.0.0.1\"]"
|
||||
.parse()
|
||||
.unwrap();
|
||||
assert_eq!(config.get_listen_addrs_ipv4(),
|
||||
vec![Ipv4Addr::new(0, 0, 0, 0), Ipv4Addr::new(127, 0, 0, 1)]);
|
||||
|
||||
@ -110,7 +112,8 @@ fn test_parse_toml() {
|
||||
|
||||
let config: Config = "listen_addrs_ipv6 = [\"::0\", \"::1\"]".parse().unwrap();
|
||||
assert_eq!(config.get_listen_addrs_ipv6(),
|
||||
vec![Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)]);
|
||||
vec![Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0),
|
||||
Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)]);
|
||||
|
||||
let config: Config = "tcp_request_timeout = 25".parse().unwrap();
|
||||
assert_eq!(config.get_tcp_request_timeout(), Duration::from_secs(25));
|
||||
@ -124,8 +127,7 @@ fn test_parse_toml() {
|
||||
|
||||
#[test]
|
||||
fn test_parse_zone_keys() {
|
||||
let config: Config =
|
||||
"
|
||||
let config: Config = "
|
||||
[[zones]]
|
||||
zone = \"example.com\"
|
||||
zone_type = \"Master\"
|
||||
@ -151,11 +153,16 @@ signer_name = \"ns.example.com.\"
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
assert_eq!(config.get_zones()[0].get_keys()[0].get_key_path(),
|
||||
assert_eq!(config.get_zones()[0].get_keys()[0].key_path(),
|
||||
Path::new("/path/to/my_ed25519.pem"));
|
||||
assert_eq!(config.get_zones()[0].get_keys()[0].get_algorithm().unwrap(),
|
||||
assert_eq!(config.get_zones()[0].get_keys()[0]
|
||||
.algorithm()
|
||||
.unwrap(),
|
||||
Algorithm::ED25519);
|
||||
assert_eq!(config.get_zones()[0].get_keys()[0].get_signer_name().unwrap().unwrap(),
|
||||
assert_eq!(config.get_zones()[0].get_keys()[0]
|
||||
.signer_name()
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Name::parse("ns.example.com.", None).unwrap());
|
||||
assert_eq!(config.get_zones()[0].get_keys()[0].is_zone_signing_key(),
|
||||
false);
|
||||
@ -163,11 +170,16 @@ signer_name = \"ns.example.com.\"
|
||||
true);
|
||||
assert_eq!(config.get_zones()[0].get_keys()[0].create_if_absent(), true);
|
||||
|
||||
assert_eq!(config.get_zones()[0].get_keys()[1].get_key_path(),
|
||||
assert_eq!(config.get_zones()[0].get_keys()[1].key_path(),
|
||||
Path::new("/path/to/my_rsa.pem"));
|
||||
assert_eq!(config.get_zones()[0].get_keys()[1].get_algorithm().unwrap(),
|
||||
assert_eq!(config.get_zones()[0].get_keys()[1]
|
||||
.algorithm()
|
||||
.unwrap(),
|
||||
Algorithm::RSASHA256);
|
||||
assert_eq!(config.get_zones()[0].get_keys()[1].get_signer_name().unwrap().unwrap(),
|
||||
assert_eq!(config.get_zones()[0].get_keys()[1]
|
||||
.signer_name()
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Name::parse("ns.example.com.", None).unwrap());
|
||||
assert_eq!(config.get_zones()[0].get_keys()[1].is_zone_signing_key(),
|
||||
false);
|
||||
@ -190,8 +202,8 @@ tls_cert = { path = \"path/to/some.pkcs12\", create_if_absent = true, \
|
||||
subject_name = \"ns.example.com\" }
|
||||
tls_listen_port = 8853
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(config.get_tls_listen_port(), 8853);
|
||||
assert_eq!(config.get_tls_cert().unwrap().get_path(),
|
||||
|
@ -14,7 +14,7 @@ use trust_dns_server::authority::persistence::CURRENT_VERSION;
|
||||
#[test]
|
||||
fn test_new_journal() {
|
||||
let conn = Connection::open_in_memory().expect("could not create in memory DB");
|
||||
assert_eq!(Journal::new(conn).expect("new Journal").get_schema_version(),
|
||||
assert_eq!(Journal::new(conn).expect("new Journal").schema_version(),
|
||||
-1);
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ fn test_init_journal() {
|
||||
let mut journal = Journal::new(conn).unwrap();
|
||||
let version = journal.schema_up().unwrap();
|
||||
assert_eq!(version, CURRENT_VERSION);
|
||||
assert_eq!(Journal::select_schema_version(journal.get_conn()).unwrap(),
|
||||
assert_eq!(Journal::select_schema_version(journal.conn()).unwrap(),
|
||||
CURRENT_VERSION);
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,7 @@ fn with_nonet<F>(test: F)
|
||||
let authority = create_secure_example();
|
||||
|
||||
let trust_anchor = {
|
||||
let signers = authority.get_secure_keys();
|
||||
let signers = authority.secure_keys();
|
||||
let public_key = signers
|
||||
.first()
|
||||
.expect("expected a key in the authority")
|
||||
@ -215,7 +215,7 @@ fn with_nonet<F>(test: F)
|
||||
};
|
||||
|
||||
let mut catalog = Catalog::new();
|
||||
catalog.upsert(authority.get_origin().clone(), authority);
|
||||
catalog.upsert(authority.origin().clone(), authority);
|
||||
|
||||
let io_loop = Core::new().unwrap();
|
||||
let (stream, sender) = TestClientStream::new(catalog);
|
||||
|
@ -241,7 +241,7 @@ fn client_thread_www<C: ClientConnection>(conn: C)
|
||||
|
||||
fn new_catalog() -> Catalog {
|
||||
let example = create_example();
|
||||
let origin = example.get_origin().clone();
|
||||
let origin = example.origin().clone();
|
||||
|
||||
let mut catalog: Catalog = Catalog::new();
|
||||
catalog.upsert(origin.clone(), example);
|
||||
|
@ -63,7 +63,7 @@ venera A 10.1.0.52
|
||||
// not validating everything, just one of each...
|
||||
|
||||
// SOA
|
||||
let soa_record = authority.get_soa().unwrap();
|
||||
let soa_record = authority.soa().unwrap();
|
||||
assert_eq!(RecordType::SOA, soa_record.rr_type());
|
||||
assert_eq!(&Name::new().label("isi").label("edu"),
|
||||
soa_record.name()); // i.e. the origin or domain
|
||||
|
Loading…
Reference in New Issue
Block a user