1use std::net::SocketAddr;
6use std::sync::Arc;
7
8use moqtap_codec::version::DraftVersion;
9use rustls::pki_types::{CertificateDer, PrivateKeyDer};
10
11use crate::error::ProxyError;
12
13const H3_ALPN: &[u8] = b"h3";
15
16pub struct ListenerConfig {
18 pub bind_addr: SocketAddr,
20 pub cert_chain: Vec<CertificateDer<'static>>,
22 pub key_der: PrivateKeyDer<'static>,
24}
25
26pub enum AcceptedConn {
32 Quic {
36 conn: quinn::Connection,
38 alpn: Vec<u8>,
40 },
41 #[cfg(feature = "webtransport")]
44 WebTransport(wtransport::Connection),
45}
46
47fn advertised_alpns() -> Vec<Vec<u8>> {
54 let mut out: Vec<Vec<u8>> = Vec::new();
57 for d in [
58 DraftVersion::Draft07,
59 DraftVersion::Draft08,
60 DraftVersion::Draft09,
61 DraftVersion::Draft10,
62 DraftVersion::Draft11,
63 DraftVersion::Draft12,
64 DraftVersion::Draft13,
65 DraftVersion::Draft14,
66 DraftVersion::Draft15,
67 DraftVersion::Draft16,
68 DraftVersion::Draft17,
69 DraftVersion::Draft18,
70 ] {
71 let alpn = d.quic_alpn().to_vec();
72 if !out.iter().any(|existing| existing == &alpn) {
73 out.push(alpn);
74 }
75 }
76 #[cfg(feature = "webtransport")]
77 out.push(H3_ALPN.to_vec());
78 out
79}
80
81pub struct Listener {
84 endpoint: quinn::Endpoint,
85}
86
87impl Listener {
88 pub fn bind(config: ListenerConfig) -> Result<Self, ProxyError> {
95 let mut server_tls = rustls::ServerConfig::builder()
96 .with_no_client_auth()
97 .with_single_cert(config.cert_chain, config.key_der)
98 .map_err(|e| ProxyError::TlsConfig(format!("server cert config: {e}")))?;
99
100 server_tls.alpn_protocols = advertised_alpns();
101 server_tls.max_early_data_size = u32::MAX;
102
103 let quic_server_config: quinn::crypto::rustls::QuicServerConfig =
104 server_tls.try_into().map_err(|e| ProxyError::TlsConfig(format!("{e}")))?;
105
106 let server_config = quinn::ServerConfig::with_crypto(Arc::new(quic_server_config));
107
108 let endpoint = quinn::Endpoint::server(server_config, config.bind_addr)
109 .map_err(|e| ProxyError::Listener(e.to_string()))?;
110
111 Ok(Self { endpoint })
112 }
113
114 pub async fn accept(&self) -> Result<AcceptedConn, ProxyError> {
122 let incoming = self
123 .endpoint
124 .accept()
125 .await
126 .ok_or_else(|| ProxyError::Listener("endpoint closed".to_string()))?;
127
128 let mut connecting =
129 incoming.accept().map_err(|e| ProxyError::Listener(e.to_string()))?;
130
131 let hs_data = connecting
135 .handshake_data()
136 .await
137 .map_err(|e| ProxyError::Listener(format!("handshake data: {e}")))?;
138
139 let alpn = hs_data
140 .downcast::<quinn::crypto::rustls::HandshakeData>()
141 .ok()
142 .and_then(|hd| hd.protocol)
143 .map(|p| p.to_vec())
144 .unwrap_or_default();
145
146 if alpn == H3_ALPN {
147 #[cfg(feature = "webtransport")]
148 {
149 let session_fut =
150 wtransport::endpoint::IncomingSessionFuture::with_quic_connecting(connecting);
151 let session_request = session_fut
152 .await
153 .map_err(|e| ProxyError::Listener(format!("webtransport handshake: {e}")))?;
154 let conn = session_request
155 .accept()
156 .await
157 .map_err(|e| ProxyError::Listener(format!("webtransport accept: {e}")))?;
158 Ok(AcceptedConn::WebTransport(conn))
159 }
160 #[cfg(not(feature = "webtransport"))]
161 {
162 let _ = connecting;
163 Err(ProxyError::Listener(
164 "client negotiated h3 but webtransport feature is not enabled".to_string(),
165 ))
166 }
167 } else {
168 let conn = connecting.await.map_err(|e| ProxyError::Listener(e.to_string()))?;
169 Ok(AcceptedConn::Quic { conn, alpn })
170 }
171 }
172
173 pub fn local_addr(&self) -> Result<SocketAddr, ProxyError> {
175 self.endpoint.local_addr().map_err(|e| ProxyError::Listener(e.to_string()))
176 }
177
178 pub fn close(&self) {
180 self.endpoint.close(0u32.into(), b"proxy shutting down");
181 }
182}