Skip to content

Upgrade to latest OpenSSL #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 37 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
817e560
Update the example to be consistent with the API
Jul 18, 2016
658c515
Simplified the README example, relying on the convention that using u…
Jul 18, 2016
cb71a5b
Fixed the rest of the API references
Jul 18, 2016
34bcf31
Fixed syntax error
Jul 18, 2016
bdfb143
Tested program in a working environment and fixed last syntax errors
Jul 18, 2016
dfb2302
Fixed indentation
Jul 18, 2016
bc5ddf7
Fixed indentation
Jul 18, 2016
3e43795
Merge branch 'master' of github.com:zsck/rust-ftp
Jul 18, 2016
4255741
Merge branch 'master' of github.com:zsck/rust-ftp
Jul 18, 2016
d02bbcd
Add a secure_with_ssl method to FtpStream so that users can supply th…
Jul 21, 2016
50c132e
Merge branch 'secure_with_ssl'
Jul 21, 2016
a1425f3
Merge branch 'master' of github.com:zsck/rust-ftp
Jul 24, 2016
ebd7911
Merge upstream
Aug 3, 2016
45698bf
Add a field to FtpStream when compiling with the secure feature that …
Aug 3, 2016
d9b1456
Fixed compilation errors
Aug 3, 2016
338842b
Merge branch 'master' into persist-custom-ssl
Aug 3, 2016
49c9888
Make the openssl requirement mandatory. It's not worth it to fragment…
Aug 3, 2016
770234a
Since the secure() and insecure() methods actually have type signatur…
Aug 3, 2016
e434d35
Create a new FtpsStream<T> type and revert back to the old FtpStream …
Aug 3, 2016
8e5bd32
Let use of OpenSsl and indeed the entire FtpsStream type be optional …
Aug 3, 2016
313bdb6
Move back to a single-type model with an SSL field that is only prese…
Aug 6, 2016
ff399cf
Merge remote-tracking branch 'upstream/master'
Aug 24, 2016
d88f80d
Have SecureError contain an openssl::ssl::error::SslError instead of …
Aug 24, 2016
d55d782
Give the original SSL object to the FtpStream
Aug 26, 2016
3962229
fix my dumb mistake
Aug 26, 2016
0e71ecb
Try some debug logging
Aug 26, 2016
1ce6887
Update to the latest rust-openssl
Aug 26, 2016
723568e
No longer a single error type in new openssl, so have SecureError con…
Aug 26, 2016
72ae558
Have FtpStream contain an SslContext since it can be cloned, unlike S…
Aug 26, 2016
8892976
Add a usable FTP server to test against, and a cert and private key t…
Aug 26, 2016
3860e5a
Add an example FTP client that uses our test certificate to connect t…
Aug 26, 2016
a6ccf9a
Use the latest OpenSSL
Dec 30, 2016
0ee21ba
Use the latest OpenSSL
Dec 30, 2016
ecaf685
remove no-longer-supported openssl dep flag
Dec 30, 2016
471b3c7
Upgrade to properly use rust-openssl 0.9.x
Jan 3, 2017
854a62c
Have example use proper rust-openssl 0.9.x code
Jan 3, 2017
93b9b3a
A tiny bit of cleanup
Jan 3, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "ftp"
version = "2.0.0"
authors = ["Matt McCoy <[email protected]>"]
version = "2.0.1"
authors = ["Matt McCoy <[email protected]>", "Zack Mullaly <[email protected]>"]
documentation = "http://mattnenterprise.github.io/rust-ftp"
repository = "https://github.com/mattnenterprise/rust-ftp"
description = "FTP client for Rust"
Expand All @@ -27,5 +27,5 @@ regex = "0.1"
chrono = "0.2"

[dependencies.openssl]
version = "0.7"
version = "^0.9"
optional = true
33 changes: 33 additions & 0 deletions examples/encrypted.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#[cfg(feature = "secure")]

extern crate ftp;
extern crate openssl;

use ftp::FtpStream;
use openssl::ssl::{
SslContext,
SslMethod,
SSL_OP_NO_SSLV2,
SSL_OP_NO_SSLV3,
SSL_OP_NO_COMPRESSION,
};

fn main() {
let mut builder = SslContext::builder(SslMethod::tls()).unwrap();
builder.set_certificate_file("./tests/test.crt", openssl::x509::X509_FILETYPE_PEM).unwrap();
builder.set_options(SSL_OP_NO_SSLV2 | SSL_OP_NO_SSLV3 | SSL_OP_NO_COMPRESSION);
builder.set_cipher_list("ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH").unwrap();
let ctx = builder.build();
let result = FtpStream::connect("127.0.0.1:21")
.and_then(|mut client| client.login("anonymous", "").map(|_| client))
.and_then(|client| client.into_secure(ctx))
.and_then(|mut client| client.list(None));
match result {
Ok(dir) => {
for file in dir.iter() {
println!("{}", file);
}
},
Err(err) => println!("Error: {:?}", err)
}
}
48 changes: 22 additions & 26 deletions src/ftp.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use std::io::{Read, BufRead, BufReader, BufWriter, Cursor, Write, copy};
#[cfg(feature = "secure")]
use std::error::Error;
use std::net::{TcpStream, SocketAddr};
use std::string::String;
use std::str::FromStr;
Expand All @@ -9,7 +7,7 @@ use regex::Regex;
use chrono::{DateTime, UTC};
use chrono::offset::TimeZone;
#[cfg(feature = "secure")]
use openssl::ssl::{Ssl, SslStream, IntoSsl};
use openssl::ssl::{Ssl, SslContext, SslStream};
use super::data_stream::DataStream;
use super::status;
use super::types::{FileType, FtpError, Line, Result};
Expand All @@ -31,7 +29,7 @@ lazy_static! {
pub struct FtpStream {
reader: BufReader<DataStream>,
#[cfg(feature = "secure")]
ssl_cfg: Option<Ssl>,
ssl_cfg: Option<SslContext>,
}

impl FtpStream {
Expand Down Expand Up @@ -84,17 +82,18 @@ impl FtpStream {
/// let mut ftp_stream = ftp_stream.into_secure(ctx).unwrap();
/// ```
#[cfg(feature = "secure")]
pub fn into_secure<T: IntoSsl + Clone>(mut self, ssl: T) -> Result<FtpStream> {
pub fn into_secure(mut self, ssl: SslContext) -> Result<FtpStream> {
// Ask the server to start securing data.
let auth_command = String::from("AUTH TLS\r\n");
try!(self.write_str(&auth_command));
try!(self.read_response(status::AUTH_OK));
let ssl_copy = try!(ssl.clone().into_ssl().map_err(|e| FtpError::SecureError(e.description().to_owned())));
let stream = try!(SslStream::connect(ssl, self.reader.into_inner().into_tcp_stream())
.map_err(|e| FtpError::SecureError(e.description().to_owned())));
//let ssl_copy = ssl.clone();
let ssl_cfg = try!(Ssl::new(&ssl).map_err(|e| FtpError::SecureError(Box::new(e))));
let tcp_stream = self.reader.into_inner().into_tcp_stream();
let stream = try!(ssl_cfg.connect(tcp_stream).map_err(|e| FtpError::SecureError(Box::new(e))));
let mut secured_ftp_tream = FtpStream {
reader: BufReader::new(DataStream::Ssl(stream)),
ssl_cfg: Some(ssl_copy)
ssl_cfg: Some(ssl),
};
// Set protection buffer size
let pbsz_command = format!("PBSZ 0\r\n");
Expand Down Expand Up @@ -149,19 +148,16 @@ impl FtpStream {
/// 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) => {
SslStream::connect(ssl.clone(), stream)
.map(|stream| DataStream::Ssl(stream))
.map_err(|e| FtpError::SecureError(e.description().to_owned()))
},
None => Ok(DataStream::Tcp(stream))
}
})
let addr = try!(self.pasv());
try!(self.write_str(cmd));
let stream = try!(TcpStream::connect(addr).map_err(|e| FtpError::ConnectionError(e)));
if let Some(ref ssl) = self.ssl_cfg {
let ssl_cfg = try!(Ssl::new(ssl).map_err(|e| FtpError::SecureError(Box::new(e))));
let ssl_stream = try!(ssl_cfg.connect(stream).map_err(|e| FtpError::SecureError(Box::new(e))));
Ok(DataStream::Ssl(ssl_stream))
} else {
Ok(DataStream::Tcp(stream))
}
}

/// Log in to the FTP server.
Expand Down Expand Up @@ -479,10 +475,10 @@ impl FtpStream {
return Err(FtpError::InvalidResponse("error: could not read reply code".to_owned()));
}

let code: u32 = try!(line[0..3].parse()
.map_err(|err| {
FtpError::InvalidResponse(format!("error: could not parse reply code: {}", err))
}));
let code: u32 = try!(
line[0..3].parse().map_err(|err| {
FtpError::InvalidResponse(format!("error: could not parse reply code: {}", err))
}));

// multiple line reply
// loop while the line does not begin with the code and a space
Expand Down
14 changes: 9 additions & 5 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ pub type Result<T> = ::std::result::Result<T, FtpError>;
#[derive(Debug)]
pub enum FtpError {
ConnectionError(::std::io::Error),
SecureError(String),
InvalidResponse(String),
InvalidAddress(::std::net::AddrParseError),
#[cfg(feature = "secure")]
SecureError(Box<Error>),
}

/// Text Format Control used in `TYPE` command
Expand Down Expand Up @@ -74,9 +75,10 @@ impl fmt::Display for FtpError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FtpError::ConnectionError(ref ioerr) => write!(f, "FTP ConnectionError: {}", ioerr),
FtpError::SecureError(ref desc) => write!(f, "FTP SecureError: {}", desc.clone()),
FtpError::InvalidResponse(ref desc) => write!(f, "FTP InvalidResponse: {}", desc.clone()),
FtpError::InvalidAddress(ref perr) => write!(f, "FTP InvalidAddress: {}", perr),
#[cfg(feature = "secure")]
FtpError::SecureError(ref desc) => write!(f, "FTP SecureError: {}", desc.clone()),
}
}
}
Expand All @@ -85,18 +87,20 @@ impl Error for FtpError {
fn description(&self) -> &str {
match *self {
FtpError::ConnectionError(ref ioerr) => ioerr.description(),
FtpError::SecureError(ref desc) => desc.as_str(),
FtpError::InvalidResponse(ref desc) => desc.as_str(),
FtpError::InvalidAddress(ref perr) => perr.description(),
#[cfg(feature = "secure")]
FtpError::SecureError(ref sslerr) => sslerr.description(),
}
}

fn cause(&self) -> Option<&Error> {
match *self {
FtpError::ConnectionError(ref ioerr) => Some(ioerr),
FtpError::SecureError(_) => None,
FtpError::InvalidResponse(_) => None,
FtpError::InvalidAddress(ref perr) => Some(perr)
FtpError::InvalidAddress(ref perr) => Some(perr),
#[cfg(feature = "secure")]
FtpError::SecureError(_) => None,
}
}
}
22 changes: 22 additions & 0 deletions tests/ftp-server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os

from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import TLS_FTPHandler
from pyftpdlib.servers import FTPServer
from pyftpdlib.filesystems import AbstractedFS

authorizer = DummyAuthorizer()
authorizer.add_anonymous(os.getcwd())

handler = TLS_FTPHandler
handler.keyfile = './test.key'
handler.certfile = './test.crt'
handler.authorizer = authorizer
handler.passive_ports = range(60000, 65535)

# Instantiate FTP server class and listen on 0.0.0.0:21
address = ('', 21)
server = FTPServer(address, handler)

# start ftp server
server.serve_forever()
27 changes: 27 additions & 0 deletions tests/test.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEoTCCA4mgAwIBAgIJAPEIZ11cXg7IMA0GCSqGSIb3DQEBBQUAMIGRMQswCQYD
VQQGEwJWQTERMA8GA1UECBMIVmlyZ2luaWExEDAOBgNVBAcTB0hlcm5kb24xHTAb
BgNVBAoTFFN0cmF0dW0gU2VjdXJpdHkgTExDMQ0wCwYDVQQLEwRYRklMMRIwEAYD
VQQDEwkxMjcuMC4wLjExGzAZBgkqhkiG9w0BCQEWDGRldkB4ZmlsLmNvbTAeFw0x
NjA4MjYxNDIzMzRaFw0xNzA4MjYxNDIzMzRaMIGRMQswCQYDVQQGEwJWQTERMA8G
A1UECBMIVmlyZ2luaWExEDAOBgNVBAcTB0hlcm5kb24xHTAbBgNVBAoTFFN0cmF0
dW0gU2VjdXJpdHkgTExDMQ0wCwYDVQQLEwRYRklMMRIwEAYDVQQDEwkxMjcuMC4w
LjExGzAZBgkqhkiG9w0BCQEWDGRldkB4ZmlsLmNvbTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMlM1YlC3nyK7ewmX2xJXWXe95QVxUwjEpPhZAB8a/Kf
jjJ6I7LpqITfq/aICMQPgYnmqeQQfX1wmPGSZUzRTmW7A/P0Ba0+XlcbZhJlN3M6
ZlbnIdv5rVSfcZp1E43upYdZK3Aqj92JBjkNjrQ4Bo5NmrQu75M5qcSvMtADSZQa
ZONkrTbLVbZaXtR9kH+6onYMggWIpYpwur8+cfzi3FbLziJbUicYVCuaz8ySxJ+T
h+MqBWDJjqoF5I5ZtCuivbhjwQjt5d/lq3by4NR8ENmyvIEb06TMfc2TYK33V2Hh
9ZxOEbTp71fz7JLrtIbqVSR6ncpcEeGet7acvdbIa9ECAwEAAaOB+TCB9jAdBgNV
HQ4EFgQU0Tj9SReQeiLeyWm4gMP9mmGzMBYwgcYGA1UdIwSBvjCBu4AU0Tj9SReQ
eiLeyWm4gMP9mmGzMBahgZekgZQwgZExCzAJBgNVBAYTAlZBMREwDwYDVQQIEwhW
aXJnaW5pYTEQMA4GA1UEBxMHSGVybmRvbjEdMBsGA1UEChMUU3RyYXR1bSBTZWN1
cml0eSBMTEMxDTALBgNVBAsTBFhGSUwxEjAQBgNVBAMTCTEyNy4wLjAuMTEbMBkG
CSqGSIb3DQEJARYMZGV2QHhmaWwuY29tggkA8QhnXVxeDsgwDAYDVR0TBAUwAwEB
/zANBgkqhkiG9w0BAQUFAAOCAQEAbRxSQ7jTCRepM/SgqEkKOgJ0zadPLimW8JRS
y1L86pVFm88crxb79xqfFMmWFPcqHKjNDoQ4qxm69u1Os+MKAYn6uvH4RQYidv0K
y0JbRkaTNUmcySxUlEYvj3QJU6AbbiyZluGbXtT6NjrIvhO7UBhQWSDa5IHOf2K7
VK9/EHllgm12V5eAyO+qf91Me/8PMRjK3xVRwO6NAx2yo48yZISJvNmNIqx20GKz
F3s19qtYvAz1Y/WvbjcpZqbnihwd9F3EFgGPwD6bhwZjxMqoBDtxDH0whdkEyCnl
Bsk5L0YMTvRyjUi+zhPKGSRxStyaarQti9iqtj486kqh6xhgfA==
-----END CERTIFICATE-----
27 changes: 27 additions & 0 deletions tests/test.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAyUzViULefIrt7CZfbEldZd73lBXFTCMSk+FkAHxr8p+OMnoj
sumohN+r9ogIxA+Bieap5BB9fXCY8ZJlTNFOZbsD8/QFrT5eVxtmEmU3czpmVuch
2/mtVJ9xmnUTje6lh1krcCqP3YkGOQ2OtDgGjk2atC7vkzmpxK8y0ANJlBpk42St
NstVtlpe1H2Qf7qidgyCBYilinC6vz5x/OLcVsvOIltSJxhUK5rPzJLEn5OH4yoF
YMmOqgXkjlm0K6K9uGPBCO3l3+WrdvLg1HwQ2bK8gRvTpMx9zZNgrfdXYeH1nE4R
tOnvV/Pskuu0hupVJHqdylwR4Z63tpy91shr0QIDAQABAoIBAQClmwxhrB2VoEY0
bS07zO+Fi3Vq4q46APCbsGWw8KtuI028wTb1Tb1R8yFp5Ggxw//yD03dTqOuux9Y
PfQQynEQyFZsMGkrKZA7YVML9zEzHuxquiPk8PdkEvhG9eJsddTAEN/nm1xYCQ6R
iVHJef4KvFV1vtdh66J7KRdIgivaJpce2joPskK4ddbk41ROxlSj/y4nbA2I020r
CptRMcOnt8zvMslAEBroo1WAvQGRtBrtFvPIo3bv2EoXpiM08tCyESvqYcTb3lLR
Nx6P5QesbC0XAEH58s61OJ2khNXJYLKIk3w2vYQW7oLyWcA9JZfVH/grGNsjdamp
UiGyavcBAoGBAOb58yULdpKhYB3yO/msTBDiWS81RY4c+k4Ap0WAI4nkpxK+I6SL
TwGu0IytBQDyIMh1liBYVTqnWCZEVKEtVrOGreqmDwq50ou6ESxEM/OunNy0kGXo
5r3r+C9Z29Q8+M4q7bOXqcpWRoq8c3UCMErblPksut37Jk6/Yw+F1IDJAoGBAN8b
0v9DG7JzWUHNLtZ/bzx14hKk/JY3pvqp7YCk35YXE7YsXKKsoFddcRnov5vWXn+u
+p74u3ApH4mHO0jD9iD//3/9zavjOZcNCjLK4CHLqvVNa+Cgz3fcjrN1tmKeVkkS
k0OIfje2SJ41+s8u2ld+0VV6eD6I2jwvrAoaud7JAoGBALtgXh3ZVNHTVJQ2pO0B
F5xC47Lmdoy5eV26Lfi14R7Gfbs4wNWFpgxpcwoMepiv1GSK78VBo7K3e01f9X/j
tghh48kN+fnrkaCEy9WrZrHzH5H0cYgbDsVYHrjbHvjolbc7ICanjLh2kTePdeKg
aejwhcQ2w0m9qvALVyOKoD/xAoGAVDgpnugTNXqrb9ZnXtr9/4G0VDtpib76TlcE
63HRYNPXQgZe9Z1abYA9aH1ejxIN2/8OZiIYh09Os1iT/XTTnUNljEgfcko0/BsV
BXVlw/wgzbZrCYFKr8FXMNE3huSkR7M2WeDVXGx33xkbU0gpbavWk4DGkTyRvPR1
6d6K2VkCgYBkg3b1oeQV+NKc6RMYIJIrfVX6TaP44eRkz4UclGqgyRNLa7LbI68g
J1sQNateIhSfMLPy0y0K62LB25+Ev7Q0FdDH0a+8EHCYojSSmbMxL8KIQMxwcwLI
6uk9ouMt4wr0q007qMLUqGZberhkeDnAbJamlOB3CIeqpWhu8JXT8A==
-----END RSA PRIVATE KEY-----