use std::{net::IpAddr, time::Duration};
use aok::{ensure, throw, Null, OK};
use rustls::pki_types::ServerName;
use tokio::{net::TcpStream, time};
use tokio_rustls::TlsConnector;
use x509_parser::prelude::{FromDer, X509Certificate};
pub async fn tls_ping(host: impl Into<String>, ip: IpAddr, timeout: u64) -> Null {
let host = host.into();
let addr = std::net::SocketAddr::new(ip, 443);
let connector = TlsConnector::from(tlsinit::CLIENT.clone());
let stream = TcpStream::connect(&addr).await?;
let dns_name = ServerName::try_from(host.clone())?;
let tls_stream = time::timeout(
Duration::from_secs(timeout),
connector.connect(dns_name, stream),
)
.await??;
if let Some(peer_certs) = tls_stream.get_ref().1.peer_certificates() {
let mut has_host_cert = false;
for cert in peer_certs {
let cert = cert.to_vec();
let cert = X509Certificate::from_der(&cert);
for (_, i) in cert.iter() {
let mut name = String::new();
for i in i.subject().iter_common_name() {
if !name.is_empty() {
name += " ";
}
name += i.as_str()?;
}
if name == host || (name.starts_with("*.") && host == name[2..]) {
has_host_cert = true;
break;
} else if let Some(h) = host.split_once('.').map(|x| x.1) {
if h == name {
has_host_cert = true;
break;
}
}
if let Some(expire_after) = i.validity.time_to_expiration() {
let expire_after = (expire_after.as_seconds_f32() as u64) / 86400;
ensure!(expire_after > 14, "{name} expire after {expire_after} days",);
} else {
throw!("{host} : {name} has not valid expire");
}
}
}
if !has_host_cert {
throw!("{} no host cert", host);
}
} else {
throw!("{} no cert", host);
}
OK
}