use std::io::{Read, Write}; use std::net::TcpStream; use zagan_bet::protocol::LineBuffer; #[derive(Clone)] pub struct TlsServer { acceptor: native_tls::TlsAcceptor, } impl TlsServer { pub fn new(cert_path: &str) -> Result> { let cert_data = match std::fs::read(cert_path) { Ok(data) => data, Err(error) => { return Err(format!( "Could not read TLS identity '{}': {}\n\ \n\ First-time setup:\n\ \x20 cargo run --bin server -- --init\n\ \n\ Or generate it manually:\n\ \x20 openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem \\\n\ \x20 -days 3650 -nodes -subj \"/CN=localhost\"\n\ \x20 openssl pkcs12 -export -out identity.p12 -inkey key.pem -in cert.pem \\\n\ \x20 -passout pass:", cert_path, error ) .into()); } }; let identity = native_tls::Identity::from_pkcs12(&cert_data, "")?; let acceptor = native_tls::TlsAcceptor::new(identity)?; Ok(Self { acceptor }) } pub fn accept(&self, stream: TcpStream) -> Result> { let stream = self.acceptor.accept(stream)?; Ok(TlsStream::new(stream)) } } pub struct TlsStream { stream: native_tls::TlsStream, lines: LineBuffer, } impl TlsStream { pub fn new(stream: native_tls::TlsStream) -> Self { Self { stream, lines: LineBuffer::new(), } } pub fn read_line(&mut self, s: &mut String) -> std::io::Result { match self.lines.read_line(&mut self.stream)? { Some(line) => { s.push_str(&line); s.push('\n'); Ok(line.len() + 1) } None => Ok(0), } } pub fn write_line(&mut self, msg: &str) -> std::io::Result<()> { use std::io::Write; self.stream.write_all(msg.as_bytes())?; self.stream.write_all(b"\n")?; self.stream.flush()?; Ok(()) } pub fn peer_addr(&self) -> std::io::Result { self.stream.get_ref().peer_addr() } /// Read timeout for the socket. The connection loop uses a short one so it /// can advance crash rounds while no request is in flight. pub fn set_read_timeout(&self, duration: Option) -> std::io::Result<()> { self.stream.get_ref().set_read_timeout(duration) } } impl Read for TlsStream { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.stream.read(buf) } } impl Write for TlsStream { fn write(&mut self, buf: &[u8]) -> std::io::Result { self.stream.write(buf) } fn flush(&mut self) -> std::io::Result<()> { self.stream.flush() } }