Files
markbase/markbase-core/src/ssh_server/channel.rs
Warren 742a40e52e
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
Implement SSH Phase 13.3: Channel.rs support for port forwarding channels
- Modify Channel struct to add direct_tcpip and forwarded_tcpip fields
- Modify handle_channel_open to support 'direct-tcpip' and 'forwarded-tcpip' channel types
- Add handle_session_channel_open() function (Phase 6)
- Add handle_direct_tcpip_channel_open() function (Phase 13.3: Remote port forwarding)
- Add handle_forwarded_tcpip_channel_open() function (Phase 13.3: Local port forwarding)
- Integrate security validation in direct-tcpip channel open
- Modify server.rs to pass security_config to handle_channel_open
- Add 128 lines of new channel handling functions
- All compilation tests passed successfully

Phase 13.1-13.3 completed: Enterprise security + Global request + Channel support
2026-06-15 18:47:40 +08:00

680 lines
26 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// SSH Channel协议实现Phase 6 + Phase 13端口转发
// 参考OpenSSH channel.c
use crate::ssh_server::packet::{SshPacket, PacketType};
use crate::ssh_server::ssh_security_config::SshSecurityConfig; // Phase 13.3: 安全配置
use crate::ssh_server::port_forward::{PortForwardManager, DirectTcpipChannel, ForwardedTcpipChannel}; // Phase 13.3
use std::io::{Read, Write}; // 导入Write traitOpenSSH标准
use anyhow::{Result, anyhow};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use log::{info, warn, debug};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use crate::ssh_server::sftp_handler::SftpHandler; // Phase 7: SFTP handler
use crate::ssh_server::scp_handler::ScpHandler; // Phase 8: SCP handler
use crate::ssh_server::rsync_handler::RsyncHandler; // Phase 8: rsync handler
use std::path::PathBuf; // Phase 7-8: Path for SFTP/SCP/rsync root directory
/// SSH Channel管理器参考OpenSSH channel.c: struct channel
pub struct ChannelManager {
channels: HashMap<u32, Channel>,
next_channel_id: u32,
}
impl ChannelManager {
pub fn new() -> Self {
Self {
channels: HashMap::new(),
next_channel_id: 0,
}
}
/// 处理SSH_MSG_CHANNEL_OPEN参考OpenSSH channel.c: channel_open())
/// Phase 13.3: 支持direct-tcpip和forwarded-tcpip channel
pub fn handle_channel_open(&mut self, packet: &SshPacket, security_config: Option<&SshSecurityConfig>) -> Result<SshPacket> {
info!("Processing SSH_MSG_CHANNEL_OPEN");
let mut cursor = std::io::Cursor::new(packet.payload.as_slice());
// Packet type
let packet_type = cursor.read_u8()?;
if packet_type != PacketType::SSH_MSG_CHANNEL_OPEN as u8 {
return Err(anyhow!("Invalid packet type for CHANNEL_OPEN"));
}
// 读取channel类型SSH string
let channel_type = read_ssh_string(&mut cursor)?;
// 读取sender channel IDu32
let sender_channel = cursor.read_u32::<BigEndian>()?;
// 读取初始窗口大小u32
let initial_window_size = cursor.read_u32::<BigEndian>()?;
// 读取最大packet大小u32
let maximum_packet_size = cursor.read_u32::<BigEndian>()?;
info!("Channel open: type={}, sender_channel={}, window={}, max_packet={}",
channel_type, sender_channel, initial_window_size, maximum_packet_size);
// Phase 13.3: 检查channel类型支持session、direct-tcpip、forwarded-tcpip
match channel_type.as_str() {
"session" => {
// 传统的session channelPhase 6
self.handle_session_channel_open(sender_channel, initial_window_size, maximum_packet_size)
}
"direct-tcpip" => {
// Phase 13.3: Remote port forwarding channel
info!("Received direct-tcpip channel open (Remote port forwarding)");
self.handle_direct_tcpip_channel_open(packet, sender_channel, initial_window_size, maximum_packet_size, security_config)
}
"forwarded-tcpip" => {
// Phase 13.3: Local port forwarding channel
info!("Received forwarded-tcpip channel open (Local port forwarding)");
self.handle_forwarded_tcpip_channel_open(packet, sender_channel, initial_window_size, maximum_packet_size)
}
_ => {
warn!("Unsupported channel type: {}", channel_type);
self.build_channel_open_failure(
sender_channel,
3, // SSH_OPEN_UNKNOWN_CHANNEL_TYPE
"Unsupported channel type",
"en"
)
}
}
}
/// 处理session channel openPhase 6
fn handle_session_channel_open(&mut self, sender_channel: u32, initial_window_size: u32, maximum_packet_size: u32) -> Result<SshPacket> {
info!("Processing session channel open");
let server_channel = self.next_channel_id;
self.next_channel_id += 1;
let channel = Channel {
server_channel,
sender_channel,
channel_type: "session".to_string(),
window_size: initial_window_size,
maximum_packet_size,
state: ChannelState::Open,
output_buffer: None,
sftp_handler: None,
scp_handler: None,
rsync_handler: None,
direct_tcpip: None,
forwarded_tcpip: None,
};
self.channels.insert(server_channel, channel);
info!("Session channel created: server_channel={}, sender_channel={}", server_channel, sender_channel);
self.build_channel_open_confirmation(server_channel, sender_channel, initial_window_size, maximum_packet_size)
}
/// 处理direct-tcpip channel openPhase 13.3: Remote port forwarding
fn handle_direct_tcpip_channel_open(
&mut self,
packet: &SshPacket,
sender_channel: u32,
initial_window_size: u32,
maximum_packet_size: u32,
security_config: Option<&SshSecurityConfig>,
) -> Result<SshPacket> {
info!("Processing direct-tcpip channel open");
// 解析direct-tcpip参数
let mut port_forward_manager = PortForwardManager::new();
let direct_tcpip = port_forward_manager.handle_direct_tcpip_channel(&packet.payload)?;
// Phase 13.3: 安全配置验证
if let Some(security) = security_config {
if let Err(e) = security.validate_direct_tcpip_channel(&direct_tcpip.host_to_connect, direct_tcpip.port_to_connect) {
warn!("direct-tcpip security validation failed: {}", e);
return self.build_channel_open_failure(
sender_channel,
2, // SSH_OPEN_CONNECT_FAILED
"Security validation failed",
"en"
);
}
info!("direct-tcpip security validation passed");
}
let server_channel = self.next_channel_id;
self.next_channel_id += 1;
let channel = Channel {
server_channel,
sender_channel,
channel_type: "direct-tcpip".to_string(),
window_size: initial_window_size,
maximum_packet_size,
state: ChannelState::Open,
output_buffer: None,
sftp_handler: None,
scp_handler: None,
rsync_handler: None,
direct_tcpip: Some(direct_tcpip),
forwarded_tcpip: None,
};
self.channels.insert(server_channel, channel);
info!("direct-tcpip channel created: server_channel={}, host={}, port={}",
server_channel,
self.channels.get(&server_channel).unwrap().direct_tcpip.as_ref().unwrap().host_to_connect,
self.channels.get(&server_channel).unwrap().direct_tcpip.as_ref().unwrap().port_to_connect);
self.build_channel_open_confirmation(server_channel, sender_channel, initial_window_size, maximum_packet_size)
}
/// 处理forwarded-tcpip channel openPhase 13.3: Local port forwarding
fn handle_forwarded_tcpip_channel_open(
&mut self,
packet: &SshPacket,
sender_channel: u32,
initial_window_size: u32,
maximum_packet_size: u32,
) -> Result<SshPacket> {
info!("Processing forwarded-tcpip channel open");
// 解析forwarded-tcpip参数
let mut port_forward_manager = PortForwardManager::new();
let forwarded_tcpip = port_forward_manager.handle_forwarded_tcpip_channel(&packet.payload)?;
let server_channel = self.next_channel_id;
self.next_channel_id += 1;
let channel = Channel {
server_channel,
sender_channel,
channel_type: "forwarded-tcpip".to_string(),
window_size: initial_window_size,
maximum_packet_size,
state: ChannelState::Open,
output_buffer: None,
sftp_handler: None,
scp_handler: None,
rsync_handler: None,
direct_tcpip: None,
forwarded_tcpip: Some(forwarded_tcpip),
};
self.channels.insert(server_channel, channel);
info!("forwarded-tcpip channel created: server_channel={}, bind={}, originator={}",
server_channel,
self.channels.get(&server_channel).unwrap().forwarded_tcpip.as_ref().unwrap().bind_port,
self.channels.get(&server_channel).unwrap().forwarded_tcpip.as_ref().unwrap().originator_address);
self.build_channel_open_confirmation(server_channel, sender_channel, initial_window_size, maximum_packet_size)
}
/// 处理SSH_MSG_CHANNEL_REQUEST参考OpenSSH channel.c: channel_request())
pub fn handle_channel_request(&mut self, packet: &SshPacket) -> Result<Option<SshPacket>> {
info!("Processing SSH_MSG_CHANNEL_REQUEST");
let mut cursor = std::io::Cursor::new(packet.payload.as_slice()); // 使用as_slice()Rust标准
// Packet type
let packet_type = cursor.read_u8()?;
if packet_type != PacketType::SSH_MSG_CHANNEL_REQUEST as u8 {
return Err(anyhow!("Invalid packet type for CHANNEL_REQUEST"));
}
// 读取recipient channelu32
let recipient_channel = cursor.read_u32::<BigEndian>()?;
// 读取请求类型SSH string
let request_type = read_ssh_string(&mut cursor)?;
// 读取want reply标志boolean
let want_reply = cursor.read_u8()? != 0;
info!("Channel request: channel={}, type={}, want_reply={}",
recipient_channel, request_type, want_reply);
// 处理不同请求类型参考OpenSSH channel.c
if request_type == "exec" {
self.handle_exec_request(&mut cursor, recipient_channel, want_reply) // 移除?操作符返回Option不是Result
} else if request_type == "subsystem" {
self.handle_subsystem_request(&mut cursor, recipient_channel, want_reply) // 移除?操作符
} else if request_type == "shell" {
self.handle_shell_request(recipient_channel, want_reply) // 移除?操作符
} else if request_type == "env" {
self.handle_env_request(&mut cursor, recipient_channel, want_reply) // 移除?操作符
} else if request_type == "pty-req" {
self.handle_pty_request(&mut cursor, recipient_channel, want_reply) // 移除?操作符
} else {
warn!("Unsupported channel request: {}", request_type);
if want_reply {
Ok(Some(self.build_channel_failure(recipient_channel)?))
} else {
Ok(None)
}
}
}
/// 处理exec请求参考OpenSSH channel.c: channel_request_exec())
fn handle_exec_request(&mut self, cursor: &mut std::io::Cursor<&[u8]>, channel: u32, want_reply: bool) -> Result<Option<SshPacket>> {
info!("Handling exec request for channel {}", channel);
// 读取命令SSH string
let command = read_ssh_string(cursor)?;
info!("Exec command: {}", command);
// Phase 8: SCP/rsync命令直接执行使用系统命令
// 不需要自己实现SCP协议让系统的scp/rsync命令处理
let output = self.execute_command(&command)?;
// 存储输出等待后续发送CHANNEL_DATA
if let Some(ch) = self.channels.get_mut(&channel) {
ch.output_buffer = Some(output);
}
if want_reply {
Ok(Some(self.build_channel_success(channel)?))
} else {
Ok(None)
}
}
/// 执行命令并捕获输出Phase 6基础实现
fn execute_command(&self, command: &str) -> Result<Vec<u8>> {
use std::process::{Command, Stdio};
info!("Executing command: {}", command);
// 使用shell执行命令参考OpenSSH session.c
let output = Command::new("sh")
.arg("-c")
.arg(command)
.output()?;
// 返回stdout + stderr
let mut result = output.stdout;
result.extend_from_slice(&output.stderr);
info!("Command output: {} bytes", result.len());
Ok(result)
}
/// 处理subsystem请求参考OpenSSH channel.c: channel_request_subsystem())
fn handle_subsystem_request(&mut self, cursor: &mut std::io::Cursor<&[u8]>, channel: u32, want_reply: bool) -> Result<Option<SshPacket>> {
info!("Handling subsystem request for channel {}", channel);
// 读取subsystem名称SSH string
let subsystem = read_ssh_string(cursor)?;
info!("Subsystem: {}", subsystem);
// 检查subsystem支持OpenSSH支持sftp
if subsystem == "sftp" {
info!("SFTP subsystem requested");
// Phase 7: 初始化SFTP handler
let root_dir = PathBuf::from("/Users/accusys/markbase"); // 默认root目录
let sftp_handler = SftpHandler::new(root_dir);
// 存储到channel
if let Some(ch) = self.channels.get_mut(&channel) {
ch.sftp_handler = Some(sftp_handler);
info!("SFTP handler initialized for channel {}", channel);
}
if want_reply {
Ok(Some(self.build_channel_success(channel)?))
} else {
Ok(None)
}
} else {
warn!("Unsupported subsystem: {}", subsystem);
if want_reply {
Ok(Some(self.build_channel_failure(channel)?))
} else {
Ok(None)
}
}
}
/// 处理shell请求参考OpenSSH channel.c
fn handle_shell_request(&mut self, channel: u32, want_reply: bool) -> Result<Option<SshPacket>> {
info!("Handling shell request for channel {}", channel);
// Phase 9将实现shell
warn!("Shell not implemented in Phase 6");
if want_reply {
Ok(Some(self.build_channel_failure(channel)?))
} else {
Ok(None)
}
}
/// 处理env请求参考OpenSSH channel.c
fn handle_env_request(&mut self, cursor: &mut std::io::Cursor<&[u8]>, channel: u32, want_reply: bool) -> Result<Option<SshPacket>> {
info!("Handling env request for channel {}", channel);
// 读取环境变量名和值
let name = read_ssh_string(cursor)?;
let value = read_ssh_string(cursor)?;
info!("Env: {}={}", name, value);
if want_reply {
Ok(Some(self.build_channel_success(channel)?))
} else {
Ok(None)
}
}
/// 处理pty请求参考OpenSSH channel.c
fn handle_pty_request(&mut self, cursor: &mut std::io::Cursor<&[u8]>, channel: u32, want_reply: bool) -> Result<Option<SshPacket>> {
info!("Handling pty request for channel {}", channel);
// 读取terminal类型SSH string
let term = read_ssh_string(cursor)?;
// 读取窗口大小4个uint32
let width = cursor.read_u32::<BigEndian>()?;
let height = cursor.read_u32::<BigEndian>()?;
let _pixel_width = cursor.read_u32::<BigEndian>()?;
let _pixel_height = cursor.read_u32::<BigEndian>()?;
// 读取terminal modesSSH string格式
let modes_len = cursor.read_u32::<BigEndian>()?;
let mut modes = vec![0u8; modes_len as usize];
cursor.read_exact(&mut modes)?;
info!("PTY: term={}, width={}, height={}, modes_len={}", term, width, height, modes_len);
if want_reply {
Ok(Some(self.build_channel_success(channel)?))
} else {
Ok(None)
}
}
/// 处理SSH_MSG_CHANNEL_DATA参考OpenSSH channel.c: channel_input_data())
pub fn handle_channel_data(&mut self, packet: &SshPacket) -> Result<Option<SshPacket>> {
info!("Processing SSH_MSG_CHANNEL_DATA");
let mut cursor = std::io::Cursor::new(packet.payload.as_slice());
// Packet type
let packet_type = cursor.read_u8()?;
if packet_type != PacketType::SSH_MSG_CHANNEL_DATA as u8 {
return Err(anyhow!("Invalid packet type for CHANNEL_DATA"));
}
// 读取recipient channel
let recipient_channel = cursor.read_u32::<BigEndian>()?;
// 读取数据SSH string
let data_length = cursor.read_u32::<BigEndian>()?;
let mut data = vec![0u8; data_length as usize];
cursor.read_exact(&mut data)?;
info!("Channel data: channel={}, length={}", recipient_channel, data.len());
info!("Channel data content (first 20 bytes): {:?}", &data[..std::cmp::min(20, data.len())]);
// Phase 7: 检查是否是SFTP channel
if let Some(channel) = self.channels.get_mut(&recipient_channel) {
if let Some(sftp_handler) = &mut channel.sftp_handler {
info!("Processing SFTP request ({} bytes)", data.len());
// SFTP data是SSH string格式前4 bytes是length field
// 真正的SFTP packet从data[4]开始跳过length field
if data.len() < 4 {
warn!("SFTP data too short (less than 4 bytes)");
return Ok(None);
}
let sftp_packet_length = u32::from_be_bytes([data[0], data[1], data[2], data[3]]) as usize;
let sftp_packet = &data[4..4 + sftp_packet_length];
info!("SFTP packet: length={}, content={:?}", sftp_packet_length, &sftp_packet[..std::cmp::min(20, sftp_packet.len())]);
// 处理SFTP请求
let response = sftp_handler.handle_request(sftp_packet)?;
info!("SFTP response: {} bytes", response.len());
// 构建SSH_MSG_CHANNEL_DATA返回SFTP响应需要SSH string格式
return Ok(Some(self.build_channel_data(recipient_channel, &response)?));
}
}
// 如果不是SFTP返回NonePhase 6的普通channel data处理
Ok(None)
}
/// 处理SSH_MSG_CHANNEL_CLOSE参考OpenSSH channel.c: channel_input_close())
pub fn handle_channel_close(&mut self, packet: &SshPacket) -> Result<Option<SshPacket>> {
info!("Processing SSH_MSG_CHANNEL_CLOSE");
let mut cursor = std::io::Cursor::new(packet.payload.as_slice()); // 使用as_slice()Rust标准
// Packet type
let packet_type = cursor.read_u8()?;
if packet_type != PacketType::SSH_MSG_CHANNEL_CLOSE as u8 {
return Err(anyhow!("Invalid packet type for CHANNEL_CLOSE"));
}
// 读取recipient channel
let recipient_channel = cursor.read_u32::<BigEndian>()?;
info!("Channel close: channel={}", recipient_channel);
// 移除channel参考OpenSSH channel.c
if let Some(channel) = self.channels.remove(&recipient_channel) {
info!("Channel {} removed", recipient_channel);
// 发送SSH_MSG_CHANNEL_CLOSE回应
Ok(Some(self.build_channel_close(channel.sender_channel)?))
} else {
warn!("Channel {} not found", recipient_channel);
Ok(None)
}
}
/// 构建SSH_MSG_CHANNEL_OPEN_CONFIRMATION参考OpenSSH channel.c
fn build_channel_open_confirmation(
&self,
server_channel: u32,
sender_channel: u32,
window_size: u32,
packet_size: u32,
) -> Result<SshPacket> {
let mut payload = Vec::new();
// Packet type
payload.write_u8(PacketType::SSH_MSG_CHANNEL_OPEN_CONFIRMATION as u8)?;
// Server channel number
payload.write_u32::<BigEndian>(server_channel)?;
// Sender channel number
payload.write_u32::<BigEndian>(sender_channel)?;
// Initial window size
payload.write_u32::<BigEndian>(window_size)?;
// Maximum packet size
payload.write_u32::<BigEndian>(packet_size)?;
Ok(SshPacket::new(payload))
}
/// 构建SSH_MSG_CHANNEL_OPEN_FAILURE参考OpenSSH channel.c
fn build_channel_open_failure(
&self,
sender_channel: u32,
reason_code: u32,
description: &str,
language: &str,
) -> Result<SshPacket> {
let mut payload = Vec::new();
// Packet type
payload.write_u8(PacketType::SSH_MSG_CHANNEL_OPEN_FAILURE as u8)?;
// Sender channel number
payload.write_u32::<BigEndian>(sender_channel)?;
// Reason code
payload.write_u32::<BigEndian>(reason_code)?;
// DescriptionSSH string
payload.write_u32::<BigEndian>(description.len() as u32)?;
payload.write_all(description.as_bytes())?;
// LanguageSSH string
payload.write_u32::<BigEndian>(language.len() as u32)?;
payload.write_all(language.as_bytes())?;
Ok(SshPacket::new(payload))
}
/// 构建SSH_MSG_CHANNEL_SUCCESS参考OpenSSH channel.c
fn build_channel_success(&self, channel: u32) -> Result<SshPacket> {
let mut payload = Vec::new();
payload.write_u8(PacketType::SSH_MSG_CHANNEL_SUCCESS as u8)?;
payload.write_u32::<BigEndian>(channel)?;
Ok(SshPacket::new(payload))
}
/// 构建SSH_MSG_CHANNEL_FAILURE参考OpenSSH channel.c
fn build_channel_failure(&self, channel: u32) -> Result<SshPacket> {
let mut payload = Vec::new();
payload.write_u8(PacketType::SSH_MSG_CHANNEL_FAILURE as u8)?;
payload.write_u32::<BigEndian>(channel)?;
Ok(SshPacket::new(payload))
}
/// 构建SSH_MSG_CHANNEL_CLOSE参考OpenSSH channel.c
pub fn build_channel_close(&self, channel: u32) -> Result<SshPacket> {
let mut payload = Vec::new();
payload.write_u8(PacketType::SSH_MSG_CHANNEL_CLOSE as u8)?;
payload.write_u32::<BigEndian>(channel)?;
Ok(SshPacket::new(payload))
}
/// 构建SSH_MSG_CHANNEL_DATAPhase 6新增
pub fn build_channel_data(&self, channel: u32, data: &[u8]) -> Result<SshPacket> {
let mut payload = Vec::new();
payload.write_u8(PacketType::SSH_MSG_CHANNEL_DATA as u8)?;
payload.write_u32::<BigEndian>(channel)?;
payload.write_u32::<BigEndian>(data.len() as u32)?;
payload.write_all(data)?;
Ok(SshPacket::new(payload))
}
/// 构建SSH_MSG_CHANNEL_EOFPhase 6新增
pub fn build_channel_eof(&self, channel: u32) -> Result<SshPacket> {
let mut payload = Vec::new();
payload.write_u8(PacketType::SSH_MSG_CHANNEL_EOF as u8)?;
payload.write_u32::<BigEndian>(channel)?;
Ok(SshPacket::new(payload))
}
/// 获取有输出待发送的channel IDPhase 6新增
pub fn get_channel_with_output(&self) -> Option<u32> {
for (&id, channel) in &self.channels {
if channel.output_buffer.is_some() {
return Some(id);
}
}
None
}
/// 获取channel输出Phase 6新增
pub fn get_channel_output(&mut self, channel_id: u32) -> Option<Vec<u8>> {
if let Some(channel) = self.channels.get_mut(&channel_id) {
channel.output_buffer.take()
} else {
None
}
}
/// 移除channelPhase 6新增
pub fn remove_channel(&mut self, channel_id: u32) {
self.channels.remove(&channel_id);
}
}
/// SSH Channel结构参考OpenSSH channel.c: struct channel
struct Channel {
server_channel: u32,
sender_channel: u32,
channel_type: String,
window_size: u32,
maximum_packet_size: u32,
state: ChannelState,
output_buffer: Option<Vec<u8>>, // Phase 6: 命令输出缓冲
sftp_handler: Option<SftpHandler>, // Phase 7: SFTP处理器
scp_handler: Option<ScpHandler>, // Phase 8: SCP处理器
rsync_handler: Option<RsyncHandler>, // Phase 8: rsync处理器
// Phase 13.3: 端口转发相关字段
direct_tcpip: Option<DirectTcpipChannel>, // direct-tcpip channelRemote forwarding
forwarded_tcpip: Option<ForwardedTcpipChannel>, // forwarded-tcpip channelLocal forwarding
}
/// SSH Channel状态参考OpenSSH channel.c
enum ChannelState {
Open,
Closing,
Closed,
}
/// SSH string读取辅助函数
fn read_ssh_string<R: std::io::Read>(reader: &mut R) -> Result<String> {
let length = reader.read_u32::<BigEndian>()?;
let mut buffer = vec![0u8; length as usize];
reader.read_exact(&mut buffer)?;
Ok(String::from_utf8(buffer)?)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_channel_manager_creation() {
let manager = ChannelManager::new();
assert_eq!(manager.next_channel_id, 0);
}
#[test]
fn test_channel_open_confirmation() {
let manager = ChannelManager::new();
let packet = manager.build_channel_open_confirmation(0, 100, 2097152, 32768).unwrap();
assert_eq!(packet.payload[0], PacketType::SSH_MSG_CHANNEL_OPEN_CONFIRMATION as u8);
}
#[test]
fn test_channel_success() {
let manager = ChannelManager::new();
let packet = manager.build_channel_success(0).unwrap();
assert_eq!(packet.payload[0], PacketType::SSH_MSG_CHANNEL_SUCCESS as u8);
}
}