use std::{
io::{Read, Write},
net::TcpStream,
sync::Arc,
};
use rustls::{client::ServerCertVerifier, ClientConnection, ServerName};
use url::Url;
pub trait Network {
type Error;
type Connection: Read + Write;
fn connect(&mut self, url: &Url) -> Result<Self::Connection, Self::Error>;
}
pub struct TcpNetwork<V> {
_verifier: V,
}
impl<V> TcpNetwork<V> {
pub fn new(ver: V) -> Self {
Self { _verifier: ver }
}
}
impl<V: ServerCertVerifier + Default + 'static> Network for TcpNetwork<V> {
type Error = !;
type Connection = rustls::StreamOwned<ClientConnection, TcpStream>;
fn connect(&mut self, url: &Url) -> Result<Self::Connection, Self::Error> {
Ok(connect::<V>(&url))
}
}
fn connect<V: Default + ServerCertVerifier + 'static>(
url: &Url,
) -> rustls::StreamOwned<ClientConnection, TcpStream> {
let host = url.host().expect("");
let sn = server(host);
let stream = {
let cfg = tls_cfg(V::default());
let conn = {
let client = ClientConnection::new(cfg, sn).expect("could not connect");
client
};
let addrs = url
.socket_addrs(|| Some(1965))
.expect("could not get addresses for URL");
let addr = addrs.get(0).expect("no addresses");
let sock = TcpStream::connect(addr).expect("failed to connect");
let stream = rustls::StreamOwned { conn, sock };
stream
};
stream
}
fn server(host: url::Host<&str>) -> ServerName {
let url::Host::Domain(s) = host
else {unreachable!("the url is always a string")};
let sn = s.try_into().expect("this should be a valid DNS name");
sn
}
fn tls_cfg<V: ServerCertVerifier + 'static>(v: V) -> Arc<rustls::ClientConfig> {
let root_store = rustls::RootCertStore::empty();
let cfg = {
let mut cfg = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_store)
.with_no_client_auth();
cfg.dangerous().set_certificate_verifier(Arc::new(v));
Arc::new(cfg)
};
cfg
}