Port to rustls v0.20 (#9)
This commit is contained in:
parent
37aa3f1bad
commit
e70f938475
|
@ -1,2 +1,2 @@
|
|||
/target
|
||||
target/
|
||||
Cargo.lock
|
||||
|
|
37
Cargo.toml
37
Cargo.toml
|
@ -1,35 +1,38 @@
|
|||
[package]
|
||||
name = "async-rustls"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
authors = [
|
||||
"Alex Crichton <alex@alexcrichton.com>",
|
||||
"quininer kel <quininer@live.com>",
|
||||
"Stjepan Glavina <stjepang@gmail.com>",
|
||||
"John Nunley <jtnunley01@gmail.com>",
|
||||
]
|
||||
edition = "2018"
|
||||
description = "Async TLS/SSL streams using rustls"
|
||||
license = "Apache-2.0 OR MIT"
|
||||
repository = "https://github.com/smol-rs/async-rustls"
|
||||
homepage = "https://github.com/smol-rs/async-rustls"
|
||||
documentation = "https://docs.rs/async-rustls"
|
||||
keywords = ["rustls", "tls", "ssl", "synchronization"]
|
||||
readme = "README.md"
|
||||
description = "Asynchronous TLS/SSL streams using Rustls."
|
||||
categories = ["asynchronous", "cryptography", "network-programming"]
|
||||
|
||||
[features]
|
||||
default = ["logging"]
|
||||
logging = ["rustls/logging"]
|
||||
dangerous_configuration = ["rustls/dangerous_configuration"]
|
||||
quic = ["rustls/quic"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
futures-lite = "1.10.1"
|
||||
webpki = "0.21"
|
||||
futures-io = "0.3.24"
|
||||
rustls = { version = "0.20", default-features = false }
|
||||
webpki = "0.22"
|
||||
|
||||
[dependencies.rustls]
|
||||
version = "0.19"
|
||||
default-features = false
|
||||
[features]
|
||||
default = ["logging", "tls12"]
|
||||
dangerous_configuration = ["rustls/dangerous_configuration"]
|
||||
early-data = []
|
||||
logging = ["rustls/logging"]
|
||||
tls12 = ["rustls/tls12"]
|
||||
quic = ["rustls/quic"]
|
||||
|
||||
[dev-dependencies]
|
||||
smol = "1.0"
|
||||
futures-util = "0.3.1"
|
||||
lazy_static = "1"
|
||||
smol = "1.2.3"
|
||||
webpki-roots = "0.20"
|
||||
webpki-roots = "0.22"
|
||||
rustls-pemfile = "1"
|
||||
once_cell = "1.14"
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
[package]
|
||||
name = "client"
|
||||
version = "0.1.0"
|
||||
authors = ["quininer <quininer@live.com>"]
|
||||
authors = ["quininer <quininer@live.com>", "John Nunley <jtnunley01@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "0.2", features = [ "full" ] }
|
||||
argh = "0.1"
|
||||
tokio-rustls = { path = "../.." }
|
||||
webpki-roots = "0.20"
|
||||
async-rustls = { path = "../.." }
|
||||
webpki-roots = "0.22"
|
||||
rustls-pemfile = "0.2"
|
||||
smol = "1"
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
use argh::FromArgs;
|
||||
use async_rustls::rustls::{self, OwnedTrustAnchor};
|
||||
use async_rustls::{webpki, TlsConnector};
|
||||
use smol::io::{copy, split, AsyncWriteExt};
|
||||
use smol::net::TcpStream;
|
||||
use smol::prelude::*;
|
||||
use smol::Unblock;
|
||||
use std::convert::TryFrom;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::BufReader;
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::io::{copy, split, stdin as tokio_stdin, stdout as tokio_stdout, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_rustls::{rustls::ClientConfig, webpki::DNSNameRef, TlsConnector};
|
||||
|
||||
/// Tokio Rustls client example
|
||||
#[derive(FromArgs)]
|
||||
|
@ -29,52 +33,69 @@ struct Options {
|
|||
cafile: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
let options: Options = argh::from_env();
|
||||
fn main() -> io::Result<()> {
|
||||
smol::block_on(async {
|
||||
let options: Options = argh::from_env();
|
||||
|
||||
let addr = (options.host.as_str(), options.port)
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))?;
|
||||
let domain = options.domain.unwrap_or(options.host);
|
||||
let content = format!("GET / HTTP/1.0\r\nHost: {}\r\n\r\n", domain);
|
||||
let addr = (options.host.as_str(), options.port)
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))?;
|
||||
let domain = options.domain.unwrap_or(options.host);
|
||||
let content = format!("GET / HTTP/1.0\r\nHost: {}\r\n\r\n", domain);
|
||||
|
||||
let mut config = ClientConfig::new();
|
||||
if let Some(cafile) = &options.cafile {
|
||||
let mut pem = BufReader::new(File::open(cafile)?);
|
||||
config
|
||||
.root_store
|
||||
.add_pem_file(&mut pem)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))?;
|
||||
} else {
|
||||
config
|
||||
.root_store
|
||||
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
|
||||
}
|
||||
let connector = TlsConnector::from(Arc::new(config));
|
||||
|
||||
let stream = TcpStream::connect(&addr).await?;
|
||||
|
||||
let (mut stdin, mut stdout) = (tokio_stdin(), tokio_stdout());
|
||||
|
||||
let domain = DNSNameRef::try_from_ascii_str(&domain)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid dnsname"))?;
|
||||
|
||||
let mut stream = connector.connect(domain, stream).await?;
|
||||
stream.write_all(content.as_bytes()).await?;
|
||||
|
||||
let (mut reader, mut writer) = split(stream);
|
||||
|
||||
tokio::select! {
|
||||
ret = copy(&mut reader, &mut stdout) => {
|
||||
ret?;
|
||||
},
|
||||
ret = copy(&mut stdin, &mut writer) => {
|
||||
ret?;
|
||||
writer.close().await?
|
||||
let mut root_cert_store = rustls::RootCertStore::empty();
|
||||
if let Some(cafile) = &options.cafile {
|
||||
let mut pem = BufReader::new(File::open(cafile)?);
|
||||
let certs = rustls_pemfile::certs(&mut pem)?;
|
||||
let trust_anchors = certs.iter().map(|cert| {
|
||||
let ta = webpki::TrustAnchor::try_from_cert_der(&cert[..]).unwrap();
|
||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
ta.subject,
|
||||
ta.spki,
|
||||
ta.name_constraints,
|
||||
)
|
||||
});
|
||||
root_cert_store.add_server_trust_anchors(trust_anchors);
|
||||
} else {
|
||||
root_cert_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,
|
||||
)
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
let config = rustls::ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_root_certificates(root_cert_store)
|
||||
.with_no_client_auth(); // i guess this was previously the default?
|
||||
let connector = TlsConnector::from(Arc::new(config));
|
||||
|
||||
let stream = TcpStream::connect(&addr).await?;
|
||||
|
||||
let (stdin_obj, stdout_obj) = (std::io::stdin(), std::io::stdout());
|
||||
let (mut stdin, mut stdout) = (Unblock::new(stdin_obj), Unblock::new(stdout_obj));
|
||||
|
||||
let domain = rustls::ServerName::try_from(domain.as_str())
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid dnsname"))?;
|
||||
|
||||
let mut stream = connector.connect(domain, stream).await?;
|
||||
stream.write_all(content.as_bytes()).await?;
|
||||
|
||||
let (mut reader, mut writer) = split(stream);
|
||||
|
||||
copy(&mut reader, &mut stdout)
|
||||
.or(async move {
|
||||
copy(&mut stdin, &mut writer).await?;
|
||||
writer.close().await?;
|
||||
Ok(0)
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
[package]
|
||||
name = "server"
|
||||
version = "0.1.0"
|
||||
authors = ["quininer <quininer@live.com>"]
|
||||
authors = ["quininer <quininer@live.com>", "John Nunley <jtnunley01@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "0.2", features = [ "full" ] }
|
||||
argh = "0.1"
|
||||
tokio-rustls = { path = "../.." }
|
||||
async-rustls = { path = "../.." }
|
||||
rustls-pemfile = "0.2.1"
|
||||
smol = "1"
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use argh::FromArgs;
|
||||
use async_rustls::rustls::{self, Certificate, PrivateKey};
|
||||
use async_rustls::TlsAcceptor;
|
||||
use rustls_pemfile::{certs, rsa_private_keys};
|
||||
use smol::io::{copy, sink, split, AsyncWriteExt};
|
||||
use smol::net::TcpListener;
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufReader};
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use tokio::io::{copy, sink, split, AsyncWriteExt};
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_rustls::rustls::internal::pemfile::{certs, rsa_private_keys};
|
||||
use tokio_rustls::rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig};
|
||||
use tokio_rustls::TlsAcceptor;
|
||||
|
||||
/// Tokio Rustls server example
|
||||
#[derive(FromArgs)]
|
||||
|
@ -33,69 +33,74 @@ struct Options {
|
|||
fn load_certs(path: &Path) -> io::Result<Vec<Certificate>> {
|
||||
certs(&mut BufReader::new(File::open(path)?))
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert"))
|
||||
.map(|mut certs| certs.drain(..).map(Certificate).collect())
|
||||
}
|
||||
|
||||
fn load_keys(path: &Path) -> io::Result<Vec<PrivateKey>> {
|
||||
rsa_private_keys(&mut BufReader::new(File::open(path)?))
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid key"))
|
||||
.map(|mut keys| keys.drain(..).map(PrivateKey).collect())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
let options: Options = argh::from_env();
|
||||
fn main() -> io::Result<()> {
|
||||
smol::block_on(async {
|
||||
let options: Options = argh::from_env();
|
||||
|
||||
let addr = options
|
||||
.addr
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.ok_or_else(|| io::Error::from(io::ErrorKind::AddrNotAvailable))?;
|
||||
let certs = load_certs(&options.cert)?;
|
||||
let mut keys = load_keys(&options.key)?;
|
||||
let flag_echo = options.echo_mode;
|
||||
let addr = options
|
||||
.addr
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.ok_or_else(|| io::Error::from(io::ErrorKind::AddrNotAvailable))?;
|
||||
let certs = load_certs(&options.cert)?;
|
||||
let mut keys = load_keys(&options.key)?;
|
||||
let flag_echo = options.echo_mode;
|
||||
|
||||
let mut config = ServerConfig::new(NoClientAuth::new());
|
||||
config
|
||||
.set_single_cert(certs, keys.remove(0))
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
|
||||
let acceptor = TlsAcceptor::from(Arc::new(config));
|
||||
let config = rustls::ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(certs, keys.remove(0))
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
|
||||
let acceptor = TlsAcceptor::from(Arc::new(config));
|
||||
|
||||
let mut listener = TcpListener::bind(&addr).await?;
|
||||
let listener = TcpListener::bind(&addr).await?;
|
||||
|
||||
loop {
|
||||
let (stream, peer_addr) = listener.accept().await?;
|
||||
let acceptor = acceptor.clone();
|
||||
loop {
|
||||
let (stream, peer_addr) = listener.accept().await?;
|
||||
let acceptor = acceptor.clone();
|
||||
|
||||
let fut = async move {
|
||||
let mut stream = acceptor.accept(stream).await?;
|
||||
let fut = async move {
|
||||
let mut stream = acceptor.accept(stream).await?;
|
||||
|
||||
if flag_echo {
|
||||
let (mut reader, mut writer) = split(stream);
|
||||
let n = copy(&mut reader, &mut writer).await?;
|
||||
writer.flush().await?;
|
||||
println!("Echo: {} - {}", peer_addr, n);
|
||||
} else {
|
||||
let mut output = sink();
|
||||
stream
|
||||
.write_all(
|
||||
&b"HTTP/1.0 200 ok\r\n\
|
||||
if flag_echo {
|
||||
let (mut reader, mut writer) = split(stream);
|
||||
let n = copy(&mut reader, &mut writer).await?;
|
||||
writer.flush().await?;
|
||||
println!("Echo: {} - {}", peer_addr, n);
|
||||
} else {
|
||||
let mut output = sink();
|
||||
stream
|
||||
.write_all(
|
||||
&b"HTTP/1.0 200 ok\r\n\
|
||||
Connection: close\r\n\
|
||||
Content-length: 12\r\n\
|
||||
\r\n\
|
||||
Hello world!"[..],
|
||||
)
|
||||
.await?;
|
||||
stream.close().await?;
|
||||
copy(&mut stream, &mut output).await?;
|
||||
println!("Hello: {}", peer_addr);
|
||||
}
|
||||
)
|
||||
.await?;
|
||||
stream.close().await?;
|
||||
copy(&mut stream, &mut output).await?;
|
||||
println!("Hello: {}", peer_addr);
|
||||
}
|
||||
|
||||
Ok(()) as io::Result<()>
|
||||
};
|
||||
Ok(()) as io::Result<()>
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = fut.await {
|
||||
eprintln!("{:?}", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
smol::spawn(async move {
|
||||
if let Err(err) = fut.await {
|
||||
eprintln!("{:?}", err);
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
128
src/client.rs
128
src/client.rs
|
@ -1,38 +1,64 @@
|
|||
use super::*;
|
||||
use crate::common::IoSession;
|
||||
use futures_lite::ready;
|
||||
use rustls::Session;
|
||||
use std::io::Write;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::io::{AsRawSocket, RawSocket};
|
||||
|
||||
/// A wrapper around an underlying raw stream which implements the TLS or SSL
|
||||
/// protocol.
|
||||
#[derive(Debug)]
|
||||
pub struct TlsStream<IO> {
|
||||
pub(crate) io: IO,
|
||||
pub(crate) session: ClientSession,
|
||||
pub(crate) session: ClientConnection,
|
||||
pub(crate) state: TlsState,
|
||||
|
||||
#[cfg(feature = "early-data")]
|
||||
pub(crate) early_waker: Option<std::task::Waker>,
|
||||
}
|
||||
|
||||
impl<IO> TlsStream<IO> {
|
||||
#[inline]
|
||||
pub fn get_ref(&self) -> (&IO, &ClientSession) {
|
||||
pub fn get_ref(&self) -> (&IO, &ClientConnection) {
|
||||
(&self.io, &self.session)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self) -> (&mut IO, &mut ClientSession) {
|
||||
pub fn get_mut(&mut self) -> (&mut IO, &mut ClientConnection) {
|
||||
(&mut self.io, &mut self.session)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_inner(self) -> (IO, ClientSession) {
|
||||
pub fn into_inner(self) -> (IO, ClientConnection) {
|
||||
(self.io, self.session)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl<S> AsRawFd for TlsStream<S>
|
||||
where
|
||||
S: AsRawFd,
|
||||
{
|
||||
#[inline]
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.get_ref().0.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl<S> AsRawSocket for TlsStream<S>
|
||||
where
|
||||
S: AsRawSocket,
|
||||
{
|
||||
#[inline]
|
||||
fn as_raw_socket(&self) -> RawSocket {
|
||||
self.get_ref().0.as_raw_socket()
|
||||
}
|
||||
}
|
||||
|
||||
impl<IO> IoSession for TlsStream<IO> {
|
||||
type Io = IO;
|
||||
type Session = ClientSession;
|
||||
type Session = ClientConnection;
|
||||
|
||||
#[inline]
|
||||
fn skip_handshake(&self) -> bool {
|
||||
|
@ -60,21 +86,43 @@ where
|
|||
buf: &mut [u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
match self.state {
|
||||
TlsState::EarlyData(..) => Poll::Pending,
|
||||
#[cfg(feature = "early-data")]
|
||||
TlsState::EarlyData(..) => {
|
||||
let this = self.get_mut();
|
||||
|
||||
// In the EarlyData state, we have not really established a Tls connection.
|
||||
// Before writing data through `AsyncWrite` and completing the tls handshake,
|
||||
// we ignore read readiness and return to pending.
|
||||
//
|
||||
// In order to avoid event loss,
|
||||
// we need to register a waker and wake it up after tls is connected.
|
||||
if this
|
||||
.early_waker
|
||||
.as_ref()
|
||||
.filter(|waker| cx.waker().will_wake(waker))
|
||||
.is_none()
|
||||
{
|
||||
this.early_waker = Some(cx.waker().clone());
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
TlsState::Stream | TlsState::WriteShutdown => {
|
||||
let this = self.get_mut();
|
||||
let mut stream =
|
||||
Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable());
|
||||
|
||||
match stream.as_mut_pin().poll_read(cx, buf) {
|
||||
Poll::Ready(Ok(0)) => {
|
||||
this.state.shutdown_read();
|
||||
Poll::Ready(Ok(0))
|
||||
Poll::Ready(Ok(n)) => {
|
||||
if n == 0 || stream.eof {
|
||||
this.state.shutdown_read();
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(n))
|
||||
}
|
||||
Poll::Ready(Ok(n)) => Poll::Ready(Ok(n)),
|
||||
Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::ConnectionAborted => {
|
||||
Poll::Ready(Err(err)) if err.kind() == io::ErrorKind::ConnectionAborted => {
|
||||
this.state.shutdown_read();
|
||||
Poll::Ready(Ok(0))
|
||||
Poll::Ready(Err(err))
|
||||
}
|
||||
output => output,
|
||||
}
|
||||
|
@ -101,7 +149,10 @@ where
|
|||
|
||||
#[allow(clippy::match_single_binding)]
|
||||
match this.state {
|
||||
#[cfg(feature = "early-data")]
|
||||
TlsState::EarlyData(ref mut pos, ref mut data) => {
|
||||
use std::io::Write;
|
||||
|
||||
// write early data
|
||||
if let Some(mut early_data) = stream.session.early_data() {
|
||||
let len = match early_data.write(buf) {
|
||||
|
@ -132,6 +183,11 @@ where
|
|||
|
||||
// end
|
||||
this.state = TlsState::Stream;
|
||||
|
||||
if let Some(waker) = this.early_waker.take() {
|
||||
waker.wake();
|
||||
}
|
||||
|
||||
stream.as_mut_pin().poll_write(cx, buf)
|
||||
}
|
||||
_ => stream.as_mut_pin().poll_write(cx, buf),
|
||||
|
@ -143,37 +199,45 @@ where
|
|||
let mut stream =
|
||||
Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable());
|
||||
|
||||
if let TlsState::EarlyData(ref mut pos, ref mut data) = this.state {
|
||||
// complete handshake
|
||||
while stream.session.is_handshaking() {
|
||||
ready!(stream.handshake(cx))?;
|
||||
}
|
||||
#[cfg(feature = "early-data")]
|
||||
{
|
||||
if let TlsState::EarlyData(ref mut pos, ref mut data) = this.state {
|
||||
// complete handshake
|
||||
while stream.session.is_handshaking() {
|
||||
ready!(stream.handshake(cx))?;
|
||||
}
|
||||
|
||||
// write early data (fallback)
|
||||
if !stream.session.is_early_data_accepted() {
|
||||
while *pos < data.len() {
|
||||
let len = ready!(stream.as_mut_pin().poll_write(cx, &data[*pos..]))?;
|
||||
*pos += len;
|
||||
// write early data (fallback)
|
||||
if !stream.session.is_early_data_accepted() {
|
||||
while *pos < data.len() {
|
||||
let len = ready!(stream.as_mut_pin().poll_write(cx, &data[*pos..]))?;
|
||||
*pos += len;
|
||||
}
|
||||
}
|
||||
|
||||
this.state = TlsState::Stream;
|
||||
|
||||
if let Some(waker) = this.early_waker.take() {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
this.state = TlsState::Stream;
|
||||
}
|
||||
|
||||
stream.as_mut_pin().poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
// complete handshake
|
||||
#[cfg(feature = "early-data")]
|
||||
if matches!(self.state, TlsState::EarlyData(..)) {
|
||||
ready!(self.as_mut().poll_flush(cx))?;
|
||||
}
|
||||
|
||||
if self.state.writeable() {
|
||||
self.session.send_close_notify();
|
||||
self.state.shutdown_write();
|
||||
}
|
||||
|
||||
// we skip the handshake
|
||||
if let TlsState::EarlyData(..) = self.state {
|
||||
return Pin::new(&mut self.io).poll_close(cx);
|
||||
}
|
||||
|
||||
let this = self.get_mut();
|
||||
let mut stream =
|
||||
Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable());
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::common::{Stream, TlsState};
|
||||
use futures_lite::io::{AsyncRead, AsyncWrite};
|
||||
use rustls::Session;
|
||||
use futures_io::{AsyncRead, AsyncWrite};
|
||||
use rustls::{ConnectionCommon, SideData};
|
||||
use std::future::Future;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{io, mem};
|
||||
|
@ -15,28 +16,30 @@ pub(crate) trait IoSession {
|
|||
fn into_io(self) -> Self::Io;
|
||||
}
|
||||
|
||||
pub(crate) enum MidHandshake<IS> {
|
||||
pub(crate) enum MidHandshake<IS: IoSession> {
|
||||
Handshaking(IS),
|
||||
End,
|
||||
Error { io: IS::Io, error: io::Error },
|
||||
}
|
||||
|
||||
impl<IS> Future for MidHandshake<IS>
|
||||
impl<IS, SD> Future for MidHandshake<IS>
|
||||
where
|
||||
IS: IoSession + Unpin,
|
||||
IS::Io: AsyncRead + AsyncWrite + Unpin,
|
||||
IS::Session: Session + Unpin,
|
||||
IS::Session: DerefMut + Deref<Target = ConnectionCommon<SD>> + Unpin,
|
||||
SD: SideData,
|
||||
{
|
||||
type Output = Result<IS, (io::Error, IS::Io)>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.get_mut();
|
||||
|
||||
let mut stream =
|
||||
if let MidHandshake::Handshaking(stream) = mem::replace(this, MidHandshake::End) {
|
||||
stream
|
||||
} else {
|
||||
panic!("unexpected polling after handshake")
|
||||
};
|
||||
let mut stream = match mem::replace(this, MidHandshake::End) {
|
||||
MidHandshake::Handshaking(stream) => stream,
|
||||
// Starting the handshake returned an error; fail the future immediately.
|
||||
MidHandshake::Error { io, error } => return Poll::Ready(Err((error, io))),
|
||||
_ => panic!("unexpected polling after handshake"),
|
||||
};
|
||||
|
||||
if !stream.skip_handshake() {
|
||||
let (state, io, session) = stream.get_mut();
|
||||
|
@ -59,9 +62,7 @@ where
|
|||
try_poll!(tls_stream.handshake(cx));
|
||||
}
|
||||
|
||||
while tls_stream.session.wants_write() {
|
||||
try_poll!(tls_stream.write_io(cx));
|
||||
}
|
||||
try_poll!(Pin::new(&mut tls_stream).poll_flush(cx));
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(stream))
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
mod handshake;
|
||||
|
||||
use futures_lite::io::{AsyncRead, AsyncWrite};
|
||||
use futures_io::{AsyncRead, AsyncWrite};
|
||||
pub(crate) use handshake::{IoSession, MidHandshake};
|
||||
use rustls::Session;
|
||||
use std::io::{self, Read, Write};
|
||||
use rustls::{ConnectionCommon, SideData};
|
||||
use std::io::{self, IoSlice, Read, Write};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TlsState {
|
||||
#[cfg(feature = "early-data")]
|
||||
EarlyData(usize, Vec<u8>),
|
||||
Stream,
|
||||
ReadShutdown,
|
||||
|
@ -35,18 +37,25 @@ impl TlsState {
|
|||
|
||||
#[inline]
|
||||
pub fn writeable(&self) -> bool {
|
||||
!matches!(self, TlsState::WriteShutdown | TlsState::FullyShutdown)
|
||||
!matches!(*self, TlsState::WriteShutdown | TlsState::FullyShutdown)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn readable(&self) -> bool {
|
||||
!matches!(self, TlsState::ReadShutdown | TlsState::FullyShutdown)
|
||||
!matches!(*self, TlsState::ReadShutdown | TlsState::FullyShutdown)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "early-data")]
|
||||
pub fn is_early_data(&self) -> bool {
|
||||
matches!(self, TlsState::EarlyData(..))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(not(feature = "early-data"))]
|
||||
pub fn is_early_data(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Stream<'a, IO, S> {
|
||||
|
@ -55,7 +64,11 @@ pub struct Stream<'a, IO, S> {
|
|||
pub eof: bool,
|
||||
}
|
||||
|
||||
impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> Stream<'a, IO, S> {
|
||||
impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S, SD> Stream<'a, IO, S>
|
||||
where
|
||||
S: DerefMut + Deref<Target = ConnectionCommon<SD>>,
|
||||
SD: SideData,
|
||||
{
|
||||
pub fn new(io: &'a mut IO, session: &'a mut S) -> Self {
|
||||
Stream {
|
||||
io,
|
||||
|
@ -76,22 +89,7 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> Stream<'a, IO, S> {
|
|||
}
|
||||
|
||||
pub fn read_io(&mut self, cx: &mut Context) -> Poll<io::Result<usize>> {
|
||||
struct Reader<'a, 'b, T> {
|
||||
io: &'a mut T,
|
||||
cx: &'a mut Context<'b>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, T: AsyncRead + Unpin> Read for Reader<'a, 'b, T> {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match Pin::new(&mut self.io).poll_read(self.cx, buf) {
|
||||
Poll::Ready(result) => result,
|
||||
Poll::Pending => Err(io::ErrorKind::WouldBlock.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut reader = Reader { io: self.io, cx };
|
||||
let mut reader = SyncReadAdapter { io: self.io, cx };
|
||||
|
||||
let n = match self.session.read_tls(&mut reader) {
|
||||
Ok(n) => n,
|
||||
|
@ -99,7 +97,7 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> Stream<'a, IO, S> {
|
|||
Err(err) => return Poll::Ready(Err(err)),
|
||||
};
|
||||
|
||||
self.session.process_new_packets().map_err(|err| {
|
||||
let stats = self.session.process_new_packets().map_err(|err| {
|
||||
// In case we have an alert to send describing this error,
|
||||
// try a last-gasp write -- but don't predate the primary
|
||||
// error.
|
||||
|
@ -108,6 +106,13 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> Stream<'a, IO, S> {
|
|||
io::Error::new(io::ErrorKind::InvalidData, err)
|
||||
})?;
|
||||
|
||||
if stats.peer_has_closed() && self.session.is_handshaking() {
|
||||
return Poll::Ready(Err(io::Error::new(
|
||||
io::ErrorKind::UnexpectedEof,
|
||||
"tls handshake alert",
|
||||
)));
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(n))
|
||||
}
|
||||
|
||||
|
@ -117,20 +122,33 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> Stream<'a, IO, S> {
|
|||
cx: &'a mut Context<'b>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, T: AsyncWrite + Unpin> Write for Writer<'a, 'b, T> {
|
||||
impl<'a, 'b, T: Unpin> Writer<'a, 'b, T> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match Pin::new(&mut self.io).poll_write(self.cx, buf) {
|
||||
fn poll_with<U>(
|
||||
&mut self,
|
||||
f: impl FnOnce(Pin<&mut T>, &mut Context<'_>) -> Poll<io::Result<U>>,
|
||||
) -> io::Result<U> {
|
||||
match f(Pin::new(self.io), self.cx) {
|
||||
Poll::Ready(result) => result,
|
||||
Poll::Pending => Err(io::ErrorKind::WouldBlock.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, T: AsyncWrite + Unpin> Write for Writer<'a, 'b, T> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.poll_with(|io, cx| io.poll_write(cx, buf))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.poll_with(|io, cx| io.poll_write_vectored(cx, bufs))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
match Pin::new(&mut self.io).poll_flush(self.cx) {
|
||||
Poll::Ready(result) => result,
|
||||
Poll::Pending => Err(io::ErrorKind::WouldBlock.into()),
|
||||
}
|
||||
self.poll_with(|io, cx| io.poll_flush(cx))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,10 +167,14 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> Stream<'a, IO, S> {
|
|||
loop {
|
||||
let mut write_would_block = false;
|
||||
let mut read_would_block = false;
|
||||
let mut need_flush = false;
|
||||
|
||||
while self.session.wants_write() {
|
||||
match self.write_io(cx) {
|
||||
Poll::Ready(Ok(n)) => wrlen += n,
|
||||
Poll::Ready(Ok(n)) => {
|
||||
wrlen += n;
|
||||
need_flush = true;
|
||||
}
|
||||
Poll::Pending => {
|
||||
write_would_block = true;
|
||||
break;
|
||||
|
@ -161,6 +183,14 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> Stream<'a, IO, S> {
|
|||
}
|
||||
}
|
||||
|
||||
if need_flush {
|
||||
match Pin::new(&mut self.io).poll_flush(cx) {
|
||||
Poll::Ready(Ok(())) => (),
|
||||
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
|
||||
Poll::Pending => write_would_block = true,
|
||||
}
|
||||
}
|
||||
|
||||
while !self.eof && self.session.wants_read() {
|
||||
match self.read_io(cx) {
|
||||
Poll::Ready(Ok(0)) => self.eof = true,
|
||||
|
@ -192,53 +222,69 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> Stream<'a, IO, S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> AsyncRead for Stream<'a, IO, S> {
|
||||
impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S, SD> AsyncRead for Stream<'a, IO, S>
|
||||
where
|
||||
S: DerefMut + Deref<Target = ConnectionCommon<SD>>,
|
||||
SD: SideData,
|
||||
{
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
let mut pos = 0;
|
||||
let mut io_pending = false;
|
||||
|
||||
while pos != buf.len() {
|
||||
let mut would_block = false;
|
||||
|
||||
// read a packet
|
||||
while self.session.wants_read() {
|
||||
match self.read_io(cx) {
|
||||
Poll::Ready(Ok(0)) => {
|
||||
self.eof = true;
|
||||
break;
|
||||
}
|
||||
Poll::Ready(Ok(_)) => (),
|
||||
Poll::Pending => {
|
||||
would_block = true;
|
||||
break;
|
||||
}
|
||||
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
|
||||
// read a packet
|
||||
while !self.eof && self.session.wants_read() {
|
||||
match self.read_io(cx) {
|
||||
Poll::Ready(Ok(0)) => {
|
||||
self.eof = true;
|
||||
break;
|
||||
}
|
||||
Poll::Ready(Ok(_)) => (),
|
||||
Poll::Pending => {
|
||||
io_pending = true;
|
||||
break;
|
||||
}
|
||||
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
|
||||
}
|
||||
|
||||
return match self.session.read(&mut buf[pos..]) {
|
||||
Ok(0) if pos == 0 && would_block => Poll::Pending,
|
||||
Ok(n) if self.eof || would_block => Poll::Ready(Ok(pos + n)),
|
||||
Ok(n) => {
|
||||
pos += n;
|
||||
continue;
|
||||
}
|
||||
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Poll::Pending,
|
||||
Err(ref err) if err.kind() == io::ErrorKind::ConnectionAborted && pos != 0 => {
|
||||
Poll::Ready(Ok(pos))
|
||||
}
|
||||
Err(err) => Poll::Ready(Err(err)),
|
||||
};
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(pos))
|
||||
match self.session.reader().read(buf) {
|
||||
// If Rustls returns `Ok(0)` (while `buf` is non-empty), the peer closed the
|
||||
// connection with a `CloseNotify` message and no more data will be forthcoming.
|
||||
//
|
||||
// Rustls yielded more data: advance the buffer, then see if more data is coming.
|
||||
//
|
||||
// We don't need to modify `self.eof` here, because it is only a temporary mark.
|
||||
// rustls will only return 0 if is has received `CloseNotify`,
|
||||
// in which case no additional processing is required.
|
||||
Ok(n) => Poll::Ready(Ok(n)),
|
||||
|
||||
// Rustls doesn't have more data to yield, but it believes the connection is open.
|
||||
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
|
||||
if !io_pending {
|
||||
// If `wants_read()` is satisfied, rustls will not return `WouldBlock`.
|
||||
// but if it does, we can try again.
|
||||
//
|
||||
// If the rustls state is abnormal, it may cause a cyclic wakeup.
|
||||
// but tokio's cooperative budget will prevent infinite wakeup.
|
||||
cx.waker().wake_by_ref();
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
|
||||
Err(err) => Poll::Ready(Err(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> AsyncWrite for Stream<'a, IO, S> {
|
||||
impl<'a, IO: AsyncRead + AsyncWrite + Unpin, C, SD> AsyncWrite for Stream<'a, IO, C>
|
||||
where
|
||||
C: DerefMut + Deref<Target = ConnectionCommon<SD>>,
|
||||
SD: SideData,
|
||||
{
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context,
|
||||
|
@ -249,9 +295,8 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> AsyncWrite for Stream<'
|
|||
while pos != buf.len() {
|
||||
let mut would_block = false;
|
||||
|
||||
match self.session.write(&buf[pos..]) {
|
||||
match self.session.writer().write(&buf[pos..]) {
|
||||
Ok(n) => pos += n,
|
||||
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => (),
|
||||
Err(err) => return Poll::Ready(Err(err)),
|
||||
};
|
||||
|
||||
|
@ -277,20 +322,40 @@ impl<'a, IO: AsyncRead + AsyncWrite + Unpin, S: Session> AsyncWrite for Stream<'
|
|||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
|
||||
self.session.flush()?;
|
||||
self.session.writer().flush()?;
|
||||
while self.session.wants_write() {
|
||||
futures_lite::ready!(self.write_io(cx))?;
|
||||
ready!(self.write_io(cx))?;
|
||||
}
|
||||
Pin::new(&mut self.io).poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
while self.session.wants_write() {
|
||||
futures_lite::ready!(self.write_io(cx))?;
|
||||
ready!(self.write_io(cx))?;
|
||||
}
|
||||
Pin::new(&mut self.io).poll_close(cx)
|
||||
}
|
||||
}
|
||||
|
||||
/// An adapter that implements a [`Read`] interface for [`AsyncRead`] types and an
|
||||
/// associated [`Context`].
|
||||
///
|
||||
/// Turns `Poll::Pending` into `WouldBlock`.
|
||||
pub struct SyncReadAdapter<'a, 'b, T> {
|
||||
pub io: &'a mut T,
|
||||
pub cx: &'a mut Context<'b>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, T: AsyncRead + Unpin> Read for SyncReadAdapter<'a, 'b, T> {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match Pin::new(&mut self.io).poll_read(self.cx, buf) {
|
||||
Poll::Ready(Ok(n)) => Ok(n),
|
||||
Poll::Ready(Err(err)) => Err(err),
|
||||
Poll::Pending => Err(io::ErrorKind::WouldBlock.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_stream;
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
use super::Stream;
|
||||
use futures_lite::future::poll_fn;
|
||||
use futures_lite::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
use futures_lite::ready;
|
||||
use rustls::internal::pemfile::{certs, rsa_private_keys};
|
||||
use rustls::{ClientConfig, ClientSession, NoClientAuth, ServerConfig, ServerSession, Session};
|
||||
use std::io::{self, BufReader, Cursor, Read, Write};
|
||||
use futures_util::future::poll_fn;
|
||||
use futures_util::task::noop_waker_ref;
|
||||
use rustls::{ClientConnection, Connection, ServerConnection};
|
||||
use smol::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
use std::io::{self, Cursor, Read, Write};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use webpki::DNSNameRef;
|
||||
|
||||
struct Good<'a>(&'a mut dyn Session);
|
||||
struct Good<'a>(&'a mut Connection);
|
||||
|
||||
impl<'a> AsyncRead for Good<'a> {
|
||||
fn poll_read(
|
||||
|
@ -42,9 +39,9 @@ impl<'a> AsyncWrite for Good<'a> {
|
|||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_close(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.0.send_close_notify();
|
||||
Poll::Ready(Ok(()))
|
||||
self.poll_flush(cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,19 +75,21 @@ impl AsyncWrite for Pending {
|
|||
}
|
||||
}
|
||||
|
||||
struct Eof;
|
||||
struct Expected(Cursor<Vec<u8>>);
|
||||
|
||||
impl AsyncRead for Eof {
|
||||
impl AsyncRead for Expected {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
_: &mut [u8],
|
||||
buf: &mut [u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
Poll::Ready(Ok(0))
|
||||
let this = self.get_mut();
|
||||
|
||||
Poll::Ready(std::io::Read::read(&mut this.0, buf))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for Eof {
|
||||
impl AsyncWrite for Expected {
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
|
@ -111,35 +110,72 @@ impl AsyncWrite for Eof {
|
|||
#[test]
|
||||
fn stream_good() -> io::Result<()> {
|
||||
smol::block_on(async {
|
||||
const FILE: &[u8] = include_bytes!("../../Cargo.toml");
|
||||
const FILE: &[u8] = include_bytes!("../../README.md");
|
||||
|
||||
let (mut server, mut client) = make_pair();
|
||||
let (server, mut client) = make_pair();
|
||||
let mut server = Connection::from(server);
|
||||
poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?;
|
||||
io::copy(&mut Cursor::new(FILE), &mut server)?;
|
||||
|
||||
io::copy(&mut Cursor::new(FILE), &mut server.writer())?;
|
||||
server.send_close_notify();
|
||||
|
||||
{
|
||||
let mut good = Good(&mut server);
|
||||
let mut stream = Stream::new(&mut good, &mut client);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
stream.read_to_end(&mut buf).await?;
|
||||
dbg!(stream.read_to_end(&mut buf).await)?;
|
||||
assert_eq!(buf, FILE);
|
||||
stream.write_all(b"Hello World!").await?;
|
||||
stream.flush().await?;
|
||||
|
||||
dbg!(stream.write_all(b"Hello World!").await)?;
|
||||
stream.session.send_close_notify();
|
||||
|
||||
dbg!(stream.close().await)?;
|
||||
}
|
||||
|
||||
let mut buf = String::new();
|
||||
server.read_to_string(&mut buf)?;
|
||||
dbg!(server.process_new_packets()).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
dbg!(server.reader().read_to_string(&mut buf))?;
|
||||
assert_eq!(buf, "Hello World!");
|
||||
|
||||
Ok(()) as io::Result<()>
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_bad() -> io::Result<()> {
|
||||
smol::block_on(async {
|
||||
let (server, mut client) = make_pair();
|
||||
let mut server = Connection::from(server);
|
||||
poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?;
|
||||
client.set_buffer_limit(Some(1024));
|
||||
|
||||
let mut bad = Pending;
|
||||
let mut stream = Stream::new(&mut bad, &mut client);
|
||||
assert_eq!(
|
||||
poll_fn(|cx| stream.as_mut_pin().poll_write(cx, &[0x42; 8])).await?,
|
||||
8
|
||||
);
|
||||
assert_eq!(
|
||||
poll_fn(|cx| stream.as_mut_pin().poll_write(cx, &[0x42; 8])).await?,
|
||||
8
|
||||
);
|
||||
let r = poll_fn(|cx| stream.as_mut_pin().poll_write(cx, &[0x00; 1024])).await?; // fill buffer
|
||||
assert!(r < 1024);
|
||||
|
||||
let mut cx = Context::from_waker(noop_waker_ref());
|
||||
let ret = stream.as_mut_pin().poll_write(&mut cx, &[0x01]);
|
||||
assert!(ret.is_pending());
|
||||
|
||||
Ok(()) as io::Result<()>
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_handshake() -> io::Result<()> {
|
||||
smol::block_on(async {
|
||||
let (mut server, mut client) = make_pair();
|
||||
let (server, mut client) = make_pair();
|
||||
let mut server = Connection::from(server);
|
||||
|
||||
{
|
||||
let mut good = Good(&mut server);
|
||||
|
@ -160,45 +196,80 @@ fn stream_handshake() -> io::Result<()> {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn stream_eof() -> io::Result<()> {
|
||||
fn stream_handshake_eof() -> io::Result<()> {
|
||||
smol::block_on(async {
|
||||
let (mut server, mut client) = make_pair();
|
||||
poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?;
|
||||
let (_, mut client) = make_pair();
|
||||
|
||||
let mut good = Good(&mut server);
|
||||
let mut stream = Stream::new(&mut good, &mut client).set_eof(true);
|
||||
let mut bad = Expected(Cursor::new(Vec::new()));
|
||||
let mut stream = Stream::new(&mut bad, &mut client);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
stream.read_to_end(&mut buf).await?;
|
||||
assert_eq!(buf.len(), 0);
|
||||
let mut cx = Context::from_waker(noop_waker_ref());
|
||||
let r = stream.handshake(&mut cx);
|
||||
assert_eq!(
|
||||
r.map_err(|err| err.kind()),
|
||||
Poll::Ready(Err(io::ErrorKind::UnexpectedEof))
|
||||
);
|
||||
|
||||
Ok(()) as io::Result<()>
|
||||
})
|
||||
}
|
||||
|
||||
fn make_pair() -> (ServerSession, ClientSession) {
|
||||
const CERT: &str = include_str!("../../tests/end.cert");
|
||||
const CHAIN: &str = include_str!("../../tests/end.chain");
|
||||
const RSA: &str = include_str!("../../tests/end.rsa");
|
||||
// see https://github.com/tokio-rs/tls/issues/77
|
||||
#[test]
|
||||
fn stream_handshake_regression_issues_77() -> io::Result<()> {
|
||||
smol::block_on(async {
|
||||
let (_, mut client) = make_pair();
|
||||
|
||||
let cert = certs(&mut BufReader::new(Cursor::new(CERT))).unwrap();
|
||||
let mut keys = rsa_private_keys(&mut BufReader::new(Cursor::new(RSA))).unwrap();
|
||||
let mut sconfig = ServerConfig::new(NoClientAuth::new());
|
||||
sconfig.set_single_cert(cert, keys.pop().unwrap()).unwrap();
|
||||
let server = ServerSession::new(&Arc::new(sconfig));
|
||||
let mut bad = Expected(Cursor::new(b"\x15\x03\x01\x00\x02\x02\x00".to_vec()));
|
||||
let mut stream = Stream::new(&mut bad, &mut client);
|
||||
|
||||
let domain = DNSNameRef::try_from_ascii_str("localhost").unwrap();
|
||||
let mut cconfig = ClientConfig::new();
|
||||
let mut chain = BufReader::new(Cursor::new(CHAIN));
|
||||
cconfig.root_store.add_pem_file(&mut chain).unwrap();
|
||||
let client = ClientSession::new(&Arc::new(cconfig), domain);
|
||||
let mut cx = Context::from_waker(noop_waker_ref());
|
||||
let r = stream.handshake(&mut cx);
|
||||
assert_eq!(
|
||||
r.map_err(|err| err.kind()),
|
||||
Poll::Ready(Err(io::ErrorKind::UnexpectedEof))
|
||||
);
|
||||
|
||||
Ok(()) as io::Result<()>
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stream_eof() -> io::Result<()> {
|
||||
smol::block_on(async {
|
||||
let (server, mut client) = make_pair();
|
||||
let mut server = Connection::from(server);
|
||||
poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?;
|
||||
|
||||
let mut bad = Expected(Cursor::new(Vec::new()));
|
||||
let mut stream = Stream::new(&mut bad, &mut client);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
let result = stream.read_to_end(&mut buf).await;
|
||||
assert_eq!(
|
||||
result.err().map(|e| e.kind()),
|
||||
Some(io::ErrorKind::UnexpectedEof)
|
||||
);
|
||||
|
||||
Ok(()) as io::Result<()>
|
||||
})
|
||||
}
|
||||
|
||||
fn make_pair() -> (ServerConnection, ClientConnection) {
|
||||
use std::convert::TryFrom;
|
||||
|
||||
let (sconfig, cconfig) = utils::make_configs();
|
||||
let server = ServerConnection::new(sconfig).unwrap();
|
||||
|
||||
let domain = rustls::ServerName::try_from("foobar.com").unwrap();
|
||||
let client = ClientConnection::new(cconfig, domain).unwrap();
|
||||
|
||||
(server, client)
|
||||
}
|
||||
|
||||
fn do_handshake(
|
||||
client: &mut ClientSession,
|
||||
server: &mut ServerSession,
|
||||
client: &mut ClientConnection,
|
||||
server: &mut Connection,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<io::Result<()>> {
|
||||
let mut good = Good(server);
|
||||
|
@ -214,3 +285,6 @@ fn do_handshake(
|
|||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
// Share `utils` module with integration tests
|
||||
include!("../../tests/utils.rs");
|
||||
|
|
243
src/lib.rs
243
src/lib.rs
|
@ -1,20 +1,30 @@
|
|||
//! Asynchronous TLS/SSL streams using [`rustls`].
|
||||
//!
|
||||
//! [`rustls`]: https://docs.rs/rustls
|
||||
//! Asynchronous TLS/SSL streams using [Rustls](https://github.com/ctz/rustls).
|
||||
|
||||
macro_rules! ready {
|
||||
( $e:expr ) => {
|
||||
match $e {
|
||||
std::task::Poll::Ready(t) => t,
|
||||
std::task::Poll::Pending => return std::task::Poll::Pending,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod client;
|
||||
mod common;
|
||||
pub mod server;
|
||||
|
||||
use common::{MidHandshake, Stream, TlsState};
|
||||
use futures_lite::io::{AsyncRead, AsyncWrite};
|
||||
use rustls::{ClientConfig, ClientSession, ServerConfig, ServerSession, Session};
|
||||
use futures_io::{AsyncRead, AsyncWrite};
|
||||
use rustls::{ClientConfig, ClientConnection, CommonState, ServerConfig, ServerConnection};
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::io::{AsRawSocket, RawSocket};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use webpki::DNSNameRef;
|
||||
|
||||
pub use rustls;
|
||||
pub use webpki;
|
||||
|
@ -23,6 +33,7 @@ pub use webpki;
|
|||
#[derive(Clone)]
|
||||
pub struct TlsConnector {
|
||||
inner: Arc<ClientConfig>,
|
||||
#[cfg(feature = "early-data")]
|
||||
early_data: bool,
|
||||
}
|
||||
|
||||
|
@ -36,6 +47,7 @@ impl From<Arc<ClientConfig>> for TlsConnector {
|
|||
fn from(inner: Arc<ClientConfig>) -> TlsConnector {
|
||||
TlsConnector {
|
||||
inner,
|
||||
#[cfg(feature = "early-data")]
|
||||
early_data: false,
|
||||
}
|
||||
}
|
||||
|
@ -52,34 +64,54 @@ impl TlsConnector {
|
|||
///
|
||||
/// If you want to use 0-RTT,
|
||||
/// You must also set `ClientConfig.enable_early_data` to `true`.
|
||||
#[cfg(feature = "early-data")]
|
||||
pub fn early_data(mut self, flag: bool) -> TlsConnector {
|
||||
self.early_data = flag;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn connect<IO>(&self, domain: DNSNameRef, stream: IO) -> Connect<IO>
|
||||
pub fn connect<IO>(&self, domain: rustls::ServerName, stream: IO) -> Connect<IO>
|
||||
where
|
||||
IO: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
self.connect_with(domain, stream, |_| ())
|
||||
}
|
||||
|
||||
pub fn connect_with<IO, F>(&self, domain: DNSNameRef, stream: IO, f: F) -> Connect<IO>
|
||||
pub fn connect_with<IO, F>(&self, domain: rustls::ServerName, stream: IO, f: F) -> Connect<IO>
|
||||
where
|
||||
IO: AsyncRead + AsyncWrite + Unpin,
|
||||
F: FnOnce(&mut ClientSession),
|
||||
F: FnOnce(&mut ClientConnection),
|
||||
{
|
||||
let mut session = ClientSession::new(&self.inner, domain);
|
||||
let mut session = match ClientConnection::new(self.inner.clone(), domain) {
|
||||
Ok(session) => session,
|
||||
Err(error) => {
|
||||
return Connect(MidHandshake::Error {
|
||||
io: stream,
|
||||
// TODO(eliza): should this really return an `io::Error`?
|
||||
// Probably not...
|
||||
error: io::Error::new(io::ErrorKind::Other, error),
|
||||
});
|
||||
}
|
||||
};
|
||||
f(&mut session);
|
||||
|
||||
Connect(MidHandshake::Handshaking(client::TlsStream {
|
||||
io: stream,
|
||||
|
||||
#[cfg(not(feature = "early-data"))]
|
||||
state: TlsState::Stream,
|
||||
|
||||
#[cfg(feature = "early-data")]
|
||||
state: if self.early_data && session.early_data().is_some() {
|
||||
TlsState::EarlyData(0, Vec::new())
|
||||
} else {
|
||||
TlsState::Stream
|
||||
},
|
||||
|
||||
#[cfg(feature = "early-data")]
|
||||
early_waker: None,
|
||||
|
||||
session,
|
||||
}))
|
||||
}
|
||||
|
@ -97,9 +129,19 @@ impl TlsAcceptor {
|
|||
pub fn accept_with<IO, F>(&self, stream: IO, f: F) -> Accept<IO>
|
||||
where
|
||||
IO: AsyncRead + AsyncWrite + Unpin,
|
||||
F: FnOnce(&mut ServerSession),
|
||||
F: FnOnce(&mut ServerConnection),
|
||||
{
|
||||
let mut session = ServerSession::new(&self.inner);
|
||||
let mut session = match ServerConnection::new(self.inner.clone()) {
|
||||
Ok(session) => session,
|
||||
Err(error) => {
|
||||
return Accept(MidHandshake::Error {
|
||||
io: stream,
|
||||
// TODO(eliza): should this really return an `io::Error`?
|
||||
// Probably not...
|
||||
error: io::Error::new(io::ErrorKind::Other, error),
|
||||
});
|
||||
}
|
||||
};
|
||||
f(&mut session);
|
||||
|
||||
Accept(MidHandshake::Handshaking(server::TlsStream {
|
||||
|
@ -110,6 +152,104 @@ impl TlsAcceptor {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct LazyConfigAcceptor<IO> {
|
||||
acceptor: rustls::server::Acceptor,
|
||||
io: Option<IO>,
|
||||
}
|
||||
|
||||
impl<IO> LazyConfigAcceptor<IO>
|
||||
where
|
||||
IO: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
#[inline]
|
||||
pub fn new(acceptor: rustls::server::Acceptor, io: IO) -> Self {
|
||||
Self {
|
||||
acceptor,
|
||||
io: Some(io),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<IO> Future for LazyConfigAcceptor<IO>
|
||||
where
|
||||
IO: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
type Output = Result<StartHandshake<IO>, io::Error>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.get_mut();
|
||||
loop {
|
||||
let io = match this.io.as_mut() {
|
||||
Some(io) => io,
|
||||
None => {
|
||||
panic!("Acceptor cannot be polled after acceptance.");
|
||||
}
|
||||
};
|
||||
|
||||
let mut reader = common::SyncReadAdapter { io, cx };
|
||||
match this.acceptor.read_tls(&mut reader) {
|
||||
Ok(0) => return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into())),
|
||||
Ok(_) => {}
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => return Poll::Pending,
|
||||
Err(e) => return Poll::Ready(Err(e)),
|
||||
}
|
||||
|
||||
match this.acceptor.accept() {
|
||||
Ok(Some(accepted)) => {
|
||||
let io = this.io.take().unwrap();
|
||||
return Poll::Ready(Ok(StartHandshake { accepted, io }));
|
||||
}
|
||||
Ok(None) => continue,
|
||||
Err(err) => {
|
||||
return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, err)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StartHandshake<IO> {
|
||||
accepted: rustls::server::Accepted,
|
||||
io: IO,
|
||||
}
|
||||
|
||||
impl<IO> StartHandshake<IO>
|
||||
where
|
||||
IO: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
pub fn client_hello(&self) -> rustls::server::ClientHello<'_> {
|
||||
self.accepted.client_hello()
|
||||
}
|
||||
|
||||
pub fn into_stream(self, config: Arc<ServerConfig>) -> Accept<IO> {
|
||||
self.into_stream_with(config, |_| ())
|
||||
}
|
||||
|
||||
pub fn into_stream_with<F>(self, config: Arc<ServerConfig>, f: F) -> Accept<IO>
|
||||
where
|
||||
F: FnOnce(&mut ServerConnection),
|
||||
{
|
||||
let mut conn = match self.accepted.into_connection(config) {
|
||||
Ok(conn) => conn,
|
||||
Err(error) => {
|
||||
return Accept(MidHandshake::Error {
|
||||
io: self.io,
|
||||
// TODO(eliza): should this really return an `io::Error`?
|
||||
// Probably not...
|
||||
error: io::Error::new(io::ErrorKind::Other, error),
|
||||
});
|
||||
}
|
||||
};
|
||||
f(&mut conn);
|
||||
|
||||
Accept(MidHandshake::Handshaking(server::TlsStream {
|
||||
session: conn,
|
||||
io: self.io,
|
||||
state: TlsState::Stream,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Future returned from `TlsConnector::connect` which will resolve
|
||||
/// once the connection handshake has finished.
|
||||
pub struct Connect<IO>(MidHandshake<client::TlsStream<IO>>);
|
||||
|
@ -119,22 +259,54 @@ pub struct Connect<IO>(MidHandshake<client::TlsStream<IO>>);
|
|||
pub struct Accept<IO>(MidHandshake<server::TlsStream<IO>>);
|
||||
|
||||
/// Like [Connect], but returns `IO` on failure.
|
||||
pub struct FailableConnect<IO>(MidHandshake<client::TlsStream<IO>>);
|
||||
pub struct FallibleConnect<IO>(MidHandshake<client::TlsStream<IO>>);
|
||||
|
||||
/// Like [Accept], but returns `IO` on failure.
|
||||
pub struct FailableAccept<IO>(MidHandshake<server::TlsStream<IO>>);
|
||||
pub struct FallibleAccept<IO>(MidHandshake<server::TlsStream<IO>>);
|
||||
|
||||
impl<IO> Connect<IO> {
|
||||
#[inline]
|
||||
pub fn into_failable(self) -> FailableConnect<IO> {
|
||||
FailableConnect(self.0)
|
||||
pub fn into_fallible(self) -> FallibleConnect<IO> {
|
||||
FallibleConnect(self.0)
|
||||
}
|
||||
|
||||
pub fn get_ref(&self) -> Option<&IO> {
|
||||
match &self.0 {
|
||||
MidHandshake::Handshaking(sess) => Some(sess.get_ref().0),
|
||||
MidHandshake::Error { io, .. } => Some(io),
|
||||
MidHandshake::End => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> Option<&mut IO> {
|
||||
match &mut self.0 {
|
||||
MidHandshake::Handshaking(sess) => Some(sess.get_mut().0),
|
||||
MidHandshake::Error { io, .. } => Some(io),
|
||||
MidHandshake::End => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<IO> Accept<IO> {
|
||||
#[inline]
|
||||
pub fn into_failable(self) -> FailableAccept<IO> {
|
||||
FailableAccept(self.0)
|
||||
pub fn into_fallible(self) -> FallibleAccept<IO> {
|
||||
FallibleAccept(self.0)
|
||||
}
|
||||
|
||||
pub fn get_ref(&self) -> Option<&IO> {
|
||||
match &self.0 {
|
||||
MidHandshake::Handshaking(sess) => Some(sess.get_ref().0),
|
||||
MidHandshake::Error { io, .. } => Some(io),
|
||||
MidHandshake::End => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> Option<&mut IO> {
|
||||
match &mut self.0 {
|
||||
MidHandshake::Handshaking(sess) => Some(sess.get_mut().0),
|
||||
MidHandshake::Error { io, .. } => Some(io),
|
||||
MidHandshake::End => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,7 +328,7 @@ impl<IO: AsyncRead + AsyncWrite + Unpin> Future for Accept<IO> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<IO: AsyncRead + AsyncWrite + Unpin> Future for FailableConnect<IO> {
|
||||
impl<IO: AsyncRead + AsyncWrite + Unpin> Future for FallibleConnect<IO> {
|
||||
type Output = Result<client::TlsStream<IO>, (io::Error, IO)>;
|
||||
|
||||
#[inline]
|
||||
|
@ -165,7 +337,7 @@ impl<IO: AsyncRead + AsyncWrite + Unpin> Future for FailableConnect<IO> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<IO: AsyncRead + AsyncWrite + Unpin> Future for FailableAccept<IO> {
|
||||
impl<IO: AsyncRead + AsyncWrite + Unpin> Future for FallibleAccept<IO> {
|
||||
type Output = Result<server::TlsStream<IO>, (io::Error, IO)>;
|
||||
|
||||
#[inline]
|
||||
|
@ -178,27 +350,28 @@ impl<IO: AsyncRead + AsyncWrite + Unpin> Future for FailableAccept<IO> {
|
|||
///
|
||||
/// This abstracts over the inner `client::TlsStream` and `server::TlsStream`, so you can use
|
||||
/// a single type to keep both client- and server-initiated TLS-encrypted connections.
|
||||
#[derive(Debug)]
|
||||
pub enum TlsStream<T> {
|
||||
Client(client::TlsStream<T>),
|
||||
Server(server::TlsStream<T>),
|
||||
}
|
||||
|
||||
impl<T> TlsStream<T> {
|
||||
pub fn get_ref(&self) -> (&T, &dyn Session) {
|
||||
pub fn get_ref(&self) -> (&T, &CommonState) {
|
||||
use TlsStream::*;
|
||||
match self {
|
||||
Client(io) => {
|
||||
let (io, session) = io.get_ref();
|
||||
(io, &*session)
|
||||
(io, session)
|
||||
}
|
||||
Server(io) => {
|
||||
let (io, session) = io.get_ref();
|
||||
(io, &*session)
|
||||
(io, session)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> (&mut T, &mut dyn Session) {
|
||||
pub fn get_mut(&mut self) -> (&mut T, &mut CommonState) {
|
||||
use TlsStream::*;
|
||||
match self {
|
||||
Client(io) => {
|
||||
|
@ -225,6 +398,28 @@ impl<T> From<server::TlsStream<T>> for TlsStream<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl<S> AsRawFd for TlsStream<S>
|
||||
where
|
||||
S: AsRawFd,
|
||||
{
|
||||
#[inline]
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.get_ref().0.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl<S> AsRawSocket for TlsStream<S>
|
||||
where
|
||||
S: AsRawSocket,
|
||||
{
|
||||
#[inline]
|
||||
fn as_raw_socket(&self) -> RawSocket {
|
||||
self.get_ref().0.as_raw_socket()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsyncRead for TlsStream<T>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin,
|
||||
|
|
|
@ -1,36 +1,40 @@
|
|||
#[cfg(unix)]
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::io::{AsRawSocket, RawSocket};
|
||||
|
||||
use super::*;
|
||||
use crate::common::IoSession;
|
||||
use rustls::Session;
|
||||
|
||||
/// A wrapper around an underlying raw stream which implements the TLS or SSL
|
||||
/// protocol.
|
||||
#[derive(Debug)]
|
||||
pub struct TlsStream<IO> {
|
||||
pub(crate) io: IO,
|
||||
pub(crate) session: ServerSession,
|
||||
pub(crate) session: ServerConnection,
|
||||
pub(crate) state: TlsState,
|
||||
}
|
||||
|
||||
impl<IO> TlsStream<IO> {
|
||||
#[inline]
|
||||
pub fn get_ref(&self) -> (&IO, &ServerSession) {
|
||||
pub fn get_ref(&self) -> (&IO, &ServerConnection) {
|
||||
(&self.io, &self.session)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self) -> (&mut IO, &mut ServerSession) {
|
||||
pub fn get_mut(&mut self) -> (&mut IO, &mut ServerConnection) {
|
||||
(&mut self.io, &mut self.session)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn into_inner(self) -> (IO, ServerSession) {
|
||||
pub fn into_inner(self) -> (IO, ServerConnection) {
|
||||
(self.io, self.session)
|
||||
}
|
||||
}
|
||||
|
||||
impl<IO> IoSession for TlsStream<IO> {
|
||||
type Io = IO;
|
||||
type Session = ServerSession;
|
||||
type Session = ServerConnection;
|
||||
|
||||
#[inline]
|
||||
fn skip_handshake(&self) -> bool {
|
||||
|
@ -64,20 +68,22 @@ where
|
|||
match &this.state {
|
||||
TlsState::Stream | TlsState::WriteShutdown => {
|
||||
match stream.as_mut_pin().poll_read(cx, buf) {
|
||||
Poll::Ready(Ok(0)) => {
|
||||
this.state.shutdown_read();
|
||||
Poll::Ready(Ok(0))
|
||||
Poll::Ready(Ok(n)) => {
|
||||
if n == 0 || stream.eof {
|
||||
this.state.shutdown_read();
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(n))
|
||||
}
|
||||
Poll::Ready(Ok(n)) => Poll::Ready(Ok(n)),
|
||||
Poll::Ready(Err(ref err)) if err.kind() == io::ErrorKind::ConnectionAborted => {
|
||||
Poll::Ready(Err(err)) if err.kind() == io::ErrorKind::UnexpectedEof => {
|
||||
this.state.shutdown_read();
|
||||
Poll::Ready(Ok(0))
|
||||
Poll::Ready(Err(err))
|
||||
}
|
||||
Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
|
||||
Poll::Pending => Poll::Pending,
|
||||
output => output,
|
||||
}
|
||||
}
|
||||
TlsState::ReadShutdown | TlsState::FullyShutdown => Poll::Ready(Ok(0)),
|
||||
#[cfg(feature = "early-data")]
|
||||
s => unreachable!("server TLS can not hit this state: {:?}", s),
|
||||
}
|
||||
}
|
||||
|
@ -119,3 +125,25 @@ where
|
|||
stream.as_mut_pin().poll_close(cx)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl<IO> AsRawFd for TlsStream<IO>
|
||||
where
|
||||
IO: AsRawFd,
|
||||
{
|
||||
#[inline]
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.get_ref().0.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl<IO> AsRawSocket for TlsStream<IO>
|
||||
where
|
||||
IO: AsRawSocket,
|
||||
{
|
||||
#[inline]
|
||||
fn as_raw_socket(&self) -> RawSocket {
|
||||
self.get_ref().0.as_raw_socket()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
use async_rustls::{client::TlsStream, TlsConnector};
|
||||
use rustls::ClientConfig;
|
||||
use async_rustls::{
|
||||
client::TlsStream,
|
||||
rustls::{self, ClientConfig, OwnedTrustAnchor},
|
||||
TlsConnector,
|
||||
};
|
||||
use smol::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use smol::net::TcpStream;
|
||||
use smol::prelude::*;
|
||||
use std::convert::TryFrom;
|
||||
use std::io;
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::sync::Arc;
|
||||
|
@ -15,7 +19,7 @@ async fn get(
|
|||
let input = format!("GET / HTTP/1.0\r\nHost: {}\r\n\r\n", domain);
|
||||
|
||||
let addr = (domain, port).to_socket_addrs()?.next().unwrap();
|
||||
let domain = webpki::DNSNameRef::try_from_ascii_str(domain).unwrap();
|
||||
let domain = rustls::ServerName::try_from(domain).unwrap();
|
||||
let mut buf = Vec::new();
|
||||
|
||||
let stream = TcpStream::connect(&addr).await?;
|
||||
|
@ -27,19 +31,35 @@ async fn get(
|
|||
Ok((stream, String::from_utf8(buf).unwrap()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "tls12")]
|
||||
#[test]
|
||||
fn test_tls12() -> io::Result<()> {
|
||||
smol::block_on(async {
|
||||
let mut config = ClientConfig::new();
|
||||
config
|
||||
.root_store
|
||||
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
|
||||
config.versions = vec![rustls::ProtocolVersion::TLSv1_2];
|
||||
let mut root_store = rustls::RootCertStore::empty();
|
||||
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,
|
||||
)
|
||||
}));
|
||||
let config = rustls::ClientConfig::builder()
|
||||
.with_safe_default_cipher_suites()
|
||||
.with_safe_default_kx_groups()
|
||||
.with_protocol_versions(&[&rustls::version::TLS12])
|
||||
.unwrap()
|
||||
.with_root_certificates(root_store)
|
||||
.with_no_client_auth();
|
||||
|
||||
let config = Arc::new(config);
|
||||
let domain = "tls-v1-2.badssl.com";
|
||||
|
||||
let (_, output) = get(config.clone(), domain, 1012).await?;
|
||||
assert!(output.contains("<title>tls-v1-2.badssl.com</title>"));
|
||||
assert!(
|
||||
output.contains("<title>tls-v1-2.badssl.com</title>"),
|
||||
"failed badssl test, output: {}",
|
||||
output
|
||||
);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
@ -55,15 +75,27 @@ fn test_tls13() {
|
|||
#[test]
|
||||
fn test_modern() -> io::Result<()> {
|
||||
smol::block_on(async {
|
||||
let mut config = ClientConfig::new();
|
||||
config
|
||||
.root_store
|
||||
.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
|
||||
let mut root_store = rustls::RootCertStore::empty();
|
||||
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,
|
||||
)
|
||||
}));
|
||||
let config = rustls::ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_root_certificates(root_store)
|
||||
.with_no_client_auth();
|
||||
let config = Arc::new(config);
|
||||
let domain = "mozilla-modern.badssl.com";
|
||||
|
||||
let (_, output) = get(config.clone(), domain, 443).await?;
|
||||
assert!(output.contains("<title>mozilla-modern.badssl.com</title>"));
|
||||
assert!(
|
||||
output.contains("<title>mozilla-modern.badssl.com</title>"),
|
||||
"failed badssl test, output: {}",
|
||||
output
|
||||
);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
#![cfg(feature = "early-data")]
|
||||
|
||||
use async_rustls::{client::TlsStream, TlsConnector};
|
||||
use futures_lite::{future, future::Future, ready};
|
||||
use rustls::Certificate;
|
||||
use rustls::ClientConfig;
|
||||
use rustls::RootCertStore;
|
||||
use rustls::ServerName;
|
||||
use smol::net::TcpStream;
|
||||
use smol::prelude::*;
|
||||
use smol::Timer;
|
||||
use smol::{future, future::Future};
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{self, BufRead, BufReader, Cursor};
|
||||
use std::net::SocketAddr;
|
||||
use std::pin::Pin;
|
||||
|
@ -19,7 +25,7 @@ impl<T: AsyncRead + Unpin> Future for Read1<T> {
|
|||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut buf = [0];
|
||||
ready!(Pin::new(&mut self.0).poll_read(cx, &mut buf))?;
|
||||
smol::ready!(Pin::new(&mut self.0).poll_read(cx, &mut buf))?;
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +42,7 @@ async fn send(
|
|||
) -> io::Result<TlsStream<TcpStream>> {
|
||||
let connector = TlsConnector::from(config).early_data(true);
|
||||
let stream = TcpStream::connect(&addr).await?;
|
||||
let domain = webpki::DNSNameRef::try_from_ascii_str("testserver.com").unwrap();
|
||||
let domain = ServerName::try_from("foobar.com").unwrap();
|
||||
|
||||
let mut stream = connector.connect(domain, stream).await?;
|
||||
stream.write_all(data).await?;
|
||||
|
@ -91,10 +97,20 @@ fn test_0rtt() -> io::Result<()> {
|
|||
// wait openssl server
|
||||
Timer::after(Duration::from_secs(1)).await;
|
||||
|
||||
let mut config = ClientConfig::new();
|
||||
let mut root_store = RootCertStore::empty();
|
||||
let mut chain = BufReader::new(Cursor::new(include_str!("end.chain")));
|
||||
config.root_store.add_pem_file(&mut chain).unwrap();
|
||||
config.versions = vec![rustls::ProtocolVersion::TLSv1_3];
|
||||
for cert in rustls_pemfile::certs(&mut chain).unwrap() {
|
||||
root_store.add(&Certificate(cert)).unwrap();
|
||||
}
|
||||
|
||||
let mut config = ClientConfig::builder()
|
||||
.with_safe_default_cipher_suites()
|
||||
.with_safe_default_kx_groups()
|
||||
.with_protocol_versions(&[&rustls::version::TLS13])
|
||||
.unwrap()
|
||||
.with_root_certificates(root_store)
|
||||
.with_no_client_auth();
|
||||
|
||||
config.enable_early_data = true;
|
||||
let config = Arc::new(config);
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 12354));
|
||||
|
|
|
@ -1,24 +1,31 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEADCCAmigAwIBAgICAcgwDQYJKoZIhvcNAQELBQAwLDEqMCgGA1UEAwwhcG9u
|
||||
eXRvd24gUlNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMB4XDTE2MTIxMDE3NDIzM1oX
|
||||
DTIyMDYwMjE3NDIzM1owGTEXMBUGA1UEAwwOdGVzdHNlcnZlci5jb20wggEiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1YDz66+7VD4DL1+/sVHMQ+BbDRgmD
|
||||
OQlX++mfW8D3QNQm/qDBEbu7T7qqdc9GKDar4WIzBN8SBkzM1EjMGwNnZPV/Tfz0
|
||||
qUAR1L/7Zzf1GaFZvWXgksyUpfwvmprH3Iy/dpkETwtPthpTPNlui3hZnm/5kkjR
|
||||
RWg9HmID4O04Ld6SK313v2ZgrPZbkKvbqlqhUnYWjL3blKVGbpXIsuZzEU9Ph+gH
|
||||
tPcEhZpFsM6eLe+2TVscIrycMEOTXqAAmO6zZ9sQWtfllu3CElm904H6+jA/9Leg
|
||||
al72pMmkYr8wWniqDDuijXuCPlVx5EDFFyxBmW18UeDEQaKV3kNfelaTAgMBAAGj
|
||||
gb4wgbswDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBsAwHQYDVR0OBBYEFIYhJkVy
|
||||
AAKT6cY/ruH1Eu+NNxteMEIGA1UdIwQ7MDmAFNwuPy4Do//Sm5CZDrocHWTrNr96
|
||||
oR6kHDAaMRgwFgYDVQQDDA9wb255dG93biBSU0EgQ0GCAXswOwYDVR0RBDQwMoIO
|
||||
dGVzdHNlcnZlci5jb22CFXNlY29uZC50ZXN0c2VydmVyLmNvbYIJbG9jYWxob3N0
|
||||
MA0GCSqGSIb3DQEBCwUAA4IBgQCWV76jfQDZKtfmj45fTwZzoe/PxjWPRbAvSEnt
|
||||
LRHrPhqQfpMLqpun8uu/w86mHiR/AmiAySMu3zivW6wfGzlRWLi/zCyO6r9LGsgH
|
||||
bNk5CF642cdZFvn1SiSm1oGXQrolIpcyXu88nUpt74RnY4ETCC1dRQKqxsYufe5T
|
||||
DOmTm3ChinNW4QRG3yvW6DVuyxVAgZvofyKJOsM3GO6oogIM41aBqZ3UTwmIwp6D
|
||||
oISdiATslFOzYzjnyXNR8DG8OOkv1ehWuyb8x+hQCZAuogQOWYtCSd6k3kKgd0EM
|
||||
4CWbt1XDV9ZJwBf2uxZeKuCu/KIy9auNtijAwPsUv9qxuzko018zhl3lWm5p2Sqw
|
||||
O7fFshU3A6df8hMw7ST6/tgFY7geT88U4iJhfWMwr/CZSRSVMXhTyJgbLIXxKYZj
|
||||
Ym5v4NAIQP6hI4HixzQaYgrhW6YX6myk+emMjQLRJHT8uHvmT7fuxMJVWWgsCkr1
|
||||
C75pRQEagykN/Uzr5e6Tm8sVu88=
|
||||
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
|
||||
MkK20x8h4oIYiTMT349sj/73v0I=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
143
tests/end.chain
143
tests/end.chain
|
@ -1,89 +1,62 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIGnzCCAoegAwIBAgIBezANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9wb255
|
||||
dG93biBSU0EgQ0EwHhcNMTYxMjEwMTc0MjMzWhcNMjYxMjA4MTc0MjMzWjAsMSow
|
||||
KAYDVQQDDCFwb255dG93biBSU0EgbGV2ZWwgMiBpbnRlcm1lZGlhdGUwggGiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDnfb7vaJbaHEyVTflswWhmHqx5W0NO
|
||||
KyKbDp2zXEJwDO+NDJq6i1HGnFd/vO4LyjJBU1wUsKtE+m55cfRmUHVuZ2w4n/VF
|
||||
p7Z7n+SNuvJNcrzDxyKVy4GIZ39zQePnniqtLqXh6eI8Ow6jiMgVxC/wbWcVLKv6
|
||||
4RM+2fLjJAC9b27QfjhOlMKVeMOEvPrrpjLSauaHAktQPhuzIAwzxM0+KnvDkWWy
|
||||
NVqAV/lq6fSO/9vJRhM4E2nxo6yqi7qTdxVxMmKsNn7L6HvjQgx+FXziAUs55Qd9
|
||||
cP7etCmPmoefkcgdbxDOIKH8D+DvfacZwngqcnr/q96Ff4uJ13d2OzR1mWVSZ2hE
|
||||
JQt/BbZBANciqu9OZf3dj6uOOXgFF705ak0GfLtpZpc29M+fVnknXPDSiKFqjzOO
|
||||
KL+SRGyuNc9ZYjBKkXPJ1OToAs6JSvgDxfOfX0thuo2rslqfpj2qCFugsRIRAqvb
|
||||
eyFwg+BPM/P/EfauXlAcQtBF04fOi7xN2okCAwEAAaNeMFwwHQYDVR0OBBYEFNwu
|
||||
Py4Do//Sm5CZDrocHWTrNr96MCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEF
|
||||
BQcDAjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB/jANBgkqhkiG9w0BAQsFAAOC
|
||||
BAEAMHZpBqDIUAVFZNw4XbuimXQ4K8q4uePrLGHLb4F/gHbr8kYrU4H+cy4l+xXf
|
||||
2dlEBdZoqjSF7uXzQg5Fd8Ff3ZgutXd1xeUJnxo0VdpKIhqeaTPqhffC2X6FQQH5
|
||||
KrN7NVWQSnUhPNpBFELpmdpY1lHigFW7nytYj0C6VJ4QsbqhfW+n/t+Zgqtfh/Od
|
||||
ZbclzxFwMM55zRA2HP6IwXS2+d61Jk/RpDHTzhWdjGH4906zGNNMa7slHpCTA9Ju
|
||||
TrtjEAGt2PBSievBJOHZW80KVAoEX2n9B3ZABaz+uX0VVZG0D2FwhPpUeA57YiXu
|
||||
qiktZR4Ankph3LabXp4IlAX16qpYsEW8TWE/HLreeqoM0WDoI6rF9qnTpV2KWqBf
|
||||
ziMYkfSkT7hQ2bWc493lW+QwSxCsuBsDwlrCwAl6jFSf1+jEQx98/8n9rDNyD9dL
|
||||
PvECmtF30WY98nwZ9/kO2DufQrd0mwSHcIT0pAwl5fimpkwTjj+TTbytO3M4jK5L
|
||||
tuIzsViQ95BmJQ3XuLdkQ/Ug8rpECYRX5fQX1qXkkvl920ohpKqKyEji1OmfmJ0Z
|
||||
tZChaEcu3Mp3U+gD4az2ogmle3i/Phz8ZEPFo4/21G5Qd72z0lBgaQIeyyCk5MHt
|
||||
Yg0vA7X0/w4bz+OJv5tf7zJsPCYSprr+c/7YUJk9Fqu6+g9ZAavI99xFKdGhz4Og
|
||||
w0trnKNCxYc6+NPopTDbXuY+fo4DK7C0CSae5sKs7013Ne6w4KvgfLKpvlemkGfg
|
||||
ZA3+1FMXVfFIEH7Cw9cx6F02Sr3k1VrU68oM3wH5nvTUkELOf8nRMlzliQjVCpKB
|
||||
yFSe9dzRVSFEbMDxChiEulGgNUHj/6wwpg0ZmCwPRHutppT3jkfEqizN5iHb69GH
|
||||
k6kol6knJofkaL656Q3Oc9o0ZrMlFh1RwmOvAk5fVK0/CV88/phROz2Wdmy5Bz4a
|
||||
t0vzqFWA54y6+9EEVoOk9SU0CYfpGtpX4URjLK1EUG/l+RR3366Uee6TPrtEZ9cg
|
||||
56VQMxhSaRNAvJ6DfiSuscSCNJzwuXaMXSZydGYnnP9Tb9p6c1uy1sXdluZkBIcK
|
||||
CgC+gdDMSNlDn9ghc4xZGkuA8bjzfAYuRuGKmfTt8uuklkjw2b9w3SHjC4/Cmd2W
|
||||
cFRnzfg2oL6e78hNg2ZGgsLzvb6Lu6/5IhXCO7RitzYf2+HLBbc+YLFsnG3qeGe1
|
||||
28yGnXOQd97Cr4+IzFucVy/33gMQkesNUSDFJSq1gE/hGrMgTTMQJ7yC3PRqg0kG
|
||||
tpqTyKNdM0g1adxlR1qfDPvpUBApkgBbySnMyWEr5+tBuoHUtH2m49oV9YD4odMJ
|
||||
yJjlGxituO/YNN6O8oANlraG1Q==
|
||||
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
|
||||
MkK20x8h4oIYiTMT349sj/73v0I=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
DLxargy39i2aQGg=
|
||||
MIIFajCCA1KgAwIBAgIJANSWIN+/hifHMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
|
||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||
aWRnaXRzIFB0eSBMdGQwHhcNMjIwNzI4MTAyMjU0WhcNMjMwNzI4MTAyMjU0WjBF
|
||||
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
||||
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
|
||||
CgKCAgEA/BOyu73gyVuKs6IIUP1wfhNSE/yeTkCaqZ33M1Nv3WkBFoCXS4n3oLbX
|
||||
1XmUdmC1ECsgIIM9DBinEfEGU3AAAYzuEieOuCESNw5MAsB4Ft2EvJe/0n5WiQCy
|
||||
EZ9PLxY4pWRb8C95AZH25sEVlZ8PZsdpQs28geeYtKJjLMrwA0wXrhkxMizZXz0G
|
||||
27ETciLv7uHVaC66bglGVzD0E5zhPOrj5wVXSqPuqbohun/DpT51lbd3y15cy8+k
|
||||
TeaFI8vEpmYnOLm0LpdErHtdKqAgQ//yO4kSA7YmOWhy4T94ZF5uhxXL7bCJ9I3y
|
||||
9tx4mnZ7WpFyDtOEwn463f2u0SWyT6gb2i/RyC7jpbtyxSkPenzcXEOIcGMuDsPt
|
||||
Nu1Y3X98HzdBkc8q3jPXKuHDgQJuH4gcEQFsyNCS6IT1h3EEJCHHOLn/OrRuSgv5
|
||||
oIM6+kqOKeTSGYCSBTYbhaCQQxDzPtlpFu/u236Gv0F/btEio/OW0SZmsUFmdhhy
|
||||
o2E9nquETuPd9Ku9jkSI81LVDFMzKdipX/YS51JKSKnPK60EFoEb4Gbmr6Hex4rF
|
||||
sE58Jj56HMjGeJRSyI5BbthUgsWk2qBIFz0zgqQ1jTPCueQtpsFBN8XWP+gRdViJ
|
||||
5xxZyDSSWx6l4Exciuen/YF5tio/05S2F2xkeKdgjVxUsYrW9UcCAwEAAaNdMFsw
|
||||
DAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAcYwHQYDVR0OBBYEFMghuWQPHguF/7Yl
|
||||
GHVjCMgXtWM6MB8GA1UdIwQYMBaAFMghuWQPHguF/7YlGHVjCMgXtWM6MA0GCSqG
|
||||
SIb3DQEBCwUAA4ICAQA5MXm4h6ocl4W28DS/PHj/I+AZ4NGP9M0bsUSyejFMV/E6
|
||||
0jBkkmC7HUTXQXv3C6nl0XI1nnAC8v3ZdOvIlaEbhDV+I5ovHHDQc9qsHM3bg4C6
|
||||
XTftYL2jwIxu6Zc3BA12Zna4ATIIuzF33aKI+SxPYdLm+rKdjl5vg7EthYPJf8W+
|
||||
gUBKDmMtsVvPj5bzjm53mPwCZURAMige9msTtOljw1xaIHfiHQRr3AqD2a1SrOZn
|
||||
XsVSz6rV0p2DpgOLm6n0nsFQJ2xfVZ9hY7rmrAkVUYgHjJv5occmydWoQ3UVusCf
|
||||
CpsXjFXp1AM6R3YIQXDNqwpsOJ8fQdc3y9+RNJXTenDDx4QH8SLijagvyJZujeiD
|
||||
clIWwAzczbKgACA6UlR5lYm6lkxYN8z0xcG8MqyCZW1I4a/TIbdkFvuCIo5f7Xqv
|
||||
llw0c78GIrbB30zZxnJGndglCqqWcXojHJ+LZAgjwv4iivCvDg2pQ6RKK+BWuRIL
|
||||
Moa0k1tc8nLcwHxb76FIXttaLN9gYL4cQWJmtORNsuO5HSMT9dU3vfQjzZ5ElHuW
|
||||
y2e5ZR+1jr45t6QSUAHrCT5I4GAx6jjDhyF2o9PGg1uY2/JFpemMx1qkPCBBAWSy
|
||||
qClGAAD9qhq+aj8UuFt8tM1sBYFcBgjPSjC+ncSDZVEGhk8WAC8ciYRiY/wirQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
|
|
@ -1,27 +1,51 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
||||
|
|
170
tests/test.rs
170
tests/test.rs
|
@ -1,80 +1,82 @@
|
|||
use async_rustls::{TlsAcceptor, TlsConnector};
|
||||
use lazy_static::lazy_static;
|
||||
use rustls::internal::pemfile::{certs, rsa_private_keys};
|
||||
use rustls::{ClientConfig, ServerConfig};
|
||||
use smol::io::{copy, split};
|
||||
use async_rustls::{LazyConfigAcceptor, TlsAcceptor, TlsConnector};
|
||||
use futures_util::future::TryFutureExt;
|
||||
use once_cell::sync::Lazy;
|
||||
use rustls::{ClientConfig, OwnedTrustAnchor};
|
||||
use rustls_pemfile::{certs, rsa_private_keys};
|
||||
use smol::io::{copy, split, AssertAsync, AsyncReadExt, AsyncWriteExt};
|
||||
use smol::net::{TcpListener, TcpStream};
|
||||
use smol::prelude::*;
|
||||
use std::io;
|
||||
use std::io::{BufReader, Cursor};
|
||||
use std::convert::TryFrom;
|
||||
use std::io::{BufReader, Cursor, ErrorKind};
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{io, thread};
|
||||
|
||||
const CERT: &str = include_str!("end.cert");
|
||||
const CHAIN: &str = include_str!("end.chain");
|
||||
const CHAIN: &[u8] = include_bytes!("end.chain");
|
||||
const RSA: &str = include_str!("end.rsa");
|
||||
|
||||
lazy_static! {
|
||||
static ref TEST_SERVER: (SocketAddr, &'static str, &'static str) = {
|
||||
let cert = certs(&mut BufReader::new(Cursor::new(CERT))).unwrap();
|
||||
let mut keys = rsa_private_keys(&mut BufReader::new(Cursor::new(RSA))).unwrap();
|
||||
static TEST_SERVER: Lazy<(SocketAddr, &'static str, &'static [u8])> = Lazy::new(|| {
|
||||
let cert = certs(&mut BufReader::new(Cursor::new(CERT)))
|
||||
.unwrap()
|
||||
.drain(..)
|
||||
.map(rustls::Certificate)
|
||||
.collect();
|
||||
let mut keys = rsa_private_keys(&mut BufReader::new(Cursor::new(RSA))).unwrap();
|
||||
let mut keys = keys.drain(..).map(rustls::PrivateKey);
|
||||
|
||||
let mut config = ServerConfig::new(rustls::NoClientAuth::new());
|
||||
config
|
||||
.set_single_cert(cert, keys.pop().unwrap())
|
||||
.expect("invalid key or certificate");
|
||||
let acceptor = TlsAcceptor::from(Arc::new(config));
|
||||
let config = rustls::ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(cert, keys.next().unwrap())
|
||||
.unwrap();
|
||||
let acceptor = TlsAcceptor::from(Arc::new(config));
|
||||
|
||||
let (send, recv) = channel();
|
||||
let (send, recv) = channel();
|
||||
|
||||
smol::spawn(async move {
|
||||
let done = async move {
|
||||
async move {
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 0));
|
||||
let listener = TcpListener::bind(&addr).await?;
|
||||
thread::spawn(move || {
|
||||
smol::block_on(
|
||||
async move {
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 0));
|
||||
let listener = TcpListener::bind(&addr).await?;
|
||||
|
||||
send.send(listener.local_addr()?).unwrap();
|
||||
send.send(listener.local_addr()?).unwrap();
|
||||
|
||||
while let Some(stream) = listener.incoming().try_next().await? {
|
||||
let acceptor = acceptor.clone();
|
||||
let fut = async move {
|
||||
async move {
|
||||
let stream = acceptor.accept(stream).await?;
|
||||
loop {
|
||||
let (stream, _) = listener.accept().await?;
|
||||
|
||||
let (mut reader, mut writer) = split(stream);
|
||||
copy(&mut reader, &mut writer).await?;
|
||||
let acceptor = acceptor.clone();
|
||||
let fut = async move {
|
||||
let stream = acceptor.accept(stream).await?;
|
||||
|
||||
Ok(()) as io::Result<()>
|
||||
}
|
||||
.await
|
||||
.unwrap_or_else(|err| eprintln!("server: {:?}", err));
|
||||
};
|
||||
let (mut reader, mut writer) = split(stream);
|
||||
copy(&mut reader, &mut writer).await?;
|
||||
|
||||
smol::spawn(fut).detach();
|
||||
Ok(()) as io::Result<()>
|
||||
}
|
||||
Ok(())
|
||||
.unwrap_or_else(|err| eprintln!("server: {:?}", err));
|
||||
|
||||
smol::spawn(fut).detach();
|
||||
}
|
||||
.await
|
||||
.unwrap_or_else(|err: io::Error| eprintln!("server: {:?}", err));
|
||||
};
|
||||
done.await;
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
.unwrap_or_else(|err: io::Error| eprintln!("server: {:?}", err)),
|
||||
);
|
||||
});
|
||||
|
||||
let addr = recv.recv().unwrap();
|
||||
(addr, "testserver.com", CHAIN)
|
||||
};
|
||||
}
|
||||
let addr = recv.recv().unwrap();
|
||||
(addr, "foobar.com", CHAIN)
|
||||
});
|
||||
|
||||
fn start_server() -> &'static (SocketAddr, &'static str, &'static str) {
|
||||
&*TEST_SERVER
|
||||
fn start_server() -> &'static (SocketAddr, &'static str, &'static [u8]) {
|
||||
&TEST_SERVER
|
||||
}
|
||||
|
||||
async fn start_client(addr: SocketAddr, domain: &str, config: Arc<ClientConfig>) -> io::Result<()> {
|
||||
const FILE: &[u8] = include_bytes!("../Cargo.toml");
|
||||
const FILE: &[u8] = include_bytes!("../README.md");
|
||||
|
||||
let domain = webpki::DNSNameRef::try_from_ascii_str(domain).unwrap();
|
||||
let domain = rustls::ServerName::try_from(domain).unwrap();
|
||||
let config = TlsConnector::from(config);
|
||||
let mut buf = vec![0; FILE.len()];
|
||||
|
||||
|
@ -100,12 +102,24 @@ fn pass() -> io::Result<()> {
|
|||
use std::time::*;
|
||||
smol::Timer::after(Duration::from_secs(1)).await;
|
||||
|
||||
let mut config = ClientConfig::new();
|
||||
let mut chain = BufReader::new(Cursor::new(chain));
|
||||
config.root_store.add_pem_file(&mut chain).unwrap();
|
||||
let chain = certs(&mut std::io::Cursor::new(*chain)).unwrap();
|
||||
let trust_anchors = chain.iter().map(|cert| {
|
||||
let ta = webpki::TrustAnchor::try_from_cert_der(&cert[..]).unwrap();
|
||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
ta.subject,
|
||||
ta.spki,
|
||||
ta.name_constraints,
|
||||
)
|
||||
});
|
||||
let mut root_store = rustls::RootCertStore::empty();
|
||||
root_store.add_server_trust_anchors(trust_anchors.into_iter());
|
||||
let config = rustls::ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_root_certificates(root_store)
|
||||
.with_no_client_auth();
|
||||
let config = Arc::new(config);
|
||||
|
||||
start_client(*addr, domain, config.clone()).await?;
|
||||
start_client(*addr, domain, config).await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
@ -116,9 +130,21 @@ fn fail() -> io::Result<()> {
|
|||
smol::block_on(async {
|
||||
let (addr, domain, chain) = start_server();
|
||||
|
||||
let mut config = ClientConfig::new();
|
||||
let mut chain = BufReader::new(Cursor::new(chain));
|
||||
config.root_store.add_pem_file(&mut chain).unwrap();
|
||||
let chain = certs(&mut std::io::Cursor::new(*chain)).unwrap();
|
||||
let trust_anchors = chain.iter().map(|cert| {
|
||||
let ta = webpki::TrustAnchor::try_from_cert_der(&cert[..]).unwrap();
|
||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
ta.subject,
|
||||
ta.spki,
|
||||
ta.name_constraints,
|
||||
)
|
||||
});
|
||||
let mut root_store = rustls::RootCertStore::empty();
|
||||
root_store.add_server_trust_anchors(trust_anchors.into_iter());
|
||||
let config = rustls::ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_root_certificates(root_store)
|
||||
.with_no_client_auth();
|
||||
let config = Arc::new(config);
|
||||
|
||||
assert_ne!(domain, &"google.com");
|
||||
|
@ -128,3 +154,29 @@ fn fail() -> io::Result<()> {
|
|||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
// This test is a follow-up from https://github.com/tokio-rs/tls/issues/85
|
||||
#[test]
|
||||
fn lazy_config_acceptor_eof() {
|
||||
smol::block_on(async {
|
||||
let buf = Cursor::new(Vec::new());
|
||||
let acceptor =
|
||||
LazyConfigAcceptor::new(rustls::server::Acceptor::default(), AssertAsync::new(buf));
|
||||
let acceptor = async move { Ok(acceptor.await) };
|
||||
let timeout = async {
|
||||
smol::Timer::after(Duration::from_secs(3)).await;
|
||||
Err(())
|
||||
};
|
||||
|
||||
let accept_result = acceptor.or(timeout).await.expect("timeout");
|
||||
|
||||
match accept_result {
|
||||
Ok(_) => panic!("accepted a connection from zero bytes of data"),
|
||||
Err(e) if e.kind() == ErrorKind::UnexpectedEof => {}
|
||||
Err(e) => panic!("unexpected error: {:?}", e),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Include `utils` module
|
||||
include!("utils.rs");
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
mod utils {
|
||||
use std::io::{BufReader, Cursor};
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustls::{ClientConfig, OwnedTrustAnchor, PrivateKey, RootCertStore, ServerConfig};
|
||||
use rustls_pemfile::{certs, rsa_private_keys};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn make_configs() -> (Arc<ServerConfig>, Arc<ClientConfig>) {
|
||||
const CERT: &str = include_str!("end.cert");
|
||||
const CHAIN: &str = include_str!("end.chain");
|
||||
const RSA: &str = include_str!("end.rsa");
|
||||
|
||||
let cert = certs(&mut BufReader::new(Cursor::new(CERT)))
|
||||
.unwrap()
|
||||
.drain(..)
|
||||
.map(rustls::Certificate)
|
||||
.collect();
|
||||
let mut keys = rsa_private_keys(&mut BufReader::new(Cursor::new(RSA))).unwrap();
|
||||
let mut keys = keys.drain(..).map(PrivateKey);
|
||||
let sconfig = ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(cert, keys.next().unwrap())
|
||||
.unwrap();
|
||||
|
||||
let mut client_root_cert_store = RootCertStore::empty();
|
||||
let mut chain = BufReader::new(Cursor::new(CHAIN));
|
||||
let certs = certs(&mut chain).unwrap();
|
||||
let trust_anchors = certs.iter().map(|cert| {
|
||||
let ta = webpki::TrustAnchor::try_from_cert_der(&cert[..]).unwrap();
|
||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
ta.subject,
|
||||
ta.spki,
|
||||
ta.name_constraints,
|
||||
)
|
||||
});
|
||||
client_root_cert_store.add_server_trust_anchors(trust_anchors.into_iter());
|
||||
let cconfig = ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_root_certificates(client_root_cert_store)
|
||||
.with_no_client_auth();
|
||||
|
||||
(Arc::new(sconfig), Arc::new(cconfig))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue