Skip to content
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

fix: Passive mode with custom provided TcpStream #91

Merged
merged 3 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
41 changes: 9 additions & 32 deletions .github/workflows/cargo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,50 +10,27 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
override: true
components: rustfmt, clippy
- name: Setup containers
run: docker-compose -f "tests/docker-compose.yml" up -d --build
run: docker compose -f "tests/docker-compose.yml" up -d --build
- name: Build simple
uses: actions-rs/cargo@v1
with:
command: build
args: --package suppaftp
run: cargo build --package suppaftp
- name: Build secure (native-tls)
uses: actions-rs/cargo@v1
with:
command: build
args: --features native-tls,deprecated --package suppaftp
run: cargo build --features native-tls,deprecated --package suppaftp
- name: Build secure (rustls)
uses: actions-rs/cargo@v1
with:
command: build
args: --features rustls,deprecated --package suppaftp
run: cargo build --features rustls,deprecated --package suppaftp
- name: Build async
uses: actions-rs/cargo@v1
with:
command: build
args: --features async,deprecated --package suppaftp
run: cargo build --features async,deprecated --package suppaftp
- name: Build async-native-tls
uses: actions-rs/cargo@v1
with:
command: build
args: --features async-native-tls,deprecated --package suppaftp
run: cargo build --features async-native-tls,deprecated --package suppaftp
- name: Build all features
uses: actions-rs/cargo@v1
with:
command: build
args: --features deprecated,native-tls,rustls,async-native-tls,async-rustls --package suppaftp
run: cargo build --features deprecated,native-tls,rustls,async-native-tls,async-rustls --package suppaftp
- name: Run tests
uses: actions-rs/cargo@v1
with:
command: test
args: --lib --package suppaftp --no-default-features --features rustls,native-tls,async-native-tls,async-rustls,with-containers --no-fail-fast
env:
RUST_LOG: trace
run: cargo test --lib --package suppaftp --no-default-features --features rustls,native-tls,async-native-tls,async-rustls,with-containers --no-fail-fast
- name: Format
run: cargo fmt --all -- --check
- name: Clippy
Expand Down
7 changes: 2 additions & 5 deletions .github/workflows/cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,13 @@ jobs:
working-directory: ./suppaftp-cli
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
override: true
components: rustfmt, clippy
- name: Build
uses: actions-rs/cargo@v1
with:
command: build
args: --package suppaftp-cli
run: cargo build --package suppaftp-cli
- name: Format
run: cargo fmt --all -- --check
- name: Clippy
Expand Down
9 changes: 3 additions & 6 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,14 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Setup containers
run: docker-compose -f "tests/docker-compose.yml" up -d --build
run: docker compose -f "tests/docker-compose.yml" up -d --build
- name: Setup nightly toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@stable
with:
toolchain: nightly
override: true
- name: Run tests (nightly)
uses: actions-rs/cargo@v1
with:
command: test
args: --lib --no-default-features --features native-tls,deprecated,async-native-tls,with-containers --no-fail-fast
run: cargo test --lib --package suppaftp --no-default-features --features native-tls,deprecated,async-native-tls,with-containers --no-fail-fast
env:
RUST_LOG: trace
CARGO_INCREMENTAL: "0"
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

- [Changelog](#changelog)
- [6.0.2](#602)
- [6.0.1](#601)
- [6.0.0](#600)
- [5.4.0](#540)
Expand Down Expand Up @@ -34,6 +35,12 @@

---

## 6.0.2

Released on 14/10/2024

- [Issue 89](https://github.com/veeso/suppaftp/issues/89): added new `FtpStream::passive_stream_builder` to provide a function to build the Passive mode `TcpStream` with a custom builder. This is useful if you need to use some proxy.

## 6.0.1

Released on 24/05/2024
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["suppaftp", "suppaftp-cli"]
resolver = "2"

[workspace.package]
version = "6.0.1"
version = "6.0.2"
edition = "2021"
authors = [
"Christian Visintin <christian.visintin@veeso.dev>",
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</p>

<p align="center">Developed by <a href="https://veeso.github.io/">veeso</a> and <a href="https://github.com/mattnenterprise">Matt McCoy</a></p>
<p align="center">Current version: 6.0.1 (24/05/2024)</p>
<p align="center">Current version: 6.0.2 (14/10/2024)</p>

<p align="center">
<a href="https://opensource.org/licenses/MIT"
Expand Down
2 changes: 1 addition & 1 deletion suppaftp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async-tls = { version = "^0.13", optional = true }
pin-project = { version = "^1", optional = true }
# secure
native-tls-crate = { package = "native-tls", version = "^0.2", optional = true }
rustls-crate = { package = "rustls", version = "^0.21", optional = true }
rustls-crate = { package = "rustls", version = "^0.23", optional = true }
futures-lite = "2.0.0"

[dev-dependencies]
Expand Down
59 changes: 53 additions & 6 deletions suppaftp/src/async_ftp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
mod data_stream;
mod tls;

use std::future::Future;
#[cfg(not(feature = "async-secure"))]
use std::marker::PhantomData;
use std::net::{Ipv4Addr, SocketAddr};
use std::pin::Pin;
use std::string::String;
use std::time::Duration;

Expand Down Expand Up @@ -35,6 +37,12 @@ use crate::command::Command;
use crate::command::ProtectionLevel;
use crate::types::Features;

/// A function that creates a new stream for the data connection in passive mode.
///
/// It takes a [`SocketAddr`] and returns a [`TcpStream`].
pub type PassiveStreamBuilder =
dyn Fn(SocketAddr) -> Pin<Box<dyn Future<Output = FtpResult<TcpStream>> + Send>>;

/// Stream to interface with the FTP server. This interface is only for the command stream.
pub struct ImplAsyncFtpStream<T>
where
Expand All @@ -45,6 +53,7 @@ where
nat_workaround: bool,
welcome_msg: Option<String>,
active_timeout: Duration,
passive_stream_builder: Box<PassiveStreamBuilder>,
#[cfg(not(feature = "async-secure"))]
marker: PhantomData<T>,
#[cfg(feature = "async-secure")]
Expand Down Expand Up @@ -85,6 +94,7 @@ where
marker: PhantomData {},
mode: Mode::Passive,
nat_workaround: false,
passive_stream_builder: Self::default_passive_stream_builder(),
welcome_msg: None,
#[cfg(feature = "async-secure")]
tls_ctx: None,
Expand Down Expand Up @@ -143,6 +153,7 @@ where
reader: BufReader::new(DataStream::Ssl(Box::new(stream))),
mode: self.mode,
nat_workaround: self.nat_workaround,
passive_stream_builder: self.passive_stream_builder,
tls_ctx: Some(Box::new(tls_connector)),
domain: Some(String::from(domain)),
welcome_msg: self.welcome_msg,
Expand Down Expand Up @@ -197,6 +208,7 @@ where
mode: Mode::Passive,
nat_workaround: false,
welcome_msg: None,
passive_stream_builder: Self::default_passive_stream_builder(),
tls_ctx: None,
domain: None,
active_timeout: Duration::from_secs(60),
Expand All @@ -213,6 +225,7 @@ where
reader: BufReader::new(DataStream::Ssl(stream.into())),
mode: Mode::Passive,
nat_workaround: false,
passive_stream_builder: Self::default_passive_stream_builder(),
tls_ctx: Some(Box::new(tls_connector)),
domain: Some(String::from(domain)),
welcome_msg: None,
Expand All @@ -238,6 +251,18 @@ where
self
}

/// Set a custom [`StreamBuilder`] for passive mode.
///
/// The stream builder is a function that takes a `SocketAddr` and returns a `TcpStream` and it's used
/// to create the [`TcpStream`] for the data connection in passive mode.
pub fn passive_stream_builder<F>(mut self, stream_builder: F) -> Self
where
F: Fn(SocketAddr) -> Pin<Box<dyn Future<Output = FtpResult<TcpStream>> + Send>> + 'static,
{
self.passive_stream_builder = Box::new(stream_builder);
self
}

/// Returns welcome message retrieved from server (if available)
pub fn get_welcome_msg(&self) -> Option<&str> {
self.welcome_msg.as_deref()
Expand Down Expand Up @@ -767,16 +792,12 @@ where
Mode::ExtendedPassive => {
let addr = self.epsv().await?;
self.perform(cmd).await?;
TcpStream::connect(addr)
.await
.map_err(FtpError::ConnectionError)?
(self.passive_stream_builder)(addr).await?
}
Mode::Passive => {
let addr = self.pasv().await?;
self.perform(cmd).await?;
TcpStream::connect(addr)
.await
.map_err(FtpError::ConnectionError)?
(self.passive_stream_builder)(addr).await?
}
};

Expand Down Expand Up @@ -1002,6 +1023,16 @@ where
self.finalize_retr_stream(data_stream).await?;
lines
}

fn default_passive_stream_builder() -> Box<PassiveStreamBuilder> {
Box::new(|address| {
Box::pin(async move {
TcpStream::connect(address)
.await
.map_err(FtpError::ConnectionError)
})
})
}
}

#[cfg(test)]
Expand Down Expand Up @@ -1400,6 +1431,22 @@ mod test {
finalize_stream(stream).await;
}

#[async_attributes::test]
async fn test_should_set_passive_stream_builder() {
crate::log_init();
let _ftp_stream = AsyncFtpStream::connect("test.rebex.net:21")
.await
.unwrap()
.passive_stream_builder(|addr| {
Box::pin(async move {
println!("Connecting to {}", addr);
TcpStream::connect(addr)
.await
.map_err(FtpError::ConnectionError)
})
});
}

// -- test utils

#[cfg(feature = "with-containers")]
Expand Down
Loading
Loading