Merge pull request #167 from amousset/use-native-tls

feat(transport): Use tls native
This commit is contained in:
Alexis Mousset
2017-07-16 21:45:36 +02:00
committed by GitHub
5 changed files with 34 additions and 45 deletions

View File

@@ -17,7 +17,7 @@ travis-ci = { repository = "lettre/lettre" }
[dependencies]
bufstream = "^0.1"
log = "^0.3"
openssl = "^0.9"
native-tls = "^0.1"
base64 = "^0.6"
hex = "^0.2"
rust-crypto = "^0.2"

View File

@@ -4,7 +4,7 @@
//! emails have to implement `SendableEmail`.
//!
#![deny(missing_docs, unsafe_code, unstable_features, warnings, missing_debug_implementations)]
#![deny(missing_docs, unsafe_code, unstable_features, warnings)]
#[macro_use]
extern crate log;
@@ -12,7 +12,7 @@ extern crate base64;
extern crate hex;
extern crate crypto;
extern crate bufstream;
extern crate openssl;
extern crate native_tls;
extern crate emailaddress;
extern crate serde_json;
extern crate serde;

View File

@@ -1,7 +1,7 @@
//! SMTP client
use bufstream::BufStream;
use openssl::ssl::SslContext;
use native_tls::TlsConnector;
use smtp::{CRLF, MESSAGE_ENDING};
use smtp::authentication::{Credentials, Mechanism};
use smtp::client::net::{Connector, NetworkStream, Timeout};
@@ -82,9 +82,9 @@ impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
}
/// Upgrades the underlying connection to SSL/TLS
pub fn upgrade_tls_stream(&mut self, ssl_context: &SslContext) -> io::Result<()> {
pub fn upgrade_tls_stream(&mut self, tls_connector: &TlsConnector) -> io::Result<()> {
match self.stream {
Some(ref mut stream) => stream.get_mut().upgrade_tls(ssl_context),
Some(ref mut stream) => stream.get_mut().upgrade_tls(tls_connector),
None => Ok(()),
}
}
@@ -113,7 +113,7 @@ impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
pub fn connect<A: ToSocketAddrs>(
&mut self,
addr: &A,
ssl_context: Option<&SslContext>,
tls_connector: Option<&TlsConnector>,
) -> SmtpResult {
// Connect should not be called when the client is already connected
if self.stream.is_some() {
@@ -130,7 +130,7 @@ impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
debug!("connecting to {}", server_addr);
// Try to connect
self.set_stream(try!(Connector::connect(&server_addr, ssl_context)));
self.set_stream(try!(Connector::connect(&server_addr, tls_connector)));
self.get_reply()
}

View File

@@ -1,7 +1,6 @@
//! A trait to represent a stream
use openssl::ssl::{Ssl, SslContext, SslStream};
use native_tls::{TlsConnector, TlsStream};
use smtp::client::mock::MockStream;
use std::io;
use std::io::{ErrorKind, Read, Write};
@@ -14,7 +13,7 @@ pub enum NetworkStream {
/// Plain TCP stream
Tcp(TcpStream),
/// Encrypted TCP stream
Ssl(SslStream<TcpStream>),
Ssl(TlsStream<TcpStream>),
/// Mock stream
Mock(MockStream),
}
@@ -74,46 +73,38 @@ impl Write for NetworkStream {
/// A trait for the concept of opening a stream
pub trait Connector: Sized {
/// Opens a connection to the given IP socket
fn connect(addr: &SocketAddr, ssl_context: Option<&SslContext>) -> io::Result<Self>;
fn connect(addr: &SocketAddr, tls_connector: Option<&TlsConnector>) -> io::Result<Self>;
/// Upgrades to TLS connection
fn upgrade_tls(&mut self, ssl_context: &SslContext) -> io::Result<()>;
fn upgrade_tls(&mut self, tls_connector: &TlsConnector) -> io::Result<()>;
/// Is the NetworkStream encrypted
fn is_encrypted(&self) -> bool;
}
impl Connector for NetworkStream {
fn connect(addr: &SocketAddr, ssl_context: Option<&SslContext>) -> io::Result<NetworkStream> {
fn connect(
addr: &SocketAddr,
tls_connector: Option<&TlsConnector>,
) -> io::Result<NetworkStream> {
let tcp_stream = try!(TcpStream::connect(addr));
match ssl_context {
match tls_connector {
Some(context) => {
match Ssl::new(context) {
Ok(ssl) => {
ssl.connect(tcp_stream).map(NetworkStream::Ssl).map_err(
|e| {
io::Error::new(ErrorKind::Other, e)
},
)
}
Err(e) => Err(io::Error::new(ErrorKind::Other, e)),
}
context
.danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication(tcp_stream)
.map(NetworkStream::Ssl)
.map_err(|e| io::Error::new(ErrorKind::Other, e))
}
None => Ok(NetworkStream::Tcp(tcp_stream)),
}
}
#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
fn upgrade_tls(&mut self, ssl_context: &SslContext) -> io::Result<()> {
fn upgrade_tls(&mut self, tls_connector: &TlsConnector) -> io::Result<()> {
*self = match *self {
NetworkStream::Tcp(ref mut stream) => {
match Ssl::new(ssl_context) {
Ok(ssl) => {
match ssl.connect(stream.try_clone().unwrap()) {
Ok(ssl_stream) => NetworkStream::Ssl(ssl_stream),
Err(err) => return Err(io::Error::new(ErrorKind::Other, err)),
}
}
Err(e) => return Err(io::Error::new(ErrorKind::Other, e)),
match tls_connector.danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication(stream.try_clone().unwrap()) {
Ok(ssl_stream) => NetworkStream::Ssl(ssl_stream),
Err(err) => return Err(io::Error::new(ErrorKind::Other, err)),
}
}
NetworkStream::Ssl(_) => return Ok(()),

View File

@@ -62,8 +62,8 @@
//! .hello_name(ClientId::Domain("my.hostname.tld".to_string()))
//! // Add credentials for authentication
//! .credentials(Credentials::new("username".to_string(), "password".to_string()))
//! // Specify a TLS security level. You can also specify an SslContext with
//! // .ssl_context(SslContext::Ssl23)
//! // Specify a TLS security level. You can also specify an TlsConnector with
//! // .tls_connector(TlsConnector::Ssl23)
//! .security_level(SecurityLevel::AlwaysEncrypt)
//! // Enable SMTPUTF8 if the server supports it
//! .smtp_utf8(true)
@@ -108,7 +108,7 @@
use EmailTransport;
use SendableEmail;
use openssl::ssl::{SslContext, SslMethod};
use native_tls::TlsConnector;
use smtp::authentication::{Credentials, Mechanism};
use smtp::client::Client;
use smtp::commands::*;
@@ -172,7 +172,6 @@ pub enum SecurityLevel {
}
/// Contains client configuration
#[derive(Debug)]
pub struct SmtpTransportBuilder {
/// Maximum connection reuse
///
@@ -187,7 +186,7 @@ pub struct SmtpTransportBuilder {
/// Socket we are connecting to
server_addr: SocketAddr,
/// SSL context to use
ssl_context: SslContext,
tls_connector: TlsConnector,
/// TLS security level
security_level: SecurityLevel,
/// Enable UTF8 mailboxes in envelope or headers
@@ -209,7 +208,7 @@ impl SmtpTransportBuilder {
Some(addr) => {
Ok(SmtpTransportBuilder {
server_addr: addr,
ssl_context: SslContext::builder(SslMethod::tls()).unwrap().build(),
tls_connector: TlsConnector::builder().unwrap().build().unwrap(),
security_level: SecurityLevel::AlwaysEncrypt,
smtp_utf8: false,
credentials: None,
@@ -230,8 +229,8 @@ impl SmtpTransportBuilder {
}
/// Use STARTTLS with a specific context
pub fn ssl_context(mut self, ssl_context: SslContext) -> SmtpTransportBuilder {
self.ssl_context = ssl_context;
pub fn tls_connector(mut self, ssl_context: TlsConnector) -> SmtpTransportBuilder {
self.tls_connector = ssl_context;
self
}
@@ -317,7 +316,6 @@ struct State {
}
/// Structure that implements the high level SMTP client
#[derive(Debug)]
pub struct SmtpTransport {
/// Information about the server
/// Value is None before HELO/EHLO
@@ -411,7 +409,7 @@ impl EmailTransport<SmtpResult> for SmtpTransport {
try!(self.client.connect(
&self.client_info.server_addr,
match self.client_info.security_level {
SecurityLevel::EncryptedWrapper => Some(&self.client_info.ssl_context),
SecurityLevel::EncryptedWrapper => Some(&self.client_info.tls_connector),
_ => None,
},
));
@@ -439,7 +437,7 @@ impl EmailTransport<SmtpResult> for SmtpTransport {
try_smtp!(self.client.smtp_command(StarttlsCommand), self);
try_smtp!(
self.client.upgrade_tls_stream(
&self.client_info.ssl_context,
&self.client_info.tls_connector,
),
self
);