Skip to content

Commit a3fc969

Browse files
authored
Feat: Closing an existing port forward (#165)
* Implement cancel_port_forwarding for process-mux * Update doc comment * Fix test * Fix test * Ignore cancel tests for native-mux * Fix local forwarding test * Update cfg * Update cfg * Update cfg * Update cfg * Update cfg * Update cfg * Rename API and to `read` 0 test in remote forward test * Fix native mux impl * Try `UnixStream::connect` * What is getting read in? * Extra LF by echo? * `try_read` would block * Update `openssh-mux-client` in Cargo.toml
1 parent 94b63e6 commit a3fc969

File tree

5 files changed

+96
-5
lines changed

5 files changed

+96
-5
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ tokio = { version = "1.36.0", features = [ "process", "io-util", "macros", "net"
4545

4646
once_cell = "1.8.0"
4747

48-
openssh-mux-client = { version = "0.17.0", optional = true }
48+
openssh-mux-client = { version = "0.17.6", optional = true }
4949

5050
libc = "0.2.137"
5151

src/native_mux_impl/session.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,24 @@ impl Session {
6767
Ok(())
6868
}
6969

70+
pub(crate) async fn close_port_forward(
71+
&self,
72+
forward_type: crate::ForwardType,
73+
listen_socket: crate::Socket<'_>,
74+
connect_socket: crate::Socket<'_>,
75+
) -> Result<(), Error> {
76+
Connection::connect(&self.ctl)
77+
.await?
78+
.close_port_forward(
79+
forward_type.into(),
80+
&listen_socket.into(),
81+
&connect_socket.into(),
82+
)
83+
.await?;
84+
85+
Ok(())
86+
}
87+
7088
async fn close_impl(&self) -> Result<(), Error> {
7189
Connection::connect(&self.ctl)
7290
.await?

src/process_impl/session.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,43 @@ impl Session {
139139
}
140140
}
141141

142+
pub(crate) async fn close_port_forward(
143+
&self,
144+
forward_type: ForwardType,
145+
listen_socket: Socket<'_>,
146+
connect_socket: Socket<'_>,
147+
) -> Result<(), Error> {
148+
let flag = match forward_type {
149+
ForwardType::Local => OsStr::new("-L"),
150+
ForwardType::Remote => OsStr::new("-R"),
151+
};
152+
153+
let mut forwarding = listen_socket.as_os_str().into_owned();
154+
forwarding.push(":");
155+
forwarding.push(connect_socket.as_os_str());
156+
157+
let port_forwarding = self
158+
.new_cmd(&[OsStr::new("-O"), OsStr::new("cancel"), flag, &*forwarding])
159+
.output()
160+
.await
161+
.map_err(Error::Ssh)?;
162+
163+
if port_forwarding.status.success() {
164+
Ok(())
165+
} else {
166+
let exit_err = String::from_utf8_lossy(&port_forwarding.stderr);
167+
let err = exit_err.trim();
168+
169+
if err.is_empty() {
170+
if let Some(master_error) = self.discover_master_error() {
171+
return Err(master_error);
172+
}
173+
}
174+
175+
Err(Error::Ssh(io::Error::new(io::ErrorKind::Other, err)))
176+
}
177+
}
178+
142179
async fn close_impl(&self) -> Result<(), Error> {
143180
let exit = self
144181
.new_cmd(&["-O", "exit"])

src/session.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,6 @@ impl Session {
471471
///
472472
/// Otherwise, `listen_socket` on the remote machine will be forwarded to `connect_socket`
473473
/// on the local machine.
474-
///
475-
/// Currently, there is no way of stopping a port forwarding due to the fact that
476-
/// openssh multiplex server/master does not support this.
477474
pub async fn request_port_forward(
478475
&self,
479476
forward_type: impl Into<ForwardType>,
@@ -490,6 +487,25 @@ impl Session {
490487
})
491488
}
492489

490+
/// Close a previously established local/remote port forwarding.
491+
///
492+
/// The same set of arguments should be passed as when the port forwarding was requested.
493+
pub async fn close_port_forward(
494+
&self,
495+
forward_type: impl Into<ForwardType>,
496+
listen_socket: impl Into<Socket<'_>>,
497+
connect_socket: impl Into<Socket<'_>>,
498+
) -> Result<(), Error> {
499+
delegate!(&self.0, imp, {
500+
imp.close_port_forward(
501+
forward_type.into(),
502+
listen_socket.into(),
503+
connect_socket.into(),
504+
)
505+
.await
506+
})
507+
}
508+
493509
/// Terminate the remote connection.
494510
///
495511
/// This destructor terminates the ssh multiplex server

tests/openssh.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,7 @@ async fn remote_socket_forward() {
829829

830830
eprintln!("Creating remote process");
831831
let cmd = format!(
832-
"echo -e '0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n' | nc localhost {} >/dev/stderr",
832+
"echo -e '0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10' | nc localhost {} >/dev/stderr",
833833
port
834834
);
835835
let child = session
@@ -851,6 +851,16 @@ async fn remote_socket_forward() {
851851

852852
assert_eq!(DATA, &buffer);
853853

854+
eprintln!("Canceling port forward");
855+
session
856+
.close_port_forward(ForwardType::Remote, (loopback(), *port), &*unix_socket)
857+
.await
858+
.unwrap();
859+
860+
eprintln!("Trying to connect again");
861+
let e = output.try_read(&mut buffer).unwrap_err();
862+
assert_eq!(e.kind(), io::ErrorKind::WouldBlock);
863+
854864
drop(output);
855865
drop(output_listener);
856866

@@ -902,6 +912,16 @@ async fn local_socket_forward() {
902912

903913
drop(output);
904914

915+
eprintln!("Closing port forward");
916+
session
917+
.close_port_forward(ForwardType::Local, &*unix_socket, (loopback(), port))
918+
.await
919+
.unwrap();
920+
921+
eprintln!("Trying to connect again");
922+
let e = UnixStream::connect(&unix_socket).await.unwrap_err();
923+
assert_eq!(e.kind(), io::ErrorKind::ConnectionRefused);
924+
905925
eprintln!("Waiting for session to end");
906926
let output = child.wait_with_output().await.unwrap();
907927
eprintln!("local_socket_forward: {:#?}", output);

0 commit comments

Comments
 (0)