2017-08-13 10:19:17 +00:00
|
|
|
extern crate rustls;
|
2018-02-28 06:36:37 +00:00
|
|
|
extern crate tokio;
|
2017-08-13 10:19:17 +00:00
|
|
|
extern crate tokio_rustls;
|
2017-09-03 22:58:55 +00:00
|
|
|
extern crate webpki;
|
2017-08-13 10:19:17 +00:00
|
|
|
|
2018-03-22 11:47:27 +00:00
|
|
|
#[cfg(feature = "unstable-futures")] extern crate futures;
|
|
|
|
|
2017-08-13 10:19:17 +00:00
|
|
|
use std::{ io, thread };
|
|
|
|
use std::io::{ BufReader, Cursor };
|
|
|
|
use std::sync::Arc;
|
|
|
|
use std::sync::mpsc::channel;
|
|
|
|
use std::net::{ SocketAddr, IpAddr, Ipv4Addr };
|
2018-02-28 06:36:37 +00:00
|
|
|
use tokio::net::{ TcpListener, TcpStream };
|
2017-08-13 10:19:17 +00:00
|
|
|
use rustls::{ Certificate, PrivateKey, ServerConfig, ClientConfig };
|
|
|
|
use rustls::internal::pemfile::{ certs, rsa_private_keys };
|
|
|
|
use tokio_rustls::{ ClientConfigExt, ServerConfigExt };
|
|
|
|
|
|
|
|
const CERT: &str = include_str!("end.cert");
|
|
|
|
const CHAIN: &str = include_str!("end.chain");
|
|
|
|
const RSA: &str = include_str!("end.rsa");
|
|
|
|
const HELLO_WORLD: &[u8] = b"Hello world!";
|
|
|
|
|
|
|
|
|
|
|
|
fn start_server(cert: Vec<Certificate>, rsa: PrivateKey) -> SocketAddr {
|
2018-03-22 11:47:27 +00:00
|
|
|
use tokio::prelude::*;
|
|
|
|
use tokio::io as aio;
|
|
|
|
|
2017-09-03 22:58:55 +00:00
|
|
|
let mut config = ServerConfig::new(rustls::NoClientAuth::new());
|
2017-08-13 10:19:17 +00:00
|
|
|
config.set_single_cert(cert, rsa);
|
|
|
|
let config = Arc::new(config);
|
|
|
|
|
|
|
|
let (send, recv) = channel();
|
|
|
|
|
|
|
|
thread::spawn(move || {
|
|
|
|
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0);
|
2018-02-28 06:36:37 +00:00
|
|
|
let listener = TcpListener::bind(&addr).unwrap();
|
2017-08-13 10:19:17 +00:00
|
|
|
|
|
|
|
send.send(listener.local_addr().unwrap()).unwrap();
|
|
|
|
|
|
|
|
let done = listener.incoming()
|
2018-02-28 06:36:37 +00:00
|
|
|
.for_each(move |stream| {
|
2017-08-13 10:19:17 +00:00
|
|
|
let done = config.accept_async(stream)
|
|
|
|
.and_then(|stream| aio::read_exact(stream, vec![0; HELLO_WORLD.len()]))
|
|
|
|
.and_then(|(stream, buf)| {
|
|
|
|
assert_eq!(buf, HELLO_WORLD);
|
|
|
|
aio::write_all(stream, HELLO_WORLD)
|
|
|
|
})
|
|
|
|
.map(drop)
|
2018-03-22 11:47:27 +00:00
|
|
|
.map_err(|err| panic!("{:?}", err));
|
2017-08-13 10:19:17 +00:00
|
|
|
|
2018-03-21 05:08:47 +00:00
|
|
|
tokio::spawn(done);
|
2017-08-13 10:19:17 +00:00
|
|
|
Ok(())
|
|
|
|
})
|
2018-03-22 11:47:27 +00:00
|
|
|
.map_err(|err| panic!("{:?}", err));
|
2018-02-28 06:36:37 +00:00
|
|
|
|
2018-03-22 11:47:27 +00:00
|
|
|
tokio::run(done);
|
2017-08-13 10:19:17 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
recv.recv().unwrap()
|
|
|
|
}
|
|
|
|
|
2018-03-22 11:47:27 +00:00
|
|
|
fn start_server2(cert: Vec<Certificate>, rsa: PrivateKey) -> SocketAddr {
|
|
|
|
use futures::{ FutureExt, StreamExt };
|
|
|
|
use futures::io::{ AsyncReadExt, AsyncWriteExt };
|
|
|
|
|
|
|
|
let mut config = ServerConfig::new(rustls::NoClientAuth::new());
|
|
|
|
config.set_single_cert(cert, rsa);
|
|
|
|
let config = Arc::new(config);
|
|
|
|
|
|
|
|
let (send, recv) = channel();
|
|
|
|
|
|
|
|
thread::spawn(move || {
|
|
|
|
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0);
|
|
|
|
let listener = TcpListener::bind(&addr).unwrap();
|
|
|
|
|
|
|
|
send.send(listener.local_addr().unwrap()).unwrap();
|
|
|
|
|
|
|
|
let done = listener.incoming()
|
|
|
|
.for_each(move |stream| {
|
|
|
|
let done = config.accept_async(stream)
|
|
|
|
.and_then(|stream| stream.read_exact(vec![0; HELLO_WORLD.len()]))
|
|
|
|
.and_then(|(stream, buf)| {
|
|
|
|
assert_eq!(buf, HELLO_WORLD);
|
|
|
|
stream.write_all(HELLO_WORLD)
|
|
|
|
})
|
|
|
|
.map(drop)
|
|
|
|
.map_err(|err| panic!("{:?}", err));
|
|
|
|
|
|
|
|
tokio::spawn2(done);
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
.map(drop)
|
|
|
|
.map_err(|err| panic!("{:?}", err));
|
|
|
|
|
|
|
|
tokio::runtime::run2(done);
|
|
|
|
});
|
|
|
|
|
|
|
|
recv.recv().unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn start_client(addr: &SocketAddr, domain: &str, chain: Option<BufReader<Cursor<&str>>>) -> io::Result<()> {
|
|
|
|
use tokio::prelude::*;
|
|
|
|
use tokio::io as aio;
|
|
|
|
|
2017-09-03 22:58:55 +00:00
|
|
|
let domain = webpki::DNSNameRef::try_from_ascii_str(domain).unwrap();
|
2017-08-13 10:19:17 +00:00
|
|
|
let mut config = ClientConfig::new();
|
|
|
|
if let Some(mut chain) = chain {
|
|
|
|
config.root_store.add_pem_file(&mut chain).unwrap();
|
|
|
|
}
|
|
|
|
let config = Arc::new(config);
|
|
|
|
|
2018-02-28 06:36:37 +00:00
|
|
|
let done = TcpStream::connect(addr)
|
Remove `danger` feature & the API it controls.
The singular purpose of this crate should be to integrate Tokio and
Rustls. Therefore, any feature that isn't about making Rustls work
nicely with Tokio should be assumed a priori to be out of scope.
In particular, it is out of scope for tokio-rustls to provide APIs to
control SNI behavior. Instead, the application should configure
Rustls's SNI behavior using Rustls's configuration APIs, and pass the
configuration to tokio-rustls. Similarly, it is out of scope for
tokio-rustls to provide APIs to control the certificate validation
behavior. Instead, the application should configure certificate
validation using Rustls's APIs. Perhaps there should be a crate that
makes it convenient to do "dangerous" certificate validation, but IMO
that shouldn't be tokio-rustls, but a different one.
FWIW, the `danger` API was inherited from tokio-tls, and I'm working on
making an analogous change there.
2017-08-29 04:40:16 +00:00
|
|
|
.and_then(|stream| config.connect_async(domain, stream))
|
2017-08-13 10:19:17 +00:00
|
|
|
.and_then(|stream| aio::write_all(stream, HELLO_WORLD))
|
|
|
|
.and_then(|(stream, _)| aio::read_exact(stream, vec![0; HELLO_WORLD.len()]))
|
|
|
|
.and_then(|(_, buf)| {
|
|
|
|
assert_eq!(buf, HELLO_WORLD);
|
|
|
|
Ok(())
|
|
|
|
});
|
|
|
|
|
2018-02-28 06:36:37 +00:00
|
|
|
done.wait()
|
2017-08-13 10:19:17 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 11:47:27 +00:00
|
|
|
#[cfg(feature = "unstable-futures")]
|
|
|
|
fn start_client2(addr: &SocketAddr, domain: &str, chain: Option<BufReader<Cursor<&str>>>) -> io::Result<()> {
|
|
|
|
use futures::FutureExt;
|
|
|
|
use futures::io::{ AsyncReadExt, AsyncWriteExt };
|
|
|
|
use futures::executor::block_on;
|
|
|
|
|
|
|
|
let domain = webpki::DNSNameRef::try_from_ascii_str(domain).unwrap();
|
|
|
|
let mut config = ClientConfig::new();
|
|
|
|
if let Some(mut chain) = chain {
|
|
|
|
config.root_store.add_pem_file(&mut chain).unwrap();
|
|
|
|
}
|
|
|
|
let config = Arc::new(config);
|
|
|
|
|
|
|
|
let done = TcpStream::connect(addr)
|
|
|
|
.and_then(|stream| config.connect_async(domain, stream))
|
|
|
|
.and_then(|stream| {
|
|
|
|
eprintln!("WRITE: {:?}", stream);
|
|
|
|
stream.write_all(HELLO_WORLD)
|
|
|
|
})
|
|
|
|
.and_then(|(stream, _)| {
|
|
|
|
eprintln!("READ: {:?}", stream);
|
|
|
|
stream.read_exact(vec![0; HELLO_WORLD.len()])
|
|
|
|
})
|
|
|
|
.and_then(|(stream, buf)| {
|
|
|
|
eprintln!("OK: {:?}", stream);
|
|
|
|
assert_eq!(buf, HELLO_WORLD);
|
|
|
|
Ok(())
|
|
|
|
});
|
|
|
|
|
|
|
|
block_on(done)
|
|
|
|
}
|
|
|
|
|
2017-08-13 10:19:17 +00:00
|
|
|
|
|
|
|
#[test]
|
2018-03-22 11:47:27 +00:00
|
|
|
fn pass() {
|
2017-08-13 10:19:17 +00:00
|
|
|
let cert = certs(&mut BufReader::new(Cursor::new(CERT))).unwrap();
|
|
|
|
let mut keys = rsa_private_keys(&mut BufReader::new(Cursor::new(RSA))).unwrap();
|
|
|
|
let chain = BufReader::new(Cursor::new(CHAIN));
|
|
|
|
|
|
|
|
let addr = start_server(cert, keys.pop().unwrap());
|
Remove `danger` feature & the API it controls.
The singular purpose of this crate should be to integrate Tokio and
Rustls. Therefore, any feature that isn't about making Rustls work
nicely with Tokio should be assumed a priori to be out of scope.
In particular, it is out of scope for tokio-rustls to provide APIs to
control SNI behavior. Instead, the application should configure
Rustls's SNI behavior using Rustls's configuration APIs, and pass the
configuration to tokio-rustls. Similarly, it is out of scope for
tokio-rustls to provide APIs to control the certificate validation
behavior. Instead, the application should configure certificate
validation using Rustls's APIs. Perhaps there should be a crate that
makes it convenient to do "dangerous" certificate validation, but IMO
that shouldn't be tokio-rustls, but a different one.
FWIW, the `danger` API was inherited from tokio-tls, and I'm working on
making an analogous change there.
2017-08-29 04:40:16 +00:00
|
|
|
start_client(&addr, "localhost", Some(chain)).unwrap();
|
2017-08-13 10:19:17 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 11:47:27 +00:00
|
|
|
#[cfg(feature = "unstable-futures")]
|
|
|
|
#[test]
|
|
|
|
fn pass2() {
|
|
|
|
let cert = certs(&mut BufReader::new(Cursor::new(CERT))).unwrap();
|
|
|
|
let mut keys = rsa_private_keys(&mut BufReader::new(Cursor::new(RSA))).unwrap();
|
|
|
|
let chain = BufReader::new(Cursor::new(CHAIN));
|
|
|
|
|
|
|
|
let addr = start_server2(cert, keys.pop().unwrap());
|
|
|
|
start_client2(&addr, "localhost", Some(chain)).unwrap();
|
|
|
|
}
|
|
|
|
|
2017-08-13 10:19:17 +00:00
|
|
|
#[should_panic]
|
|
|
|
#[test]
|
|
|
|
fn fail() {
|
|
|
|
let cert = certs(&mut BufReader::new(Cursor::new(CERT))).unwrap();
|
|
|
|
let mut keys = rsa_private_keys(&mut BufReader::new(Cursor::new(RSA))).unwrap();
|
|
|
|
let chain = BufReader::new(Cursor::new(CHAIN));
|
|
|
|
|
|
|
|
let addr = start_server(cert, keys.pop().unwrap());
|
|
|
|
|
Remove `danger` feature & the API it controls.
The singular purpose of this crate should be to integrate Tokio and
Rustls. Therefore, any feature that isn't about making Rustls work
nicely with Tokio should be assumed a priori to be out of scope.
In particular, it is out of scope for tokio-rustls to provide APIs to
control SNI behavior. Instead, the application should configure
Rustls's SNI behavior using Rustls's configuration APIs, and pass the
configuration to tokio-rustls. Similarly, it is out of scope for
tokio-rustls to provide APIs to control the certificate validation
behavior. Instead, the application should configure certificate
validation using Rustls's APIs. Perhaps there should be a crate that
makes it convenient to do "dangerous" certificate validation, but IMO
that shouldn't be tokio-rustls, but a different one.
FWIW, the `danger` API was inherited from tokio-tls, and I'm working on
making an analogous change there.
2017-08-29 04:40:16 +00:00
|
|
|
start_client(&addr, "google.com", Some(chain)).unwrap();
|
2017-08-13 10:19:17 +00:00
|
|
|
}
|