tokio_quiche/settings/
config.rs1use foundations::telemetry::log;
28use std::borrow::Cow;
29use std::fs::File;
30use std::time::Duration;
31
32use crate::result::QuicResult;
33use crate::settings::CertificateKind;
34use crate::settings::ConnectionParams;
35use crate::settings::TlsCertificatePaths;
36use crate::socket::SocketCapabilities;
37
38const KEYLOGFILE_ENABLED: bool =
41 cfg!(capture_keylogs) || cfg!(feature = "capture_keylogs");
42
43pub(crate) struct Config {
45 pub quiche_config: quiche::Config,
46 pub disable_client_ip_validation: bool,
47 pub qlog_dir: Option<String>,
48 pub has_gso: bool,
49 pub pacing_offload: bool,
50 pub enable_expensive_packet_count_metrics: bool,
51 pub keylog_file: Option<File>,
52 pub listen_backlog: usize,
53 pub handshake_timeout: Option<Duration>,
54 pub has_ippktinfo: bool,
55 pub has_ipv6pktinfo: bool,
56}
57
58impl AsMut<quiche::Config> for Config {
59 fn as_mut(&mut self) -> &mut quiche::Config {
60 &mut self.quiche_config
61 }
62}
63
64impl Config {
65 pub(crate) fn new(
66 params: &ConnectionParams, socket_capabilities: SocketCapabilities,
67 ) -> QuicResult<Self> {
68 let quic_settings = ¶ms.settings;
69 let keylog_path = match &quic_settings.keylog_file {
70 Some(f) => Some(Cow::Borrowed(f.as_ref())),
71 None => std::env::var_os("SSLKEYLOGFILE").map(Cow::from),
72 };
73 let keylog_file = keylog_path.and_then(|path| if KEYLOGFILE_ENABLED {
74 File::options().create(true).append(true).open(path)
75 .inspect_err(|e| log::warn!("failed to open SSLKEYLOGFILE"; "error" => e))
76 .ok()
77 } else {
78 log::warn!("SSLKEYLOGFILE is set, but `--cfg capture_keylogs` was not enabled. No keys will be logged.");
79 None
80 });
81
82 let SocketCapabilities {
83 has_gso,
84 has_txtime: pacing_offload,
85 has_ippktinfo,
86 has_ipv6pktinfo,
87 ..
88 } = socket_capabilities;
89
90 Ok(Config {
91 quiche_config: make_quiche_config(params, keylog_file.is_some())?,
92 disable_client_ip_validation: quic_settings
93 .disable_client_ip_validation,
94 qlog_dir: quic_settings.qlog_dir.clone(),
95 has_gso,
96 pacing_offload: quic_settings.enable_pacing && pacing_offload,
99 enable_expensive_packet_count_metrics: quic_settings
100 .enable_expensive_packet_count_metrics,
101 keylog_file,
102 listen_backlog: quic_settings.listen_backlog,
103 handshake_timeout: quic_settings.handshake_timeout,
104 has_ippktinfo,
105 has_ipv6pktinfo,
106 })
107 }
108}
109
110fn make_quiche_config(
111 params: &ConnectionParams, should_log_keys: bool,
112) -> QuicResult<quiche::Config> {
113 let ssl_ctx_builder = params
114 .hooks
115 .connection_hook
116 .as_ref()
117 .zip(params.tls_cert)
118 .and_then(|(hook, tls)| hook.create_custom_ssl_context_builder(tls));
119
120 let mut config = if let Some(builder) = ssl_ctx_builder {
121 quiche::Config::with_boring_ssl_ctx_builder(
122 quiche::PROTOCOL_VERSION,
123 builder,
124 )?
125 } else {
126 quiche_config_with_tls(params.tls_cert)?
127 };
128
129 let quic_settings = ¶ms.settings;
130 let alpns: Vec<&[u8]> =
131 quic_settings.alpn.iter().map(Vec::as_slice).collect();
132 config.set_application_protos(&alpns).unwrap();
133
134 if let Some(timeout) = quic_settings.max_idle_timeout {
135 let ms = timeout
136 .as_millis()
137 .try_into()
138 .map_err(|_| "QuicSettings::max_idle_timeout exceeds u64")?;
139 config.set_max_idle_timeout(ms);
140 }
141
142 config.enable_dgram(
143 quic_settings.enable_dgram,
144 quic_settings.dgram_recv_max_queue_len,
145 quic_settings.dgram_send_max_queue_len,
146 );
147
148 config.set_max_recv_udp_payload_size(quic_settings.max_recv_udp_payload_size);
149 config.set_max_send_udp_payload_size(quic_settings.max_send_udp_payload_size);
150 config.set_initial_max_data(quic_settings.initial_max_data);
151 config.set_initial_max_stream_data_bidi_local(
152 quic_settings.initial_max_stream_data_bidi_local,
153 );
154 config.set_initial_max_stream_data_bidi_remote(
155 quic_settings.initial_max_stream_data_bidi_remote,
156 );
157 config.set_initial_max_stream_data_uni(
158 quic_settings.initial_max_stream_data_uni,
159 );
160 config.set_initial_max_streams_bidi(quic_settings.initial_max_streams_bidi);
161 config.set_initial_max_streams_uni(quic_settings.initial_max_streams_uni);
162 config.set_disable_active_migration(quic_settings.disable_active_migration);
163 config.set_cc_algorithm_name(quic_settings.cc_algorithm.as_str())?;
164 config.enable_hystart(quic_settings.enable_hystart);
165 config.enable_pacing(quic_settings.enable_pacing);
166
167 if should_log_keys {
168 config.log_keys();
169 }
170
171 Ok(config)
172}
173
174fn quiche_config_with_tls(
175 tls_cert: Option<TlsCertificatePaths>,
176) -> QuicResult<quiche::Config> {
177 let Some(tls) = tls_cert else {
178 return Ok(quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap());
179 };
180
181 match tls.kind {
182 #[cfg(not(feature = "rpk"))]
183 CertificateKind::RawPublicKey => {
184 panic!("Can't use RPK when compiled without rpk feature");
186 },
187 #[cfg(feature = "rpk")]
188 CertificateKind::RawPublicKey => {
189 let mut ssl_ctx_builder = boring::ssl::SslContextBuilder::new_rpk()?;
190 let raw_public_key = std::fs::read(tls.cert)?;
191 ssl_ctx_builder.set_rpk_certificate(&raw_public_key)?;
192
193 let raw_private_key = std::fs::read(tls.private_key)?;
194 let pkey =
195 boring::pkey::PKey::private_key_from_pem(&raw_private_key)?;
196 ssl_ctx_builder.set_null_chain_private_key(&pkey)?;
197
198 Ok(quiche::Config::with_boring_ssl_ctx_builder(
199 quiche::PROTOCOL_VERSION,
200 ssl_ctx_builder,
201 )?)
202 },
203 CertificateKind::X509 => {
204 let mut config =
205 quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
206 config.load_cert_chain_from_pem_file(tls.cert)?;
207 config.load_priv_key_from_pem_file(tls.private_key)?;
208 Ok(config)
209 },
210 }
211}