hickory_proto/rustls/
tls_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// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! DNS over TLS I/O stream implementation for Rustls
9
10use std::future::Future;
11use std::io;
12use std::net::SocketAddr;
13use std::pin::Pin;
14use std::sync::Arc;
15
16use rustls::ClientConfig;
17use tokio;
18use tokio::net::TcpStream as TokioTcpStream;
19use tokio_rustls::TlsConnector;
20
21use crate::iocompat::{AsyncIoStdAsTokio, AsyncIoTokioAsStd};
22use crate::tcp::Connect;
23use crate::tcp::{DnsTcpStream, TcpStream};
24use crate::xfer::{BufDnsStreamHandle, StreamReceiver};
25
26/// Predefined type for abstracting the TlsClientStream with TokioTls
27pub type TokioTlsClientStream<S> = tokio_rustls::client::TlsStream<AsyncIoStdAsTokio<S>>;
28
29/// Predefined type for abstracting the TlsServerStream with TokioTls
30pub type TokioTlsServerStream = tokio_rustls::server::TlsStream<TokioTcpStream>;
31
32/// Predefined type for abstracting the base I/O TlsStream with TokioTls
33pub type TlsStream<S> = TcpStream<S>;
34
35/// Initializes a TlsStream with an existing tokio_tls::TlsStream.
36///
37/// This is intended for use with a TlsListener and Incoming connections
38pub fn tls_from_stream<S: DnsTcpStream>(
39    stream: S,
40    peer_addr: SocketAddr,
41) -> (TlsStream<S>, BufDnsStreamHandle) {
42    let (message_sender, outbound_messages) = BufDnsStreamHandle::new(peer_addr);
43    let stream = TcpStream::from_stream_with_receiver(stream, peer_addr, outbound_messages);
44    (stream, message_sender)
45}
46
47/// Creates a new TlsStream to the specified name_server
48///
49/// [RFC 7858](https://tools.ietf.org/html/rfc7858), DNS over TLS, May 2016
50///
51/// ```text
52/// 3.2.  TLS Handshake and Authentication
53///
54///   Once the DNS client succeeds in connecting via TCP on the well-known
55///   port for DNS over TLS, it proceeds with the TLS handshake [RFC5246],
56///   following the best practices specified in [BCP195].
57///
58///   The client will then authenticate the server, if required.  This
59///   document does not propose new ideas for authentication.  Depending on
60///   the privacy profile in use (Section 4), the DNS client may choose not
61///   to require authentication of the server, or it may make use of a
62///   trusted Subject Public Key Info (SPKI) Fingerprint pin set.
63///
64///   After TLS negotiation completes, the connection will be encrypted and
65///   is now protected from eavesdropping.
66/// ```
67///
68/// # Arguments
69///
70/// * `name_server` - IP and Port for the remote DNS resolver
71/// * `bind_addr` - IP and port to connect from
72/// * `dns_name` - The DNS name,  Subject Public Key Info (SPKI) name, as associated to a certificate
73#[allow(clippy::type_complexity)]
74pub fn tls_connect<S: Connect>(
75    name_server: SocketAddr,
76    dns_name: String,
77    client_config: Arc<ClientConfig>,
78) -> (
79    Pin<
80        Box<
81            dyn Future<
82                    Output = Result<
83                        TlsStream<AsyncIoTokioAsStd<TokioTlsClientStream<S>>>,
84                        io::Error,
85                    >,
86                > + Send,
87        >,
88    >,
89    BufDnsStreamHandle,
90) {
91    tls_connect_with_bind_addr(name_server, None, dns_name, client_config)
92}
93
94/// Creates a new TlsStream to the specified name_server connecting from a specific address.
95///
96/// # Arguments
97///
98/// * `name_server` - IP and Port for the remote DNS resolver
99/// * `bind_addr` - IP and port to connect from
100/// * `dns_name` - The DNS name,  Subject Public Key Info (SPKI) name, as associated to a certificate
101#[allow(clippy::type_complexity)]
102pub fn tls_connect_with_bind_addr<S: Connect>(
103    name_server: SocketAddr,
104    bind_addr: Option<SocketAddr>,
105    dns_name: String,
106    client_config: Arc<ClientConfig>,
107) -> (
108    Pin<
109        Box<
110            dyn Future<
111                    Output = Result<
112                        TlsStream<AsyncIoTokioAsStd<TokioTlsClientStream<S>>>,
113                        io::Error,
114                    >,
115                > + Send,
116        >,
117    >,
118    BufDnsStreamHandle,
119) {
120    let (message_sender, outbound_messages) = BufDnsStreamHandle::new(name_server);
121    let early_data_enabled = client_config.enable_early_data;
122    let tls_connector = TlsConnector::from(client_config).early_data(early_data_enabled);
123
124    // This set of futures collapses the next tcp socket into a stream which can be used for
125    //  sending and receiving tcp packets.
126    let stream = Box::pin(connect_tls(
127        tls_connector,
128        name_server,
129        bind_addr,
130        dns_name,
131        outbound_messages,
132    ));
133
134    (stream, message_sender)
135}
136
137/// Creates a new TlsStream to the specified name_server connecting from a specific address.
138///
139/// # Arguments
140///
141/// * `name_server` - IP and Port for the remote DNS resolver
142/// * `bind_addr` - IP and port to connect from
143/// * `dns_name` - The DNS name,  Subject Public Key Info (SPKI) name, as associated to a certificate
144#[allow(clippy::type_complexity)]
145pub fn tls_connect_with_future<S, F>(
146    future: F,
147    name_server: SocketAddr,
148    dns_name: String,
149    client_config: Arc<ClientConfig>,
150) -> (
151    Pin<
152        Box<
153            dyn Future<
154                    Output = Result<
155                        TlsStream<AsyncIoTokioAsStd<TokioTlsClientStream<S>>>,
156                        io::Error,
157                    >,
158                > + Send,
159        >,
160    >,
161    BufDnsStreamHandle,
162)
163where
164    S: DnsTcpStream,
165    F: Future<Output = io::Result<S>> + Send + Unpin + 'static,
166{
167    let (message_sender, outbound_messages) = BufDnsStreamHandle::new(name_server);
168    let early_data_enabled = client_config.enable_early_data;
169    let tls_connector = TlsConnector::from(client_config).early_data(early_data_enabled);
170
171    // This set of futures collapses the next tcp socket into a stream which can be used for
172    //  sending and receiving tcp packets.
173    let stream = Box::pin(connect_tls_with_future(
174        tls_connector,
175        future,
176        name_server,
177        dns_name,
178        outbound_messages,
179    ));
180
181    (stream, message_sender)
182}
183
184async fn connect_tls<S: Connect>(
185    tls_connector: TlsConnector,
186    name_server: SocketAddr,
187    bind_addr: Option<SocketAddr>,
188    dns_name: String,
189    outbound_messages: StreamReceiver,
190) -> io::Result<TcpStream<AsyncIoTokioAsStd<TokioTlsClientStream<S>>>> {
191    let tcp = S::connect_with_bind(name_server, bind_addr);
192    connect_tls_with_future(tls_connector, tcp, name_server, dns_name, outbound_messages).await
193}
194
195async fn connect_tls_with_future<S, F>(
196    tls_connector: TlsConnector,
197    future: F,
198    name_server: SocketAddr,
199    dns_name: String,
200    outbound_messages: StreamReceiver,
201) -> io::Result<TcpStream<AsyncIoTokioAsStd<TokioTlsClientStream<S>>>>
202where
203    S: DnsTcpStream,
204    F: Future<Output = io::Result<S>> + Send + Unpin,
205{
206    let dns_name = match dns_name.as_str().try_into() {
207        Ok(name) => name,
208        Err(_) => return Err(io::Error::new(io::ErrorKind::InvalidInput, "bad dns_name")),
209    };
210
211    let stream = future.await?;
212    let s = tls_connector
213        .connect(dns_name, AsyncIoStdAsTokio(stream))
214        .await
215        .map_err(|e| io::Error::new(io::ErrorKind::ConnectionRefused, format!("tls error: {e}")))?;
216
217    Ok(TcpStream::from_stream_with_receiver(
218        AsyncIoTokioAsStd(s),
219        name_server,
220        outbound_messages,
221    ))
222}