feat(transport): Use tls native

This commit is contained in:
Alexis Mousset
2017-07-16 21:38:18 +02:00
parent 858cecfdde
commit 441c4e8228
5 changed files with 34 additions and 45 deletions

View File

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

View File

@@ -4,7 +4,7 @@
//! emails have to implement `SendableEmail`. //! 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] #[macro_use]
extern crate log; extern crate log;
@@ -12,7 +12,7 @@ extern crate base64;
extern crate hex; extern crate hex;
extern crate crypto; extern crate crypto;
extern crate bufstream; extern crate bufstream;
extern crate openssl; extern crate native_tls;
extern crate emailaddress; extern crate emailaddress;
extern crate serde_json; extern crate serde_json;
extern crate serde; extern crate serde;

View File

@@ -1,7 +1,7 @@
//! SMTP client //! SMTP client
use bufstream::BufStream; use bufstream::BufStream;
use openssl::ssl::SslContext; use native_tls::TlsConnector;
use smtp::{CRLF, MESSAGE_ENDING}; use smtp::{CRLF, MESSAGE_ENDING};
use smtp::authentication::{Credentials, Mechanism}; use smtp::authentication::{Credentials, Mechanism};
use smtp::client::net::{Connector, NetworkStream, Timeout}; 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 /// 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 { 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(()), None => Ok(()),
} }
} }
@@ -113,7 +113,7 @@ impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
pub fn connect<A: ToSocketAddrs>( pub fn connect<A: ToSocketAddrs>(
&mut self, &mut self,
addr: &A, addr: &A,
ssl_context: Option<&SslContext>, tls_connector: Option<&TlsConnector>,
) -> SmtpResult { ) -> SmtpResult {
// Connect should not be called when the client is already connected // Connect should not be called when the client is already connected
if self.stream.is_some() { if self.stream.is_some() {
@@ -130,7 +130,7 @@ impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
debug!("connecting to {}", server_addr); debug!("connecting to {}", server_addr);
// Try to connect // 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() self.get_reply()
} }

View File

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

View File

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