use std::net::{TcpListener, TcpStream};
use std::path::Path;
use std::sync::{Arc, Mutex, PoisonError};
use rustls::pki_types::pem::PemObject;
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use rustls::server::WebPkiClientVerifier;
use rustls::{ServerConfig, ServerConnection, StreamOwned};
use serde_json::{Value, json};
use crate::channel::{frame, material, tls};
const FRAMES_IN: usize = 2;
pub(crate) struct Engine {
seen: Arc<Mutex<Vec<Value>>>,
}
impl Engine {
pub(crate) fn start(dir: &Path, protocol: u32, script: Vec<Vec<Value>>) -> Self {
let config = server_config(dir);
let listener = TcpListener::bind("127.0.0.1:0").expect("a loopback port");
let address = listener.local_addr().expect("bound").to_string();
std::fs::write(dir.join(material::ADDRESS), address).expect("the address file");
let seen = Arc::new(Mutex::new(Vec::new()));
let recorded = Arc::clone(&seen);
std::thread::spawn(move || {
for answer in script {
let Ok((tcp, _)) = listener.accept() else {
return;
};
serve(&config, tcp, protocol, &answer, &recorded);
}
});
Self { seen }
}
pub(crate) fn heard(&self) -> Vec<Value> {
self.seen
.lock()
.unwrap_or_else(PoisonError::into_inner)
.clone()
}
}
fn serve(
config: &Arc<ServerConfig>,
tcp: TcpStream,
protocol: u32,
answer: &[Value],
seen: &Arc<Mutex<Vec<Value>>>,
) {
let Ok(conn) = ServerConnection::new(Arc::clone(config)) else {
return;
};
let mut tls = StreamOwned::new(conn, tcp);
let _ = frame::write_value(&mut tls, &json!({ "protocol": protocol }));
for _ in 0..FRAMES_IN {
if let Ok(Some(said)) = frame::read_value(&mut tls) {
seen.lock()
.unwrap_or_else(PoisonError::into_inner)
.push(said);
}
}
for value in answer {
let _ = frame::write_value(&mut tls, value);
}
let _ = frame::write_end(&mut tls);
}
fn server_config(dir: &Path) -> Arc<ServerConfig> {
let provider = Arc::new(rustls::crypto::ring::default_provider());
let anchors = tls::anchors(&dir.join(material::ANCHORS)).expect("the operator CA");
let verifier =
WebPkiClientVerifier::builder_with_provider(Arc::new(anchors), Arc::clone(&provider))
.build()
.expect("a client verifier");
let chain: Vec<CertificateDer<'static>> =
CertificateDer::pem_file_iter(dir.join(format!("{}.pem", super::mint::ENGINE)))
.expect("the engine chain")
.collect::<Result<_, _>>()
.expect("the engine chain");
let key = PrivateKeyDer::from_pem_file(dir.join(format!("{}.key", super::mint::ENGINE)))
.expect("the engine key");
Arc::new(
ServerConfig::builder_with_provider(provider)
.with_safe_default_protocol_versions()
.expect("tls versions")
.with_client_cert_verifier(verifier)
.with_single_cert(chain, key)
.expect("the engine identity"),
)
}