trust-dns 0.11.0

TRust-DNS is a safe and secure DNS library. This is the Client library with DNSec support. DNSSec with NSEC validation for negative records, is complete. The client supports dynamic DNS with SIG0 authenticated requests, implementing easy to use high level funtions. TRust-DNS is based on the Tokio and Futures libraries, which means it should be easily integrated into other software that also use those libraries.
Documentation
// Copyright 2015-2016 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.

use std::net::SocketAddr;
use std::io;

use futures::{future, Future, IntoFuture};
use futures::sync::mpsc::unbounded;
use openssl::pkcs12::ParsedPkcs12;
use openssl::pkey::PKeyRef;
use openssl::ssl;
use openssl::ssl::{SslConnectorBuilder, SslConnector as TlsConnector, SslContextBuilder, SslMethod};
use openssl::stack::StackRef;
use openssl::x509::{X509, X509Ref};
use openssl::x509::store::X509StoreBuilder;
use tokio_core::net::TcpStream as TokioTcpStream;
use tokio_core::reactor::Handle;
use tokio_openssl::{SslConnectorExt, SslStream as TokioTlsStream};

use BufStreamHandle;
use tcp::TcpStream;

pub trait TlsIdentityExt {
    fn identity(&mut self, pkcs12: &ParsedPkcs12) -> io::Result<()> {
        self.identity_parts(&pkcs12.cert, &pkcs12.pkey, &pkcs12.chain)
    }

    fn identity_parts(&mut self,
                      cert: &X509Ref,
                      pkey: &PKeyRef,
                      chain: &StackRef<X509>)
                      -> io::Result<()>;
}

impl TlsIdentityExt for SslContextBuilder {
    fn identity_parts(&mut self,
                      cert: &X509Ref,
                      pkey: &PKeyRef,
                      chain: &StackRef<X509>)
                      -> io::Result<()> {
        try!(self.set_certificate(cert));
        try!(self.set_private_key(pkey));
        try!(self.check_private_key());
        for cert in chain {
            try!(self.add_extra_chain_cert(cert.to_owned()));
        }
        Ok(())
    }
}

/// A TlsStream counterpart to the TcpStream which embeds a secure TlsStream
pub type TlsStream = TcpStream<TokioTlsStream<TokioTcpStream>>;

impl TlsStream {
    /// A builder for associating trust information to the `TlsStream`.
    pub fn builder() -> TlsStreamBuilder {
        TlsStreamBuilder {
            ca_chain: vec![],
            identity: None,
        }
    }

    fn new(certs: Vec<X509>, pkcs12: Option<ParsedPkcs12>) -> io::Result<TlsConnector> {
        let mut tls = try!(SslConnectorBuilder::new(SslMethod::tls()).map_err(|e| {
            io::Error::new(io::ErrorKind::ConnectionRefused,
                           format!("tls error: {}", e))
        }));

        // mutable reference block
        {
            let mut openssl_ctx_builder = tls.builder_mut();

            // only want to support current TLS versions, 1.2 or future
            openssl_ctx_builder.set_options(ssl::SSL_OP_NO_SSLV2 | ssl::SSL_OP_NO_SSLV3 |
                                            ssl::SSL_OP_NO_TLSV1 |
                                            ssl::SSL_OP_NO_TLSV1_1);

            let mut store = try!(X509StoreBuilder::new().map_err(|e| {
                io::Error::new(io::ErrorKind::ConnectionRefused,
                               format!("tls error: {}", e))
            }));

            for cert in certs {
                try!(store
                         .add_cert(cert)
                         .map_err(|e| {
                                      io::Error::new(io::ErrorKind::ConnectionRefused,
                                                     format!("tls error: {}", e))
                                  }));
            }

            try!(openssl_ctx_builder
                     .set_verify_cert_store(store.build())
                     .map_err(|e| {
                                  io::Error::new(io::ErrorKind::ConnectionRefused,
                                                 format!("tls error: {}", e))
                              }));

            // if there was a pkcs12 associated, we'll add it to the identity
            if let Some(pkcs12) = pkcs12 {
                try!(openssl_ctx_builder.identity(&pkcs12));
            }
        }
        Ok(tls.build())
    }

    /// Initializes a TlsStream with an existing tokio_tls::TlsStream.
    ///
    /// This is intended for use with a TlsListener and Incoming connections
    pub fn from_tls_stream(stream: TokioTlsStream<TokioTcpStream>,
                           peer_addr: SocketAddr)
                           -> (Self, BufStreamHandle) {
        let (message_sender, outbound_messages) = unbounded();

        let stream = TcpStream::from_stream_with_receiver(stream, peer_addr, outbound_messages);

        (stream, message_sender)
    }
}

/// A builder for the TlsStream
pub struct TlsStreamBuilder {
    ca_chain: Vec<X509>,
    identity: Option<ParsedPkcs12>,
}

impl TlsStreamBuilder {
    /// 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
    pub fn add_ca(&mut self, ca: X509) {
        self.ca_chain.push(ca);
    }

    /// Client side identity for client auth in TLS (aka mutual TLS auth)
    #[cfg(feature = "mtls")]
    pub fn identity(&mut self, pkcs12: ParsedPkcs12) {
        self.identity = Some(pkcs12);
    }

    /// Creates a new TlsStream to the specified name_server
    ///
    /// [RFC 7858](https://tools.ietf.org/html/rfc7858), DNS over TLS, May 2016
    ///
    /// ```text
    /// 3.2.  TLS Handshake and Authentication
    ///
    ///   Once the DNS client succeeds in connecting via TCP on the well-known
    ///   port for DNS over TLS, it proceeds with the TLS handshake [RFC5246],
    ///   following the best practices specified in [BCP195].
    ///
    ///   The client will then authenticate the server, if required.  This
    ///   document does not propose new ideas for authentication.  Depending on
    ///   the privacy profile in use (Section 4), the DNS client may choose not
    ///   to require authentication of the server, or it may make use of a
    ///   trusted Subject Public Key Info (SPKI) Fingerprint pin set.
    ///
    ///   After TLS negotiation completes, the connection will be encrypted and
    ///   is now protected from eavesdropping.
    /// ```
    ///
    /// # 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,
                 subject_name: String,
                 loop_handle: &Handle)
                 -> (Box<Future<Item = TlsStream, Error = io::Error>>, BufStreamHandle) {
        let (message_sender, outbound_messages) = unbounded();
        let tls_connector =
            match TlsStream::new(self.ca_chain, self.identity) {
                Ok(c) => c,
                Err(e) => {
                return (Box::new(future::err(e).into_future().map_err(|e| {
                            io::Error::new(io::ErrorKind::ConnectionRefused,
                                           format!("tls error: {}", e))
                        })),
                        message_sender)
            }
            };

        let tcp = TokioTcpStream::connect(&name_server, &loop_handle);

        // This set of futures collapses the next tcp socket into a stream which can be used for
        //  sending and receiving tcp packets.
        let stream: Box<Future<Item = TlsStream, Error = io::Error>> =
            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)
                             })
                        .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)
    }
}