From ac70626715d619ab49be7b10e4ad9e0b5888ea80 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Fri, 10 Apr 2026 13:26:03 +0200 Subject: [PATCH 01/11] Retry service location auto-connect on startup --- .../enterprise/service_locations/windows.rs | 2 +- src-tauri/src/service/windows.rs | 58 ++++++++++++++----- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src-tauri/src/enterprise/service_locations/windows.rs b/src-tauri/src/enterprise/service_locations/windows.rs index f40c1044..17a442c2 100644 --- a/src-tauri/src/enterprise/service_locations/windows.rs +++ b/src-tauri/src/enterprise/service_locations/windows.rs @@ -725,7 +725,7 @@ impl ServiceLocationManager { if let Err(err) = self.setup_service_location_interface(&location, &instance_data.private_key) { - debug!( + warn!( "Failed to setup service location interface for '{}': {err:?}", location.name ); diff --git a/src-tauri/src/service/windows.rs b/src-tauri/src/service/windows.rs index c72a176a..d0a3647b 100644 --- a/src-tauri/src/service/windows.rs +++ b/src-tauri/src/service/windows.rs @@ -32,6 +32,8 @@ use crate::{ static SERVICE_NAME: &str = "DefguardService"; const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS; const LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS: Duration = Duration::from_secs(5); +const SERVICE_LOCATION_CONNECT_RETRY_COUNT: u32 = 5; +const SERVICE_LOCATION_CONNECT_RETRY_DELAY_SECS: u64 = 30; pub fn run() -> Result<(), windows_service::Error> { // Register generated `ffi_service_main` with the system and start the service, blocking @@ -112,25 +114,51 @@ fn run_service() -> Result<(), DaemonError> { let service_location_manager = Arc::new(RwLock::new(service_location_manager)); - // Spawn service location management task - let service_location_manager_clone = service_location_manager.clone(); + // Spawn service location auto-connect task with retries. + // Each attempt skips locations that are already connected, so it is safe to call + // connect_to_service_locations repeatedly. The retry loop exists to handle the case + // where the connection may fail initially at startup because the network + // (e.g. Wi-Fi) is not yet available (mainly DNS resolution issues). + let service_location_manager_connect = service_location_manager.clone(); runtime.spawn(async move { - let manager = service_location_manager_clone; - - info!("Starting service location management task"); - - info!("Attempting to auto-connect to service locations"); - match manager.write().unwrap().connect_to_service_locations() { - Ok(()) => { - info!("Auto-connect to service locations completed successfully"); + for attempt in 1..=SERVICE_LOCATION_CONNECT_RETRY_COUNT { + info!( + "Attempting to auto-connect to service locations \ + (attempt {attempt}/{SERVICE_LOCATION_CONNECT_RETRY_COUNT})" + ); + match service_location_manager_connect + .write() + .unwrap() + .connect_to_service_locations() + { + Ok(()) => { + info!( + "Auto-connect attempt {attempt}/{SERVICE_LOCATION_CONNECT_RETRY_COUNT} \ + completed" + ); + } + Err(err) => { + warn!( + "Auto-connect attempt {attempt}/{SERVICE_LOCATION_CONNECT_RETRY_COUNT} \ + failed: {err}" + ); + } } - Err(err) => { - warn!( - "Error while trying to auto-connect to service locations: {err}. \ - Will continue monitoring for login/logoff events.", - ); + + if attempt < SERVICE_LOCATION_CONNECT_RETRY_COUNT { + tokio::time::sleep(Duration::from_secs( + SERVICE_LOCATION_CONNECT_RETRY_DELAY_SECS, + )) + .await; } } + info!("Service location auto-connect task finished"); + }); + + // Spawn login/logoff monitoring task, runs concurrently with the auto-connect task above. + let service_location_manager_clone = service_location_manager.clone(); + runtime.spawn(async move { + let manager = service_location_manager_clone; info!("Starting login/logoff event monitoring"); loop { From 48cdb9de1fb3a641457c0412cab571162c3bf814 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Fri, 10 Apr 2026 13:38:56 +0200 Subject: [PATCH 02/11] listen for address change --- src-tauri/Cargo.toml | 3 ++ .../enterprise/service_locations/windows.rs | 54 +++++++++++++++++++ src-tauri/src/service/windows.rs | 39 ++++++++++++-- 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 080ef5c1..456abc86 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -160,6 +160,9 @@ windows-sys = { version = "0.61", features = [ # HANDLE & file functions "Win32_System_IO", "Win32_System_Threading", + + # Network address change notifications (NotifyAddrChange) + "Win32_NetworkManagement_IpHelper", ] } [features] diff --git a/src-tauri/src/enterprise/service_locations/windows.rs b/src-tauri/src/enterprise/service_locations/windows.rs index 17a442c2..3bdd63a3 100644 --- a/src-tauri/src/enterprise/service_locations/windows.rs +++ b/src-tauri/src/enterprise/service_locations/windows.rs @@ -9,6 +9,8 @@ use std::{ time::Duration, }; +use windows_sys::Win32::NetworkManagement::IpHelper::NotifyAddrChange; + use common::{dns_borrow, find_free_tcp_port, get_interface_name}; use defguard_wireguard_rs::{ key::Key, net::IpAddrMask, peer::Peer, InterfaceConfiguration, WireguardInterfaceApi, @@ -36,10 +38,62 @@ use crate::{ }; const LOGIN_LOGOFF_EVENT_RETRY_DELAY_SECS: u64 = 5; +// How long to wait after a network change before attempting to connect. +// Gives DHCP time to complete and DNS to become available. +const NETWORK_STABILIZATION_DELAY_SECS: u64 = 3; +// How long to wait before restarting the network change watcher on error. +const NETWORK_CHANGE_MONITOR_RESTART_DELAY_SECS: u64 = 5; const DEFAULT_WIREGUARD_PORT: u16 = 51820; const DEFGUARD_DIR: &str = "Defguard"; const SERVICE_LOCATIONS_SUBDIR: &str = "service_locations"; +/// Watches for IP address changes on any network interface and attempts to connect to any +/// service locations that are not yet connected. This handles the case where the endpoint +/// hostname cannot be resolved at service startup because the network (e.g. Wi-Fi) is not +/// yet available. When the network comes up and an IP is assigned, this watcher fires and +/// retries the connection. +/// +/// Note: `NotifyAddrChange` also fires when WireGuard interfaces are created. This is +/// harmless because `connect_to_service_locations` skips already-connected locations. +pub(crate) async fn watch_for_network_change( + service_location_manager: Arc>, +) -> Result<(), ServiceLocationError> { + loop { + // NotifyAddrChange blocks until any IP address is added or removed on any interface. + // Passing NULL for both handle and overlapped selects the synchronous (blocking) mode. + let result = unsafe { NotifyAddrChange(std::ptr::null_mut(), std::ptr::null()) }; + + if result != 0 { + error!("NotifyAddrChange failed with error code: {result}"); + tokio::time::sleep(Duration::from_secs( + NETWORK_CHANGE_MONITOR_RESTART_DELAY_SECS, + )) + .await; + continue; + } + + debug!( + "Network address change detected, waiting {NETWORK_STABILIZATION_DELAY_SECS}s for \ + network to stabilize before attempting service location connections..." + ); + tokio::time::sleep(Duration::from_secs(NETWORK_STABILIZATION_DELAY_SECS)).await; + + debug!("Attempting to connect to service locations after network change"); + match service_location_manager + .write() + .unwrap() + .connect_to_service_locations() + { + Ok(()) => { + debug!("Service location connect attempt after network change completed"); + } + Err(err) => { + warn!("Failed to connect to service locations after network change: {err}"); + } + } + } +} + pub(crate) async fn watch_for_login_logoff( service_location_manager: Arc>, ) -> Result<(), ServiceLocationError> { diff --git a/src-tauri/src/service/windows.rs b/src-tauri/src/service/windows.rs index d0a3647b..ec4b4730 100644 --- a/src-tauri/src/service/windows.rs +++ b/src-tauri/src/service/windows.rs @@ -20,7 +20,8 @@ use windows_service::{ use crate::{ enterprise::service_locations::{ - windows::watch_for_login_logoff, ServiceLocationError, ServiceLocationManager, + windows::{watch_for_login_logoff, watch_for_network_change}, + ServiceLocationError, ServiceLocationManager, }, service::{ config::Config, @@ -32,6 +33,7 @@ use crate::{ static SERVICE_NAME: &str = "DefguardService"; const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS; const LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS: Duration = Duration::from_secs(5); +const NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS: Duration = Duration::from_secs(5); const SERVICE_LOCATION_CONNECT_RETRY_COUNT: u32 = 5; const SERVICE_LOCATION_CONNECT_RETRY_DELAY_SECS: u64 = 30; @@ -114,11 +116,42 @@ fn run_service() -> Result<(), DaemonError> { let service_location_manager = Arc::new(RwLock::new(service_location_manager)); + // Spawn network change monitoring task first so NotifyAddrChange is registered as early + // as possible, minimising the window in which a network event could be missed before + // the watcher is listening. The retry task below is the backstop for any event that + // still slips through that window. + let service_location_manager_clone = service_location_manager.clone(); + runtime.spawn(async move { + let manager = service_location_manager_clone; + + info!("Starting network change monitoring"); + loop { + match watch_for_network_change(manager.clone()).await { + Ok(()) => { + warn!( + "Network change monitoring ended unexpectedly. Restarting in \ + {NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS:?}..." + ); + tokio::time::sleep(NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS).await; + } + Err(e) => { + error!( + "Error in network change monitoring: {e}. Restarting in \ + {NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS:?}...", + ); + tokio::time::sleep(NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS).await; + info!("Restarting network change monitoring"); + } + } + } + }); + // Spawn service location auto-connect task with retries. // Each attempt skips locations that are already connected, so it is safe to call // connect_to_service_locations repeatedly. The retry loop exists to handle the case // where the connection may fail initially at startup because the network - // (e.g. Wi-Fi) is not yet available (mainly DNS resolution issues). + // (e.g. Wi-Fi) is not yet available (mainly DNS resolution issues), and serves as + // a backstop for any network events missed by the watcher above. let service_location_manager_connect = service_location_manager.clone(); runtime.spawn(async move { for attempt in 1..=SERVICE_LOCATION_CONNECT_RETRY_COUNT { @@ -155,7 +188,7 @@ fn run_service() -> Result<(), DaemonError> { info!("Service location auto-connect task finished"); }); - // Spawn login/logoff monitoring task, runs concurrently with the auto-connect task above. + // Spawn login/logoff monitoring task, runs concurrently with the tasks above. let service_location_manager_clone = service_location_manager.clone(); runtime.spawn(async move { let manager = service_location_manager_clone; From 7d3ed8ed98fc14035d516e0eeebe4ca802cfd050 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Thu, 16 Apr 2026 12:23:41 +0200 Subject: [PATCH 03/11] dont retry if succeeded --- .../src/enterprise/service_locations/windows.rs | 14 +++++++++++--- src-tauri/src/service/windows.rs | 12 ++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src-tauri/src/enterprise/service_locations/windows.rs b/src-tauri/src/enterprise/service_locations/windows.rs index 3bdd63a3..b1f8c089 100644 --- a/src-tauri/src/enterprise/service_locations/windows.rs +++ b/src-tauri/src/enterprise/service_locations/windows.rs @@ -84,7 +84,7 @@ pub(crate) async fn watch_for_network_change( .unwrap() .connect_to_service_locations() { - Ok(()) => { + Ok(_) => { debug!("Service location connect attempt after network change completed"); } Err(err) => { @@ -734,12 +734,19 @@ impl ServiceLocationManager { Ok(()) } - pub(crate) fn connect_to_service_locations(&mut self) -> Result<(), ServiceLocationError> { + /// Attempts to connect to all service locations that are not already connected. + /// + /// Returns `Ok(true)` if every location is now connected (either it was already connected or + /// it was successfully connected during this call), and `Ok(false)` if at least one location + /// failed to connect (indicating that a retry may be worthwhile). + pub(crate) fn connect_to_service_locations(&mut self) -> Result { debug!("Attempting to auto-connect to VPN..."); let data = self.load_service_locations()?; debug!("Loaded {} instance(s) from ServiceLocationApi", data.len()); + let mut all_connected = true; + for instance_data in data { debug!( "Found service locations for instance ID: {}", @@ -783,6 +790,7 @@ impl ServiceLocationManager { "Failed to setup service location interface for '{}': {err:?}", location.name ); + all_connected = false; continue; } @@ -803,7 +811,7 @@ impl ServiceLocationManager { debug!("Auto-connect attempt completed"); - Ok(()) + Ok(all_connected) } pub fn save_service_locations( diff --git a/src-tauri/src/service/windows.rs b/src-tauri/src/service/windows.rs index ec4b4730..ca7aca82 100644 --- a/src-tauri/src/service/windows.rs +++ b/src-tauri/src/service/windows.rs @@ -152,6 +152,7 @@ fn run_service() -> Result<(), DaemonError> { // where the connection may fail initially at startup because the network // (e.g. Wi-Fi) is not yet available (mainly DNS resolution issues), and serves as // a backstop for any network events missed by the watcher above. + // If all locations connect successfully on a given attempt, no further retries are made. let service_location_manager_connect = service_location_manager.clone(); runtime.spawn(async move { for attempt in 1..=SERVICE_LOCATION_CONNECT_RETRY_COUNT { @@ -164,10 +165,17 @@ fn run_service() -> Result<(), DaemonError> { .unwrap() .connect_to_service_locations() { - Ok(()) => { + Ok(true) => { info!( + "All service locations connected successfully \ + (attempt {attempt}/{SERVICE_LOCATION_CONNECT_RETRY_COUNT})" + ); + break; + } + Ok(false) => { + warn!( "Auto-connect attempt {attempt}/{SERVICE_LOCATION_CONNECT_RETRY_COUNT} \ - completed" + completed with some failures" ); } Err(err) => { From df8e48a45016e9e902a0d24ab985782a0c43d988 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Thu, 16 Apr 2026 12:29:53 +0200 Subject: [PATCH 04/11] cleanup --- .trivyignore.yaml | 2 +- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- .../src/enterprise/service_locations/windows.rs | 10 +++++----- src-tauri/src/service/windows.rs | 12 ++++++------ src-tauri/src/wg_config.rs | 4 ++-- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.trivyignore.yaml b/.trivyignore.yaml index f29211f5..67f4f502 100644 --- a/.trivyignore.yaml +++ b/.trivyignore.yaml @@ -1,4 +1,4 @@ vulnerabilities: - id: GHSA-wrw7-89jp-8q8g - expired_at: 2026-04-18 + expired_at: 2026-05-16 statement: 'glib is a transitive dependency of Tauri which we cannot update ourselves. Waiting for tauri to finish migration to gtk4-rs: https://github.com/tauri-apps/tauri/issues/12563' diff --git a/package.json b/package.json index d62d1507..baae2fc0 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "html-react-parser": "^5.2.17", "itertools": "^2.6.0", "js-base64": "^3.7.8", - "lodash-es": "^4.17.23", + "lodash-es": "^4.18.1", "merge-refs": "^2.0.0", "millify": "^6.1.0", "motion": "^12.38.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f53e0d68..b047c2ca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -114,8 +114,8 @@ importers: specifier: ^3.7.8 version: 3.7.8 lodash-es: - specifier: ^4.17.23 - version: 4.17.23 + specifier: ^4.18.1 + version: 4.18.1 merge-refs: specifier: ^2.0.0 version: 2.0.0(@types/react@19.2.14) @@ -2183,8 +2183,8 @@ packages: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} - lodash-es@4.17.23: - resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} lodash@4.17.23: resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} @@ -4907,7 +4907,7 @@ snapshots: pify: 3.0.0 strip-bom: 3.0.0 - lodash-es@4.17.23: {} + lodash-es@4.18.1: {} lodash@4.17.23: {} diff --git a/src-tauri/src/enterprise/service_locations/windows.rs b/src-tauri/src/enterprise/service_locations/windows.rs index b1f8c089..8de104fd 100644 --- a/src-tauri/src/enterprise/service_locations/windows.rs +++ b/src-tauri/src/enterprise/service_locations/windows.rs @@ -9,14 +9,13 @@ use std::{ time::Duration, }; -use windows_sys::Win32::NetworkManagement::IpHelper::NotifyAddrChange; - use common::{dns_borrow, find_free_tcp_port, get_interface_name}; use defguard_wireguard_rs::{ key::Key, net::IpAddrMask, peer::Peer, InterfaceConfiguration, WireguardInterfaceApi, }; use known_folders::get_known_folder_path; use log::{debug, error, warn}; +use tokio::time::sleep; use windows::{ core::PSTR, Win32::System::RemoteDesktop::{ @@ -25,6 +24,7 @@ use windows::{ }, }; use windows_acl::acl::ACL; +use windows_sys::Win32::NetworkManagement::IpHelper::NotifyAddrChange; use crate::{ enterprise::service_locations::{ @@ -65,7 +65,7 @@ pub(crate) async fn watch_for_network_change( if result != 0 { error!("NotifyAddrChange failed with error code: {result}"); - tokio::time::sleep(Duration::from_secs( + sleep(Duration::from_secs( NETWORK_CHANGE_MONITOR_RESTART_DELAY_SECS, )) .await; @@ -76,7 +76,7 @@ pub(crate) async fn watch_for_network_change( "Network address change detected, waiting {NETWORK_STABILIZATION_DELAY_SECS}s for \ network to stabilize before attempting service location connections..." ); - tokio::time::sleep(Duration::from_secs(NETWORK_STABILIZATION_DELAY_SECS)).await; + sleep(Duration::from_secs(NETWORK_STABILIZATION_DELAY_SECS)).await; debug!("Attempting to connect to service locations after network change"); match service_location_manager @@ -113,7 +113,7 @@ pub(crate) async fn watch_for_login_logoff( } Err(err) => { error!("Failed waiting for login/logoff event: {err:?}"); - tokio::time::sleep(Duration::from_secs(LOGIN_LOGOFF_EVENT_RETRY_DELAY_SECS)).await; + sleep(Duration::from_secs(LOGIN_LOGOFF_EVENT_RETRY_DELAY_SECS)).await; continue; } }; diff --git a/src-tauri/src/service/windows.rs b/src-tauri/src/service/windows.rs index ca7aca82..8b5948d0 100644 --- a/src-tauri/src/service/windows.rs +++ b/src-tauri/src/service/windows.rs @@ -7,7 +7,7 @@ use std::{ use clap::Parser; use error; -use tokio::runtime::Runtime; +use tokio::{runtime::Runtime, time::sleep}; use windows_service::{ define_windows_service, service::{ @@ -132,14 +132,14 @@ fn run_service() -> Result<(), DaemonError> { "Network change monitoring ended unexpectedly. Restarting in \ {NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS:?}..." ); - tokio::time::sleep(NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS).await; + sleep(NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS).await; } Err(e) => { error!( "Error in network change monitoring: {e}. Restarting in \ {NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS:?}...", ); - tokio::time::sleep(NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS).await; + sleep(NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS).await; info!("Restarting network change monitoring"); } } @@ -187,7 +187,7 @@ fn run_service() -> Result<(), DaemonError> { } if attempt < SERVICE_LOCATION_CONNECT_RETRY_COUNT { - tokio::time::sleep(Duration::from_secs( + sleep(Duration::from_secs( SERVICE_LOCATION_CONNECT_RETRY_DELAY_SECS, )) .await; @@ -209,14 +209,14 @@ fn run_service() -> Result<(), DaemonError> { "Login/logoff event monitoring ended unexpectedly. Restarting in \ {LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS:?}..." ); - tokio::time::sleep(LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS).await; + sleep(LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS).await; } Err(e) => { error!( "Error in login/logoff event monitoring: {e}. Restarting in \ {LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS:?}...", ); - tokio::time::sleep(LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS).await; + sleep(LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS).await; info!("Restarting login/logoff event monitoring"); } } diff --git a/src-tauri/src/wg_config.rs b/src-tauri/src/wg_config.rs index b7d91b7c..78938a7c 100644 --- a/src-tauri/src/wg_config.rs +++ b/src-tauri/src/wg_config.rs @@ -1,6 +1,6 @@ +use std::{array::TryFromSliceError, net::IpAddr, path::Path}; + use base64::{prelude::BASE64_STANDARD, DecodeError, Engine}; -use std::path::Path; -use std::{array::TryFromSliceError, net::IpAddr}; use thiserror::Error; use x25519_dalek::{PublicKey, StaticSecret}; From f921ffe2d378a105facff0b48e9300bb6a91ba45 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Thu, 16 Apr 2026 12:39:26 +0200 Subject: [PATCH 05/11] bump --- src-tauri/Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 20f67bd6..6f6f3ffc 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4913,7 +4913,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "343d3bd7056eda839b03204e68deff7d1b13aba7af2b2fd16890697274262ee7" dependencies = [ - "heck 0.5.0", + "heck 0.4.1", "itertools", "log", "multimap", @@ -5645,9 +5645,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.10" +version = "0.103.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" +checksum = "8279bb85272c9f10811ae6a6c547ff594d6a7f3c6c6b02ee9726d1d0dcfcdd06" dependencies = [ "aws-lc-rs", "ring", From 925487bcaeb3984e353069e838eabf59467a44f7 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Thu, 16 Apr 2026 13:13:58 +0200 Subject: [PATCH 06/11] Apply suggestions from code review Co-authored-by: Adam --- src-tauri/src/enterprise/service_locations/windows.rs | 10 +++++----- src-tauri/src/service/windows.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src-tauri/src/enterprise/service_locations/windows.rs b/src-tauri/src/enterprise/service_locations/windows.rs index 8de104fd..1ab9aa2a 100644 --- a/src-tauri/src/enterprise/service_locations/windows.rs +++ b/src-tauri/src/enterprise/service_locations/windows.rs @@ -40,9 +40,9 @@ use crate::{ const LOGIN_LOGOFF_EVENT_RETRY_DELAY_SECS: u64 = 5; // How long to wait after a network change before attempting to connect. // Gives DHCP time to complete and DNS to become available. -const NETWORK_STABILIZATION_DELAY_SECS: u64 = 3; +const NETWORK_STABILIZATION_DELAY: Duration = Duration::from_secs(3); // How long to wait before restarting the network change watcher on error. -const NETWORK_CHANGE_MONITOR_RESTART_DELAY_SECS: u64 = 5; +const NETWORK_CHANGE_MONITOR_RESTART_DELAY: Durartion = Duration::from_secs(5); const DEFAULT_WIREGUARD_PORT: u16 = 51820; const DEFGUARD_DIR: &str = "Defguard"; const SERVICE_LOCATIONS_SUBDIR: &str = "service_locations"; @@ -65,7 +65,7 @@ pub(crate) async fn watch_for_network_change( if result != 0 { error!("NotifyAddrChange failed with error code: {result}"); - sleep(Duration::from_secs( + sleep(NETWORK_CHANGE_MONITOR_RESTART_DELAY) NETWORK_CHANGE_MONITOR_RESTART_DELAY_SECS, )) .await; @@ -73,10 +73,10 @@ pub(crate) async fn watch_for_network_change( } debug!( - "Network address change detected, waiting {NETWORK_STABILIZATION_DELAY_SECS}s for \ + "Network address change detected, waiting {NETWORK_STABILIZATION_DELAY_SECS:?}s for \ network to stabilize before attempting service location connections..." ); - sleep(Duration::from_secs(NETWORK_STABILIZATION_DELAY_SECS)).await; + sleep(NETWORK_STABILIZATION_DELAY).await; debug!("Attempting to connect to service locations after network change"); match service_location_manager diff --git a/src-tauri/src/service/windows.rs b/src-tauri/src/service/windows.rs index 8b5948d0..1f2a18a7 100644 --- a/src-tauri/src/service/windows.rs +++ b/src-tauri/src/service/windows.rs @@ -35,7 +35,7 @@ const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS; const LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS: Duration = Duration::from_secs(5); const NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS: Duration = Duration::from_secs(5); const SERVICE_LOCATION_CONNECT_RETRY_COUNT: u32 = 5; -const SERVICE_LOCATION_CONNECT_RETRY_DELAY_SECS: u64 = 30; +const SERVICE_LOCATION_CONNECT_RETRY_DELAY_SECS: Duration = Duration::from_secs(30); pub fn run() -> Result<(), windows_service::Error> { // Register generated `ffi_service_main` with the system and start the service, blocking From 33b4cb0772be620da35df23b95cbf02a2f82c263 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Thu, 16 Apr 2026 13:24:03 +0200 Subject: [PATCH 07/11] simplify code --- src-tauri/src/enterprise/service_locations/windows.rs | 7 ++----- src-tauri/src/service/windows.rs | 5 ++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/enterprise/service_locations/windows.rs b/src-tauri/src/enterprise/service_locations/windows.rs index 1ab9aa2a..ba7c4b3f 100644 --- a/src-tauri/src/enterprise/service_locations/windows.rs +++ b/src-tauri/src/enterprise/service_locations/windows.rs @@ -57,7 +57,7 @@ const SERVICE_LOCATIONS_SUBDIR: &str = "service_locations"; /// harmless because `connect_to_service_locations` skips already-connected locations. pub(crate) async fn watch_for_network_change( service_location_manager: Arc>, -) -> Result<(), ServiceLocationError> { +) { loop { // NotifyAddrChange blocks until any IP address is added or removed on any interface. // Passing NULL for both handle and overlapped selects the synchronous (blocking) mode. @@ -65,10 +65,7 @@ pub(crate) async fn watch_for_network_change( if result != 0 { error!("NotifyAddrChange failed with error code: {result}"); - sleep(NETWORK_CHANGE_MONITOR_RESTART_DELAY) - NETWORK_CHANGE_MONITOR_RESTART_DELAY_SECS, - )) - .await; + sleep(NETWORK_CHANGE_MONITOR_RESTART_DELAY).await; continue; } diff --git a/src-tauri/src/service/windows.rs b/src-tauri/src/service/windows.rs index 1f2a18a7..6c4e9769 100644 --- a/src-tauri/src/service/windows.rs +++ b/src-tauri/src/service/windows.rs @@ -132,17 +132,16 @@ fn run_service() -> Result<(), DaemonError> { "Network change monitoring ended unexpectedly. Restarting in \ {NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS:?}..." ); - sleep(NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS).await; } Err(e) => { error!( "Error in network change monitoring: {e}. Restarting in \ {NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS:?}...", ); - sleep(NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS).await; - info!("Restarting network change monitoring"); } } + sleep(NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS).await; + info!("Restarting network change monitoring"); } }); From a8d67d426a45a3db001a0eb2672bf56c660da7c8 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Thu, 16 Apr 2026 13:34:55 +0200 Subject: [PATCH 08/11] simplify further --- src-tauri/src/service/windows.rs | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/src-tauri/src/service/windows.rs b/src-tauri/src/service/windows.rs index 6c4e9769..7a59e324 100644 --- a/src-tauri/src/service/windows.rs +++ b/src-tauri/src/service/windows.rs @@ -123,26 +123,9 @@ fn run_service() -> Result<(), DaemonError> { let service_location_manager_clone = service_location_manager.clone(); runtime.spawn(async move { let manager = service_location_manager_clone; - info!("Starting network change monitoring"); - loop { - match watch_for_network_change(manager.clone()).await { - Ok(()) => { - warn!( - "Network change monitoring ended unexpectedly. Restarting in \ - {NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS:?}..." - ); - } - Err(e) => { - error!( - "Error in network change monitoring: {e}. Restarting in \ - {NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS:?}...", - ); - } - } - sleep(NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS).await; - info!("Restarting network change monitoring"); - } + watch_for_network_change(manager.clone()).await; + error!("Network change monitoring ended unexpectedly."); }); // Spawn service location auto-connect task with retries. From aa96382b0eb411d9f1e6fb263764b71c62b37fbd Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Thu, 16 Apr 2026 14:09:26 +0200 Subject: [PATCH 09/11] fix --- src-tauri/src/enterprise/service_locations/windows.rs | 4 ++-- src-tauri/src/service/windows.rs | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/enterprise/service_locations/windows.rs b/src-tauri/src/enterprise/service_locations/windows.rs index ba7c4b3f..2205eb9d 100644 --- a/src-tauri/src/enterprise/service_locations/windows.rs +++ b/src-tauri/src/enterprise/service_locations/windows.rs @@ -42,7 +42,7 @@ const LOGIN_LOGOFF_EVENT_RETRY_DELAY_SECS: u64 = 5; // Gives DHCP time to complete and DNS to become available. const NETWORK_STABILIZATION_DELAY: Duration = Duration::from_secs(3); // How long to wait before restarting the network change watcher on error. -const NETWORK_CHANGE_MONITOR_RESTART_DELAY: Durartion = Duration::from_secs(5); +const NETWORK_CHANGE_MONITOR_RESTART_DELAY: Duration = Duration::from_secs(5); const DEFAULT_WIREGUARD_PORT: u16 = 51820; const DEFGUARD_DIR: &str = "Defguard"; const SERVICE_LOCATIONS_SUBDIR: &str = "service_locations"; @@ -70,7 +70,7 @@ pub(crate) async fn watch_for_network_change( } debug!( - "Network address change detected, waiting {NETWORK_STABILIZATION_DELAY_SECS:?}s for \ + "Network address change detected, waiting {NETWORK_STABILIZATION_DELAY:?}s for \ network to stabilize before attempting service location connections..." ); sleep(NETWORK_STABILIZATION_DELAY).await; diff --git a/src-tauri/src/service/windows.rs b/src-tauri/src/service/windows.rs index 7a59e324..dec55f23 100644 --- a/src-tauri/src/service/windows.rs +++ b/src-tauri/src/service/windows.rs @@ -33,9 +33,8 @@ use crate::{ static SERVICE_NAME: &str = "DefguardService"; const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS; const LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS: Duration = Duration::from_secs(5); -const NETWORK_CHANGE_MONITORING_RESTART_DELAY_SECS: Duration = Duration::from_secs(5); const SERVICE_LOCATION_CONNECT_RETRY_COUNT: u32 = 5; -const SERVICE_LOCATION_CONNECT_RETRY_DELAY_SECS: Duration = Duration::from_secs(30); +const SERVICE_LOCATION_CONNECT_RETRY_DELAY: Duration = Duration::from_secs(30); pub fn run() -> Result<(), windows_service::Error> { // Register generated `ffi_service_main` with the system and start the service, blocking @@ -169,10 +168,7 @@ fn run_service() -> Result<(), DaemonError> { } if attempt < SERVICE_LOCATION_CONNECT_RETRY_COUNT { - sleep(Duration::from_secs( - SERVICE_LOCATION_CONNECT_RETRY_DELAY_SECS, - )) - .await; + sleep(SERVICE_LOCATION_CONNECT_RETRY_DELAY).await; } } info!("Service location auto-connect task finished"); From 1a15004043f7552b7c3d7e89d5346cb25e5173da Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:20:13 +0200 Subject: [PATCH 10/11] Update .npmrc --- .npmrc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.npmrc b/.npmrc index fa4e0952..5608fccf 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ -strict-peer-dependencies=false \ No newline at end of file +strict-peer-dependencies=false +audit=false \ No newline at end of file From e30e9f0ed79dd45f6bf9d023eab744e67c86d4dc Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:23:06 +0200 Subject: [PATCH 11/11] Update lint.yaml --- .github/workflows/lint.yaml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 5603c40f..508a6ff8 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -1,22 +1,22 @@ -name: "lint" +name: 'lint' on: push: branches: - main - dev - - "release/**" + - 'release/**' paths-ignore: - - "*.md" - - "LICENSE" + - '*.md' + - 'LICENSE' pull_request: branches: - main - dev - - "release/**" + - 'release/**' paths-ignore: - - "*.md" - - "LICENSE" + - '*.md' + - 'LICENSE' jobs: lint-web: @@ -30,7 +30,7 @@ jobs: - uses: actions/setup-node@v6 with: - node-version: "24" + node-version: '24' - uses: pnpm/action-setup@v5 with: @@ -56,5 +56,6 @@ jobs: - name: Run Biome and Prettier Lint run: pnpm lint - - name: Audit - run: pnpm audit --prod + # TODO: Restore when it works again: https://github.com/pnpm/pnpm/issues/11265 + # - name: Audit + # run: pnpm audit --prod