Skip to content

Commit

Permalink
Merge pull request #64 from kyrias/direct-tcpip-channel
Browse files Browse the repository at this point in the history
Add support for direct-tcpip channels
  • Loading branch information
Miyoshi-Ryota authored Jul 28, 2024
2 parents c08a763 + 92a4c3f commit ea67486
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 0 deletions.
69 changes: 69 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,48 @@ impl Client {
.map_err(crate::Error::SshError)
}

/// Open a TCP/IP forwarding channel.
///
/// This opens a `direct-tcpip` channel to the given target.
pub async fn open_direct_tcpip_channel<
T: ToSocketAddrsWithHostname,
S: Into<Option<SocketAddr>>,
>(
&self,
target: T,
src: S,
) -> Result<Channel<Msg>, crate::Error> {
let targets = target
.to_socket_addrs()
.map_err(crate::Error::AddressInvalid)?;
let src = src
.into()
.map(|src| (src.ip().to_string(), src.port().into()))
.unwrap_or_else(|| ("127.0.0.1".to_string(), 22));

let mut connect_err = crate::Error::AddressInvalid(io::Error::new(
io::ErrorKind::InvalidInput,
"could not resolve to any addresses",
));
for target in targets {
match self
.connection_handle
.channel_open_direct_tcpip(
target.ip().to_string(),
target.port().into(),
src.0.clone(),
src.1,
)
.await
{
Ok(channel) => return Ok(channel),
Err(err) => connect_err = crate::Error::SshError(err),
}
}

return Err(connect_err);
}

/// Upload a file with sftp to the remote server.
///
/// `src_file_path` is the path to the file on the local machine.
Expand Down Expand Up @@ -572,6 +614,8 @@ impl Handler for ClientHandler {
mod tests {
use core::time;

use tokio::io::AsyncReadExt;

use crate::client::*;

fn env(name: &str) -> String {
Expand Down Expand Up @@ -674,6 +718,31 @@ ASYNC_SSH2_TEST_UPLOAD_FILE
assert_eq!("Hello World\n", output);
}

#[tokio::test]
async fn direct_tcpip_channel() {
let client = establish_test_host_connection().await;
let channel = client
.open_direct_tcpip_channel(
format!(
"{}:{}",
env("ASYNC_SSH2_TEST_HTTP_SERVER_IP"),
env("ASYNC_SSH2_TEST_HTTP_SERVER_PORT"),
),
None,
)
.await
.unwrap();

let mut stream = channel.into_stream();
stream.write_all(b"GET / HTTP/1.0\r\n\r\n").await.unwrap();

let mut response = String::new();
stream.read_to_string(&mut response).await.unwrap();

let body = response.split_once("\r\n\r\n").unwrap().1;
assert_eq!("Hello", body);
}

#[tokio::test]
async fn stderr_redirection() {
let client = establish_test_host_connection().await;
Expand Down
2 changes: 2 additions & 0 deletions tests/async-ssh2-tokio/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ FROM rust:1.71.0
ENV ASYNC_SSH2_TEST_HOST_IP=10.10.10.2
ENV ASYNC_SSH2_TEST_HOST_USER=root
ENV ASYNC_SSH2_TEST_HOST_PW=root
ENV ASYNC_SSH2_TEST_HTTP_SERVER_IP=10.10.10.4
ENV ASYNC_SSH2_TEST_HTTP_SERVER_PORT=8000
ENV ASYNC_SSH2_TEST_CLIENT_PRIV=/root/.ssh/id_ed25519
ENV ASYNC_SSH2_TEST_CLIENT_PROT_PRIV=/root/.ssh/prot.id_ed25519
ENV ASYNC_SSH2_TEST_CLIENT_PROT_PASS=test
Expand Down
12 changes: 12 additions & 0 deletions tests/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ services:
networks:
ssh-network:
ipv4_address: 10.10.10.2
# The HTTP server is used for the `direct-tcpip` channel test.
http-server:
image: python:alpine
command: >
sh -c "
echo -n Hello > index.html &&
python -m http.server
"
networks:
ssh-network:
ipv4_address: 10.10.10.4
async-ssh2-tokio:
build: # Change build context to be copy async-ssh2-tokio which is located parent directory.
context: ../
Expand All @@ -19,6 +30,7 @@ services:
ipv4_address: 10.10.10.3
depends_on:
- ssh-server
- http-server
networks:
ssh-network:
driver: bridge
Expand Down

0 comments on commit ea67486

Please sign in to comment.