use std::net::SocketAddr;
use std::sync::Arc;
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use crate::error::ProxyError;
pub struct ListenerConfig {
pub bind_addr: SocketAddr,
pub cert_chain: Vec<CertificateDer<'static>>,
pub key_der: PrivateKeyDer<'static>,
pub alpn: Vec<Vec<u8>>,
}
pub struct Listener {
endpoint: quinn::Endpoint,
}
impl Listener {
pub fn bind(config: ListenerConfig) -> Result<Self, ProxyError> {
let mut server_tls = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(config.cert_chain, config.key_der)
.map_err(|e| ProxyError::TlsConfig(format!("server cert config: {e}")))?;
server_tls.alpn_protocols = config.alpn;
server_tls.max_early_data_size = u32::MAX;
let quic_server_config: quinn::crypto::rustls::QuicServerConfig =
server_tls.try_into().map_err(|e| ProxyError::TlsConfig(format!("{e}")))?;
let server_config = quinn::ServerConfig::with_crypto(Arc::new(quic_server_config));
let endpoint = quinn::Endpoint::server(server_config, config.bind_addr)
.map_err(|e| ProxyError::Listener(e.to_string()))?;
Ok(Self { endpoint })
}
pub async fn accept(&self) -> Result<(quinn::Connection, Vec<u8>), ProxyError> {
let incoming = self
.endpoint
.accept()
.await
.ok_or_else(|| ProxyError::Listener("endpoint closed".to_string()))?;
let conn = incoming.await.map_err(|e| ProxyError::Listener(e.to_string()))?;
let alpn = conn
.handshake_data()
.and_then(|hd| hd.downcast::<quinn::crypto::rustls::HandshakeData>().ok())
.and_then(|hd| hd.protocol)
.map(|p| p.to_vec())
.unwrap_or_default();
Ok((conn, alpn))
}
pub fn local_addr(&self) -> Result<SocketAddr, ProxyError> {
self.endpoint.local_addr().map_err(|e| ProxyError::Listener(e.to_string()))
}
pub fn close(&self) {
self.endpoint.close(0u32.into(), b"proxy shutting down");
}
}
#[cfg(feature = "webtransport")]
pub struct WtListenerConfig {
pub bind_addr: SocketAddr,
pub cert_chain: Vec<CertificateDer<'static>>,
pub key_der: PrivateKeyDer<'static>,
}
#[cfg(feature = "webtransport")]
pub struct WtListener {
endpoint: wtransport::Endpoint<wtransport::endpoint::endpoint_side::Server>,
}
#[cfg(feature = "webtransport")]
impl WtListener {
pub fn bind(config: WtListenerConfig) -> Result<Self, ProxyError> {
let mut server_tls = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(config.cert_chain, config.key_der)
.map_err(|e| ProxyError::TlsConfig(format!("server cert config: {e}")))?;
server_tls.alpn_protocols = vec![b"h3".to_vec()];
let wt_config = wtransport::ServerConfig::builder()
.with_bind_address(config.bind_addr)
.with_custom_tls(server_tls)
.build();
let endpoint = wtransport::Endpoint::server(wt_config)
.map_err(|e| ProxyError::Listener(e.to_string()))?;
Ok(Self { endpoint })
}
pub async fn accept(&self) -> Result<wtransport::Connection, ProxyError> {
let incoming = self.endpoint.accept().await;
let session_request = incoming.await.map_err(|e| ProxyError::Listener(e.to_string()))?;
let conn =
session_request.accept().await.map_err(|e| ProxyError::Listener(e.to_string()))?;
Ok(conn)
}
pub fn local_addr(&self) -> Result<SocketAddr, ProxyError> {
self.endpoint.local_addr().map_err(|e| ProxyError::Listener(e.to_string()))
}
pub fn close(&self) {
self.endpoint.close(wtransport::VarInt::from_u32(0), b"proxy shutting down");
}
}