net/
connector.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::collections::hash_map::HashMap;
6use std::convert::TryFrom;
7use std::sync::Arc;
8
9use futures::task::{Context, Poll};
10use futures::{Future, TryFutureExt};
11use http::uri::{Authority, Uri as Destination};
12use http_body_util::combinators::BoxBody;
13use hyper::body::Bytes;
14use hyper::rt::Executor;
15use hyper_rustls::HttpsConnector as HyperRustlsHttpsConnector;
16use hyper_util::client::legacy::Client;
17use hyper_util::client::legacy::connect::HttpConnector as HyperHttpConnector;
18use hyper_util::client::legacy::connect::proxy::Tunnel;
19use hyper_util::rt::TokioIo;
20use log::warn;
21use parking_lot::Mutex;
22use rustls::client::WebPkiServerVerifier;
23use rustls::{ClientConfig, RootCertStore};
24use rustls_pki_types::{CertificateDer, ServerName, UnixTime};
25use tokio::net::TcpStream;
26use tower::Service;
27
28use crate::async_runtime::spawn_task;
29use crate::hosts::replace_host;
30
31pub const BUF_SIZE: usize = 32768;
32
33#[derive(Clone)]
34pub struct ServoHttpConnector {
35    inner: HyperHttpConnector,
36}
37
38impl ServoHttpConnector {
39    fn new() -> ServoHttpConnector {
40        let mut inner = HyperHttpConnector::new();
41        inner.enforce_http(false);
42        inner.set_happy_eyeballs_timeout(None);
43        ServoHttpConnector { inner }
44    }
45}
46
47impl Service<Destination> for ServoHttpConnector {
48    type Response = TokioIo<TcpStream>;
49    type Error = ConnectionError;
50    type Future =
51        std::pin::Pin<Box<dyn Future<Output = Result<TokioIo<TcpStream>, ConnectionError>> + Send>>;
52
53    fn call(&mut self, dest: Destination) -> Self::Future {
54        // Perform host replacement when making the actual TCP connection.
55        let mut new_dest = dest.clone();
56        let mut parts = dest.into_parts();
57
58        if let Some(auth) = parts.authority {
59            let host = auth.host();
60            let host = replace_host(host);
61
62            let authority = if let Some(port) = auth.port() {
63                format!("{}:{}", host, port.as_str())
64            } else {
65                (*host).to_string()
66            };
67
68            if let Ok(authority) = Authority::from_maybe_shared(authority) {
69                parts.authority = Some(authority);
70                if let Ok(dest) = Destination::from_parts(parts) {
71                    new_dest = dest
72                }
73            }
74        }
75
76        Box::pin(
77            self.inner
78                .call(new_dest)
79                .map_err(|e| ConnectionError::HttpError(format!("{e}"))),
80        )
81    }
82
83    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
84        Ok(()).into()
85    }
86}
87
88pub type Connector = HyperRustlsHttpsConnector<ServoHttpConnector>;
89pub type TlsConfig = ClientConfig;
90
91#[derive(Clone, Debug, Default)]
92struct CertificateErrorOverrideManagerInternal {
93    /// A mapping of certificates and their hosts, which have seen certificate errors.
94    /// This is used to later create an override in this [CertificateErrorOverrideManager].
95    certificates_failing_to_verify: HashMap<ServerName<'static>, CertificateDer<'static>>,
96    /// A list of certificates that should be accepted despite encountering verification
97    /// errors.
98    overrides: Vec<CertificateDer<'static>>,
99}
100
101/// This data structure is used to track certificate verification errors and overrides.
102/// It tracks:
103///  - A list of [Certificate]s with verification errors mapped by their [ServerName]
104///  - A list of [Certificate]s for which to ignore verification errors.
105#[derive(Clone, Debug, Default)]
106pub struct CertificateErrorOverrideManager(Arc<Mutex<CertificateErrorOverrideManagerInternal>>);
107
108impl CertificateErrorOverrideManager {
109    pub fn new() -> Self {
110        Self(Default::default())
111    }
112
113    /// Add a certificate to this manager's list of certificates for which to ignore
114    /// validation errors.
115    pub fn add_override(&self, certificate: &CertificateDer<'static>) {
116        self.0.lock().overrides.push(certificate.clone());
117    }
118
119    /// Given the a string representation of a sever host name, remove information about
120    /// a [Certificate] with verification errors. If a certificate with
121    /// verification errors was found, return it, otherwise None.
122    pub(crate) fn remove_certificate_failing_verification(
123        &self,
124        host: &str,
125    ) -> Option<CertificateDer<'static>> {
126        let server_name = match ServerName::try_from(host) {
127            Ok(name) => name.to_owned(),
128            Err(error) => {
129                warn!("Could not convert host string into RustTLS ServerName: {error:?}");
130                return None;
131            },
132        };
133        self.0
134            .lock()
135            .certificates_failing_to_verify
136            .remove(&server_name)
137    }
138}
139
140#[derive(Clone, Debug)]
141pub enum CACertificates {
142    Default,
143    Override(RootCertStore),
144}
145
146/// Create a [TlsConfig] to use for managing a HTTP connection. This currently creates
147/// a rustls [ClientConfig].
148///
149/// FIXME: The `ignore_certificate_errors` argument ignores all certificate errors. This
150/// is used when running the WPT tests, because rustls currently rejects the WPT certificiate.
151/// See <https://github.com/servo/servo/issues/30080>
152pub fn create_tls_config(
153    ca_certificates: CACertificates,
154    ignore_certificate_errors: bool,
155    override_manager: CertificateErrorOverrideManager,
156) -> TlsConfig {
157    let verifier = CertificateVerificationOverrideVerifier::new(
158        ca_certificates,
159        ignore_certificate_errors,
160        override_manager,
161    );
162    rustls::ClientConfig::builder()
163        .dangerous()
164        .with_custom_certificate_verifier(Arc::new(verifier))
165        .with_no_client_auth()
166}
167
168#[derive(Clone)]
169struct TokioExecutor {}
170
171impl<F> Executor<F> for TokioExecutor
172where
173    F: Future<Output = ()> + 'static + std::marker::Send,
174{
175    fn execute(&self, fut: F) {
176        spawn_task(fut);
177    }
178}
179
180#[derive(Debug)]
181struct CertificateVerificationOverrideVerifier {
182    webpki_verifier: Arc<WebPkiServerVerifier>,
183    ignore_certificate_errors: bool,
184    override_manager: CertificateErrorOverrideManager,
185}
186
187impl CertificateVerificationOverrideVerifier {
188    fn new(
189        ca_certficates: CACertificates,
190        ignore_certificate_errors: bool,
191        override_manager: CertificateErrorOverrideManager,
192    ) -> Self {
193        let root_cert_store = match ca_certficates {
194            CACertificates::Default => rustls::RootCertStore {
195                roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
196            },
197            CACertificates::Override(root_cert_store) => root_cert_store,
198        };
199
200        Self {
201            // See https://github.com/rustls/rustls/blame/v/0.21.6/rustls/src/client/builder.rs#L141
202            // This is the default verifier for Rustls that we are wrapping.
203            webpki_verifier: WebPkiServerVerifier::builder(root_cert_store.into())
204                .build()
205                .unwrap(),
206            ignore_certificate_errors,
207            override_manager,
208        }
209    }
210}
211
212impl rustls::client::danger::ServerCertVerifier for CertificateVerificationOverrideVerifier {
213    fn verify_tls12_signature(
214        &self,
215        message: &[u8],
216        cert: &CertificateDer<'_>,
217        dss: &rustls::DigitallySignedStruct,
218    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
219        self.webpki_verifier
220            .verify_tls12_signature(message, cert, dss)
221    }
222
223    fn verify_tls13_signature(
224        &self,
225        message: &[u8],
226        cert: &CertificateDer<'_>,
227        dss: &rustls::DigitallySignedStruct,
228    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
229        self.webpki_verifier
230            .verify_tls13_signature(message, cert, dss)
231    }
232
233    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
234        self.webpki_verifier.supported_verify_schemes()
235    }
236
237    fn verify_server_cert(
238        &self,
239        end_entity: &CertificateDer<'_>,
240        intermediates: &[CertificateDer<'_>],
241        server_name: &ServerName<'_>,
242        ocsp_response: &[u8],
243        now: UnixTime,
244    ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
245        let error = match self.webpki_verifier.verify_server_cert(
246            end_entity,
247            intermediates,
248            server_name,
249            ocsp_response,
250            now,
251        ) {
252            Ok(result) => return Ok(result),
253            Err(error) => error,
254        };
255
256        if self.ignore_certificate_errors {
257            warn!("Ignoring certficate error: {error:?}");
258            return Ok(rustls::client::danger::ServerCertVerified::assertion());
259        }
260
261        // If there's an override for this certificate, just accept it.
262        for cert_with_exception in &*self.override_manager.0.lock().overrides {
263            if *end_entity == *cert_with_exception {
264                return Ok(rustls::client::danger::ServerCertVerified::assertion());
265            }
266        }
267        self.override_manager
268            .0
269            .lock()
270            .certificates_failing_to_verify
271            .insert(server_name.to_owned(), end_entity.clone().into_owned());
272        Err(error)
273    }
274}
275
276pub type BoxedBody = BoxBody<Bytes, hyper::Error>;
277
278fn create_maybe_proxy_connector() -> MaybeProxyConnector {
279    let network_http_proxy_uri = servo_config::pref!(network_http_proxy_uri);
280    if !network_http_proxy_uri.is_empty() {
281        if let Ok(http_proxy_uri) = network_http_proxy_uri.parse() {
282            log::info!("Using proxy specified via {:?}", http_proxy_uri);
283            return MaybeProxyConnector::Right(TunnelErrorMasker(Tunnel::new(
284                http_proxy_uri,
285                ServoHttpConnector::new(),
286            )));
287        }
288    }
289
290    MaybeProxyConnector::Left(ServoHttpConnector::new())
291}
292
293/// Either a proxy tunnel or the ServoHttpConnector
294pub type MaybeProxyConnector = tower::util::Either<ServoHttpConnector, TunnelErrorMasker>;
295
296#[derive(Debug)]
297/// The error type for the MaybeProxyConnector
298pub enum ConnectionError {
299    HttpError(String),
300    // It looks like currently the type is not exported.
301    ProxyError(String),
302}
303
304impl std::fmt::Display for ConnectionError {
305    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
306        write!(f, "{self:?}")
307    }
308}
309
310impl std::error::Error for ConnectionError {}
311
312#[derive(Clone)]
313/// This is just used to give us control over the error types 'Tunnel<>' returns.
314pub struct TunnelErrorMasker(Tunnel<ServoHttpConnector>);
315
316// Just forward everything to the inner type except that we modify the errors returned.
317impl Service<Destination> for TunnelErrorMasker {
318    type Response = TokioIo<TcpStream>;
319    type Error = ConnectionError;
320    type Future =
321        std::pin::Pin<Box<dyn Future<Output = Result<TokioIo<TcpStream>, ConnectionError>> + Send>>;
322
323    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
324        self.0
325            .poll_ready(cx)
326            .map_err(|e| ConnectionError::ProxyError(format!("{e}")))
327    }
328
329    fn call(&mut self, req: Destination) -> Self::Future {
330        Box::pin(
331            self.0
332                .call(req)
333                .map_err(|e| ConnectionError::ProxyError(format!("{e}"))),
334        )
335    }
336}
337
338pub type ServoClient = Client<hyper_rustls::HttpsConnector<MaybeProxyConnector>, BoxedBody>;
339
340pub fn create_http_client(tls_config: TlsConfig) -> ServoClient {
341    let maybe_proxy_connector = create_maybe_proxy_connector();
342    let connector = hyper_rustls::HttpsConnectorBuilder::new()
343        .with_tls_config(tls_config)
344        .https_or_http()
345        .enable_http1()
346        .enable_http2()
347        .wrap_connector(maybe_proxy_connector);
348
349    Client::builder(TokioExecutor {})
350        .http1_title_case_headers(true)
351        .build(connector)
352}