Merge pull request #167 from amousset/use-native-tls
feat(transport): Use tls native
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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(()),
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user