trust_dns_proto/openssl/
tls_client_stream.rs

1// Copyright 2015-2016 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use std::future::Future;
9use std::io;
10use std::net::SocketAddr;
11use std::pin::Pin;
12
13use futures_util::TryFutureExt;
14#[cfg(feature = "mtls")]
15use openssl::pkcs12::Pkcs12;
16use openssl::x509::X509;
17use tokio_openssl::SslStream as TokioTlsStream;
18
19use crate::error::ProtoError;
20use crate::iocompat::AsyncIoStdAsTokio;
21use crate::iocompat::AsyncIoTokioAsStd;
22use crate::tcp::{Connect, DnsTcpStream, TcpClientStream};
23use crate::xfer::BufDnsStreamHandle;
24
25use super::TlsStreamBuilder;
26
27/// A Type definition for the TLS stream
28pub type TlsClientStream<S> =
29    TcpClientStream<AsyncIoTokioAsStd<TokioTlsStream<AsyncIoStdAsTokio<S>>>>;
30
31/// A Builder for the TlsClientStream
32pub struct TlsClientStreamBuilder<S>(TlsStreamBuilder<S>);
33
34impl<S: DnsTcpStream> TlsClientStreamBuilder<S> {
35    /// Creates a builder for the construction of a TlsClientStream.
36    pub fn new() -> Self {
37        Self(TlsStreamBuilder::new())
38    }
39
40    /// Add a custom trusted peer certificate or certificate authority.
41    ///
42    /// 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.
43    pub fn add_ca(&mut self, ca: X509) {
44        self.0.add_ca(ca);
45    }
46
47    /// Add a custom trusted peer certificate or certificate authority encoded as a (binary) DER-encoded X.509 certificate.
48    ///
49    /// 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.
50    pub fn add_ca_der(&mut self, ca_der: &[u8]) -> io::Result<()> {
51        let ca = X509::from_der(ca_der)
52            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))?;
53        self.add_ca(ca);
54        Ok(())
55    }
56
57    /// Client side identity for client auth in TLS (aka mutual TLS auth)
58    #[cfg(feature = "mtls")]
59    pub fn identity(&mut self, pkcs12: Pkcs12) {
60        self.0.identity(pkcs12);
61    }
62
63    /// Sets the address to connect from.
64    pub fn bind_addr(&mut self, bind_addr: SocketAddr) {
65        self.0.bind_addr(bind_addr);
66    }
67
68    /// Creates a new TlsStream to the specified name_server with future
69    ///
70    /// # Arguments
71    ///
72    /// * `future` - future for underlying tcp stream
73    /// * `name_server` - IP and Port for the remote DNS resolver
74    /// * `dns_name` - The DNS name, Subject Public Key Info (SPKI) name, as associated to a certificate
75    #[allow(clippy::type_complexity)]
76    pub fn build_with_future<F>(
77        self,
78        future: F,
79        name_server: SocketAddr,
80        dns_name: String,
81    ) -> (
82        Pin<Box<dyn Future<Output = Result<TlsClientStream<S>, ProtoError>> + Send>>,
83        BufDnsStreamHandle,
84    )
85    where
86        F: Future<Output = io::Result<S>> + Send + Unpin + 'static,
87    {
88        let (stream_future, sender) = self.0.build_with_future(future, name_server, dns_name);
89
90        let new_future = Box::pin(
91            stream_future
92                .map_ok(TcpClientStream::from_stream)
93                .map_err(ProtoError::from),
94        );
95
96        (new_future, sender)
97    }
98}
99
100impl<S: DnsTcpStream> Default for TlsClientStreamBuilder<S> {
101    fn default() -> Self {
102        Self::new()
103    }
104}
105
106impl<S: Connect> TlsClientStreamBuilder<S> {
107    /// Creates a new TlsStream to the specified name_server
108    ///
109    /// # Arguments
110    ///
111    /// * `name_server` - IP and Port for the remote DNS resolver
112    /// * `bind_addr` - IP and port to connect from
113    /// * `dns_name` - The DNS name, Subject Public Key Info (SPKI) name, as associated to a certificate
114    #[allow(clippy::type_complexity)]
115    pub fn build(
116        self,
117        name_server: SocketAddr,
118        dns_name: String,
119    ) -> (
120        Pin<Box<dyn Future<Output = Result<TlsClientStream<S>, ProtoError>> + Send>>,
121        BufDnsStreamHandle,
122    ) {
123        let (stream_future, sender) = self.0.build(name_server, dns_name);
124
125        let new_future = Box::pin(
126            stream_future
127                .map_ok(TcpClientStream::from_stream)
128                .map_err(ProtoError::from),
129        );
130
131        (new_future, sender)
132    }
133}