Skip to content

Commit a58d250

Browse files
authored
TLS Peer Dependency (#4)
* Native TLS Shadowing Addresses #1 and #2. Provides interfaces without needing to use native-tls library.
1 parent 6156936 commit a58d250

File tree

12 files changed

+492
-224
lines changed

12 files changed

+492
-224
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
language: rust
33
rust:
4-
- nightly
4+
- nightly-2019-04-07
55
sudo: false
66
cache:
77
- apt

Cargo.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,20 @@ edition = "2018"
77
# - Update CHANGELOG.md.
88
# - Update doc URL.
99
# - Create "v0.1.x" git tag.
10-
version = "0.3.0-alpha.2"
10+
version = "0.3.0-alpha.3"
1111
license = "MIT"
1212
readme = "README.md"
1313
description = """
1414
TLS support for AsyncRead/AsyncWrite using native-tls
1515
"""
1616
authors = ["Danny Browning <[email protected]>", "Carl Lerche <[email protected]>"]
1717
categories = ["asynchronous", "network-programming"]
18-
documentation = "https://docs.rs/tls-async/0.3.0-alpha.2/tls_async/"
18+
documentation = "https://docs.rs/tls-async/0.3.0-alpha.3/tls_async/"
1919
repository = "https://github.com/dbcfd/tls-async"
2020

2121
[dependencies]
22+
failure = "0.1"
23+
failure_derive = "0.1"
2224
log = "0.4.1"
2325
native-tls = "0.2"
2426

@@ -29,7 +31,7 @@ features = ["compat", "io-compat", "std"]
2931

3032
[dev-dependencies]
3133
cfg-if = "0.1"
32-
romio = "0.3.0-alpha.2"
34+
romio = "0.3.0-alpha.3"
3335
tokio = "0.1"
3436

3537
[dev-dependencies.env_logger]

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ This is an experimental fork of [tokio-tls](https://github.com/tokio-rs/tokio/tr
1010
An implementation of TLS/SSL streams for [Futures 0.3](https://github.com/rust-lang-nursery/futures-rs) built on top of the [`native-tls`
1111
crate]
1212

13-
[Documentation](https://docs.rs/tls-async/0.3.0-alpha.1/)
13+
[Documentation](https://docs.rs/tls-async/0.3.0-alpha.3/)
1414

1515
[`native-tls` crate]: https://github.com/sfackler/rust-native-tls
1616

@@ -29,8 +29,7 @@ First, add this to your `Cargo.toml`:
2929

3030
```toml
3131
[dependencies]
32-
native-tls = "0.2"
33-
tls-async = "0.3.0-alpha.1"
32+
tls-async = "0.3.0-alpha.3"
3433
```
3534

3635
Next, add this to your crate:

examples/download-rust-lang.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use std::net::ToSocketAddrs;
33

44
use futures::{FutureExt, TryFutureExt};
55
use futures::io::{AsyncReadExt, AsyncWriteExt};
6-
use native_tls::TlsConnector;
76
use romio::TcpStream;
7+
use tls_async::TlsConnector;
88
use tokio::runtime::Runtime;
99

1010
fn main() {
@@ -19,7 +19,6 @@ fn main() {
1919

2020
let socket = await!(TcpStream::connect(&addr)).expect("Could not connect");
2121
let cx = TlsConnector::builder().build().expect("Could not build");
22-
let cx = tls_async::TlsConnector::from(cx);
2322

2423
let mut socket = await!(cx.connect("www.rust-lang.org", socket)).expect("Could not form tls connection");
2524
let _ = await!(socket.write_all(b"\

src/acceptor.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
use crate::errors::Error;
2+
use crate::pending::PendingTlsStream;
3+
use crate::{Identity, Protocol};
4+
5+
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite};
6+
7+
/// A builder for `TlsAcceptor`s.
8+
pub struct TlsAcceptorBuilder {
9+
inner: native_tls::TlsAcceptorBuilder,
10+
}
11+
12+
impl TlsAcceptorBuilder {
13+
/// Sets the minimum supported protocol version.
14+
///
15+
/// A value of `None` enables support for the oldest protocols supported by the implementation.
16+
///
17+
/// Defaults to `Some(Protocol::Tlsv10)`.
18+
pub fn min_protocol_version(&mut self, protocol: Option<Protocol>) -> &mut TlsAcceptorBuilder {
19+
self.inner.min_protocol_version(protocol);
20+
self
21+
}
22+
23+
/// Sets the maximum supported protocol version.
24+
///
25+
/// A value of `None` enables support for the newest protocols supported by the implementation.
26+
///
27+
/// Defaults to `None`.
28+
pub fn max_protocol_version(&mut self, protocol: Option<Protocol>) -> &mut TlsAcceptorBuilder {
29+
self.inner.max_protocol_version(protocol);
30+
self
31+
}
32+
33+
/// Creates a new `TlsAcceptor`.
34+
pub fn build(&self) -> Result<TlsAcceptor, Error> {
35+
let acceptor = self.inner.build().map_err(Error::Acceptor)?;
36+
Ok(TlsAcceptor {
37+
inner: acceptor
38+
})
39+
}
40+
}
41+
42+
/// A builder for server-side TLS connections.
43+
///
44+
/// # Examples
45+
///
46+
/// ```rust,no_run
47+
/// #![feature(async_await, await_macro, futures_api)]
48+
/// use futures::StreamExt;
49+
/// use futures::io::AsyncRead;
50+
/// use tls_async::{Identity, TlsAcceptor, TlsStream};
51+
/// use std::fs::File;
52+
/// use std::io::{Read};
53+
/// use romio::{TcpListener, TcpStream};
54+
/// use std::sync::Arc;
55+
/// use std::thread;
56+
///
57+
/// let mut file = File::open("identity.pfx").unwrap();
58+
/// let mut identity = vec![];
59+
/// file.read_to_end(&mut identity).unwrap();
60+
/// let identity = Identity::from_pkcs12(&identity, "hunter2").unwrap();
61+
///
62+
/// let mut listener = TcpListener::bind(&"0.0.0.0:8443".parse().unwrap()).unwrap();
63+
/// let acceptor = TlsAcceptor::new(identity).unwrap();
64+
/// let acceptor = Arc::new(acceptor);
65+
///
66+
/// fn handle_client<S: AsyncRead>(stream: S) {
67+
/// // ...
68+
/// }
69+
///
70+
/// let mut incoming = listener.incoming();
71+
/// # futures::executor::block_on(async {
72+
/// for stream in await!(incoming.next()) {
73+
/// match stream {
74+
/// Ok(stream) => {
75+
/// let acceptor = acceptor.clone();
76+
/// let stream = await!(acceptor.accept(stream)).unwrap();
77+
/// handle_client(stream);
78+
/// }
79+
/// Err(e) => { /* connection failed */ }
80+
/// }
81+
/// }
82+
/// # })
83+
/// ```
84+
#[derive(Clone)]
85+
pub struct TlsAcceptor {
86+
inner: native_tls::TlsAcceptor,
87+
}
88+
89+
impl TlsAcceptor {
90+
/// Creates a acceptor with default settings.
91+
///
92+
/// The identity acts as the server's private key/certificate chain.
93+
pub fn new(identity: Identity) -> Result<TlsAcceptor, Error> {
94+
let native_acceptor = native_tls::TlsAcceptor::new(identity).map_err(Error::Acceptor)?;
95+
Ok(TlsAcceptor {
96+
inner: native_acceptor,
97+
})
98+
}
99+
100+
/// Returns a new builder for a `TlsAcceptor`.
101+
///
102+
/// The identity acts as the server's private key/certificate chain.
103+
pub fn builder(identity: Identity) -> TlsAcceptorBuilder {
104+
let builder = native_tls::TlsAcceptor::builder(identity);
105+
TlsAcceptorBuilder {
106+
inner: builder,
107+
}
108+
}
109+
110+
/// Accepts a new client connection with the provided stream.
111+
///
112+
/// This function will internally call `TlsAcceptor::accept` to connect
113+
/// the stream and returns a future representing the resolution of the
114+
/// connection operation. The returned future will resolve to either
115+
/// `TlsStream<S>` or `Error` depending if it's successful or not.
116+
///
117+
/// This is typically used after a new socket has been accepted from a
118+
/// `TcpListener`. That socket is then passed to this function to perform
119+
/// the server half of accepting a client connection.
120+
pub fn accept<S>(&self, stream: S) -> PendingTlsStream<S>
121+
where S: AsyncRead + AsyncWrite,
122+
{
123+
PendingTlsStream::new(self.inner.accept(stream.compat()))
124+
}
125+
}
126+
127+
impl From<native_tls::TlsAcceptor> for TlsAcceptor {
128+
fn from(inner: native_tls::TlsAcceptor) -> Self {
129+
Self {
130+
inner,
131+
}
132+
}
133+
}

src/connector.rs

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
use crate::errors::Error;
2+
use crate::pending::PendingTlsStream;
3+
use crate::{Certificate, Identity, Protocol};
4+
5+
use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite};
6+
7+
/// A builder for `TlsConnector`s.
8+
pub struct TlsConnectorBuilder {
9+
inner: native_tls::TlsConnectorBuilder,
10+
}
11+
12+
impl TlsConnectorBuilder {
13+
/// Sets the identity to be used for client certificate authentication.
14+
pub fn identity(&mut self, identity: Identity) -> &mut TlsConnectorBuilder {
15+
self.inner.identity(identity);
16+
self
17+
}
18+
19+
/// Sets the minimum supported protocol version.
20+
///
21+
/// A value of `None` enables support for the oldest protocols supported by the implementation.
22+
///
23+
/// Defaults to `Some(Protocol::Tlsv10)`.
24+
pub fn min_protocol_version(&mut self, protocol: Option<Protocol>) -> &mut TlsConnectorBuilder {
25+
self.inner.min_protocol_version(protocol);
26+
self
27+
}
28+
29+
/// Sets the maximum supported protocol version.
30+
///
31+
/// A value of `None` enables support for the newest protocols supported by the implementation.
32+
///
33+
/// Defaults to `None`.
34+
pub fn max_protocol_version(&mut self, protocol: Option<Protocol>) -> &mut TlsConnectorBuilder {
35+
self.inner.max_protocol_version(protocol);
36+
self
37+
}
38+
39+
/// Adds a certificate to the set of roots that the connector will trust.
40+
///
41+
/// The connector will use the system's trust root by default. This method can be used to add
42+
/// to that set when communicating with servers not trusted by the system.
43+
///
44+
/// Defaults to an empty set.
45+
pub fn add_root_certificate(&mut self, cert: Certificate) -> &mut TlsConnectorBuilder {
46+
self.inner.add_root_certificate(cert);
47+
self
48+
}
49+
50+
/// Controls the use of certificate validation.
51+
///
52+
/// Defaults to `false`.
53+
///
54+
/// # Warning
55+
///
56+
/// You should think very carefully before using this method. If invalid certificates are trusted, *any*
57+
/// certificate for *any* site will be trusted for use. This includes expired certificates. This introduces
58+
/// significant vulnerabilities, and should only be used as a last resort.
59+
pub fn danger_accept_invalid_certs(
60+
&mut self,
61+
accept_invalid_certs: bool,
62+
) -> &mut TlsConnectorBuilder {
63+
self.inner.danger_accept_invalid_certs(accept_invalid_certs);
64+
self
65+
}
66+
67+
/// Controls the use of Server Name Indication (SNI).
68+
///
69+
/// Defaults to `true`.
70+
pub fn use_sni(&mut self, use_sni: bool) -> &mut TlsConnectorBuilder {
71+
self.inner.use_sni(use_sni);
72+
self
73+
}
74+
75+
/// Controls the use of hostname verification.
76+
///
77+
/// Defaults to `false`.
78+
///
79+
/// # Warning
80+
///
81+
/// You should think very carefully before using this method. If invalid hostnames are trusted, *any* valid
82+
/// certificate for *any* site will be trusted for use. This introduces significant vulnerabilities, and should
83+
/// only be used as a last resort.
84+
pub fn danger_accept_invalid_hostnames(
85+
&mut self,
86+
accept_invalid_hostnames: bool,
87+
) -> &mut TlsConnectorBuilder {
88+
self.inner.danger_accept_invalid_hostnames(accept_invalid_hostnames);
89+
self
90+
}
91+
92+
/// Creates a new `TlsConnector`.
93+
pub fn build(&self) -> Result<TlsConnector, Error> {
94+
let connector = self.inner.build().map_err(Error::Connector)?;
95+
Ok(TlsConnector {
96+
inner: connector
97+
})
98+
}
99+
}
100+
101+
///
102+
/// # Examples
103+
///
104+
/// ```rust,no_run
105+
/// #![feature(async_await, await_macro, futures_api)]
106+
/// use futures::io::{AsyncReadExt, AsyncWriteExt};
107+
/// use tls_async::TlsConnector;
108+
/// use std::io::{Read, Write};
109+
/// use std::net::ToSocketAddrs;
110+
/// use romio::TcpStream;
111+
///
112+
/// # futures::executor::block_on(async {
113+
/// let connector = TlsConnector::new().unwrap();
114+
///
115+
/// let addr = "google.com:443".to_socket_addrs().unwrap().next().unwrap();
116+
/// let stream = await!(TcpStream::connect(&addr)).unwrap();
117+
/// let mut stream = await!(connector.connect("google.com", stream)).unwrap();
118+
///
119+
/// await!(stream.write_all(b"GET / HTTP/1.0\r\n\r\n")).unwrap();
120+
/// let mut res = vec![];
121+
/// await!(stream.read_to_end(&mut res)).unwrap();
122+
/// println!("{}", String::from_utf8_lossy(&res));
123+
/// # })
124+
/// ```
125+
#[derive(Clone)]
126+
pub struct TlsConnector {
127+
inner: native_tls::TlsConnector,
128+
}
129+
130+
impl TlsConnector {
131+
/// Returns a new connector with default settings.
132+
pub fn new() -> Result<TlsConnector, Error> {
133+
let native_connector = native_tls::TlsConnector::new().map_err(Error::Connector)?;
134+
Ok( TlsConnector {
135+
inner: native_connector,
136+
})
137+
}
138+
139+
/// Returns a new builder for a `TlsConnector`.
140+
pub fn builder() -> TlsConnectorBuilder {
141+
TlsConnectorBuilder {
142+
inner: native_tls::TlsConnector::builder(),
143+
}
144+
}
145+
146+
/// Connects the provided stream with this connector, assuming the provided
147+
/// domain.
148+
///
149+
/// This function will internally call `TlsConnector::connect` to connect
150+
/// the stream and returns a future representing the resolution of the
151+
/// connection operation. The returned future will resolve to either
152+
/// `TlsStream<S>` or `Error` depending if it's successful or not.
153+
///
154+
/// This is typically used for clients who have already established, for
155+
/// example, a TCP connection to a remote server. That stream is then
156+
/// provided here to perform the client half of a connection to a
157+
/// TLS-powered server.
158+
pub fn connect<'a, S>(&'a self, domain: &'a str, stream: S) -> PendingTlsStream<S>
159+
where S: AsyncRead + AsyncWrite,
160+
{
161+
PendingTlsStream::new(self.inner.connect(domain, stream.compat()))
162+
}
163+
}

src/errors.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use failure::Fail;
2+
3+
#[derive(Debug, Fail)]
4+
pub enum Error {
5+
#[fail(display="NativeTls Acceptor Error")]
6+
Acceptor(#[cause] native_tls::Error),
7+
#[fail(display="NativeTls Connector Error")]
8+
Connector(#[cause] native_tls::Error),
9+
#[fail(display="Error during handshake")]
10+
Handshake(#[cause] native_tls::Error),
11+
#[fail(display="NativeTls Error")]
12+
Native(#[cause] native_tls::Error),
13+
#[fail(display="Cannot repeat handshake")]
14+
RepeatedHandshake,
15+
}
16+
17+
unsafe impl Sync for Error {}
18+
unsafe impl Send for Error {}

0 commit comments

Comments
 (0)