mod support;
use std::io::{Read, Write};
use std::net::TcpStream;
use wolfssl::{ProtocolVersion, RootCertStore, TlsClient, TlsClientConfig};
use support::{server_config, start_echo_server, CA_CERT_PEM};
#[test]
fn client_connects_to_localhost_server() {
let (port, server_handle) = start_echo_server(server_config(false), 1);
let mut root_store = RootCertStore::new();
root_store.add_pem(CA_CERT_PEM);
let config = TlsClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth()
.build()
.expect("client config build failed");
let stream = TcpStream::connect(format!("127.0.0.1:{port}")).unwrap();
let mut tls = TlsClient::new(config, "localhost", stream).expect("TLS handshake failed");
tls.write_all(b"hello").expect("write failed");
let mut response = vec![0u8; 5];
tls.read_exact(&mut response).expect("read failed");
assert_eq!(&response, b"hello");
drop(tls);
server_handle.join().unwrap();
}
#[test]
fn client_rejects_self_signed_cert_without_ca() {
let (port, server_handle) = start_echo_server(server_config(false), 1);
let root_store = RootCertStore::new();
let config = TlsClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth()
.build()
.expect("client config build should succeed (empty store is valid)");
let stream = TcpStream::connect(format!("127.0.0.1:{port}")).unwrap();
let result = TlsClient::new(config, "localhost", stream);
assert!(result.is_err(), "connection should fail without trusted CA");
match result.unwrap_err() {
wolfssl::TlsError::CertificateVerification(_) => {} wolfssl::TlsError::Ffi { code, func } => {
eprintln!("got Ffi error: {func} code={code} (acceptable)");
}
other => panic!("unexpected error type: {other}"),
}
let _ = server_handle.join();
}
#[test]
fn client_accepts_cert_when_ca_in_root_store() {
let (port, server_handle) = start_echo_server(server_config(false), 1);
let mut root_store = RootCertStore::new();
root_store.add_pem(CA_CERT_PEM);
let config = TlsClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth()
.build()
.unwrap();
let stream = TcpStream::connect(format!("127.0.0.1:{port}")).unwrap();
let mut tls = TlsClient::new(config, "localhost", stream)
.expect("handshake should succeed with CA in store");
tls.write_all(b"ping").unwrap();
let mut buf = [0u8; 4];
tls.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"ping");
drop(tls);
server_handle.join().unwrap();
}
#[test]
fn large_data_transfer() {
let (port, server_handle) = start_echo_server(server_config(false), 1);
let mut root_store = RootCertStore::new();
root_store.add_pem(CA_CERT_PEM);
let config = TlsClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth()
.build()
.unwrap();
let stream = TcpStream::connect(format!("127.0.0.1:{port}")).unwrap();
let mut tls = TlsClient::new(config, "localhost", stream).unwrap();
let send_data: Vec<u8> = (0..1_000_000).map(|i| (i % 251) as u8).collect();
tls.write_all(&send_data).unwrap();
let mut recv_data = vec![0u8; send_data.len()];
tls.read_exact(&mut recv_data)
.expect("failed to read 1MB back");
assert_eq!(send_data.len(), recv_data.len(), "length mismatch");
assert!(
send_data == recv_data,
"data mismatch: sent and received bytes differ"
);
drop(tls);
server_handle.join().unwrap();
}
#[test]
fn tls_client_implements_read_write() {
let (port, server_handle) = start_echo_server(server_config(false), 1);
let mut root_store = RootCertStore::new();
root_store.add_pem(CA_CERT_PEM);
let config = TlsClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth()
.build()
.unwrap();
let stream = TcpStream::connect(format!("127.0.0.1:{port}")).unwrap();
let mut tls = TlsClient::new(config, "localhost", stream).unwrap();
let msg = b"generic io test data 12345";
tls.write_all(msg).unwrap();
let mut buf = vec![0u8; msg.len()];
tls.read_exact(&mut buf).unwrap();
assert_eq!(&buf, msg);
drop(tls);
server_handle.join().unwrap();
}
#[test]
fn connection_uses_modern_tls() {
let (port, server_handle) = start_echo_server(server_config(false), 1);
let mut root_store = RootCertStore::new();
root_store.add_pem(CA_CERT_PEM);
let config = TlsClientConfig::builder()
.with_protocol_versions(&[ProtocolVersion::Tls13])
.with_root_certificates(root_store)
.with_no_client_auth()
.build()
.unwrap();
let stream = TcpStream::connect(format!("127.0.0.1:{port}")).unwrap();
let mut tls =
TlsClient::new(config, "localhost", stream).expect("TLS 1.3 handshake should succeed");
tls.write_all(b"tls13").unwrap();
let mut buf = [0u8; 5];
tls.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"tls13");
drop(tls);
server_handle.join().unwrap();
}
#[test]
fn rejects_oversized_server_name() {
let mut root_store = RootCertStore::new();
root_store.add_pem(CA_CERT_PEM);
let config = TlsClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth()
.build()
.unwrap();
let huge_name = "a".repeat(254);
let dummy_io = std::io::Cursor::new(Vec::<u8>::new());
let result = TlsClient::new(config, &huge_name, dummy_io);
match result {
Err(wolfssl::TlsError::InvalidConfig(msg)) => {
assert!(
msg.contains("server name") || msg.contains("253"),
"error should describe the server name limit, got: {msg}"
);
}
Err(other) => panic!("expected InvalidConfig, got: {other}"),
Ok(_) => panic!("should have rejected oversized server name"),
}
}