update TLS docs and others

This commit is contained in:
Benjamin Fry 2017-03-11 22:29:04 -08:00
parent 1e38d34bad
commit bb75b3bf3a
18 changed files with 274 additions and 202 deletions

View File

@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 0.10.1 (unreleased)
### Changed
- Fixed TLS documentation, and add more elsewhere; fixes #102
## 0.10.0
### Changed
- *Important* Possible breaking API change, the original Client has been renamed

View File

@ -26,6 +26,7 @@ readme = "../README.md"
# This is a small list of keywords used to categorize and search for this
# package.
keywords = ["DNS", "BIND", "dig", "named", "dnssec"]
categories = ["network-programming"]
# This is a string description of the license for this package. Currently
# crates.io will validate the license provided against a whitelist of known
@ -36,6 +37,9 @@ license = "MIT/Apache-2.0"
# custom build steps
build = "build.rs"
[badges]
travis-ci = { repository = "bluejekyll/trust-dns" }
[features]
default = ["openssl", "tls"]
tls = ["native-tls", "tokio-tls", "security-framework"]

View File

@ -19,7 +19,7 @@ use futures::Stream;
use tokio_core::reactor::Core;
use client::{ClientHandle, BasicClientHandle, ClientConnection, ClientFuture, SecureClientHandle};
use ::error::*;
use error::*;
use rr::{domain, DNSClass, IntoRecordSet, RecordType, Record};
use rr::dnssec::Signer;
#[cfg(any(feature = "openssl", feature = "ring"))]
@ -28,7 +28,7 @@ use op::Message;
/// Client trait which implements basic DNS Client operations.
///
/// As of 0.9.4, the Client is now a wrapper around the `ClientFuture`, which is a futures-rs
/// As of 0.10.0, the Client is now a wrapper around the `ClientFuture`, which is a futures-rs
/// and tokio-rs based implementation. This trait implements syncronous functions for ease of use.
///
/// There was a strong attempt to make it backwards compatible, but making it a drop in replacement
@ -58,8 +58,9 @@ pub trait Client<C: ClientHandle> {
query_class: DNSClass,
query_type: RecordType)
-> ClientResult<Message> {
self.get_io_loop()
.run(self.get_client_handle().query(name.clone(), query_class, query_type))
self.get_io_loop().run(self.get_client_handle().query(name.clone(),
query_class,
query_type))
}
/// Sends a NOTIFY message to the remote system
@ -78,8 +79,10 @@ pub trait Client<C: ClientHandle> {
-> ClientResult<Message>
where R: IntoRecordSet
{
self.get_io_loop()
.run(self.get_client_handle().notify(name, query_class, query_type, rrset))
self.get_io_loop().run(self.get_client_handle().notify(name,
query_class,
query_type,
rrset))
}
/// Sends a record to create on the server, this will fail if the record exists (atomicity
@ -326,8 +329,9 @@ pub trait Client<C: ClientHandle> {
zone_origin: domain::Name,
dns_class: DNSClass)
-> ClientResult<Message> {
self.get_io_loop()
.run(self.get_client_handle().delete_all(name_of_records, zone_origin, dns_class))
self.get_io_loop().run(self.get_client_handle().delete_all(name_of_records,
zone_origin,
dns_class))
}
}
@ -347,8 +351,8 @@ impl SyncClient {
/// # Arguments
///
/// * `client_connection` - the client_connection to use for all communication
pub fn new<CC: ClientConnection>(client_connection: CC) -> SyncClient
where <CC as ClientConnection>::MessageStream: Stream<Item=Vec<u8>, Error=io::Error> + 'static {
pub fn new<CC: ClientConnection>(client_connection: CC) -> SyncClient
where <CC as ClientConnection>::MessageStream: Stream<Item=Vec<u8>, Error=io::Error> + 'static{
let (io_loop, stream, stream_handle) = client_connection.unwrap();
let client = ClientFuture::new(stream, stream_handle, io_loop.handle(), None);
@ -368,7 +372,7 @@ impl SyncClient {
/// * `client_connection` - the client_connection to use for all communication
/// * `signer` - signer to use, this needs an associated private key
pub fn with_signer<CC: ClientConnection>(client_connection: CC, signer: Signer) -> SyncClient
where <CC as ClientConnection>::MessageStream: Stream<Item=Vec<u8>, Error=io::Error> + 'static {
where <CC as ClientConnection>::MessageStream: Stream<Item=Vec<u8>, Error=io::Error> + 'static{
let (io_loop, stream, stream_handle) = client_connection.unwrap();
let client = ClientFuture::new(stream, stream_handle, io_loop.handle(), Some(signer));
@ -390,6 +394,7 @@ impl Client<BasicClientHandle> for SyncClient {
}
}
/// A DNS client which will validate DNSSec records upon receipt
#[cfg(any(feature = "openssl", feature = "ring"))]
pub struct SecureSyncClient {
client_handle: RefCell<SecureClientHandle<BasicClientHandle>>,
@ -405,7 +410,7 @@ impl SecureSyncClient {
/// * `client_connection` - the client_connection to use for all communication
pub fn new<CC>(client_connection: CC) -> SecureSyncClientBuilder<CC>
where CC: ClientConnection,
<CC as ClientConnection>::MessageStream: Stream<Item=Vec<u8>, Error=io::Error> + 'static {
<CC as ClientConnection>::MessageStream: Stream<Item=Vec<u8>, Error=io::Error> + 'static{
SecureSyncClientBuilder {
client_connection: client_connection,
trust_anchor: None,
@ -439,8 +444,9 @@ impl SecureSyncClient {
query_class: DNSClass,
query_type: RecordType)
-> ClientResult<Message> {
self.get_io_loop()
.run(self.get_client_handle().query(query_name.clone(), query_class, query_type))
self.get_io_loop().run(self.get_client_handle().query(query_name.clone(),
query_class,
query_type))
}
}

View File

@ -20,7 +20,7 @@ use rand::Rng;
use rand;
use tokio_core::reactor::{Handle, Timeout};
use ::error::*;
use error::*;
use op::{Message, MessageType, OpCode, Query, UpdateMessage};
use rr::{domain, DNSClass, IntoRecordSet, RData, Record, RecordType};
use rr::dnssec::Signer;
@ -31,14 +31,17 @@ const QOS_MAX_RECEIVE_MSGS: usize = 100; // max number of messages to receive fr
/// A reference to a Sender of bytes returned from the creation of a UdpClientStream or TcpClientStream
pub type StreamHandle = UnboundedSender<Vec<u8>>;
/// Implementations of Sinks for sending DNS messages
pub trait ClientStreamHandle {
fn send(&mut self, buffer: Vec<u8>) -> io::Result<()>;
}
impl ClientStreamHandle for StreamHandle {
fn send(&mut self, buffer: Vec<u8>) -> io::Result<()> {
UnboundedSender::send(self, buffer)
.map_err(|_| io::Error::new(io::ErrorKind::Other, "unknown"))
UnboundedSender::send(self, buffer).map_err(|_| {
io::Error::new(io::ErrorKind::Other,
"unknown")
})
}
}
@ -115,10 +118,10 @@ impl<S: Stream<Item = Vec<u8>, Error = io::Error> + 'static> ClientFuture<S> {
signer: signer,
}
})
.flatten()
.map_err(|e| {
error!("error in Client: {}", e);
}));
.flatten()
.map_err(|e| {
error!("error in Client: {}", e);
}));
BasicClientHandle { message_sender: sender }
}
@ -338,8 +341,8 @@ impl ClientHandle for BasicClientHandle {
// conver the oneshot into a Box of a Future message and error.
Box::new(receiver.map_err(|c| ClientError::from(c))
.map(|result| result.into_future())
.flatten())
.map(|result| result.into_future())
.flatten())
}
}
@ -544,7 +547,9 @@ pub trait ClientHandle: Clone {
// for updates, the query section is used for the zone
let mut zone: Query = Query::new();
zone.set_name(zone_origin).set_query_class(rrset.dns_class()).set_query_type(RecordType::SOA);
zone.set_name(zone_origin)
.set_query_class(rrset.dns_class())
.set_query_type(RecordType::SOA);
// build the message
let mut message: Message = Message::new();
@ -615,7 +620,9 @@ pub trait ClientHandle: Clone {
// for updates, the query section is used for the zone
let mut zone: Query = Query::new();
zone.set_name(zone_origin).set_query_class(rrset.dns_class()).set_query_type(RecordType::SOA);
zone.set_name(zone_origin)
.set_query_class(rrset.dns_class())
.set_query_type(RecordType::SOA);
// build the message
let mut message: Message = Message::new();
@ -626,8 +633,7 @@ pub trait ClientHandle: Clone {
message.add_zone(zone);
if must_exist {
let mut prerequisite =
Record::with(rrset.name().clone(), rrset.record_type(), 0);
let mut prerequisite = Record::with(rrset.name().clone(), rrset.record_type(), 0);
prerequisite.set_dns_class(DNSClass::ANY);
message.add_pre_requisite(prerequisite);
}
@ -784,7 +790,9 @@ pub trait ClientHandle: Clone {
// for updates, the query section is used for the zone
let mut zone: Query = Query::new();
zone.set_name(zone_origin).set_query_class(rrset.dns_class()).set_query_type(RecordType::SOA);
zone.set_name(zone_origin)
.set_query_class(rrset.dns_class())
.set_query_type(RecordType::SOA);
// build the message
let mut message: Message = Message::new();
@ -852,7 +860,9 @@ pub trait ClientHandle: Clone {
// for updates, the query section is used for the zone
let mut zone: Query = Query::new();
zone.set_name(zone_origin).set_query_class(record.dns_class()).set_query_type(RecordType::SOA);
zone.set_name(zone_origin)
.set_query_class(record.dns_class())
.set_query_type(RecordType::SOA);
// build the message
let mut message: Message = Message::new();

View File

@ -12,10 +12,10 @@ use futures::Future;
use client::ClientHandle;
use client::rc_future::{rc_future, RcFuture};
use ::error::*;
use error::*;
use op::{Message, Query};
/// Will return memoized (cached) responses to queries
/// A ClienHandle for memoized (cached) responses to queries.
///
/// This wraps a ClientHandle, changing the implementation `send()` to store the response against
/// the Message.Query that was sent. This should reduce network traffic especially during things
@ -45,7 +45,10 @@ impl<H> ClientHandle for MemoizeClientHandle<H>
where H: ClientHandle
{
fn send(&mut self, message: Message) -> Box<Future<Item = Message, Error = ClientError>> {
let query = message.queries().first().expect("no query!").clone();
let query = message.queries()
.first()
.expect("no query!")
.clone();
if let Some(rc_future) = self.active_queries.borrow().get(&query) {
// FIXME check TTLs?
@ -72,10 +75,10 @@ impl<H> ClientHandle for MemoizeClientHandle<H>
#[cfg(test)]
mod test {
use std::cell::Cell;
use ::client::*;
use ::error::*;
use ::op::*;
use ::rr::*;
use client::*;
use error::*;
use op::*;
use rr::*;
use futures::*;
#[derive(Clone)]
@ -105,17 +108,29 @@ mod test {
let mut test2 = Message::new();
test2.add_query(Query::new().set_query_type(RecordType::AAAA).clone());
let result = client.send(test1.clone()).wait().ok().unwrap();
let result = client.send(test1.clone())
.wait()
.ok()
.unwrap();
assert_eq!(result.id(), 0);
let result = client.send(test2.clone()).wait().ok().unwrap();
let result = client.send(test2.clone())
.wait()
.ok()
.unwrap();
assert_eq!(result.id(), 1);
// should get the same result for each...
let result = client.send(test1).wait().ok().unwrap();
let result = client.send(test1)
.wait()
.ok()
.unwrap();
assert_eq!(result.id(), 0);
let result = client.send(test2).wait().ok().unwrap();
let result = client.send(test2)
.wait()
.ok()
.unwrap();
assert_eq!(result.id(), 1);
}

View File

@ -13,7 +13,7 @@ use std::rc::Rc;
use futures::*;
use client::ClientHandle;
use ::error::*;
use error::*;
use op::{Message, OpCode, Query};
use rr::{domain, DNSClass, RData, Record, RecordType};
use rr::dnssec::{Algorithm, KeyPair, SupportedAlgorithms, TrustAnchor};
@ -96,14 +96,17 @@ impl<H> ClientHandle for SecureClientHandle<H>
// backstop, this might need to be configurable at some point
if self.request_depth > 20 {
return Box::new(failed(ClientErrorKind::Message("exceeded max validation depth")
.into()));
.into()));
}
// dnssec only matters on queries.
if let OpCode::Query = message.op_code() {
// This will panic on no queries, that is a very odd type of request, isn't it?
// TODO: there should only be one
let query = message.queries().first().cloned().unwrap();
let query = message.queries()
.first()
.cloned()
.unwrap();
let client: SecureClientHandle<H> = self.clone_with_context();
// TODO: cache response of the server about understood algorithms
@ -115,7 +118,8 @@ impl<H> ClientHandle for SecureClientHandle<H>
// send along the algorithms which are supported by this client
let mut algorithms = SupportedAlgorithms::new();
#[cfg(feature = "openssl")] {
#[cfg(feature = "openssl")]
{
algorithms.set(Algorithm::RSASHA256);
algorithms.set(Algorithm::ECDSAP256SHA256);
algorithms.set(Algorithm::ECDSAP384SHA384);
@ -135,33 +139,33 @@ impl<H> ClientHandle for SecureClientHandle<H>
let dns_class = message.queries().first().map_or(DNSClass::IN, |q| q.query_class());
return Box::new(self.client
.send(message)
.and_then(move |message_response| {
// group the record sets by name and type
// each rrset type needs to validated independently
debug!("validating message_response: {}", message_response.id());
verify_rrsets(client, message_response, dns_class)
})
.and_then(move |verified_message| {
// at this point all of the message is verified.
// This is where NSEC (and possibly NSEC3) validation occurs
// As of now, only NSEC is supported.
if verified_message.answers().is_empty() {
let nsecs = verified_message.name_servers()
.iter()
.filter(|rr| rr.rr_type() == RecordType::NSEC)
.collect::<Vec<_>>();
.send(message)
.and_then(move |message_response| {
// group the record sets by name and type
// each rrset type needs to validated independently
debug!("validating message_response: {}", message_response.id());
verify_rrsets(client, message_response, dns_class)
})
.and_then(move |verified_message| {
// at this point all of the message is verified.
// This is where NSEC (and possibly NSEC3) validation occurs
// As of now, only NSEC is supported.
if verified_message.answers().is_empty() {
let nsecs = verified_message.name_servers()
.iter()
.filter(|rr| rr.rr_type() == RecordType::NSEC)
.collect::<Vec<_>>();
if !verify_nsec(&query, nsecs) {
// TODO change this to remove the NSECs, like we do for the others?
return Err(ClientErrorKind::Message("could not validate nxdomain \
if !verify_nsec(&query, nsecs) {
// TODO change this to remove the NSECs, like we do for the others?
return Err(ClientErrorKind::Message("could not validate nxdomain \
with NSEC")
.into());
}
.into());
}
}
Ok(verified_message)
}));
Ok(verified_message)
}));
}
self.client.send(message)
@ -233,10 +237,10 @@ fn verify_rrsets<H>(client: SecureClientHandle<H>,
.chain(message_result.additionals())
.filter(|rr| rr.rr_type() == RecordType::RRSIG)
.filter(|rr| if let &RData::SIG(ref rrsig) = rr.rdata() {
rrsig.type_covered() == record_type
} else {
false
})
rrsig.type_covered() == record_type
} else {
false
})
.cloned()
.collect();
@ -262,10 +266,10 @@ fn verify_rrsets<H>(client: SecureClientHandle<H>,
// return the full Message validator
Box::new(VerifyRrsetsFuture {
message_result: Some(message_result),
rrsets: rrsets_to_verify,
verified_rrsets: HashSet::new(),
})
message_result: Some(message_result),
rrsets: rrsets_to_verify,
verified_rrsets: HashSet::new(),
})
}
impl Future for VerifyRrsetsFuture {
@ -317,25 +321,25 @@ impl Future for VerifyRrsetsFuture {
let answers = message_result.take_answers()
.into_iter()
.filter(|record| {
self.verified_rrsets
.contains(&(record.name().clone(), record.rr_type()))
})
self.verified_rrsets.contains(&(record.name().clone(),
record.rr_type()))
})
.collect::<Vec<Record>>();
let name_servers = message_result.take_name_servers()
.into_iter()
.filter(|record| {
self.verified_rrsets
.contains(&(record.name().clone(), record.rr_type()))
})
self.verified_rrsets.contains(&(record.name().clone(),
record.rr_type()))
})
.collect::<Vec<Record>>();
let additionals = message_result.take_additionals()
.into_iter()
.filter(|record| {
self.verified_rrsets
.contains(&(record.name().clone(), record.rr_type()))
})
self.verified_rrsets.contains(&(record.name().clone(),
record.rr_type()))
})
.collect::<Vec<Record>>();
// add the filtered records back to the message
@ -414,16 +418,16 @@ fn verify_dnskey_rrset<H>(mut client: SecureClientHandle<H>,
.enumerate()
.filter(|&(_, rr)| rr.rr_type() == RecordType::DNSKEY)
.filter_map(|(i, rr)| if let &RData::DNSKEY(ref rdata) = rr.rdata() {
Some((i, rdata))
} else {
None
})
Some((i, rdata))
} else {
None
})
.filter_map(|(i, rdata)| if client.trust_anchor.contains(rdata.public_key()) {
debug!("in trust_anchor");
Some(i)
} else {
None
})
debug!("in trust_anchor");
Some(i)
} else {
None
})
.collect::<Vec<usize>>();
if !anchored_keys.is_empty() {
@ -445,10 +449,10 @@ fn verify_dnskey_rrset<H>(mut client: SecureClientHandle<H>,
.enumerate()
.filter(|&(_, rr)| rr.rr_type() == RecordType::DNSKEY)
.filter_map(|(i, rr)| if let &RData::DNSKEY(ref rdata) = rr.rdata() {
Some((i, rdata))
} else {
None
})
Some((i, rdata))
} else {
None
})
.filter(|&(_, key_rdata)| {
ds_message.answers()
.iter()
@ -555,13 +559,13 @@ fn verify_default_rrset<H>(client: SecureClientHandle<H>,
rrset.record_type);
// Special case for self-signed DNSKEYS, validate with itself...
if rrsigs.iter()
.filter(|rrsig| rrsig.rr_type() == RecordType::RRSIG)
.any(|rrsig| if let &RData::SIG(ref sig) = rrsig.rdata() {
if rrsigs.iter().filter(|rrsig| rrsig.rr_type() == RecordType::RRSIG).any(|rrsig| {
if let &RData::SIG(ref sig) = rrsig.rdata() {
return RecordType::DNSKEY == rrset.record_type && sig.signer_name() == &rrset.name;
} else {
panic!("expected a SIG here");
}) {
}
}) {
// in this case it was looks like a self-signed key, first validate the signature
// then return rrset. Like the standard case below, the DNSKEY is validated
// after this function. This function is only responsible for validating the signature
@ -649,11 +653,12 @@ fn verify_default_rrset<H>(client: SecureClientHandle<H>,
validation: {}, {:?}",
rrset.name,
rrset.record_type))
.into()));
.into()));
}
// as long as any of the verifcations is good, then the RRSET is valid.
let select = select_ok(verifications)
let select =
select_ok(verifications)
// getting here means at least one of the rrsigs succeeded...
.map(move |(rrset, rest)| {
drop(rest); // drop all others, should free up Rc
@ -695,11 +700,11 @@ fn verify_rrset_with_dnskey(dnskey: &DNSKEY, sig: &SIG, rrset: &Rrset) -> Client
.and_then(|rrset_hash| {
signer.verify(&rrset_hash, sig.sig())
.map(|_| {
debug!("verified rrset: {}, type: {:?}",
rrset.name,
rrset.record_type);
()
})
debug!("verified rrset: {}, type: {:?}",
rrset.name,
rrset.record_type);
()
})
.map_err(|e| e.into())
})
}
@ -782,15 +787,18 @@ fn verify_nsec(query: &Query, nsecs: Vec<&Record>) -> bool {
}
// based on the WTF? above, we will ignore any NSEC records of the same name
if nsecs.iter()
.filter(|r| query.name() != r.name())
.any(|r| query.name() > r.name() && {
if let &RData::NSEC(ref rdata) = r.rdata() {
query.name() < rdata.next_domain_name()
} else {
panic!("expected NSEC was {:?}", r.rr_type()) // valid panic, never should happen
if nsecs.iter().filter(|r| query.name() != r.name()).any(|r| {
query.name() > r.name() &&
{
if let &RData::NSEC(ref rdata) = r.rdata() {
query.name() < rdata.next_domain_name()
} else {
panic!("expected NSEC was {:?}", r.rr_type()) // valid panic, never should happen
}
}
}) {
return true;
}
}) { return true }
// TODO: need to validate ANY or *.domain record existance, which doesn't make sense since
// that would have been returned in the request

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
//! TCP protocol related components for DNS.
//! TCP protocol related components for DNS
mod tcp_client_connection;
mod tcp_client_stream;

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! TCP based DNS client
//! TCP based DNS client connection for Client impls
use std::net::SocketAddr;
use std::io;
@ -21,11 +21,13 @@ use futures::Future;
use tokio_core::net::TcpStream;
use tokio_core::reactor::Core;
use ::error::*;
use error::*;
use client::{ClientConnection, ClientStreamHandle};
use tcp::TcpClientStream;
/// TCP based DNS client
/// Tcp client connection
///
/// Use with `trust_dns::client::Client` impls
pub struct TcpClientConnection {
io_loop: Core,
tcp_client_stream: Box<Future<Item = TcpClientStream<TcpStream>, Error = io::Error>>,
@ -47,17 +49,17 @@ impl TcpClientConnection {
io_loop.handle());
Ok(TcpClientConnection {
io_loop: io_loop,
tcp_client_stream: tcp_client_stream,
client_stream_handle: handle,
})
io_loop: io_loop,
tcp_client_stream: tcp_client_stream,
client_stream_handle: handle,
})
}
}
impl ClientConnection for TcpClientConnection {
type MessageStream = TcpClientStream<TcpStream>;
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.tcp_client_stream, self.client_stream_handle)
}
}

View File

@ -17,6 +17,9 @@ use BufClientStreamHandle;
use tcp::TcpStream;
use client::ClientStreamHandle;
/// Tcp client stream
///
/// Use with `trust_dns::client::ClientFuture` impls
#[must_use = "futures do nothing unless polled"]
pub struct TcpClientStream<S> {
tcp_stream: TcpStream<S>,
@ -32,17 +35,15 @@ impl TcpClientStream<TokioTcpStream> {
Box<ClientStreamHandle>) {
let (stream_future, sender) = TcpStream::new(name_server, loop_handle);
let new_future: Box<Future<Item=TcpClientStream<TokioTcpStream>, Error=io::Error>> =
Box::new(stream_future.map(move |tcp_stream| {
TcpClientStream {
tcp_stream: tcp_stream,
}
}));
let new_future: Box<Future<Item = TcpClientStream<TokioTcpStream>, Error = io::Error>> =
Box::new(stream_future.map(move |tcp_stream| {
TcpClientStream { tcp_stream: tcp_stream }
}));
let sender = Box::new(BufClientStreamHandle {
name_server: name_server,
sender: sender,
});
name_server: name_server,
sender: sender,
});
(new_future, sender)
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
//! TCP protocol related components for DNS.
//! TLS protocol related components for DNS over TLS
mod tls_client_connection;
mod tls_client_stream;

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! TCP based DNS client
//! TLS based DNS client connection for Client impls
use std::net::SocketAddr;
use std::io;
@ -26,11 +26,13 @@ use openssl::x509::X509 as OpensslX509;
use security_framework::certificate::SecCertificate;
use tokio_core::reactor::Core;
use ::error::*;
use error::*;
use client::{ClientConnection, ClientStreamHandle};
use tls::{TlsClientStream, TlsClientStreamBuilder};
/// TCP based DNS client
/// Tls client connection
///
/// Use with `trust_dns::client::Client` impls
pub struct TlsClientConnection {
io_loop: Core,
tls_client_stream: Box<Future<Item = TlsClientStream, Error = io::Error>>,
@ -46,7 +48,7 @@ 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)
}
}
@ -54,11 +56,17 @@ impl ClientConnection for TlsClientConnection {
pub struct TlsClientConnectionBuilder(TlsClientStreamBuilder);
impl TlsClientConnectionBuilder {
/// Add a custom trusted peer certificate or certificate auhtority.
///
/// If this is the 'client' then the 'server' must have it associated as it's `identity`, or have had the `identity` signed by this certificate.
#[cfg(target_os = "macos")]
pub fn add_ca(&mut self, ca: SecCertificate) {
self.0.add_ca(ca);
}
/// Add a custom trusted peer certificate or certificate auhtority.
///
/// If this is the 'client' then the 'server' must have it associated as it's `identity`, or have had the `identity` signed by this certificate.
#[cfg(target_os = "linux")]
pub fn add_ca(&mut self, ca: OpensslX509) {
self.0.add_ca(ca);
@ -77,7 +85,9 @@ impl TlsClientConnectionBuilder {
///
/// # Arguments
///
/// * `name_server` - address of the name server to use for queries
/// * `name_server` - IP and Port for the remote DNS resolver
/// * `subject_name` - The Subject Public Key Info (SPKI) name as associated to a certificate
/// * `loop_handle` - The reactor Core handle
pub fn build(self,
name_server: SocketAddr,
subject_name: String)
@ -86,9 +96,9 @@ impl TlsClientConnectionBuilder {
let (tls_client_stream, handle) = self.0.build(name_server, subject_name, io_loop.handle());
Ok(TlsClientConnection {
io_loop: io_loop,
tls_client_stream: tls_client_stream,
client_stream_handle: handle,
})
io_loop: io_loop,
tls_client_stream: tls_client_stream,
client_stream_handle: handle,
})
}
}

View File

@ -35,11 +35,17 @@ impl TlsClientStream {
pub struct TlsClientStreamBuilder(TlsStreamBuilder);
impl TlsClientStreamBuilder {
/// Add a custom trusted peer certificate or certificate auhtority.
///
/// If this is the 'client' then the 'server' must have it associated as it's `identity`, or have had the `identity` signed by this certificate.
#[cfg(target_os = "macos")]
pub fn add_ca(&mut self, ca: SecCertificate) {
self.0.add_ca(ca);
}
/// Add a custom trusted peer certificate or certificate auhtority.
///
/// If this is the 'client' then the 'server' must have it associated as it's `identity`, or have had the `identity` signed by this certificate.
#[cfg(target_os = "linux")]
pub fn add_ca(&mut self, ca: OpensslX509) {
self.0.add_ca(ca);
@ -47,10 +53,17 @@ impl TlsClientStreamBuilder {
/// Client side identity for client auth in TLS (aka mutual TLS auth)
#[cfg(feature = "mtls")]
pub fn identity(&mut self, pkcs12: Pkcs12) {
pub fn identity(&mut self, pkcs12: Pkcs12) {
self.0.identity(pkcs12);
}
/// Creates a new TlsStream to the specified name_server
///
/// # Arguments
///
/// * `name_server` - IP and Port for the remote DNS resolver
/// * `subject_name` - The Subject Public Key Info (SPKI) name as associated to a certificate
/// * `loop_handle` - The reactor Core handle
pub fn build
(self,
name_server: SocketAddr,
@ -63,9 +76,9 @@ pub fn identity(&mut self, pkcs12: Pkcs12) {
Box::new(stream_future.map(move |tls_stream| TcpClientStream::from_stream(tls_stream)));
let sender = Box::new(BufClientStreamHandle {
name_server: name_server,
sender: sender,
});
name_server: name_server,
sender: sender,
});
(new_future, sender)
}

View File

@ -78,14 +78,14 @@ impl TlsStream {
// if there was a pkcs12 associated, we'll add it to the identity
if let Some(pkcs12) = pkcs12 {
try!(tls.identity(pkcs12).map_err(|e| {
io::Error::new(io::ErrorKind::ConnectionRefused,
format!("tls error: {}", e))
}));
io::Error::new(io::ErrorKind::ConnectionRefused,
format!("tls error: {}", e))
}));
}
tls.build().map_err(|e| {
io::Error::new(io::ErrorKind::ConnectionRefused,
format!("tls error: {}", e))
})
io::Error::new(io::ErrorKind::ConnectionRefused,
format!("tls error: {}", e))
})
}
#[cfg(target_os = "macos")]
@ -107,14 +107,14 @@ impl TlsStream {
}));
}
builder.build().map_err(|e| {
io::Error::new(io::ErrorKind::ConnectionRefused,
format!("tls error: {}", e))
})
io::Error::new(io::ErrorKind::ConnectionRefused,
format!("tls error: {}", e))
})
}
/// Initializes a TcpStream with an existing tokio_core::net::TcpStream.
/// Initializes a TlsStream with an existing tokio_tls::TlsStream.
///
/// This is intended for use with a TcpListener and Incoming.
/// This is intended for use with a TlsListener and Incoming connections
pub fn from_tls_stream(stream: TokioTlsStream<TokioTcpStream>,
peer_addr: SocketAddr)
-> (Self, BufStreamHandle) {
@ -184,9 +184,6 @@ impl TlsStreamBuilder {
/// * `name_server` - IP and Port for the remote DNS resolver
/// * `subject_name` - The Subject Public Key Info (SPKI) name as associated to a certificate
/// * `loop_handle` - The reactor Core handle
/// * `certs` - list of trusted certificate authorities
/// * `pkcs12` - optional client identity for client auth (i.e. for mutual TLS authentication)
/// TODO: make a builder for the certifiates...
pub fn build(self,
name_server: SocketAddr,
subject_name: String,
@ -212,17 +209,19 @@ impl TlsStreamBuilder {
Box::new(tcp.and_then(move |tcp_stream| {
tls_connector.connect_async(&subject_name, tcp_stream)
.map(move |s| {
TcpStream::from_stream_with_receiver(s, name_server, outbound_messages)
})
TcpStream::from_stream_with_receiver(s,
name_server,
outbound_messages)
})
.map_err(|e| {
io::Error::new(io::ErrorKind::ConnectionRefused,
format!("tls error: {}", e))
})
io::Error::new(io::ErrorKind::ConnectionRefused,
format!("tls error: {}", e))
})
})
.map_err(|e| {
io::Error::new(io::ErrorKind::ConnectionRefused,
format!("tls error: {}", e))
}));
.map_err(|e| {
io::Error::new(io::ErrorKind::ConnectionRefused,
format!("tls error: {}", e))
}));
(stream, message_sender)
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
//! UDP protocol related components for DNS.
//! UDP protocol related components for DNS
mod udp_client_connection;
mod udp_client_stream;

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! UDP based DNS client
//! UDP based DNS client connection for Client impls
use std::io;
use std::net::SocketAddr;
@ -20,11 +20,13 @@ use std::net::SocketAddr;
use futures::Future;
use tokio_core::reactor::Core;
use ::error::*;
use error::*;
use client::{ClientConnection, ClientStreamHandle};
use udp::UdpClientStream;
/// UDP based DNS client
/// UDP based DNS Client connection
///
/// Use with `trust_dns::client::Client` impls
pub struct UdpClientConnection {
io_loop: Core,
udp_client_stream: Box<Future<Item = UdpClientStream, Error = io::Error>>,
@ -45,17 +47,17 @@ impl UdpClientConnection {
let (udp_client_stream, handle) = UdpClientStream::new(name_server, io_loop.handle());
Ok(UdpClientConnection {
io_loop: io_loop,
udp_client_stream: udp_client_stream,
client_stream_handle: handle,
})
io_loop: io_loop,
udp_client_stream: udp_client_stream,
client_stream_handle: handle,
})
}
}
impl ClientConnection for UdpClientConnection {
type MessageStream = UdpClientStream;
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.udp_client_stream, self.client_stream_handle)
}
}

View File

@ -99,7 +99,11 @@ fn root_ca() -> (PKey, X509Name, X509) {
x509_build.set_pubkey(&pkey).unwrap();
x509_build.set_serial_number(&serial).unwrap();
let basic_constraints = BasicConstraints::new().critical().ca().build().unwrap();
let basic_constraints = BasicConstraints::new()
.critical()
.ca()
.build()
.unwrap();
x509_build.append_extension(basic_constraints).unwrap();
let subject_alternative_name = SubjectAlternativeName::new()
@ -134,15 +138,11 @@ fn cert(subject_name: &str, ca_pkey: &PKey, ca_name: &X509Name, _: &X509) -> (PK
x509_build.set_pubkey(&pkey).unwrap();
x509_build.set_serial_number(&serial).unwrap();
let ext_key_usage = ExtendedKeyUsage::new()
.server_auth()
.build()
.unwrap();
let ext_key_usage = ExtendedKeyUsage::new().server_auth().build().unwrap();
x509_build.append_extension(ext_key_usage).unwrap();
let subject_key_identifier = SubjectKeyIdentifier::new()
.build(&x509_build.x509v3_context(None, None))
.unwrap();
let subject_key_identifier =
SubjectKeyIdentifier::new().build(&x509_build.x509v3_context(None, None)).unwrap();
x509_build.append_extension(subject_key_identifier).unwrap();
let authority_key_identifier = AuthorityKeyIdentifier::new()

View File

@ -27,6 +27,7 @@ readme = "../README.md"
# This is a small list of keywords used to categorize and search for this
# package.
keywords = ["DNS", "BIND", "dig", "named", "dnssec"]
categories = ["network-programming"]
# This is a string description of the license for this package. Currently
# crates.io will validate the license provided against a whitelist of known
@ -37,6 +38,9 @@ license = "MIT/Apache-2.0"
# custom build steps
build = "build.rs"
[badges]
travis-ci = { repository = "bluejekyll/trust-dns" }
[features]
default = ["trust-dns/tls"]
ring = ["trust-dns/ring"]

View File

@ -372,30 +372,24 @@ fn generate_cert(subject_name: &str,
try!(x509_build.set_pubkey(&pkey));
try!(x509_build.set_serial_number(&serial));
let ext_key_usage = try!(ExtendedKeyUsage::new()
.server_auth()
.client_auth()
.build());
let ext_key_usage = try!(ExtendedKeyUsage::new().server_auth().client_auth().build());
try!(x509_build.append_extension(ext_key_usage));
let subject_key_identifier =
try!(SubjectKeyIdentifier::new().build(&x509_build.x509v3_context(None, None)));
let subject_key_identifier = try!(SubjectKeyIdentifier::new()
.build(&x509_build.x509v3_context(None, None)));
try!(x509_build.append_extension(subject_key_identifier));
let authority_key_identifier = try!(AuthorityKeyIdentifier::new()
.keyid(true)
.build(&x509_build.x509v3_context(None, None)));
.keyid(true)
.build(&x509_build.x509v3_context(None, None)));
try!(x509_build.append_extension(authority_key_identifier));
let subject_alternative_name = try!(SubjectAlternativeName::new()
.dns(subject_name)
.build(&x509_build.x509v3_context(None, None)));
.dns(subject_name)
.build(&x509_build.x509v3_context(None, None)));
try!(x509_build.append_extension(subject_alternative_name));
let basic_constraints = try!(BasicConstraints::new()
.critical()
.ca()
.build());
let basic_constraints = try!(BasicConstraints::new().critical().ca().build());
try!(x509_build.append_extension(basic_constraints));
try!(x509_build.sign(&pkey, hash::MessageDigest::sha256()));