use core::fmt::Debug;
use core::fmt::Formatter;
use std::fs::File;
use std::fs::OpenOptions;
use std::io;
use std::io::Write;
use std::path::PathBuf;
use std::sync::Mutex;
use rustls::KeyLog;
struct KeyLogFileInner {
file: Option<File>,
buf: Vec<u8>,
}
impl KeyLogFileInner {
fn new(path: &PathBuf) -> Self {
let file = match OpenOptions::new().append(true).create(true).open(path) {
Ok(f) => Some(f),
Err(e) => {
eprintln!("unable to create key log file {path:?}: {e}");
None
}
};
Self {
file,
buf: Vec::new(),
}
}
fn try_write(&mut self, label: &str, client_random: &[u8], secret: &[u8]) -> io::Result<()> {
let Some(file) = &mut self.file else {
return Ok(());
};
self.buf.truncate(0);
write!(self.buf, "{label} ")?;
for b in client_random.iter() {
write!(self.buf, "{b:02x}")?;
}
write!(self.buf, " ")?;
for b in secret.iter() {
write!(self.buf, "{b:02x}")?;
}
writeln!(self.buf)?;
file.write_all(&self.buf)
}
}
impl Debug for KeyLogFileInner {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("KeyLogFileInner")
.field("file", &self.file)
.finish_non_exhaustive()
}
}
pub struct KeyLogFile(Mutex<KeyLogFileInner>);
impl KeyLogFile {
pub fn new(path: &PathBuf) -> Self {
Self(Mutex::new(KeyLogFileInner::new(path)))
}
}
impl KeyLog for KeyLogFile {
fn log(&self, label: &str, client_random: &[u8], secret: &[u8]) {
match self
.0
.lock()
.unwrap()
.try_write(label, client_random, secret)
{
Ok(()) => {}
Err(e) => {
eprintln!("error writing to key log file: {e}");
}
}
}
}
impl Debug for KeyLogFile {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self.0.try_lock() {
Ok(key_log_file) => write!(f, "{key_log_file:?}"),
Err(_) => write!(f, "KeyLogFile {{ <locked> }}"),
}
}
}