Implement SSH X11 forwarding Phase 2
- Add 'x11' channel type in handle_channel_open() - Add handle_x11_channel_open() method - Add 'x11-req' request in handle_channel_request() - Add handle_x11_request() method - Parse x11-req parameters (single_connection, auth_protocol, auth_cookie, screen_number) - Create X11ForwardContext from DISPLAY env All 182 tests pass.
This commit is contained in:
@@ -130,6 +130,16 @@ impl ChannelManager {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"x11" => {
|
||||||
|
// Phase 2: X11 forwarding channel (RFC 4254 §7.2)
|
||||||
|
info!("Received x11 channel open (X11 forwarding)");
|
||||||
|
self.handle_x11_channel_open(
|
||||||
|
sender_channel,
|
||||||
|
initial_window_size,
|
||||||
|
maximum_packet_size,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
warn!("Unsupported channel type: {}", channel_type);
|
warn!("Unsupported channel type: {}", channel_type);
|
||||||
self.build_channel_open_failure(
|
self.build_channel_open_failure(
|
||||||
@@ -373,6 +383,68 @@ impl ChannelManager {
|
|||||||
maximum_packet_size,
|
maximum_packet_size,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Phase 2: 处理x11 channel open(RFC 4254 §7.2)
|
||||||
|
fn handle_x11_channel_open(
|
||||||
|
&mut self,
|
||||||
|
sender_channel: u32,
|
||||||
|
initial_window_size: u32,
|
||||||
|
maximum_packet_size: u32,
|
||||||
|
) -> Result<SshPacket> {
|
||||||
|
info!("Processing x11 channel open");
|
||||||
|
|
||||||
|
// 创建 X11ForwardContext(从 DISPLAY 环境变量)
|
||||||
|
let display = std::env::var("DISPLAY").unwrap_or_else(|_| ":0".to_string());
|
||||||
|
let x11_ctx = super::x11_forward::X11ForwardContext::new(&display)?;
|
||||||
|
|
||||||
|
let server_channel = self.next_channel_id;
|
||||||
|
self.next_channel_id += 1;
|
||||||
|
|
||||||
|
let channel = Channel {
|
||||||
|
server_channel,
|
||||||
|
sender_channel,
|
||||||
|
channel_type: "x11".to_string(),
|
||||||
|
|
||||||
|
// Phase 15: Window Control
|
||||||
|
remote_window: initial_window_size,
|
||||||
|
remote_maxpacket: maximum_packet_size,
|
||||||
|
local_window: 2097152,
|
||||||
|
local_window_max: 2097152,
|
||||||
|
local_consumed: 0,
|
||||||
|
local_maxpacket: 32768,
|
||||||
|
|
||||||
|
window_size: initial_window_size,
|
||||||
|
maximum_packet_size,
|
||||||
|
state: ChannelState::Open,
|
||||||
|
output_buffer: None,
|
||||||
|
sftp_handler: None,
|
||||||
|
scp_handler: None,
|
||||||
|
rsync_handler: None,
|
||||||
|
exec_process: None,
|
||||||
|
exit_status: None,
|
||||||
|
sftp_input_buffer: Vec::new(),
|
||||||
|
scp_input_buffer: Vec::new(),
|
||||||
|
scp_state: ScpState::Idle,
|
||||||
|
scp_output_file: None,
|
||||||
|
direct_tcpip: None,
|
||||||
|
forwarded_tcpip: None,
|
||||||
|
auth_agent_socket: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.channels.insert(server_channel, channel);
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"x11 channel created: server_channel={}, display={}",
|
||||||
|
server_channel, display
|
||||||
|
);
|
||||||
|
|
||||||
|
self.build_channel_open_confirmation(
|
||||||
|
server_channel,
|
||||||
|
sender_channel,
|
||||||
|
initial_window_size,
|
||||||
|
maximum_packet_size,
|
||||||
|
)
|
||||||
|
}
|
||||||
/// 处理SSH_MSG_CHANNEL_REQUEST(参考OpenSSH channel.c: channel_request())
|
/// 处理SSH_MSG_CHANNEL_REQUEST(参考OpenSSH channel.c: channel_request())
|
||||||
pub fn handle_channel_request(&mut self, packet: &SshPacket) -> Result<Option<SshPacket>> {
|
pub fn handle_channel_request(&mut self, packet: &SshPacket) -> Result<Option<SshPacket>> {
|
||||||
info!("Processing SSH_MSG_CHANNEL_REQUEST");
|
info!("Processing SSH_MSG_CHANNEL_REQUEST");
|
||||||
@@ -414,6 +486,9 @@ impl ChannelManager {
|
|||||||
self.handle_pty_request(&mut cursor, recipient_channel, want_reply) // 移除?操作符
|
self.handle_pty_request(&mut cursor, recipient_channel, want_reply) // 移除?操作符
|
||||||
} else if request_type == "auth-agent-req@openssh.com" {
|
} else if request_type == "auth-agent-req@openssh.com" {
|
||||||
self.handle_auth_agent_request(recipient_channel, want_reply)
|
self.handle_auth_agent_request(recipient_channel, want_reply)
|
||||||
|
} else if request_type == "x11-req" {
|
||||||
|
// Phase 2: X11 forwarding request (RFC 4254 §7.2)
|
||||||
|
self.handle_x11_request(&mut cursor, recipient_channel, want_reply)
|
||||||
} else {
|
} else {
|
||||||
warn!("Unsupported channel request: {}", request_type);
|
warn!("Unsupported channel request: {}", request_type);
|
||||||
if want_reply {
|
if want_reply {
|
||||||
@@ -1407,6 +1482,50 @@ impl ChannelManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Phase 2: 处理x11-req请求(RFC 4254 §7.2)
|
||||||
|
fn handle_x11_request(
|
||||||
|
&mut self,
|
||||||
|
cursor: &mut std::io::Cursor<&[u8]>,
|
||||||
|
channel: u32,
|
||||||
|
want_reply: bool,
|
||||||
|
) -> Result<Option<SshPacket>> {
|
||||||
|
info!("Handling x11-req request for channel {}", channel);
|
||||||
|
|
||||||
|
// 读取 x11-req 参数(RFC 4254 §7.2)
|
||||||
|
// single_connection: boolean
|
||||||
|
let single_connection = cursor.read_u8()? != 0;
|
||||||
|
|
||||||
|
// auth_protocol: SSH string (e.g., "MIT-MAGIC-COOKIE-1")
|
||||||
|
let auth_protocol = read_ssh_string(cursor)?;
|
||||||
|
|
||||||
|
// auth_cookie: SSH string (hex-encoded cookie)
|
||||||
|
let auth_cookie_hex = read_ssh_string(cursor)?;
|
||||||
|
|
||||||
|
// screen_number: u32
|
||||||
|
let screen_number = cursor.read_u32::<BigEndian>()?;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"x11-req: single={}, protocol={}, screen={}",
|
||||||
|
single_connection, auth_protocol, screen_number
|
||||||
|
);
|
||||||
|
|
||||||
|
// 创建 X11ForwardContext
|
||||||
|
let display = std::env::var("DISPLAY").unwrap_or_else(|_| ":0".to_string());
|
||||||
|
let x11_ctx = super::x11_forward::X11ForwardContext::new(&display)?;
|
||||||
|
|
||||||
|
// 设置 DISPLAY 环境变量(client 会使用)
|
||||||
|
// Server 需要在 exec/shell 环境中设置 DISPLAY
|
||||||
|
// 这里只是记录,实际设置在 exec/shell handler 中
|
||||||
|
|
||||||
|
info!("X11 forwarding enabled: display={}", x11_ctx.display_env());
|
||||||
|
|
||||||
|
if want_reply {
|
||||||
|
Ok(Some(self.build_channel_success(channel)?))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// ⭐⭐⭐⭐⭐ Phase 17: Check if a specific channel has an exec process
|
/// ⭐⭐⭐⭐⭐ Phase 17: Check if a specific channel has an exec process
|
||||||
pub fn channel_has_exec_process(&self, channel_id: u32) -> bool {
|
pub fn channel_has_exec_process(&self, channel_id: u32) -> bool {
|
||||||
self.channels.get(&channel_id).is_some_and(|ch| ch.exec_process.is_some())
|
self.channels.get(&channel_id).is_some_and(|ch| ch.exec_process.is_some())
|
||||||
@@ -1685,9 +1804,9 @@ impl ChannelManager {
|
|||||||
if let Some(hook) = &self.upload_hook {
|
if let Some(hook) = &self.upload_hook {
|
||||||
if let Err(e) = hook.trigger(&path, &self.user_uuid) {
|
if let Err(e) = hook.trigger(&path, &self.user_uuid) {
|
||||||
warn!("Upload hook failed for {:?}: {}", path, e);
|
warn!("Upload hook failed for {:?}: {}", path, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 没有剩余数据,返回child_exited标志
|
// 没有剩余数据,返回child_exited标志
|
||||||
|
|||||||
Reference in New Issue
Block a user