feat(transport): Use tls native
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(()),
|
||||||
|
|||||||
@@ -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
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user