Skip to content

Commit 2ea22f4

Browse files
zsckmattnenterprise
authored andcommitted
Persist custom ssl (#49)
* Update the example to be consistent with the API * Simplified the README example, relying on the convention that using unwrap in examples is acceptable * Fixed the rest of the API references * Fixed syntax error * Tested program in a working environment and fixed last syntax errors * Fixed indentation * Fixed indentation * Add a secure_with_ssl method to FtpStream so that users can supply their own SSL configuration * Add a field to FtpStream when compiling with the secure feature that will contain ssl configuration information to be used in data_command * Fixed compilation errors * Make the openssl requirement mandatory. It's not worth it to fragment the codebase and have multiple copies of definitions of things to work around a feature flag when the tradeoff is having a lot more usable SSL * Since the secure() and insecure() methods actually have type signatures very similar to the into() method (https://doc.rust-lang.org/std/convert/trait.Into.html#tymethod.into), I changed their names to into_secure and into_insecure to be a little clearer about how they work * Create a new FtpsStream<T> type and revert back to the old FtpStream type for other cases. The new type allows us to retain (and require) information about an Ssl configuration only when the user wants to supply one. Need a way to avoid so much code duplication though * Let use of OpenSsl and indeed the entire FtpsStream type be optional under the secure feature flag again * Move back to a single-type model with an SSL field that is only present when the secure feature flag is used
1 parent 8538e5f commit 2ea22f4

File tree

3 files changed

+56
-78
lines changed

3 files changed

+56
-78
lines changed

src/data_stream.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ pub enum DataStream {
1212
Ssl(SslStream<TcpStream>),
1313
}
1414

15-
1615
#[cfg(feature = "secure")]
1716
impl DataStream {
1817
/// Unwrap the stream into TcpStream. This method is only used in secure connection.

src/ftp.rs

Lines changed: 56 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use std::io::{Read, BufRead, BufReader, BufWriter, Cursor, Write, copy};
2+
#[cfg(feature = "secure")]
3+
use std::error::Error;
24
use std::net::{TcpStream, SocketAddr};
35
use std::string::String;
46
use std::str::FromStr;
@@ -7,7 +9,7 @@ use regex::Regex;
79
use chrono::{DateTime, UTC};
810
use chrono::offset::TimeZone;
911
#[cfg(feature = "secure")]
10-
use openssl::ssl::{Ssl, SslContext, SslMethod, SslStream, IntoSsl};
12+
use openssl::ssl::{Ssl, SslStream, IntoSsl};
1113
use super::data_stream::DataStream;
1214
use super::status;
1315
use super::types::{FileType, FtpError, Line, Result};
@@ -24,61 +26,44 @@ lazy_static! {
2426
static ref SIZE_RE: Regex = Regex::new(r"\s+(\d+)\s*$").unwrap();
2527
}
2628

27-
#[cfg(feature = "secure")]
28-
lazy_static! {
29-
// Shared SSL context
30-
static ref SSL_CONTEXT: SslContext = match SslContext::new(SslMethod::Sslv23) {
31-
Ok(ctx) => ctx,
32-
Err(e) => panic!("{}", e)
33-
};
34-
}
35-
3629
/// Stream to interface with the FTP server. This interface is only for the command stream.
3730
#[derive(Debug)]
3831
pub struct FtpStream {
3932
reader: BufReader<DataStream>,
33+
#[cfg(feature = "secure")]
34+
ssl_cfg: Option<Ssl>,
4035
}
4136

4237
impl FtpStream {
4338
/// Creates an FTP Stream.
39+
#[cfg(not(feature = "secure"))]
4440
pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<FtpStream> {
4541
TcpStream::connect(addr)
4642
.map_err(|e| FtpError::ConnectionError(e))
4743
.and_then(|stream| {
4844
let mut ftp_stream = FtpStream {
49-
reader: BufReader::new(DataStream::Tcp(stream))
45+
reader: BufReader::new(DataStream::Tcp(stream)),
5046
};
5147
ftp_stream.read_response(status::READY)
5248
.map(|_| ftp_stream)
5349
})
5450
}
55-
56-
/// Switch to secure mode if possible. If the connection is already
57-
/// secure does nothing.
58-
///
59-
/// ## Panics
60-
///
61-
/// Panics if the plain TCP connection cannot be switched to TLS mode.
62-
///
63-
/// ## Example
64-
///
65-
/// ```
66-
/// use ftp::FtpStream;
67-
/// let mut ftp_stream = FtpStream::connect("127.0.0.1:21").unwrap();
68-
/// // Switch to the secure mode
69-
/// let mut ftp_stream = ftp_stream.secure().unwrap();
70-
/// // Do all secret things
71-
/// let _ = ftp_stream.quit();
72-
/// ```
73-
///
51+
52+
/// Creates an FTP Stream.
7453
#[cfg(feature = "secure")]
75-
pub fn secure(mut self) -> Result<FtpStream> {
76-
// Initialize SSL with a default context and make secure the stream.
77-
Ssl::new(&SSL_CONTEXT)
78-
.map_err(|e| FtpError::SecureError(e.description().to_owned()))
79-
.and_then(|ssl| self.secure_with_ssl(ssl))
54+
pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<FtpStream> {
55+
TcpStream::connect(addr)
56+
.map_err(|e| FtpError::ConnectionError(e))
57+
.and_then(|stream| {
58+
let mut ftp_stream = FtpStream {
59+
reader: BufReader::new(DataStream::Tcp(stream)),
60+
ssl_cfg: None,
61+
};
62+
ftp_stream.read_response(status::READY)
63+
.map(|_| ftp_stream)
64+
})
8065
}
81-
66+
8267
/// Switch to a secure mode if possible, using a provided SSL configuration.
8368
/// This method does nothing if the connect is already secured.
8469
///
@@ -96,22 +81,20 @@ impl FtpStream {
9681
/// let mut ctx = SslContext::new(SslMethod::Sslv23).unwrap();
9782
/// let _ = ctx.set_CA_file("/path/to/a/cert.pem").unwrap();
9883
/// let mut ftp_stream = FtpStream::connect("127.0.0.1:21").unwrap();
99-
/// let mut ftp_stream = ftp_stream.secure_with_ssl(ctx).unwrap();
84+
/// let mut ftp_stream = ftp_stream.into_secure(ctx).unwrap();
10085
/// ```
10186
#[cfg(feature = "secure")]
102-
pub fn secure_with_ssl<S: IntoSsl>(mut self, ssl: S) -> Result<FtpStream> {
103-
// Do nothing if the connection is already secured.
104-
if self.reader.get_ref().is_ssl() {
105-
return Ok(self);
106-
}
87+
pub fn into_secure<T: IntoSsl + Clone>(mut self, ssl: T) -> Result<FtpStream> {
10788
// Ask the server to start securing data.
10889
let auth_command = String::from("AUTH TLS\r\n");
10990
try!(self.write_str(&auth_command));
11091
try!(self.read_response(status::AUTH_OK));
92+
let ssl_copy = try!(ssl.clone().into_ssl().map_err(|e| FtpError::SecureError(e.description().to_owned())));
11193
let stream = try!(SslStream::connect(ssl, self.reader.into_inner().into_tcp_stream())
11294
.map_err(|e| FtpError::SecureError(e.description().to_owned())));
11395
let mut secured_ftp_tream = FtpStream {
11496
reader: BufReader::new(DataStream::Ssl(stream)),
97+
ssl_cfg: Some(ssl_copy)
11598
};
11699
// Set protection buffer size
117100
let pbsz_command = format!("PBSZ 0\r\n");
@@ -123,7 +106,7 @@ impl FtpStream {
123106
try!(secured_ftp_tream.read_response(status::COMMAND_OK));
124107
Ok(secured_ftp_tream)
125108
}
126-
109+
127110
/// Switch to insecure mode. If the connection is already
128111
/// insecure does nothing.
129112
///
@@ -140,21 +123,46 @@ impl FtpStream {
140123
/// // Do all public things
141124
/// let _ = ftp_stream.quit();
142125
/// ```
143-
///
144126
#[cfg(feature = "secure")]
145-
pub fn insecure(mut self) -> Result<FtpStream> {
146-
if !self.reader.get_ref().is_ssl() {
147-
return Ok(self);
148-
}
127+
pub fn into_insecure(mut self) -> Result<FtpStream> {
149128
// Ask the server to stop securing data
150129
let ccc_command = String::from("CCC\r\n");
151130
try!(self.write_str(&ccc_command));
152131
try!(self.read_response(status::COMMAND_OK));
153132
let plain_ftp_stream = FtpStream {
154133
reader: BufReader::new(DataStream::Tcp(self.reader.into_inner().into_tcp_stream())),
134+
ssl_cfg: None,
155135
};
156136
Ok(plain_ftp_stream)
157137
}
138+
139+
/// Execute command which send data back in a separate stream
140+
#[cfg(not(feature = "secure"))]
141+
fn data_command(&mut self, cmd: &str) -> Result<DataStream> {
142+
self.pasv()
143+
.and_then(|addr| self.write_str(cmd).map(|_| addr))
144+
.and_then(|addr| TcpStream::connect(addr)
145+
.map_err(|e| FtpError::ConnectionError(e)))
146+
.map(|stream| DataStream::Tcp(stream))
147+
}
148+
149+
/// Execute command which send data back in a separate stream
150+
#[cfg(feature = "secure")]
151+
fn data_command(&mut self, cmd: &str) -> Result<DataStream> {
152+
self.pasv()
153+
.and_then(|addr| self.write_str(cmd).map(|_| addr))
154+
.and_then(|addr| TcpStream::connect(addr).map_err(|e| FtpError::ConnectionError(e)))
155+
.and_then(|stream| {
156+
match self.ssl_cfg {
157+
Some(ref ssl) => {
158+
SslStream::connect(ssl.clone(), stream)
159+
.map(|stream| DataStream::Ssl(stream))
160+
.map_err(|e| FtpError::SecureError(e.description().to_owned()))
161+
},
162+
None => Ok(DataStream::Tcp(stream))
163+
}
164+
})
165+
}
158166

159167
/// Log in to the FTP server.
160168
pub fn login(&mut self, user: &str, password: &str) -> Result<()> {
@@ -242,33 +250,6 @@ impl FtpStream {
242250
})
243251
}
244252

245-
// Execute command which send data back in a separate stream
246-
#[cfg(not(feature = "secure"))]
247-
fn data_command(&mut self, cmd: &str) -> Result<DataStream> {
248-
self.pasv()
249-
.and_then(|addr| self.write_str(cmd).map(|_| addr))
250-
.and_then(|addr| TcpStream::connect(addr)
251-
.map_err(|e| FtpError::ConnectionError(e)))
252-
.map(|stream| DataStream::Tcp(stream))
253-
}
254-
255-
#[cfg(feature = "secure")]
256-
fn data_command(&mut self, cmd: &str) -> Result<DataStream> {
257-
self.pasv()
258-
.and_then(|addr| self.write_str(cmd).map(|_| addr))
259-
.and_then(|addr| TcpStream::connect(addr))
260-
.and_then(|stream| {
261-
if self.reader.get_ref().is_ssl() {
262-
Ssl::new(&SSL_CONTEXT)
263-
.and_then(|ssl| SslStream::connect(ssl, stream))
264-
.map(|stream| DataStream::Ssl(stream))
265-
.map_err(|e| FtpError::SecureError(e.description().to_owned()))
266-
} else {
267-
Ok(DataStream::Tcp(stream))
268-
}
269-
})
270-
}
271-
272253
/// Sets the type of file to be transferred. That is the implementation
273254
/// of `TYPE` command.
274255
pub fn transfer_type(&mut self, file_type: FileType) -> Result<()> {

src/types.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
//! The set of valid values for FTP commands
22
3-
#[cfg(feature = "secure")]
4-
use openssl::ssl;
53
use std::convert::From;
64
use std::error::Error;
75
use std::fmt;

0 commit comments

Comments
 (0)