use openssl::error::ErrorStack;
use openssl::ssl::{SslConnector, SslMethod, SslStream, SslVersion};
use openssl::x509::X509;
use std::fs;
use std::io::{self, Write};
use std::net::TcpStream;
use crate::Error;
use super::{Connection, HttpStream};
pub type SecuredStream = SslStream<TcpStream>;
impl From<ErrorStack> for Error {
fn from(err: ErrorStack) -> Self {
Error::IoError(io::Error::new(io::ErrorKind::Other, err))
}
}
pub fn create_secured_stream(conn: &Connection) -> Result<HttpStream, Error> {
#[cfg(feature = "log")]
log::trace!("Setting up TLS parameters for {}.", conn.request.url.host);
let connector = {
let mut connector_builder = SslConnector::builder(SslMethod::tls())?;
connector_builder.set_min_proto_version(Some(SslVersion::TLS1))?;
#[cfg(feature = "openssl-probe")]
{
let probe = openssl_probe::probe();
connector_builder
.load_verify_locations(probe.cert_file.as_deref(), probe.cert_dir.as_deref())?;
}
if cfg!(target_os = "android") {
if let Ok(dir) = fs::read_dir("/system/etc/security/cacerts") {
let certs = dir
.filter_map(|r| r.ok())
.filter_map(|e| fs::read(e.path()).ok())
.filter_map(|b| X509::from_pem(&b).ok());
for cert in certs {
if let Err(_err) = connector_builder.cert_store_mut().add_cert(cert) {
#[cfg(feature = "log")]
log::debug!("load_android_root_certs error: {:?}", _err);
}
}
}
}
connector_builder.build().configure()?
};
#[cfg(feature = "log")]
log::trace!("Establishing TCP connection to {}.", conn.request.url.host);
let tcp = conn.connect()?;
#[cfg(feature = "log")]
log::trace!("Establishing TLS session to {}.", conn.request.url.host);
let mut tls = match connector
.use_server_name_indication(true)
.verify_hostname(true)
.connect(&conn.request.url.host, tcp)
{
Ok(tls) => tls,
Err(err) => return Err(Error::OpenSslCreateConnection(err)),
};
#[cfg(feature = "log")]
log::trace!("Writing HTTPS request to {}.", conn.request.url.host);
let _ = tls.get_ref().set_write_timeout(conn.timeout()?);
tls.write_all(&conn.request.as_bytes())?;
Ok(HttpStream::create_secured(tls, conn.timeout_at))
}