trust-dns-native-tls 0.1.3

TRust-DNS is a safe and secure DNS library. This is an extension for the TRust-DNS client. To use, disable the tls feature on TRust-DNS.
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.

//! Base TlsStream

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

use futures::{future, Future, IntoFuture};
use futures::sync::mpsc::unbounded;
use native_tls::{Certificate, Pkcs12, TlsConnector};
use native_tls::Protocol::{Tlsv11, Tlsv12};
use tokio_core::net::TcpStream as TokioTcpStream;
use tokio_core::reactor::Handle;
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> {
    let mut builder = try!(TlsConnector::builder().map_err(|e| {
            io::Error::new(io::ErrorKind::ConnectionRefused,
                           format!("tls error: {}", e))
        }));
    try!(builder
             .supported_protocols(&[Tlsv11, Tlsv12])
             .map_err(|e| {
                          io::Error::new(io::ErrorKind::ConnectionRefused,
                                         format!("tls error: {}", e))
                      }));

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

    if let Some(pkcs12) = pkcs12 {
        try!(builder
                 .identity(pkcs12)
                 .map_err(|e| {
                              io::Error::new(io::ErrorKind::ConnectionRefused,
                                             format!("tls error: {}", e))
                          }));
    }
    builder
        .build()
        .map_err(|e| {
                     io::Error::new(io::ErrorKind::ConnectionRefused,
                                    format!("tls error: {}", e))
                 })
}

/// Initializes a TlsStream with an existing tokio_tls::TlsStream.
///
/// This is intended for use with a TlsListener and Incoming connections
pub fn tls_from_stream(stream: TokioTlsStream<TokioTcpStream>,
                       peer_addr: SocketAddr)
                       -> (TlsStream, 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<Certificate>,
    identity: Option<Pkcs12>,
}

impl TlsStreamBuilder {
    /// Constructs a new TlsStreamBuilder
    pub fn new() -> TlsStreamBuilder {
        TlsStreamBuilder {
            ca_chain: vec![],
            identity: None,
        }
    }

    /// 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.
    pub fn add_ca(&mut self, ca: Certificate) {
        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: Pkcs12) {
        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 ::tls_stream::tls_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)
    }
}