use crate::channel_exec::ChannelExec;
use crate::channel_scp::ChannelScp;
use crate::channel_shell::ChannelShell;
use crate::constant::ssh_msg_code;
use crate::data::Data;
use crate::error::{SshError, SshResult};
use crate::slog::log;
use crate::window_size::WindowSize;
use crate::Session;
use std::{
io::{Read, Write},
ops::{Deref, DerefMut},
};
pub struct Channel<S>
where
S: Read + Write,
{
pub(crate) remote_close: bool,
pub(crate) local_close: bool,
pub(crate) window_size: WindowSize,
pub(crate) session: *mut Session<S>,
}
impl<S> Deref for Channel<S>
where
S: Read + Write,
{
type Target = WindowSize;
fn deref(&self) -> &Self::Target {
&self.window_size
}
}
impl<S> DerefMut for Channel<S>
where
S: Read + Write,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.window_size
}
}
impl<S> Channel<S>
where
S: Read + Write,
{
pub(crate) fn other(&mut self, message_code: u8, mut result: Data) -> SshResult<()> {
match message_code {
ssh_msg_code::SSH_MSG_GLOBAL_REQUEST => {
let mut data = Data::new();
data.put_u8(ssh_msg_code::SSH_MSG_REQUEST_FAILURE);
let session = self.get_session_mut();
session.client.as_mut().unwrap().write(data)?;
}
ssh_msg_code::SSH_MSG_CHANNEL_WINDOW_ADJUST => {
result.get_u32();
let rws = result.get_u32();
self.window_size.add_remote_window_size(rws);
}
ssh_msg_code::SSH_MSG_CHANNEL_EOF => {}
ssh_msg_code::SSH_MSG_CHANNEL_REQUEST => {}
ssh_msg_code::SSH_MSG_CHANNEL_SUCCESS => {}
ssh_msg_code::SSH_MSG_CHANNEL_FAILURE => {
return Err(SshError::from("channel failure."))
}
ssh_msg_code::SSH_MSG_CHANNEL_CLOSE => {
let cc = result.get_u32();
if cc == self.client_channel_no {
self.remote_close = true;
self.close()?;
}
}
_ => {}
}
Ok(())
}
pub fn open_shell(self) -> SshResult<ChannelShell<S>> {
log::info!("shell opened.");
ChannelShell::open(self)
}
pub fn open_exec(self) -> SshResult<ChannelExec<S>> {
log::info!("exec opened.");
Ok(ChannelExec::open(self))
}
pub fn open_scp(self) -> SshResult<ChannelScp<S>> {
log::info!("scp opened.");
Ok(ChannelScp::open(self))
}
pub fn close(&mut self) -> SshResult<()> {
log::info!("channel close.");
self.send_close()?;
self.receive_close()
}
fn send_close(&mut self) -> SshResult<()> {
if self.local_close {
return Ok(());
}
let mut data = Data::new();
data.put_u8(ssh_msg_code::SSH_MSG_CHANNEL_CLOSE)
.put_u32(self.server_channel_no);
let session = self.get_session_mut();
session.client.as_mut().unwrap().write(data)?;
self.local_close = true;
Ok(())
}
fn receive_close(&mut self) -> SshResult<()> {
if self.remote_close {
return Ok(());
}
loop {
let results = { self.get_session_mut().client.as_mut().unwrap().read() }?;
for mut result in results {
if result.is_empty() {
continue;
}
let message_code = result.get_u8();
match message_code {
ssh_msg_code::SSH_MSG_CHANNEL_CLOSE => {
let cc = result.get_u32();
if cc == self.client_channel_no {
self.remote_close = true;
return Ok(());
}
}
_ => self.other(message_code, result)?,
}
}
}
}
#[allow(clippy::mut_from_ref)]
pub(crate) fn get_session_mut(&self) -> &mut Session<S> {
unsafe { &mut *self.session }
}
pub(crate) fn is_close(&self) -> bool {
self.local_close && self.remote_close
}
}