Description
Hello !
Context
We were working on a fork of your repo to allow ssl session reuse and planning to offer a PR once stabilized, however there's no way to implement this since #92 as native_tls doesn't offer an interface to use sessions reuse. This is actually quite problematique as some servers require to use session reuse when in passive mode.
Thoughts around native_tls
Apparently the change to native_tls was made in #92 to make it easier to run on Windows and MacOs machine
. As I use ArchLinux I have no idea how much easier it is, however I would like to point out that more than 90% of cloud based webservers run on linux. Since native_tls uses three different crates to implement ssl the configuration available is actually minimal when openssl provides a large set of parameters. This can be easily seen in the number of functions in the builders, 61 for SslConnectorBuilder versus 9 for TlsConnectorBuilder. As an example, I've added at the end how we can implement session reuse with openssl. Therefore I'm not sure the flexibility loss is worth it and would love to read more arguments againts it.
Next Steps
I see three possible solutions:
- Revert to openssl (I can provide a PR)
- Use two different feature flags
secure-openssl
andsecure-native-tls
that would basically use openssl or native_tls (I can provide a PR) - This crate stays on native_tls and we work on a fork.
Solution 1
Pros
- Easy to maintain
- Allows more flexibility
- Always use the same implementation of SSL (easier to debug)
- We'll provide updates to add features when required as FIX - Impl Error for FtpError #95
Cons
- Running on Microsoft & MacOs would be more complicated
Solution 2
Pros
- Keep it easy on Microsoft & MacOs
- Allows more flexibility
- We'll provide updates to add features when required as FIX - Impl Error for FtpError #95
Cons
- Maintainance is twice as hard
Solution 3
Pros
- Easy to maintain
- Keep it easy on Microsoft & MacOs
Cons
- Lot of flexibility lost
- We'll probably maintain only our fork
@mattnenterprise Please tell me which which solution you prefer 🙂
Thanks and have a nice day !
Implementing Session Reuse
Basically it ends up in replacing the function data_command
(taken from version 3.0.1) from:
/// Execute command which send data back in a separate stream
#[cfg(feature = "secure")]
fn data_command(&mut self, cmd: &str) -> Result<DataStream> {
self.pasv()
.and_then(|addr| self.write_str(cmd).map(|_| addr))
.and_then(|addr| TcpStream::connect(addr).map_err(|e| FtpError::ConnectionError(e)))
.and_then(|stream| {
match self.ssl_cfg {
Some(ref ssl) => {
Ssl::new(ssl).unwrap().connect(stream)
.map(|stream| DataStream::Ssl(stream))
.map_err(|e| FtpError::SecureError(e.description().to_owned()))
},
None => Ok(DataStream::Tcp(stream))
}
})
}
To:
/// Execute command which send data back in a separate stream
#[cfg(feature = "secure")]
fn data_command(&mut self, cmd: &str) -> Result<DataStream> {
self.pasv()
.and_then(|addr| self.write_str(cmd).map(|_| addr))
.and_then(|addr| TcpStream::connect(addr).map_err(|e| FtpError::ConnectionError(e)))
.and_then(|stream| match self.ssl_cfg {
Some(ref ssl) => {
let mut ssl = Ssl::new(ssl).unwrap();
match self.reader.get_ref() {
DataStream::Tcp(_) => {}
DataStream::Ssl(ssl_stream) => unsafe {
// SAFETY: ssl_stream was also using the context from self.ssl_cfg
ssl.set_session(ssl_stream.ssl().session().unwrap())
.map_err(|e| FtpError::SecureError(format!("{}", e)))?
},
}
ssl.connect(stream)
.map(|stream| DataStream::Ssl(stream))
.map_err(|e| FtpError::SecureError(format!("{}", e)))
}
None => Ok(DataStream::Tcp(stream)),
})
}