Skip to content
Closed
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
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ embedded-io = ["dep:embedded-io"]
# Arbitrary for fuzzing. std is required for derive(Arbitrary)
arbitrary = ["dep:arbitrary", "std"]

# Raise config::MAX_CHANNELS from 4 to 16. The channel array is fixed size, so
# this costs memory; it is off by default for `no_std` targets.
many-channels = []

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't want this feature just so that I can bump MAX_CHANNELS.
Should we just increase it for everybody?


# Allocate larger buffers for things such as usernames.
# See config.rs for details
larger = []
Expand Down
8 changes: 8 additions & 0 deletions src/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,14 @@ impl Channels {
self.get(num).is_ok_and(|c| c.valid_send(dt))
}

/// Whether a channel can no longer carry data, in any of the ways that can
/// happen: refused, closed, or already gone.
pub(crate) fn is_finished(&self, num: ChanNum) -> bool {
self.get_any(num).is_ok_and(|c| {
matches!(c.state, ChanState::PendingDone | ChanState::RecvClose)
}) || self.get_any(num).is_err()
}

pub fn progress(&mut self, s: &mut TrafSend) -> DispatchEvent {
for ch in self.ch.iter_mut().filter_map(|c| c.as_mut()) {
ch.check_send_window_adjust(s);
Expand Down
6 changes: 5 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ pub(crate) const SSH_MAX_PACKET: usize = 35000;
//
// This size is arbitrary and may be increased, though note that some code paths assume
// a linear scan of channels can happen quickly, so may need reworking for performance.
pub const MAX_CHANNELS: usize = 4;
//
// The channel array is fixed size, so the default stays small for `no_std`
// targets. Hosts that multiplex several sessions or forwarded connections over
// one transport can opt in to more with the `many-channels` feature.
pub const MAX_CHANNELS: usize = if cfg!(feature = "many-channels") { 16 } else { 4 };

// Enough for longest 23 of "screen.konsole-256color" on my system
// Unsure if this is specified somewhere
Expand Down
45 changes: 45 additions & 0 deletions src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,20 @@ impl<'a, CS: CliServ> Runner<'a, CS> {
self.conn.channels.valid_send(chan.0, dt)
}

/// Returns `true` once the channel can no longer carry data.
///
/// This covers a channel the peer refused to open, one that has been closed,
/// and one already released. The handle must still be returned with
/// [`Runner::channel_done`].
///
/// Callers waiting for an open to complete need this to tell "not yet" from
/// "never". A refused open leaves the channel in a finished state rather
/// than removing it, so without this a rejection is indistinguishable from a
/// slow open until the caller's own deadline expires.
pub fn is_channel_finished(&self, chan: &ChanHandle) -> bool {
self.conn.channels.is_finished(chan.0)
}

/// Must be called when an application has finished with a channel.
///
/// Channel numbers will not be re-used without calling this, so
Expand Down Expand Up @@ -887,6 +901,37 @@ impl<'a> Runner<'a, client::Client> {
self.wake();
Ok(ChanHandle(chan))
}

/// Open a `direct-tcpip` channel, asking the server to connect to
/// `address:port` on our behalf.
///
/// This is the channel type behind `ssh -L` and `ssh -J`. `origin` and
/// `origin_port` describe the local end the server is told about; they are
/// informational, and servers commonly log them.
///
/// The returned handle is not usable until the server confirms the open.
/// [`Runner::is_write_channel_valid`] reports `false` until then, because a
/// channel that is still opening is not yet a channel that can carry data.
pub fn open_client_tcpip(
&mut self,
address: &str,
port: u16,
origin: &str,
origin_port: u16,
) -> Result<ChanHandle> {
trace!("open_client_tcpip {address}:{port}");

let ty = packets::ChannelOpenType::DirectTcpip(packets::DirectTcpip {
address: address.into(),
port: port as u32,
origin: origin.into(),
origin_port: origin_port as u32,
});
let (chan, p) = self.conn.channels.open(ty)?;
self.traf_out.send_packet(p, &mut self.keys)?;
self.wake();
Ok(ChanHandle(chan))
}
}

/// Sets a waker, waking any existing waker
Expand Down