Skip to content

Commit 597c941

Browse files
committed
refactor: updated server logic to latest now-proto version, dvc restart, refactoring for optional IO redirection
1 parent 25213d1 commit 597c941

File tree

10 files changed

+462
-210
lines changed

10 files changed

+462
-210
lines changed

Cargo.lock

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/win-api-wrappers/src/process.rs

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use std::{ptr, slice};
77

88
use anyhow::{Context, Result, bail};
99
use windows::Win32::Foundation::{
10-
E_INVALIDARG, ERROR_INCORRECT_SIZE, ERROR_NO_MORE_FILES, FreeLibrary, HANDLE, HMODULE, MAX_PATH, WAIT_EVENT,
11-
WAIT_FAILED,
10+
E_INVALIDARG, ERROR_INCORRECT_SIZE, ERROR_NO_MORE_FILES, FreeLibrary, HANDLE, HMODULE, HWND, LPARAM, MAX_PATH,
11+
WAIT_EVENT, WAIT_FAILED, WPARAM,
1212
};
1313
use windows::Win32::Security::{TOKEN_ACCESS_MASK, TOKEN_ADJUST_PRIVILEGES, TOKEN_QUERY};
1414
use windows::Win32::System::Com::{COINIT_APARTMENTTHREADED, COINIT_DISABLE_OLE1DDE};
@@ -29,8 +29,10 @@ use windows::Win32::System::Threading::{
2929
STARTUPINFOW, STARTUPINFOW_FLAGS, TerminateProcess, WaitForSingleObject,
3030
};
3131
use windows::Win32::UI::Shell::{SEE_MASK_NOCLOSEPROCESS, SHELLEXECUTEINFOW, ShellExecuteExW};
32-
use windows::Win32::UI::WindowsAndMessaging::SHOW_WINDOW_CMD;
33-
use windows::core::PCWSTR;
32+
use windows::Win32::UI::WindowsAndMessaging::{
33+
EnumWindows, GetWindowThreadProcessId, PostThreadMessageW, SHOW_WINDOW_CMD,
34+
};
35+
use windows::core::{BOOL, PCWSTR};
3436

3537
use crate::Error;
3638
use crate::handle::{Handle, HandleWrapper};
@@ -309,6 +311,14 @@ impl Process {
309311
bail!(Error::from_win32(ERROR_INCORRECT_SIZE))
310312
}
311313
}
314+
315+
/// Terminates the process and provides the exit code.
316+
pub fn terminate(&self, exit_code: u32) -> Result<()> {
317+
// SAFETY: FFI call with no outstanding preconditions.
318+
unsafe { TerminateProcess(self.handle.raw(), exit_code) }?;
319+
320+
Ok(())
321+
}
312322
}
313323

314324
impl HandleWrapper for Process {
@@ -897,3 +907,64 @@ fn process_id_to_session(pid: u32) -> Result<u32> {
897907
unsafe { ProcessIdToSessionId(pid, &mut session_id as *mut _) }?;
898908
Ok(session_id)
899909
}
910+
911+
struct EnumWindowsContext {
912+
expected_pid: u32,
913+
threads: Vec<u32>,
914+
}
915+
916+
extern "system" fn windows_enum_func(hwnd: HWND, lparam: LPARAM) -> BOOL {
917+
// SAFETY: lparam.0 set to valid EnumWindowsContext memory by caller (Windows itself).
918+
let enum_ctx = unsafe { &mut *(lparam.0 as *mut EnumWindowsContext) };
919+
920+
let mut pid: u32 = 0;
921+
// SAFETY: pid points to valid memory.
922+
unsafe {
923+
GetWindowThreadProcessId(hwnd, Some(&mut pid as *mut _));
924+
}
925+
if pid == 0 || pid != enum_ctx.expected_pid {
926+
// Continue enumeration.
927+
return true.into();
928+
}
929+
930+
// SAFETY: FFI call with no outstanding preconditions.
931+
let thread_id = unsafe { GetWindowThreadProcessId(hwnd, None) };
932+
if thread_id == 0 {
933+
// Continue enumeration.
934+
return true.into();
935+
}
936+
937+
enum_ctx.threads.push(thread_id);
938+
939+
true.into()
940+
}
941+
942+
/// Posts message with given WPARAM and LPARAM values to the specific
943+
/// appication with provided `pid`.
944+
pub fn post_message_for_pid(pid: u32, msg: u32, wparam: WPARAM, lparam: LPARAM) -> Result<()> {
945+
let mut windows_enum_ctx = EnumWindowsContext {
946+
expected_pid: pid,
947+
threads: Default::default(),
948+
};
949+
950+
// SAFETY: EnumWindows is safe to call with valid callback function
951+
// and context. Lifetime of windows_enum_ctx is guaranteed to be valid
952+
// until EnumWindows returns.
953+
unsafe {
954+
// Enumerate all windows associated with the process.
955+
EnumWindows(
956+
Some(windows_enum_func),
957+
LPARAM(&mut windows_enum_ctx as *mut EnumWindowsContext as isize),
958+
)
959+
}?;
960+
961+
// Send message to all threads.
962+
if !windows_enum_ctx.threads.is_empty() {
963+
for thread in windows_enum_ctx.threads {
964+
// SAFETY: No outstanding preconditions.
965+
let _ = unsafe { PostThreadMessageW(thread, msg, wparam, lparam) };
966+
}
967+
}
968+
969+
Ok(())
970+
}

devolutions-session/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ win-api-wrappers = { path = "../crates/win-api-wrappers", optional = true }
4444

4545
[dependencies.now-proto-pdu]
4646
optional = true
47-
version = "0.2"
47+
path = "C:/core/devolutions/now-proto/rust/now-proto-pdu"
48+
#version = "0.2"
4849
features = ["std"]
4950

5051
[target.'cfg(windows)'.build-dependencies]

devolutions-session/build.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,23 @@ mod win {
88
use std::{env, fs};
99

1010
pub(super) fn embed_version_rc() {
11-
let out_dir = env::var("OUT_DIR").expect("BUG: failed to get OUT_DIR");
12-
let version_rc_file = format!("{}/version.rc", out_dir);
11+
let out_dir = env::var("OUT_DIR").expect("failed to get OUT_DIR");
12+
let version_rc_file = format!("{out_dir}/version.rc");
1313
let version_rc_data = generate_version_rc();
14-
fs::write(&version_rc_file, version_rc_data).expect("BUG: failed to write version.rc");
14+
fs::write(&version_rc_file, version_rc_data).expect("failed to write version.rc");
1515

1616
embed_resource::compile(&version_rc_file, embed_resource::NONE)
1717
.manifest_required()
18-
.unwrap();
18+
.expect("failed to compile version.rc");
1919
}
2020

2121
fn generate_version_rc() -> String {
2222
let output_name = "DevolutionsSession";
23-
let filename = format!("{}.exe", output_name);
23+
let filename = format!("{output_name}.exe");
2424
let company_name = "Devolutions Inc.";
25-
let legal_copyright = format!("Copyright 2020-2024 {}", company_name);
25+
let legal_copyright = format!("Copyright 2020-2024 {company_name}");
2626

27-
let mut version_number = env::var("CARGO_PKG_VERSION").expect("BUG: failed to get CARGO_PKG_VERSION");
27+
let mut version_number = env::var("CARGO_PKG_VERSION").expect("failed to get CARGO_PKG_VERSION");
2828
version_number.push_str(".0");
2929
let version_commas = version_number.replace('.', ",");
3030
let file_description = output_name;

devolutions-session/src/dvc/channel.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ impl<T: Send + Sync + Debug + 'static> WinapiSignaledSender<T> {
2525
Ok(())
2626
}
2727

28+
pub fn try_send(&self, message: T) -> anyhow::Result<()> {
29+
self.tx.try_send(message)?;
30+
31+
self.semaphore.release(1)?;
32+
Ok(())
33+
}
34+
2835
pub fn blocking_send(&self, message: T) -> anyhow::Result<()> {
2936
self.tx.blocking_send(message)?;
3037

devolutions-session/src/dvc/fs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ impl TmpFileGuard {
88
// Create empty temporary file and release the handle.
99
let (_file, path) = tempfile::Builder::new()
1010
.prefix("devolutions-")
11-
.suffix(&format!(".{}", extension))
11+
.suffix(&format!(".{extension}"))
1212
.tempfile()?
1313
.keep()?;
1414

devolutions-session/src/dvc/io.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use anyhow::Context;
12
use tokio::sync::mpsc::Sender;
23
use tokio::sync::mpsc::error::TrySendError;
34
use windows::Win32::Foundation::{ERROR_IO_PENDING, GetLastError, WAIT_EVENT, WAIT_OBJECT_0};
@@ -86,22 +87,28 @@ pub fn run_dvc_io(
8687

8788
let messages = message_dissector
8889
.dissect(&pdu_chunk_buffer[HEADER_SIZE..HEADER_SIZE + chunk_data_size])
89-
.expect("BUG: Failed to dissect messages");
90+
.context("Failed to dissect DVC messages");
91+
92+
let messages = match messages {
93+
Ok(messages) => messages,
94+
Err(err) => {
95+
error!(?err, "Failed to dissect DVC messages");
96+
return Err(err);
97+
}
98+
};
9099

91100
// Send all messages over the channel.
92101
for message in messages {
93102
debug!(?message, "DVC message received");
94103
// We do non-blocking send to avoid blocking the IO thread. Processing
95104
// task is expected to be fast enough to keep up with the incoming messages.
96105
match read_tx.try_send(message) {
97-
Ok(_) => {
98-
trace!("Received DVC message is sent to the processing channel");
99-
}
106+
Ok(_) => {}
100107
Err(TrySendError::Full(_)) => {
101-
trace!("DVC message is dropped due to busy channel");
108+
error!("DVC message was dropped due to channel overflow");
102109
}
103110
Err(e) => {
104-
trace!("DVC message is dropped due to closed channel");
111+
error!("DVC message was dropped due to closed channel");
105112
return Err(e.into());
106113
}
107114
}

devolutions-session/src/dvc/now_message_dissector.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use anyhow::Context;
12
use now_proto_pdu::NowMessage;
23
use now_proto_pdu::ironrdp_core::{Decode, DecodeError, DecodeErrorKind, IntoOwned, ReadCursor, WriteBuf};
34

@@ -19,7 +20,8 @@ impl NowMessageDissector {
1920
.pdu_body_buffer
2021
.filled_len()
2122
.checked_sub(self.start_pos)
22-
.expect("start_pos is greater than filled_len");
23+
.context("Failed to get usable chunk size")?;
24+
2325
let mut cursor = ReadCursor::new(&self.pdu_body_buffer.filled()[self.start_pos..]);
2426

2527
match NowMessage::decode(&mut cursor) {

0 commit comments

Comments
 (0)