Skip to main content

hickory_resolver/
connection_provider.rs

1// Copyright 2015-2019 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
8use std::future::Future;
9use std::marker::Unpin;
10use std::net::{IpAddr, SocketAddr};
11#[cfg(feature = "__quic")]
12use std::net::{Ipv4Addr, Ipv6Addr};
13use std::pin::Pin;
14#[cfg(any(feature = "__tls", feature = "__https"))]
15use std::sync::Arc;
16
17#[cfg(feature = "__https")]
18use hickory_net::h2::HttpsClientStream;
19#[cfg(feature = "__tls")]
20use rustls::DigitallySignedStruct;
21#[cfg(feature = "__tls")]
22use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
23#[cfg(feature = "__tls")]
24use rustls::crypto::{CryptoProvider, verify_tls12_signature, verify_tls13_signature};
25#[cfg(feature = "__tls")]
26use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
27#[cfg(not(feature = "__tls"))]
28use tracing::warn;
29
30#[cfg(feature = "__h3")]
31use crate::net::h3::H3ClientStream;
32#[cfg(feature = "__quic")]
33use crate::net::quic::QuicClientStream;
34#[cfg(feature = "__tls")]
35use crate::net::tls::{client_config, default_provider, tls_exchange_with_bind_addr};
36use crate::{
37    config::{ConnectionConfig, ProtocolConfig},
38    name_server_pool::PoolContext,
39    net::{
40        NetError,
41        runtime::RuntimeProvider,
42        tcp::TcpClientStream,
43        udp::UdpClientStream,
44        xfer::{DnsExchange, DnsHandle},
45    },
46};
47
48/// Create `DnsHandle` with the help of `RuntimeProvider`.
49/// This trait is designed for customization.
50pub trait ConnectionProvider: 'static + Clone + Send + Sync + Unpin {
51    /// The handle to the connection for sending DNS requests.
52    type Conn: DnsHandle + Clone + Send + Sync + 'static;
53    /// Ths future is responsible for spawning any background tasks as necessary.
54    type FutureConn: Future<Output = Result<Self::Conn, NetError>> + Send + 'static;
55    /// Provider that handles the underlying I/O and timing.
56    type RuntimeProvider: RuntimeProvider;
57
58    /// Create a new connection.
59    fn new_connection(
60        &self,
61        ip: IpAddr,
62        config: &ConnectionConfig,
63        cx: &PoolContext,
64    ) -> Result<Self::FutureConn, NetError>;
65
66    /// Get a reference to a [`RuntimeProvider`].
67    fn runtime_provider(&self) -> &Self::RuntimeProvider;
68}
69
70impl<P: RuntimeProvider> ConnectionProvider for P {
71    type Conn = DnsExchange<P>;
72    type FutureConn = Pin<Box<dyn Future<Output = Result<Self::Conn, NetError>> + Send + 'static>>;
73    type RuntimeProvider = P;
74
75    fn new_connection(
76        &self,
77        ip: IpAddr,
78        config: &ConnectionConfig,
79        cx: &PoolContext,
80    ) -> Result<Self::FutureConn, NetError> {
81        let remote_addr = SocketAddr::new(ip, config.port);
82        match (&config.protocol, self.quic_binder()) {
83            (ProtocolConfig::Udp, _) => {
84                let (timeout, os_port_selection, avoid_local_udp_ports, bind_addr, provider) = (
85                    cx.options.timeout,
86                    cx.options.os_port_selection,
87                    cx.options.avoid_local_udp_ports.clone(),
88                    config.bind_addr,
89                    self.clone(),
90                );
91
92                Ok(Box::pin(async move {
93                    Ok(UdpClientStream::builder(remote_addr, provider)
94                        .with_timeout(Some(timeout))
95                        .with_os_port_selection(os_port_selection)
96                        .avoid_local_ports(avoid_local_udp_ports)
97                        .with_bind_addr(bind_addr)
98                        .exchange())
99                }))
100            }
101            (ProtocolConfig::Tcp, _) => Ok(Box::pin(TcpClientStream::exchange(
102                remote_addr,
103                config.bind_addr,
104                cx.options.timeout,
105                Some(cx.options.max_active_requests),
106                self.clone(),
107            ))),
108            #[cfg(feature = "__tls")]
109            (ProtocolConfig::Tls { server_name }, _) => {
110                let Ok(server_name) = ServerName::try_from(&**server_name) else {
111                    return Err(NetError::from(format!(
112                        "invalid server name: {server_name}"
113                    )));
114                };
115
116                let server_name = server_name.to_owned();
117                Ok(Box::pin(tls_exchange_with_bind_addr(
118                    remote_addr,
119                    config.bind_addr,
120                    server_name,
121                    cx.tls.clone(),
122                    cx.options.timeout,
123                    Some(cx.options.max_active_requests),
124                    self.clone(),
125                )))
126            }
127            #[cfg(feature = "__https")]
128            (ProtocolConfig::Https { server_name, path }, _) => {
129                let mut builder =
130                    HttpsClientStream::builder(Arc::new(cx.tls.clone()), self.clone());
131                builder.request_timeout(cx.options.timeout);
132                if let Some(bind_addr) = config.bind_addr {
133                    builder.bind_addr(bind_addr);
134                }
135                Ok(Box::pin(builder.exchange(
136                    remote_addr,
137                    server_name.clone(),
138                    path.clone(),
139                )))
140            }
141
142            #[cfg(feature = "__quic")]
143            (ProtocolConfig::Quic { server_name }, Some(binder)) => {
144                let bind_addr = config.bind_addr.unwrap_or(match remote_addr {
145                    SocketAddr::V4(_) => SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0),
146                    SocketAddr::V6(_) => SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0),
147                });
148
149                Ok(Box::pin(
150                    QuicClientStream::builder()
151                        .crypto_config(cx.tls.clone())
152                        .request_timeout(cx.options.timeout)
153                        .exchange(
154                            binder.bind_quic(bind_addr, remote_addr)?,
155                            remote_addr,
156                            server_name.clone(),
157                            self.clone(),
158                        ),
159                ))
160            }
161            #[cfg(feature = "__h3")]
162            (
163                ProtocolConfig::H3 {
164                    server_name,
165                    path,
166                    disable_grease,
167                },
168                Some(binder),
169            ) => {
170                let bind_addr = config.bind_addr.unwrap_or(match remote_addr {
171                    SocketAddr::V4(_) => SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0),
172                    SocketAddr::V6(_) => SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0),
173                });
174
175                Ok(Box::pin(
176                    H3ClientStream::builder()
177                        .crypto_config(cx.tls.clone())
178                        .disable_grease(*disable_grease)
179                        .request_timeout(cx.options.timeout)
180                        .exchange(
181                            binder.bind_quic(bind_addr, remote_addr)?,
182                            remote_addr,
183                            server_name.clone(),
184                            path.clone(),
185                            self.clone(),
186                        ),
187                ))
188            }
189            #[cfg(feature = "__quic")]
190            (ProtocolConfig::Quic { .. }, None) => {
191                Err(NetError::from("runtime provider does not support QUIC"))
192            }
193            #[cfg(feature = "__h3")]
194            (ProtocolConfig::H3 { .. }, None) => {
195                Err(NetError::from("runtime provider does not support QUIC"))
196            }
197        }
198    }
199
200    fn runtime_provider(&self) -> &Self::RuntimeProvider {
201        self
202    }
203}
204
205/// TLS configuration for the connection provider.
206pub struct TlsConfig {
207    /// The TLS configuration to use for secure connections.
208    #[cfg(feature = "__tls")]
209    pub config: rustls::ClientConfig,
210}
211
212impl TlsConfig {
213    /// Create a new `TlsConfig` with default settings.
214    pub fn new() -> Result<Self, NetError> {
215        Ok(Self {
216            #[cfg(feature = "__tls")]
217            config: client_config()?,
218        })
219    }
220
221    /// Disable certificate verification.
222    ///
223    /// This is typically unsafe and insecure, except in the context of RFC 9539 opportunistic
224    /// encryption which requires the peer certificate not be verified.
225    #[cfg(feature = "__tls")]
226    pub fn insecure_skip_verify(&mut self) {
227        self.config
228            .dangerous()
229            .set_certificate_verifier(Arc::new(NoCertificateVerification::default()))
230    }
231
232    /// Disable certificate verification.
233    ///
234    /// This is typically unsafe and insecure, except in the context of RFC 9539 opportunistic
235    /// encryption which requires the peer certificate not be verified.
236    #[cfg(not(feature = "__tls"))]
237    pub fn insecure_skip_verify(&mut self) {
238        warn!("asked to skip TLS verification without TLS support")
239    }
240}
241
242/// A rustls ServerCertVerifier that performs **no** certificate verification.
243///
244/// This should only be used with great care, as skipping certificate verification is insecure
245/// and could allow person-in-the-middle attacks.
246#[cfg(feature = "__tls")]
247#[derive(Debug)]
248struct NoCertificateVerification(CryptoProvider);
249
250#[cfg(feature = "__tls")]
251impl Default for NoCertificateVerification {
252    fn default() -> Self {
253        Self(default_provider())
254    }
255}
256
257#[cfg(feature = "__tls")]
258impl ServerCertVerifier for NoCertificateVerification {
259    fn verify_server_cert(
260        &self,
261        _end_entity: &CertificateDer<'_>,
262        _intermediates: &[CertificateDer<'_>],
263        _server_name: &ServerName<'_>,
264        _ocsp: &[u8],
265        _now: UnixTime,
266    ) -> Result<ServerCertVerified, rustls::Error> {
267        Ok(ServerCertVerified::assertion())
268    }
269
270    fn verify_tls12_signature(
271        &self,
272        message: &[u8],
273        cert: &CertificateDer<'_>,
274        dss: &DigitallySignedStruct,
275    ) -> Result<HandshakeSignatureValid, rustls::Error> {
276        verify_tls12_signature(
277            message,
278            cert,
279            dss,
280            &self.0.signature_verification_algorithms,
281        )
282    }
283
284    fn verify_tls13_signature(
285        &self,
286        message: &[u8],
287        cert: &CertificateDer<'_>,
288        dss: &DigitallySignedStruct,
289    ) -> Result<HandshakeSignatureValid, rustls::Error> {
290        verify_tls13_signature(
291            message,
292            cert,
293            dss,
294            &self.0.signature_verification_algorithms,
295        )
296    }
297
298    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
299        self.0.signature_verification_algorithms.supported_schemes()
300    }
301}
302
303#[cfg(all(
304    test,
305    feature = "tokio",
306    any(feature = "webpki-roots", feature = "rustls-platform-verifier"),
307    any(
308        feature = "__tls",
309        feature = "__https",
310        feature = "__quic",
311        feature = "__h3"
312    )
313))]
314mod tests {
315    #[cfg(feature = "__quic")]
316    use std::net::IpAddr;
317
318    use test_support::subscribe;
319
320    use crate::TokioResolver;
321    #[cfg(any(feature = "__tls", feature = "__https"))]
322    use crate::config::CLOUDFLARE;
323    #[cfg(any(
324        feature = "__tls",
325        feature = "__https",
326        feature = "__quic",
327        feature = "__h3"
328    ))]
329    use crate::config::GOOGLE;
330    use crate::config::ResolverConfig;
331    #[cfg(feature = "__quic")]
332    use crate::config::ServerGroup;
333    #[cfg(feature = "__quic")]
334    use crate::config::ServerOrderingStrategy;
335    use crate::net::runtime::TokioRuntimeProvider;
336    #[cfg(feature = "__quic")]
337    use crate::net::tls::client_config;
338
339    #[cfg(feature = "__h3")]
340    #[tokio::test]
341    async fn test_google_h3() {
342        subscribe();
343        h3_test(ResolverConfig::h3(&GOOGLE)).await
344    }
345
346    #[cfg(feature = "__h3")]
347    async fn h3_test(config: ResolverConfig) {
348        let mut builder =
349            TokioResolver::builder_with_config(config, TokioRuntimeProvider::default());
350        // Prefer IPv4 addresses for this test.
351        builder.options_mut().server_ordering_strategy = ServerOrderingStrategy::UserProvidedOrder;
352        let resolver = builder.build().unwrap();
353
354        let response = resolver
355            .lookup_ip("www.example.com.")
356            .await
357            .expect("failed to run lookup");
358
359        assert_ne!(response.iter().count(), 0);
360
361        // check if there is another connection created
362        let response = resolver
363            .lookup_ip("www.example.com.")
364            .await
365            .expect("failed to run lookup");
366
367        assert_ne!(response.iter().count(), 0);
368    }
369
370    #[cfg(feature = "__quic")]
371    #[tokio::test]
372    async fn test_adguard_quic() {
373        subscribe();
374
375        // AdGuard requires SNI.
376        let config = client_config().unwrap();
377
378        let group = ServerGroup {
379            ips: &[
380                IpAddr::from([94, 140, 14, 140]),
381                IpAddr::from([94, 140, 14, 141]),
382                IpAddr::from([0x2a10, 0x50c0, 0, 0, 0, 0, 0x1, 0xff]),
383                IpAddr::from([0x2a10, 0x50c0, 0, 0, 0, 0, 0x2, 0xff]),
384            ],
385            server_name: "unfiltered.adguard-dns.com",
386            path: "/dns-query",
387        };
388
389        quic_test(ResolverConfig::quic(&group), config).await
390    }
391
392    #[cfg(feature = "__quic")]
393    async fn quic_test(config: ResolverConfig, tls_config: rustls::ClientConfig) {
394        let mut resolver_builder =
395            TokioResolver::builder_with_config(config, TokioRuntimeProvider::default());
396        resolver_builder.options_mut().try_tcp_on_error = true;
397        // Prefer IPv4 addresses for this test.
398        resolver_builder.options_mut().server_ordering_strategy =
399            ServerOrderingStrategy::UserProvidedOrder;
400        resolver_builder = resolver_builder.with_tls_config(tls_config);
401        let resolver = resolver_builder.build().unwrap();
402
403        let response = resolver
404            .lookup_ip("www.example.com.")
405            .await
406            .expect("failed to run lookup");
407
408        assert_ne!(response.iter().count(), 0);
409
410        // check if there is another connection created
411        let response = resolver
412            .lookup_ip("www.example.com.")
413            .await
414            .expect("failed to run lookup");
415
416        assert_ne!(response.iter().count(), 0);
417    }
418
419    #[cfg(feature = "__https")]
420    #[tokio::test]
421    async fn test_google_https() {
422        subscribe();
423        https_test(ResolverConfig::https(&GOOGLE)).await
424    }
425
426    #[cfg(feature = "__https")]
427    #[tokio::test]
428    async fn test_cloudflare_https() {
429        subscribe();
430        https_test(ResolverConfig::https(&CLOUDFLARE)).await
431    }
432
433    #[cfg(feature = "__https")]
434    async fn https_test(config: ResolverConfig) {
435        let mut resolver_builder =
436            TokioResolver::builder_with_config(config, TokioRuntimeProvider::default());
437        resolver_builder.options_mut().try_tcp_on_error = true;
438        let resolver = resolver_builder.build().unwrap();
439
440        let response = resolver
441            .lookup_ip("www.example.com.")
442            .await
443            .expect("failed to run lookup");
444
445        assert_ne!(response.iter().count(), 0);
446
447        // check if there is another connection created
448        let response = resolver
449            .lookup_ip("www.example.com.")
450            .await
451            .expect("failed to run lookup");
452
453        assert_ne!(response.iter().count(), 0);
454    }
455
456    #[cfg(feature = "__tls")]
457    #[tokio::test]
458    async fn test_google_tls() {
459        subscribe();
460        tls_test(ResolverConfig::tls(&GOOGLE)).await
461    }
462
463    #[cfg(feature = "__tls")]
464    #[tokio::test]
465    async fn test_cloudflare_tls() {
466        subscribe();
467        tls_test(ResolverConfig::tls(&CLOUDFLARE)).await
468    }
469
470    #[cfg(feature = "__tls")]
471    async fn tls_test(config: ResolverConfig) {
472        let mut resolver_builder =
473            TokioResolver::builder_with_config(config, TokioRuntimeProvider::default());
474        resolver_builder.options_mut().try_tcp_on_error = true;
475        let resolver = resolver_builder.build().unwrap();
476
477        let response = resolver
478            .lookup_ip("www.example.com.")
479            .await
480            .expect("failed to run lookup");
481
482        assert_ne!(response.iter().count(), 0);
483    }
484}