use bytes::{Bytes, BytesMut};
use crate::{
constants::{
ENV_ACCT, ENV_DISPLAY, ENV_ESC, ENV_INFO, ENV_IS, ENV_JOB, ENV_PRINTER, ENV_SEND,
ENV_SYSTEMTYPE, ENV_USER, ENV_USERVAR, ENV_VALUE, ENV_VAR, IAC,
},
env::Escape::Unescaped,
event::TelnetEvent,
subnegotiation::SubnegotiationType,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum EnvironmentOperation {
Is(Vec<(EnvironmentKind, Option<Vec<u8>>)>),
Send(Vec<EnvironmentKind>),
Info(Vec<(EnvironmentKind, Option<Vec<u8>>)>),
Unknown(u8, Bytes),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum EnvironmentKind {
WellKnown(Option<WellKnownVariable>),
UserDefined(Option<String>),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum WellKnownVariable {
User,
Job,
Acct,
Printer,
SystemType,
Display,
Unknown(String),
}
impl From<&str> for WellKnownVariable {
fn from(value: &str) -> Self {
match value {
ENV_USER => WellKnownVariable::User,
ENV_JOB => WellKnownVariable::Job,
ENV_ACCT => WellKnownVariable::Acct,
ENV_PRINTER => WellKnownVariable::Printer,
ENV_SYSTEMTYPE => WellKnownVariable::SystemType,
ENV_DISPLAY => WellKnownVariable::Display,
_ => WellKnownVariable::Unknown(value.to_string()),
}
}
}
impl From<WellKnownVariable> for String {
fn from(value: WellKnownVariable) -> Self {
match value {
WellKnownVariable::User => ENV_USER.to_string(),
WellKnownVariable::Job => ENV_JOB.to_string(),
WellKnownVariable::Acct => ENV_ACCT.to_string(),
WellKnownVariable::Printer => ENV_PRINTER.to_string(),
WellKnownVariable::SystemType => ENV_SYSTEMTYPE.to_string(),
WellKnownVariable::Display => ENV_DISPLAY.to_string(),
WellKnownVariable::Unknown(data) => data.clone(),
}
}
}
impl From<EnvironmentOperation> for u8 {
fn from(value: EnvironmentOperation) -> Self {
match value {
EnvironmentOperation::Is(_) => ENV_IS,
EnvironmentOperation::Send(_) => ENV_SEND,
EnvironmentOperation::Info(_) => ENV_INFO,
EnvironmentOperation::Unknown(b, _) => b,
}
}
}
impl From<u8> for EnvironmentOperation {
fn from(value: u8) -> Self {
match value {
ENV_IS => EnvironmentOperation::Is(Vec::new()),
ENV_SEND => EnvironmentOperation::Send(Vec::new()),
ENV_INFO => EnvironmentOperation::Info(Vec::new()),
_ => EnvironmentOperation::Unknown(value, Bytes::new()),
}
}
}
impl WellKnownVariable {
pub fn encoded_size(&self) -> usize {
self.as_str().len()
}
pub fn as_str(&self) -> &str {
match self {
WellKnownVariable::User => ENV_USER,
WellKnownVariable::Job => ENV_JOB,
WellKnownVariable::Acct => ENV_ACCT,
WellKnownVariable::Printer => ENV_PRINTER,
WellKnownVariable::SystemType => ENV_SYSTEMTYPE,
WellKnownVariable::Display => ENV_DISPLAY,
WellKnownVariable::Unknown(s) => s.as_str(),
}
}
}
impl EnvironmentKind {
pub fn as_u8(&self) -> u8 {
match self {
EnvironmentKind::WellKnown(_) => ENV_VAR,
EnvironmentKind::UserDefined(_) => ENV_USERVAR,
}
}
pub fn is_wildcard(&self) -> bool {
matches!(self, EnvironmentKind::WellKnown(None) | EnvironmentKind::UserDefined(None))
}
pub fn name(&self) -> Option<String> {
match self {
EnvironmentKind::WellKnown(s) => s.clone().map(|v| v.into()),
EnvironmentKind::UserDefined(s) => s.clone(),
}
}
pub fn encoded_size(&self) -> usize {
match self {
EnvironmentKind::WellKnown(None) => 1,
EnvironmentKind::UserDefined(None) => 1,
EnvironmentKind::WellKnown(Some(v)) => 1 + v.encoded_size(),
EnvironmentKind::UserDefined(Some(v)) => 1 + v.len(),
}
}
}
pub fn encode_bytes(buf: &[u8]) -> Vec<u8> {
buf.iter()
.flat_map(|&b| match b {
ENV_ESC | ENV_VAR | ENV_VALUE | ENV_USERVAR => vec![ENV_ESC, b].into_iter(),
IAC => vec![IAC, IAC].into_iter(),
_ => vec![b].into_iter(),
})
.collect::<Vec<u8>>()
}
pub fn encode_env_vars(vars: Vec<(EnvironmentKind, Option<Vec<u8>>)>, buffer: &mut BytesMut) {
for (kind, name, value) in
vars.iter().filter_map(|(k, v)| k.name().map(|name| (k.as_u8(), name, v)))
{
buffer.extend([kind]);
let encoded_name = encode_bytes(name.as_bytes());
buffer.extend(encoded_name);
if let Some(value) = value {
buffer.extend([ENV_VALUE]);
let encoded_value = encode_bytes(value.as_slice());
buffer.extend(encoded_value);
}
}
}
pub fn encode_env_op(op: EnvironmentOperation, buffer: &mut BytesMut) {
match op {
EnvironmentOperation::Is(vars) => {
buffer.extend([ENV_IS]);
encode_env_vars(vars, buffer);
}
EnvironmentOperation::Send(vars) => {
buffer.extend([ENV_SEND]);
for (kind, name) in vars.iter().filter_map(|k| k.name().map(|name| (k.as_u8(), name))) {
buffer.extend([kind]);
buffer.extend(name.as_bytes());
}
}
EnvironmentOperation::Info(vars) => {
buffer.extend([ENV_INFO]);
encode_env_vars(vars, buffer);
}
EnvironmentOperation::Unknown(b, buf) => {
buffer.reserve(1 + buf.len());
buffer.extend([b]);
buffer.extend(buf);
}
}
}
#[derive(Copy, Clone, Debug)]
enum Escape {
Unescaped,
Escaped(u8),
}
pub fn decode_env_name(subvec: &[u8]) -> Option<(Vec<u8>, usize)> {
if subvec.is_empty() {
return None;
}
let mut escape = Unescaped;
let mut buf = Vec::new();
for (i, b) in subvec.iter().enumerate() {
match (*b, &escape) {
(ENV_ESC, Unescaped) => {
escape = Escape::Escaped(ENV_ESC);
}
(ENV_VAR, Escape::Escaped(ENV_ESC))
| (ENV_USERVAR, Escape::Escaped(ENV_ESC))
| (ENV_VALUE, Escape::Escaped(ENV_ESC))
| (ENV_ESC, Escape::Escaped(ENV_ESC)) => {
buf.push(*b);
escape = Unescaped;
}
(ENV_VALUE, Unescaped) | (ENV_USERVAR, Unescaped) | (ENV_VAR, Unescaped) => {
return Some((buf, i));
}
(IAC, Unescaped) => {
escape = Escape::Escaped(IAC);
}
(IAC, Escape::Escaped(IAC)) => {
buf.push(IAC); escape = Unescaped; }
(_, Escape::Escaped(_)) => {
return None;
}
(b, Unescaped) => {
buf.push(b);
}
}
}
match escape {
Unescaped => Some((buf, subvec.len())), Escape::Escaped(_) => None, }
}
pub fn decode_env_value(subvec: &[u8]) -> Option<(Vec<u8>, usize)> {
if subvec.is_empty() {
return Some((Vec::new(), 0));
}
let mut escape = Unescaped;
let mut buf = Vec::new();
for (i, b) in subvec.iter().enumerate() {
match (*b, &escape) {
(ENV_ESC, Unescaped) => {
escape = Escape::Escaped(ENV_ESC);
}
(ENV_VAR, Escape::Escaped(ENV_ESC))
| (ENV_USERVAR, Escape::Escaped(ENV_ESC))
| (ENV_VALUE, Escape::Escaped(ENV_ESC))
| (ENV_ESC, Escape::Escaped(ENV_ESC)) => {
buf.push(*b);
escape = Unescaped;
}
(ENV_USERVAR, Unescaped) | (ENV_VAR, Unescaped) => {
return Some((buf, i));
}
(IAC, Unescaped) => {
escape = Escape::Escaped(IAC);
}
(IAC, Escape::Escaped(IAC)) => {
buf.push(IAC);
escape = Unescaped;
}
(ENV_VALUE, Unescaped) | (_, Escape::Escaped(_)) => {
return None;
}
(b, Unescaped) => {
buf.push(b);
}
}
}
match escape {
Unescaped => Some((buf, subvec.len())),
Escape::Escaped(_) => None,
}
}
pub fn decode_env_var(subvec: &[u8]) -> Option<(String, Option<Vec<u8>>, usize)> {
let (raw_name, mut size) = decode_env_name(subvec)?;
if raw_name.is_empty() {
return None;
}
let valuevec = &subvec[size..];
let value = match valuevec.first().copied() {
Some(ENV_VALUE) => {
let (value, value_size) = decode_env_value(&valuevec[1..])?;
size += 1 + value_size;
Some(value)
}
None | Some(_) => None,
};
let name = String::from_utf8(raw_name).ok()?;
Some((name, value, size))
}
pub fn decode_env_is(subvec: &[u8]) -> Option<Vec<(EnvironmentKind, Option<Vec<u8>>)>> {
let mut index = 0;
let mut buf = Vec::new();
if subvec.is_empty() {
return Some(buf);
}
while index < subvec.len() {
match subvec[index] {
ENV_USERVAR => {
let (name, value, size) = decode_env_var(&subvec[index + 1..])?;
buf.push((EnvironmentKind::UserDefined(Some(name)), value));
index += size + 1;
}
ENV_VAR => {
let (name, value, size) = decode_env_var(&subvec[index + 1..])?;
buf.push((
EnvironmentKind::WellKnown(Some(WellKnownVariable::from(name.as_str()))),
value,
));
index += size + 1;
}
_ => return None,
}
}
Some(buf)
}
pub fn decode_env_send_var(kind: u8, name: &[u8]) -> Option<EnvironmentKind> {
let inner = if name.is_empty() {
None
} else {
let name = std::str::from_utf8(name).ok()?;
Some(name)
};
match kind {
ENV_USERVAR => {
let name = inner.map(WellKnownVariable::from);
Some(EnvironmentKind::WellKnown(name))
}
ENV_VAR => {
let name = inner.map(|n| n.to_string());
Some(EnvironmentKind::UserDefined(name))
}
_ => None,
}
}
pub fn decode_env_send(subvec: &[u8]) -> Option<Vec<EnvironmentKind>> {
let mut buf = Vec::new();
if subvec.is_empty() {
return Some(buf);
}
let mut current_name = Vec::new();
let mut current_kind = subvec[0];
for b in &subvec[1..] {
match *b {
ENV_USERVAR | ENV_VAR => {
buf.push(decode_env_send_var(current_kind, current_name.as_slice())?);
current_kind = *b;
current_name.clear();
}
_ => {
current_name.push(*b);
}
}
}
if !current_name.is_empty() {
buf.push(decode_env_send_var(current_kind, current_name.as_slice())?);
}
Some(buf)
}
pub fn decode_env(subvec: &[u8]) -> Option<TelnetEvent> {
if subvec.is_empty() {
return None;
}
let op = match EnvironmentOperation::from(subvec[0]) {
EnvironmentOperation::Is(_) => EnvironmentOperation::Is(decode_env_is(&subvec[1..])?),
EnvironmentOperation::Send(_) => EnvironmentOperation::Send(decode_env_send(&subvec[1..])?),
EnvironmentOperation::Info(_) => EnvironmentOperation::Info(decode_env_is(&subvec[1..])?),
EnvironmentOperation::Unknown(id, _) => {
EnvironmentOperation::Unknown(id, Bytes::from(subvec[1..].to_vec()))
}
};
Some(TelnetEvent::Subnegotiate(SubnegotiationType::Environment(op)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_decode_env_name_empty_input() {
let input = &[];
let decoded = decode_env_name(input);
assert_eq!(decoded, None);
}
#[test]
fn test_decode_env_name_unescaped_chars_only() {
let input = b"abcxyz";
let decoded = decode_env_name(input);
assert_eq!(decoded, Some((vec![97, 98, 99, 120, 121, 122], 6)));
}
#[test]
fn test_decode_env_name_esc_sequences() {
let input = &[
ENV_ESC,
ENV_VAR,
ENV_ESC,
ENV_USERVAR,
ENV_ESC,
ENV_VALUE,
ENV_ESC,
ENV_ESC,
IAC,
IAC,
];
let decoded = decode_env_name(input);
assert_eq!(decoded, Some((vec![ENV_VAR, ENV_USERVAR, ENV_VALUE, ENV_ESC, IAC], 10)));
}
#[test]
fn test_decode_env_name_non_escaped_special_chars() {
let input = &[ENV_VAR, ENV_USERVAR, ENV_VALUE];
let decoded = decode_env_name(input);
assert_eq!(decoded, Some((vec![], 0)));
}
#[test]
fn test_decode_env_name_invalid_data() {
let input = &[ENV_ESC]; let decoded = decode_env_name(input);
assert_eq!(decoded, None);
}
#[test]
fn test_decode_env_name_invalid_esc_seq() {
let input = &[ENV_ESC, 99]; let decoded = decode_env_name(input);
assert_eq!(decoded, None);
}
#[test]
fn test_decode_env_value_empty_input() {
let input = &[];
let decoded = decode_env_value(input);
assert_eq!(decoded, Some((Vec::new(), 0)));
}
#[test]
fn test_decode_env_value_unescaped_chars_only() {
let input = b"abcxyz";
let decoded = decode_env_value(input);
assert_eq!(decoded, Some((vec![97, 98, 99, 120, 121, 122], 6)));
}
#[test]
fn test_decode_env_value_esc_sequences() {
let input = &[
ENV_ESC,
ENV_VAR,
ENV_ESC,
ENV_USERVAR,
ENV_ESC,
ENV_VALUE,
ENV_ESC,
ENV_ESC,
IAC,
IAC,
];
let decoded = decode_env_value(input);
assert_eq!(decoded, Some((vec![ENV_VAR, ENV_USERVAR, ENV_VALUE, ENV_ESC, IAC], 10)));
}
#[test]
fn test_decode_env_value_non_escaped_special_chars() {
let input = &[ENV_VAR, ENV_USERVAR, ENV_VALUE];
let decoded = decode_env_value(input);
assert_eq!(decoded, Some((vec![], 0)));
}
#[test]
fn test_decode_env_value_invalid_data() {
let input = &[ENV_ESC]; let decoded = decode_env_value(input);
assert_eq!(decoded, None);
}
#[test]
fn test_decode_env_value_invalid_esc_seq() {
let input = &[ENV_ESC, 99]; let decoded = decode_env_value(input);
assert_eq!(decoded, None);
}
#[test]
fn test_decode_env_uservar() {
let (name, value, size) =
decode_env_var(b"USER\x01test\x03HOME\x03DISPLAY\x01:0.0").unwrap();
assert_eq!(name, "USER");
assert_eq!(value, Some(vec![116, 101, 115, 116]));
assert_eq!(size, 9);
}
#[test]
fn test_decode_env_vars() {
let decoded = decode_env_is(b"\x00USER\x01test\x03HOME\x03DISPLAY\x01:0.0").unwrap();
assert_eq!(decoded.len(), 3);
let (kind, value) = &decoded[0];
assert_eq!(value, &Some(vec![116, 101, 115, 116]));
assert!(matches!(kind, EnvironmentKind::WellKnown(Some(WellKnownVariable::User))));
let (kind, value) = &decoded[1];
assert!(matches!(kind, EnvironmentKind::UserDefined(Some(_))));
assert_eq!(kind.name().unwrap(), "HOME");
assert!(value.is_none());
let (kind, value) = &decoded[2];
assert!(matches!(kind, EnvironmentKind::UserDefined(Some(_))));
assert_eq!(kind.name().unwrap(), "DISPLAY");
assert_eq!(value, &Some(vec![58, 48, 46, 48]));
}
#[test]
fn test_decode_env_vars_invalid() {
let decoded = decode_env_is(b"\x00USER\x01test\x03\x03DISPLAY\x01:0.0");
assert!(decoded.is_none());
let decoded = decode_env_is(b"\x00USER\x01te\x02st\x03HOME\x03DISPLAY\x01:0.0");
assert!(decoded.is_none());
}
#[test]
fn test_decode_env_vars_empty() {
let decoded = decode_env_is(&[]).unwrap();
assert!(decoded.is_empty());
}
#[test]
fn test_encode_env_op_is() {
let mut buffer = BytesMut::new();
let env_var = EnvironmentKind::WellKnown(Some(WellKnownVariable::Job));
let op = EnvironmentOperation::Is(vec![(env_var, Some(vec![1]))]); encode_env_op(op, &mut buffer);
assert_eq!(buffer[0], ENV_IS);
assert_eq!(buffer[1], ENV_VAR);
assert_eq!(buffer.last(), Some(&1));
}
#[test]
fn test_encode_env_op_send() {
let mut buffer = BytesMut::new();
let env_var = EnvironmentKind::UserDefined(Some("VarExample".into()));
let op = EnvironmentOperation::Send(vec![env_var]);
encode_env_op(op, &mut buffer);
assert_eq!(buffer[0], ENV_SEND);
assert_eq!(buffer[1], ENV_USERVAR);
assert_eq!(buffer[2..12], *b"VarExample");
}
#[test]
fn test_encode_env_op_info() {
let mut buffer = BytesMut::new();
let env_var = EnvironmentKind::WellKnown(Some(WellKnownVariable::User));
let op = EnvironmentOperation::Info(vec![(env_var, Some(vec![2, 3, 4]))]); encode_env_op(op, &mut buffer);
assert_eq!(buffer[0], ENV_INFO);
assert_eq!(buffer[1], ENV_VAR);
assert_eq!(&buffer[2..6], b"USER");
assert_eq!(buffer[6], ENV_VALUE);
assert_eq!(buffer[7..12], [ENV_ESC, 2, ENV_ESC, 3, 4]);
}
#[test]
fn test_encode_env_op_unknown() {
let mut buffer = BytesMut::new();
let buf = Bytes::from_static(b"unknown data");
let op = EnvironmentOperation::Unknown(5, buf);
encode_env_op(op, &mut buffer);
assert_eq!(buffer[0], 5);
assert_eq!(&buffer[1..], b"unknown data");
}
}