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 = incoming.accept().map_err(|e| ProxyError::Listener(e.to_string()))?;
129
130 let hs_data = connecting
134 .handshake_data()
135 .await
136 .map_err(|e| ProxyError::Listener(format!("handshake data: {e}")))?;
137
138 let alpn = hs_data
139 .downcast::<quinn::crypto::rustls::HandshakeData>()
140 .ok()
141 .and_then(|hd| hd.protocol)
142 .map(|p| p.to_vec())
143 .unwrap_or_default();
144
145 if alpn == H3_ALPN {
146 #[cfg(feature = "webtransport")]
147 {
148 let session_fut =
149 wtransport::endpoint::IncomingSessionFuture::with_quic_connecting(connecting);
150 let session_request = session_fut
151 .await
152 .map_err(|e| ProxyError::Listener(format!("webtransport handshake: {e}")))?;
153 let conn = session_request
154 .accept()
155 .await
156 .map_err(|e| ProxyError::Listener(format!("webtransport accept: {e}")))?;
157 Ok(AcceptedConn::WebTransport(conn))
158 }
159 #[cfg(not(feature = "webtransport"))]
160 {
161 drop(connecting);
162 Err(ProxyError::Listener(
163 "client negotiated h3 but webtransport feature is not enabled".to_string(),
164 ))
165 }
166 } else {
167 let conn = connecting.await.map_err(|e| ProxyError::Listener(e.to_string()))?;
168 Ok(AcceptedConn::Quic { conn, alpn })
169 }
170 }
171
172 pub fn local_addr(&self) -> Result<SocketAddr, ProxyError> {
174 self.endpoint.local_addr().map_err(|e| ProxyError::Listener(e.to_string()))
175 }
176
177 pub fn close(&self) {
179 self.endpoint.close(0u32.into(), b"proxy shutting down");
180 }
181}