diff --git a/src/connector.rs b/src/connector.rs index e97be1d..74a125e 100644 --- a/src/connector.rs +++ b/src/connector.rs @@ -21,6 +21,7 @@ pub struct HttpsConnector { force_https: bool, http: T, tls_config: Arc, + server_name_override: Option, } impl fmt::Debug for HttpsConnector { @@ -40,6 +41,7 @@ where force_https: false, http, tls_config: cfg.into(), + server_name_override: None, } } } @@ -83,10 +85,21 @@ where Box::pin(f) } else if sch == &http::uri::Scheme::HTTPS { let cfg = self.tls_config.clone(); - let hostname = dst - .host() - .unwrap_or_default() - .to_string(); + let hostname = match self.server_name_override.as_deref() { + Some(h) => h, + None => dst + .host() + .unwrap_or_default() + }; + let hostname = match rustls::ServerName::try_from(hostname) { + Ok(dnsname) => dnsname, + Err(_) => { + let err = io::Error::new(io::ErrorKind::Other, "invalid dnsname"); + let err: Box = Box::new(err); + let res = std::future::ready(Err(err)); + return Box::pin(res); + } + }; let connecting_future = self.http.call(dst); let f = async move { @@ -94,10 +107,8 @@ where .await .map_err(Into::into)?; let connector = TlsConnector::from(cfg); - let dnsname = rustls::ServerName::try_from(hostname.as_str()) - .map_err(|_| io::Error::new(io::ErrorKind::Other, "invalid dnsname"))?; let tls = connector - .connect(dnsname, tcp) + .connect(hostname, tcp) .await .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; Ok(MaybeHttpsStream::Https(tls)) diff --git a/src/connector/builder.rs b/src/connector/builder.rs index 0398f8b..4c3dafd 100644 --- a/src/connector/builder.rs +++ b/src/connector/builder.rs @@ -106,6 +106,7 @@ impl ConnectorBuilder { ConnectorBuilder(WantsProtocols1 { tls_config: self.0.tls_config, https_only: true, + server_name_override: None, }) } @@ -117,6 +118,7 @@ impl ConnectorBuilder { ConnectorBuilder(WantsProtocols1 { tls_config: self.0.tls_config, https_only: false, + server_name_override: None }) } } @@ -128,6 +130,7 @@ impl ConnectorBuilder { pub struct WantsProtocols1 { tls_config: ClientConfig, https_only: bool, + server_name_override: Option } impl WantsProtocols1 { @@ -136,6 +139,7 @@ impl WantsProtocols1 { force_https: self.https_only, http: conn, tls_config: std::sync::Arc::new(self.tls_config), + server_name_override: None } } @@ -169,6 +173,15 @@ impl ConnectorBuilder { enable_http1: false, }) } + + /// Override expected server name + /// + /// If called, server certificates will be validated against `server_name_override`, + /// and host portion of URL will not be used for server authentication. + pub fn override_server_name(mut self, server_name_override: String) -> Self { + self.0.server_name_override = Some(server_name_override); + self + } } /// State of a builder with HTTP1 enabled, that may have some other @@ -195,6 +208,15 @@ impl ConnectorBuilder { }) } + /// Override expected server name + /// + /// If called, server certificates will be validated against `server_name_override`, + /// and host portion of URL will not be used for server authentication. + pub fn override_server_name(mut self, server_name_override: String) -> Self { + self.0.inner.server_name_override = Some(server_name_override); + self + } + /// This builds an [`HttpsConnector`] built on hyper's default [`HttpConnector`] #[cfg(feature = "tokio-runtime")] pub fn build(self) -> HttpsConnector { @@ -226,6 +248,15 @@ pub struct WantsProtocols3 { #[cfg(feature = "http2")] impl ConnectorBuilder { + /// Override expected server name + /// + /// If called, server certificates will be validated against `server_name_override`, + /// and host portion of URL will not be used for server authentication. + pub fn override_server_name(mut self, server_name_override: String) -> Self { + self.0.inner.server_name_override = Some(server_name_override); + self + } + /// This builds an [`HttpsConnector`] built on hyper's default [`HttpConnector`] #[cfg(feature = "tokio-runtime")] pub fn build(self) -> HttpsConnector {