use crate::error::{NetError, NetResult};
use quinn::{Connection, Endpoint, RecvStream, SendStream};
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use std::net::SocketAddr;
use std::sync::Arc;
pub struct QuicServerConfig {
pub bind_addr: SocketAddr,
pub cert_der: Vec<u8>,
pub key_der: Vec<u8>,
pub max_concurrent_bidi_streams: u32,
pub max_concurrent_uni_streams: u32,
pub session_tickets: bool,
}
impl Default for QuicServerConfig {
fn default() -> Self {
Self {
bind_addr: "127.0.0.1:0".parse().expect("valid addr"),
cert_der: Vec::new(),
key_der: Vec::new(),
max_concurrent_bidi_streams: 100,
max_concurrent_uni_streams: 0,
session_tickets: true,
}
}
}
pub struct QuicServer {
endpoint: Endpoint,
#[allow(dead_code)]
config: QuicServerConfig,
}
impl QuicServer {
pub fn new(config: QuicServerConfig) -> NetResult<Self> {
let cert = CertificateDer::from(config.cert_der.clone());
let key = PrivateKeyDer::try_from(config.key_der.clone())
.map_err(|e| NetError::ServerInternal(format!("Invalid private key: {}", e)))?;
let provider = Arc::new(rustls::crypto::ring::default_provider());
let mut tls_server_config = rustls::ServerConfig::builder_with_provider(provider)
.with_protocol_versions(&[&rustls::version::TLS13])
.map_err(|e| NetError::ServerInternal(format!("TLS protocol version error: {}", e)))?
.with_no_client_auth()
.with_single_cert(vec![cert], key)
.map_err(|e| NetError::ServerInternal(format!("TLS config error: {}", e)))?;
tls_server_config.alpn_protocols = vec![b"amaters".to_vec()];
let quinn_server_config = quinn::ServerConfig::with_crypto(Arc::new(
quinn::crypto::rustls::QuicServerConfig::try_from(tls_server_config).map_err(|e| {
NetError::ServerInternal(format!("QUIC server config error: {}", e))
})?,
));
let endpoint =
quinn::Endpoint::server(quinn_server_config, config.bind_addr).map_err(|e| {
NetError::ServerInternal(format!("Failed to create server endpoint: {}", e))
})?;
Ok(Self { endpoint, config })
}
pub async fn accept_connection(&self) -> NetResult<Connection> {
let incoming = self
.endpoint
.accept()
.await
.ok_or_else(|| NetError::ServerInternal("Server endpoint closed".to_string()))?;
let connection = incoming
.await
.map_err(|e| NetError::ServerInternal(format!("Connection handshake error: {}", e)))?;
Ok(connection)
}
pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
self.endpoint.local_addr()
}
}
pub struct QuicClientConfig {
pub server_addr: SocketAddr,
pub server_name: String,
pub ca_cert_der: Option<Vec<u8>>,
}
pub struct QuicClient {
endpoint: Endpoint,
connection: Option<Connection>,
config: QuicClientConfig,
}
#[derive(Debug)]
struct SkipVerification(Arc<rustls::crypto::CryptoProvider>);
impl rustls::client::danger::ServerCertVerifier for SkipVerification {
fn verify_server_cert(
&self,
_end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>],
_server_name: &rustls::pki_types::ServerName<'_>,
_ocsp_response: &[u8],
_now: rustls::pki_types::UnixTime,
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
Ok(rustls::client::danger::ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
rustls::crypto::verify_tls12_signature(
message,
cert,
dss,
&self.0.signature_verification_algorithms,
)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
rustls::crypto::verify_tls13_signature(
message,
cert,
dss,
&self.0.signature_verification_algorithms,
)
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
self.0.signature_verification_algorithms.supported_schemes()
}
}
impl QuicClient {
pub fn new(config: QuicClientConfig) -> NetResult<Self> {
let mut tls_client_config = if let Some(ref ca_cert_der) = config.ca_cert_der {
let mut root_store = rustls::RootCertStore::empty();
root_store
.add(CertificateDer::from(ca_cert_der.clone()))
.map_err(|e| NetError::ServerInternal(format!("Invalid CA certificate: {}", e)))?;
let provider = Arc::new(rustls::crypto::ring::default_provider());
rustls::ClientConfig::builder_with_provider(provider)
.with_protocol_versions(&[&rustls::version::TLS13])
.map_err(|e| {
NetError::ServerInternal(format!("TLS protocol version error: {}", e))
})?
.with_root_certificates(root_store)
.with_no_client_auth()
} else {
let provider = Arc::new(rustls::crypto::ring::default_provider());
rustls::ClientConfig::builder_with_provider(provider.clone())
.with_protocol_versions(&[&rustls::version::TLS13])
.map_err(|e| {
NetError::ServerInternal(format!("TLS protocol version error: {}", e))
})?
.dangerous()
.with_custom_certificate_verifier(Arc::new(SkipVerification(provider)))
.with_no_client_auth()
};
tls_client_config.alpn_protocols = vec![b"amaters".to_vec()];
let quinn_client_config = quinn::ClientConfig::new(Arc::new(
quinn::crypto::rustls::QuicClientConfig::try_from(tls_client_config).map_err(|e| {
NetError::ServerInternal(format!("QUIC client config error: {}", e))
})?,
));
let mut endpoint = quinn::Endpoint::client("0.0.0.0:0".parse().expect("valid bind addr"))
.map_err(|e| {
NetError::ServerInternal(format!("Failed to create client endpoint: {}", e))
})?;
endpoint.set_default_client_config(quinn_client_config);
Ok(Self {
endpoint,
connection: None,
config,
})
}
pub async fn connect(&mut self) -> NetResult<&Connection> {
let connecting = self
.endpoint
.connect(self.config.server_addr, &self.config.server_name)
.map_err(|e| {
NetError::ServerInternal(format!("Failed to initiate connection: {}", e))
})?;
let connection = connecting
.await
.map_err(|e| NetError::ServerInternal(format!("Connection failed: {}", e)))?;
self.connection = Some(connection);
Ok(self.connection.as_ref().expect("just set"))
}
pub async fn open_bidi_stream(&self) -> NetResult<(SendStream, RecvStream)> {
let conn = self.connection.as_ref().ok_or_else(|| {
NetError::ServerInternal("Not connected — call connect() first".to_string())
})?;
conn.open_bi()
.await
.map_err(|e| NetError::ServerInternal(format!("Failed to open bidi stream: {}", e)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use rcgen::generate_simple_self_signed;
fn make_test_cert() -> (Vec<u8>, Vec<u8>) {
let certified_key =
generate_simple_self_signed(vec!["localhost".to_string()]).expect("cert gen failed");
let cert_der = certified_key.cert.der().to_vec();
let key_der = certified_key.signing_key.serialize_der();
(cert_der, key_der)
}
#[tokio::test]
async fn test_quic_server_start() {
let (cert_der, key_der) = make_test_cert();
let config = QuicServerConfig {
bind_addr: "127.0.0.1:0".parse().expect("valid loopback addr"),
cert_der,
key_der,
..Default::default()
};
let server = QuicServer::new(config).expect("server creation failed");
let addr = server.local_addr().expect("local_addr failed");
assert_ne!(addr.port(), 0);
}
#[tokio::test]
async fn test_quic_client_connect() {
let (cert_der, key_der) = make_test_cert();
let server_config = QuicServerConfig {
bind_addr: "127.0.0.1:0".parse().expect("valid loopback addr"),
cert_der: cert_der.clone(),
key_der,
..Default::default()
};
let server = QuicServer::new(server_config).expect("server creation failed");
let server_addr = server.local_addr().expect("local_addr failed");
let accept_task = tokio::spawn(async move { server.accept_connection().await });
let mut client = QuicClient::new(QuicClientConfig {
server_addr,
server_name: "localhost".to_string(),
ca_cert_der: None, })
.expect("client creation failed");
client.connect().await.expect("connect failed");
accept_task
.await
.expect("join failed")
.expect("accept failed");
}
#[tokio::test]
async fn test_quic_bidi_stream() {
let (cert_der, key_der) = make_test_cert();
let server_config = QuicServerConfig {
bind_addr: "127.0.0.1:0".parse().expect("valid loopback addr"),
cert_der: cert_der.clone(),
key_der,
..Default::default()
};
let server = QuicServer::new(server_config).expect("server creation");
let server_addr = server.local_addr().expect("local_addr");
let server_task = tokio::spawn(async move {
let conn = server.accept_connection().await.expect("accept");
let (mut send, mut recv) = conn.accept_bi().await.expect("accept_bi");
let mut buf = vec![0u8; 1024];
let mut total = 0usize;
while let Some(n) = recv.read(&mut buf[total..]).await.expect("read") {
total += n;
}
send.write_all(&buf[..total]).await.expect("echo");
send.finish().expect("finish");
tokio::time::timeout(std::time::Duration::from_secs(5), conn.closed())
.await
.ok(); });
let mut client = QuicClient::new(QuicClientConfig {
server_addr,
server_name: "localhost".to_string(),
ca_cert_der: None,
})
.expect("client");
client.connect().await.expect("connect");
let (mut send, mut recv) = client.open_bidi_stream().await.expect("open_bidi");
let msg = b"hello quic";
send.write_all(msg).await.expect("write");
send.finish().expect("finish");
let mut buf = vec![0u8; 1024];
let mut total = 0usize;
while let Some(n) = recv.read(&mut buf[total..]).await.expect("read") {
total += n;
}
assert_eq!(&buf[..total], msg);
server_task.await.expect("server task");
}
}