rustls/rustls/examples/internal/trytls_shim.rs

118 lines
3.3 KiB
Rust

// 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::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<dyn StdError>> {
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<Verdict, Box<dyn StdError>> {
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::InvalidCertificate(_) | 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<String> = 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);
}
}
}