From b7126b255f84122907e78e43ebc1fdecf4ab7b27 Mon Sep 17 00:00:00 2001 From: funman300 Date: Thu, 20 Aug 2026 16:08:36 +0000 Subject: [PATCH] wip(blaze-proto): retained Blaze frame/codec refinement WIP RETAINED PRE-EXISTING WIP (brought forward after verification, not authored here, not production-ready). Blaze wire frame/packet layout work (Fire2 header still flagged as an unvalidated FIFA23 guess). No secrets/captures staged (captures/ + certs/ gitignored). Preserved off detached base f4f33969f2f7e0a600ce4c368cff56acd66d3c43. --- crates/blaze-proto/src/codec.rs | 35 +++++++++----- crates/blaze-proto/src/frame.rs | 81 ++++++++++++++++++++------------ crates/blaze-proto/src/lib.rs | 4 +- crates/blaze-proto/src/packet.rs | 47 +++++++++--------- crates/server/src/capture.rs | 76 ++++++++++++++++++++---------- crates/server/src/config.rs | 27 +++++------ crates/server/src/dispatch.rs | 22 +++++---- crates/server/src/main.rs | 64 +++++++++++++++---------- crates/server/src/tls.rs | 15 +++--- 9 files changed, 227 insertions(+), 144 deletions(-) diff --git a/crates/blaze-proto/src/codec.rs b/crates/blaze-proto/src/codec.rs index ba2c302..3b9a089 100644 --- a/crates/blaze-proto/src/codec.rs +++ b/crates/blaze-proto/src/codec.rs @@ -12,7 +12,7 @@ use std::io; use tokio_util::codec::{Decoder, Encoder}; use super::{ - frame::{FireFrame, FIRE2_MIN_HEADER, FIRE2_JUMBO_EXT, PacketOptions}, + frame::{FireFrame, PacketOptions, FIRE2_JUMBO_EXT, FIRE2_MIN_HEADER}, packet::Packet, }; @@ -31,8 +31,8 @@ pub enum FramingVariant { /// Tracks partial-decode state between `decode()` calls. struct PartialFire2 { - frame : FireFrame, - body_len : usize, + frame: FireFrame, + body_len: usize, } /// Tokio codec for encoding and decoding Blaze packets. @@ -44,18 +44,23 @@ pub struct PacketCodec { impl PacketCodec { pub fn new(variant: FramingVariant) -> Self { - PacketCodec { variant, partial: None } + PacketCodec { + variant, + partial: None, + } } } impl Decoder for PacketCodec { - type Item = Packet; + type Item = Packet; type Error = io::Error; fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { match self.variant { FramingVariant::Raw => { - if src.is_empty() { return Ok(None); } + if src.is_empty() { + return Ok(None); + } let body = src.copy_to_bytes(src.len()); Ok(Some(Packet::raw(body))) } @@ -63,21 +68,29 @@ impl Decoder for PacketCodec { FramingVariant::Fire2 => { // ── Resume waiting for the body ─────────────────────────── if let Some(ref p) = self.partial { - if src.len() < p.body_len { return Ok(None); } - let PartialFire2 { frame, body_len, .. } = self.partial.take().unwrap(); + if src.len() < p.body_len { + return Ok(None); + } + let PartialFire2 { + frame, body_len, .. + } = self.partial.take().unwrap(); let body = src.copy_to_bytes(body_len); return Ok(Some(Packet { frame, body })); } // ── Need at least the minimum header ───────────────────── - if src.len() < FIRE2_MIN_HEADER { return Ok(None); } + if src.len() < FIRE2_MIN_HEADER { + return Ok(None); + } // Peek at options to see if we need a jumbo ext too. let opt_nibble = (src[9] >> 4) & 0xF; - let has_jumbo = (opt_nibble & PacketOptions::JUMBO_FRAME.0) != 0; + let has_jumbo = (opt_nibble & PacketOptions::JUMBO_FRAME.0) != 0; let header_len = FIRE2_MIN_HEADER + if has_jumbo { FIRE2_JUMBO_EXT } else { 0 }; - if src.len() < header_len { return Ok(None); } + if src.len() < header_len { + return Ok(None); + } let (frame, body_len) = FireFrame::read(src); diff --git a/crates/blaze-proto/src/frame.rs b/crates/blaze-proto/src/frame.rs index c3471c7..b3b5552 100644 --- a/crates/blaze-proto/src/frame.rs +++ b/crates/blaze-proto/src/frame.rs @@ -52,10 +52,10 @@ pub const FIRE2_JUMBO_EXT: usize = 2; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[repr(u8)] pub enum FrameType { - Request = 0x0, + Request = 0x0, Response = 0x1, - Notify = 0x2, - Error = 0x3, + Notify = 0x2, + Error = 0x3, } impl From for FrameType { @@ -64,7 +64,7 @@ impl From for FrameType { 0x1 => FrameType::Response, 0x2 => FrameType::Notify, 0x3 => FrameType::Error, - _ => FrameType::Request, + _ => FrameType::Request, } } } @@ -72,10 +72,10 @@ impl From for FrameType { impl std::fmt::Display for FrameType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - FrameType::Request => write!(f, "REQUEST"), + FrameType::Request => write!(f, "REQUEST"), FrameType::Response => write!(f, "RESPONSE"), - FrameType::Notify => write!(f, "NOTIFY"), - FrameType::Error => write!(f, "ERROR"), + FrameType::Notify => write!(f, "NOTIFY"), + FrameType::Error => write!(f, "ERROR"), } } } @@ -85,22 +85,24 @@ impl std::fmt::Display for FrameType { pub struct PacketOptions(pub u8); impl PacketOptions { - pub const NONE : Self = Self(0x0); + pub const NONE: Self = Self(0x0); /// Body length exceeds 16 bits; an extra u16 follows the header. pub const JUMBO_FRAME: Self = Self(0x1); - pub fn contains(self, flag: Self) -> bool { (self.0 & flag.0) != 0 } + pub fn contains(self, flag: Self) -> bool { + (self.0 & flag.0) != 0 + } } /// Fire2 packet header. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FireFrame { - pub component : u16, - pub command : u16, - pub error : u16, - pub ty : FrameType, - pub options : PacketOptions, - pub seq : u16, + pub component: u16, + pub command: u16, + pub error: u16, + pub ty: FrameType, + pub options: PacketOptions, + pub seq: u16, } impl FireFrame { @@ -108,23 +110,32 @@ impl FireFrame { pub fn response(&self) -> Self { Self { component: self.component, - command: self.command, - error: 0, - ty: FrameType::Response, - options: PacketOptions::NONE, - seq: self.seq, + command: self.command, + error: 0, + ty: FrameType::Response, + options: PacketOptions::NONE, + seq: self.seq, } } /// Build a notify frame. pub fn notify(component: u16, command: u16) -> Self { - Self { component, command, error: 0, ty: FrameType::Notify, options: PacketOptions::NONE, seq: 0 } + Self { + component, + command, + error: 0, + ty: FrameType::Notify, + options: PacketOptions::NONE, + seq: 0, + } } /// Encode this header into `dst`, prepending `body_len` at the front. pub fn write(&self, dst: &mut BytesMut, body_len: usize) { let mut options = self.options; - if body_len > u16::MAX as usize { options = PacketOptions(options.0 | PacketOptions::JUMBO_FRAME.0); } + if body_len > u16::MAX as usize { + options = PacketOptions(options.0 | PacketOptions::JUMBO_FRAME.0); + } dst.put_u16(body_len as u16); dst.put_u16(self.component); @@ -142,15 +153,15 @@ impl FireFrame { /// Parse a Fire2 header from `src`, which must already have `>= FIRE2_MIN_HEADER` bytes. /// Returns `(frame, body_length)`. Advances `src` past the header (including jumbo ext). pub fn read(src: &mut BytesMut) -> (FireFrame, usize) { - let len_lo = src.get_u16() as usize; + let len_lo = src.get_u16() as usize; let component = src.get_u16(); - let command = src.get_u16(); - let error = src.get_u16(); - let ty_byte = src.get_u8(); - let opt_byte = src.get_u8(); - let seq = src.get_u16(); + let command = src.get_u16(); + let error = src.get_u16(); + let ty_byte = src.get_u8(); + let opt_byte = src.get_u8(); + let seq = src.get_u16(); - let ty = FrameType::from(ty_byte >> 4); + let ty = FrameType::from(ty_byte >> 4); let options = PacketOptions(opt_byte >> 4); let body_len = if options.contains(PacketOptions::JUMBO_FRAME) { @@ -160,6 +171,16 @@ impl FireFrame { len_lo }; - (FireFrame { component, command, error, ty, options, seq }, body_len) + ( + FireFrame { + component, + command, + error, + ty, + options, + seq, + }, + body_len, + ) } } diff --git a/crates/blaze-proto/src/lib.rs b/crates/blaze-proto/src/lib.rs index cd4bb8a..1636483 100644 --- a/crates/blaze-proto/src/lib.rs +++ b/crates/blaze-proto/src/lib.rs @@ -1,7 +1,7 @@ -pub mod frame; pub mod codec; +pub mod frame; pub mod packet; +pub use codec::{FramingVariant, PacketCodec}; pub use frame::{FireFrame, FrameType, PacketOptions}; -pub use codec::{PacketCodec, FramingVariant}; pub use packet::Packet; diff --git a/crates/blaze-proto/src/packet.rs b/crates/blaze-proto/src/packet.rs index 5aa596d..1dceead 100644 --- a/crates/blaze-proto/src/packet.rs +++ b/crates/blaze-proto/src/packet.rs @@ -2,8 +2,8 @@ use bytes::Bytes; use serde::Serialize; use super::frame::{FireFrame, FrameType, PacketOptions}; -use tdf::stringify::TdfStringifier; use tdf::reader::TdfDeserializer; +use tdf::stringify::TdfStringifier; /// A fully framed Blaze packet. #[derive(Debug, Clone)] @@ -20,11 +20,11 @@ impl Packet { Packet { frame: FireFrame { component: 0, - command: 0, - error: 0, - ty: FrameType::Request, - options: PacketOptions::NONE, - seq: 0, + command: 0, + error: 0, + ty: FrameType::Request, + options: PacketOptions::NONE, + seq: 0, }, body, } @@ -32,14 +32,17 @@ impl Packet { /// Build an empty response that mirrors `req`'s routing fields. pub fn response_empty(req: &Packet) -> Self { - Packet { frame: req.frame.response(), body: Bytes::new() } + Packet { + frame: req.frame.response(), + body: Bytes::new(), + } } /// Build a response with the given TDF body. pub fn response(req: &Packet, value: &V) -> Self { Packet { frame: req.frame.response(), - body: Bytes::from(tdf::serialize_vec(value)), + body: Bytes::from(tdf::serialize_vec(value)), } } @@ -62,13 +65,13 @@ impl Packet { pub fn to_capture(&self) -> CaptureRecord { CaptureRecord { component: self.frame.component, - command: self.frame.command, - error: self.frame.error, - ty: self.frame.ty.to_string(), - seq: self.frame.seq, - body_len: self.body.len(), - raw_hex: hex::encode(&self.body), - tdf: self.tdf_string(), + command: self.frame.command, + error: self.frame.error, + ty: self.frame.ty.to_string(), + seq: self.frame.seq, + body_len: self.body.len(), + raw_hex: hex::encode(&self.body), + tdf: self.tdf_string(), } } } @@ -76,11 +79,11 @@ impl Packet { #[derive(Debug, Serialize)] pub struct CaptureRecord { pub component: u16, - pub command: u16, - pub error: u16, - pub ty: String, - pub seq: u16, - pub body_len: usize, - pub raw_hex: String, - pub tdf: String, + pub command: u16, + pub error: u16, + pub ty: String, + pub seq: u16, + pub body_len: usize, + pub raw_hex: String, + pub tdf: String, } diff --git a/crates/server/src/capture.rs b/crates/server/src/capture.rs index 0b101fc..cf7766e 100644 --- a/crates/server/src/capture.rs +++ b/crates/server/src/capture.rs @@ -3,45 +3,57 @@ //! Every packet (in and out) is appended to a JSONL file so the session can //! be replayed with `jq` and the component/command IDs can be catalogued. +use blaze_proto::Packet; +use chrono::Utc; +use serde_json::json; use std::{ fs::{File, OpenOptions}, io::Write, sync::{Arc, Mutex}, }; -use blaze_proto::Packet; -use chrono::Utc; -use serde_json::json; use tracing::{info, warn}; #[derive(Clone, Copy)] -pub enum Dir { In, Out } +pub enum Dir { + In, + Out, +} impl Dir { - fn as_str(self) -> &'static str { match self { Dir::In => "IN", Dir::Out => "OUT" } } + fn as_str(self) -> &'static str { + match self { + Dir::In => "IN", + Dir::Out => "OUT", + } + } } pub struct CaptureWriter { - file : Arc>, - pretty : bool, - counter : Arc>, + file: Arc>, + pretty: bool, + counter: Arc>, } impl CaptureWriter { pub fn open(dir: &str, pretty: bool) -> anyhow::Result { std::fs::create_dir_all(dir)?; - let ts = Utc::now().format("%Y%m%d-%H%M%S"); + let ts = Utc::now().format("%Y%m%d-%H%M%S"); let path = format!("{dir}/capture-{ts}.jsonl"); let file = OpenOptions::new().create(true).append(true).open(&path)?; info!(path, "capture file opened"); Ok(CaptureWriter { - file: Arc::new(Mutex::new(file)), + file: Arc::new(Mutex::new(file)), pretty, counter: Arc::new(Mutex::new(0)), }) } pub fn record(&self, pkt: &Packet, peer: &str, dir: Dir) { - let n = { let mut c = self.counter.lock().unwrap(); *c += 1; *c }; + let n = { + let mut c = self.counter.lock().unwrap(); + *c += 1; + *c + }; let ts = Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true); let rec = pkt.to_capture(); @@ -67,21 +79,35 @@ impl CaptureWriter { } if self.pretty { - println!("\n╔══ #{n:04} {ts} {peer} {d} ══", - d = dir.as_str()); - println!("║ component=0x{:04X} command=0x{:04X} {ty} seq={seq}", - rec.component, rec.command, ty = rec.ty, seq = rec.seq); + println!("\n╔══ #{n:04} {ts} {peer} {d} ══", d = dir.as_str()); + println!( + "║ component=0x{:04X} command=0x{:04X} {ty} seq={seq}", + rec.component, + rec.command, + ty = rec.ty, + seq = rec.seq + ); if rec.body_len > 0 { - println!("║ body ({} bytes):\n║ {}", - rec.body_len, - pkt.body.chunks(16) - .enumerate() - .map(|(i, chunk)| { - let h: String = chunk.iter().map(|b| format!("{b:02X}")).collect::>().join(" "); - let a: String = chunk.iter().map(|&b| if b.is_ascii_graphic() { b as char } else { '.' }).collect(); - format!("\n║ {:04X}: {h:<47} {a}", i * 16) - }) - .collect::()); + println!( + "║ body ({} bytes):\n║ {}", + rec.body_len, + pkt.body + .chunks(16) + .enumerate() + .map(|(i, chunk)| { + let h: String = chunk + .iter() + .map(|b| format!("{b:02X}")) + .collect::>() + .join(" "); + let a: String = chunk + .iter() + .map(|&b| if b.is_ascii_graphic() { b as char } else { '.' }) + .collect(); + format!("\n║ {:04X}: {h:<47} {a}", i * 16) + }) + .collect::() + ); println!("║ TDF: {}", rec.tdf); } println!("╚══════════════════════════════════════════════════"); diff --git a/crates/server/src/config.rs b/crates/server/src/config.rs index 2b67712..6a6bac8 100644 --- a/crates/server/src/config.rs +++ b/crates/server/src/config.rs @@ -2,50 +2,49 @@ use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct Config { - pub redirector : RedirectorConfig, - pub blaze : BlazeConfig, - pub tls : TlsConfig, - pub capture : CaptureConfig, + pub redirector: RedirectorConfig, + pub blaze: BlazeConfig, + pub tls: TlsConfig, + pub capture: CaptureConfig, } #[derive(Debug, Deserialize)] pub struct RedirectorConfig { /// Address to listen on. EA clients connect to gosredirector.ea.com which /// you redirect to 127.0.0.1 via /etc/hosts. - pub listen : String, + pub listen: String, } #[derive(Debug, Deserialize)] pub struct BlazeConfig { /// Address the Blaze server listens on. - pub listen : String, + pub listen: String, /// Address / hostname we tell the client to connect to (in the redirect response). - pub advertise_host : String, + pub advertise_host: String, /// Port we tell the client to connect to (in the redirect response). - pub advertise_port : u16, + pub advertise_port: u16, } #[derive(Debug, Deserialize)] pub struct TlsConfig { /// PEM certificate file (RSA-2048 recommended — DirtySDK rejects ECDSA). - pub cert : String, + pub cert: String, /// PEM private key file. - pub key : String, + pub key: String, } #[derive(Debug, Deserialize)] pub struct CaptureConfig { /// Directory where JSONL capture files are written. - pub dir : String, + pub dir: String, /// Also print decoded packet trees to stdout. - pub pretty_print : bool, + pub pretty_print: bool, } impl Config { pub fn load(path: &str) -> anyhow::Result { let text = std::fs::read_to_string(path) .map_err(|e| anyhow::anyhow!("cannot read {path}: {e}"))?; - toml::from_str(&text) - .map_err(|e| anyhow::anyhow!("config parse error in {path}: {e}")) + toml::from_str(&text).map_err(|e| anyhow::anyhow!("config parse error in {path}: {e}")) } } diff --git a/crates/server/src/dispatch.rs b/crates/server/src/dispatch.rs index 2de03ec..8b75def 100644 --- a/crates/server/src/dispatch.rs +++ b/crates/server/src/dispatch.rs @@ -3,8 +3,8 @@ //! All FIFA 23 component/command IDs are UNKNOWN until capture reveals them. //! The default handler returns an empty response — never panics, never hangs. -use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc}; use blaze_proto::Packet; +use std::{collections::HashMap, future::Future, pin::Pin, sync::Arc}; use tracing::warn; pub type BoxFuture = Pin + Send>>; @@ -15,15 +15,19 @@ pub struct Dispatcher { } impl Dispatcher { - pub fn new() -> Self { Dispatcher { table: HashMap::new() } } + pub fn new() -> Self { + Dispatcher { + table: HashMap::new(), + } + } pub fn register(&mut self, component: u16, command: u16, f: F) where - F: Fn(Packet) -> Fut + Send + Sync + 'static, + F: Fn(Packet) -> Fut + Send + Sync + 'static, Fut: Future> + Send + 'static, { - self.table.insert((component, command), - Arc::new(move |p| Box::pin(f(p)))); + self.table + .insert((component, command), Arc::new(move |p| Box::pin(f(p)))); } pub async fn dispatch(&self, pkt: Packet) -> Option { @@ -31,9 +35,11 @@ impl Dispatcher { if let Some(h) = self.table.get(&key) { h(pkt).await } else { - warn!(component = format!("0x{:04X}", key.0), - command = format!("0x{:04X}", key.1), - "unhandled — returning empty response"); + warn!( + component = format!("0x{:04X}", key.0), + command = format!("0x{:04X}", key.1), + "unhandled — returning empty response" + ); Some(Packet::response_empty(&pkt)) } } diff --git a/crates/server/src/main.rs b/crates/server/src/main.rs index b25487c..8a502a0 100644 --- a/crates/server/src/main.rs +++ b/crates/server/src/main.rs @@ -33,14 +33,19 @@ async fn main() -> anyhow::Result<()> { ) .init(); - let cfg_path = std::env::args().nth(1).unwrap_or_else(|| "config.toml".into()); + let cfg_path = std::env::args() + .nth(1) + .unwrap_or_else(|| "config.toml".into()); let cfg = config::Config::load(&cfg_path)?; info!(config = cfg_path, "loaded"); let tls_cfg = tls::load_tls_config(&cfg.tls.cert, &cfg.tls.key)?; let acceptor = TlsAcceptor::from(Arc::clone(&tls_cfg)); - let capture = Arc::new(CaptureWriter::open(&cfg.capture.dir, cfg.capture.pretty_print)?); + let capture = Arc::new(CaptureWriter::open( + &cfg.capture.dir, + cfg.capture.pretty_print, + )?); let dispatcher = Arc::new(Dispatcher::new()); let redir_addr: SocketAddr = cfg.redirector.listen.parse()?; @@ -55,18 +60,22 @@ async fn main() -> anyhow::Result<()> { // ── Redirector loop ─────────────────────────────────────────────────────── let redir_acceptor = acceptor.clone(); - let redir_cap = Arc::clone(&capture); - let adv_host = Arc::clone(&advertise_host); + let redir_cap = Arc::clone(&capture); + let adv_host = Arc::clone(&advertise_host); tokio::spawn(async move { loop { match redir_listener.accept().await { - Err(e) => { error!(error=%e, "redirector accept error"); } + Err(e) => { + error!(error=%e, "redirector accept error"); + } Ok((tcp, peer)) => { - let acc = redir_acceptor.clone(); - let cap = Arc::clone(&redir_cap); + let acc = redir_acceptor.clone(); + let cap = Arc::clone(&redir_cap); let host = Arc::clone(&adv_host); tokio::spawn(async move { - if let Err(e) = handle_redirector(tcp, peer, acc, cap, &host, advertise_port).await { + if let Err(e) = + handle_redirector(tcp, peer, acc, cap, &host, advertise_port).await + { warn!(%peer, error=%e, "redirector session error"); } }); @@ -78,10 +87,12 @@ async fn main() -> anyhow::Result<()> { // ── Blaze loop ──────────────────────────────────────────────────────────── loop { match blaze_listener.accept().await { - Err(e) => { error!(error=%e, "blaze accept error"); } + Err(e) => { + error!(error=%e, "blaze accept error"); + } Ok((tcp, peer)) => { - let acc = acceptor.clone(); - let cap = Arc::clone(&capture); + let acc = acceptor.clone(); + let cap = Arc::clone(&capture); let disp = Arc::clone(&dispatcher); tokio::spawn(async move { if let Err(e) = handle_blaze(tcp, peer, acc, cap, disp).await { @@ -96,15 +107,15 @@ async fn main() -> anyhow::Result<()> { // ── Redirector handler ──────────────────────────────────────────────────────── async fn handle_redirector( - tcp : TcpStream, - peer : SocketAddr, + tcp: TcpStream, + peer: SocketAddr, acceptor: TlsAcceptor, - capture : Arc, - host : &str, - port : u16, + capture: Arc, + host: &str, + port: u16, ) -> anyhow::Result<()> { - let tls = acceptor.accept(tcp).await?; - let codec = PacketCodec::new(FramingVariant::Fire2); + let tls = acceptor.accept(tcp).await?; + let codec = PacketCodec::new(FramingVariant::Fire2); let mut io = Framed::new(tls, codec); let peer_str = peer.to_string(); @@ -120,7 +131,10 @@ async fn handle_redirector( // capture will reveal it. For now we return our advertise address as a // simple string blob so the client gets something to parse. let body = build_redirector_body(host, port); - let resp = Packet { frame: req.frame.response(), body }; + let resp = Packet { + frame: req.frame.response(), + body, + }; capture.record(&resp, &peer_str, Dir::Out); io.send(resp).await?; } @@ -146,14 +160,14 @@ fn build_redirector_body(host: &str, port: u16) -> Bytes { // ── Blaze session handler ───────────────────────────────────────────────────── async fn handle_blaze( - tcp : TcpStream, - peer : SocketAddr, - acceptor : TlsAcceptor, - capture : Arc, + tcp: TcpStream, + peer: SocketAddr, + acceptor: TlsAcceptor, + capture: Arc, dispatcher: Arc, ) -> anyhow::Result<()> { - let tls = acceptor.accept(tcp).await?; - let codec = PacketCodec::new(FramingVariant::Fire2); + let tls = acceptor.accept(tcp).await?; + let codec = PacketCodec::new(FramingVariant::Fire2); let mut io = Framed::new(tls, codec); let peer_str = peer.to_string(); diff --git a/crates/server/src/tls.rs b/crates/server/src/tls.rs index a04aa6c..48276aa 100644 --- a/crates/server/src/tls.rs +++ b/crates/server/src/tls.rs @@ -1,7 +1,10 @@ -use std::{fs::File, io::BufReader, sync::Arc}; -use rustls::{ServerConfig, pki_types::{CertificateDer, PrivateKeyDer}}; -use rustls_pemfile::{certs, private_key}; use anyhow::Context; +use rustls::{ + pki_types::{CertificateDer, PrivateKeyDer}, + ServerConfig, +}; +use rustls_pemfile::{certs, private_key}; +use std::{fs::File, io::BufReader, sync::Arc}; /// Load a TLS `ServerConfig` from PEM cert and key files. /// @@ -13,10 +16,8 @@ pub fn load_tls_config(cert_path: &str, key_path: &str) -> anyhow::Result = certs(&mut BufReader::new(cert_file)) .collect::>()