use alloc::string::{String, ToString};
use alloc::vec::Vec;
use crate::error::{Error, Result};
use crate::format::{Reader, Writer};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GlobalRequest {
TcpipForward {
bind_address: String,
bind_port: u32,
},
CancelTcpipForward {
bind_address: String,
bind_port: u32,
},
Keepalive,
Other {
name: String,
raw: Vec<u8>,
},
}
impl GlobalRequest {
pub fn name(&self) -> &str {
match self {
GlobalRequest::TcpipForward { .. } => "tcpip-forward",
GlobalRequest::CancelTcpipForward { .. } => "cancel-tcpip-forward",
GlobalRequest::Keepalive => "keepalive@openssh.com",
GlobalRequest::Other { name, .. } => name.as_str(),
}
}
pub fn encode(&self, w: &mut Writer) {
match self {
GlobalRequest::TcpipForward {
bind_address,
bind_port,
}
| GlobalRequest::CancelTcpipForward {
bind_address,
bind_port,
} => {
w.write_string(bind_address.as_bytes());
w.write_u32(*bind_port);
}
GlobalRequest::Keepalive => {}
GlobalRequest::Other { raw, .. } => {
w.write_raw(raw);
}
}
}
pub fn decode(name: &str, body: &[u8]) -> Result<Self> {
let mut r = Reader::new(body);
match name {
"tcpip-forward" => {
let bind_address = read_utf8(&mut r)?;
let bind_port = r.read_u32()?;
Ok(GlobalRequest::TcpipForward {
bind_address,
bind_port,
})
}
"cancel-tcpip-forward" => {
let bind_address = read_utf8(&mut r)?;
let bind_port = r.read_u32()?;
Ok(GlobalRequest::CancelTcpipForward {
bind_address,
bind_port,
})
}
"keepalive@openssh.com" => Ok(GlobalRequest::Keepalive),
other => Ok(GlobalRequest::Other {
name: other.to_string(),
raw: body.to_vec(),
}),
}
}
}
fn read_utf8(r: &mut Reader<'_>) -> Result<String> {
let bytes = r.read_string()?;
core::str::from_utf8(bytes)
.map(|s| s.to_string())
.map_err(|_| Error::Format("invalid utf-8 in global request"))
}