From 4b4d9c3805aa8f53486c88f9bfceff30f5020178 Mon Sep 17 00:00:00 2001 From: Warren Date: Mon, 15 Jun 2026 16:13:07 +0800 Subject: [PATCH] Implement SSH Phase 13: Port forwarding foundation - 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 --- data/auth.sqlite | Bin 73728 -> 73728 bytes markbase-core/src/ssh_server/mod.rs | 1 + markbase-core/src/ssh_server/port_forward.rs | 299 +++++++++++++++++++ 3 files changed, 300 insertions(+) create mode 100644 markbase-core/src/ssh_server/port_forward.rs diff --git a/data/auth.sqlite b/data/auth.sqlite index 10cf1ffc119ac6bb096569805eee2c93f11bd025..1a317901c48cd9b19dd93237edd3f1956332750e 100644 GIT binary patch delta 171 zcmZoTz|wGlWr8%L+(a2?M!Ag%?J|s9lMhNMOy-ci!?tN^6GtD<=2x<8j6il{T_5-4 zZ*qT_Hcj39MgBRzBsVX!EK_cNZfaghQ6)PMC$lJ1N@{LCJJ;qf`XBfOxLKJwnb>(4 p7+Amzpr}1oQGf6CjDeFK{%-;s{Qo~cW8n5L{EYwkc>@`c000CxF@yjB delta 171 zcmZoTz|wGlWr8%L)I=F)MyZVn?J|t)lMhNMOy-ci!?tWk6GtEC=2x<8j6il{T_4Bf zZ*qT_mhIU5MgBRzBnL0EEK_cNZfaghQ6)PkC$lJ1N@{LCJNxD@`XBfOI9QoEnb@@b000+SF|hyu diff --git a/markbase-core/src/ssh_server/mod.rs b/markbase-core/src/ssh_server/mod.rs index cc3901d..ed1fa40 100644 --- a/markbase-core/src/ssh_server/mod.rs +++ b/markbase-core/src/ssh_server/mod.rs @@ -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}; diff --git a/markbase-core/src/ssh_server/port_forward.rs b/markbase-core/src/ssh_server/port_forward.rs new file mode 100644 index 0000000..545323d --- /dev/null +++ b/markbase-core/src/ssh_server/port_forward.rs @@ -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>>, +} + +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>)> { + 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>)> { + // 读取bind address(SSH string) + let bind_address = read_ssh_string(cursor)?; + + // 读取bind port + let bind_port = cursor.read_u32::()?; + + 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>)> { + let bind_address = read_ssh_string(cursor)?; + let bind_port = cursor.read_u32::()?; + + 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) -> Result> { + 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::(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 { + 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::()?; + + // 读取initial window size + let initial_window_size = cursor.read_u32::()?; + + // 读取maximum packet size + let max_packet_size = cursor.read_u32::()?; + + // 读取host to connect(SSH string) + let host_to_connect = read_ssh_string(&mut cursor)?; + + // 读取port to connect + let port_to_connect = cursor.read_u32::()?; + + // 读取originator address(SSH string) + let originator_address = read_ssh_string(&mut cursor)?; + + // 读取originator port + let originator_port = cursor.read_u32::()?; + + 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 { + 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::()?; + let initial_window_size = cursor.read_u32::()?; + let max_packet_size = cursor.read_u32::()?; + + // 读取bind address(SSH string) + let bind_address = read_ssh_string(&mut cursor)?; + + // 读取bind port + let bind_port = cursor.read_u32::()?; + + // 读取originator address(SSH string) + let originator_address = read_ssh_string(&mut cursor)?; + + // 读取originator port + let originator_port = cursor.read_u32::()?; + + 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 { + 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 { + 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(reader: &mut R) -> Result { + let length = reader.read_u32::()?; + 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); + } +} \ No newline at end of file