Skip to content

Commit 9dd2d82

Browse files
authored
Merge pull request #86 from openssh-rust/optimize
Optimize and make `request_port_forward` easier to use
2 parents 8b3f582 + c0fa5b9 commit 9dd2d82

File tree

12 files changed

+163
-109
lines changed

12 files changed

+163
-109
lines changed

.github/workflows/coverage.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ jobs:
4949
# we cannot use 127.0.0.1 (the default here)
5050
# since we are running from a different container
5151
TEST_HOST: ssh://test-user@localhost:2222
52+
XDG_RUNTIME_DIR: /tmp
5253
- name: Upload to codecov.io
5354
uses: codecov/codecov-action@v2
5455
with:

.github/workflows/minimal.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ jobs:
5555
env:
5656
# makes all the ignored tests not ignored
5757
RUSTFLAGS: --cfg=ci
58+
XDG_RUNTIME_DIR: /tmp
5859
services:
5960
openssh:
6061
image: linuxserver/openssh-server:amd64-latest

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ jobs:
4949
env:
5050
# makes all the ignored tests not ignored
5151
RUSTFLAGS: --cfg=ci
52+
XDG_RUNTIME_DIR: /tmp
5253
- run: docker logs $(docker ps | grep openssh-server | awk '{print $1}')
5354
name: ssh container log
5455
if: ${{ failure() }}

src/builder.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
use super::{Error, Session};
22

3-
#[cfg(feature = "process-mux")]
4-
use super::process_impl;
5-
6-
#[cfg(feature = "native-mux")]
7-
use super::native_mux_impl;
8-
93
use std::borrow::Cow;
104
use std::ffi::OsString;
115
use std::fs;
@@ -185,10 +179,8 @@ impl SessionBuilder {
185179
#[cfg(feature = "process-mux")]
186180
#[cfg_attr(docsrs, doc(cfg(feature = "process-mux")))]
187181
pub async fn connect<S: AsRef<str>>(&self, destination: S) -> Result<Session, Error> {
188-
let destination = destination.as_ref();
189-
let (builder, destination) = self.resolve(destination);
190-
let tempdir = builder.launch_master(destination).await?;
191-
Ok(process_impl::Session::new(tempdir, destination).into())
182+
self.connect_impl(destination.as_ref(), Session::new_process_mux)
183+
.await
192184
}
193185

194186
/// Connect to the host at the given `host` over SSH using native mux, which will
@@ -207,10 +199,18 @@ impl SessionBuilder {
207199
#[cfg(feature = "native-mux")]
208200
#[cfg_attr(docsrs, doc(cfg(feature = "native-mux")))]
209201
pub async fn connect_mux<S: AsRef<str>>(&self, destination: S) -> Result<Session, Error> {
210-
let destination = destination.as_ref();
202+
self.connect_impl(destination.as_ref(), Session::new_native_mux)
203+
.await
204+
}
205+
206+
async fn connect_impl(
207+
&self,
208+
destination: &str,
209+
f: fn(TempDir) -> Session,
210+
) -> Result<Session, Error> {
211211
let (builder, destination) = self.resolve(destination);
212212
let tempdir = builder.launch_master(destination).await?;
213-
Ok(native_mux_impl::Session::new(tempdir).into())
213+
Ok(f(tempdir))
214214
}
215215

216216
fn resolve<'a, 'b>(&'a self, mut destination: &'b str) -> (Cow<'a, Self>, &'b str) {

src/changelog.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22
use crate::*;
33

44
/// TODO: RENAME THIS INTO THE NEXT VERSION BEFORE RELEASE
5+
///
6+
/// ## Added
7+
/// - `From<SocketAddr> for Socket<'static>`
8+
/// - `From<Cow<'a, Path>> for Socket<'a>`
9+
/// - `From<&'a Path> for Socket<'a>`
10+
/// - `From<PathBuf> for Socket<'static>`
11+
/// - `From<Box<Path>> for Socket<'static>`
12+
/// - `From<(IpAddr, u16)> for Socket<'static>`
13+
/// - `From<(Ipv4Addr, u16)> for Socket<'static>`
14+
/// - `From<(Ipv6Addr, u16)> for Socket<'static>`
15+
///
16+
/// ## Changed
17+
/// - [`Session::request_port_forward`] now takes `impl Into<...>`
18+
/// to make it much easier to use.
19+
/// - [`Socket::new`] now returns `Socket<'static>`
520
#[doc(hidden)]
621
pub mod unreleased {}
722

src/command.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ impl From<super::process_impl::Command> for CommandImp {
2323
}
2424

2525
#[cfg(feature = "native-mux")]
26-
impl<'s> From<super::native_mux_impl::Command> for CommandImp {
26+
impl From<super::native_mux_impl::Command> for CommandImp {
2727
fn from(imp: super::native_mux_impl::Command) -> Self {
2828
CommandImp::NativeMuxImpl(imp)
2929
}
@@ -139,7 +139,7 @@ impl<'s> Command<'s> {
139139
/// To pass multiple unescaped arguments see [`raw_args`](Command::raw_args).
140140
pub fn raw_arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
141141
delegate!(&mut self.imp, imp, {
142-
imp.raw_arg(arg);
142+
imp.raw_arg(arg.as_ref());
143143
});
144144
self
145145
}

src/native_mux_impl/session.rs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{Command, Error, ForwardType, Socket};
1+
use super::{Command, Error};
22

33
use std::ffi::OsStr;
44
use std::os::unix::ffi::OsStrExt;
@@ -51,9 +51,9 @@ impl Session {
5151

5252
pub(crate) async fn request_port_forward(
5353
&self,
54-
forward_type: impl Into<ForwardType>,
55-
listen_socket: impl Into<Socket<'_>>,
56-
connect_socket: impl Into<Socket<'_>>,
54+
forward_type: crate::ForwardType,
55+
listen_socket: crate::Socket<'_>,
56+
connect_socket: crate::Socket<'_>,
5757
) -> Result<(), Error> {
5858
Connection::connect(&self.ctl)
5959
.await?
@@ -76,17 +76,13 @@ impl Session {
7676
Ok(())
7777
}
7878

79-
pub(crate) async fn close(mut self) -> Result<(), Error> {
80-
// This also set self.tempdir to None so that Drop::drop would do nothing.
81-
if let Some(tempdir) = self.tempdir.take() {
82-
self.close_impl().await?;
79+
pub(crate) async fn close(mut self) -> Result<Option<TempDir>, Error> {
80+
// Take self.tempdir so that drop would do nothing
81+
let tempdir = self.tempdir.take();
8382

84-
tempdir.close().map_err(Error::Cleanup)?;
85-
} else {
86-
self.close_impl().await?;
87-
}
83+
self.close_impl().await?;
8884

89-
Ok(())
85+
Ok(tempdir)
9086
}
9187

9288
pub(crate) fn detach(mut self) -> (Box<Path>, Option<Box<Path>>) {

src/native_mux_impl/stdio.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,7 @@ impl Stdio {
6262
}
6363
}
6464

65-
fn to_output(
66-
&self,
67-
get_inherit_rawfd: impl FnOnce() -> RawFd,
68-
) -> Result<(Fd, Option<PipeRead>), Error> {
65+
fn to_output(&self, get_inherit_rawfd: fn() -> RawFd) -> Result<(Fd, Option<PipeRead>), Error> {
6966
match &self.0 {
7067
StdioImpl::Inherit => Ok((Fd::Borrowed(get_inherit_rawfd()), None)),
7168
StdioImpl::Null => Ok((Fd::Null, None)),

src/port_forwarding.rs

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use std::ffi::OsStr;
77
use std::borrow::Cow;
88
use std::fmt;
99
use std::io;
10-
use std::net::{SocketAddr, ToSocketAddrs};
11-
use std::path::Path;
10+
use std::net::{self, SocketAddr, ToSocketAddrs};
11+
use std::path::{Path, PathBuf};
1212

1313
/// Type of forwarding
1414
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -46,16 +46,64 @@ pub enum Socket<'a> {
4646
/// Tcp socket.
4747
TcpSocket(SocketAddr),
4848
}
49-
impl Socket<'_> {
50-
/// Create a new TcpSocket
51-
pub fn new<T: ToSocketAddrs>(addr: &T) -> Result<Self, io::Error> {
52-
let mut it = addr.to_socket_addrs()?;
5349

54-
let addr = it.next().ok_or_else(|| {
55-
io::Error::new(io::ErrorKind::Other, "no more socket addresses to try")
56-
})?;
50+
impl From<SocketAddr> for Socket<'static> {
51+
fn from(addr: SocketAddr) -> Self {
52+
Socket::TcpSocket(addr)
53+
}
54+
}
55+
56+
macro_rules! impl_from_addr {
57+
($ip:ty) => {
58+
impl From<($ip, u16)> for Socket<'static> {
59+
fn from((ip, port): ($ip, u16)) -> Self {
60+
SocketAddr::new(ip.into(), port).into()
61+
}
62+
}
63+
};
64+
}
65+
66+
impl_from_addr!(net::IpAddr);
67+
impl_from_addr!(net::Ipv4Addr);
68+
impl_from_addr!(net::Ipv6Addr);
69+
70+
impl<'a> From<Cow<'a, Path>> for Socket<'a> {
71+
fn from(path: Cow<'a, Path>) -> Self {
72+
Socket::UnixSocket { path }
73+
}
74+
}
75+
76+
impl<'a> From<&'a Path> for Socket<'a> {
77+
fn from(path: &'a Path) -> Self {
78+
Socket::UnixSocket {
79+
path: Cow::Borrowed(path),
80+
}
81+
}
82+
}
83+
84+
impl From<PathBuf> for Socket<'static> {
85+
fn from(path: PathBuf) -> Self {
86+
Socket::UnixSocket {
87+
path: Cow::Owned(path),
88+
}
89+
}
90+
}
5791

58-
Ok(Socket::TcpSocket(addr))
92+
impl From<Box<Path>> for Socket<'static> {
93+
fn from(path: Box<Path>) -> Self {
94+
Socket::UnixSocket {
95+
path: Cow::Owned(path.into()),
96+
}
97+
}
98+
}
99+
100+
impl Socket<'_> {
101+
/// Create a new TcpSocket
102+
pub fn new<T: ToSocketAddrs>(addr: &T) -> Result<Socket<'static>, io::Error> {
103+
addr.to_socket_addrs()?
104+
.next()
105+
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "no more socket addresses to try"))
106+
.map(Socket::TcpSocket)
59107
}
60108

61109
#[cfg(feature = "process-mux")]

src/process_impl/session.rs

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,17 @@ use tempfile::TempDir;
1414
pub(crate) struct Session {
1515
tempdir: Option<TempDir>,
1616
ctl: Box<Path>,
17-
addr: Box<str>,
1817
master_log: Option<Box<Path>>,
1918
}
2019

2120
impl Session {
22-
pub(crate) fn new(tempdir: TempDir, addr: &str) -> Self {
21+
pub(crate) fn new(tempdir: TempDir) -> Self {
2322
let log = tempdir.path().join("log").into_boxed_path();
2423
let ctl = tempdir.path().join("master").into_boxed_path();
2524

2625
Self {
2726
tempdir: Some(tempdir),
2827
ctl,
29-
addr: addr.into(),
3028
master_log: Some(log),
3129
}
3230
}
@@ -35,10 +33,6 @@ impl Session {
3533
Self {
3634
tempdir: None,
3735
ctl,
38-
// ssh don't care about the addr as long as we have the ctl.
39-
//
40-
// I tested this behavior on OpenSSH_8.9p1 Ubuntu-3, OpenSSL 3.0.2 15 Mar 2022
41-
addr: "none".into(),
4236
master_log,
4337
}
4438
}
@@ -51,7 +45,10 @@ impl Session {
5145
.arg("-o")
5246
.arg("BatchMode=yes")
5347
.args(args)
54-
.arg(&*self.addr);
48+
// ssh does not care about the addr as long as we have passed
49+
// `-S &*self.ctl`.
50+
// It is tested on OpenSSH 8.2p1, 8.9p1, 9.0p1
51+
.arg("none");
5552
cmd
5653
}
5754

@@ -107,18 +104,18 @@ impl Session {
107104

108105
pub(crate) async fn request_port_forward(
109106
&self,
110-
forward_type: impl Into<ForwardType>,
111-
listen_socket: impl Into<Socket<'_>>,
112-
connect_socket: impl Into<Socket<'_>>,
107+
forward_type: ForwardType,
108+
listen_socket: Socket<'_>,
109+
connect_socket: Socket<'_>,
113110
) -> Result<(), Error> {
114-
let flag = match forward_type.into() {
111+
let flag = match forward_type {
115112
ForwardType::Local => OsStr::new("-L"),
116113
ForwardType::Remote => OsStr::new("-R"),
117114
};
118115

119-
let mut forwarding = listen_socket.into().as_os_str().into_owned();
116+
let mut forwarding = listen_socket.as_os_str().into_owned();
120117
forwarding.push(":");
121-
forwarding.push(connect_socket.into().as_os_str());
118+
forwarding.push(connect_socket.as_os_str());
122119

123120
let port_forwarding = self
124121
.new_cmd(&[OsStr::new("-fNT"), flag, &*forwarding])
@@ -178,17 +175,13 @@ impl Session {
178175
Ok(())
179176
}
180177

181-
pub(crate) async fn close(mut self) -> Result<(), Error> {
178+
pub(crate) async fn close(mut self) -> Result<Option<TempDir>, Error> {
182179
// Take self.tempdir so that drop would do nothing
183-
if let Some(tempdir) = self.tempdir.take() {
184-
self.close_impl().await?;
180+
let tempdir = self.tempdir.take();
185181

186-
tempdir.close().map_err(Error::Cleanup)?;
187-
} else {
188-
self.close_impl().await?;
189-
}
182+
self.close_impl().await?;
190183

191-
Ok(())
184+
Ok(tempdir)
192185
}
193186

194187
pub(crate) fn detach(mut self) -> (Box<Path>, Option<Box<Path>>) {

0 commit comments

Comments
 (0)