From 1acac6834428923bda4c489037377ec07fdc1eb7 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 23 Oct 2025 04:27:18 -0700 Subject: [PATCH 01/27] windows grpc communication over named pipes --- src-tauri/Cargo.lock | 24 ++++++++++ src-tauri/Cargo.toml | 4 ++ src-tauri/src/lib.rs | 2 + src-tauri/src/named_pipe.rs | 80 ++++++++++++++++++++++++++++++++++ src-tauri/src/service/mod.rs | 10 +++-- src-tauri/src/service/utils.rs | 27 +++++++++++- 6 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 src-tauri/src/named_pipe.rs diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 95bd80ca..34892095 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -437,6 +437,28 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.107", +] + [[package]] name = "async-task" version = "4.7.1" @@ -1371,6 +1393,7 @@ name = "defguard-client" version = "1.6.0" dependencies = [ "anyhow", + "async-stream", "base64 0.22.1", "chrono", "clap", @@ -1378,6 +1401,7 @@ dependencies = [ "dark-light", "defguard_wireguard_rs", "dirs-next", + "futures-core", "hyper-util", "log", "nix", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index e2d361c6..25cd63bc 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -110,6 +110,10 @@ x25519-dalek = { version = "2", features = [ "serde", "static_secrets", ] } +async-stream = "0.3.6" +futures-core = "0.3.31" +tower = "0.5" +hyper-util = "0.1" [target.'cfg(unix)'.dependencies] hyper-util = "0.1" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index e22f4938..c1e1af6c 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -29,6 +29,8 @@ pub mod service; pub mod tray; pub mod utils; pub mod wg_config; +#[cfg(windows)] +pub mod named_pipe; pub mod proto { use crate::database::models::{ diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs new file mode 100644 index 00000000..5053d126 --- /dev/null +++ b/src-tauri/src/named_pipe.rs @@ -0,0 +1,80 @@ +use async_stream::stream; +use futures_core::stream::Stream; +use std::pin::Pin; +use tokio::{ + io::{self, AsyncRead, AsyncWrite}, + net::windows::named_pipe::{NamedPipeServer, ServerOptions}, +}; +use tonic::transport::server::Connected; + +pub static PIPE_NAME: &str = r"\\.\pipe\defguard_daemon"; + +pub struct TonicNamedPipeServer { + inner: NamedPipeServer, +} + +impl TonicNamedPipeServer { + pub fn new(inner: NamedPipeServer) -> Self { + Self { inner } + } +} + +impl Connected for TonicNamedPipeServer { + type ConnectInfo = (); + + fn connect_info(&self) -> Self::ConnectInfo { + () + } +} + +impl AsyncRead for TonicNamedPipeServer { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_read(cx, buf) + } +} + +impl AsyncWrite for TonicNamedPipeServer { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_write(cx, buf) + } + + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_flush(cx) + } + + fn poll_shutdown( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_shutdown(cx) + } +} + +pub fn get_named_pipe_server_stream() -> impl Stream> { + stream! { + let mut server = ServerOptions::new() + .first_pipe_instance(true) + .create(PIPE_NAME)?; + + loop { + server.connect().await?; + + let client = TonicNamedPipeServer::new(server); + + yield Ok(client); + + server = ServerOptions::new().create(PIPE_NAME)?; + } + } +} \ No newline at end of file diff --git a/src-tauri/src/service/mod.rs b/src-tauri/src/service/mod.rs index 25c41f1d..b3aa3d34 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -414,18 +414,22 @@ pub async fn run_server(config: Config) -> anyhow::Result<()> { #[cfg(windows)] pub async fn run_server(config: Config) -> anyhow::Result<()> { + use crate::named_pipe::get_named_pipe_server_stream; + debug!("Starting Defguard interface management daemon"); - let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), DAEMON_HTTP_PORT); + // let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), DAEMON_HTTP_PORT); + let stream = get_named_pipe_server_stream(); let daemon_service = DaemonService::new(&config); - info!("Defguard daemon version {VERSION} started, listening on {addr}",); + // info!("Defguard daemon version {VERSION} started, listening on {addr}",); debug!("Defguard daemon configuration: {config:?}"); Server::builder() .trace_fn(|_| tracing::info_span!("defguard_service")) .add_service(DesktopDaemonServiceServer::new(daemon_service)) - .serve(addr) + // .serve(addr) + .serve_with_incoming(stream) .await?; Ok(()) diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index 2572f2ad..963041be 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -50,7 +50,32 @@ pub(crate) static DAEMON_CLIENT: LazyLock> = }; #[cfg(windows)] { - channel = endpoint.connect_lazy(); + use hyper_util::rt::TokioIo; + use std::time::Duration; + use tokio::net::windows::named_pipe::ClientOptions; + use tower::service_fn; + use winapi::shared::winerror::ERROR_PIPE_BUSY; + use crate::named_pipe::PIPE_NAME; + + // channel = endpoint.connect_lazy(); + info!("DBG: Connecting pipe"); + channel = endpoint + .connect_with_connector_lazy(service_fn(|_| async { + info!("DBG: lazy connection"); + + let client = loop { + info!("DBG: LOOP"); + match ClientOptions::new().open(PIPE_NAME) { + Ok(client) => break client, + Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (), + Err(e) => return Err(e), + } + + tokio::time::sleep(Duration::from_millis(50)).await; + }; + + Ok::<_, std::io::Error>(TokioIo::new(client)) + })); } DesktopDaemonServiceClient::new(channel) }); From 5779c641f0c181e22bf1bccf2c2de4ce18672444 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Fri, 24 Oct 2025 02:48:19 -0700 Subject: [PATCH 02/27] acl --- src-tauri/Cargo.lock | 1 + src-tauri/Cargo.toml | 17 +++++- src-tauri/src/named_pipe.rs | 107 ++++++++++++++++++++++++++++++++---- 3 files changed, 114 insertions(+), 11 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 34892095..973baafb 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1444,6 +1444,7 @@ dependencies = [ "vergen-git2", "webbrowser", "winapi", + "windows 0.61.3", "windows-service", "x25519-dalek", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 25cd63bc..fe40eaa8 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -122,8 +122,23 @@ tokio-stream = "0.1" tower = "0.5" [target.'cfg(windows)'.dependencies] -winapi = { version = "0.3", features = ["winsvc", "winerror"] } +winapi = { version = "0.3", features = ["winsvc", "winerror", "namedpipeapi"] } windows-service = "0.7" +windows = { version = "0.61.3", features = [ + # Core Win32 types + "Win32_Foundation", + + # Security descriptors, ACLs, SDDL + "Win32_Security", + "Win32_Security_Authorization", + + # Named pipes (CreateNamedPipeW) + "Win32_System_Pipes", + + # HANDLE & file functions + "Win32_System_IO", + "Win32_System_Threading", +] } [features] # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index 5053d126..e9dd8f7b 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -1,6 +1,7 @@ use async_stream::stream; use futures_core::stream::Stream; -use std::pin::Pin; +use winapi::um::{minwinbase::SECURITY_ATTRIBUTES, namedpipeapi::CreateNamedPipeW, winbase::{FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_ACCESS_DUPLEX}, winnt::LPCWSTR}; +use std::{os::windows::io::AsRawHandle, pin::Pin}; use tokio::{ io::{self, AsyncRead, AsyncWrite}, net::windows::named_pipe::{NamedPipeServer, ServerOptions}, @@ -61,20 +62,106 @@ impl AsyncWrite for TonicNamedPipeServer { } } +use windows::{core::{Param, PCWSTR}, Win32::{Foundation::INVALID_HANDLE_VALUE, Security::{ + InitializeSecurityDescriptor, SetSecurityDescriptorDacl, ACL, PSECURITY_DESCRIPTOR, SECURITY_DESCRIPTOR +}}}; +use windows::Win32::Security::Authorization::{ + ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1 +}; +// use windows::Win32::System::Pipes::CreateNamedPipeW; +use windows::Win32::System::Pipes::{ + // PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, + PIPE_TYPE_BYTE, + PIPE_READMODE_BYTE, PIPE_WAIT, PIPE_UNLIMITED_INSTANCES +}; +// use windows::Win32::System::IO::FILE_FLAG_FIRST_PIPE_INSTANCE; +use windows::core::PWSTR; + +static PIPE_NAME_W: &str = r"\\.\pipe\defguard_daemon\0"; + +/// Converts an str to wide (u16), null-terminated +fn str_to_wide_null_terminated(s: &str) -> Vec { + s.encode_utf16().chain(Some(0)).collect() +} + +fn create_secure_pipe() -> winapi::um::winnt::HANDLE { + // ✅ SDDL: Grant RW access to group "defguard", no access to others + // - O: = Owner (not set -> default) + // - G: = Group (set to "defguard") + // - D: = DACL: + // - (A;;0x12019f;;;SY) → SYSTEM full access + // - (A;;0x12019f;;;BA) → Builtin administrators full access + // - (A;;0x12019f;;;defguard) → "defguard" group RW access + let security_descriptor= PCWSTR::from_raw( + str_to_wide_null_terminated("D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;defguard)").as_mut_ptr() + ); + + let mut sd_ptr = PSECURITY_DESCRIPTOR::default(); + unsafe { + ConvertStringSecurityDescriptorToSecurityDescriptorW( + security_descriptor, + SDDL_REVISION_1, + &mut sd_ptr, + // std::ptr::null_mut(), + None, + ) + .expect("Failed to create security descriptor"); + let pipe_name = LPCWSTR::from(str_to_wide_null_terminated(PIPE_NAME_W).as_ptr()); + let handle: winapi::um::winnt::HANDLE = CreateNamedPipeW( + pipe_name, + PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_BYTE.0 | PIPE_READMODE_BYTE.0 | PIPE_WAIT.0, + PIPE_UNLIMITED_INSTANCES, + 65536, + 65536, + 0, + sd_ptr.0 as *mut SECURITY_ATTRIBUTES, + ); + + // TODO: handle invalid handle + // if handle.is_invalid() { + // panic!("Failed to create secure named pipe. Access denied / invalid group?"); + // } + + // if handle == INVALID_HANDLE_VALUE.0 || handle.is_null() { + // panic!("Failed to create secure named pipe. Access denied / invalid group?"); + // } + + handle + } +} + +use std::os::windows::io::{FromRawHandle, OwnedHandle}; + +fn create_tokio_secure_pipe() -> NamedPipeServer { + let raw = create_secure_pipe(); + let owned = unsafe { OwnedHandle::from_raw_handle(raw as _) }; + unsafe {NamedPipeServer::from_raw_handle(owned.as_raw_handle()).unwrap()} +} + pub fn get_named_pipe_server_stream() -> impl Stream> { - stream! { - let mut server = ServerOptions::new() - .first_pipe_instance(true) - .create(PIPE_NAME)?; + // stream! { + // let mut server = ServerOptions::new() + // .first_pipe_instance(true) + // .create(PIPE_NAME)?; - loop { - server.connect().await?; + // loop { + // server.connect().await?; + + // let client = TonicNamedPipeServer::new(server); - let client = TonicNamedPipeServer::new(server); + // yield Ok(client); - yield Ok(client); + // server = ServerOptions::new().create(PIPE_NAME)?; + // } + // } + stream! { + let mut server = create_tokio_secure_pipe(); - server = ServerOptions::new().create(PIPE_NAME)?; + loop { + server.connect().await?; + yield Ok(TonicNamedPipeServer::new(server)); + server = create_tokio_secure_pipe(); } } } \ No newline at end of file From 312e02969c0e60659c9c863372741c30ba04a294 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Fri, 24 Oct 2025 07:44:42 -0700 Subject: [PATCH 03/27] wip --- src-tauri/Cargo.lock | 1 - src-tauri/Cargo.toml | 32 ++-- src-tauri/src/named_pipe.rs | 316 +++++++++++++++++++++++++------ src-tauri/src/service/windows.rs | 91 ++++----- 4 files changed, 323 insertions(+), 117 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 973baafb..34892095 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1444,7 +1444,6 @@ dependencies = [ "vergen-git2", "webbrowser", "winapi", - "windows 0.61.3", "windows-service", "x25519-dalek", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index fe40eaa8..7895f39e 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -122,23 +122,23 @@ tokio-stream = "0.1" tower = "0.5" [target.'cfg(windows)'.dependencies] -winapi = { version = "0.3", features = ["winsvc", "winerror", "namedpipeapi"] } +winapi = { version = "0.3", features = ["winsvc", "winerror", "namedpipeapi", "sddl", "handleapi", "winnt", "errhandlingapi"] } windows-service = "0.7" -windows = { version = "0.61.3", features = [ - # Core Win32 types - "Win32_Foundation", - - # Security descriptors, ACLs, SDDL - "Win32_Security", - "Win32_Security_Authorization", - - # Named pipes (CreateNamedPipeW) - "Win32_System_Pipes", - - # HANDLE & file functions - "Win32_System_IO", - "Win32_System_Threading", -] } +# windows = { version = "0.61.3", features = [ +# # Core Win32 types +# "Win32_Foundation", +# +# # Security descriptors, ACLs, SDDL +# "Win32_Security", +# "Win32_Security_Authorization", +# +# # Named pipes (CreateNamedPipeW) +# "Win32_System_Pipes", +# +# # HANDLE & file functions +# "Win32_System_IO", +# "Win32_System_Threading", +# ] } [features] # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index e9dd8f7b..315b57f0 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -1,7 +1,10 @@ use async_stream::stream; use futures_core::stream::Stream; -use winapi::um::{minwinbase::SECURITY_ATTRIBUTES, namedpipeapi::CreateNamedPipeW, winbase::{FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_ACCESS_DUPLEX}, winnt::LPCWSTR}; -use std::{os::windows::io::AsRawHandle, pin::Pin}; +use winapi::{shared::{sddl::{ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1}, winerror::ERROR_INSUFFICIENT_BUFFER}, + um::{ + errhandlingapi::GetLastError, handleapi::INVALID_HANDLE_VALUE, minwinbase::SECURITY_ATTRIBUTES, namedpipeapi::CreateNamedPipeW, winbase::{LocalFree, LookupAccountNameW, FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, PIPE_WAIT}, winnt::{LPCWSTR, PCWSTR, PSECURITY_DESCRIPTOR, PSID} + }}; +use std::{os::windows::io::{AsRawHandle, RawHandle}, pin::Pin}; use tokio::{ io::{self, AsyncRead, AsyncWrite}, net::windows::named_pipe::{NamedPipeServer, ServerOptions}, @@ -62,20 +65,20 @@ impl AsyncWrite for TonicNamedPipeServer { } } -use windows::{core::{Param, PCWSTR}, Win32::{Foundation::INVALID_HANDLE_VALUE, Security::{ - InitializeSecurityDescriptor, SetSecurityDescriptorDacl, ACL, PSECURITY_DESCRIPTOR, SECURITY_DESCRIPTOR -}}}; -use windows::Win32::Security::Authorization::{ - ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1 -}; -// use windows::Win32::System::Pipes::CreateNamedPipeW; -use windows::Win32::System::Pipes::{ - // PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, - PIPE_TYPE_BYTE, - PIPE_READMODE_BYTE, PIPE_WAIT, PIPE_UNLIMITED_INSTANCES -}; -// use windows::Win32::System::IO::FILE_FLAG_FIRST_PIPE_INSTANCE; -use windows::core::PWSTR; +// use windows::{core::{Param, PCWSTR}, Win32::{Foundation::INVALID_HANDLE_VALUE, Security::{ +// InitializeSecurityDescriptor, SetSecurityDescriptorDacl, ACL, PSECURITY_DESCRIPTOR, SECURITY_DESCRIPTOR +// }}}; +// use windows::Win32::Security::Authorization::{ +// ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1 +// }; +// // use windows::Win32::System::Pipes::CreateNamedPipeW; +// use windows::Win32::System::Pipes::{ +// // PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, +// PIPE_TYPE_BYTE, +// PIPE_READMODE_BYTE, PIPE_WAIT, PIPE_UNLIMITED_INSTANCES +// }; +// // use windows::Win32::System::IO::FILE_FLAG_FIRST_PIPE_INSTANCE; +// use windows::core::PWSTR; static PIPE_NAME_W: &str = r"\\.\pipe\defguard_daemon\0"; @@ -84,59 +87,262 @@ fn str_to_wide_null_terminated(s: &str) -> Vec { s.encode_utf16().chain(Some(0)).collect() } -fn create_secure_pipe() -> winapi::um::winnt::HANDLE { - // ✅ SDDL: Grant RW access to group "defguard", no access to others - // - O: = Owner (not set -> default) - // - G: = Group (set to "defguard") - // - D: = DACL: - // - (A;;0x12019f;;;SY) → SYSTEM full access - // - (A;;0x12019f;;;BA) → Builtin administrators full access - // - (A;;0x12019f;;;defguard) → "defguard" group RW access - let security_descriptor= PCWSTR::from_raw( - str_to_wide_null_terminated("D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;defguard)").as_mut_ptr() - ); - - let mut sd_ptr = PSECURITY_DESCRIPTOR::default(); +// fn create_secure_pipe() -> winapi::um::winnt::HANDLE { +// // ✅ SDDL: Grant RW access to group "defguard", no access to others +// // - O: = Owner (not set -> default) +// // - G: = Group (set to "defguard") +// // - D: = DACL: +// // - (A;;0x12019f;;;SY) → SYSTEM full access +// // - (A;;0x12019f;;;BA) → Builtin administrators full access +// // - (A;;0x12019f;;;defguard) → "defguard" group RW access +// let security_descriptor= PCWSTR::from( +// str_to_wide_null_terminated("D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;defguard)").as_mut_ptr() +// ); + +// // let mut sd_ptr = PSECURITY_DESCRIPTOR::default(); +// let mut sd_ptr = PSECURITY_DESCRIPTOR::default(); +// let mut sds_ptr = 0u32; +// unsafe { +// ConvertStringSecurityDescriptorToSecurityDescriptorW( +// security_descriptor, +// SDDL_REVISION_1 as u32, +// &mut sd_ptr, +// // std::ptr::null_mut(), +// &mut sds_ptr, +// ); +// // .expect("Failed to create security descriptor"); +// let pipe_name = LPCWSTR::from(str_to_wide_null_terminated(PIPE_NAME_W).as_ptr()); +// let handle: winapi::um::winnt::HANDLE = CreateNamedPipeW( +// pipe_name, +// PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, +// PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, +// PIPE_UNLIMITED_INSTANCES, +// 65536, +// 65536, +// 0, +// // sd_ptr.0 as *mut SECURITY_ATTRIBUTES, +// sd_ptr as *mut SECURITY_ATTRIBUTES, +// ); + +// // TODO: handle invalid handle +// // if handle.is_invalid() { +// // panic!("Failed to create secure named pipe. Access denied / invalid group?"); +// // } + +// if handle == INVALID_HANDLE_VALUE || handle.is_null() { +// panic!("Failed to create secure named pipe. Access denied / invalid group?"); +// } +// info!("Handle: {:?}", handle); + +// handle +// } +// } + +// fn create_secure_pipe() -> winapi::um::winnt::HANDLE { +// unsafe { +// // SDDL string - allow SYSTEM, Administrators, and group "defguard" +// // let sddl = to_wide("D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;defguard)"); +// let sddl = str_to_wide_null_terminated("D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;defguard)"); + +// let mut sd: PSECURITY_DESCRIPTOR = std::ptr::null_mut(); +// if ConvertStringSecurityDescriptorToSecurityDescriptorW( +// sddl.as_ptr(), +// SDDL_REVISION_1 as u32, +// &mut sd, +// std::ptr::null_mut(), +// ) == 0 { +// panic!("Failed to convert SDDL: {}", std::io::Error::last_os_error()); +// } + +// // Build SECURITY_ATTRIBUTES properly +// let mut sa = SECURITY_ATTRIBUTES { +// nLength: std::mem::size_of::() as u32, +// lpSecurityDescriptor: sd as *mut _, +// bInheritHandle: 0, +// }; + +// // let name = to_wide(r"\\.\pipe\defguard_daemon"); +// let name = str_to_wide_null_terminated(r"\\.\pipe\defguard_daemon"); + +// let handle = CreateNamedPipeW( +// name.as_ptr(), +// PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, +// PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, +// PIPE_UNLIMITED_INSTANCES, +// 65536, +// 65536, +// 0, +// &mut sa, +// ); + +// if handle == INVALID_HANDLE_VALUE || handle.is_null() { +// panic!( +// "CreateNamedPipeW failed: {}", +// std::io::Error::last_os_error() +// ); +// } + +// handle +// } +// } + +// fn to_wide(s: &str) -> Vec { +// std::ffi::OsStr::new(s) +// .encode_wide() +// .chain(std::iter::once(0)) +// .collect() +// } + +/// Resolve account name (e.g. "defguard") -> SID string like "S-1-5-21-...". +fn account_name_to_sid_string(account: &str) -> Result { unsafe { - ConvertStringSecurityDescriptorToSecurityDescriptorW( - security_descriptor, - SDDL_REVISION_1, - &mut sd_ptr, - // std::ptr::null_mut(), - None, - ) - .expect("Failed to create security descriptor"); - let pipe_name = LPCWSTR::from(str_to_wide_null_terminated(PIPE_NAME_W).as_ptr()); - let handle: winapi::um::winnt::HANDLE = CreateNamedPipeW( - pipe_name, - PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, - PIPE_TYPE_BYTE.0 | PIPE_READMODE_BYTE.0 | PIPE_WAIT.0, - PIPE_UNLIMITED_INSTANCES, + let name_w = str_to_wide_null_terminated(account); + // First call to get buffer sizes + let mut sid_size: u32 = 0; + let mut domain_size: u32 = 0; + let mut pe_use = 0u32; + + let ok = LookupAccountNameW( + std::ptr::null(), // local system + name_w.as_ptr(), + std::ptr::null_mut(), // PSID buffer + &mut sid_size, + std::ptr::null_mut(), // domain buffer + &mut domain_size, + &mut pe_use, + ); + + if ok != 0 { + // Shouldn't succeed with null buffers, but handle defensively + } else { + // TODO handle error != ERROR_INSUFFICIENT_BUFFER + // panic!("ok == 0"); + let err = GetLastError(); + if err != ERROR_INSUFFICIENT_BUFFER { + error!("Other error"); + return Err(std::io::Error::from_raw_os_error(err as i32)); + } + } + + // allocate buffers + let mut sid_buf: Vec = vec![0u8; sid_size as usize]; + let mut domain_buf: Vec = vec![0u16; domain_size as usize]; + + let ok2 = LookupAccountNameW( + std::ptr::null(), + name_w.as_ptr(), + sid_buf.as_mut_ptr() as PSID, + &mut sid_size, + domain_buf.as_mut_ptr(), + &mut domain_size, + &mut pe_use, + ); + + if ok2 == 0 { + return Err(std::io::Error::last_os_error()); + } + + // Convert SID to string + let mut sid_string_ptr: *mut u16 = std::ptr::null_mut(); + if ConvertSidToStringSidW( + sid_buf.as_mut_ptr() as PSID, + &mut sid_string_ptr as *mut *mut u16, + ) == 0 + { + return Err(std::io::Error::last_os_error()); + } + + // sid_string_ptr is a LPWSTR allocated by LocalAlloc/LocalFree. Convert to Rust String. + let mut len = 0usize; + while *sid_string_ptr.add(len) != 0 { + len += 1; + } + let slice = std::slice::from_raw_parts(sid_string_ptr, len); + let sid_string = String::from_utf16_lossy(slice); + + // free returned string + LocalFree(sid_string_ptr as *mut _); + + Ok(sid_string) + } +} + +fn create_secure_pipe() -> Result { + unsafe { + // Resolve group name "defguard" to SID string + let group = "defguard"; + let sid_str = account_name_to_sid_string(group) + .map_err(|e| std::io::Error::new(e.kind(), format!("Failed resolve group SID: {}", e)))?; + + // Compose SDDL: SYSTEM & Administrators full, and group RW. + let sddl = format!("D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;{})", sid_str); + let sddl_w = str_to_wide_null_terminated(&sddl); + + let mut p_sd: PSECURITY_DESCRIPTOR = std::ptr::null_mut(); + + if ConvertStringSecurityDescriptorToSecurityDescriptorW( + sddl_w.as_ptr(), + SDDL_REVISION_1 as u32, + &mut p_sd as *mut PSECURITY_DESCRIPTOR as *mut *mut _, + std::ptr::null_mut(), + ) == 0 + { + return Err(std::io::Error::last_os_error()); + } + + // Build SECURITY_ATTRIBUTES pointing to the security descriptor + let mut sa = SECURITY_ATTRIBUTES { + nLength: std::mem::size_of::() as u32, + lpSecurityDescriptor: p_sd as *mut _, + bInheritHandle: 0, + }; + + let name_w = str_to_wide_null_terminated(r"\\.\pipe\defguard_daemon"); + + let handle = CreateNamedPipeW( + name_w.as_ptr(), + // PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_ACCESS_DUPLEX, + // PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_TYPE_BYTE, + 1, 65536, 65536, 0, - sd_ptr.0 as *mut SECURITY_ATTRIBUTES, + // &mut sa, + std::ptr::null_mut(), ); - // TODO: handle invalid handle - // if handle.is_invalid() { - // panic!("Failed to create secure named pipe. Access denied / invalid group?"); - // } + // Free the security descriptor memory returned by ConvertStringSecurityDescriptorToSecurityDescriptorW + LocalFree(p_sd as *mut _); - // if handle == INVALID_HANDLE_VALUE.0 || handle.is_null() { - // panic!("Failed to create secure named pipe. Access denied / invalid group?"); - // } + if handle == INVALID_HANDLE_VALUE || handle.is_null() { + return Err(std::io::Error::last_os_error()); + } - handle + Ok(handle) } } use std::os::windows::io::{FromRawHandle, OwnedHandle}; fn create_tokio_secure_pipe() -> NamedPipeServer { - let raw = create_secure_pipe(); - let owned = unsafe { OwnedHandle::from_raw_handle(raw as _) }; - unsafe {NamedPipeServer::from_raw_handle(owned.as_raw_handle()).unwrap()} + // let raw = create_secure_pipe(); + // let owned = unsafe { OwnedHandle::from_raw_handle(raw as _) }; + // unsafe {NamedPipeServer::from_raw_handle(owned.as_raw_handle()).unwrap()} + let raw = create_secure_pipe().unwrap(); + info!("Raw handle: {raw:?}"); + unsafe { + let result = NamedPipeServer::from_raw_handle(raw as RawHandle); + match result { + Ok(server) => server, + Err(err) => { + let error = GetLastError(); + error!("Windows error: {error:}"); + panic!("Other error: {err:?}"); + } + } + } } pub fn get_named_pipe_server_stream() -> impl Stream> { diff --git a/src-tauri/src/service/windows.rs b/src-tauri/src/service/windows.rs index aa79ab5a..be27058a 100644 --- a/src-tauri/src/service/windows.rs +++ b/src-tauri/src/service/windows.rs @@ -21,56 +21,57 @@ const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS; pub fn run() -> Result<()> { // Register generated `ffi_service_main` with the system and start the service, blocking // this thread until the service is stopped. - service_dispatcher::start(SERVICE_NAME, ffi_service_main) + // service_dispatcher::start(SERVICE_NAME, ffi_service_main) + Ok(run_service()?) } -define_windows_service!(ffi_service_main, service_main); +// define_windows_service!(ffi_service_main, service_main); -pub fn service_main(_arguments: Vec) { - if let Err(err) = run_service() { - error!("Error while running the service. {err}"); - panic!("{err}"); - } -} +// pub fn service_main(_arguments: Vec) { +// if let Err(err) = run_service() { +// error!("Error while running the service. {err}"); +// panic!("{err}"); +// } +// } fn run_service() -> Result<()> { - // Create a channel to be able to poll a stop event from the service worker loop. + // // Create a channel to be able to poll a stop event from the service worker loop. let (shutdown_tx, shutdown_rx) = mpsc::channel::(); let shutdown_tx_server = shutdown_tx.clone(); - // Define system service event handler that will be receiving service events. - let event_handler = move |control_event| -> ServiceControlHandlerResult { - match control_event { - // Notifies a service to report its current status information to the service - // control manager. Always return NoError even if not implemented. - ServiceControl::Interrogate => ServiceControlHandlerResult::NoError, - - // Handle stop - ServiceControl::Stop => { - let _ = shutdown_tx.send(1); - ServiceControlHandlerResult::NoError - } + // // Define system service event handler that will be receiving service events. + // let event_handler = move |control_event| -> ServiceControlHandlerResult { + // match control_event { + // // Notifies a service to report its current status information to the service + // // control manager. Always return NoError even if not implemented. + // ServiceControl::Interrogate => ServiceControlHandlerResult::NoError, - _ => ServiceControlHandlerResult::NotImplemented, - } - }; + // // Handle stop + // ServiceControl::Stop => { + // let _ = shutdown_tx.send(1); + // ServiceControlHandlerResult::NoError + // } + + // _ => ServiceControlHandlerResult::NotImplemented, + // } + // }; - // Register system service event handler. - // The returned status handle should be used to report service status changes to the system. - let status_handle = register(SERVICE_NAME, event_handler)?; + // // Register system service event handler. + // // The returned status handle should be used to report service status changes to the system. + // let status_handle = register(SERVICE_NAME, event_handler)?; let rt = Runtime::new(); if let Ok(runtime) = rt { - status_handle.set_service_status(ServiceStatus { - service_type: SERVICE_TYPE, - current_state: ServiceState::Running, - controls_accepted: ServiceControlAccept::STOP, - exit_code: ServiceExitCode::Win32(0), - checkpoint: 0, - wait_hint: Duration::default(), - process_id: None, - })?; + // status_handle.set_service_status(ServiceStatus { + // service_type: SERVICE_TYPE, + // current_state: ServiceState::Running, + // controls_accepted: ServiceControlAccept::STOP, + // exit_code: ServiceExitCode::Win32(0), + // checkpoint: 0, + // wait_hint: Duration::default(), + // process_id: None, + // })?; let config: Config = Config::parse(); let _guard = logging_setup(&config.log_dir, &config.log_level); @@ -104,15 +105,15 @@ fn run_service() -> Result<()> { }; } - status_handle.set_service_status(ServiceStatus { - service_type: SERVICE_TYPE, - current_state: ServiceState::Stopped, - controls_accepted: ServiceControlAccept::empty(), - exit_code: ServiceExitCode::Win32(0), - checkpoint: 0, - wait_hint: Duration::default(), - process_id: None, - })?; + // status_handle.set_service_status(ServiceStatus { + // service_type: SERVICE_TYPE, + // current_state: ServiceState::Stopped, + // controls_accepted: ServiceControlAccept::empty(), + // exit_code: ServiceExitCode::Win32(0), + // checkpoint: 0, + // wait_hint: Duration::default(), + // process_id: None, + // })?; } Ok(()) From 4f043634b02ff4a99a1dff79e888a40053b31c67 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 27 Oct 2025 09:07:46 -0700 Subject: [PATCH 04/27] wip debugging, rename variables --- src-tauri/src/named_pipe.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index 315b57f0..c9cba5f0 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -249,6 +249,7 @@ fn account_name_to_sid_string(account: &str) -> Result { &mut sid_string_ptr as *mut *mut u16, ) == 0 { + error!("ConvertSidToStringSidW"); return Err(std::io::Error::last_os_error()); } @@ -276,31 +277,33 @@ fn create_secure_pipe() -> Result { // Compose SDDL: SYSTEM & Administrators full, and group RW. let sddl = format!("D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;{})", sid_str); - let sddl_w = str_to_wide_null_terminated(&sddl); + let sddl_wide = str_to_wide_null_terminated(&sddl); - let mut p_sd: PSECURITY_DESCRIPTOR = std::ptr::null_mut(); + let mut descriptor: PSECURITY_DESCRIPTOR = std::ptr::null_mut(); + warn!("DESCRIPTOR BEFORE: {descriptor:?}"); if ConvertStringSecurityDescriptorToSecurityDescriptorW( - sddl_w.as_ptr(), + sddl_wide.as_ptr(), SDDL_REVISION_1 as u32, - &mut p_sd as *mut PSECURITY_DESCRIPTOR as *mut *mut _, + &mut descriptor as *mut PSECURITY_DESCRIPTOR as *mut *mut _, std::ptr::null_mut(), ) == 0 { return Err(std::io::Error::last_os_error()); } + warn!("DESCRIPTOR AFTER: {descriptor:?}"); // Build SECURITY_ATTRIBUTES pointing to the security descriptor - let mut sa = SECURITY_ATTRIBUTES { + let mut attributes = SECURITY_ATTRIBUTES { nLength: std::mem::size_of::() as u32, - lpSecurityDescriptor: p_sd as *mut _, + lpSecurityDescriptor: descriptor as *mut _, bInheritHandle: 0, }; - let name_w = str_to_wide_null_terminated(r"\\.\pipe\defguard_daemon"); + let name_wide = str_to_wide_null_terminated(r"\\.\pipe\defguard_daemon"); let handle = CreateNamedPipeW( - name_w.as_ptr(), + name_wide.as_ptr(), // PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_ACCESS_DUPLEX, // PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, @@ -309,12 +312,12 @@ fn create_secure_pipe() -> Result { 65536, 65536, 0, - // &mut sa, - std::ptr::null_mut(), + &mut attributes, + // std::ptr::null_mut(), ); // Free the security descriptor memory returned by ConvertStringSecurityDescriptorToSecurityDescriptorW - LocalFree(p_sd as *mut _); + LocalFree(descriptor as *mut _); if handle == INVALID_HANDLE_VALUE || handle.is_null() { return Err(std::io::Error::last_os_error()); From c5e10737a66599d4df7d4e996bbed9e72ec28af7 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 27 Oct 2025 10:10:15 -0700 Subject: [PATCH 05/27] use windows-sys instead of winapi crate --- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 18 +++++++++++++++++- src-tauri/src/named_pipe.rs | 32 +++++++++++++++++++++++--------- src-tauri/src/service/utils.rs | 3 ++- src-tauri/src/utils.rs | 4 ++-- 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 34892095..dbb72585 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1443,8 +1443,8 @@ dependencies = [ "tracing-subscriber", "vergen-git2", "webbrowser", - "winapi", "windows-service", + "windows-sys 0.61.2", "x25519-dalek", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 7895f39e..84c90691 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -114,6 +114,7 @@ async-stream = "0.3.6" futures-core = "0.3.31" tower = "0.5" hyper-util = "0.1" +windows-sys = "0.61.2" [target.'cfg(unix)'.dependencies] hyper-util = "0.1" @@ -122,8 +123,23 @@ tokio-stream = "0.1" tower = "0.5" [target.'cfg(windows)'.dependencies] -winapi = { version = "0.3", features = ["winsvc", "winerror", "namedpipeapi", "sddl", "handleapi", "winnt", "errhandlingapi"] } +# winapi = { version = "0.3", features = ["winsvc", "winerror", "namedpipeapi", "sddl", "handleapi", "winnt", "errhandlingapi"] } windows-service = "0.7" +windows-sys = { version = "0.61.2", features = [ + # Core Win32 types + "Win32_Foundation", + + # Security descriptors, ACLs, SDDL + "Win32_Security", + "Win32_Security_Authorization", + + # Named pipes (CreateNamedPipeW) + "Win32_System_Pipes", + + # HANDLE & file functions + "Win32_System_IO", + "Win32_System_Threading", +] } # windows = { version = "0.61.3", features = [ # # Core Win32 types # "Win32_Foundation", diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index c9cba5f0..7cce50ed 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -1,9 +1,22 @@ use async_stream::stream; use futures_core::stream::Stream; -use winapi::{shared::{sddl::{ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1}, winerror::ERROR_INSUFFICIENT_BUFFER}, - um::{ - errhandlingapi::GetLastError, handleapi::INVALID_HANDLE_VALUE, minwinbase::SECURITY_ATTRIBUTES, namedpipeapi::CreateNamedPipeW, winbase::{LocalFree, LookupAccountNameW, FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, PIPE_WAIT}, winnt::{LPCWSTR, PCWSTR, PSECURITY_DESCRIPTOR, PSID} - }}; +use windows_sys::Win32::{ + Foundation::{ + GetLastError, LocalFree, ERROR_INSUFFICIENT_BUFFER, HANDLE, INVALID_HANDLE_VALUE + }, + Security::{ + PSID, + Authorization::{ + ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1, + }, + LookupAccountNameW, PSECURITY_DESCRIPTOR, SECURITY_ATTRIBUTES + }, + Storage::FileSystem::PIPE_ACCESS_DUPLEX, System::Pipes::{CreateNamedPipeW, PIPE_TYPE_BYTE} +}; +// use winapi::{shared::{sddl::{ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1}, winerror::ERROR_INSUFFICIENT_BUFFER}, +// um::{ +// errhandlingapi::GetLastError, handleapi::INVALID_HANDLE_VALUE, minwinbase::SECURITY_ATTRIBUTES, namedpipeapi::CreateNamedPipeW, winbase::{LocalFree, LookupAccountNameW, FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, PIPE_WAIT}, winnt::{LPCWSTR, PCWSTR, PSECURITY_DESCRIPTOR, PSID} +// }}; use std::{os::windows::io::{AsRawHandle, RawHandle}, pin::Pin}; use tokio::{ io::{self, AsyncRead, AsyncWrite}, @@ -196,15 +209,16 @@ fn str_to_wide_null_terminated(s: &str) -> Vec { /// Resolve account name (e.g. "defguard") -> SID string like "S-1-5-21-...". fn account_name_to_sid_string(account: &str) -> Result { unsafe { - let name_w = str_to_wide_null_terminated(account); + let name_wide = str_to_wide_null_terminated(account); // First call to get buffer sizes let mut sid_size: u32 = 0; let mut domain_size: u32 = 0; - let mut pe_use = 0u32; + let mut pe_use = 0i32; + // let mut pe_use: *mut u32 = std::ptr::null_mut(); let ok = LookupAccountNameW( std::ptr::null(), // local system - name_w.as_ptr(), + name_wide.as_ptr(), std::ptr::null_mut(), // PSID buffer &mut sid_size, std::ptr::null_mut(), // domain buffer @@ -230,7 +244,7 @@ fn account_name_to_sid_string(account: &str) -> Result { let ok2 = LookupAccountNameW( std::ptr::null(), - name_w.as_ptr(), + name_wide.as_ptr(), sid_buf.as_mut_ptr() as PSID, &mut sid_size, domain_buf.as_mut_ptr(), @@ -268,7 +282,7 @@ fn account_name_to_sid_string(account: &str) -> Result { } } -fn create_secure_pipe() -> Result { +fn create_secure_pipe() -> Result { unsafe { // Resolve group name "defguard" to SID string let group = "defguard"; diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index 963041be..ed569580 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -54,7 +54,6 @@ pub(crate) static DAEMON_CLIENT: LazyLock> = use std::time::Duration; use tokio::net::windows::named_pipe::ClientOptions; use tower::service_fn; - use winapi::shared::winerror::ERROR_PIPE_BUSY; use crate::named_pipe::PIPE_NAME; // channel = endpoint.connect_lazy(); @@ -64,6 +63,8 @@ pub(crate) static DAEMON_CLIENT: LazyLock> = info!("DBG: lazy connection"); let client = loop { + use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY; + info!("DBG: LOOP"); match ClientOptions::new().open(PIPE_NAME) { Ok(client) => break client, diff --git a/src-tauri/src/utils.rs b/src-tauri/src/utils.rs index 987c1358..f86e73aa 100644 --- a/src-tauri/src/utils.rs +++ b/src-tauri/src/utils.rs @@ -7,8 +7,6 @@ use tauri::{AppHandle, Emitter, Manager}; use tonic::Code; use tracing::Level; #[cfg(target_os = "windows")] -use winapi::shared::winerror::ERROR_SERVICE_DOES_NOT_EXIST; -#[cfg(target_os = "windows")] use windows_service::{ service::{ServiceAccess, ServiceState}, service_manager::{ServiceManager, ServiceManagerAccess}, @@ -863,6 +861,8 @@ async fn check_connection( connection_type: ConnectionType, app_handle: &AppHandle, ) -> Result<(), Error> { + use windows_sys::Win32::Foundation::ERROR_SERVICE_DOES_NOT_EXIST; + let appstate = app_handle.state::(); let interface_name = get_interface_name(name); let service_name = format!("WireGuardTunnel${interface_name}"); From 28377e9853b54d97dbea3ae443b6e9f0300a62fd Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 27 Oct 2025 10:27:07 -0700 Subject: [PATCH 06/27] pipe created, fails on communication --- src-tauri/src/named_pipe.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index 7cce50ed..7bfe80ea 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -5,13 +5,11 @@ use windows_sys::Win32::{ GetLastError, LocalFree, ERROR_INSUFFICIENT_BUFFER, HANDLE, INVALID_HANDLE_VALUE }, Security::{ - PSID, Authorization::{ ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1, - }, - LookupAccountNameW, PSECURITY_DESCRIPTOR, SECURITY_ATTRIBUTES + }, LookupAccountNameW, PSECURITY_DESCRIPTOR, PSID, SECURITY_ATTRIBUTES }, - Storage::FileSystem::PIPE_ACCESS_DUPLEX, System::Pipes::{CreateNamedPipeW, PIPE_TYPE_BYTE} + Storage::FileSystem::{FILE_FLAG_OVERLAPPED, PIPE_ACCESS_DUPLEX}, System::Pipes::{CreateNamedPipeW, PIPE_TYPE_BYTE} }; // use winapi::{shared::{sddl::{ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1}, winerror::ERROR_INSUFFICIENT_BUFFER}, // um::{ @@ -319,7 +317,8 @@ fn create_secure_pipe() -> Result { let handle = CreateNamedPipeW( name_wide.as_ptr(), // PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, - PIPE_ACCESS_DUPLEX, + // PIPE_ACCESS_DUPLEX, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_TYPE_BYTE, 1, From abc0f2886f56c6c18c648aaa974db7f23dce9543 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 27 Oct 2025 02:55:19 -0700 Subject: [PATCH 07/27] fixed "pipe busy" error --- src-tauri/src/named_pipe.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index 7bfe80ea..50511471 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -9,7 +9,7 @@ use windows_sys::Win32::{ ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1, }, LookupAccountNameW, PSECURITY_DESCRIPTOR, PSID, SECURITY_ATTRIBUTES }, - Storage::FileSystem::{FILE_FLAG_OVERLAPPED, PIPE_ACCESS_DUPLEX}, System::Pipes::{CreateNamedPipeW, PIPE_TYPE_BYTE} + Storage::FileSystem::{FILE_FLAG_OVERLAPPED, PIPE_ACCESS_DUPLEX}, System::Pipes::{CreateNamedPipeW, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, PIPE_WAIT} }; // use winapi::{shared::{sddl::{ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1}, winerror::ERROR_INSUFFICIENT_BUFFER}, // um::{ @@ -320,8 +320,8 @@ fn create_secure_pipe() -> Result { // PIPE_ACCESS_DUPLEX, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, - PIPE_TYPE_BYTE, - 1, + PIPE_TYPE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, From 87bcea87030f76c1cd41a813acc5fd3e52b76a1c Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 27 Oct 2025 03:07:55 -0700 Subject: [PATCH 08/27] cleanup --- src-tauri/src/named_pipe.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index 50511471..6db15437 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -9,7 +9,7 @@ use windows_sys::Win32::{ ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1, }, LookupAccountNameW, PSECURITY_DESCRIPTOR, PSID, SECURITY_ATTRIBUTES }, - Storage::FileSystem::{FILE_FLAG_OVERLAPPED, PIPE_ACCESS_DUPLEX}, System::Pipes::{CreateNamedPipeW, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, PIPE_WAIT} + Storage::FileSystem::{FILE_FLAG_FIRST_PIPE_INSTANCE, FILE_FLAG_OVERLAPPED, PIPE_ACCESS_DUPLEX}, System::Pipes::{CreateNamedPipeW, PIPE_TYPE_BYTE, PIPE_WAIT} }; // use winapi::{shared::{sddl::{ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1}, winerror::ERROR_INSUFFICIENT_BUFFER}, // um::{ @@ -316,17 +316,13 @@ fn create_secure_pipe() -> Result { let handle = CreateNamedPipeW( name_wide.as_ptr(), - // PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, - // PIPE_ACCESS_DUPLEX, - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, - // PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, - PIPE_TYPE_BYTE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, + PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_BYTE, + 2, 65536, 65536, 0, &mut attributes, - // std::ptr::null_mut(), ); // Free the security descriptor memory returned by ConvertStringSecurityDescriptorToSecurityDescriptorW From 59fcaff1f548dc12a4a38fbc3e2ae1aa0c9168a8 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 27 Oct 2025 04:59:17 -0700 Subject: [PATCH 09/27] add pipe flag --- src-tauri/src/named_pipe.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index 6db15437..90019962 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -316,7 +316,7 @@ fn create_secure_pipe() -> Result { let handle = CreateNamedPipeW( name_wide.as_ptr(), - PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE, 2, 65536, From 37d58576c9581762b8d5415dfd624b73a633619d Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 28 Oct 2025 09:26:32 -0700 Subject: [PATCH 10/27] cargo fmt --- src-tauri/src/lib.rs | 4 ++-- src-tauri/src/named_pipe.rs | 31 ++++++++++++++++++++----------- src-tauri/src/service/mod.rs | 3 ++- src-tauri/src/service/utils.rs | 7 +++---- 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index ebe26bac..c29a5d78 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -24,13 +24,13 @@ pub mod enterprise; pub mod error; pub mod events; pub mod log_watcher; +#[cfg(windows)] +pub mod named_pipe; pub mod periodic; pub mod service; pub mod tray; pub mod utils; pub mod wg_config; -#[cfg(windows)] -pub mod named_pipe; pub mod proto { use crate::database::models::{ diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index 90019962..ffb3e335 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -2,20 +2,28 @@ use async_stream::stream; use futures_core::stream::Stream; use windows_sys::Win32::{ Foundation::{ - GetLastError, LocalFree, ERROR_INSUFFICIENT_BUFFER, HANDLE, INVALID_HANDLE_VALUE + GetLastError, LocalFree, ERROR_INSUFFICIENT_BUFFER, HANDLE, INVALID_HANDLE_VALUE, }, Security::{ Authorization::{ - ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1, - }, LookupAccountNameW, PSECURITY_DESCRIPTOR, PSID, SECURITY_ATTRIBUTES + ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, + SDDL_REVISION_1, + }, + LookupAccountNameW, PSECURITY_DESCRIPTOR, PSID, SECURITY_ATTRIBUTES, }, - Storage::FileSystem::{FILE_FLAG_FIRST_PIPE_INSTANCE, FILE_FLAG_OVERLAPPED, PIPE_ACCESS_DUPLEX}, System::Pipes::{CreateNamedPipeW, PIPE_TYPE_BYTE, PIPE_WAIT} + Storage::FileSystem::{ + FILE_FLAG_FIRST_PIPE_INSTANCE, FILE_FLAG_OVERLAPPED, PIPE_ACCESS_DUPLEX, + }, + System::Pipes::{CreateNamedPipeW, PIPE_TYPE_BYTE, PIPE_WAIT}, }; // use winapi::{shared::{sddl::{ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1}, winerror::ERROR_INSUFFICIENT_BUFFER}, // um::{ // errhandlingapi::GetLastError, handleapi::INVALID_HANDLE_VALUE, minwinbase::SECURITY_ATTRIBUTES, namedpipeapi::CreateNamedPipeW, winbase::{LocalFree, LookupAccountNameW, FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, PIPE_WAIT}, winnt::{LPCWSTR, PCWSTR, PSECURITY_DESCRIPTOR, PSID} // }}; -use std::{os::windows::io::{AsRawHandle, RawHandle}, pin::Pin}; +use std::{ + os::windows::io::{AsRawHandle, RawHandle}, + pin::Pin, +}; use tokio::{ io::{self, AsyncRead, AsyncWrite}, net::windows::named_pipe::{NamedPipeServer, ServerOptions}, @@ -215,11 +223,11 @@ fn account_name_to_sid_string(account: &str) -> Result { // let mut pe_use: *mut u32 = std::ptr::null_mut(); let ok = LookupAccountNameW( - std::ptr::null(), // local system + std::ptr::null(), // local system name_wide.as_ptr(), - std::ptr::null_mut(), // PSID buffer + std::ptr::null_mut(), // PSID buffer &mut sid_size, - std::ptr::null_mut(), // domain buffer + std::ptr::null_mut(), // domain buffer &mut domain_size, &mut pe_use, ); @@ -284,8 +292,9 @@ fn create_secure_pipe() -> Result { unsafe { // Resolve group name "defguard" to SID string let group = "defguard"; - let sid_str = account_name_to_sid_string(group) - .map_err(|e| std::io::Error::new(e.kind(), format!("Failed resolve group SID: {}", e)))?; + let sid_str = account_name_to_sid_string(group).map_err(|e| { + std::io::Error::new(e.kind(), format!("Failed resolve group SID: {}", e)) + })?; // Compose SDDL: SYSTEM & Administrators full, and group RW. let sddl = format!("D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;{})", sid_str); @@ -382,4 +391,4 @@ pub fn get_named_pipe_server_stream() -> impl Stream anyhow::Result<()> { #[cfg(windows)] pub async fn run_server( config: Config, - service_location_manager: Arc>) -> anyhow::Result<()> { + service_location_manager: Arc>, +) -> anyhow::Result<()> { use crate::named_pipe::get_named_pipe_server_stream; debug!("Starting Defguard interface management daemon"); diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index ed569580..51e01e3d 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -50,17 +50,16 @@ pub(crate) static DAEMON_CLIENT: LazyLock> = }; #[cfg(windows)] { + use crate::named_pipe::PIPE_NAME; use hyper_util::rt::TokioIo; use std::time::Duration; use tokio::net::windows::named_pipe::ClientOptions; use tower::service_fn; - use crate::named_pipe::PIPE_NAME; // channel = endpoint.connect_lazy(); info!("DBG: Connecting pipe"); - channel = endpoint - .connect_with_connector_lazy(service_fn(|_| async { - info!("DBG: lazy connection"); + channel = endpoint.connect_with_connector_lazy(service_fn(|_| async { + info!("DBG: lazy connection"); let client = loop { use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY; From 7566ae787c123a09318ce30f94f536d50a75475e Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 28 Oct 2025 09:29:14 -0700 Subject: [PATCH 11/27] restore visibility --- src-tauri/src/service/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/service/mod.rs b/src-tauri/src/service/mod.rs index 80e2e9bc..31e2d842 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -543,7 +543,7 @@ pub async fn run_server(config: Config) -> anyhow::Result<()> { } #[cfg(windows)] -pub async fn run_server( +pub(crate) async fn run_server( config: Config, service_location_manager: Arc>, ) -> anyhow::Result<()> { @@ -554,6 +554,7 @@ pub async fn run_server( let stream = get_named_pipe_server_stream(); let daemon_service = DaemonService::new(&config, service_location_manager); + info!("Defguard daemon version {VERSION} started"); debug!("Defguard daemon configuration: {config:?}"); Server::builder() From a2eb983474ddbcdbaa0087484ee6de5bd2b08374 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 28 Oct 2025 05:11:01 -0700 Subject: [PATCH 12/27] fix the flags, use built-in users group --- src-tauri/src/named_pipe.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index ffb3e335..d3a62c60 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -290,14 +290,8 @@ fn account_name_to_sid_string(account: &str) -> Result { fn create_secure_pipe() -> Result { unsafe { - // Resolve group name "defguard" to SID string - let group = "defguard"; - let sid_str = account_name_to_sid_string(group).map_err(|e| { - std::io::Error::new(e.kind(), format!("Failed resolve group SID: {}", e)) - })?; - - // Compose SDDL: SYSTEM & Administrators full, and group RW. - let sddl = format!("D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;{})", sid_str); + // Compose SDDL: SYSTEM & Administrators full, users RW. + let sddl = format!("D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;BU)"); let sddl_wide = str_to_wide_null_terminated(&sddl); let mut descriptor: PSECURITY_DESCRIPTOR = std::ptr::null_mut(); @@ -325,7 +319,7 @@ fn create_secure_pipe() -> Result { let handle = CreateNamedPipeW( name_wide.as_ptr(), - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, 2, 65536, From eccf8183961db4a4c64813bb641638964ad05a85 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 28 Oct 2025 05:20:46 -0700 Subject: [PATCH 13/27] cleanup, clippy --- src-tauri/src/named_pipe.rs | 253 ++--------------------------------- src-tauri/src/service/mod.rs | 4 - 2 files changed, 13 insertions(+), 244 deletions(-) diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index d3a62c60..ea3575e9 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -2,31 +2,27 @@ use async_stream::stream; use futures_core::stream::Stream; use windows_sys::Win32::{ Foundation::{ - GetLastError, LocalFree, ERROR_INSUFFICIENT_BUFFER, HANDLE, INVALID_HANDLE_VALUE, + GetLastError, LocalFree, HANDLE, INVALID_HANDLE_VALUE, }, Security::{ Authorization::{ - ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, + ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1, }, - LookupAccountNameW, PSECURITY_DESCRIPTOR, PSID, SECURITY_ATTRIBUTES, + PSECURITY_DESCRIPTOR, SECURITY_ATTRIBUTES, }, Storage::FileSystem::{ - FILE_FLAG_FIRST_PIPE_INSTANCE, FILE_FLAG_OVERLAPPED, PIPE_ACCESS_DUPLEX, + FILE_FLAG_OVERLAPPED, PIPE_ACCESS_DUPLEX, }, - System::Pipes::{CreateNamedPipeW, PIPE_TYPE_BYTE, PIPE_WAIT}, + System::Pipes::{CreateNamedPipeW, PIPE_TYPE_BYTE}, }; -// use winapi::{shared::{sddl::{ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1}, winerror::ERROR_INSUFFICIENT_BUFFER}, -// um::{ -// errhandlingapi::GetLastError, handleapi::INVALID_HANDLE_VALUE, minwinbase::SECURITY_ATTRIBUTES, namedpipeapi::CreateNamedPipeW, winbase::{LocalFree, LookupAccountNameW, FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_ACCESS_DUPLEX, PIPE_READMODE_BYTE, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, PIPE_WAIT}, winnt::{LPCWSTR, PCWSTR, PSECURITY_DESCRIPTOR, PSID} -// }}; use std::{ - os::windows::io::{AsRawHandle, RawHandle}, + os::windows::io::RawHandle, pin::Pin, }; use tokio::{ io::{self, AsyncRead, AsyncWrite}, - net::windows::named_pipe::{NamedPipeServer, ServerOptions}, + net::windows::named_pipe::NamedPipeServer, }; use tonic::transport::server::Connected; @@ -46,7 +42,6 @@ impl Connected for TonicNamedPipeServer { type ConnectInfo = (); fn connect_info(&self) -> Self::ConnectInfo { - () } } @@ -84,232 +79,31 @@ impl AsyncWrite for TonicNamedPipeServer { } } -// use windows::{core::{Param, PCWSTR}, Win32::{Foundation::INVALID_HANDLE_VALUE, Security::{ -// InitializeSecurityDescriptor, SetSecurityDescriptorDacl, ACL, PSECURITY_DESCRIPTOR, SECURITY_DESCRIPTOR -// }}}; -// use windows::Win32::Security::Authorization::{ -// ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1 -// }; -// // use windows::Win32::System::Pipes::CreateNamedPipeW; -// use windows::Win32::System::Pipes::{ -// // PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, -// PIPE_TYPE_BYTE, -// PIPE_READMODE_BYTE, PIPE_WAIT, PIPE_UNLIMITED_INSTANCES -// }; -// // use windows::Win32::System::IO::FILE_FLAG_FIRST_PIPE_INSTANCE; -// use windows::core::PWSTR; - -static PIPE_NAME_W: &str = r"\\.\pipe\defguard_daemon\0"; - /// Converts an str to wide (u16), null-terminated fn str_to_wide_null_terminated(s: &str) -> Vec { s.encode_utf16().chain(Some(0)).collect() } -// fn create_secure_pipe() -> winapi::um::winnt::HANDLE { -// // ✅ SDDL: Grant RW access to group "defguard", no access to others -// // - O: = Owner (not set -> default) -// // - G: = Group (set to "defguard") -// // - D: = DACL: -// // - (A;;0x12019f;;;SY) → SYSTEM full access -// // - (A;;0x12019f;;;BA) → Builtin administrators full access -// // - (A;;0x12019f;;;defguard) → "defguard" group RW access -// let security_descriptor= PCWSTR::from( -// str_to_wide_null_terminated("D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;defguard)").as_mut_ptr() -// ); - -// // let mut sd_ptr = PSECURITY_DESCRIPTOR::default(); -// let mut sd_ptr = PSECURITY_DESCRIPTOR::default(); -// let mut sds_ptr = 0u32; -// unsafe { -// ConvertStringSecurityDescriptorToSecurityDescriptorW( -// security_descriptor, -// SDDL_REVISION_1 as u32, -// &mut sd_ptr, -// // std::ptr::null_mut(), -// &mut sds_ptr, -// ); -// // .expect("Failed to create security descriptor"); -// let pipe_name = LPCWSTR::from(str_to_wide_null_terminated(PIPE_NAME_W).as_ptr()); -// let handle: winapi::um::winnt::HANDLE = CreateNamedPipeW( -// pipe_name, -// PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, -// PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, -// PIPE_UNLIMITED_INSTANCES, -// 65536, -// 65536, -// 0, -// // sd_ptr.0 as *mut SECURITY_ATTRIBUTES, -// sd_ptr as *mut SECURITY_ATTRIBUTES, -// ); - -// // TODO: handle invalid handle -// // if handle.is_invalid() { -// // panic!("Failed to create secure named pipe. Access denied / invalid group?"); -// // } - -// if handle == INVALID_HANDLE_VALUE || handle.is_null() { -// panic!("Failed to create secure named pipe. Access denied / invalid group?"); -// } -// info!("Handle: {:?}", handle); - -// handle -// } -// } - -// fn create_secure_pipe() -> winapi::um::winnt::HANDLE { -// unsafe { -// // SDDL string - allow SYSTEM, Administrators, and group "defguard" -// // let sddl = to_wide("D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;defguard)"); -// let sddl = str_to_wide_null_terminated("D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;defguard)"); - -// let mut sd: PSECURITY_DESCRIPTOR = std::ptr::null_mut(); -// if ConvertStringSecurityDescriptorToSecurityDescriptorW( -// sddl.as_ptr(), -// SDDL_REVISION_1 as u32, -// &mut sd, -// std::ptr::null_mut(), -// ) == 0 { -// panic!("Failed to convert SDDL: {}", std::io::Error::last_os_error()); -// } - -// // Build SECURITY_ATTRIBUTES properly -// let mut sa = SECURITY_ATTRIBUTES { -// nLength: std::mem::size_of::() as u32, -// lpSecurityDescriptor: sd as *mut _, -// bInheritHandle: 0, -// }; - -// // let name = to_wide(r"\\.\pipe\defguard_daemon"); -// let name = str_to_wide_null_terminated(r"\\.\pipe\defguard_daemon"); - -// let handle = CreateNamedPipeW( -// name.as_ptr(), -// PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, -// PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, -// PIPE_UNLIMITED_INSTANCES, -// 65536, -// 65536, -// 0, -// &mut sa, -// ); - -// if handle == INVALID_HANDLE_VALUE || handle.is_null() { -// panic!( -// "CreateNamedPipeW failed: {}", -// std::io::Error::last_os_error() -// ); -// } - -// handle -// } -// } - -// fn to_wide(s: &str) -> Vec { -// std::ffi::OsStr::new(s) -// .encode_wide() -// .chain(std::iter::once(0)) -// .collect() -// } - -/// Resolve account name (e.g. "defguard") -> SID string like "S-1-5-21-...". -fn account_name_to_sid_string(account: &str) -> Result { - unsafe { - let name_wide = str_to_wide_null_terminated(account); - // First call to get buffer sizes - let mut sid_size: u32 = 0; - let mut domain_size: u32 = 0; - let mut pe_use = 0i32; - // let mut pe_use: *mut u32 = std::ptr::null_mut(); - - let ok = LookupAccountNameW( - std::ptr::null(), // local system - name_wide.as_ptr(), - std::ptr::null_mut(), // PSID buffer - &mut sid_size, - std::ptr::null_mut(), // domain buffer - &mut domain_size, - &mut pe_use, - ); - - if ok != 0 { - // Shouldn't succeed with null buffers, but handle defensively - } else { - // TODO handle error != ERROR_INSUFFICIENT_BUFFER - // panic!("ok == 0"); - let err = GetLastError(); - if err != ERROR_INSUFFICIENT_BUFFER { - error!("Other error"); - return Err(std::io::Error::from_raw_os_error(err as i32)); - } - } - - // allocate buffers - let mut sid_buf: Vec = vec![0u8; sid_size as usize]; - let mut domain_buf: Vec = vec![0u16; domain_size as usize]; - - let ok2 = LookupAccountNameW( - std::ptr::null(), - name_wide.as_ptr(), - sid_buf.as_mut_ptr() as PSID, - &mut sid_size, - domain_buf.as_mut_ptr(), - &mut domain_size, - &mut pe_use, - ); - - if ok2 == 0 { - return Err(std::io::Error::last_os_error()); - } - - // Convert SID to string - let mut sid_string_ptr: *mut u16 = std::ptr::null_mut(); - if ConvertSidToStringSidW( - sid_buf.as_mut_ptr() as PSID, - &mut sid_string_ptr as *mut *mut u16, - ) == 0 - { - error!("ConvertSidToStringSidW"); - return Err(std::io::Error::last_os_error()); - } - - // sid_string_ptr is a LPWSTR allocated by LocalAlloc/LocalFree. Convert to Rust String. - let mut len = 0usize; - while *sid_string_ptr.add(len) != 0 { - len += 1; - } - let slice = std::slice::from_raw_parts(sid_string_ptr, len); - let sid_string = String::from_utf16_lossy(slice); - - // free returned string - LocalFree(sid_string_ptr as *mut _); - - Ok(sid_string) - } -} - fn create_secure_pipe() -> Result { unsafe { // Compose SDDL: SYSTEM & Administrators full, users RW. - let sddl = format!("D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;BU)"); - let sddl_wide = str_to_wide_null_terminated(&sddl); + let sddl = "D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;BU)"; + let sddl_wide = str_to_wide_null_terminated(sddl); let mut descriptor: PSECURITY_DESCRIPTOR = std::ptr::null_mut(); - warn!("DESCRIPTOR BEFORE: {descriptor:?}"); if ConvertStringSecurityDescriptorToSecurityDescriptorW( sddl_wide.as_ptr(), - SDDL_REVISION_1 as u32, - &mut descriptor as *mut PSECURITY_DESCRIPTOR as *mut *mut _, + SDDL_REVISION_1, + &mut descriptor as *mut PSECURITY_DESCRIPTOR, std::ptr::null_mut(), ) == 0 { return Err(std::io::Error::last_os_error()); } - warn!("DESCRIPTOR AFTER: {descriptor:?}"); // Build SECURITY_ATTRIBUTES pointing to the security descriptor - let mut attributes = SECURITY_ATTRIBUTES { + let attributes = SECURITY_ATTRIBUTES { nLength: std::mem::size_of::() as u32, lpSecurityDescriptor: descriptor as *mut _, bInheritHandle: 0, @@ -325,7 +119,7 @@ fn create_secure_pipe() -> Result { 65536, 65536, 0, - &mut attributes, + &attributes, ); // Free the security descriptor memory returned by ConvertStringSecurityDescriptorToSecurityDescriptorW @@ -339,14 +133,8 @@ fn create_secure_pipe() -> Result { } } -use std::os::windows::io::{FromRawHandle, OwnedHandle}; - fn create_tokio_secure_pipe() -> NamedPipeServer { - // let raw = create_secure_pipe(); - // let owned = unsafe { OwnedHandle::from_raw_handle(raw as _) }; - // unsafe {NamedPipeServer::from_raw_handle(owned.as_raw_handle()).unwrap()} let raw = create_secure_pipe().unwrap(); - info!("Raw handle: {raw:?}"); unsafe { let result = NamedPipeServer::from_raw_handle(raw as RawHandle); match result { @@ -361,21 +149,6 @@ fn create_tokio_secure_pipe() -> NamedPipeServer { } pub fn get_named_pipe_server_stream() -> impl Stream> { - // stream! { - // let mut server = ServerOptions::new() - // .first_pipe_instance(true) - // .create(PIPE_NAME)?; - - // loop { - // server.connect().await?; - - // let client = TonicNamedPipeServer::new(server); - - // yield Ok(client); - - // server = ServerOptions::new().create(PIPE_NAME)?; - // } - // } stream! { let mut server = create_tokio_secure_pipe(); diff --git a/src-tauri/src/service/mod.rs b/src-tauri/src/service/mod.rs index 31e2d842..264ff57d 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -6,8 +6,6 @@ pub mod utils; #[cfg(windows)] pub mod windows; -#[cfg(windows)] -use std::net::{Ipv4Addr, SocketAddr}; use std::{ collections::HashMap, net::IpAddr, @@ -57,8 +55,6 @@ use crate::{ service::proto::{DeleteServiceLocationsRequest, SaveServiceLocationsRequest}, }; -#[cfg(windows)] -const DAEMON_HTTP_PORT: u16 = 54127; pub(super) const DAEMON_BASE_URL: &str = "http://localhost:54127"; #[cfg(unix)] From 44f54a3889ab8535b35572d09f8fcdfbe83d31ec Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 28 Oct 2025 05:23:38 -0700 Subject: [PATCH 14/27] more clippy fixes --- src-tauri/src/enterprise/service_locations/windows.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/enterprise/service_locations/windows.rs b/src-tauri/src/enterprise/service_locations/windows.rs index b8eb8b8c..27d3794b 100644 --- a/src-tauri/src/enterprise/service_locations/windows.rs +++ b/src-tauri/src/enterprise/service_locations/windows.rs @@ -303,7 +303,7 @@ impl ServiceLocationManager { ) -> Result<(), ServiceLocationError> { self.connected_service_locations .entry(instance_id.to_string()) - .or_insert_with(Vec::new) + .or_default() .push(location.clone()); debug!( @@ -407,7 +407,7 @@ impl ServiceLocationManager { if let Some(locations) = self.connected_service_locations.get(instance_id) { // Collect locations to disconnect to avoid borrowing issues - let locations_to_disconnect: Vec<_> = locations.iter().cloned().collect(); + let locations_to_disconnect = locations.to_vec(); for location in locations_to_disconnect { let ifname = get_interface_name(&location.name); @@ -456,7 +456,7 @@ impl ServiceLocationManager { if let Some(locations) = self.connected_service_locations.get_mut(instance_id) { if let Some(pos) = locations .iter() - .position(|loc| &loc.pubkey == location_pubkey) + .position(|loc| loc.pubkey == location_pubkey) { let location = locations.remove(pos); let ifname = get_interface_name(&location.name); From 26180e371461b7f83f3a10c7c29e9ad414c78558 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 28 Oct 2025 05:46:19 -0700 Subject: [PATCH 15/27] fix imports and dependencies --- src-tauri/Cargo.toml | 43 ++++++++++------------------------ src-tauri/src/named_pipe.rs | 3 ++- src-tauri/src/service/mod.rs | 5 ++-- src-tauri/src/service/utils.rs | 25 ++++++++------------ src-tauri/src/utils.rs | 4 ++-- 5 files changed, 28 insertions(+), 52 deletions(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index eeb1440a..9bfbe204 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -59,6 +59,7 @@ common = { path = "common" } dark-light = "2.0" defguard_wireguard_rs = { workspace = true, features = ["check_dependencies"] } dirs-next.workspace = true +hyper-util = "0.1" log = { version = "0.4", features = ["serde"] } prost.workspace = true regex = "1.11" @@ -101,6 +102,7 @@ tokio.workspace = true tokio-util = "0.7" tonic.workspace = true tonic-prost.workspace = true +tower = "0.5" tracing.workspace = true tracing-appender = "0.2" tracing-subscriber.workspace = true @@ -110,22 +112,23 @@ x25519-dalek = { version = "2", features = [ "serde", "static_secrets", ] } -async-stream = "0.3.6" -futures-core = "0.3.31" -tower = "0.5" -hyper-util = "0.1" -windows-sys = "0.61.2" [target.'cfg(unix)'.dependencies] -hyper-util = "0.1" nix = { version = "0.30.1", features = ["user", "fs"] } tokio-stream = "0.1" -tower = "0.5" [target.'cfg(windows)'.dependencies] -# winapi = { version = "0.3", features = ["winsvc", "winerror", "namedpipeapi", "sddl", "handleapi", "winnt", "errhandlingapi"] } +async-stream = "0.3" +futures-core = "0.3" +known-folders = "1.3" +windows = { version = "0.62", features = [ + "Win32", + "Win32_System", + "Win32_System_RemoteDesktop", +] } +windows-acl = "0.3" windows-service = "0.7" -windows-sys = { version = "0.61.2", features = [ +windows-sys = { version = "0.61", features = [ # Core Win32 types "Win32_Foundation", @@ -140,28 +143,6 @@ windows-sys = { version = "0.61.2", features = [ "Win32_System_IO", "Win32_System_Threading", ] } -# windows = { version = "0.61.3", features = [ -# # Core Win32 types -# "Win32_Foundation", -# -# # Security descriptors, ACLs, SDDL -# "Win32_Security", -# "Win32_Security_Authorization", -# -# # Named pipes (CreateNamedPipeW) -# "Win32_System_Pipes", -# -# # HANDLE & file functions -# "Win32_System_IO", -# "Win32_System_Threading", -# ] } -known-folders = "1.3" -windows = { version = "0.62", features = [ - "Win32", - "Win32_System", - "Win32_System_RemoteDesktop", -] } -windows-acl = "0.3" [features] # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index ea3575e9..8c3e4134 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -109,12 +109,13 @@ fn create_secure_pipe() -> Result { bInheritHandle: 0, }; - let name_wide = str_to_wide_null_terminated(r"\\.\pipe\defguard_daemon"); + let name_wide = str_to_wide_null_terminated(PIPE_NAME); let handle = CreateNamedPipeW( name_wide.as_ptr(), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, + // need instances for client and server 2, 65536, 65536, diff --git a/src-tauri/src/service/mod.rs b/src-tauri/src/service/mod.rs index 264ff57d..ccfa8ec2 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -49,7 +49,8 @@ use tracing::{debug, error, info, info_span, Instrument}; use self::config::Config; use super::VERSION; #[cfg(windows)] -use crate::enterprise::service_locations::ServiceLocationManager; +use crate::{named_pipe::get_named_pipe_server_stream, enterprise::service_locations::ServiceLocationManager}; + use crate::{ enterprise::service_locations::ServiceLocationError, service::proto::{DeleteServiceLocationsRequest, SaveServiceLocationsRequest}, @@ -543,8 +544,6 @@ pub(crate) async fn run_server( config: Config, service_location_manager: Arc>, ) -> anyhow::Result<()> { - use crate::named_pipe::get_named_pipe_server_stream; - debug!("Starting Defguard interface management daemon"); let stream = get_named_pipe_server_stream(); diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index 51e01e3d..75716b6a 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -1,6 +1,5 @@ use std::{io::stdout, sync::LazyLock}; -#[cfg(unix)] use hyper_util::rt::TokioIo; #[cfg(unix)] use tokio::net::UnixStream; @@ -15,7 +14,17 @@ use tracing_subscriber::{ fmt, fmt::writer::MakeWriterExt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer, }; +#[cfg(windows)] +use std::time::Duration; +#[cfg(windows)] +use tokio::net::windows::named_pipe::ClientOptions; +#[cfg(windows)] +use tower::service_fn; +#[cfg(windows)] +use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY; +#[cfg(windows)] +use crate::named_pipe::PIPE_NAME; use crate::service::{ proto::desktop_daemon_service_client::DesktopDaemonServiceClient, DAEMON_BASE_URL, }; @@ -50,30 +59,16 @@ pub(crate) static DAEMON_CLIENT: LazyLock> = }; #[cfg(windows)] { - use crate::named_pipe::PIPE_NAME; - use hyper_util::rt::TokioIo; - use std::time::Duration; - use tokio::net::windows::named_pipe::ClientOptions; - use tower::service_fn; - - // channel = endpoint.connect_lazy(); - info!("DBG: Connecting pipe"); channel = endpoint.connect_with_connector_lazy(service_fn(|_| async { - info!("DBG: lazy connection"); let client = loop { - use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY; - - info!("DBG: LOOP"); match ClientOptions::new().open(PIPE_NAME) { Ok(client) => break client, Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (), Err(e) => return Err(e), } - tokio::time::sleep(Duration::from_millis(50)).await; }; - Ok::<_, std::io::Error>(TokioIo::new(client)) })); } diff --git a/src-tauri/src/utils.rs b/src-tauri/src/utils.rs index c1e24224..bc6858ed 100644 --- a/src-tauri/src/utils.rs +++ b/src-tauri/src/utils.rs @@ -11,6 +11,8 @@ use windows_service::{ service::{ServiceAccess, ServiceState}, service_manager::{ServiceManager, ServiceManagerAccess}, }; +#[cfg(windows)] +use windows_sys::Win32::Foundation::ERROR_SERVICE_DOES_NOT_EXIST; #[cfg(windows)] use crate::active_connections::find_connection; @@ -860,8 +862,6 @@ async fn check_connection( connection_type: ConnectionType, app_handle: &AppHandle, ) -> Result<(), Error> { - use windows_sys::Win32::Foundation::ERROR_SERVICE_DOES_NOT_EXIST; - let appstate = app_handle.state::(); let interface_name = get_interface_name(name); let service_name = format!("WireGuardTunnel${interface_name}"); From 00b552d9cac7a3043c9ac59346370c7bcc85d5a0 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 28 Oct 2025 05:48:38 -0700 Subject: [PATCH 16/27] cargo fmt --- src-tauri/src/named_pipe.rs | 31 ++++++++++--------------------- src-tauri/src/service/mod.rs | 4 +++- src-tauri/src/service/utils.rs | 13 ++++++------- 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index 8c3e4134..d6262feb 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -1,30 +1,20 @@ use async_stream::stream; use futures_core::stream::Stream; +use std::{os::windows::io::RawHandle, pin::Pin}; +use tokio::{ + io::{self, AsyncRead, AsyncWrite}, + net::windows::named_pipe::NamedPipeServer, +}; +use tonic::transport::server::Connected; use windows_sys::Win32::{ - Foundation::{ - GetLastError, LocalFree, HANDLE, INVALID_HANDLE_VALUE, - }, + Foundation::{GetLastError, LocalFree, HANDLE, INVALID_HANDLE_VALUE}, Security::{ - Authorization::{ - ConvertStringSecurityDescriptorToSecurityDescriptorW, - SDDL_REVISION_1, - }, + Authorization::{ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1}, PSECURITY_DESCRIPTOR, SECURITY_ATTRIBUTES, }, - Storage::FileSystem::{ - FILE_FLAG_OVERLAPPED, PIPE_ACCESS_DUPLEX, - }, + Storage::FileSystem::{FILE_FLAG_OVERLAPPED, PIPE_ACCESS_DUPLEX}, System::Pipes::{CreateNamedPipeW, PIPE_TYPE_BYTE}, }; -use std::{ - os::windows::io::RawHandle, - pin::Pin, -}; -use tokio::{ - io::{self, AsyncRead, AsyncWrite}, - net::windows::named_pipe::NamedPipeServer, -}; -use tonic::transport::server::Connected; pub static PIPE_NAME: &str = r"\\.\pipe\defguard_daemon"; @@ -41,8 +31,7 @@ impl TonicNamedPipeServer { impl Connected for TonicNamedPipeServer { type ConnectInfo = (); - fn connect_info(&self) -> Self::ConnectInfo { - } + fn connect_info(&self) -> Self::ConnectInfo {} } impl AsyncRead for TonicNamedPipeServer { diff --git a/src-tauri/src/service/mod.rs b/src-tauri/src/service/mod.rs index ccfa8ec2..1747133c 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -49,7 +49,9 @@ use tracing::{debug, error, info, info_span, Instrument}; use self::config::Config; use super::VERSION; #[cfg(windows)] -use crate::{named_pipe::get_named_pipe_server_stream, enterprise::service_locations::ServiceLocationManager}; +use crate::{ + enterprise::service_locations::ServiceLocationManager, named_pipe::get_named_pipe_server_stream, +}; use crate::{ enterprise::service_locations::ServiceLocationError, diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index 75716b6a..0d9de027 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -1,6 +1,10 @@ use std::{io::stdout, sync::LazyLock}; use hyper_util::rt::TokioIo; +#[cfg(windows)] +use std::time::Duration; +#[cfg(windows)] +use tokio::net::windows::named_pipe::ClientOptions; #[cfg(unix)] use tokio::net::UnixStream; use tonic::transport::channel::{Channel, Endpoint}; @@ -8,6 +12,8 @@ use tonic::transport::channel::{Channel, Endpoint}; use tonic::transport::Uri; #[cfg(unix)] use tower::service_fn; +#[cfg(windows)] +use tower::service_fn; use tracing::{debug, Level}; use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::{ @@ -15,12 +21,6 @@ use tracing_subscriber::{ Layer, }; #[cfg(windows)] -use std::time::Duration; -#[cfg(windows)] -use tokio::net::windows::named_pipe::ClientOptions; -#[cfg(windows)] -use tower::service_fn; -#[cfg(windows)] use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY; #[cfg(windows)] @@ -60,7 +60,6 @@ pub(crate) static DAEMON_CLIENT: LazyLock> = #[cfg(windows)] { channel = endpoint.connect_with_connector_lazy(service_fn(|_| async { - let client = loop { match ClientOptions::new().open(PIPE_NAME) { Ok(client) => break client, From 5341f3b88e5a68dc3baff2a528ddde2b67c5d0d5 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 28 Oct 2025 06:07:47 -0700 Subject: [PATCH 17/27] comments and error handling --- src-tauri/src/named_pipe.rs | 53 ++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index d6262feb..3d387d10 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -16,8 +16,10 @@ use windows_sys::Win32::{ System::Pipes::{CreateNamedPipeW, PIPE_TYPE_BYTE}, }; +// Named-pipe name used for IPC between defguard client and windows service. pub static PIPE_NAME: &str = r"\\.\pipe\defguard_daemon"; +/// Tonic-compatible wrapper around a Windows named pipe server handle. pub struct TonicNamedPipeServer { inner: NamedPipeServer, } @@ -45,6 +47,7 @@ impl AsyncRead for TonicNamedPipeServer { } impl AsyncWrite for TonicNamedPipeServer { + /// Delegate async write to the underlying pipe. fn poll_write( mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, @@ -53,6 +56,7 @@ impl AsyncWrite for TonicNamedPipeServer { Pin::new(&mut self.inner).poll_write(cx, buf) } + /// Delegate flush to the underlying pipe. fn poll_flush( mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, @@ -60,6 +64,7 @@ impl AsyncWrite for TonicNamedPipeServer { Pin::new(&mut self.inner).poll_flush(cx) } + /// Delegate shutdown to the underlying pipe. fn poll_shutdown( mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, @@ -68,14 +73,21 @@ impl AsyncWrite for TonicNamedPipeServer { } } -/// Converts an str to wide (u16), null-terminated +/// Convert a Rust `&str` to a null-terminated UTF-16 buffer suitable for Win32 APIs. fn str_to_wide_null_terminated(s: &str) -> Vec { s.encode_utf16().chain(Some(0)).collect() } +/// Create a secure Windows named pipe handle with ACLs: +/// - `SY` (LocalSystem) - full control +/// - `BA` (Administrators) - full control +/// - `BU` (Built-in Users) - read/write +/// +/// Uses `FILE_FLAG_OVERLAPPED` for Tokio compatibility and sets `nMaxInstances = 2` +/// (one client + one service instance). fn create_secure_pipe() -> Result { unsafe { - // Compose SDDL: SYSTEM & Administrators full, users RW. + // Compose SDDL: SYSTEM & Administrators full access, users read-write. let sddl = "D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;BU)"; let sddl_wide = str_to_wide_null_terminated(sddl); @@ -104,7 +116,7 @@ fn create_secure_pipe() -> Result { name_wide.as_ptr(), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, - // need instances for client and server + // 1 client + 1 service 2, 65536, 65536, @@ -112,7 +124,7 @@ fn create_secure_pipe() -> Result { &attributes, ); - // Free the security descriptor memory returned by ConvertStringSecurityDescriptorToSecurityDescriptorW + // Free memory allocated by ConvertStringSecurityDescriptorToSecurityDescriptorW. LocalFree(descriptor as *mut _); if handle == INVALID_HANDLE_VALUE || handle.is_null() { @@ -123,29 +135,26 @@ fn create_secure_pipe() -> Result { } } -fn create_tokio_secure_pipe() -> NamedPipeServer { - let raw = create_secure_pipe().unwrap(); - unsafe { - let result = NamedPipeServer::from_raw_handle(raw as RawHandle); - match result { - Ok(server) => server, - Err(err) => { - let error = GetLastError(); - error!("Windows error: {error:}"); - panic!("Other error: {err:?}"); - } - } - } +/// Wrap a raw pipe `HANDLE` into a Tokio `NamedPipeServer`. +fn create_tokio_secure_pipe() -> Result { + let raw = create_secure_pipe()?; + unsafe { NamedPipeServer::from_raw_handle(raw as RawHandle) } } -pub fn get_named_pipe_server_stream() -> impl Stream> { - stream! { - let mut server = create_tokio_secure_pipe(); +/// Produce a `Stream` of connected pipe servers for `tonic::transport::Server::serve_with_incoming`. +/// +/// Each loop: +/// 1. Creates a fresh listening instance. +/// 2. Awaits a client connection (`connect().await`). +/// 3. Yields the connected `TonicNamedPipeServer`. +pub fn get_named_pipe_server_stream() -> Result>, std::io::Error> { + Ok(stream! { + let mut server = create_tokio_secure_pipe()?; loop { server.connect().await?; yield Ok(TonicNamedPipeServer::new(server)); - server = create_tokio_secure_pipe(); + server = create_tokio_secure_pipe()?; } - } + }) } From 2c87bc8632cc02b8ef7e6e7584b53365ac949e36 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 28 Oct 2025 06:10:32 -0700 Subject: [PATCH 18/27] format, fix run_server function --- src-tauri/src/named_pipe.rs | 5 +++-- src-tauri/src/service/mod.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index 3d387d10..c13a0d2a 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -7,7 +7,7 @@ use tokio::{ }; use tonic::transport::server::Connected; use windows_sys::Win32::{ - Foundation::{GetLastError, LocalFree, HANDLE, INVALID_HANDLE_VALUE}, + Foundation::{LocalFree, HANDLE, INVALID_HANDLE_VALUE}, Security::{ Authorization::{ConvertStringSecurityDescriptorToSecurityDescriptorW, SDDL_REVISION_1}, PSECURITY_DESCRIPTOR, SECURITY_ATTRIBUTES, @@ -147,7 +147,8 @@ fn create_tokio_secure_pipe() -> Result { /// 1. Creates a fresh listening instance. /// 2. Awaits a client connection (`connect().await`). /// 3. Yields the connected `TonicNamedPipeServer`. -pub fn get_named_pipe_server_stream() -> Result>, std::io::Error> { +pub fn get_named_pipe_server_stream( +) -> Result>, std::io::Error> { Ok(stream! { let mut server = create_tokio_secure_pipe()?; diff --git a/src-tauri/src/service/mod.rs b/src-tauri/src/service/mod.rs index 1747133c..25d265c9 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -548,7 +548,7 @@ pub(crate) async fn run_server( ) -> anyhow::Result<()> { debug!("Starting Defguard interface management daemon"); - let stream = get_named_pipe_server_stream(); + let stream = get_named_pipe_server_stream()?; let daemon_service = DaemonService::new(&config, service_location_manager); info!("Defguard daemon version {VERSION} started"); From 35f14b96857a783032d2f6cc8a9fcd696138b460 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 28 Oct 2025 06:20:54 -0700 Subject: [PATCH 19/27] remove delay --- src-tauri/src/service/mod.rs | 5 +++-- src-tauri/src/service/utils.rs | 6 ------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/service/mod.rs b/src-tauri/src/service/mod.rs index 25d265c9..18f922d6 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -50,7 +50,8 @@ use self::config::Config; use super::VERSION; #[cfg(windows)] use crate::{ - enterprise::service_locations::ServiceLocationManager, named_pipe::get_named_pipe_server_stream, + enterprise::service_locations::ServiceLocationManager, + named_pipe::get_named_pipe_server_stream, named_pipe::PIPE_NAME, }; use crate::{ @@ -551,7 +552,7 @@ pub(crate) async fn run_server( let stream = get_named_pipe_server_stream()?; let daemon_service = DaemonService::new(&config, service_location_manager); - info!("Defguard daemon version {VERSION} started"); + info!("Defguard daemon version {VERSION} started, listening on named pipe {PIPE_NAME}"); debug!("Defguard daemon configuration: {config:?}"); Server::builder() diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index 0d9de027..1bb76b85 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -2,17 +2,12 @@ use std::{io::stdout, sync::LazyLock}; use hyper_util::rt::TokioIo; #[cfg(windows)] -use std::time::Duration; -#[cfg(windows)] use tokio::net::windows::named_pipe::ClientOptions; #[cfg(unix)] use tokio::net::UnixStream; use tonic::transport::channel::{Channel, Endpoint}; #[cfg(unix)] use tonic::transport::Uri; -#[cfg(unix)] -use tower::service_fn; -#[cfg(windows)] use tower::service_fn; use tracing::{debug, Level}; use tracing_appender::non_blocking::WorkerGuard; @@ -66,7 +61,6 @@ pub(crate) static DAEMON_CLIENT: LazyLock> = Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (), Err(e) => return Err(e), } - tokio::time::sleep(Duration::from_millis(50)).await; }; Ok::<_, std::io::Error>(TokioIo::new(client)) })); From cb65f0a280ce950b732b4396579955fbfca20cbb Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 28 Oct 2025 06:28:58 -0700 Subject: [PATCH 20/27] restore sqlx fixture --- ...ae9515952d077106d303f398167caf68a8a70.json | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src-tauri/.sqlx/query-2d6a90bf82aa92118b36d90bf16ae9515952d077106d303f398167caf68a8a70.json diff --git a/src-tauri/.sqlx/query-2d6a90bf82aa92118b36d90bf16ae9515952d077106d303f398167caf68a8a70.json b/src-tauri/.sqlx/query-2d6a90bf82aa92118b36d90bf16ae9515952d077106d303f398167caf68a8a70.json new file mode 100644 index 00000000..5db9905d --- /dev/null +++ b/src-tauri/.sqlx/query-2d6a90bf82aa92118b36d90bf16ae9515952d077106d303f398167caf68a8a70.json @@ -0,0 +1,20 @@ +{ + "db_name": "SQLite", + "query": "SELECT count(*) FROM tunnel_stats", + "describe": { + "columns": [ + { + "name": "count(*)", + "ordinal": 0, + "type_info": "Integer" + } + ], + "parameters": { + "Right": 0 + }, + "nullable": [ + false + ] + }, + "hash": "2d6a90bf82aa92118b36d90bf16ae9515952d077106d303f398167caf68a8a70" +} From 987154034a316ec4778e6e047afc6eca038deacd Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 29 Oct 2025 08:49:32 -0700 Subject: [PATCH 21/27] logs, style --- src-tauri/src/named_pipe.rs | 84 ++++++++++++++++++++-------------- src-tauri/src/service/utils.rs | 8 ++-- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/named_pipe.rs index c13a0d2a..2d6e3e14 100644 --- a/src-tauri/src/named_pipe.rs +++ b/src-tauri/src/named_pipe.rs @@ -19,6 +19,12 @@ use windows_sys::Win32::{ // Named-pipe name used for IPC between defguard client and windows service. pub static PIPE_NAME: &str = r"\\.\pipe\defguard_daemon"; +// SDDL defining named pipe ACL: +/// - `SY` (LocalSystem) - full control +/// - `BA` (Administrators) - full control +/// - `BU` (Built-in Users) - read/write +pub static SDDL: &str = "D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;BU)"; + /// Tonic-compatible wrapper around a Windows named pipe server handle. pub struct TonicNamedPipeServer { inner: NamedPipeServer, @@ -78,41 +84,41 @@ fn str_to_wide_null_terminated(s: &str) -> Vec { s.encode_utf16().chain(Some(0)).collect() } -/// Create a secure Windows named pipe handle with ACLs: -/// - `SY` (LocalSystem) - full control -/// - `BA` (Administrators) - full control -/// - `BU` (Built-in Users) - read/write -/// +/// Create a secure Windows named pipe handle with appropriate ACL. /// Uses `FILE_FLAG_OVERLAPPED` for Tokio compatibility and sets `nMaxInstances = 2` /// (one client + one service instance). fn create_secure_pipe() -> Result { - unsafe { - // Compose SDDL: SYSTEM & Administrators full access, users read-write. - let sddl = "D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;BU)"; - let sddl_wide = str_to_wide_null_terminated(sddl); + debug!("Creating secure named pipe {PIPE_NAME}"); + + // Compose SDDL: SYSTEM & Administrators full access, users read-write. + let sddl_wide = str_to_wide_null_terminated(SDDL); - let mut descriptor: PSECURITY_DESCRIPTOR = std::ptr::null_mut(); + let mut descriptor: PSECURITY_DESCRIPTOR = std::ptr::null_mut(); - if ConvertStringSecurityDescriptorToSecurityDescriptorW( + let result = unsafe { + ConvertStringSecurityDescriptorToSecurityDescriptorW( sddl_wide.as_ptr(), SDDL_REVISION_1, &mut descriptor as *mut PSECURITY_DESCRIPTOR, std::ptr::null_mut(), - ) == 0 - { - return Err(std::io::Error::last_os_error()); - } + ) + }; + if result == 0 { + error!("Error calling ConvertStringSecurityDescriptorToSecurityDescriptorW"); + return Err(std::io::Error::last_os_error()); + } - // Build SECURITY_ATTRIBUTES pointing to the security descriptor - let attributes = SECURITY_ATTRIBUTES { - nLength: std::mem::size_of::() as u32, - lpSecurityDescriptor: descriptor as *mut _, - bInheritHandle: 0, - }; + // Build SECURITY_ATTRIBUTES pointing to the security descriptor + let attributes = SECURITY_ATTRIBUTES { + nLength: std::mem::size_of::() as u32, + lpSecurityDescriptor: descriptor as *mut _, + bInheritHandle: 0, + }; - let name_wide = str_to_wide_null_terminated(PIPE_NAME); + let name_wide = str_to_wide_null_terminated(PIPE_NAME); - let handle = CreateNamedPipeW( + let handle = unsafe { + CreateNamedPipeW( name_wide.as_ptr(), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, @@ -122,23 +128,30 @@ fn create_secure_pipe() -> Result { 65536, 0, &attributes, - ); - + ) + }; + unsafe { // Free memory allocated by ConvertStringSecurityDescriptorToSecurityDescriptorW. - LocalFree(descriptor as *mut _); - - if handle == INVALID_HANDLE_VALUE || handle.is_null() { - return Err(std::io::Error::last_os_error()); - } + LocalFree(descriptor); + } - Ok(handle) + if handle == INVALID_HANDLE_VALUE || handle.is_null() { + error!("CreateNamedPipeW returned invalid handle: {handle:?}"); + return Err(std::io::Error::last_os_error()); } + + info!("Created secure named pipe {PIPE_NAME}"); + Ok(handle) } /// Wrap a raw pipe `HANDLE` into a Tokio `NamedPipeServer`. fn create_tokio_secure_pipe() -> Result { + debug!("Creating tokio secure pipe"); let raw = create_secure_pipe()?; - unsafe { NamedPipeServer::from_raw_handle(raw as RawHandle) } + let pipe = unsafe { NamedPipeServer::from_raw_handle(raw as RawHandle)? }; + + info!("Created tokio secure pipe"); + Ok(pipe) } /// Produce a `Stream` of connected pipe servers for `tonic::transport::Server::serve_with_incoming`. @@ -149,7 +162,8 @@ fn create_tokio_secure_pipe() -> Result { /// 3. Yields the connected `TonicNamedPipeServer`. pub fn get_named_pipe_server_stream( ) -> Result>, std::io::Error> { - Ok(stream! { + debug!("Creating named pipe server stream"); + let stream = stream! { let mut server = create_tokio_secure_pipe()?; loop { @@ -157,5 +171,7 @@ pub fn get_named_pipe_server_stream( yield Ok(TonicNamedPipeServer::new(server)); server = create_tokio_secure_pipe()?; } - }) + }; + info!("Created named pipe server stream"); + Ok(stream) } diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index 1bb76b85..ba543218 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -17,7 +17,6 @@ use tracing_subscriber::{ }; #[cfg(windows)] use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY; - #[cfg(windows)] use crate::named_pipe::PIPE_NAME; use crate::service::{ @@ -58,8 +57,11 @@ pub(crate) static DAEMON_CLIENT: LazyLock> = let client = loop { match ClientOptions::new().open(PIPE_NAME) { Ok(client) => break client, - Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (), - Err(e) => return Err(e), + Err(err) if err.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (), + Err(err) => { + error!("Problem connecting to named pipe: {err}"); + return Err(err); + } } }; Ok::<_, std::io::Error>(TokioIo::new(client)) From bf54523ecf81cbec08367194b1fc0cf638c59273 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 29 Oct 2025 08:49:59 -0700 Subject: [PATCH 22/27] cargo fmt --- src-tauri/src/service/utils.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index ba543218..83e29f51 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -1,5 +1,10 @@ use std::{io::stdout, sync::LazyLock}; +#[cfg(windows)] +use crate::named_pipe::PIPE_NAME; +use crate::service::{ + proto::desktop_daemon_service_client::DesktopDaemonServiceClient, DAEMON_BASE_URL, +}; use hyper_util::rt::TokioIo; #[cfg(windows)] use tokio::net::windows::named_pipe::ClientOptions; @@ -17,11 +22,6 @@ use tracing_subscriber::{ }; #[cfg(windows)] use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY; -#[cfg(windows)] -use crate::named_pipe::PIPE_NAME; -use crate::service::{ - proto::desktop_daemon_service_client::DesktopDaemonServiceClient, DAEMON_BASE_URL, -}; pub(crate) static DAEMON_CLIENT: LazyLock> = LazyLock::new(|| { From a097a06a707edf849556cb63234b42855dc66d49 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 29 Oct 2025 09:54:46 -0700 Subject: [PATCH 23/27] move named_pipe module --- src-tauri/src/lib.rs | 2 -- src-tauri/src/service/mod.rs | 5 ++++- src-tauri/src/{ => service}/named_pipe.rs | 0 src-tauri/src/service/utils.rs | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) rename src-tauri/src/{ => service}/named_pipe.rs (100%) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index c29a5d78..d02dbc1b 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -24,8 +24,6 @@ pub mod enterprise; pub mod error; pub mod events; pub mod log_watcher; -#[cfg(windows)] -pub mod named_pipe; pub mod periodic; pub mod service; pub mod tray; diff --git a/src-tauri/src/service/mod.rs b/src-tauri/src/service/mod.rs index 18f922d6..e535c8b7 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -5,6 +5,8 @@ pub mod proto { pub mod utils; #[cfg(windows)] pub mod windows; +#[cfg(windows)] +pub mod named_pipe; use std::{ collections::HashMap, @@ -51,8 +53,9 @@ use super::VERSION; #[cfg(windows)] use crate::{ enterprise::service_locations::ServiceLocationManager, - named_pipe::get_named_pipe_server_stream, named_pipe::PIPE_NAME, }; +#[cfg(windows)] +use crate::service::named_pipe::{get_named_pipe_server_stream, PIPE_NAME}; use crate::{ enterprise::service_locations::ServiceLocationError, diff --git a/src-tauri/src/named_pipe.rs b/src-tauri/src/service/named_pipe.rs similarity index 100% rename from src-tauri/src/named_pipe.rs rename to src-tauri/src/service/named_pipe.rs diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index 83e29f51..a60af21b 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -1,8 +1,8 @@ use std::{io::stdout, sync::LazyLock}; #[cfg(windows)] -use crate::named_pipe::PIPE_NAME; use crate::service::{ + named_pipe::PIPE_NAME, proto::desktop_daemon_service_client::DesktopDaemonServiceClient, DAEMON_BASE_URL, }; use hyper_util::rt::TokioIo; From 51074526d297a548f4546af908570242a2841c11 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 29 Oct 2025 10:12:41 -0700 Subject: [PATCH 24/27] remove unused static --- src-tauri/src/service/mod.rs | 2 -- src-tauri/src/service/utils.rs | 5 +++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src-tauri/src/service/mod.rs b/src-tauri/src/service/mod.rs index e535c8b7..7e4343ec 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -62,8 +62,6 @@ use crate::{ service::proto::{DeleteServiceLocationsRequest, SaveServiceLocationsRequest}, }; -pub(super) const DAEMON_BASE_URL: &str = "http://localhost:54127"; - #[cfg(unix)] pub(super) const DAEMON_SOCKET_PATH: &str = "/var/run/defguard.socket"; diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index a60af21b..c1415f34 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -3,7 +3,7 @@ use std::{io::stdout, sync::LazyLock}; #[cfg(windows)] use crate::service::{ named_pipe::PIPE_NAME, - proto::desktop_daemon_service_client::DesktopDaemonServiceClient, DAEMON_BASE_URL, + proto::desktop_daemon_service_client::DesktopDaemonServiceClient, }; use hyper_util::rt::TokioIo; #[cfg(windows)] @@ -26,7 +26,8 @@ use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY; pub(crate) static DAEMON_CLIENT: LazyLock> = LazyLock::new(|| { debug!("Setting up gRPC client"); - let endpoint = Endpoint::from_static(DAEMON_BASE_URL); // Should not panic. + // URL is ignored since we provide our own connectors for unix socket and windows named pipes. + let endpoint = Endpoint::from_static("http://localhost"); let channel; #[cfg(unix)] { From 652797affd4ff678c371dd69ef39d02e7cfacccc Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 29 Oct 2025 10:14:38 -0700 Subject: [PATCH 25/27] add logs --- src-tauri/src/service/utils.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index c1415f34..9d2792bc 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -49,6 +49,7 @@ pub(crate) static DAEMON_CLIENT: LazyLock> = return Err(err); } }; + info!("Created unix gRPC client"); Ok::<_, std::io::Error>(TokioIo::new(stream)) })); }; @@ -65,6 +66,7 @@ pub(crate) static DAEMON_CLIENT: LazyLock> = } } }; + info!("Created windows gRPC client"); Ok::<_, std::io::Error>(TokioIo::new(client)) })); } From 1369d78ab2c35c06f3cd545fe7d9e1a501e338ff Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 29 Oct 2025 02:21:33 -0700 Subject: [PATCH 26/27] cargo fmt --- src-tauri/src/service/mod.rs | 8 +++----- src-tauri/src/service/utils.rs | 3 +-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src-tauri/src/service/mod.rs b/src-tauri/src/service/mod.rs index 7e4343ec..859f8d0c 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -2,11 +2,11 @@ pub mod config; pub mod proto { tonic::include_proto!("client"); } +#[cfg(windows)] +pub mod named_pipe; pub mod utils; #[cfg(windows)] pub mod windows; -#[cfg(windows)] -pub mod named_pipe; use std::{ collections::HashMap, @@ -51,9 +51,7 @@ use tracing::{debug, error, info, info_span, Instrument}; use self::config::Config; use super::VERSION; #[cfg(windows)] -use crate::{ - enterprise::service_locations::ServiceLocationManager, -}; +use crate::enterprise::service_locations::ServiceLocationManager; #[cfg(windows)] use crate::service::named_pipe::{get_named_pipe_server_stream, PIPE_NAME}; diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index 9d2792bc..33e209d2 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -2,8 +2,7 @@ use std::{io::stdout, sync::LazyLock}; #[cfg(windows)] use crate::service::{ - named_pipe::PIPE_NAME, - proto::desktop_daemon_service_client::DesktopDaemonServiceClient, + named_pipe::PIPE_NAME, proto::desktop_daemon_service_client::DesktopDaemonServiceClient, }; use hyper_util::rt::TokioIo; #[cfg(windows)] From f957e254dbf6cdf88f0fd1a88163482d28fd29df Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Wed, 29 Oct 2025 02:32:41 -0700 Subject: [PATCH 27/27] fix import --- src-tauri/src/service/utils.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index 33e209d2..0f51654f 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -1,9 +1,5 @@ use std::{io::stdout, sync::LazyLock}; -#[cfg(windows)] -use crate::service::{ - named_pipe::PIPE_NAME, proto::desktop_daemon_service_client::DesktopDaemonServiceClient, -}; use hyper_util::rt::TokioIo; #[cfg(windows)] use tokio::net::windows::named_pipe::ClientOptions; @@ -22,6 +18,10 @@ use tracing_subscriber::{ #[cfg(windows)] use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY; +#[cfg(windows)] +use crate::service::named_pipe::PIPE_NAME; +use crate::service::proto::desktop_daemon_service_client::DesktopDaemonServiceClient; + pub(crate) static DAEMON_CLIENT: LazyLock> = LazyLock::new(|| { debug!("Setting up gRPC client");