Implement SSH Phase 13: Port forwarding foundation
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled

- Add port_forward.rs module (285 lines)
- Define PortForwardType enum (Local/Remote/Dynamic)
- Implement PortForwardManager for managing forwards
- Handle SSH_MSG_GLOBAL_REQUEST for tcpip-forward
- Handle direct-tcpip and forwarded-tcpip channel types
- Support Local port forwarding (-L) foundation
- Support Remote port forwarding (-R) foundation
- Ready for integration with channel.rs
This commit is contained in:
Warren
2026-06-15 16:13:07 +08:00
parent eaabab2bff
commit 4b4d9c3805
3 changed files with 300 additions and 0 deletions

View File

@@ -14,6 +14,7 @@ pub mod channel;
pub mod sftp_handler;
pub mod scp_handler;
pub mod rsync_handler;
pub mod port_forward; // Phase 13: 端口转发模块
pub use server::SshServer;
pub use packet::{SshPacket, PacketType};

View File

@@ -0,0 +1,299 @@
// SSH端口转发协议实现Phase 13
// 参考OpenSSH channels.c和RFC 4254
use anyhow::{Result, anyhow};
use log::{info, warn, debug};
use std::net::{TcpListener, TcpStream, SocketAddr};
use std::io::{Read, Write};
use std::sync::{Arc, Mutex};
use std::thread;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
/// 端口转发类型参考RFC 4254
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PortForwardType {
Local, // Local port forwarding (-L)
Remote, // Remote port forwarding (-R)
Dynamic, // Dynamic port forwarding (-D, SOCKS)
}
/// 端口转发请求参考RFC 4254 Section 7
#[derive(Debug, Clone)]
pub struct PortForwardRequest {
pub forward_type: PortForwardType,
pub bind_address: String,
pub bind_port: u32,
pub host_to_connect: String,
pub port_to_connect: u32,
pub originator_address: String,
pub originator_port: u32,
}
/// 端口转发管理器参考OpenSSH channels.c
pub struct PortForwardManager {
// Active forwards: (bind_port, forward_type)
active_forwards: Arc<Mutex<Vec<(u32, PortForwardType)>>>,
}
impl PortForwardManager {
pub fn new() -> Self {
Self {
active_forwards: Arc::new(Mutex::new(Vec::new())),
}
}
/// 处理SSH_MSG_GLOBAL_REQUEST端口转发请求
/// 参考RFC 4254 Section 4
pub fn handle_global_request(&mut self, data: &[u8]) -> Result<(bool, Option<Vec<u8>>)> {
info!("Processing SSH_MSG_GLOBAL_REQUEST for port forwarding");
let mut cursor = std::io::Cursor::new(data);
cursor.set_position(1); // Skip packet type
// 读取请求名称SSH string
let request_name = read_ssh_string(&mut cursor)?;
info!("Global request: {}", request_name);
// 读取want-reply标志
let want_reply = cursor.read_u8()? != 0;
match request_name.as_str() {
"tcpip-forward" => {
// Local port forwarding (-L)
self.handle_tcpip_forward(&mut cursor, want_reply)
}
"cancel-tcpip-forward" => {
// Cancel port forwarding
self.handle_cancel_tcpip_forward(&mut cursor, want_reply)
}
"streamlocal-forward@openssh.com" => {
// Unix socket forwarding未实现
warn!("Unix socket forwarding not implemented");
Ok((false, None))
}
_ => {
warn!("Unsupported global request: {}", request_name);
Ok((false, None))
}
}
}
/// 处理tcpip-forward请求Local port forwarding
/// 参考RFC 4254 Section 7.1
fn handle_tcpip_forward(&mut self, cursor: &mut std::io::Cursor<&[u8]>, want_reply: bool) -> Result<(bool, Option<Vec<u8>>)> {
// 读取bind addressSSH string
let bind_address = read_ssh_string(cursor)?;
// 读取bind port
let bind_port = cursor.read_u32::<BigEndian>()?;
info!("tcpip-forward request: bind_address={}, bind_port={}", bind_address, bind_port);
// 添加到active forwards
let mut forwards = self.active_forwards.lock().unwrap();
forwards.push((bind_port, PortForwardType::Local));
// 返回成功响应包含bind_port
if want_reply {
let response = self.build_global_request_response(true, Some(bind_port))?;
Ok((true, Some(response)))
} else {
Ok((true, None))
}
}
/// 处理cancel-tcpip-forward请求
fn handle_cancel_tcpip_forward(&mut self, cursor: &mut std::io::Cursor<&[u8]>, want_reply: bool) -> Result<(bool, Option<Vec<u8>>)> {
let bind_address = read_ssh_string(cursor)?;
let bind_port = cursor.read_u32::<BigEndian>()?;
info!("cancel-tcpip-forward: bind_address={}, bind_port={}", bind_address, bind_port);
// 移除active forward
let mut forwards = self.active_forwards.lock().unwrap();
forwards.retain(|(port, _)| *port != bind_port);
if want_reply {
let response = self.build_global_request_response(true, None)?;
Ok((true, Some(response)))
} else {
Ok((true, None))
}
}
/// 构建SSH_MSG_REQUEST_SUCCESS/FAILURE响应
fn build_global_request_response(&self, success: bool, bound_port: Option<u32>) -> Result<Vec<u8>> {
use crate::ssh_server::packet::PacketType;
let mut response = Vec::new();
if success {
response.write_u8(PacketType::SSH_MSG_REQUEST_SUCCESS as u8)?;
// 如果有bound_port写入用于tcpip-forward响应
if let Some(port) = bound_port {
response.write_u32::<BigEndian>(port)?;
}
} else {
response.write_u8(PacketType::SSH_MSG_REQUEST_FAILURE as u8)?;
}
Ok(response)
}
/// 处理SSH_MSG_CHANNEL_OPEN for "direct-tcpip"Remote port forwarding
/// 参考RFC 4254 Section 7.2
pub fn handle_direct_tcpip_channel(&mut self, data: &[u8]) -> Result<DirectTcpipChannel> {
info!("Processing direct-tcpip channel open");
let mut cursor = std::io::Cursor::new(data);
cursor.set_position(1); // Skip packet type
// 读取channel type已知道是"direct-tcpip",跳过)
let _channel_type = read_ssh_string(&mut cursor)?;
// 读取sender_channel
let sender_channel = cursor.read_u32::<BigEndian>()?;
// 读取initial window size
let initial_window_size = cursor.read_u32::<BigEndian>()?;
// 读取maximum packet size
let max_packet_size = cursor.read_u32::<BigEndian>()?;
// 读取host to connectSSH string
let host_to_connect = read_ssh_string(&mut cursor)?;
// 读取port to connect
let port_to_connect = cursor.read_u32::<BigEndian>()?;
// 读取originator addressSSH string
let originator_address = read_ssh_string(&mut cursor)?;
// 读取originator port
let originator_port = cursor.read_u32::<BigEndian>()?;
info!("direct-tcpip: host={}, port={}, originator={}:{}",
host_to_connect, port_to_connect, originator_address, originator_port);
Ok(DirectTcpipChannel {
sender_channel,
initial_window_size,
max_packet_size,
host_to_connect,
port_to_connect,
originator_address,
originator_port,
})
}
/// 处理SSH_MSG_CHANNEL_OPEN for "forwarded-tcpip"Local port forwarding
/// 参考RFC 4254 Section 7.1
pub fn handle_forwarded_tcpip_channel(&mut self, data: &[u8]) -> Result<ForwardedTcpipChannel> {
info!("Processing forwarded-tcpip channel open");
let mut cursor = std::io::Cursor::new(data);
cursor.set_position(1);
let _channel_type = read_ssh_string(&mut cursor)?;
let sender_channel = cursor.read_u32::<BigEndian>()?;
let initial_window_size = cursor.read_u32::<BigEndian>()?;
let max_packet_size = cursor.read_u32::<BigEndian>()?;
// 读取bind addressSSH string
let bind_address = read_ssh_string(&mut cursor)?;
// 读取bind port
let bind_port = cursor.read_u32::<BigEndian>()?;
// 读取originator addressSSH string
let originator_address = read_ssh_string(&mut cursor)?;
// 读取originator port
let originator_port = cursor.read_u32::<BigEndian>()?;
info!("forwarded-tcpip: bind={}:{}, originator={}:{}",
bind_address, bind_port, originator_address, originator_port);
Ok(ForwardedTcpipChannel {
sender_channel,
initial_window_size,
max_packet_size,
bind_address,
bind_port,
originator_address,
originator_port,
})
}
/// 连接到目标主机(用于端口转发)
pub fn connect_to_target(host: &str, port: u32) -> Result<TcpStream> {
let addr = format!("{}:{}", host, port);
info!("Connecting to target: {}", addr);
let stream = TcpStream::connect(&addr)?;
info!("Connected to target successfully");
Ok(stream)
}
/// 创建监听socket用于Local port forwarding
pub fn create_listener(bind_address: &str, bind_port: u32) -> Result<TcpListener> {
let addr = if bind_address.is_empty() || bind_address == "0.0.0.0" {
format!("127.0.0.1:{}", bind_port)
} else {
format!("{}:{}", bind_address, bind_port)
};
info!("Creating listener on {}", addr);
let listener = TcpListener::bind(&addr)?;
info!("Listener created successfully");
Ok(listener)
}
}
/// Direct-tcpip channel结构Remote port forwarding
#[derive(Debug, Clone)]
pub struct DirectTcpipChannel {
pub sender_channel: u32,
pub initial_window_size: u32,
pub max_packet_size: u32,
pub host_to_connect: String,
pub port_to_connect: u32,
pub originator_address: String,
pub originator_port: u32,
}
/// Forwarded-tcpip channel结构Local port forwarding
#[derive(Debug, Clone)]
pub struct ForwardedTcpipChannel {
pub sender_channel: u32,
pub initial_window_size: u32,
pub max_packet_size: u32,
pub bind_address: String,
pub bind_port: u32,
pub originator_address: String,
pub originator_port: u32,
}
/// 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_port_forward_manager_creation() {
let manager = PortForwardManager::new();
assert_eq!(manager.active_forwards.lock().unwrap().len(), 0);
}
}