From 8b2ba27f459114426da7d14e6e9fa14a5dd01c1b Mon Sep 17 00:00:00 2001 From: Vladyslav Nikonov Date: Thu, 16 Jan 2025 12:00:18 +0200 Subject: [PATCH] chore: remove now-proto-pdu crate (#633) --- Cargo.lock | 10 - Cargo.toml | 1 - crates/ironrdp-testsuite-core/Cargo.toml | 1 - crates/ironrdp-testsuite-core/src/lib.rs | 2 - .../ironrdp-testsuite-core/src/now_proto.rs | 17 - crates/ironrdp-testsuite-core/tests/main.rs | 1 - .../tests/now_proto/buffer.rs | 30 - .../tests/now_proto/exec.rs | 126 -- .../tests/now_proto/mod.rs | 7 - .../tests/now_proto/number.rs | 127 -- .../tests/now_proto/session.rs | 41 - .../tests/now_proto/status.rs | 20 - .../tests/now_proto/string.rs | 68 - .../tests/now_proto/system.rs | 16 - crates/now-proto-pdu/Cargo.toml | 31 - crates/now-proto-pdu/README.md | 7 - crates/now-proto-pdu/src/core/buffer.rs | 174 --- crates/now-proto-pdu/src/core/header.rs | 70 - crates/now-proto-pdu/src/core/mod.rs | 13 - crates/now-proto-pdu/src/core/number.rs | 766 --------- crates/now-proto-pdu/src/core/status.rs | 139 -- crates/now-proto-pdu/src/core/string.rs | 320 ---- crates/now-proto-pdu/src/exec/abort.rs | 95 -- crates/now-proto-pdu/src/exec/batch.rs | 94 -- crates/now-proto-pdu/src/exec/cancel_req.rs | 77 - crates/now-proto-pdu/src/exec/cancel_rsp.rs | 93 -- crates/now-proto-pdu/src/exec/capset.rs | 105 -- crates/now-proto-pdu/src/exec/cmd.rs | 1 - crates/now-proto-pdu/src/exec/data.rs | 136 -- crates/now-proto-pdu/src/exec/mod.rs | 142 -- crates/now-proto-pdu/src/exec/process.rs | 140 -- crates/now-proto-pdu/src/exec/pwsh.rs | 175 --- crates/now-proto-pdu/src/exec/result.rs | 93 -- crates/now-proto-pdu/src/exec/run.rs | 112 -- crates/now-proto-pdu/src/exec/shell.rs | 125 -- crates/now-proto-pdu/src/exec/win_ps.rs | 217 --- crates/now-proto-pdu/src/lib.rs | 30 - crates/now-proto-pdu/src/macros.rs | 14 - crates/now-proto-pdu/src/message.rs | 58 - crates/now-proto-pdu/src/session/lock.rs | 54 - crates/now-proto-pdu/src/session/logoff.rs | 54 - crates/now-proto-pdu/src/session/mod.rs | 78 - .../now-proto-pdu/src/session/msg_box_req.rs | 255 --- .../now-proto-pdu/src/session/msg_box_rsp.rs | 140 -- crates/now-proto-pdu/src/system/mod.rs | 57 - crates/now-proto-pdu/src/system/shutdown.rs | 118 -- docs/NOW-spec.md | 1367 ----------------- docs/make.ps1 | 3 - 48 files changed, 5820 deletions(-) delete mode 100644 crates/ironrdp-testsuite-core/src/now_proto.rs delete mode 100644 crates/ironrdp-testsuite-core/tests/now_proto/buffer.rs delete mode 100644 crates/ironrdp-testsuite-core/tests/now_proto/exec.rs delete mode 100644 crates/ironrdp-testsuite-core/tests/now_proto/mod.rs delete mode 100644 crates/ironrdp-testsuite-core/tests/now_proto/number.rs delete mode 100644 crates/ironrdp-testsuite-core/tests/now_proto/session.rs delete mode 100644 crates/ironrdp-testsuite-core/tests/now_proto/status.rs delete mode 100644 crates/ironrdp-testsuite-core/tests/now_proto/string.rs delete mode 100644 crates/ironrdp-testsuite-core/tests/now_proto/system.rs delete mode 100644 crates/now-proto-pdu/Cargo.toml delete mode 100644 crates/now-proto-pdu/README.md delete mode 100644 crates/now-proto-pdu/src/core/buffer.rs delete mode 100644 crates/now-proto-pdu/src/core/header.rs delete mode 100644 crates/now-proto-pdu/src/core/mod.rs delete mode 100644 crates/now-proto-pdu/src/core/number.rs delete mode 100644 crates/now-proto-pdu/src/core/status.rs delete mode 100644 crates/now-proto-pdu/src/core/string.rs delete mode 100644 crates/now-proto-pdu/src/exec/abort.rs delete mode 100644 crates/now-proto-pdu/src/exec/batch.rs delete mode 100644 crates/now-proto-pdu/src/exec/cancel_req.rs delete mode 100644 crates/now-proto-pdu/src/exec/cancel_rsp.rs delete mode 100644 crates/now-proto-pdu/src/exec/capset.rs delete mode 100644 crates/now-proto-pdu/src/exec/cmd.rs delete mode 100644 crates/now-proto-pdu/src/exec/data.rs delete mode 100644 crates/now-proto-pdu/src/exec/mod.rs delete mode 100644 crates/now-proto-pdu/src/exec/process.rs delete mode 100644 crates/now-proto-pdu/src/exec/pwsh.rs delete mode 100644 crates/now-proto-pdu/src/exec/result.rs delete mode 100644 crates/now-proto-pdu/src/exec/run.rs delete mode 100644 crates/now-proto-pdu/src/exec/shell.rs delete mode 100644 crates/now-proto-pdu/src/exec/win_ps.rs delete mode 100644 crates/now-proto-pdu/src/lib.rs delete mode 100644 crates/now-proto-pdu/src/macros.rs delete mode 100644 crates/now-proto-pdu/src/message.rs delete mode 100644 crates/now-proto-pdu/src/session/lock.rs delete mode 100644 crates/now-proto-pdu/src/session/logoff.rs delete mode 100644 crates/now-proto-pdu/src/session/mod.rs delete mode 100644 crates/now-proto-pdu/src/session/msg_box_req.rs delete mode 100644 crates/now-proto-pdu/src/session/msg_box_rsp.rs delete mode 100644 crates/now-proto-pdu/src/system/mod.rs delete mode 100644 crates/now-proto-pdu/src/system/shutdown.rs delete mode 100644 docs/NOW-spec.md delete mode 100644 docs/make.ps1 diff --git a/Cargo.lock b/Cargo.lock index 6c48c5a5..7bc682ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2713,7 +2713,6 @@ dependencies = [ "ironrdp-rdpsnd", "ironrdp-session", "lazy_static", - "now-proto-pdu", "paste", "png", "pretty_assertions", @@ -3178,15 +3177,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "now-proto-pdu" -version = "0.1.0" -dependencies = [ - "bitflags 2.6.0", - "ironrdp-core", - "ironrdp-pdu", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" diff --git a/Cargo.toml b/Cargo.toml index abf7b701..05056442 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,6 @@ ironrdp-testsuite-core = { path = "crates/ironrdp-testsuite-core" } ironrdp-tls = { version = "0.1", path = "crates/ironrdp-tls" } ironrdp-tokio = { version = "0.2", path = "crates/ironrdp-tokio" } ironrdp = { version = "0.7", path = "crates/ironrdp" } -now-proto-pdu = { version = "0.1", path = "crates/now-proto-pdu" } bitflags = "2.4" expect-test = "1" diff --git a/crates/ironrdp-testsuite-core/Cargo.toml b/crates/ironrdp-testsuite-core/Cargo.toml index c55133db..c6417212 100644 --- a/crates/ironrdp-testsuite-core/Cargo.toml +++ b/crates/ironrdp-testsuite-core/Cargo.toml @@ -21,7 +21,6 @@ expect-test = "1" ironrdp-core.workspace = true ironrdp-pdu.workspace = true lazy_static.workspace = true # TODO: remove in favor of https://doc.rust-lang.org/std/sync/struct.OnceLock.html -now-proto-pdu.workspace = true paste = "1" [dev-dependencies] diff --git a/crates/ironrdp-testsuite-core/src/lib.rs b/crates/ironrdp-testsuite-core/src/lib.rs index 253ec042..190f12f3 100644 --- a/crates/ironrdp-testsuite-core/src/lib.rs +++ b/crates/ironrdp-testsuite-core/src/lib.rs @@ -31,7 +31,5 @@ pub mod network_data; pub mod rdp; pub mod security_data; -pub mod now_proto; - #[doc(hidden)] pub use paste::paste; diff --git a/crates/ironrdp-testsuite-core/src/now_proto.rs b/crates/ironrdp-testsuite-core/src/now_proto.rs deleted file mode 100644 index 008d125f..00000000 --- a/crates/ironrdp-testsuite-core/src/now_proto.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! Various test utilities -use expect_test::Expect; -use ironrdp_core::{Decode as _, ReadCursor}; -use now_proto_pdu::NowMessage; - -pub fn now_msg_roundtrip(msg: impl Into, expected_bytes: Expect) { - let msg = msg.into(); - - let buf = ironrdp_core::encode_vec(&msg).unwrap(); - - expected_bytes.assert_eq(&format!("{:02X?}", buf)); - - let mut cursor = ReadCursor::new(&buf); - let decoded = NowMessage::decode(&mut cursor).unwrap(); - - assert_eq!(msg, decoded); -} diff --git a/crates/ironrdp-testsuite-core/tests/main.rs b/crates/ironrdp-testsuite-core/tests/main.rs index d0a24b46..65a30584 100644 --- a/crates/ironrdp-testsuite-core/tests/main.rs +++ b/crates/ironrdp-testsuite-core/tests/main.rs @@ -17,7 +17,6 @@ mod dvc; mod fuzz_regression; mod graphics; mod input; -mod now_proto; mod pcb; mod pdu; mod rdcleanpath; diff --git a/crates/ironrdp-testsuite-core/tests/now_proto/buffer.rs b/crates/ironrdp-testsuite-core/tests/now_proto/buffer.rs deleted file mode 100644 index 05468a27..00000000 --- a/crates/ironrdp-testsuite-core/tests/now_proto/buffer.rs +++ /dev/null @@ -1,30 +0,0 @@ -use now_proto_pdu::*; -use rstest::rstest; - -#[rstest] -#[case(b"hello", &[0x05, 0x00, 0x00, 0x00, b'h', b'e', b'l', b'l', b'o'])] -#[case(&[], &[0x00, 0x00, 0x00, 0x00])] -fn now_lrgbuf_roundtrip(#[case] value: &[u8], #[case] expected_encoded: &[u8]) { - let mut encoded_value = [0u8; 32]; - let encoded_size = ironrdp_core::encode(&NowLrgBuf::new(value).unwrap(), &mut encoded_value).unwrap(); - - assert_eq!(encoded_size, expected_encoded.len()); - assert_eq!(&encoded_value[..encoded_size], expected_encoded); - - let decoded_value = ironrdp_core::decode::(&encoded_value).unwrap(); - assert_eq!(decoded_value.value(), value); -} - -#[rstest] -#[case(b"hello", &[0x05, b'h', b'e', b'l', b'l', b'o'])] -#[case(&[], &[0x00])] -fn now_varbuf_roundtrip(#[case] value: &[u8], #[case] expected_encoded: &[u8]) { - let mut encoded_value = [0u8; 32]; - let encoded_size = ironrdp_core::encode(&NowVarBuf::new(value).unwrap(), &mut encoded_value).unwrap(); - - assert_eq!(encoded_size, expected_encoded.len()); - assert_eq!(&encoded_value[..encoded_size], expected_encoded); - - let decoded_value = ironrdp_core::decode::(&encoded_value).unwrap(); - assert_eq!(decoded_value.value(), value); -} diff --git a/crates/ironrdp-testsuite-core/tests/now_proto/exec.rs b/crates/ironrdp-testsuite-core/tests/now_proto/exec.rs deleted file mode 100644 index 61b5328e..00000000 --- a/crates/ironrdp-testsuite-core/tests/now_proto/exec.rs +++ /dev/null @@ -1,126 +0,0 @@ -use expect_test::expect; -use ironrdp_testsuite_core::now_proto::now_msg_roundtrip; -use now_proto_pdu::*; - -#[test] -fn roundtrip_exec_capset() { - now_msg_roundtrip( - NowExecCapsetMsg::new(NowExecCapsetFlags::all()), - expect!["[00, 00, 00, 00, 13, 00, FF, 00]"], - ); -} - -#[test] -fn roundtrip_exec_abort() { - now_msg_roundtrip( - NowExecAbortMsg::new(0x12345678, NowStatus::new(NowSeverity::Fatal, NowStatusCode::FAILURE)), - expect!["[08, 00, 00, 00, 13, 01, 00, 00, 78, 56, 34, 12, C0, 00, FF, FF]"], - ); -} - -#[test] -fn roundtrip_exec_cancel_req() { - now_msg_roundtrip( - NowExecCancelReqMsg::new(0x12345678), - expect!["[04, 00, 00, 00, 13, 02, 00, 00, 78, 56, 34, 12]"], - ); -} - -#[test] -fn roundtrip_exec_cancel_rsp() { - now_msg_roundtrip( - NowExecCancelRspMsg::new(0x12345678, NowStatus::new(NowSeverity::Error, NowStatusCode::FAILURE)), - expect!["[08, 00, 00, 00, 13, 03, 00, 00, 78, 56, 34, 12, 80, 00, FF, FF]"], - ); -} - -#[test] -fn roundtrip_exec_result() { - now_msg_roundtrip( - NowExecResultMsg::new(0x12345678, NowStatus::new(NowSeverity::Error, NowStatusCode::FAILURE)), - expect!["[08, 00, 00, 00, 13, 04, 00, 00, 78, 56, 34, 12, 80, 00, FF, FF]"], - ); -} - -#[test] -fn roundtrip_exec_data() { - now_msg_roundtrip( - NowExecDataMsg::new( - NowExecDataFlags::LAST, - 0x12345678, - NowVarBuf::new(vec![0x01, 0x02, 0x03]).unwrap(), - ), - expect!["[08, 00, 00, 00, 13, 05, 02, 00, 78, 56, 34, 12, 03, 01, 02, 03]"], - ); -} - -#[test] -fn roundtrip_exec_run() { - now_msg_roundtrip( - NowExecRunMsg::new(0x1234567, NowVarStr::new("hello".to_owned()).unwrap()).unwrap(), - expect!["[0B, 00, 00, 00, 13, 10, 00, 00, 67, 45, 23, 01, 05, 68, 65, 6C, 6C, 6F, 00]"], - ); -} - -#[test] -fn roundtrip_exec_process() { - now_msg_roundtrip( - NowExecProcessMsg::new( - 0x12345678, - NowVarStr::new("a".to_owned()).unwrap(), - NowVarStr::new("b".to_owned()).unwrap(), - NowVarStr::new("c".to_owned()).unwrap(), - ) - .unwrap(), - expect!["[0D, 00, 00, 00, 13, 12, 00, 00, 78, 56, 34, 12, 01, 61, 00, 01, 62, 00, 01, 63, 00]"], - ); -} - -#[test] -fn roundtrip_exec_shell() { - now_msg_roundtrip( - NowExecShellMsg::new( - 0x12345678, - NowVarStr::new("a".to_owned()).unwrap(), - NowVarStr::new("b".to_owned()).unwrap(), - ) - .unwrap(), - expect!["[0A, 00, 00, 00, 13, 13, 00, 00, 78, 56, 34, 12, 01, 61, 00, 01, 62, 00]"], - ); -} - -#[test] -fn roundtrip_exec_batch() { - now_msg_roundtrip( - NowExecBatchMsg::new(0x12345678, NowVarStr::new("a".to_owned()).unwrap()), - expect!["[07, 00, 00, 00, 13, 14, 00, 00, 78, 56, 34, 12, 01, 61, 00]"], - ); -} - -#[test] -fn roundtrip_exec_ps() { - now_msg_roundtrip( - NowExecWinPsMsg::new(0x12345678, NowVarStr::new("a".to_owned()).unwrap()) - .unwrap() - .with_flags(NowExecWinPsFlags::NO_PROFILE) - .with_execution_policy(NowVarStr::new("b".to_owned()).unwrap()) - .unwrap() - .with_configuration_name(NowVarStr::new("c".to_owned()).unwrap()) - .unwrap(), - expect!["[0D, 00, 00, 00, 13, 15, D0, 00, 78, 56, 34, 12, 01, 61, 00, 01, 62, 00, 01, 63, 00]"], - ); -} - -#[test] -fn roundtrip_exec_pwsh() { - now_msg_roundtrip( - NowExecPwshMsg::new(0x12345678, NowVarStr::new("a".to_owned()).unwrap()) - .unwrap() - .with_flags(NowExecWinPsFlags::NO_PROFILE) - .with_execution_policy(NowVarStr::new("b".to_owned()).unwrap()) - .unwrap() - .with_configuration_name(NowVarStr::new("c".to_owned()).unwrap()) - .unwrap(), - expect!["[0D, 00, 00, 00, 13, 16, D0, 00, 78, 56, 34, 12, 01, 61, 00, 01, 62, 00, 01, 63, 00]"], - ); -} diff --git a/crates/ironrdp-testsuite-core/tests/now_proto/mod.rs b/crates/ironrdp-testsuite-core/tests/now_proto/mod.rs deleted file mode 100644 index 062e1773..00000000 --- a/crates/ironrdp-testsuite-core/tests/now_proto/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod buffer; -mod exec; -mod number; -mod session; -mod status; -mod string; -mod system; diff --git a/crates/ironrdp-testsuite-core/tests/now_proto/number.rs b/crates/ironrdp-testsuite-core/tests/now_proto/number.rs deleted file mode 100644 index 3e914ee9..00000000 --- a/crates/ironrdp-testsuite-core/tests/now_proto/number.rs +++ /dev/null @@ -1,127 +0,0 @@ -use now_proto_pdu::*; -use rstest::rstest; - -#[rstest] -#[case(0x00, &[0x00])] -#[case(0x7F, &[0x7F])] -#[case(0x80, &[0x80, 0x80])] -fn var_u16_roundtrip(#[case] value: u16, #[case] expected_encoded: &'static [u8]) { - let mut encoded_value = [0u8; 4]; - let encoded_size = ironrdp_core::encode(&VarU16::new(value).unwrap(), &mut encoded_value).unwrap(); - - assert_eq!(encoded_size, expected_encoded.len()); - assert_eq!(&encoded_value[..encoded_size], expected_encoded); - - let decoded_value = ironrdp_core::decode::(&encoded_value).unwrap(); - assert_eq!(decoded_value.value(), value); -} - -#[rstest] -#[case(0x00, &[0x00])] -#[case(0x3F, &[0x3F])] -#[case(0x40, &[0x80, 0x40])] -#[case(-0x3F, &[0x7F])] -#[case(-0x40, &[0xC0, 0x40])] -fn var_i16_roundtrip(#[case] value: i16, #[case] expected_encoded: &'static [u8]) { - let mut encoded_value = [0u8; 4]; - let encoded_size = ironrdp_core::encode(&VarI16::new(value).unwrap(), &mut encoded_value).unwrap(); - - assert_eq!(encoded_size, expected_encoded.len()); - assert_eq!(&encoded_value[..encoded_size], expected_encoded); - - let decoded_value = ironrdp_core::decode::(&encoded_value).unwrap(); - assert_eq!(decoded_value.value(), value); -} - -#[rstest] -#[case(0x00, &[0x00])] -#[case(0x3F, &[0x3F])] -#[case(0x40, &[0x40, 0x40])] -#[case(0x14000, &[0x81, 0x40, 0x00])] -#[case(0x3FFFFFFF, &[0xFF, 0xFF, 0xFF, 0xFF])] -fn var_u32_roundtrip(#[case] value: u32, #[case] expected_encoded: &'static [u8]) { - let mut encoded_value = [0u8; 4]; - let encoded_size = ironrdp_core::encode(&VarU32::new(value).unwrap(), &mut encoded_value).unwrap(); - - assert_eq!(encoded_size, expected_encoded.len()); - assert_eq!(&encoded_value[..encoded_size], expected_encoded); - - let decoded_value = ironrdp_core::decode::(&encoded_value).unwrap(); - assert_eq!(decoded_value.value(), value); -} - -#[rstest] -#[case(0x00, &[0x00])] -#[case(0x1F, &[0x1F])] -#[case(0x20, &[0x40, 0x20])] -#[case(0x14000, &[0x81, 0x40, 0x00])] -#[case(0x1FFFFFFF, &[0xDF, 0xFF, 0xFF, 0xFF])] -#[case(-0x1F, &[0x3F])] -#[case(-0x1FFFFFFF, &[0xFF, 0xFF, 0xFF, 0xFF])] -fn var_i32_roundtrip(#[case] value: i32, #[case] expected_encoded: &'static [u8]) { - let mut encoded_value = [0u8; 4]; - let encoded_size = ironrdp_core::encode(&VarI32::new(value).unwrap(), &mut encoded_value).unwrap(); - - assert_eq!(encoded_size, expected_encoded.len()); - assert_eq!(&encoded_value[..encoded_size], expected_encoded); - - let decoded_value = ironrdp_core::decode::(&encoded_value).unwrap(); - assert_eq!(decoded_value.value(), value); -} - -#[rstest] -#[case(0x00, &[0x00])] -#[case(0x1F, &[0x1F])] -#[case(0x20, &[0x20, 0x20])] -#[case(0x14000, &[0x41, 0x40, 0x00])] -#[case(0x1FFFFFFF, &[0x7F, 0xFF, 0xFF, 0xFF])] -#[case(0x1FFFFFFFFF, &[0x9F, 0xFF, 0xFF, 0xFF, 0xFF])] -#[case(0x1FFFFFFFFFFF, &[0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])] -#[case(0x1FFFFFFFFFFFFF, &[0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])] -#[case(0x1FFFFFFFFFFFFFFF, &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])] -fn var_u64_roundtrip(#[case] value: u64, #[case] expected_encoded: &'static [u8]) { - let mut encoded_value = [0u8; 8]; - let encoded_size = ironrdp_core::encode(&VarU64::new(value).unwrap(), &mut encoded_value).unwrap(); - - assert_eq!(encoded_size, expected_encoded.len()); - assert_eq!(&encoded_value[..encoded_size], expected_encoded); - - let decoded_value = ironrdp_core::decode::(&encoded_value).unwrap(); - assert_eq!(decoded_value.value(), value); -} - -#[rstest] -#[case(0x00, &[0x00])] -#[case(0x0F, &[0x0F])] -#[case(0x10, &[0x20, 0x10])] -#[case(0x021400, &[0x42, 0x14, 0x00])] -#[case(0x0FFFFFFF, &[0x6F, 0xFF, 0xFF, 0xFF])] -#[case(0x0FFFFFFFFF, &[0x8F, 0xFF, 0xFF, 0xFF, 0xFF])] -#[case(0x0FFFFFFFFFFF, &[0xAF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])] -#[case(0x0FFFFFFFFFFFFF, &[0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])] -#[case(0x0FFFFFFFFFFFFFFF, &[0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])] -#[case(-0x0F, &[0x1F])] -#[case(-0x0FFFFFFF, &[0x7F, 0xFF, 0xFF, 0xFF])] -fn var_i64_roundtrip(#[case] value: i64, #[case] expected_encoded: &'static [u8]) { - let mut encoded_value = [0u8; 8]; - let encoded_size = ironrdp_core::encode(&VarI64::new(value).unwrap(), &mut encoded_value).unwrap(); - - assert_eq!(encoded_size, expected_encoded.len()); - assert_eq!(&encoded_value[..encoded_size], expected_encoded); - - let decoded_value = ironrdp_core::decode::(&encoded_value).unwrap(); - assert_eq!(decoded_value.value(), value); -} - -#[test] -fn constructed_var_int_too_large() { - VarU16::new(0x8000).unwrap_err(); - VarI16::new(0x4000).unwrap_err(); - VarI16::new(-0x4000).unwrap_err(); - VarU32::new(0x40000000).unwrap_err(); - VarI32::new(0x20000000).unwrap_err(); - VarI32::new(-0x20000000).unwrap_err(); - VarU64::new(0x2000000000000000).unwrap_err(); - VarI64::new(0x1000000000000000).unwrap_err(); - VarI64::new(-0x1000000000000000).unwrap_err(); -} diff --git a/crates/ironrdp-testsuite-core/tests/now_proto/session.rs b/crates/ironrdp-testsuite-core/tests/now_proto/session.rs deleted file mode 100644 index d7add6f2..00000000 --- a/crates/ironrdp-testsuite-core/tests/now_proto/session.rs +++ /dev/null @@ -1,41 +0,0 @@ -use expect_test::expect; -use ironrdp_testsuite_core::now_proto::now_msg_roundtrip; -use now_proto_pdu::*; - -#[test] -fn roundtrip_session_lock() { - now_msg_roundtrip( - NowSessionLockMsg::default(), - expect!["[00, 00, 00, 00, 12, 01, 00, 00]"], - ); -} - -#[test] -fn roundtrip_session_logoff() { - now_msg_roundtrip( - NowSessionLogoffMsg::default(), - expect!["[00, 00, 00, 00, 12, 02, 00, 00]"], - ); -} - -#[test] -fn roundtip_session_msgbox_req() { - now_msg_roundtrip( - NowSessionMsgBoxReqMsg::new( - 0x76543210, - NowVarStr::new("hello".to_owned()).unwrap(), - ).unwrap().with_response().with_style(NowMessageBoxStyle::ABORT_RETRY_IGNORE) - .with_title(NowVarStr::new("world".to_owned()).unwrap()) - .unwrap() - .with_timeout(3), - expect!["[1A, 00, 00, 00, 12, 03, 0F, 00, 10, 32, 54, 76, 02, 00, 00, 00, 03, 00, 00, 00, 05, 77, 6F, 72, 6C, 64, 00, 05, 68, 65, 6C, 6C, 6F, 00]"] - ); -} - -#[test] -fn roundtrip_session_msgbox_rsp() { - now_msg_roundtrip( - NowSessionMsgBoxRspMsg::new(0x01234567, NowMsgBoxResponse::RETRY), - expect!["[08, 00, 00, 00, 12, 04, 00, 00, 67, 45, 23, 01, 04, 00, 00, 00]"], - ); -} diff --git a/crates/ironrdp-testsuite-core/tests/now_proto/status.rs b/crates/ironrdp-testsuite-core/tests/now_proto/status.rs deleted file mode 100644 index 4e5de747..00000000 --- a/crates/ironrdp-testsuite-core/tests/now_proto/status.rs +++ /dev/null @@ -1,20 +0,0 @@ -use ironrdp_core::{Decode as _, Encode as _, ReadCursor, WriteCursor}; -use now_proto_pdu::{NowSeverity, NowStatus, NowStatusCode}; - -#[test] -fn now_status_roundtrip() { - let status = NowStatus::new(NowSeverity::Error, NowStatusCode::FILE_NOT_FOUND) - .with_kind(0x07) - .unwrap(); - - let mut buf = [0; 4]; - let mut cursor = WriteCursor::new(&mut buf); - status.encode(&mut cursor).unwrap(); - - assert_eq!(&buf, &[0x80, 0x07, 0x02, 0x00]); - - let mut cursor = ReadCursor::new(&buf); - let decoded = NowStatus::decode(&mut cursor).unwrap(); - - assert_eq!(status, decoded); -} diff --git a/crates/ironrdp-testsuite-core/tests/now_proto/string.rs b/crates/ironrdp-testsuite-core/tests/now_proto/string.rs deleted file mode 100644 index 26e72c61..00000000 --- a/crates/ironrdp-testsuite-core/tests/now_proto/string.rs +++ /dev/null @@ -1,68 +0,0 @@ -use now_proto_pdu::*; -use rstest::rstest; - -#[rstest] -#[case("hello", &[0x05, 0x00, 0x00, 0x00, b'h', b'e', b'l', b'l', b'o', 0x00])] -#[case("", &[0x00, 0x00, 0x00, 0x00, 0x00])] -fn now_lrgstr_roundtrip(#[case] value: &str, #[case] expected_encoded: &'static [u8]) { - let mut encoded_value = [0u8; 32]; - let encoded_size = ironrdp_core::encode(&NowLrgStr::new(value).unwrap(), &mut encoded_value).unwrap(); - - assert_eq!(encoded_size, expected_encoded.len()); - assert_eq!(&encoded_value[..encoded_size], expected_encoded); - - let decoded_value = ironrdp_core::decode::(&encoded_value).unwrap(); - assert_eq!(decoded_value.value(), value); -} - -#[test] -fn decoded_now_lrgstr_invalid_utf8() { - let encoded = [0x01, 0x00, 0x00, 0x00, 0xFF, 0x00]; - ironrdp_core::decode::(&encoded).unwrap_err(); -} - -#[rstest] -#[case("hello", &[0x05, b'h', b'e', b'l', b'l', b'o', 0x00])] -#[case("", &[0x00, 0x00])] -fn now_varstr_roundtrip(#[case] value: &str, #[case] expected_encoded: &'static [u8]) { - let mut encoded_value = [0u8; 32]; - let encoded_size = ironrdp_core::encode(&NowVarStr::new(value).unwrap(), &mut encoded_value).unwrap(); - - assert_eq!(encoded_size, expected_encoded.len()); - assert_eq!(&encoded_value[..encoded_size], expected_encoded); - - let decoded_value = ironrdp_core::decode::(&encoded_value).unwrap(); - assert_eq!(decoded_value.value(), value); -} - -#[test] -fn decoded_now_varstr_invalid_utf8() { - let encoded = [0x01, 0xFF, 0x00]; - ironrdp_core::decode::(&encoded).unwrap_err(); -} - -#[rstest] -#[case("hello", &[0x05, b'h', b'e', b'l', b'l', b'o', 0x00])] -#[case("", &[0x00, 0x00])] -fn now_restricted_string_roundtrip(#[case] value: &str, #[case] expected_encoded: &'static [u8]) { - let mut encoded_value = [0u8; 32]; - let encoded_size = ironrdp_core::encode(&NowString16::new(value).unwrap(), &mut encoded_value).unwrap(); - - assert_eq!(encoded_size, expected_encoded.len()); - assert_eq!(&encoded_value[..encoded_size], expected_encoded); - - let decoded_value = ironrdp_core::decode::(&encoded_value).unwrap(); - assert_eq!(decoded_value.value(), value); -} - -#[test] -fn now_restricted_string_too_large_constructed() { - let value = "a".repeat(16); - NowString16::new(value).unwrap_err(); -} - -#[test] -fn decoded_now_restricted_string_invalid_utf8() { - let encoded = [0x01, 0xFF, 0x00]; - ironrdp_core::decode::(&encoded).unwrap_err(); -} diff --git a/crates/ironrdp-testsuite-core/tests/now_proto/system.rs b/crates/ironrdp-testsuite-core/tests/now_proto/system.rs deleted file mode 100644 index 1136f41b..00000000 --- a/crates/ironrdp-testsuite-core/tests/now_proto/system.rs +++ /dev/null @@ -1,16 +0,0 @@ -use expect_test::expect; -use ironrdp_testsuite_core::now_proto::now_msg_roundtrip; -use now_proto_pdu::*; - -#[test] -fn roundtip_system_shutdown() { - now_msg_roundtrip( - NowSystemShutdownMsg::new( - NowSystemShutdownFlags::FORCE, - 0x12345678, - NowVarStr::new("hello".to_owned()).unwrap(), - ) - .unwrap(), - expect!["[0B, 00, 00, 00, 11, 03, 01, 00, 78, 56, 34, 12, 05, 68, 65, 6C, 6C, 6F, 00]"], - ); -} diff --git a/crates/now-proto-pdu/Cargo.toml b/crates/now-proto-pdu/Cargo.toml deleted file mode 100644 index e426a1d2..00000000 --- a/crates/now-proto-pdu/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "now-proto-pdu" -version = "0.1.0" -readme = "README.md" -description = "NOW protocol PDU encoding and decoding" -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true -authors.workspace = true -keywords.workspace = true -categories.workspace = true - -# FIXME: Will be published once moved to Devolutions/proto-proto -publish = false - -[lib] -doctest = false -test = false - -[dependencies] -bitflags = "2" -ironrdp-core.workspace = true -ironrdp-pdu = { path = "../ironrdp-pdu" } - -[features] -default = ["std"] -std = ["ironrdp-pdu/std", "ironrdp-core/std"] - -[lints] -workspace = true diff --git a/crates/now-proto-pdu/README.md b/crates/now-proto-pdu/README.md deleted file mode 100644 index 4eaf2948..00000000 --- a/crates/now-proto-pdu/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# IronRDP now-proto-pdu - -The NOW virtual channel protocol use an RDP dynamic virtual channel ("Devolutions::Now::Agent") as a transport type. - -This crate is part of the [IronRDP] project. - -[IronRDP]: https://github.com/Devolutions/IronRDP diff --git a/crates/now-proto-pdu/src/core/buffer.rs b/crates/now-proto-pdu/src/core/buffer.rs deleted file mode 100644 index 0be118fe..00000000 --- a/crates/now-proto-pdu/src/core/buffer.rs +++ /dev/null @@ -1,174 +0,0 @@ -//! Buffer types for NOW protocol. - -use alloc::vec::Vec; - -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, - ReadCursor, WriteCursor, -}; - -use crate::VarU32; - -/// String value up to 2^32 bytes long. -/// -/// NOW-PROTO: NOW_LRGBUF -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowLrgBuf(Vec); - -impl NowLrgBuf { - const NAME: &'static str = "NOW_LRGBUF"; - const FIXED_PART_SIZE: usize = 4; - - /// Create a new `NowLrgBuf` instance. Returns an error if the provided value is too large. - pub fn new(value: impl Into>) -> DecodeResult { - let value: Vec = value.into(); - - if value.len() > VarU32::MAX as usize { - return Err(invalid_field_err!("data", "data is too large for NOW_LRGBUF")); - } - - Self::ensure_message_size(value.len())?; - - Ok(NowLrgBuf(value)) - } - - /// Get the buffer value. - pub fn value(&self) -> &[u8] { - self.0.as_slice() - } - - fn ensure_message_size(buffer_size: usize) -> DecodeResult<()> { - if buffer_size > usize::try_from(VarU32::MAX).expect("BUG: too small usize") { - return Err(invalid_field_err!("data", "data is too large for NOW_LRGBUF")); - } - - if buffer_size > usize::MAX - Self::FIXED_PART_SIZE { - return Err(invalid_field_err!( - "data", - "data size is too large to fit in 32-bit usize" - )); - } - - Ok(()) - } -} - -impl Encode for NowLrgBuf { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let encoded_size = self.size(); - ensure_size!(in: dst, size: encoded_size); - - let len: u32 = self.0.len().try_into().expect("BUG: validated in constructor"); - - dst.write_u32(len); - dst.write_slice(self.0.as_slice()); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - // + - Self::FIXED_PART_SIZE - .checked_add(self.0.len()) - .expect("BUG: size overflow") - } -} - -impl Decode<'_> for NowLrgBuf { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let len: usize = cast_length!("len", src.read_u32())?; - - Self::ensure_message_size(len)?; - - ensure_size!(in: src, size: len); - let bytes = src.read_slice(len); - - Ok(NowLrgBuf(bytes.to_vec())) - } -} - -impl From for Vec { - fn from(buf: NowLrgBuf) -> Self { - buf.0 - } -} - -/// Buffer up to 2^31 bytes long (Length has compact variable length encoding). -/// -/// NOW-PROTO: NOW_VARBUF -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowVarBuf(Vec); - -impl NowVarBuf { - const NAME: &'static str = "NOW_VARBUF"; - - /// Create a new `NowVarBuf` instance. Returns an error if the provided value is too large. - pub fn new(value: impl Into>) -> DecodeResult { - let value = value.into(); - - let _: u32 = value - .len() - .try_into() - .ok() - .and_then(|val| if val <= VarU32::MAX { Some(val) } else { None }) - .ok_or_else(|| invalid_field_err!("data", "too large buffer"))?; - - Ok(NowVarBuf(value)) - } - - /// Get the buffer value. - pub fn value(&self) -> &[u8] { - self.0.as_slice() - } -} - -impl Encode for NowVarBuf { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let encoded_size = self.size(); - ensure_size!(in: dst, size: encoded_size); - - let len: u32 = self.0.len().try_into().expect("BUG: validated in constructor"); - - VarU32::new(len)?.encode(dst)?; - dst.write_slice(self.0.as_slice()); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - // + - // NOTE: Wrapping add will not overflow because the size is limited by VarU32::MAX - VarU32::new(self.0.len().try_into().unwrap()) - .unwrap() - .size() - .wrapping_add(self.0.len()) - } -} - -impl Decode<'_> for NowVarBuf { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let len_u32 = VarU32::decode(src)?.value(); - let len: usize = cast_length!("len", len_u32)?; - - ensure_size!(in: src, size: len); - let bytes = src.read_slice(len); - - Ok(NowVarBuf(bytes.to_vec())) - } -} - -impl From for Vec { - fn from(buf: NowVarBuf) -> Self { - buf.0 - } -} diff --git a/crates/now-proto-pdu/src/core/header.rs b/crates/now-proto-pdu/src/core/header.rs deleted file mode 100644 index 5d0fa590..00000000 --- a/crates/now-proto-pdu/src/core/header.rs +++ /dev/null @@ -1,70 +0,0 @@ -use ironrdp_core::{ensure_fixed_part_size, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct NowMessageClass(pub u8); - -impl NowMessageClass { - /// NOW-PROTO: NOW_SYSTEM_MSG_CLASS_ID - pub const SYSTEM: Self = Self(0x11); - - /// NOW-PROTO: NOW_SESSION_MSG_CLASS_ID - pub const SESSION: Self = Self(0x12); - - /// NOW-PROTO: NOW_EXEC_MSG_CLASS_ID - pub const EXEC: Self = Self(0x13); -} - -/// The NOW_HEADER structure is the header common to all NOW protocol messages. -/// -/// NOW-PROTO: NOW_HEADER -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowHeader { - pub size: u32, - pub class: NowMessageClass, - pub kind: u8, - pub flags: u16, -} - -impl NowHeader { - const NAME: &'static str = "NOW_HEADER"; - pub const FIXED_PART_SIZE: usize = 8; -} - -impl Encode for NowHeader { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - dst.write_u32(self.size); - dst.write_u8(self.class.0); - dst.write_u8(self.kind); - dst.write_u16(self.flags); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl Decode<'_> for NowHeader { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let size = src.read_u32(); - let class = NowMessageClass(src.read_u8()); - let kind = src.read_u8(); - let flags = src.read_u16(); - - Ok(NowHeader { - size, - class, - kind, - flags, - }) - } -} diff --git a/crates/now-proto-pdu/src/core/mod.rs b/crates/now-proto-pdu/src/core/mod.rs deleted file mode 100644 index 1e0572ef..00000000 --- a/crates/now-proto-pdu/src/core/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! This module contains `NOW-PROTO` core types definitions. - -mod buffer; -mod header; -mod number; -mod status; -mod string; - -pub use buffer::{NowLrgBuf, NowVarBuf}; -pub use header::{NowHeader, NowMessageClass}; -pub use number::{VarI16, VarI32, VarI64, VarU16, VarU32, VarU64}; -pub use status::{NowSeverity, NowStatus, NowStatusCode}; -pub use string::{NowLrgStr, NowString128, NowString16, NowString256, NowString32, NowString64, NowVarStr}; diff --git a/crates/now-proto-pdu/src/core/number.rs b/crates/now-proto-pdu/src/core/number.rs deleted file mode 100644 index bcd99deb..00000000 --- a/crates/now-proto-pdu/src/core/number.rs +++ /dev/null @@ -1,766 +0,0 @@ -//! Variable-length number types. -use ironrdp_core::{ - ensure_size, invalid_field_err, Decode, DecodeError, DecodeResult, Encode, EncodeError, EncodeResult, ReadCursor, - WriteCursor, -}; - -/// Variable-length encoded u16. -/// Value range:`[0..0x7FFF]` -/// -/// NOW-PROTO: NOW_VARU16 -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct VarU16(u16); - -impl VarU16 { - pub const MIN: u16 = 0x0000; - pub const MAX: u16 = 0x7FFF; - - const NAME: &'static str = "NOW_VARU16"; - - pub fn new(value: u16) -> DecodeResult { - if value > Self::MAX { - return Err(invalid_field_err!("value", "too large number")); - } - - Ok(VarU16(value)) - } - - pub fn value(&self) -> u16 { - self.0 - } -} - -impl Encode for VarU16 { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let encoded_size = self.size(); - - ensure_size!(in: dst, size: encoded_size); - - // LINTS: encoded_size will always be 1 or 2, therefore following arithmetic is safe - #[allow(clippy::arithmetic_side_effects)] - let mut shift = (encoded_size - 1) * 8; - let mut bytes = [0u8; 2]; - - for byte in bytes.iter_mut().take(encoded_size) { - *byte = ((self.0 >> shift) & 0xFF).try_into().unwrap(); - - // LINTS: as per code above, shift is always 8 or 16 - #[allow(clippy::arithmetic_side_effects)] - if shift != 0 { - shift -= 8; - } - } - - // LINTS: encoded_size is always >= 1 - #[allow(clippy::arithmetic_side_effects)] - let c: u8 = (encoded_size - 1).try_into().unwrap(); - bytes[0] |= c << 7; - - dst.write_slice(&bytes[..encoded_size]); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - match self.0 { - 0x00..=0x7F => 1, - 0x80..=0x7FFF => 2, - _ => unreachable!("BUG: value is out of range!"), - } - } -} - -impl Decode<'_> for VarU16 { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - // Ensure we have at least 1 byte available to determine the size of the value - ensure_size!(in: src, size: 1); - - let header = src.read_u8(); - let c: usize = ((header >> 7) & 0x01).into(); - - if c == 0 { - return Ok(VarU16((header & 0x7F).into())); - } - - ensure_size!(in: src, size: c); - let bytes = src.read_slice(c); - - let val1 = header & 0x7F; - // LINTS: c is always 1 or 2 - #[allow(clippy::arithmetic_side_effects)] - let mut shift = c * 8; - let mut num = u16::from(val1) << shift; - - // Read val2..valN - // LINTS: shift is always 8 or 16 - #[allow(clippy::arithmetic_side_effects)] - for val in bytes.iter().take(c) { - shift -= 8; - num |= (u16::from(*val)) << shift; - } - - Ok(VarU16(num)) - } -} - -impl From for u16 { - fn from(value: VarU16) -> Self { - value.value() - } -} - -impl TryFrom for VarU16 { - type Error = DecodeError; - - fn try_from(value: u16) -> Result { - Self::new(value) - } -} - -/// Variable-length encoded i16. -/// Value range:`[-0x3FFF..0x3FFF]` -/// -/// NOW-PROTO: NOW_VARI16 -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct VarI16(i16); - -impl VarI16 { - pub const MIN: i16 = -0x3FFF; - pub const MAX: i16 = 0x3FFF; - - const NAME: &'static str = "NOW_VARI16"; - - pub fn new(value: i16) -> DecodeResult { - if value.abs() > Self::MAX { - return Err(invalid_field_err!("value", "too large number")); - } - - Ok(VarI16(value)) - } - - pub fn value(&self) -> i16 { - self.0 - } -} - -impl Encode for VarI16 { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let encoded_size = self.size(); - - ensure_size!(in: dst, size: encoded_size); - - // LINTS: encoded_size will always be 1 or 2, therefore following arithmetic is safe - #[allow(clippy::arithmetic_side_effects)] - let mut shift = (encoded_size - 1) * 8; - let mut bytes = [0u8; 2]; - - let abs_value = self.0.unsigned_abs(); - - for byte in bytes.iter_mut().take(encoded_size) { - *byte = ((abs_value >> shift) & 0xFF).try_into().unwrap(); - - // LINTS: as per code above, shift is always 8 or 16 - #[allow(clippy::arithmetic_side_effects)] - if shift != 0 { - shift -= 8; - } - } - - // LINTS: encoded_size is always >= 1 - #[allow(clippy::arithmetic_side_effects)] - let c: u8 = (encoded_size - 1).try_into().unwrap(); - bytes[0] |= c << 7; - if self.0 < 0 { - // set sign bit - bytes[0] |= 0x40; - } - - dst.write_slice(&bytes[..encoded_size]); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - match self.0.unsigned_abs() { - 0..=0x3F => 1, - 0x40..=0x3FFF => 2, - _ => unreachable!("BUG: value is out of range!"), - } - } -} - -impl Decode<'_> for VarI16 { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - // Ensure we have at least 1 byte available to determine the size of the value - ensure_size!(in: src, size: 1); - - let header = src.read_u8(); - let c: usize = ((header >> 7) & 0x01).into(); - let is_negative = (header & 0x40) != 0; - - if c == 0 { - let val = i16::from(header & 0x3F); - // LINTS: Variable integer range is always smaller than underlying type range, - // therefore negation is always safe - #[allow(clippy::arithmetic_side_effects)] - return Ok(VarI16(if is_negative { -val } else { val })); - } - - ensure_size!(in: src, size: c); - let bytes = src.read_slice(c); - - let val1 = header & 0x3F; - - // LINTS: c is always 1 or 2 - #[allow(clippy::arithmetic_side_effects)] - let mut shift = c * 8; - let mut num = i16::from(val1) << shift; - - // Read val2..valN - // LINTS: shift is always 8 or 16 - #[allow(clippy::arithmetic_side_effects)] - for val in bytes.iter().take(c) { - shift -= 8; - num |= (i16::from(*val)) << shift; - } - - // LINTS: Variable integer range is always smaller than underlying type range, - // therefore negation is always safe - #[allow(clippy::arithmetic_side_effects)] - Ok(VarI16(if is_negative { -num } else { num })) - } -} - -impl From for i16 { - fn from(value: VarI16) -> Self { - value.value() - } -} - -impl TryFrom for VarI16 { - type Error = DecodeError; - - fn try_from(value: i16) -> Result { - Self::new(value) - } -} - -/// Variable-length encoded u32. -/// Value range: `[0..0x3FFFFFFF]` -/// -/// NOW-PROTO: NOW_VARU32 -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct VarU32(u32); - -impl VarU32 { - pub const MIN: u32 = 0x00000000; - pub const MAX: u32 = 0x3FFFFFFF; - - const NAME: &'static str = "NOW_VARU32"; - - pub fn new(value: u32) -> EncodeResult { - if value > Self::MAX { - return Err(invalid_field_err!("value", "too large number")); - } - - Ok(VarU32(value)) - } - - pub fn value(&self) -> u32 { - self.0 - } -} - -impl Encode for VarU32 { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let encoded_size = self.size(); - - ensure_size!(in: dst, size: encoded_size); - - // LINTS: encoded_size will always be [1..4], therefore following arithmetic is safe - #[allow(clippy::arithmetic_side_effects)] - let mut shift = (encoded_size - 1) * 8; - let mut bytes = [0u8; 4]; - - for byte in bytes.iter_mut().take(encoded_size) { - *byte = ((self.0 >> shift) & 0xFF).try_into().unwrap(); - - // LINTS: as per code above, shift is always 8, 16, 24 - #[allow(clippy::arithmetic_side_effects)] - if shift != 0 { - shift -= 8; - } - } - - // LINTS: encoded_size is always >= 1 - #[allow(clippy::arithmetic_side_effects)] - let c: u8 = (encoded_size - 1).try_into().unwrap(); - bytes[0] |= c << 6; - - dst.write_slice(&bytes[..encoded_size]); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - match self.0 { - 0x00..=0x3F => 1, - 0x40..=0x3FFF => 2, - 0x4000..=0x3FFFFF => 3, - 0x400000..=0x3FFFFFFF => 4, - _ => unreachable!("BUG: value is out of range!"), - } - } -} - -impl Decode<'_> for VarU32 { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - // Ensure we have at least 1 byte available to determine the size of the value - ensure_size!(in: src, size: 1); - - let header = src.read_u8(); - let c: usize = ((header >> 6) & 0x03).into(); - - if c == 0 { - return Ok(VarU32((header & 0x3F).into())); - } - - ensure_size!(in: src, size: c); - let bytes = src.read_slice(c); - - let val1 = header & 0x3F; - - // LINTS: c is always [1..4] - #[allow(clippy::arithmetic_side_effects)] - let mut shift = c * 8; - let mut num = u32::from(val1) << shift; - - // Read val2..valN - // LINTS: shift is always 8, 16, 24 - #[allow(clippy::arithmetic_side_effects)] - for val in bytes.iter().take(c) { - shift -= 8; - num |= (u32::from(*val)) << shift; - } - - Ok(VarU32(num)) - } -} - -impl From for u32 { - fn from(value: VarU32) -> Self { - value.value() - } -} - -impl TryFrom for VarU32 { - type Error = EncodeError; - - fn try_from(value: u32) -> Result { - Self::new(value) - } -} - -/// Variable-length encoded i32. -/// Value range: `[-0x1FFFFFFF..0x1FFFFFFF]` -/// -/// NOW-PROTO: NOW_VARI32 -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct VarI32(i32); - -impl VarI32 { - pub const MIN: i32 = -0x1FFFFFFF; - pub const MAX: i32 = 0x1FFFFFFF; - - const NAME: &'static str = "NOW_VARI32"; - - pub fn new(value: i32) -> DecodeResult { - if value.abs() > Self::MAX { - return Err(invalid_field_err!("value", "too large number")); - } - - Ok(VarI32(value)) - } - - pub fn value(&self) -> i32 { - self.0 - } -} - -impl Encode for VarI32 { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let encoded_size = self.size(); - - ensure_size!(in: dst, size: encoded_size); - - // LINTS: encoded_size will always be [1..4], therefore following arithmetic is safe - #[allow(clippy::arithmetic_side_effects)] - let mut shift = (encoded_size - 1) * 8; - let mut bytes = [0u8; 4]; - - let abs_value = self.0.unsigned_abs(); - - for byte in bytes.iter_mut().take(encoded_size) { - *byte = ((abs_value >> shift) & 0xFF).try_into().unwrap(); - - // LINTS: as per code above, shift is always 8, 16, 24 - #[allow(clippy::arithmetic_side_effects)] - if shift != 0 { - shift -= 8; - } - } - - // LINTS: encoded_size is always >= 1 - #[allow(clippy::arithmetic_side_effects)] - let c: u8 = (encoded_size - 1).try_into().unwrap(); - bytes[0] |= c << 6; - if self.0 < 0 { - // set sign bit - bytes[0] |= 0x20; - } - - dst.write_slice(&bytes[..encoded_size]); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - match self.0.unsigned_abs() { - 0..=0x1F => 1, - 0x20..=0x1FFF => 2, - 0x2000..=0x1FFFFF => 3, - 0x200000..=0x1FFFFFFF => 4, - _ => unreachable!("BUG: value is out of range!"), - } - } -} - -impl Decode<'_> for VarI32 { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - // Ensure we have at least 1 byte available to determine the size of the value - ensure_size!(in: src, size: 1); - - let header = src.read_u8(); - let c: usize = ((header >> 6) & 0x03).into(); - let is_negative = (header & 0x20) != 0; - - if c == 0 { - let val = i32::from(header & 0x1F); - // LINTS: Variable integer range is always smaller than underlying type range, - // therefore negation is always safe - #[allow(clippy::arithmetic_side_effects)] - return Ok(VarI32(if is_negative { -val } else { val })); - } - - ensure_size!(in: src, size: c); - let bytes = src.read_slice(c); - - let val1 = header & 0x1F; - - // LINTS: c is always [1..4] - #[allow(clippy::arithmetic_side_effects)] - let mut shift = c * 8; - let mut num = i32::from(val1) << shift; - - // Read val2..valN - // LINTS: shift is always 8, 16, 24 - #[allow(clippy::arithmetic_side_effects)] - for val in bytes.iter().take(c) { - shift -= 8; - num |= (i32::from(*val)) << shift; - } - - // LINTS: Variable integer range is always smaller than underlying type range, - // therefore negation is always safe - #[allow(clippy::arithmetic_side_effects)] - Ok(VarI32(if is_negative { -num } else { num })) - } -} - -impl From for i32 { - fn from(value: VarI32) -> Self { - value.value() - } -} - -impl TryFrom for VarI32 { - type Error = DecodeError; - - fn try_from(value: i32) -> Result { - Self::new(value) - } -} - -/// Variable-length encoded u64. -/// Value range: `[0..0x1FFFFFFFFFFFFFFF]` -/// -/// NOW-PROTO: NOW_VARU64 -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct VarU64(u64); - -impl VarU64 { - pub const MIX: u64 = 0x0000000000000000; - pub const MAX: u64 = 0x1FFFFFFFFFFFFFFF; - - const NAME: &'static str = "NOW_VARU64"; - - pub fn new(value: u64) -> DecodeResult { - if value > Self::MAX { - return Err(invalid_field_err!("value", "too large number")); - } - - Ok(VarU64(value)) - } - - pub fn value(&self) -> u64 { - self.0 - } -} - -impl Encode for VarU64 { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let encoded_size = self.size(); - - ensure_size!(in: dst, size: encoded_size); - - // LINTS: encoded_size will always be [1..8], therefore following arithmetic is safe - #[allow(clippy::arithmetic_side_effects)] - let mut shift = (encoded_size - 1) * 8; - let mut bytes = [0u8; 8]; - - for byte in bytes.iter_mut().take(encoded_size) { - *byte = ((self.0 >> shift) & 0xFF).try_into().unwrap(); - - // LINTS: as per code above, shift is always >= 8 - #[allow(clippy::arithmetic_side_effects)] - if shift != 0 { - shift -= 8; - } - } - - // LINTS: encoded_size is always >= 1 - #[allow(clippy::arithmetic_side_effects)] - let c: u8 = (encoded_size - 1).try_into().unwrap(); - bytes[0] |= c << 5; - - dst.write_slice(&bytes[..encoded_size]); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - match self.0 { - 0x00..=0x1F => 1, - 0x20..=0x1FFF => 2, - 0x2000..=0x1FFFFF => 3, - 0x200000..=0x1FFFFFFF => 4, - 0x20000000..=0x1FFFFFFFFF => 5, - 0x2000000000..=0x1FFFFFFFFFFF => 6, - 0x200000000000..=0x1FFFFFFFFFFFFF => 7, - 0x20000000000000..=0x1FFFFFFFFFFFFFFF => 8, - _ => unreachable!("BUG: value is out of range!"), - } - } -} - -impl Decode<'_> for VarU64 { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - // Ensure we have at least 1 byte available to determine the size of the value - ensure_size!(in: src, size: 1); - - let header = src.read_u8(); - let c: usize = ((header >> 5) & 0x07).into(); - - if c == 0 { - return Ok(VarU64((header & 0x1F).into())); - } - - ensure_size!(in: src, size: c); - let bytes = src.read_slice(c); - - let val1 = header & 0x1F; - // LINTS: c is always [1..8] - #[allow(clippy::arithmetic_side_effects)] - let mut shift = c * 8; - let mut num = u64::from(val1) << shift; - - // Read val2..valN - // LINTS: shift is always >= 8 - #[allow(clippy::arithmetic_side_effects)] - for val in bytes.iter().take(c) { - shift -= 8; - num |= (u64::from(*val)) << shift; - } - - Ok(VarU64(num)) - } -} - -impl From for u64 { - fn from(value: VarU64) -> Self { - value.value() - } -} - -impl TryFrom for VarU64 { - type Error = DecodeError; - - fn try_from(value: u64) -> Result { - Self::new(value) - } -} - -/// Variable-length encoded i64. -/// Value range: `[-0x0FFFFFFFFFFFFFFF..0x0FFFFFFFFFFFFFFF]` -/// -/// NOW-PROTO: NOW_VARI64 -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct VarI64(i64); - -impl VarI64 { - const NAME: &'static str = "NOW_VARI64"; - const MAX: i64 = 0x0FFFFFFFFFFFFFFF; - - pub fn new(value: i64) -> DecodeResult { - if value.abs() > Self::MAX { - return Err(invalid_field_err!("value", "too large number")); - } - - Ok(VarI64(value)) - } - - pub fn value(&self) -> i64 { - self.0 - } -} - -impl Encode for VarI64 { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let encoded_size = self.size(); - - ensure_size!(in: dst, size: encoded_size); - - // LINTS: encoded_size will always be [1..8], therefore following arithmetic is safe - #[allow(clippy::arithmetic_side_effects)] - let mut shift = (encoded_size - 1) * 8; - let mut bytes = [0u8; 8]; - - let abs_value = self.0.unsigned_abs(); - - for byte in bytes.iter_mut().take(encoded_size) { - *byte = ((abs_value >> shift) & 0xFF).try_into().unwrap(); - - // LINTS: as per code above, shift is always >= 8 - #[allow(clippy::arithmetic_side_effects)] - if shift != 0 { - shift -= 8; - } - } - - // LINTS: encoded_size is always >= 1 - #[allow(clippy::arithmetic_side_effects)] - let c: u8 = (encoded_size - 1).try_into().unwrap(); - bytes[0] |= c << 5; - if self.0 < 0 { - // set sign bit - bytes[0] |= 0x10; - } - - dst.write_slice(&bytes[..encoded_size]); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - match self.0.unsigned_abs() { - 0..=0x0F => 1, - 0x10..=0x0FFF => 2, - 0x1000..=0x0FFFFF => 3, - 0x100000..=0x0FFFFFFF => 4, - 0x10000000..=0x0FFFFFFFFF => 5, - 0x1000000000..=0x0FFFFFFFFFFF => 6, - 0x100000000000..=0x0FFFFFFFFFFFFF => 7, - 0x10000000000000..=0x0FFFFFFFFFFFFFFF => 8, - _ => unreachable!("BUG: value is out of range!"), - } - } -} - -impl Decode<'_> for VarI64 { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - // Ensure we have at least 1 byte available to determine the size of the value - ensure_size!(in: src, size: 1); - - let header = src.read_u8(); - let c: usize = ((header >> 5) & 0x07).into(); - let is_negative = (header & 0x10) != 0; - - if c == 0 { - let val = i64::from(header & 0x0F); - // LINTS: Variable integer range is always smaller than underlying type range, - // therefore negation is always safe - #[allow(clippy::arithmetic_side_effects)] - return Ok(VarI64(if is_negative { -val } else { val })); - } - - ensure_size!(in: src, size: c); - let bytes = src.read_slice(c); - - let val1 = header & 0x0F; - // LINTS: c is always [1..8] - #[allow(clippy::arithmetic_side_effects)] - let mut shift = c * 8; - let mut num = i64::from(val1) << shift; - - // Read val2..valN - // LINTS: shift is always >= 8 - #[allow(clippy::arithmetic_side_effects)] - for val in bytes.iter().take(c) { - shift -= 8; - num |= (i64::from(*val)) << shift; - } - - // LINTS: Variable integer range is always smaller than underlying type range, - // therefore negation is always safe - #[allow(clippy::arithmetic_side_effects)] - Ok(VarI64(if is_negative { -num } else { num })) - } -} - -impl From for i64 { - fn from(value: VarI64) -> Self { - value.value() - } -} - -impl TryFrom for VarI64 { - type Error = DecodeError; - - fn try_from(value: i64) -> Result { - Self::new(value) - } -} diff --git a/crates/now-proto-pdu/src/core/status.rs b/crates/now-proto-pdu/src/core/status.rs deleted file mode 100644 index 8231719a..00000000 --- a/crates/now-proto-pdu/src/core/status.rs +++ /dev/null @@ -1,139 +0,0 @@ -use ironrdp_core::{ - ensure_fixed_part_size, invalid_field_err, Decode, DecodeError, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, -}; - -/// Error or status severity. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] -pub enum NowSeverity { - /// Informative status - /// - /// NOW-PROTO: NOW_SEVERITY_INFO - Info = 0, - /// Warning status - /// - /// NOW-PROTO: NOW_SEVERITY_WARN - Warn = 1, - /// Error status (recoverable) - /// - /// NOW-PROTO: NOW_SEVERITY_ERROR - Error = 2, - /// Error status (non-recoverable) - /// - /// NOW-PROTO: NOW_SEVERITY_FATAL - Fatal = 3, -} - -impl TryFrom for NowSeverity { - type Error = DecodeError; - - fn try_from(value: u8) -> DecodeResult { - match value { - 0 => Ok(Self::Info), - 1 => Ok(Self::Warn), - 2 => Ok(Self::Error), - 3 => Ok(Self::Fatal), - _ => Err(invalid_field_err!("severity", "invalid value")), - } - } -} - -/// Error or status code. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct NowStatusCode(pub u16); - -impl NowStatusCode { - /// NOW-PROTO: NOW_CODE_SUCCESS - pub const SUCCESS: Self = Self(0x0000); - /// NOW-PROTO: NOW_CODE_FAILURE - pub const FAILURE: Self = Self(0xFFFF); - /// NOW-PROTO: NOW_CODE_FILE_NOT_FOUND - pub const FILE_NOT_FOUND: Self = Self(0x0002); - /// NOW-PROTO: NOW_CODE_ACCESS_DENIED - pub const ACCESS_DENIED: Self = Self(0x0005); - /// NOW-PROTO: NOW_CODE_BAD_FORMAT - pub const BAD_FORMAT: Self = Self(0x000B); -} -/// A status code, with a structure similar to HRESULT. -/// -/// NOW-PROTO: NOW_STATUS -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowStatus { - severity: NowSeverity, - kind: u8, - code: NowStatusCode, -} - -impl NowStatus { - const NAME: &'static str = "NOW_STATUS"; - const FIXED_PART_SIZE: usize = 4; - - pub fn new(severity: NowSeverity, code: NowStatusCode) -> Self { - Self { - severity, - kind: 0, - code, - } - } - - pub fn with_kind(self, kind: u8) -> DecodeResult { - if kind > 0x0F { - return Err(invalid_field_err!("type", "status type is too large")); - } - - Ok(Self { kind, ..self }) - } - - pub fn severity(&self) -> NowSeverity { - self.severity - } - - pub fn kind(&self) -> u8 { - self.kind - } - - pub fn code(&self) -> NowStatusCode { - self.code - } -} - -impl Encode for NowStatus { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - ensure_fixed_part_size!(in: dst); - - // Y, Z, class fields are reserved and must be set to 0. - let header_byte = (self.severity as u8) << 6; - - dst.write_u8(header_byte); - dst.write_u8(self.kind); - dst.write_u16(self.code.0); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - Self::FIXED_PART_SIZE - } -} - -impl Decode<'_> for NowStatus { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let header_byte = src.read_u8(); - let severity = (header_byte >> 6) & 0x03; - let kind = src.read_u8(); - let code = src.read_u16(); - - Ok(NowStatus { - severity: NowSeverity::try_from(severity)?, - kind, - code: NowStatusCode(code), - }) - } -} diff --git a/crates/now-proto-pdu/src/core/string.rs b/crates/now-proto-pdu/src/core/string.rs deleted file mode 100644 index d9064951..00000000 --- a/crates/now-proto-pdu/src/core/string.rs +++ /dev/null @@ -1,320 +0,0 @@ -//! String types - -use alloc::string::String; - -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, - ReadCursor, WriteCursor, -}; - -use crate::VarU32; - -/// String value up to 2^32 bytes long. -/// -/// NOW-PROTO: NOW_LRGSTR -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowLrgStr(String); - -impl NowLrgStr { - pub const MAX_SIZE: usize = u32::MAX as usize; - - const NAME: &'static str = "NOW_LRGSTR"; - const FIXED_PART_SIZE: usize = 4; - - /// Returns empty string. - pub fn empty() -> Self { - Self(String::new()) - } - - /// Creates new `NowLrgStr`. Returns error if string is too big for the protocol. - pub fn new(value: impl Into) -> DecodeResult { - let value: String = value.into(); - // IMPORTANT: we need to check for encoded UTF-8 size, not the string length. - - Self::ensure_message_size(value.as_bytes().len())?; - - Ok(NowLrgStr(value)) - } - - pub fn value(&self) -> &str { - &self.0 - } - - fn ensure_message_size(string_size: usize) -> DecodeResult<()> { - if string_size > usize::try_from(VarU32::MAX).expect("BUG: too small usize") { - return Err(invalid_field_err!("data", "data is too large for NOW_LRGSTR")); - } - - if string_size > usize::MAX - Self::FIXED_PART_SIZE - 1 { - return Err(invalid_field_err!( - "string", - "string size is too large to fit in 32-bit usize" - )); - } - - Ok(()) - } -} - -impl Encode for NowLrgStr { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let encoded_size = self.size(); - ensure_size!(in: dst, size: encoded_size); - - let len: u32 = self.0.len().try_into().expect("BUG: validated in constructor"); - - dst.write_u32(len); - dst.write_slice(self.0.as_bytes()); - dst.write_u8(b'\0'); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - // + + - self.0 - .len() - .checked_add(Self::FIXED_PART_SIZE + 1) - .expect("BUG: size overflow") - } -} - -impl Decode<'_> for NowLrgStr { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let len: usize = cast_length!("len", src.read_u32())?; - - Self::ensure_message_size(len)?; - - ensure_size!(in: src, size: len); - let bytes = src.read_slice(len); - ensure_size!(in: src, size: 1); - let _null = src.read_u8(); - - let string = - String::from_utf8(bytes.to_vec()).map_err(|_| invalid_field_err!("string value", "invalid utf-8"))?; - - Ok(NowLrgStr(string)) - } -} - -impl From for String { - fn from(value: NowLrgStr) -> Self { - value.0 - } -} - -/// String value up to 2^31 bytes long (Length has compact variable length encoding). -/// -/// NOW-PROTO: NOW_VARSTR -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowVarStr(String); - -impl NowVarStr { - pub const MAX_SIZE: usize = VarU32::MAX as usize; - - const NAME: &'static str = "NOW_VARSTR"; - - /// Returns empty string. - pub fn empty() -> Self { - Self(String::new()) - } - - /// Creates `NowVarStr` from std string. Returns error if string is too big for the protocol. - pub fn new(value: impl Into) -> EncodeResult { - let value = value.into(); - // IMPORTANT: we need to check for encoded UTF-8 size, not the string length. - - let _: u32 = value - .as_bytes() - .len() - .try_into() - .ok() - .and_then(|val| if val <= VarU32::MAX { Some(val) } else { None }) - .ok_or_else(|| invalid_field_err!("string value", "too large string"))?; - - Ok(NowVarStr(value)) - } - - pub fn value(&self) -> &str { - &self.0 - } -} - -impl Encode for NowVarStr { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let encoded_size = self.size(); - ensure_size!(in: dst, size: encoded_size); - - let len: u32 = self.0.len().try_into().expect("BUG: validated in constructor"); - - VarU32::new(len)?.encode(dst)?; - dst.write_slice(self.0.as_bytes()); - dst.write_u8(b'\0'); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - // LINTS: Use of VarU32 ensures that the overall size value is within the bounds of usize. - #[allow(clippy::arithmetic_side_effects)] - fn size(&self) -> usize { - VarU32::new(self.0.len().try_into().unwrap()).unwrap().size() /* variable-length size */ - + self.0.len() /* utf-8 bytes */ - + 1 /* null terminator */ - } -} - -impl Decode<'_> for NowVarStr { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let len_u32 = VarU32::decode(src)?.value(); - let len: usize = cast_length!("len", len_u32)?; - - ensure_size!(in: src, size: len); - let bytes = src.read_slice(len); - ensure_size!(in: src, size: 1); - let _null = src.read_u8(); - - let string = - String::from_utf8(bytes.to_vec()).map_err(|_| invalid_field_err!("string value", "invalid utf-8"))?; - - Ok(NowVarStr(string)) - } -} - -impl From for String { - fn from(value: NowVarStr) -> Self { - value.0 - } -} - -const fn restricted_str_name(str_len: u8) -> &'static str { - match str_len { - 15 => "NOW_STRING16", - 31 => "NOW_STRING32", - 63 => "NOW_STRING64", - 127 => "NOW_STRING128", - 255 => "NOW_STRING256", - _ => panic!("BUG: Requested restricted string variant is not defined in the protocol"), - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowRestrictedStr(String); - -impl NowRestrictedStr { - pub const MAX_ENCODED_UTF8_LEN: usize = MAX_LEN as usize; - - const NAME: &'static str = restricted_str_name(MAX_LEN); - const FIXED_PART_SIZE: usize = 1; - - /// Returns empty string. - pub fn empty() -> Self { - Self(String::new()) - } - - /// Creates `NowRestrictedStr` from std string. Returns error if string is too big for the protocol. - pub fn new(value: impl Into) -> EncodeResult { - let value = value.into(); - - // IMPORTANT: we need to check for encoded UTF-8 size, not the string length - if value.as_bytes().len() > MAX_LEN as usize { - return Err(invalid_field_err!("string value", concat!("too large string"))); - } - Ok(NowRestrictedStr(value)) - } - - pub fn value(&self) -> &str { - &self.0 - } -} - -impl Encode for NowRestrictedStr { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let encoded_size = self.size(); - ensure_size!(in: dst, size: encoded_size); - - let len: u8 = self.0.len().try_into().expect("BUG: validated in constructor"); - - dst.write_u8(len); - dst.write_slice(self.0.as_bytes()); - dst.write_u8(b'\0'); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - // LINTS: Restricted string with u8 length ensures that the overall size value is within - // the bounds of usize. - #[allow(clippy::arithmetic_side_effects)] - fn size(&self) -> usize { - Self::FIXED_PART_SIZE /* u8 size */ - + self.0.len() /* utf-8 bytes */ - + 1 /* null terminator */ - } -} - -impl Decode<'_> for NowRestrictedStr { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let len = src.read_u8(); - if len > MAX_LEN { - return Err(invalid_field_err!("string value", "too large string")); - } - - let len_usize = len.into(); - - ensure_size!(in: src, size: len_usize); - let bytes = src.read_slice(len_usize); - ensure_size!(in: src, size: 1); - let _null = src.read_u8(); - - let string = - String::from_utf8(bytes.to_vec()).map_err(|_| invalid_field_err!("string value", "invalid utf-8"))?; - - Ok(NowRestrictedStr(string)) - } -} - -impl From> for String { - fn from(value: NowRestrictedStr) -> Self { - value.0 - } -} - -/// String value up to 16 bytes long. -/// -/// NOW-PROTO: NOW_STRING16 -pub type NowString16 = NowRestrictedStr<15>; - -/// String value up to 32 bytes long. -/// -/// NOW-PROTO: NOW_STRING32 -pub type NowString32 = NowRestrictedStr<31>; - -/// String value up to 64 bytes long. -/// -/// NOW-PROTO: NOW_STRING64 -pub type NowString64 = NowRestrictedStr<63>; - -/// String value up to 128 bytes long. -/// -/// NOW-PROTO: NOW_STRING128 -pub type NowString128 = NowRestrictedStr<127>; - -/// String value up to 256 bytes long. -/// -/// NOW-PROTO: NOW_STRING256 -pub type NowString256 = NowRestrictedStr<255>; diff --git a/crates/now-proto-pdu/src/exec/abort.rs b/crates/now-proto-pdu/src/exec/abort.rs deleted file mode 100644 index 75a59866..00000000 --- a/crates/now-proto-pdu/src/exec/abort.rs +++ /dev/null @@ -1,95 +0,0 @@ -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, -}; - -use crate::{NowExecMessage, NowExecMsgKind, NowHeader, NowMessage, NowMessageClass, NowStatus}; - -/// The NOW_EXEC_ABORT_MSG message is used to abort a remote execution immediately due to an -/// unrecoverable error. This message can be sent at any time without an explicit response message. -/// The session is considered aborted as soon as this message is sent. -/// -/// NOW-PROTO: NOW_EXEC_ABORT_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowExecAbortMsg { - session_id: u32, - status: NowStatus, -} - -impl NowExecAbortMsg { - const NAME: &'static str = "NOW_EXEC_ABORT_MSG"; - const FIXED_PART_SIZE: usize = 4; - - pub fn new(session_id: u32, status: NowStatus) -> Self { - Self { session_id, status } - } - - pub fn session_id(&self) -> u32 { - self.session_id - } - - pub fn status(&self) -> &NowStatus { - &self.status - } - - // LINTS: Overall message size always fits into usize - #[allow(clippy::arithmetic_side_effects)] - fn body_size(&self) -> usize { - Self::FIXED_PART_SIZE + self.status.size() - } - - pub(super) fn decode_from_body(_header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let session_id = src.read_u32(); - let status = NowStatus::decode(src)?; - - Ok(Self { session_id, status }) - } -} - -impl Encode for NowExecAbortMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: cast_length!("size", self.body_size())?, - class: NowMessageClass::EXEC, - kind: NowExecMsgKind::ABORT.0, - flags: 0, - }; - - header.encode(dst)?; - - ensure_fixed_part_size!(in: dst); - dst.write_u32(self.session_id); - self.status.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - // LINTS: Overall message size always fits into usize - #[allow(clippy::arithmetic_side_effects)] - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE + self.body_size() - } -} - -impl Decode<'_> for NowExecAbortMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowExecMsgKind(header.kind)) { - (NowMessageClass::EXEC, NowExecMsgKind::ABORT) => Self::decode_from_body(header, src), - _ => Err(invalid_field_err!("type", "invalid message type")), - } - } -} - -impl From for NowMessage { - fn from(msg: NowExecAbortMsg) -> Self { - NowMessage::Exec(NowExecMessage::Abort(msg)) - } -} diff --git a/crates/now-proto-pdu/src/exec/batch.rs b/crates/now-proto-pdu/src/exec/batch.rs deleted file mode 100644 index 78ca7d20..00000000 --- a/crates/now-proto-pdu/src/exec/batch.rs +++ /dev/null @@ -1,94 +0,0 @@ -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, -}; - -use crate::{NowExecMessage, NowExecMsgKind, NowHeader, NowMessage, NowMessageClass, NowVarStr}; - -/// The NOW_EXEC_BATCH_MSG message is used to execute a remote batch command. -/// -/// NOW-PROTO: NOW_EXEC_BATCH_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowExecBatchMsg { - session_id: u32, - command: NowVarStr, -} - -impl NowExecBatchMsg { - const NAME: &'static str = "NOW_EXEC_BATCH_MSG"; - const FIXED_PART_SIZE: usize = 4; - - pub fn new(session_id: u32, command: NowVarStr) -> Self { - Self { session_id, command } - } - - pub fn session_id(&self) -> u32 { - self.session_id - } - - pub fn command(&self) -> &NowVarStr { - &self.command - } - - // LINTS: Overall message size always fits into usize; VarStr size always a few powers of 2 less - // than u32::MAX, therefore it fits into usize - #[allow(clippy::arithmetic_side_effects)] - fn body_size(&self) -> usize { - Self::FIXED_PART_SIZE + self.command.size() - } - - pub(super) fn decode_from_body(_header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let session_id = src.read_u32(); - let command = NowVarStr::decode(src)?; - - Ok(Self { session_id, command }) - } -} - -impl Encode for NowExecBatchMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: cast_length!("size", self.body_size())?, - class: NowMessageClass::EXEC, - kind: NowExecMsgKind::BATCH.0, - flags: 0, - }; - - header.encode(dst)?; - - ensure_fixed_part_size!(in: dst); - dst.write_u32(self.session_id); - self.command.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - // LINTS: See body_size() - #[allow(clippy::arithmetic_side_effects)] - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE + self.body_size() - } -} - -impl Decode<'_> for NowExecBatchMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowExecMsgKind(header.kind)) { - (NowMessageClass::EXEC, NowExecMsgKind::BATCH) => Self::decode_from_body(header, src), - _ => Err(invalid_field_err!("type", "invalid message type")), - } - } -} - -impl From for NowMessage { - fn from(msg: NowExecBatchMsg) -> Self { - NowMessage::Exec(NowExecMessage::Batch(msg)) - } -} diff --git a/crates/now-proto-pdu/src/exec/cancel_req.rs b/crates/now-proto-pdu/src/exec/cancel_req.rs deleted file mode 100644 index 8597d749..00000000 --- a/crates/now-proto-pdu/src/exec/cancel_req.rs +++ /dev/null @@ -1,77 +0,0 @@ -use ironrdp_core::{ - ensure_fixed_part_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor, -}; - -use crate::{NowExecMessage, NowExecMsgKind, NowHeader, NowMessage, NowMessageClass}; - -/// The NOW_EXEC_CANCEL_REQ_MSG message is used to cancel a remote execution session. -/// -/// NOW-PROTO: NOW_EXEC_CANCEL_REQ_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowExecCancelReqMsg { - session_id: u32, -} - -impl NowExecCancelReqMsg { - const NAME: &'static str = "NOW_EXEC_CANCEL_REQ_MSG"; - const FIXED_PART_SIZE: usize = 4; - - pub fn new(session_id: u32) -> Self { - Self { session_id } - } - - pub fn session_id(&self) -> u32 { - self.session_id - } - - pub(super) fn decode_from_body(_header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let session_id = src.read_u32(); - - Ok(Self { session_id }) - } -} - -impl Encode for NowExecCancelReqMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: u32::try_from(Self::FIXED_PART_SIZE).unwrap(), - class: NowMessageClass::EXEC, - kind: NowExecMsgKind::CANCEL_REQ.0, - flags: 0, - }; - - header.encode(dst)?; - - ensure_fixed_part_size!(in: dst); - dst.write_u32(self.session_id); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE + Self::FIXED_PART_SIZE - } -} - -impl Decode<'_> for NowExecCancelReqMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowExecMsgKind(header.kind)) { - (NowMessageClass::EXEC, NowExecMsgKind::CANCEL_REQ) => Self::decode_from_body(header, src), - _ => Err(invalid_field_err!("type", "invalid message type")), - } - } -} - -impl From for NowMessage { - fn from(msg: NowExecCancelReqMsg) -> Self { - NowMessage::Exec(NowExecMessage::CancelReq(msg)) - } -} diff --git a/crates/now-proto-pdu/src/exec/cancel_rsp.rs b/crates/now-proto-pdu/src/exec/cancel_rsp.rs deleted file mode 100644 index d98d409e..00000000 --- a/crates/now-proto-pdu/src/exec/cancel_rsp.rs +++ /dev/null @@ -1,93 +0,0 @@ -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, -}; - -use crate::{NowExecMessage, NowExecMsgKind, NowHeader, NowMessage, NowMessageClass, NowStatus}; - -/// The NOW_EXEC_CANCEL_RSP_MSG message is used to respond to a remote execution cancel request. -/// -/// NOW_PROTO: NOW_EXEC_CANCEL_RSP_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowExecCancelRspMsg { - session_id: u32, - status: NowStatus, -} - -impl NowExecCancelRspMsg { - const NAME: &'static str = "NOW_EXEC_CANCEL_RSP_MSG"; - const FIXED_PART_SIZE: usize = 4; - - pub fn new(session_id: u32, status: NowStatus) -> Self { - Self { session_id, status } - } - - pub fn session_id(&self) -> u32 { - self.session_id - } - - pub fn status(&self) -> &NowStatus { - &self.status - } - - // LINTS: Overall message size always fits into usize - #[allow(clippy::arithmetic_side_effects)] - fn body_size(&self) -> usize { - Self::FIXED_PART_SIZE + self.status.size() - } - - pub(super) fn decode_from_body(_header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let session_id = src.read_u32(); - let status = NowStatus::decode(src)?; - - Ok(Self { session_id, status }) - } -} - -impl Encode for NowExecCancelRspMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: cast_length!("size", self.body_size())?, - class: NowMessageClass::EXEC, - kind: NowExecMsgKind::CANCEL_RSP.0, - flags: 0, - }; - - header.encode(dst)?; - - ensure_fixed_part_size!(in: dst); - dst.write_u32(self.session_id); - self.status.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - // LINTS: See body_size() - #[allow(clippy::arithmetic_side_effects)] - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE + self.body_size() - } -} - -impl Decode<'_> for NowExecCancelRspMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowExecMsgKind(header.kind)) { - (NowMessageClass::EXEC, NowExecMsgKind::CANCEL_RSP) => Self::decode_from_body(header, src), - _ => Err(invalid_field_err!("type", "invalid message type")), - } - } -} - -impl From for NowMessage { - fn from(msg: NowExecCancelRspMsg) -> Self { - NowMessage::Exec(NowExecMessage::CancelRsp(msg)) - } -} diff --git a/crates/now-proto-pdu/src/exec/capset.rs b/crates/now-proto-pdu/src/exec/capset.rs deleted file mode 100644 index eba3b556..00000000 --- a/crates/now-proto-pdu/src/exec/capset.rs +++ /dev/null @@ -1,105 +0,0 @@ -use bitflags::bitflags; -use ironrdp_core::{invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor}; - -use crate::{NowExecMessage, NowExecMsgKind, NowHeader, NowMessage, NowMessageClass}; - -bitflags! { - /// NOW-PROTO: NOW_EXEC_CAPSET_MSG msgFlags field. - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub struct NowExecCapsetFlags: u16 { - /// Generic "Run" execution style. - /// - /// NOW-PROTO: NOW_EXEC_STYLE_RUN - const STYLE_RUN = 0x0001; - /// Generic command execution style. - /// - /// NOW-PROTO: NOW_EXEC_STYLE_CMD - const STYLE_CMD = 0x0002; - /// CreateProcess() execution style. - /// - /// NOW-PROTO: NOW_EXEC_STYLE_PROCESS - const STYLE_PROCESS = 0x0004; - /// System shell (.sh) execution style. - /// - /// NOW-PROTO: NOW_EXEC_STYLE_SHELL - const STYLE_SHELL = 0x0008; - /// Windows batch file (.bat) execution style. - /// - /// NOW-PROTO: NOW_EXEC_STYLE_BATCH - const STYLE_BATCH = 0x0010; - /// Windows PowerShell (.ps1) execution style. - /// - /// NOW-PROTO: NOW_EXEC_STYLE_WINPS - const STYLE_WINPS = 0x0020; - /// PowerShell 7 (.ps1) execution style. - /// - /// NOW-PROTO: NOW_EXEC_STYLE_PWSH - const STYLE_PWSH = 0x0040; - /// Applescript (.scpt) execution style. - /// - /// NOW-PROTO: NOW_EXEC_STYLE_APPLESCRIPT - const STYLE_APPLESCRIPT = 0x0080; - } -} - -/// The NOW_EXEC_CAPSET_MSG message is sent to advertise capabilities. -/// -/// NOW-PROTO: NOW_EXEC_CAPSET_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowExecCapsetMsg { - flags: NowExecCapsetFlags, -} - -impl NowExecCapsetMsg { - const NAME: &'static str = "NOW_EXEC_CAPSET_MSG"; - - pub fn new(flags: NowExecCapsetFlags) -> Self { - Self { flags } - } - - pub fn flags(&self) -> NowExecCapsetFlags { - self.flags - } -} - -impl Encode for NowExecCapsetMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: 0, - class: NowMessageClass::EXEC, - kind: NowExecMsgKind::CAPSET.0, - flags: self.flags.bits(), - }; - - header.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE - } -} - -impl Decode<'_> for NowExecCapsetMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowExecMsgKind(header.kind)) { - (NowMessageClass::EXEC, NowExecMsgKind::CAPSET) => Ok(Self { - flags: NowExecCapsetFlags::from_bits_retain(header.flags), - }), - _ => Err(invalid_field_err!("type", "invalid message type")), - } - } -} - -impl From for NowMessage { - fn from(msg: NowExecCapsetMsg) -> Self { - NowMessage::Exec(NowExecMessage::Capset(msg)) - } -} diff --git a/crates/now-proto-pdu/src/exec/cmd.rs b/crates/now-proto-pdu/src/exec/cmd.rs deleted file mode 100644 index d9ea4615..00000000 --- a/crates/now-proto-pdu/src/exec/cmd.rs +++ /dev/null @@ -1 +0,0 @@ -// TODO: Not yet defined in specification diff --git a/crates/now-proto-pdu/src/exec/data.rs b/crates/now-proto-pdu/src/exec/data.rs deleted file mode 100644 index d41822cc..00000000 --- a/crates/now-proto-pdu/src/exec/data.rs +++ /dev/null @@ -1,136 +0,0 @@ -use bitflags::bitflags; -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, -}; - -use crate::{NowExecMessage, NowExecMsgKind, NowHeader, NowMessage, NowMessageClass, NowVarBuf}; - -bitflags! { - /// NOW-PROTO: NOW_EXEC_DATA_MSG flags field. - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub struct NowExecDataFlags: u16 { - /// This is the first data message. - /// - /// NOW-PROTO: NOW_EXEC_FLAG_DATA_FIRST - const FIRST = 0x0001; - /// This is the last data message, the command completed execution. - /// - /// NOW-PROTO: NOW_EXEC_FLAG_DATA_LAST - const LAST = 0x0002; - /// The data is from the standard input. - /// - /// NOW-PROTO: NOW_EXEC_FLAG_DATA_STDIN - const STDIN = 0x0004; - /// The data is from the standard output. - /// - /// NOW-PROTO: NOW_EXEC_FLAG_DATA_STDOUT - const STDOUT = 0x0008; - /// The data is from the standard error. - /// - /// NOW-PROTO: NOW_EXEC_FLAG_DATA_STDERR - const STDERR = 0x0010; - } -} - -/// The NOW_EXEC_DATA_MSG message is used to send input/output data as part of a remote execution. -/// -/// NOW-PROTO: NOW_EXEC_DATA_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowExecDataMsg { - flags: NowExecDataFlags, - session_id: u32, - data: NowVarBuf, -} - -impl NowExecDataMsg { - const NAME: &'static str = "NOW_EXEC_DATA_MSG"; - const FIXED_PART_SIZE: usize = 4; - - pub fn new(flags: NowExecDataFlags, session_id: u32, data: NowVarBuf) -> Self { - Self { - flags, - session_id, - data, - } - } - - pub fn flags(&self) -> NowExecDataFlags { - self.flags - } - - pub fn session_id(&self) -> u32 { - self.session_id - } - - pub fn data(&self) -> &NowVarBuf { - &self.data - } - - // LINTS: Overall message size always fits into usize; VarBuf size always a few powers of 2 less - // than u32::MAX, therefore it fits into usize - #[allow(clippy::arithmetic_side_effects)] - fn body_size(&self) -> usize { - Self::FIXED_PART_SIZE + self.data.size() - } - - pub(super) fn decode_from_body(header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let flags = NowExecDataFlags::from_bits_retain(header.flags); - let session_id = src.read_u32(); - let data = NowVarBuf::decode(src)?; - - Ok(Self { - flags, - session_id, - data, - }) - } -} - -impl Encode for NowExecDataMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: cast_length!("size", self.body_size())?, - class: NowMessageClass::EXEC, - kind: NowExecMsgKind::DATA.0, - flags: self.flags.bits(), - }; - - header.encode(dst)?; - - ensure_fixed_part_size!(in: dst); - dst.write_u32(self.session_id); - self.data.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - // LINTS: See body_size() - #[allow(clippy::arithmetic_side_effects)] - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE + self.body_size() - } -} - -impl Decode<'_> for NowExecDataMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowExecMsgKind(header.kind)) { - (NowMessageClass::EXEC, NowExecMsgKind::DATA) => Self::decode_from_body(header, src), - _ => Err(invalid_field_err!("type", "invalid message type")), - } - } -} - -impl From for NowMessage { - fn from(msg: NowExecDataMsg) -> Self { - NowMessage::Exec(NowExecMessage::Data(msg)) - } -} diff --git a/crates/now-proto-pdu/src/exec/mod.rs b/crates/now-proto-pdu/src/exec/mod.rs deleted file mode 100644 index 99560344..00000000 --- a/crates/now-proto-pdu/src/exec/mod.rs +++ /dev/null @@ -1,142 +0,0 @@ -mod abort; -mod batch; -mod cancel_req; -mod cancel_rsp; -mod capset; -mod cmd; -mod data; -mod process; -mod pwsh; -mod result; -mod run; -mod shell; -mod win_ps; - -pub use abort::NowExecAbortMsg; -pub use batch::NowExecBatchMsg; -pub use cancel_req::NowExecCancelReqMsg; -pub use cancel_rsp::NowExecCancelRspMsg; -pub use capset::{NowExecCapsetFlags, NowExecCapsetMsg}; -pub use data::{NowExecDataFlags, NowExecDataMsg}; -use ironrdp_core::{invalid_field_err, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor}; -pub use process::NowExecProcessMsg; -pub use pwsh::NowExecPwshMsg; -pub use result::NowExecResultMsg; -pub use run::NowExecRunMsg; -pub use shell::NowExecShellMsg; -pub use win_ps::{NowExecWinPsFlags, NowExecWinPsMsg}; - -use crate::NowHeader; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum NowExecMessage { - Capset(NowExecCapsetMsg), - Abort(NowExecAbortMsg), - CancelReq(NowExecCancelReqMsg), - CancelRsp(NowExecCancelRspMsg), - Result(NowExecResultMsg), - Data(NowExecDataMsg), - Run(NowExecRunMsg), - // TODO: Define `Cmd` message in specification - Process(NowExecProcessMsg), - Shell(NowExecShellMsg), - Batch(NowExecBatchMsg), - WinPs(NowExecWinPsMsg), - Pwsh(NowExecPwshMsg), -} - -impl NowExecMessage { - const NAME: &'static str = "NOW_EXEC_MSG"; - - pub fn decode_from_body(header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - match NowExecMsgKind(header.kind) { - NowExecMsgKind::CAPSET => Ok(Self::Capset(NowExecCapsetMsg::new( - NowExecCapsetFlags::from_bits_retain(header.flags), - ))), - NowExecMsgKind::ABORT => Ok(Self::Abort(NowExecAbortMsg::decode_from_body(header, src)?)), - NowExecMsgKind::CANCEL_REQ => Ok(Self::CancelReq(NowExecCancelReqMsg::decode_from_body(header, src)?)), - NowExecMsgKind::CANCEL_RSP => Ok(Self::CancelRsp(NowExecCancelRspMsg::decode_from_body(header, src)?)), - NowExecMsgKind::RESULT => Ok(Self::Result(NowExecResultMsg::decode_from_body(header, src)?)), - NowExecMsgKind::DATA => Ok(Self::Data(NowExecDataMsg::decode_from_body(header, src)?)), - NowExecMsgKind::RUN => Ok(Self::Run(NowExecRunMsg::decode_from_body(header, src)?)), - NowExecMsgKind::PROCESS => Ok(Self::Process(NowExecProcessMsg::decode_from_body(header, src)?)), - NowExecMsgKind::SHELL => Ok(Self::Shell(NowExecShellMsg::decode_from_body(header, src)?)), - NowExecMsgKind::BATCH => Ok(Self::Batch(NowExecBatchMsg::decode_from_body(header, src)?)), - NowExecMsgKind::WINPS => Ok(Self::WinPs(NowExecWinPsMsg::decode_from_body(header, src)?)), - NowExecMsgKind::PWSH => Ok(Self::Pwsh(NowExecPwshMsg::decode_from_body(header, src)?)), - _ => Err(invalid_field_err!("type", "invalid message type")), - } - } -} - -impl Encode for NowExecMessage { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - match self { - Self::Capset(msg) => msg.encode(dst), - Self::Abort(msg) => msg.encode(dst), - Self::CancelReq(msg) => msg.encode(dst), - Self::CancelRsp(msg) => msg.encode(dst), - Self::Result(msg) => msg.encode(dst), - Self::Data(msg) => msg.encode(dst), - Self::Run(msg) => msg.encode(dst), - Self::Process(msg) => msg.encode(dst), - Self::Shell(msg) => msg.encode(dst), - Self::Batch(msg) => msg.encode(dst), - Self::WinPs(msg) => msg.encode(dst), - Self::Pwsh(msg) => msg.encode(dst), - } - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - match self { - Self::Capset(msg) => msg.size(), - Self::Abort(msg) => msg.size(), - Self::CancelReq(msg) => msg.size(), - Self::CancelRsp(msg) => msg.size(), - Self::Result(msg) => msg.size(), - Self::Data(msg) => msg.size(), - Self::Run(msg) => msg.size(), - Self::Process(msg) => msg.size(), - Self::Shell(msg) => msg.size(), - Self::Batch(msg) => msg.size(), - Self::WinPs(msg) => msg.size(), - Self::Pwsh(msg) => msg.size(), - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct NowExecMsgKind(pub u8); - -impl NowExecMsgKind { - /// NOW-PROTO: NOW_EXEC_CAPSET_MSG_ID - pub const CAPSET: Self = Self(0x00); - /// NOW-PROTO: NOW_EXEC_ABORT_MSG_ID - pub const ABORT: Self = Self(0x01); - /// NOW-PROTO: NOW_EXEC_CANCEL_REQ_MSG_ID - pub const CANCEL_REQ: Self = Self(0x02); - /// NOW-PROTO: NOW_EXEC_CANCEL_RSP_MSG_ID - pub const CANCEL_RSP: Self = Self(0x03); - /// NOW-PROTO: NOW_EXEC_RESULT_MSG_ID - pub const RESULT: Self = Self(0x04); - /// NOW-PROTO: NOW_EXEC_DATA_MSG_ID - pub const DATA: Self = Self(0x05); - /// NOW-PROTO: NOW_EXEC_RUN_MSG_ID - pub const RUN: Self = Self(0x10); - // /// NOW-PROTO: NOW_EXEC_CMD_MSG_ID - // pub const CMD: Self = Self(0x11); - /// NOW-PROTO: NOW_EXEC_PROCESS_MSG_ID - pub const PROCESS: Self = Self(0x12); - /// NOW-PROTO: NOW_EXEC_SHELL_MSG_ID - pub const SHELL: Self = Self(0x13); - /// NOW-PROTO: NOW_EXEC_BATCH_MSG_ID - pub const BATCH: Self = Self(0x14); - /// NOW-PROTO: NOW_EXEC_WINPS_MSG_ID - pub const WINPS: Self = Self(0x15); - /// NOW-PROTO: NOW_EXEC_PWSH_MSG_ID - pub const PWSH: Self = Self(0x16); -} diff --git a/crates/now-proto-pdu/src/exec/process.rs b/crates/now-proto-pdu/src/exec/process.rs deleted file mode 100644 index 24dc0d3c..00000000 --- a/crates/now-proto-pdu/src/exec/process.rs +++ /dev/null @@ -1,140 +0,0 @@ -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, -}; - -use crate::{NowExecMessage, NowExecMsgKind, NowHeader, NowMessage, NowMessageClass, NowVarStr}; - -/// The NOW_EXEC_PROCESS_MSG message is used to send a Windows CreateProcess() request. -/// -/// NOW-PROTO: NOW_EXEC_PROCESS_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowExecProcessMsg { - session_id: u32, - filename: NowVarStr, - parameters: NowVarStr, - directory: NowVarStr, -} - -impl NowExecProcessMsg { - const NAME: &'static str = "NOW_EXEC_PROCESS_MSG"; - const FIXED_PART_SIZE: usize = 4; - - pub fn new( - session_id: u32, - filename: NowVarStr, - parameters: NowVarStr, - directory: NowVarStr, - ) -> DecodeResult { - let msg = Self { - session_id, - filename, - parameters, - directory, - }; - - msg.ensure_message_size()?; - - Ok(msg) - } - - fn ensure_message_size(&self) -> DecodeResult<()> { - let _message_size = Self::FIXED_PART_SIZE - .checked_add(self.filename.size()) - .and_then(|size| size.checked_add(self.parameters.size())) - .and_then(|size| size.checked_add(self.directory.size())) - .ok_or_else(|| invalid_field_err!("size", "message size overflow"))?; - - Ok(()) - } - - pub fn session_id(&self) -> u32 { - self.session_id - } - - pub fn filename(&self) -> &NowVarStr { - &self.filename - } - - pub fn parameters(&self) -> &NowVarStr { - &self.parameters - } - - pub fn directory(&self) -> &NowVarStr { - &self.directory - } - - // LINTS: Overall message size is validated in the constructor/decode method - #[allow(clippy::arithmetic_side_effects)] - fn body_size(&self) -> usize { - Self::FIXED_PART_SIZE + self.filename.size() + self.parameters.size() + self.directory.size() - } - - pub(super) fn decode_from_body(_header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let session_id = src.read_u32(); - let filename = NowVarStr::decode(src)?; - let parameters = NowVarStr::decode(src)?; - let directory = NowVarStr::decode(src)?; - - let msg = Self { - session_id, - filename, - parameters, - directory, - }; - - msg.ensure_message_size()?; - - Ok(msg) - } -} - -impl Encode for NowExecProcessMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: cast_length!("size", self.body_size())?, - class: NowMessageClass::EXEC, - kind: NowExecMsgKind::PROCESS.0, - flags: 0, - }; - - header.encode(dst)?; - - ensure_fixed_part_size!(in: dst); - dst.write_u32(self.session_id); - self.filename.encode(dst)?; - self.parameters.encode(dst)?; - self.directory.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - // LINTS: See body_size() - #[allow(clippy::arithmetic_side_effects)] - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE + self.body_size() - } -} - -impl Decode<'_> for NowExecProcessMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowExecMsgKind(header.kind)) { - (NowMessageClass::EXEC, NowExecMsgKind::PROCESS) => Self::decode_from_body(header, src), - _ => Err(invalid_field_err!("type", "invalid message type")), - } - } -} - -impl From for NowMessage { - fn from(msg: NowExecProcessMsg) -> Self { - NowMessage::Exec(NowExecMessage::Process(msg)) - } -} diff --git a/crates/now-proto-pdu/src/exec/pwsh.rs b/crates/now-proto-pdu/src/exec/pwsh.rs deleted file mode 100644 index 2d655462..00000000 --- a/crates/now-proto-pdu/src/exec/pwsh.rs +++ /dev/null @@ -1,175 +0,0 @@ -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, -}; - -use crate::{NowExecMessage, NowExecMsgKind, NowExecWinPsFlags, NowHeader, NowMessage, NowMessageClass, NowVarStr}; - -/// The NOW_EXEC_PWSH_MSG message is used to execute a remote PowerShell 7 (pwsh) command. -/// -/// NOW-PROTO: NOW_EXEC_PWSH_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowExecPwshMsg { - flags: NowExecWinPsFlags, - session_id: u32, - command: NowVarStr, - execution_policy: NowVarStr, - configuration_name: NowVarStr, -} - -impl NowExecPwshMsg { - const NAME: &'static str = "NOW_EXEC_PWSH_MSG"; - const FIXED_PART_SIZE: usize = 4; - - pub fn new(session_id: u32, command: NowVarStr) -> DecodeResult { - let msg = Self { - session_id, - command, - flags: NowExecWinPsFlags::empty(), - execution_policy: NowVarStr::empty(), - configuration_name: NowVarStr::empty(), - }; - - msg.ensure_message_size()?; - - Ok(msg) - } - - fn ensure_message_size(&self) -> DecodeResult<()> { - let _message_size = Self::FIXED_PART_SIZE - .checked_add(self.command.size()) - .and_then(|size| size.checked_add(self.execution_policy.size())) - .and_then(|size| size.checked_add(self.configuration_name.size())) - .ok_or_else(|| invalid_field_err!("size", "message size overflow"))?; - - Ok(()) - } - - #[must_use] - pub fn with_flags(mut self, flags: NowExecWinPsFlags) -> Self { - self.flags = flags; - self - } - - pub fn with_execution_policy(mut self, execution_policy: NowVarStr) -> DecodeResult { - self.execution_policy = execution_policy; - self.flags |= NowExecWinPsFlags::EXECUTION_POLICY; - - self.ensure_message_size()?; - - Ok(self) - } - - pub fn with_configuration_name(mut self, configuration_name: NowVarStr) -> DecodeResult { - self.configuration_name = configuration_name; - self.flags |= NowExecWinPsFlags::CONFIGURATION_NAME; - - self.ensure_message_size()?; - - Ok(self) - } - - pub fn flags(&self) -> NowExecWinPsFlags { - self.flags - } - - pub fn session_id(&self) -> u32 { - self.session_id - } - - pub fn command(&self) -> &NowVarStr { - &self.command - } - - pub fn execution_policy(&self) -> Option<&NowVarStr> { - if self.flags.contains(NowExecWinPsFlags::EXECUTION_POLICY) { - Some(&self.execution_policy) - } else { - None - } - } - - pub fn configuration_name(&self) -> Option<&NowVarStr> { - if self.flags.contains(NowExecWinPsFlags::CONFIGURATION_NAME) { - Some(&self.configuration_name) - } else { - None - } - } - - // LINTS: Overall message size is validated in the constructor/decode method - #[allow(clippy::arithmetic_side_effects)] - fn body_size(&self) -> usize { - Self::FIXED_PART_SIZE + self.command.size() + self.execution_policy.size() + self.configuration_name.size() - } - - pub(super) fn decode_from_body(header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let flags = NowExecWinPsFlags::from_bits_retain(header.flags); - let session_id = src.read_u32(); - let command = NowVarStr::decode(src)?; - let execution_policy = NowVarStr::decode(src)?; - let configuration_name = NowVarStr::decode(src)?; - - let msg = Self { - flags, - session_id, - command, - execution_policy, - configuration_name, - }; - - msg.ensure_message_size()?; - - Ok(msg) - } -} - -impl Encode for NowExecPwshMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: cast_length!("size", self.body_size())?, - class: NowMessageClass::EXEC, - kind: NowExecMsgKind::PWSH.0, - flags: self.flags.bits(), - }; - - header.encode(dst)?; - - ensure_fixed_part_size!(in: dst); - dst.write_u32(self.session_id); - self.command.encode(dst)?; - self.execution_policy.encode(dst)?; - self.configuration_name.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - // LINTS: see body_size() - #[allow(clippy::arithmetic_side_effects)] - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE + self.body_size() - } -} - -impl Decode<'_> for NowExecPwshMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowExecMsgKind(header.kind)) { - (NowMessageClass::EXEC, NowExecMsgKind::PWSH) => Self::decode_from_body(header, src), - _ => Err(invalid_field_err!("type", "invalid message type")), - } - } -} - -impl From for NowMessage { - fn from(msg: NowExecPwshMsg) -> Self { - NowMessage::Exec(NowExecMessage::Pwsh(msg)) - } -} diff --git a/crates/now-proto-pdu/src/exec/result.rs b/crates/now-proto-pdu/src/exec/result.rs deleted file mode 100644 index d6010673..00000000 --- a/crates/now-proto-pdu/src/exec/result.rs +++ /dev/null @@ -1,93 +0,0 @@ -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, -}; - -use crate::{NowExecMessage, NowExecMsgKind, NowHeader, NowMessage, NowMessageClass, NowStatus}; - -/// The NOW_EXEC_RESULT_MSG message is used to return the result of an execution request. -/// -/// NOW_PROTO: NOW_EXEC_RESULT_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowExecResultMsg { - session_id: u32, - status: NowStatus, -} - -impl NowExecResultMsg { - const NAME: &'static str = "NOW_EXEC_RESULT_MSG"; - const FIXED_PART_SIZE: usize = 4; - - pub fn new(session_id: u32, status: NowStatus) -> Self { - Self { session_id, status } - } - - pub fn session_id(&self) -> u32 { - self.session_id - } - - pub fn status(&self) -> &NowStatus { - &self.status - } - - // LINTS: Overall message size always fits into usize - #[allow(clippy::arithmetic_side_effects)] - fn body_size(&self) -> usize { - Self::FIXED_PART_SIZE + self.status.size() - } - - pub(super) fn decode_from_body(_header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let session_id = src.read_u32(); - let status = NowStatus::decode(src)?; - - Ok(Self { session_id, status }) - } -} - -impl Encode for NowExecResultMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: cast_length!("size", self.body_size())?, - class: NowMessageClass::EXEC, - kind: NowExecMsgKind::RESULT.0, - flags: 0, - }; - - header.encode(dst)?; - - ensure_fixed_part_size!(in: dst); - dst.write_u32(self.session_id); - self.status.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - // LINTS: See body_size() - #[allow(clippy::arithmetic_side_effects)] - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE + self.body_size() - } -} - -impl Decode<'_> for NowExecResultMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowExecMsgKind(header.kind)) { - (NowMessageClass::EXEC, NowExecMsgKind::RESULT) => Self::decode_from_body(header, src), - _ => Err(invalid_field_err!("type", "invalid message type")), - } - } -} - -impl From for NowMessage { - fn from(msg: NowExecResultMsg) -> Self { - NowMessage::Exec(NowExecMessage::Result(msg)) - } -} diff --git a/crates/now-proto-pdu/src/exec/run.rs b/crates/now-proto-pdu/src/exec/run.rs deleted file mode 100644 index b39174ea..00000000 --- a/crates/now-proto-pdu/src/exec/run.rs +++ /dev/null @@ -1,112 +0,0 @@ -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, -}; - -use crate::{NowExecMessage, NowExecMsgKind, NowHeader, NowMessage, NowMessageClass, NowVarStr}; - -/// The NOW_EXEC_RUN_MSG message is used to send a run request. This request type maps to starting -/// a program by using the “Run” menu on operating systems (the Start Menu on Windows, the Dock on -/// macOS etc.). The execution of programs started with NOW_EXEC_RUN_MSG is not followed and does -/// not send back the output. -/// -/// NOW_PROTO: NOW_EXEC_RUN_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowExecRunMsg { - session_id: u32, - command: NowVarStr, -} - -impl NowExecRunMsg { - const NAME: &'static str = "NOW_EXEC_RUN_MSG"; - const FIXED_PART_SIZE: usize = 4; - - pub fn new(session_id: u32, command: NowVarStr) -> DecodeResult { - let msg = Self { session_id, command }; - - msg.ensure_message_size()?; - - Ok(msg) - } - - fn ensure_message_size(&self) -> DecodeResult<()> { - let _message_size = Self::FIXED_PART_SIZE - .checked_add(self.command.size()) - .ok_or_else(|| invalid_field_err!("size", "message size overflow"))?; - - Ok(()) - } - - pub fn session_id(&self) -> u32 { - self.session_id - } - - pub fn command(&self) -> &NowVarStr { - &self.command - } - - // LINTS: Overall message size is validated in the constructor/decode method - #[allow(clippy::arithmetic_side_effects)] - fn body_size(&self) -> usize { - Self::FIXED_PART_SIZE + self.command.size() - } - - pub(super) fn decode_from_body(_header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let session_id = src.read_u32(); - let command = NowVarStr::decode(src)?; - - let msg = Self { session_id, command }; - - msg.ensure_message_size()?; - - Ok(msg) - } -} - -impl Encode for NowExecRunMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: cast_length!("size", self.body_size())?, - class: NowMessageClass::EXEC, - kind: NowExecMsgKind::RUN.0, - flags: 0, - }; - - header.encode(dst)?; - - ensure_fixed_part_size!(in: dst); - dst.write_u32(self.session_id); - self.command.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - // LINTS: See body_size() - #[allow(clippy::arithmetic_side_effects)] - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE + self.body_size() - } -} - -impl Decode<'_> for NowExecRunMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowExecMsgKind(header.kind)) { - (NowMessageClass::EXEC, NowExecMsgKind::RUN) => Self::decode_from_body(header, src), - _ => Err(invalid_field_err!("type", "invalid message type")), - } - } -} - -impl From for NowMessage { - fn from(msg: NowExecRunMsg) -> Self { - NowMessage::Exec(NowExecMessage::Run(msg)) - } -} diff --git a/crates/now-proto-pdu/src/exec/shell.rs b/crates/now-proto-pdu/src/exec/shell.rs deleted file mode 100644 index 53905bc2..00000000 --- a/crates/now-proto-pdu/src/exec/shell.rs +++ /dev/null @@ -1,125 +0,0 @@ -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, -}; - -use crate::{NowExecMessage, NowExecMsgKind, NowHeader, NowMessage, NowMessageClass, NowVarStr}; - -/// The NOW_EXEC_SHELL_MSG message is used to execute a remote shell command. -/// -/// NOW-PROTO: NOW_EXEC_SHELL_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowExecShellMsg { - session_id: u32, - command: NowVarStr, - shell: NowVarStr, -} - -impl NowExecShellMsg { - const NAME: &'static str = "NOW_EXEC_SHELL_MSG"; - const FIXED_PART_SIZE: usize = 4; - - pub fn new(session_id: u32, command: NowVarStr, shell: NowVarStr) -> DecodeResult { - let msg = Self { - session_id, - command, - shell, - }; - - msg.ensure_message_size()?; - - Ok(msg) - } - - fn ensure_message_size(&self) -> DecodeResult<()> { - let _message_size = Self::FIXED_PART_SIZE - .checked_add(self.command.size()) - .and_then(|size| size.checked_add(self.shell.size())) - .ok_or_else(|| invalid_field_err!("size", "message size overflow"))?; - - Ok(()) - } - - pub fn session_id(&self) -> u32 { - self.session_id - } - - pub fn command(&self) -> &NowVarStr { - &self.command - } - - pub fn shell(&self) -> &NowVarStr { - &self.shell - } - - // LINTS: Overall message size is validated in the constructor/decode method - #[allow(clippy::arithmetic_side_effects)] - fn body_size(&self) -> usize { - Self::FIXED_PART_SIZE + self.command.size() + self.shell.size() - } - - pub(super) fn decode_from_body(_header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let session_id = src.read_u32(); - let command = NowVarStr::decode(src)?; - let shell = NowVarStr::decode(src)?; - - let msg = Self { - session_id, - command, - shell, - }; - - msg.ensure_message_size()?; - - Ok(msg) - } -} - -impl Encode for NowExecShellMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: cast_length!("size", self.body_size())?, - class: NowMessageClass::EXEC, - kind: NowExecMsgKind::SHELL.0, - flags: 0, - }; - - header.encode(dst)?; - - ensure_fixed_part_size!(in: dst); - dst.write_u32(self.session_id); - self.command.encode(dst)?; - self.shell.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - // LINTS: See body_size() - #[allow(clippy::arithmetic_side_effects)] - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE + self.body_size() - } -} - -impl Decode<'_> for NowExecShellMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowExecMsgKind(header.kind)) { - (NowMessageClass::EXEC, NowExecMsgKind::SHELL) => Self::decode_from_body(header, src), - _ => Err(invalid_field_err!("type", "invalid message type")), - } - } -} - -impl From for NowMessage { - fn from(msg: NowExecShellMsg) -> Self { - NowMessage::Exec(NowExecMessage::Shell(msg)) - } -} diff --git a/crates/now-proto-pdu/src/exec/win_ps.rs b/crates/now-proto-pdu/src/exec/win_ps.rs deleted file mode 100644 index 0eb4a225..00000000 --- a/crates/now-proto-pdu/src/exec/win_ps.rs +++ /dev/null @@ -1,217 +0,0 @@ -use bitflags::bitflags; -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, -}; - -use crate::{NowExecMessage, NowExecMsgKind, NowHeader, NowMessage, NowMessageClass, NowVarStr}; - -bitflags! { - /// NOW-PROTO: NOW_EXEC_WINPS_MSG msgFlags field. - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub struct NowExecWinPsFlags: u16 { - /// PowerShell -NoLogo option. - /// - /// NOW-PROTO: NOW_EXEC_FLAG_PS_NO_LOGO - const NO_LOGO = 0x0001; - /// PowerShell -NoExit option. - /// - /// NOW-PROTO: NOW_EXEC_FLAG_PS_NO_EXIT - const NO_EXIT = 0x0002; - /// PowerShell -Sta option. - /// - /// NOW-PROTO: NOW_EXEC_FLAG_PS_STA - const STA = 0x0004; - /// PowerShell -Mta option - /// - /// NOW-PROTO: NOW_EXEC_FLAG_PS_MTA - const MTA = 0x0008; - /// PowerShell -NoProfile option. - /// - /// NOW-PROTO: NOW_EXEC_FLAG_PS_NO_PROFILE - const NO_PROFILE = 0x0010; - /// PowerShell -NonInteractive option. - /// - /// NOW-PROTO: NOW_EXEC_FLAG_PS_NON_INTERACTIVE - const NON_INTERACTIVE = 0x0020; - /// The PowerShell -ExecutionPolicy parameter is specified with value in - /// executionPolicy field. - /// - /// NOW-PROTO: NOW_EXEC_FLAG_PS_EXECUTION_POLICY - const EXECUTION_POLICY = 0x0040; - /// The PowerShell -ConfigurationName parameter is specified with value in - /// configurationName field. - /// - /// NOW-PROTO: NOW_EXEC_FLAG_PS_CONFIGURATION_NAME - const CONFIGURATION_NAME = 0x0080; - } -} - -/// The NOW_EXEC_WINPS_MSG message is used to execute a remote Windows PowerShell (powershell.exe) command. -/// -/// NOW-PROTO: NOW_EXEC_WINPS_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowExecWinPsMsg { - flags: NowExecWinPsFlags, - session_id: u32, - command: NowVarStr, - execution_policy: NowVarStr, - configuration_name: NowVarStr, -} - -impl NowExecWinPsMsg { - const NAME: &'static str = "NOW_EXEC_WINPS_MSG"; - const FIXED_PART_SIZE: usize = 4; - - pub fn new(session_id: u32, command: NowVarStr) -> DecodeResult { - let msg = Self { - session_id, - command, - flags: NowExecWinPsFlags::empty(), - execution_policy: NowVarStr::empty(), - configuration_name: NowVarStr::empty(), - }; - - msg.ensure_message_size()?; - - Ok(msg) - } - - fn ensure_message_size(&self) -> DecodeResult<()> { - let _message_size = Self::FIXED_PART_SIZE - .checked_add(self.command.size()) - .and_then(|size| size.checked_add(self.execution_policy.size())) - .and_then(|size| size.checked_add(self.configuration_name.size())) - .ok_or_else(|| invalid_field_err!("size", "message size overflow"))?; - - Ok(()) - } - - #[must_use] - pub fn with_flags(mut self, flags: NowExecWinPsFlags) -> Self { - self.flags = flags; - self - } - - pub fn with_execution_policy(mut self, execution_policy: NowVarStr) -> DecodeResult { - self.execution_policy = execution_policy; - self.flags |= NowExecWinPsFlags::EXECUTION_POLICY; - - self.ensure_message_size()?; - - Ok(self) - } - - pub fn with_configuration_name(mut self, configuration_name: NowVarStr) -> DecodeResult { - self.configuration_name = configuration_name; - self.flags |= NowExecWinPsFlags::CONFIGURATION_NAME; - - self.ensure_message_size()?; - - Ok(self) - } - - pub fn flags(&self) -> NowExecWinPsFlags { - self.flags - } - - pub fn session_id(&self) -> u32 { - self.session_id - } - - pub fn command(&self) -> &NowVarStr { - &self.command - } - - pub fn execution_policy(&self) -> Option<&NowVarStr> { - if self.flags.contains(NowExecWinPsFlags::EXECUTION_POLICY) { - Some(&self.execution_policy) - } else { - None - } - } - - pub fn configuration_name(&self) -> Option<&NowVarStr> { - if self.flags.contains(NowExecWinPsFlags::CONFIGURATION_NAME) { - Some(&self.configuration_name) - } else { - None - } - } - - // LINTS: Overall message size is validated in the constructor/decode method - #[allow(clippy::arithmetic_side_effects)] - fn body_size(&self) -> usize { - Self::FIXED_PART_SIZE + self.command.size() + self.execution_policy.size() + self.configuration_name.size() - } - - pub(super) fn decode_from_body(header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let flags = NowExecWinPsFlags::from_bits_retain(header.flags); - let session_id = src.read_u32(); - let command = NowVarStr::decode(src)?; - let execution_policy = NowVarStr::decode(src)?; - let configuration_name = NowVarStr::decode(src)?; - - let msg = Self { - flags, - session_id, - command, - execution_policy, - configuration_name, - }; - - msg.ensure_message_size()?; - - Ok(msg) - } -} - -impl Encode for NowExecWinPsMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: cast_length!("size", self.body_size())?, - class: NowMessageClass::EXEC, - kind: NowExecMsgKind::WINPS.0, - flags: self.flags.bits(), - }; - - header.encode(dst)?; - - ensure_fixed_part_size!(in: dst); - dst.write_u32(self.session_id); - self.command.encode(dst)?; - self.execution_policy.encode(dst)?; - self.configuration_name.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - // LINTS: See body_size() - #[allow(clippy::arithmetic_side_effects)] - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE + self.body_size() - } -} - -impl Decode<'_> for NowExecWinPsMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowExecMsgKind(header.kind)) { - (NowMessageClass::EXEC, NowExecMsgKind::WINPS) => Self::decode_from_body(header, src), - _ => Err(invalid_field_err!("type", "invalid message type")), - } - } -} - -impl From for NowMessage { - fn from(msg: NowExecWinPsMsg) -> Self { - NowMessage::Exec(NowExecMessage::WinPs(msg)) - } -} diff --git a/crates/now-proto-pdu/src/lib.rs b/crates/now-proto-pdu/src/lib.rs deleted file mode 100644 index f6e05c5e..00000000 --- a/crates/now-proto-pdu/src/lib.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![doc = include_str!("../README.md")] -#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")] -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; - -#[macro_use] -extern crate ironrdp_pdu; - -// Ensure that we do not compile on platforms with less than 4 bytes per u32. It is pretty safe -// to assume that NOW-PROTO will not ever be used on 8/16-bit MCUs or CPUs. -// -// This is required to safely cast u32 to usize without additional checks. -const_assert!(size_of::() >= 4); - -#[macro_use] -mod macros; - -mod core; -mod exec; -mod message; -mod session; -mod system; - -pub use core::*; - -pub use exec::*; -pub use message::*; -pub use session::*; -pub use system::*; diff --git a/crates/now-proto-pdu/src/macros.rs b/crates/now-proto-pdu/src/macros.rs deleted file mode 100644 index e1fb25b6..00000000 --- a/crates/now-proto-pdu/src/macros.rs +++ /dev/null @@ -1,14 +0,0 @@ -/// Creates a `PduError` with `UnsupportedValue` kind -#[macro_export] -macro_rules! unsupported_message_err { - ( $name:expr, class: $class:expr, kind: $kind:expr $(,)? ) => {{ - ironrdp_core::unsupported_value_err( - "NOW-PROTO", - $name, - alloc::format!("CLASS({}); KIND({})", $class, $kind) - ) - }}; - ( class: $class:expr, kind: $kind:expr $(,)? ) => {{ - unsupported_message_err!(Self::NAME, class: $class, kind: $kind) - }}; -} diff --git a/crates/now-proto-pdu/src/message.rs b/crates/now-proto-pdu/src/message.rs deleted file mode 100644 index 259101fc..00000000 --- a/crates/now-proto-pdu/src/message.rs +++ /dev/null @@ -1,58 +0,0 @@ -use ironrdp_core::{Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor}; - -use crate::{NowExecMessage, NowHeader, NowMessageClass, NowSessionMessage, NowSystemMessage}; - -/// Wrapper type for messages transferred over the NOW-PROTO communication channel. -/// -/// NOW-PROTO: NOW_*_MSG messages -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum NowMessage { - System(NowSystemMessage), - Session(NowSessionMessage), - Exec(NowExecMessage), -} - -impl NowMessage { - const NAME: &'static str = "NOW_MSG"; -} - -impl Encode for NowMessage { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - match self { - Self::System(msg) => msg.encode(dst), - Self::Session(msg) => msg.encode(dst), - Self::Exec(msg) => msg.encode(dst), - } - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - match self { - Self::System(msg) => msg.size(), - Self::Session(msg) => msg.size(), - Self::Exec(msg) => msg.size(), - } - } -} - -impl Decode<'_> for NowMessage { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - Self::decode_from_body(header, src) - } -} - -impl NowMessage { - pub fn decode_from_body(header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - match NowMessageClass(header.class.0) { - NowMessageClass::SYSTEM => Ok(Self::System(NowSystemMessage::decode_from_body(header, src)?)), - NowMessageClass::SESSION => Ok(Self::Session(NowSessionMessage::decode_from_body(header, src)?)), - NowMessageClass::EXEC => Ok(Self::Exec(NowExecMessage::decode_from_body(header, src)?)), - // Handle unknown class; Unknown kind is handled by underlying message type. - _ => Err(unsupported_message_err!(class: header.class.0, kind: header.kind)), - } - } -} diff --git a/crates/now-proto-pdu/src/session/lock.rs b/crates/now-proto-pdu/src/session/lock.rs deleted file mode 100644 index d77f70bd..00000000 --- a/crates/now-proto-pdu/src/session/lock.rs +++ /dev/null @@ -1,54 +0,0 @@ -use ironrdp_core::{Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor}; - -use crate::{NowHeader, NowMessage, NowMessageClass, NowSessionMessage, NowSessionMessageKind}; - -/// The NOW_SESSION_LOCK_MSG is used to request locking the user session. -/// -/// NOW_PROTO: NOW_SESSION_LOCK_MSG -#[derive(Debug, Clone, PartialEq, Eq, Default)] -#[non_exhaustive] -pub struct NowSessionLockMsg; - -impl NowSessionLockMsg { - const NAME: &'static str = "NOW_SESSION_LOCK_MSG"; -} - -impl Encode for NowSessionLockMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: 0, - class: NowMessageClass::SESSION, - kind: NowSessionMessageKind::LOCK.0, - flags: 0, - }; - - header.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE - } -} - -impl Decode<'_> for NowSessionLockMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowSessionMessageKind(header.kind)) { - (NowMessageClass::SESSION, NowSessionMessageKind::LOCK) => Ok(Self::default()), - _ => Err(unsupported_message_err!(class: header.class.0, kind: header.kind)), - } - } -} - -impl From for NowMessage { - fn from(val: NowSessionLockMsg) -> Self { - NowMessage::Session(NowSessionMessage::Lock(val)) - } -} diff --git a/crates/now-proto-pdu/src/session/logoff.rs b/crates/now-proto-pdu/src/session/logoff.rs deleted file mode 100644 index 8bba4ee7..00000000 --- a/crates/now-proto-pdu/src/session/logoff.rs +++ /dev/null @@ -1,54 +0,0 @@ -use ironrdp_core::{Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor}; - -use crate::{NowHeader, NowMessage, NowMessageClass, NowSessionMessage, NowSessionMessageKind}; - -/// The NOW_SESSION_LOGOFF_MSG is used to request a user session logoff. -/// -/// NOW_PROTO: NOW_SESSION_LOGOFF_MSG -#[derive(Debug, Clone, PartialEq, Eq, Default)] -#[non_exhaustive] -pub struct NowSessionLogoffMsg; - -impl NowSessionLogoffMsg { - const NAME: &'static str = "NOW_SESSION_LOGOFF_MSG"; -} - -impl Encode for NowSessionLogoffMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: 0, - class: NowMessageClass::SESSION, - kind: NowSessionMessageKind::LOGOFF.0, - flags: 0, - }; - - header.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE - } -} - -impl Decode<'_> for NowSessionLogoffMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowSessionMessageKind(header.kind)) { - (NowMessageClass::SESSION, NowSessionMessageKind::LOGOFF) => Ok(Self::default()), - _ => Err(unsupported_message_err!(class: header.class.0, kind: header.kind)), - } - } -} - -impl From for NowMessage { - fn from(msg: NowSessionLogoffMsg) -> Self { - NowMessage::Session(NowSessionMessage::Logoff(msg)) - } -} diff --git a/crates/now-proto-pdu/src/session/mod.rs b/crates/now-proto-pdu/src/session/mod.rs deleted file mode 100644 index 46a2e407..00000000 --- a/crates/now-proto-pdu/src/session/mod.rs +++ /dev/null @@ -1,78 +0,0 @@ -mod lock; -mod logoff; -mod msg_box_req; -mod msg_box_rsp; - -use ironrdp_core::{DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor}; -pub use lock::NowSessionLockMsg; -pub use logoff::NowSessionLogoffMsg; -pub use msg_box_req::{NowMessageBoxStyle, NowSessionMsgBoxReqMsg}; -pub use msg_box_rsp::{NowMsgBoxResponse, NowSessionMsgBoxRspMsg}; - -use crate::NowHeader; - -/// Wrapper for the `NOW_SESSION_MSG_CLASS_ID` message class. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowSessionMessageKind(pub u8); - -impl NowSessionMessageKind { - /// NOW-PROTO: NOW_SESSION_LOCK_MSG_ID - pub const LOCK: Self = Self(0x01); - /// NOW-PROTO: NOW_SESSION_LOGOFF_MSG_ID - pub const LOGOFF: Self = Self(0x02); - /// NOW-PROTO: NOW_SESSION_MSGBOX_REQ_MSG_ID - pub const MSGBOX_REQ: Self = Self(0x03); - /// NOW-PROTO: NOW_SESSION_MSGBOX_RSP_MSG_ID - pub const MSGBOX_RSP: Self = Self(0x04); -} - -// Wrapper for the `NOW_SESSION_MSG_CLASS_ID` message class. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum NowSessionMessage { - Lock(NowSessionLockMsg), - Logoff(NowSessionLogoffMsg), - MsgBoxReq(NowSessionMsgBoxReqMsg), - MsgBoxRsp(NowSessionMsgBoxRspMsg), -} - -impl NowSessionMessage { - const NAME: &'static str = "NOW_SESSION_MSG"; - - pub fn decode_from_body(header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - match NowSessionMessageKind(header.kind) { - NowSessionMessageKind::LOCK => Ok(Self::Lock(NowSessionLockMsg::default())), - NowSessionMessageKind::LOGOFF => Ok(Self::Logoff(NowSessionLogoffMsg::default())), - NowSessionMessageKind::MSGBOX_REQ => { - Ok(Self::MsgBoxReq(NowSessionMsgBoxReqMsg::decode_from_body(header, src)?)) - } - NowSessionMessageKind::MSGBOX_RSP => { - Ok(Self::MsgBoxRsp(NowSessionMsgBoxRspMsg::decode_from_body(header, src)?)) - } - _ => Err(unsupported_message_err!(class: header.class.0, kind: header.kind)), - } - } -} - -impl Encode for NowSessionMessage { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - match self { - Self::Lock(msg) => msg.encode(dst), - Self::Logoff(msg) => msg.encode(dst), - Self::MsgBoxReq(msg) => msg.encode(dst), - Self::MsgBoxRsp(msg) => msg.encode(dst), - } - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - match self { - Self::Lock(msg) => msg.size(), - Self::Logoff(msg) => msg.size(), - Self::MsgBoxReq(msg) => msg.size(), - Self::MsgBoxRsp(msg) => msg.size(), - } - } -} diff --git a/crates/now-proto-pdu/src/session/msg_box_req.rs b/crates/now-proto-pdu/src/session/msg_box_req.rs deleted file mode 100644 index 9c875810..00000000 --- a/crates/now-proto-pdu/src/session/msg_box_req.rs +++ /dev/null @@ -1,255 +0,0 @@ -use alloc::string::String; - -use bitflags::bitflags; -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, -}; - -use crate::{NowHeader, NowMessage, NowMessageClass, NowSessionMessage, NowSessionMessageKind, NowVarStr}; - -/// Message box style; Directly maps to the WinAPI MessageBox function message box style field. -/// -/// NOW_PROTO: `style` field from NOW_SESSION_MESSAGE_BOX_REQ_MSG -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct NowMessageBoxStyle(u32); - -impl NowMessageBoxStyle { - pub const OK: Self = Self(0x00000000); - pub const OK_CANCEL: Self = Self(0x00000001); - pub const ABORT_RETRY_IGNORE: Self = Self(0x00000002); - pub const YES_NO_CANCEL: Self = Self(0x00000003); - pub const YES_NO: Self = Self(0x00000004); - pub const RETRY_CANCEL: Self = Self(0x00000005); - pub const CANCEL_TRY_CONTINUE: Self = Self(0x00000006); - pub const HELP: Self = Self(0x00004000); - - pub fn new(style: u32) -> Self { - Self(style) - } - - pub fn value(&self) -> u32 { - self.0 - } -} - -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub struct NowSessionMessageBoxFlags: u16 { - /// The title field contains non-default value. - /// - /// NOW_PROTO: NOW_SESSION_MSGBOX_FLAG_TITLE - const TITLE = 0x0001; - - /// The style field contains non-default value. - /// - /// NOW_PROTO: NOW_SESSION_MSGBOX_FLAG_STYLE - const STYLE = 0x0002; - - /// The timeout field contains non-default value. - /// - /// NOW_PROTO: NOW_SESSION_MSGBOX_FLAG_TIMEOUT - const TIMEOUT = 0x0004; - - /// A response message is expected (don't fire and forget) - /// - /// NOW_PROTO: NOW_SESSION_MSGBOX_FLAG_RESPONSE - const RESPONSE = 0x0008; - } -} - -/// The NOW_SESSION_MSGBOX_REQ_MSG is used to show a message box in the user session, similar to -/// what the [WTSSendMessage function](https://learn.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtssendmessagew) -/// does. -/// -/// NOW_PROTO: NOW_SESSION_MSGBOX_REQ_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowSessionMsgBoxReqMsg { - flags: NowSessionMessageBoxFlags, - request_id: u32, - style: NowMessageBoxStyle, - timeout: u32, - title: NowVarStr, - message: NowVarStr, -} - -impl NowSessionMsgBoxReqMsg { - const NAME: &'static str = "NOW_SESSION_MSGBOX_REQ_MSG"; - const FIXED_PART_SIZE: usize = 12; - - pub fn new(request_id: u32, message: NowVarStr) -> DecodeResult { - let msg = Self { - flags: NowSessionMessageBoxFlags::empty(), - request_id, - style: NowMessageBoxStyle::OK, - timeout: 0, - title: NowVarStr::new(String::new()).unwrap(), - message, - }; - - msg.ensure_message_size()?; - - Ok(msg) - } - - fn ensure_message_size(&self) -> DecodeResult<()> { - let _message_size = Self::FIXED_PART_SIZE - .checked_add(self.title.size()) - .and_then(|size| size.checked_add(self.message.size())) - .ok_or_else(|| invalid_field_err!("size", "message size overflow"))?; - - Ok(()) - } - - pub fn with_title(mut self, title: NowVarStr) -> DecodeResult { - self.flags |= NowSessionMessageBoxFlags::TITLE; - self.title = title; - - self.ensure_message_size()?; - - Ok(self) - } - - pub fn with_message(mut self, message: NowVarStr) -> DecodeResult { - self.message = message; - - self.ensure_message_size()?; - - Ok(self) - } - - #[must_use] - pub fn with_style(mut self, style: NowMessageBoxStyle) -> Self { - self.flags |= NowSessionMessageBoxFlags::STYLE; - self.style = style; - self - } - - #[must_use] - pub fn with_timeout(mut self, timeout: u32) -> Self { - self.flags |= NowSessionMessageBoxFlags::TIMEOUT; - self.timeout = timeout; - self - } - - #[must_use] - pub fn with_response(mut self) -> Self { - self.flags |= NowSessionMessageBoxFlags::RESPONSE; - self - } - - pub fn request_id(&self) -> u32 { - self.request_id - } - - pub fn style(&self) -> NowMessageBoxStyle { - if self.flags.contains(NowSessionMessageBoxFlags::STYLE) { - self.style - } else { - NowMessageBoxStyle::OK - } - } - - pub fn timeout(&self) -> Option { - if self.flags.contains(NowSessionMessageBoxFlags::TIMEOUT) && self.timeout > 0 { - Some(self.timeout) - } else { - None - } - } - - pub fn title(&self) -> Option<&NowVarStr> { - if self.flags.contains(NowSessionMessageBoxFlags::TITLE) { - Some(&self.title) - } else { - None - } - } - - pub fn message(&self) -> &NowVarStr { - &self.message - } - - pub fn is_response_expected(&self) -> bool { - self.flags.contains(NowSessionMessageBoxFlags::RESPONSE) - } - - // LINTS: Overall message size is validated in the constructor/decode method - #[allow(clippy::arithmetic_side_effects)] - fn body_size(&self) -> usize { - Self::FIXED_PART_SIZE + self.title.size() + self.message.size() - } - - pub(super) fn decode_from_body(header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let flags = NowSessionMessageBoxFlags::from_bits_retain(header.flags); - let request_id = src.read_u32(); - let style = NowMessageBoxStyle(src.read_u32()); - let timeout = src.read_u32(); - let title = NowVarStr::decode(src)?; - let message = NowVarStr::decode(src)?; - - let msg = Self { - flags, - request_id, - style, - timeout, - title, - message, - }; - - msg.ensure_message_size()?; - - Ok(msg) - } -} - -impl Encode for NowSessionMsgBoxReqMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: cast_length!("size", self.body_size())?, - class: NowMessageClass::SESSION, - kind: NowSessionMessageKind::MSGBOX_REQ.0, - flags: self.flags.bits(), - }; - - header.encode(dst)?; - - ensure_fixed_part_size!(in: dst); - dst.write_u32(self.request_id); - dst.write_u32(self.style.value()); - dst.write_u32(self.timeout); - self.title.encode(dst)?; - self.message.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - // LINTS: See body_size() - #[allow(clippy::arithmetic_side_effects)] - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE + self.body_size() - } -} - -impl Decode<'_> for NowSessionMsgBoxReqMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowSessionMessageKind(header.kind)) { - (NowMessageClass::SESSION, NowSessionMessageKind::MSGBOX_REQ) => Self::decode_from_body(header, src), - _ => Err(unsupported_message_err!(class: header.class.0, kind: header.kind)), - } - } -} - -impl From for NowMessage { - fn from(val: NowSessionMsgBoxReqMsg) -> Self { - NowMessage::Session(NowSessionMessage::MsgBoxReq(val)) - } -} diff --git a/crates/now-proto-pdu/src/session/msg_box_rsp.rs b/crates/now-proto-pdu/src/session/msg_box_rsp.rs deleted file mode 100644 index 2f91cfe9..00000000 --- a/crates/now-proto-pdu/src/session/msg_box_rsp.rs +++ /dev/null @@ -1,140 +0,0 @@ -use ironrdp_core::{ensure_fixed_part_size, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor}; - -use crate::{NowHeader, NowMessage, NowMessageClass, NowSessionMessage, NowSessionMessageKind}; - -/// Message box response; Directly maps to the WinAPI MessageBox function response. -/// -/// NOW_PROTO: `response` field from NOW_SESSION_MESSAGE_BOX_RSP_MSG -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct NowMsgBoxResponse(u32); - -impl NowMsgBoxResponse { - /// OK - /// - /// NOW_PROTO: IDOK - pub const OK: Self = Self(1); - /// Cancel - /// - /// NOW_PROTO: IDCANCEL - pub const CANCEL: Self = Self(2); - /// Abort - /// - /// NOW_PROTO: IDABORT - pub const ABORT: Self = Self(3); - /// Retry - /// - /// NOW_PROTO: IDRETRY - pub const RETRY: Self = Self(4); - /// Ignore - /// - /// NOW_PROTO: IDIGNORE - pub const IGNORE: Self = Self(5); - /// Yes - /// - /// NOW_PROTO: IDYES - pub const YES: Self = Self(6); - /// No - /// - /// NOW_PROTO: IDNO - pub const NO: Self = Self(7); - /// Try Again - /// - /// NOW_PROTO: IDTRYAGAIN - pub const TRY_AGAIN: Self = Self(10); - /// Continue - /// - /// NOW_PROTO: IDCONTINUE - pub const CONTINUE: Self = Self(11); - /// Timeout - /// - /// NOW_PROTO: IDTIMEOUT - pub const TIMEOUT: Self = Self(32000); - - pub fn new(response: u32) -> Self { - Self(response) - } - - pub fn value(&self) -> u32 { - self.0 - } -} - -/// The NOW_SESSION_MSGBOX_RSP_MSG is a message sent in response to NOW_SESSION_MSGBOX_REQ_MSG if -/// the NOW_MSGBOX_FLAG_RESPONSE has been set, and contains the result from the message box dialog. -/// -/// NOW_PROTO: NOW_SESSION_MSGBOX_RSP_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowSessionMsgBoxRspMsg { - request_id: u32, - response: NowMsgBoxResponse, -} - -impl NowSessionMsgBoxRspMsg { - const NAME: &'static str = "NOW_SESSION_MSGBOX_RSP_MSG"; - const FIXED_PART_SIZE: usize = 8; - - pub fn new(request_id: u32, response: NowMsgBoxResponse) -> Self { - Self { request_id, response } - } - - pub fn request_id(&self) -> u32 { - self.request_id - } - - pub fn response(&self) -> NowMsgBoxResponse { - self.response - } - - pub(super) fn decode_from_body(_header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let request_id = src.read_u32(); - let response = NowMsgBoxResponse(src.read_u32()); - - Ok(Self { request_id, response }) - } -} - -impl Encode for NowSessionMsgBoxRspMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: u32::try_from(Self::FIXED_PART_SIZE).unwrap(), - class: NowMessageClass::SESSION, - kind: NowSessionMessageKind::MSGBOX_RSP.0, - flags: 0, - }; - - header.encode(dst)?; - - ensure_fixed_part_size!(in: dst); - dst.write_u32(self.request_id); - dst.write_u32(self.response.value()); - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE + Self::FIXED_PART_SIZE - } -} - -impl Decode<'_> for NowSessionMsgBoxRspMsg { - fn decode(src: &mut ReadCursor<'_>) -> DecodeResult { - let header = NowHeader::decode(src)?; - - match (header.class, NowSessionMessageKind(header.kind)) { - (NowMessageClass::SESSION, NowSessionMessageKind::MSGBOX_RSP) => Self::decode_from_body(header, src), - _ => Err(unsupported_message_err!(class: header.class.0, kind: header.kind)), - } - } -} - -impl From for NowMessage { - fn from(val: NowSessionMsgBoxRspMsg) -> Self { - NowMessage::Session(NowSessionMessage::MsgBoxRsp(val)) - } -} diff --git a/crates/now-proto-pdu/src/system/mod.rs b/crates/now-proto-pdu/src/system/mod.rs deleted file mode 100644 index 7ce5f945..00000000 --- a/crates/now-proto-pdu/src/system/mod.rs +++ /dev/null @@ -1,57 +0,0 @@ -mod shutdown; - -use ironrdp_core::{DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor}; -pub use shutdown::{NowSystemShutdownFlags, NowSystemShutdownMsg}; - -use crate::NowHeader; - -// Wrapper for the `NOW_SYSTEM_MSG_CLASS_ID` message class. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum NowSystemMessage { - Shutdown(NowSystemShutdownMsg), -} - -impl NowSystemMessage { - const NAME: &'static str = "NOW_SYSTEM_MSG"; - - pub fn decode_from_body(header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - match NowSystemMessageKind(header.kind) { - NowSystemMessageKind::SHUTDOWN => Ok(Self::Shutdown(NowSystemShutdownMsg::decode_from_body(header, src)?)), - _ => Err(unsupported_message_err!(class: header.class.0, kind: header.kind)), - } - } -} - -impl Encode for NowSystemMessage { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - match self { - Self::Shutdown(msg) => msg.encode(dst), - } - } - - fn name(&self) -> &'static str { - Self::NAME - } - - fn size(&self) -> usize { - match self { - Self::Shutdown(msg) => msg.size(), - } - } -} - -/// NOW-PROTO: NOW_SYSTEM_INFO_*_ID -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct NowSystemMessageKind(pub u8); - -impl NowSystemMessageKind { - // TODO: NOW_SYSTEM_INFO_REQ_ID/NOW_SYSTEM_INFO_RSP_ID when will be added to the protocol - // specification. - - // /// NOW-PROTO: NOW_SYSTEM_INFO_REQ_ID - // pub const INFO_REQ: Self = Self(0x01); - // /// NOW-PROTO: NOW_SYSTEM_INFO_RSP_ID - // pub const INFO_RSP: Self = Self(0x02); - /// NOW-PROTO: NOW_SYSTEM_SHUTDOWN_ID - pub const SHUTDOWN: Self = Self(0x03); -} diff --git a/crates/now-proto-pdu/src/system/shutdown.rs b/crates/now-proto-pdu/src/system/shutdown.rs deleted file mode 100644 index b16014c8..00000000 --- a/crates/now-proto-pdu/src/system/shutdown.rs +++ /dev/null @@ -1,118 +0,0 @@ -use bitflags::bitflags; -use ironrdp_core::{ - cast_length, ensure_fixed_part_size, invalid_field_err, Decode as _, DecodeResult, Encode, EncodeResult, - ReadCursor, WriteCursor, -}; - -use crate::system::NowSystemMessageKind; -use crate::{NowHeader, NowMessage, NowMessageClass, NowSystemMessage, NowVarStr}; - -bitflags! { - /// NOW_PROTO: NOW_SYSTEM_SHUTDOWN_FLAG_* constants. - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub struct NowSystemShutdownFlags: u16 { - /// Force shutdown - /// - /// NOW-PROTO: NOW_SHUTDOWN_FLAG_FORCE - const FORCE = 0x0001; - /// Reboot after shutdown - /// - /// NOW-PROTO: NOW_SHUTDOWN_FLAG_REBOOT - const REBOOT = 0x0002; - } -} - -/// The NOW_SYSTEM_SHUTDOWN_MSG structure is used to request a system shutdown. -/// -/// NOW_PROTO: NOW_SYSTEM_SHUTDOWN_MSG -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct NowSystemShutdownMsg { - flags: NowSystemShutdownFlags, - /// This system shutdown timeout, in seconds. - timeout: u32, - /// Optional shutdown message. - message: NowVarStr, -} - -impl NowSystemShutdownMsg { - const NAME: &'static str = "NOW_SYSTEM_SHUTDOWN_MSG"; - const FIXED_PART_SIZE: usize = 4 /* u32 timeout */; - - pub fn new(flags: NowSystemShutdownFlags, timeout: u32, message: NowVarStr) -> DecodeResult { - let msg = Self { - flags, - timeout, - message, - }; - - msg.ensure_message_size()?; - - Ok(msg) - } - - fn ensure_message_size(&self) -> DecodeResult<()> { - let _message_size = Self::FIXED_PART_SIZE - .checked_add(self.message.size()) - .ok_or_else(|| invalid_field_err!("size", "message size overflow"))?; - - Ok(()) - } - - // LINTS: Overall message size is validated in the constructor/decode method - #[allow(clippy::arithmetic_side_effects)] - fn body_size(&self) -> usize { - Self::FIXED_PART_SIZE + self.message.size() - } - - pub fn decode_from_body(header: NowHeader, src: &mut ReadCursor<'_>) -> DecodeResult { - ensure_fixed_part_size!(in: src); - - let timeout = src.read_u32(); - let message = NowVarStr::decode(src)?; - - let msg = Self { - flags: NowSystemShutdownFlags::from_bits_retain(header.flags), - timeout, - message, - }; - - msg.ensure_message_size()?; - - Ok(msg) - } -} - -impl Encode for NowSystemShutdownMsg { - fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { - let header = NowHeader { - size: cast_length!("size", self.body_size())?, - class: NowMessageClass::SYSTEM, - kind: NowSystemMessageKind::SHUTDOWN.0, - flags: self.flags.bits(), - }; - - header.encode(dst)?; - - ensure_fixed_part_size!(in: dst); - dst.write_u32(self.timeout); - self.message.encode(dst)?; - - Ok(()) - } - - fn name(&self) -> &'static str { - Self::NAME - } - - // LINTS: See body_size() - #[allow(clippy::arithmetic_side_effects)] - fn size(&self) -> usize { - NowHeader::FIXED_PART_SIZE + self.body_size() - } -} - -impl From for NowMessage { - fn from(msg: NowSystemShutdownMsg) -> Self { - NowMessage::System(NowSystemMessage::Shutdown(msg)) - } -} diff --git a/docs/NOW-spec.md b/docs/NOW-spec.md deleted file mode 100644 index e4504405..00000000 --- a/docs/NOW-spec.md +++ /dev/null @@ -1,1367 +0,0 @@ - -# NOW-PROTO 1.0 - - - - -[[_TOC_]] - -# Messages - -## Transport - -The NOW virtual channel protocol use an RDP dynamic virtual channel ("Devolutions::Now::Agent") as a transport type. - -## Message Syntax - -The following sections specify the NOW protocol message syntax. Unless otherwise specified, all fields defined in this document use the little-endian format. - -### Common Structures - -#### NOW_INTEGER - -Signed and unsigned integer encoding structures of various sizes. - -##### NOW_VARU32 - -The NOW_VARU32 structure is used to encode signed integer values in the range [0, 0x3FFFFFFF]. - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
cval1val2 (optional)val3 (optional)val4 (optional)
- -**c (2 bits)**: A 2-bit integer containing an encoded representation of the number of bytes in this structure. - -| Value | Meaning | -|-------|---------| -| 0 | The val1 field is present (1 byte). | -| 1 | The val1, val2 fields are present (2 bytes). | -| 2 | The val1, val2, val3 fields are present (3 bytes). | -| 3 | The val1, val2, val3, val4 fields are present (4 bytes). | - -**val1 (6 bits)**: A 6-bit integer containing the 6 most significant bits of the integer value represented by this structure. - -**val2 (1 byte)**: An 8-bit integer containing the second most significant bits of the integer value represented by this structure. - -**val3 (1 byte)**: An 8-bit integer containing the third most significant bits of the integer value represented by this structure. - -**val4 (1 byte)**: An 8-bit integer containing the least significant bits of the integer value represented by this structure. - -#### NOW_STRING - -##### NOW_VARSTR - -The NOW_VARSTR structure is used to represent variable-length strings that could be large, while remaining compact in size for small strings. - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
len (variable)
str (variable)
- -**len (variable)**: A NOW_VARU32 structure containing the string length, excluding the null terminator. - -**str (variable)**: The UTF-8 encoded string excluding the null terminator. - -#### NOW_HEADER - -The NOW_HEADER structure is the header common to all NOW protocol messages. - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class. - -| Flag | Meaning | -|---------------------------------|----------------------| -| NOW_CHANNEL_MSG_CLASS_ID
0x10 | Channel message class. | -| NOW_SYSTEM_MSG_CLASS_ID
0x11 | System message class. | -| NOW_SESSION_MSG_CLASS_ID
0x12 | Session message class. | -| NOW_EXEC_MSG_CLASS_ID
0x13 | Exec message class. | - -**msgType (1 byte)**: The message type, specific to the message class. - -**msgFlags (2 bytes)**: The message flags, specific to the message type and class. - -#### NOW_STATUS -Operation status code. - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
flagskindreserved
code
errorMessage(variable)
- -**flags (2 bytes)**: Status flags. - -| Value | Meaning | -|-------|---------| -| NOW_NOW_STATUS_ERROR
0x0001 | This flag set for all error statuses. If flag is not set, operation was successful. | -| NOW_NOW_STATUS_ERROR_MESSAGE
0x0002 | `errorMessage` contains optional error message. | - -**kind (1 byte)**: The status kind. -When `NOW_STATUS_ERROR` is set, this field represents error kind. - -| Value | Meaning | -|-------|---------| -| NOW_STATUS_ERROR_KIND_GENERIC
0x0000 | `code` value is undefined and could be ignored. | -| NOW_STATUS_ERROR_KIND_NOW
0x0001 | `code` contains NowProto-defined error code. | -| NOW_STATUS_ERROR_KIND_WINAPI
0x0002 | `code` field contains Windows error code. | -| NOW_STATUS_ERROR_KIND_UNIX
0x0003 | `code` field contains Unix error code. | - -For successful operation this field value is operation specific. - -**reserved (1 byte)**: Reserved value. Should be set to 0 and ignored during parsing. - -**code (4 bytes)**: The status code. - -- If `NOW_STATUS_ERROR` flag is NOT set, this value should contain `0` value -- If `NOW_STATUS_ERROR` is set, this value represents error code according to - `NOW_STATUS_ERROR_KIND_*` value. If no error kind flags set, value of this - field is undefined and should be ignored. - - - `NOW_STATUS_ERROR_KIND_NOW` codes: - - | Value | Meaning | - |-------|---------| - | NOW_CODE_IN_USE
0x0001 | Resource (e.g. exec session id is already in use). | - | NOW_CODE_INVALID_REQUEST
0x0002 | Sent request is invalid (e.g. invalid exec request params). | - | NOW_CODE_ABORTED
0x0003 | Operation has been aborted on the server side. | - | NOW_CODE_NOT_FOUND
0x0004 | Resource not found. | - | NOW_CODE_ACCESS_DENIED
0x0005 | Resource can't be accessed. | - | NOW_CODE_INTERNAL
0x0006 | Internal error. | - | NOW_CODE_NOT_IMPLEMENTED
0x0007 | Operation is not implemented on current platform. | - - - `NOW_STATUS_ERROR_KIND_WINAPI`: code contains standard WinAPI error. - - `NOW_STATUS_ERROR_KIND_UNIX`: code contains standard UNIX error code. - -**errorMessage(variable, optional)**: this value contains either optional error message if -`NOW_STATUS_ERROR_MESSAGE` flag is set, or empty sting if the flag is not set. - -### Channel Messages -Channel negotiation and life cycle messages. - -#### NOW_CHANNEL_MSG - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_NEGOTIATION_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type. - -| Value | Meaning | -|---------------------------------|----------------------| -| NOW_CHANNEL_CAPSET_MSG_ID
0x01 | NOW_CHANNEL_CAPSET_MSG | -| NOW_CHANNEL_HEARTBEAT_MSG_ID
0x02 | NOW_CHANNEL_HEARTBEAT_MSG | - -#### NOW_CHANNEL_CAPSET_MSG - -This message is first set by the client side, to advertise capabilities. - -Received client message should be downgraded by the server (remove non-intersecting capabilities) and sent back to the client at the start of DVC channel communications. DVC channel should be closed if protocol -versions are not compatible. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
versionMajorversionMinor
systemCapset
sessionCapset
execCapset
heartbeatInterval
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_CHANNEL_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_CHANNEL_CAPSET_MSG_ID). - -**msgFlags (2 bytes)**: Message flags. - -| Flag | Meaning | -|-------|---------| -| NOW_CHANNEL_SET_HEATBEAT
0x0001 | Set if `heartbeat` specify channel heartbeat interval. | - -**versionMajor (1 byte)**: Major protocol version. Breaking changes in protocol should -increment major version; Protocol implementations with different major version are not compatible. - -**versionMinor (1 byte)**: Minor protocol version. Incremented when new non-breaking feature is added. - -**systemCapset (4 bytes)**: System commands capabilities set. - -| Flag | Meaning | -|-------|---------| -| NOW_CAP_SYSTEM_SHUTDOWN
0x0001 | System shutdown command support. | - -**sessionCapset (4 bytes)**: Session commands capabilities set. - -| Flag | Meaning | -|-------|---------| -| NOW_CAP_SESSION_LOCK
0x00000001 | Lock command support. | -| NOW_CAP_SESSION_LOGOFF
0x00000002 | Logoff command support. | -| NOW_CAP_SESSION_MSGBOX
0x00000004 | Message box command support. | - -**execCapset (4 bytes)**: Remote execution capabilities set. - -| Flag | Meaning | -|-------|---------| -| NOW_CAP_EXEC_STYLE_RUN
0x00000001 | Generic "Run" execution style. | -| NOW_CAP_EXEC_STYLE_PROCESS
0x00000002 | CreateProcess() execution style. | -| NOW_CAP_EXEC_STYLE_SHELL
0x00000004 | System shell (.sh) execution style. | -| NOW_CAP_EXEC_STYLE_BATCH
0x00000008 | Windows batch file (.bat) execution style. | -| NOW_CAP_EXEC_STYLE_WINPS
0x00000010 | Windows PowerShell (.ps1) execution style. | -| NOW_CAP_EXEC_STYLE_PWSH
0x00000020 | PowerShell 7 (.ps1) execution style. | - - - -**heartbeatInterval (4 bytes, optional)**: A 32-bit unsigned integer, which represents -periodic heartbeat interval *hint* for a server (60 seconds by default). -Disables periodic heartbeat if set to `0`. Ignored if `NOW_CHANNEL_SET_HEATBEAT` is not set. - - -#### NOW_CHANNEL_HEARTBEAT_MSG - -Periodic heartbeat message sent by the server. If the client does not receive this message within -the specified interval, it should consider the connection as lost. - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_CHANNEL_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_CHANNEL_HEARTBEAT_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -### System Messages - -#### NOW_SYSTEM_MSG - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_SYSTEM_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type. - -| Value | Meaning | -|---------------------------------|----------------------| -| NOW_SYSTEM_INFO_REQ_ID
0x01 | NOW_SYSTEM_INFO_REQ_MSG | -| NOW_SYSTEM_INFO_RSP_ID
0x02 | NOW_SYSTEM_INFO_RSP_MSG | -| NOW_SYSTEM_SHUTDOWN_ID
0x03 | NOW_SYSTEM_SHUTDOWN_MSG | - - - -#### NOW_SYSTEM_SHUTDOWN_MSG - -The NOW_SYSTEM_SHUTDOWN_MSG structure is used to request a system shutdown.NOW_SESSION_LOGOFF_MSG - - - - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
timeout
message (variable)
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_SYSTEM_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_SYSTEM_SHUTDOWN_MSG_ID) - -**msgFlags (2 bytes)**: The message flags. - -| Flag | Meaning | -|------|---------| -| NOW_SHUTDOWN_FLAG_FORCE
0x0001 | Force shutdown | -| NOW_SHUTDOWN_FLAG_REBOOT
0x0002 | Reboot after shutdown | - -**timeout (4 bytes)**: This system shutdown timeout, in seconds. - -**message (variable)**: A NOW_STRING structure containing an optional shutdown message. - -### Session Messages - -#### NOW_SESSION_MSG - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_SESSION_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type. - -| Value | Meaning | -|---------------------------------|----------------------| -| NOW_SESSION_LOCK_MSG_ID
0x01 | NOW_SESSION_LOCK_MSG | -| NOW_SESSION_LOGOFF_MSG_ID
0x02 | NOW_SESSION_LOGOFF_MSG | -| NOW_SESSION_MESSAGE_BOX_MSG_REQ_ID
0x03 | NOW_SESSION_MESSAGE_BOX_MSG | -| NOW_SESSION_MESSAGE_BOX_RSP_MSG_ID
0x04 | NOW_SESSION_MESSAGE_RSP_MSG | - -**msgFlags (2 bytes)**: The message flags. - -#### NOW_SESSION_LOCK_MSG - -The NOW_SESSION_LOCK_MSG is used to request locking the user session. - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_SESSION_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_SESSION_LOCK_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -#### NOW_SESSION_LOGOFF_MSG - -The NOW_SESSION_LOGOFF_MSG is used to request a user session logoff. - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_SESSION_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_SESSION_LOGOFF_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -#### NOW_SESSION_MSGBOX_REQ_MSG - -The NOW_SESSION_MSGBOX_REQ_MSG is used to show a message box in the user session, similar to what the [WTSSendMessage function](https://learn.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtssendmessagew) does. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
requestId
style
timeout
text (variable)
title (variable)
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_SESSION_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_SESSION_MESSAGE_BOX_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -| Flag | Meaning | -|-------------------------------------|-----------------------------------------| -| NOW_MSGBOX_FLAG_TITLE
0x00000001 | The `title` field is contains a non-default value. | -| NOW_MSGBOX_FLAG_STYLE
0x00000002 | The `style` field contains a non-default value. | -| NOW_MSGBOX_FLAG_TIMEOUT
0x00000004 | The `timeout` field contains a non-default value. | -| NOW_MSGBOX_FLAG_RESPONSE
0x00000008 | A response message is expected (don't fire and forget). | - -**requestId (4 bytes)**: the message request id, sent back in the response. - -**style (4 bytes)**: The message box style, ignored if NOW_MSGBOX_FLAG_STYLE is not set. MBOK is the default, refer to the -[MessageBox function](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox) -for all possible styles. This field may be ignored on platforms other than Windows. - -**timeout (4 bytes)**: The timeout, in seconds, that the message box dialog should wait for the user response. This value is ignored if NOW_MSGBOX_FLAG_TIMEOUT is not set. - -**text (variable)**: The message box text. - -**title (variable)**: The message box title. Ignored if NOW_MSGBOX_FLAG_TITLE is not set. - -#### NOW_SESSION_MSGBOX_RSP_MSG - -The NOW_SESSION_MSGBOX_RSP_MSG is a message sent in response to NOW_SESSION_MSGBOX_REQ_MSG if the NOW_MSGBOX_FLAG_RESPONSE has been set, and contains the result from the message box dialog. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
requestId
response
status (variable)
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_SESSION_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_SESSION_MESSAGE_RSP_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -**requestId (4 bytes)**: Message box request id. - -**response (4 bytes)**: Message response code. If **status** is successful, response value is defined as following: - -| Value | Meaning | -|--------------|---------| -| NOW_MSGBOX_RSP_ABORT
3 | Abort | -| NOW_MSGBOX_RSP_CANCEL
2 | Cancel | -| NOW_MSGBOX_RSP_CONTINUE
11 | Continue | -| NOW_MSGBOX_RSP_IGNORE
5 | Ignore | -| NOW_MSGBOX_RSP_NO
7 | No | -| NOW_MSGBOX_RSP_OK
1 | OK | -| NOW_MSGBOX_RSP_RETRY
4 | Retry | -| NOW_MSGBOX_RSP_TRYAGAIN
10 | Try Again | -| NOW_MSGBOX_RSP_YES
6 | Yes | -| NOW_MSGBOX_RSP_TIMEOUT
32000 | Timeout | - -If `status` specifies error, this field should be set to `0`. - -**status (variable)**: `NOW_STATUS` structure containing message box response status. - -### Execution Messages - -#### NOW_EXEC_MSG - -The NOW_EXEC_MSG message is used to execute remote commands or scripts. - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_EXEC_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type. - -| Value | Meaning | -|-------|---------| -| NOW_EXEC_CAPSET_MSG_ID
0x00 | NOW_EXEC_CAPSET_MSG | -| NOW_EXEC_ABORT_MSG_ID
0x01 | NOW_EXEC_ABORT_MSG | -| NOW_EXEC_CANCEL_REQ_MSG_ID
0x02 | NOW_EXEC_CANCEL_REQ_MSG | -| NOW_EXEC_CANCEL_RSP_MSG_ID
0x03 | NOW_EXEC_CANCEL_RSP_MSG | -| NOW_EXEC_RESULT_MSG_ID
0x04 | NOW_EXEC_RESULT_MSG | -| NOW_EXEC_DATA_MSG_ID
0x05 | NOW_EXEC_DATA_MSG | -| NOW_EXEC_STARTED_MSG_ID
0x06 | NOW_EXEC_STARTED_MSG | -| NOW_EXEC_RUN_MSG_ID
0x10 | NOW_EXEC_RUN_MSG | -| NOW_EXEC_PROCESS_MSG_ID
0x11 | NOW_EXEC_PROCESS_MSG | -| NOW_EXEC_SHELL_MSG_ID
0x12 | NOW_EXEC_SHELL_MSG | -| NOW_EXEC_BATCH_MSG_ID
0x13 | NOW_EXEC_BATCH_MSG | -| NOW_EXEC_WINPS_MSG_ID
0x14 | NOW_EXEC_WINPS_MSG | -| NOW_EXEC_PWSH_MSG_ID
0x15 | NOW_EXEC_PWSH_MSG | - -**msgFlags (2 bytes)**: The message flags. - -#### NOW_EXEC_ABORT_MSG - -The NOW_EXEC_ABORT_MSG message is used to abort a remote execution immediately due to an unrecoverable error. This message can be sent at any time without an explicit response message. The session is considered aborted as soon as this message is sent. - - - - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
sessionId
exitCode
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_EXEC_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_EXEC_ABORT_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -**sessionId (4 bytes)**: A 32-bit unsigned integer containing a unique remote execution session id. - -**exitCode (4 bytes)**: Exit code for application abort (Ignored if not supported by OS). - -#### NOW_EXEC_CANCEL_REQ_MSG - -The NOW_EXEC_CANCEL_REQ_MSG message is used to cancel a remote execution session. - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
sessionId
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_EXEC_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_EXEC_CANCEL_REQ_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -**sessionId (4 bytes)**: A 32-bit unsigned integer containing a unique remote execution session id. - -#### NOW_EXEC_CANCEL_RSP_MSG - -The NOW_EXEC_CANCEL_RSP_MSG message is used to respond to a remote execution cancel request. - - - - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
sessionId
status (variable)
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_EXEC_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_EXEC_CANCEL_RSP_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -**sessionId (4 bytes)**: A 32-bit unsigned integer containing a unique remote execution session id. - -**status (4 bytes)**: `NOW_STATUS` structure containing execution session cancellation request status. - -#### NOW_EXEC_RESULT_MSG - -The NOW_EXEC_RESULT_MSG message is used to return the result of an execution request. The session is considered terminated as soon as this message is sent. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
sessionId
exitCode
status (variable)
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_EXEC_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_EXEC_RESULT_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -**sessionId (4 bytes)**: A 32-bit unsigned integer containing a unique remote execution session id. - -**exitCode (4 bytes)**: Value containing either process exit code or `0` value if -`status` field specifies error. - -**status (variable)**: `NOW_STATUS` structure containing session execution result. - -#### NOW_EXEC_DATA_MSG - -The NOW_EXEC_DATA_MSG message is used to send input/output data as part of a remote execution. - - - - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
sessionId
data (variable)
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_EXEC_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_EXEC_DATA_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -| Flag | Meaning | -|----------------------------------------|---------------------------------| -| NOW_EXEC_FLAG_DATA_FIRST
0x00000001 | This is the first data message. | -| NOW_EXEC_FLAG_DATA_LAST
0x00000002 | This is the last data message, the command completed execution. | -| NOW_EXEC_FLAG_DATA_STDIN
0x00000004 | The data is from the standard input. | -| NOW_EXEC_FLAG_DATA_STDOUT
0x00000008 | The data is from the standard output. | -| NOW_EXEC_FLAG_DATA_STDERR
0x00000010 | The data is from the standard error. | - -Message should contain exactly one of `NOW_EXEC_FLAG_DATA_STDIN`, `NOW_EXEC_FLAG_DATA_STDOUT` or `NOW_EXEC_FLAG_DATA_STDERR` flags set. - -First stream message should always contain `NOW_EXEC_FLAG_DATA_FIRST` -flag. - -`NOW_EXEC_FLAG_DATA_LAST` should indicate EOF for a stream, all consecutive messages for the given stream will be ignored by either party (client or sever). - - -**sessionId (4 bytes)**: A 32-bit unsigned integer containing a unique remote execution session id. - -**data (variable)**: The input/output data represented as `NOW_VARBUF` - -#### NOW_EXEC_STARTED_MSG - -The NOW_EXEC_STARTED_MSG message is sent by the server after the execution session has been successfully -started. - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
sessionId
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_EXEC_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_EXEC_RESULT_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -**sessionId (4 bytes)**: A 32-bit unsigned integer containing a unique remote execution session id. - -#### NOW_EXEC_RUN_MSG - -The NOW_EXEC_RUN_MSG message is used to send a run request. This request type maps to starting a program by using the “Run” menu on operating systems (the Start Menu on Windows, the Dock on macOS etc.). The execution of programs started with NOW_EXEC_RUN_MSG is not followed and does not send back the output. - - - - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
sessionId
command (variable)
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_EXEC_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_EXEC_RUN_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -**sessionId (4 bytes)**: A 32-bit unsigned integer containing a unique remote execution session id. - -**command (variable)**: A NOW_VARSTR structure containing the command to execute. - -#### NOW_EXEC_PROCESS_MSG - -The NOW_EXEC_PROCESS_MSG message is used to send a Windows [CreateProcess()](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw) request. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
sessionId
filename (variable)
parameters (variable)
directory (variable)
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_EXEC_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_EXEC_PROCESS_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -| Flag | Meaning | -|----------------------------------------|---------------------------| -| NOW_EXEC_FLAG_PROCESS_PARAMETERS_SET
0x00000001 | `parameters` field contains non-default value. | -| NOW_EXEC_FLAG_PROCESS_DIRECTORY_SET
0x00000002 | `directory` field contains non-default value. | - - -**sessionId (4 bytes)**: A 32-bit unsigned integer containing a unique remote execution session id. - -**filename (variable)**: A NOW_VARSTR structure containing the file name. Corresponds to the lpApplicationName parameter. - -**parameters (variable)**: A NOW_VARSTR structure containing the command parameters. Corresponds to the lpCommandLine parameter. Ignored if NOW_EXEC_FLAG_PROCESS_PARAMETERS_SET is not set. - -**directory (variable)**: A NOW_VARSTR structure containing the command working directory. Corresponds to the lpCurrentDirectory parameter. Ignored if NOW_EXEC_FLAG_PROCESS_DIRECTORY_SET is not set. - -#### NOW_EXEC_SHELL_MSG - -The NOW_EXEC_SHELL_MSG message is used to execute a remote shell script. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
sessionId
command (variable)
shell (variable)
directory (variable)
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_EXEC_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_EXEC_SHELL_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -| Flag | Meaning | -|----------------------------------------|---------------------------| -| NOW_EXEC_FLAG_SHELL_SHELL_SET
0x00000001 | `shell` field contains non-default value. | -| NOW_EXEC_FLAG_SHELL_DIRECTORY_SET
0x00000002 | `directory` field contains non-default value. | - -**sessionId (4 bytes)**: A 32-bit unsigned integer containing a unique remote execution session id. - -**command (variable)**: A NOW_VARSTR structure containing the script file contents to execute. - -**shell (variable)**: A NOW_VARSTR structure containing the shell to use for execution. If no shell is specified, the default system shell (/bin/sh) will be used. Ignored if NOW_EXEC_FLAG_SHELL_SHELL_SET -is not set. - -**directory (variable)**: A NOW_VARSTR structure containing the command working directory. Ignored if -NOW_EXEC_FLAG_SHELL_DIRECTORY_SET is not set. - -#### NOW_EXEC_BATCH_MSG - -The NOW_EXEC_BATCH_MSG message is used to execute a remote batch script. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
sessionId
command (variable)
directory (variable)
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_EXEC_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_EXEC_BATCH_MSG_ID). - -**msgFlags (2 bytes)**: The message flags. - -| Flag | Meaning | -|----------------------------------------|---------------------------| -| NOW_EXEC_FLAG_BATCH_DIRECTORY_SET
0x00000001 | `directory` field contains non-default value. | - -**sessionId (4 bytes)**: A 32-bit unsigned integer containing a unique remote execution session id. - -**command (variable)**: A NOW_VARSTR structure containing the script file contents to execute. - -**directory (variable)**: A NOW_VARSTR structure containing the command working directory. Ignored -if NOW_EXEC_FLAG_BATCH_DIRECTORY_SET is not set. - -#### NOW_EXEC_WINPS_MSG - -The NOW_EXEC_WINPS_MSG message is used to execute a remote Windows PowerShell (powershell.exe) command. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
sessionId
command (variable)
executionPolicy (variable)
configurationName (variable)
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_EXEC_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_EXEC_WINPS_MSG_ID). - -**msgFlags (2 bytes)**: The message flags, specifying the PowerShell command-line arguments. - -| Flag | Meaning | -|----------------------------------------|---------------------------| -| NOW_EXEC_FLAG_PS_NO_LOGO
0x00000001 | PowerShell -NoLogo option | -| NOW_EXEC_FLAG_PS_NO_EXIT
0x00000002 | PowerShell -NoExit option | -| NOW_EXEC_FLAG_PS_STA
0x00000004 | PowerShell -Sta option | -| NOW_EXEC_FLAG_PS_MTA
0x00000008 | PowerShell -Mta option | -| NOW_EXEC_FLAG_PS_NO_PROFILE
0x00000010 | PowerShell -NoProfile option | -| NOW_EXEC_FLAG_PS_NON_INTERACTIVE
0x00000020 | PowerShell -NonInteractive option | -| NOW_EXEC_FLAG_PS_EXECUTION_POLICY
0x00000040 | `executionPolicy` field contains non-default value and specifies the PowerShell -ExecutionPolicy parameter | -| NOW_EXEC_FLAG_PS_CONFIGURATION_NAME
0x00000080 | `configurationName` field contains non-default value and specifies the PowerShell -ConfigurationName parameter | - -**sessionId (4 bytes)**: A 32-bit unsigned integer containing a unique remote execution session id. - -**command (variable)**: A NOW_VARSTR structure containing the command to execute. - -**executionPolicy (variable)**: A NOW_VARSTR structure containing the execution policy (-ExecutionPolicy) parameter value. Ignored if NOW_EXEC_FLAG_PS_EXECUTION_POLICY is not set. - -**configurationName (variable)**: A NOW_VARSTR structure containing the configuration name (-ConfigurationName) parameter value. Ignored if NOW_EXEC_FLAG_PS_CONFIGURATION_NAME is not set. - -#### NOW_EXEC_PWSH_MSG - -The NOW_EXEC_PWSH_MSG message is used to execute a remote PowerShell 7 (pwsh) command. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
01234567891012345678920123456789301
msgSize
msgClassmsgTypemsgFlags
sessionId
command (variable)
executionPolicy (variable)
configurationName (variable)
- -**msgSize (4 bytes)**: The message size, excluding the header size (8 bytes). - -**msgClass (1 byte)**: The message class (NOW_EXEC_MSG_CLASS_ID). - -**msgType (1 byte)**: The message type (NOW_EXEC_PWSH_MSG_ID). - -**msgFlags (2 bytes)**: The message flags, specifying the PowerShell command-line arguments, same as with NOW_EXEC_WINPS_MSG. - -**sessionId (4 bytes)**: A 32-bit unsigned integer containing a unique remote execution session id. - -**command (variable)**: A NOW_VARSTR structure containing the command to execute. - -**executionPolicy (variable)**: A NOW_VARSTR structure, same as with NOW_EXEC_WINPS_MSG. - -**configurationName (variable)**: A NOW_VARSTR structure, same as with NOW_EXEC_WINPS_MSG. - -### Version History -- 1.0 - - Initial protocol version diff --git a/docs/make.ps1 b/docs/make.ps1 deleted file mode 100644 index 9afc19bf..00000000 --- a/docs/make.ps1 +++ /dev/null @@ -1,3 +0,0 @@ -# NOTE: Markdown Monster CLI may be broken at the moment (WinGet version 3.5.4 definitely is), -# the PDF could be generated manually via GUI instead. -mmcli markdowntopdf -i NOW-spec.md -o NOW-spec.pdf --theme Github --orientation Portrait --page-size A4 \ No newline at end of file