pub mod proto; use std::{ fmt::Debug, io::{self, BufWriter, Read, Write}, net::TcpStream, }; use crate::proto::TLSPlaintext; type Result = std::result::Result; pub struct ClientConnection {} impl ClientConnection { pub fn establish(host: &str, port: u16) -> Result { let _setup = ClientSetupConnection::establish(host, port)?; todo!() } } struct ClientSetupConnection {} impl ClientSetupConnection { fn establish(host: &str, port: u16) -> Result { let mut stream = BufWriter::new(LoggingWriter(TcpStream::connect((host, port))?)); let handshake = proto::Handshake::ClientHello { legacy_version: proto::LEGACY_TLSV12, random: rand::random(), legacy_session_id: [(); 32].map(|()| rand::random()).to_vec().into(), cipher_suites: vec![proto::CipherSuite::TlsAes128GcmSha256].into(), legacy_compressions_methods: vec![0].into(), extensions: vec![ proto::ExtensionCH::ServerName { server_name: vec![proto::ServerName::HostName { host_name: host.as_bytes().to_vec().into(), }] .into(), }, proto::ExtensionCH::ECPointFormat { formats: vec![proto::ECPointFormat::Uncompressed].into(), }, proto::ExtensionCH::SupportedGroups { groups: vec![proto::NamedGroup::X25519].into(), }, proto::ExtensionCH::SupportedVersions { versions: vec![proto::TLSV13].into(), }, ] .into(), }; let plaintext = proto::TLSPlaintext::Handshake { handshake }; plaintext.write(&mut stream)?; stream.flush()?; println!("hello!"); let out = proto::TLSPlaintext::read(stream.get_mut())?; dbg!(&out); if matches!(out, TLSPlaintext::Handshake { handshake } if handshake.is_hello_retry_request()) { println!("hello retry request, the server doesnt like us :("); } // let res: proto::TLSPlaintext = proto::Value::read(&mut stream.get_mut())?; // dbg!(res); todo!() } } #[derive(Debug)] pub struct Error { pub kind: ErrorKind, } #[derive(Debug)] pub enum ErrorKind { InvalidFrame(Box), Io(io::Error), } impl From for Error { fn from(value: io::Error) -> Self { Self { kind: ErrorKind::Io(value), } } } impl From for Error { fn from(value: ErrorKind) -> Self { Self { kind: value } } } #[derive(Debug)] struct LoggingWriter(W); impl io::Write for LoggingWriter { fn write(&mut self, buf: &[u8]) -> io::Result { let len = self.0.write(buf); if let Ok(len) = len { eprintln!(" bytes: {:02x?}", &buf[..len]); } len } fn flush(&mut self) -> io::Result<()> { self.0.flush() } } impl io::Read for LoggingWriter { fn read(&mut self, buf: &mut [u8]) -> io::Result { let len = self.0.read(buf); if let Ok(len) = len { eprintln!("read bytes: {:02x?}", &buf[..len]); } len } }