use anyhow::{anyhow, Result};
use futures_util::StreamExt;
use quinn::ServerConfig;
use std::{error::Error, fs, net::SocketAddr, net::ToSocketAddrs, sync::Arc};
use url::Url;
pub struct QuicListener {}
impl QuicListener {
#[allow(clippy::field_reassign_with_default)] pub async fn recv(listen: SocketAddr, path: String) -> Result<std::vec::Vec<u8>> {
let (server_config, _) = configure_server(path).unwrap();
let (_, mut incoming) = quinn::Endpoint::server(server_config, listen)?;
let mut ret = None;
while let Some(conn) = incoming.next().await {
ret = Some(tokio::spawn(handle_connection(conn)).await);
break;
}
Ok(ret.unwrap().unwrap().unwrap())
}
pub async fn send(
ca: String,
remote_url: Url,
host: Option<String>,
payload: &mut [u8],
) -> Result<()> {
let remote = (
remote_url.host_str().unwrap(),
remote_url.port().unwrap_or(4433),
)
.to_socket_addrs()?
.next()
.ok_or_else(|| anyhow!("couldn't resolve to an address"))
.unwrap();
let mut roots = rustls::RootCertStore::empty();
roots.add(&rustls::Certificate(fs::read(&ca)?)).unwrap();
let mut client_crypto = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(roots)
.with_no_client_auth();
client_crypto.alpn_protocols = ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect();
let mut endpoint = quinn::Endpoint::client("[::]:0".parse().unwrap())?;
endpoint.set_default_client_config(quinn::ClientConfig::new(Arc::new(client_crypto)));
let host = host
.as_ref()
.map_or_else(|| remote_url.host_str(), |x| Some(x))
.ok_or_else(|| anyhow!("no hostname specified"))?;
let new_conn = endpoint
.connect(remote, host)?
.await
.map_err(|e| anyhow!("failed to connect: {}", e))?;
let quinn::NewConnection {
connection: conn, ..
} = new_conn;
let (mut send, _) = conn
.open_bi()
.await
.map_err(|e| anyhow!("failed to open stream: {}", e))?;
send.write_all(payload)
.await
.map_err(|e| anyhow!("failed to send request: {}", e))?;
send.finish()
.await
.map_err(|e| anyhow!("failed to shutdown stream: {}", e))?;
conn.close(0u32.into(), b"done");
endpoint.wait_idle().await;
Ok(())
}
}
#[allow(clippy::field_reassign_with_default)] fn configure_server(path: String) -> Result<(ServerConfig, Vec<u8>), Box<dyn Error>> {
let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()]).unwrap();
let cert_der = cert.serialize_der().unwrap();
let priv_key = cert.serialize_private_key_der();
let priv_key = rustls::PrivateKey(priv_key);
let cert_chain = vec![rustls::Certificate(cert_der.clone())];
let mut server_crypto = rustls::ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(cert_chain, priv_key)?;
server_crypto.alpn_protocols = ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect();
let mut server_config = quinn::ServerConfig::with_crypto(Arc::new(server_crypto));
Arc::get_mut(&mut server_config.transport)
.unwrap()
.max_concurrent_uni_streams(0_u8.into());
fs::write(path, &cert_der).unwrap();
Ok((server_config, cert_der))
}
#[allow(unused)]
pub const ALPN_QUIC_HTTP: &[&[u8]] = &[b"hq-29"];
async fn handle_connection(conn: quinn::Connecting) -> Result<std::vec::Vec<u8>> {
Ok(handle_spawn(conn).await.unwrap())
}
async fn handle_spawn(conn: quinn::Connecting) -> Result<std::vec::Vec<u8>> {
let quinn::NewConnection {
connection: _,
mut bi_streams,
..
} = conn.await?;
let mut req = None;
while let Some(stream) = bi_streams.next().await {
let stream = match stream {
Err(quinn::ConnectionError::ApplicationClosed { .. }) => {
return Ok(vec![]);
}
Err(e) => {
return Err(anyhow::Error::new(e));
}
Ok(s) => s,
};
req = Some(tokio::spawn(handle_request(stream)).await?);
break;
}
Ok(req.unwrap())
}
async fn handle_request((_, recv): (quinn::SendStream, quinn::RecvStream)) -> std::vec::Vec<u8> {
let req = recv
.read_to_end(64 * 1024)
.await
.map_err(|e| anyhow!("failed reading request: {}", e))
.unwrap();
req
}