// A Rustls stub for TryTLS // // Author: Joachim Viide // See: https://github.com/HowNetWorks/trytls-rustls-stub // use rustls::{ClientConfig, ClientConnection, Error, OwnedTrustAnchor, RootCertStore}; use std::convert::TryInto; use std::env; use std::error::Error as StdError; use std::fs::File; use std::io::{BufReader, Read, Write}; use std::net::TcpStream; use std::process; use std::sync::Arc; enum Verdict { Accept, Reject(Error), } fn parse_args(args: &[String]) -> Result<(String, u16, ClientConfig), Box> { let mut root_store = RootCertStore::empty(); match args.len() { 3 => { root_store.add_server_trust_anchors( webpki_roots::TLS_SERVER_ROOTS .0 .iter() .map(|ta| { OwnedTrustAnchor::from_subject_spki_name_constraints( ta.subject, ta.spki, ta.name_constraints, ) }), ); } 4 => { let f = File::open(&args[3])?; root_store .add_parsable_certificates(&rustls_pemfile::certs(&mut BufReader::new(f)).unwrap()); } _ => { return Err(From::from("Incorrect number of arguments")); } }; let config = rustls::ClientConfig::builder() .with_safe_defaults() .with_root_certificates(root_store) .with_no_client_auth(); let port = args[2].parse()?; Ok((args[1].clone(), port, config)) } fn communicate( host: String, port: u16, config: ClientConfig, ) -> Result> { let server_name = host.as_str().try_into().unwrap(); let rc_config = Arc::new(config); let mut client = ClientConnection::new(rc_config, server_name).unwrap(); let mut stream = TcpStream::connect((&*host, port))?; client .writer() .write_all(b"GET / HTTP/1.0\r\nConnection: close\r\nContent-Length: 0\r\n\r\n")?; loop { while client.wants_write() { client.write_tls(&mut stream)?; } if client.wants_read() { if client.read_tls(&mut stream)? == 0 { return Err(From::from("Connection closed")); } if let Err(err) = client.process_new_packets() { return match err { Error::InvalidCertificateData(_) | Error::InvalidCertificateSignature | Error::InvalidCertificateSignatureType | Error::InvalidCertificateEncoding | Error::AlertReceived(_) => Ok(Verdict::Reject(err)), _ => Err(From::from(format!("{:?}", err))), }; } if client.reader().read(&mut [0])? > 0 { return Ok(Verdict::Accept); } } } } fn main() { let args: Vec = env::args().collect(); let (host, port, config) = parse_args(&args).unwrap_or_else(|err| { println!("Argument error: {}", err); process::exit(2); }); match communicate(host, port, config) { Ok(Verdict::Accept) => { println!("ACCEPT"); process::exit(0); } Ok(Verdict::Reject(reason)) => { println!("{:?}", reason); println!("REJECT"); process::exit(0); } Err(err) => { println!("{}", err); process::exit(1); } } }