From 5e7006fa3b8190066e09a884dab2dac8de98b4dc Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Thu, 23 Apr 2026 12:47:46 +0200 Subject: [PATCH 1/4] fix --- .../enterprise/service_locations/windows.rs | 33 +++++---- src-tauri/src/service/windows.rs | 70 ++++++++++--------- 2 files changed, 57 insertions(+), 46 deletions(-) diff --git a/src-tauri/src/enterprise/service_locations/windows.rs b/src-tauri/src/enterprise/service_locations/windows.rs index 2205eb9d..e6aa14d6 100644 --- a/src-tauri/src/enterprise/service_locations/windows.rs +++ b/src-tauri/src/enterprise/service_locations/windows.rs @@ -15,7 +15,6 @@ use defguard_wireguard_rs::{ }; use known_folders::get_known_folder_path; use log::{debug, error, warn}; -use tokio::time::sleep; use windows::{ core::PSTR, Win32::System::RemoteDesktop::{ @@ -55,7 +54,9 @@ const SERVICE_LOCATIONS_SUBDIR: &str = "service_locations"; /// /// 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( +/// +/// Runs on a dedicated OS thread because `NotifyAddrChange` is a blocking syscall. +pub(crate) fn watch_for_network_change( service_location_manager: Arc>, ) { loop { @@ -65,7 +66,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).await; + std::thread::sleep(NETWORK_CHANGE_MONITOR_RESTART_DELAY); continue; } @@ -73,14 +74,14 @@ pub(crate) async fn watch_for_network_change( "Network address change detected, waiting {NETWORK_STABILIZATION_DELAY:?}s for \ network to stabilize before attempting service location connections..." ); - sleep(NETWORK_STABILIZATION_DELAY).await; + std::thread::sleep(NETWORK_STABILIZATION_DELAY); debug!("Attempting to connect to service locations after network change"); - match service_location_manager + let connect_result = service_location_manager .write() .unwrap() - .connect_to_service_locations() - { + .connect_to_service_locations(); + match connect_result { Ok(_) => { debug!("Service location connect attempt after network change completed"); } @@ -91,11 +92,15 @@ pub(crate) async fn watch_for_network_change( } } -pub(crate) async fn watch_for_login_logoff( +/// Watches for user logon/logoff events and connects/disconnects pre-logon service locations +/// accordingly. +/// +/// Runs on a dedicated OS thread because `WTSWaitSystemEvent` is a blocking syscall. +pub(crate) fn watch_for_login_logoff( service_location_manager: Arc>, ) -> Result<(), ServiceLocationError> { loop { - let mut event_flags = 0; + let mut event_flags: u32 = 0; let success = unsafe { WTSWaitSystemEvent( Some(WTS_CURRENT_SERVER_HANDLE), @@ -110,7 +115,7 @@ pub(crate) async fn watch_for_login_logoff( } Err(err) => { error!("Failed waiting for login/logoff event: {err:?}"); - sleep(Duration::from_secs(LOGIN_LOGOFF_EVENT_RETRY_DELAY_SECS)).await; + std::thread::sleep(Duration::from_secs(LOGIN_LOGOFF_EVENT_RETRY_DELAY_SECS)); continue; } }; @@ -118,7 +123,6 @@ pub(crate) async fn watch_for_login_logoff( if event_flags & WTS_EVENT_LOGON != 0 { debug!("Detected user logon, attempting to auto-disconnect from service locations."); service_location_manager - .clone() .write() .unwrap() .disconnect_service_locations(Some(ServiceLocationMode::PreLogon))?; @@ -126,7 +130,6 @@ pub(crate) async fn watch_for_login_logoff( if event_flags & WTS_EVENT_LOGOFF != 0 { debug!("Detected user logoff, attempting to auto-connect to service locations."); service_location_manager - .clone() .write() .unwrap() .connect_to_service_locations()?; @@ -281,7 +284,11 @@ pub(crate) fn is_user_logged_in() -> bool { buffer.0 as *mut _, ); - // We found an active session with a username + // We found an active session with a username. + // Free the session list before returning to avoid a leak. + windows::Win32::System::RemoteDesktop::WTSFreeMemory( + pp_sessions as _, + ); return true; } } diff --git a/src-tauri/src/service/windows.rs b/src-tauri/src/service/windows.rs index dec55f23..00c5c0a0 100644 --- a/src-tauri/src/service/windows.rs +++ b/src-tauri/src/service/windows.rs @@ -115,17 +115,19 @@ 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. + // Spawn network change monitoring on a dedicated OS thread so the blocking + // NotifyAddrChange syscall does not stall Tokio's async worker threads. + // Register it first so no network event can be missed before the watcher is listening; + // the retry loop below is the backstop for any event that slips through the startup 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"); - watch_for_network_change(manager.clone()).await; - error!("Network change monitoring ended unexpectedly."); - }); + std::thread::Builder::new() + .name("network-change-monitor".to_string()) + .spawn(move || { + info!("Starting network change monitoring"); + watch_for_network_change(service_location_manager_clone); + error!("Network change monitoring ended unexpectedly."); + }) + .expect("Failed to spawn network change monitor thread"); // Spawn service location auto-connect task with retries. // Each attempt skips locations that are already connected, so it is safe to call @@ -174,32 +176,34 @@ fn run_service() -> Result<(), DaemonError> { info!("Service location auto-connect task finished"); }); - // Spawn login/logoff monitoring task, runs concurrently with the tasks above. + // Spawn login/logoff monitoring on a dedicated OS thread so the blocking + // WTSWaitSystemEvent syscall does not stall Tokio's async worker threads. 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 { - match watch_for_login_logoff(manager.clone()).await { - Ok(()) => { - warn!( - "Login/logoff event monitoring ended unexpectedly. Restarting in \ - {LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS:?}..." - ); - 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:?}...", - ); - sleep(LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS).await; - info!("Restarting login/logoff event monitoring"); + std::thread::Builder::new() + .name("login-logoff-monitor".to_string()) + .spawn(move || { + info!("Starting login/logoff event monitoring"); + loop { + match watch_for_login_logoff(service_location_manager_clone.clone()) { + Ok(()) => { + warn!( + "Login/logoff event monitoring ended unexpectedly. Restarting in \ + {LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS:?}..." + ); + std::thread::sleep(LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS); + } + Err(e) => { + error!( + "Error in login/logoff event monitoring: {e}. Restarting in \ + {LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS:?}...", + ); + std::thread::sleep(LOGIN_LOGOFF_MONITORING_RESTART_DELAY_SECS); + info!("Restarting login/logoff event monitoring"); + } } } - } - }); + }) + .expect("Failed to spawn login/logoff monitor thread"); // Spawn the main gRPC server task let service_location_manager_clone = service_location_manager.clone(); From 4f26abe534bf6a04724474af061375ea32d9f359 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Thu, 23 Apr 2026 12:55:50 +0200 Subject: [PATCH 2/4] build --- .github/workflows/build-windows-branch.yaml | 85 +++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 .github/workflows/build-windows-branch.yaml diff --git a/.github/workflows/build-windows-branch.yaml b/.github/workflows/build-windows-branch.yaml new file mode 100644 index 00000000..655a18ec --- /dev/null +++ b/.github/workflows/build-windows-branch.yaml @@ -0,0 +1,85 @@ +name: "Build Windows client" +on: + push: + branches: + - fix-blocking-calls + +jobs: + build-windows: + strategy: + fail-fast: false + matrix: + windows_runner: + - windows-latest + - windows-11-arm + include: + - windows_runner: windows-latest + cpu: x64 + - windows_runner: windows-11-arm + cpu: arm64 + runs-on: ${{ matrix.windows_runner }} + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + - uses: actions/setup-node@v6 + with: + node-version: "24" + - uses: pnpm/action-setup@v5 + with: + version: 10 + run_install: false + - name: Get pnpm store directory + shell: bash + run: echo "STORE_PATH=$(pnpm store path --silent)" >> ${GITHUB_ENV} + - uses: actions/cache@v5 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-build-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-build-store- + - name: Install deps + run: pnpm install --frozen-lockfile + - uses: dtolnay/rust-toolchain@stable + - name: Install Protoc + uses: arduino/setup-protoc@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Build packages + uses: tauri-apps/tauri-action@v0.5.23 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload unsigned bundle + uses: actions/upload-artifact@v4 + with: + name: unsigned-bundle-${{ matrix.cpu }} + path: src-tauri/target/release/bundle/msi/Defguard_1.6.8_${{ matrix.cpu }}_en-US.msi + + sign-bundle: + needs: + - build-windows + strategy: + fail-fast: false + matrix: + cpu: + - x64 + - arm64 + runs-on: + - self-hosted + - Linux + - X64 + steps: + - name: Download unsigned bundle + uses: actions/download-artifact@v4 + with: + name: unsigned-bundle-${{ matrix.cpu }} + - name: Sign bundle + run: osslsigncode sign -pkcs11module /srv/codesign/certum/sc30pkcs11-3.0.6.72-MS.so -pkcs11cert ${{ secrets.CODESIGN_KEYID }} -key ${{ secrets.CODESIGN_KEYID }} -pass ${{ secrets.CODESIGN_PIN }} -h sha256 -t http://time.certum.pl/ -in Defguard_1.6.8_${{ matrix.cpu }}_en-US.msi -out Defguard-signed.msi + - name: Rename signed bundle + run: mv Defguard-signed.msi Defguard_1.6.8_${{ matrix.cpu }}_en-US.msi + - name: Upload signed artifact + uses: actions/upload-artifact@v4 + with: + name: Defguard_1.6.8_${{ matrix.cpu }}_en-US + path: Defguard_1.6.8_${{ matrix.cpu }}_en-US.msi From 3a7261791db1f431f34844fb02c687fc75f8075e Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:16:01 +0200 Subject: [PATCH 3/4] Update Cargo.lock --- src-tauri/Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index fab3f62a..a24fc160 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -5626,9 +5626,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.12" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8279bb85272c9f10811ae6a6c547ff594d6a7f3c6c6b02ee9726d1d0dcfcdd06" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "aws-lc-rs", "ring", From 08853c43e35c7bb078574ecb2e864e697cd3579b Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:17:41 +0200 Subject: [PATCH 4/4] Delete build-windows-branch.yaml --- .github/workflows/build-windows-branch.yaml | 85 --------------------- 1 file changed, 85 deletions(-) delete mode 100644 .github/workflows/build-windows-branch.yaml diff --git a/.github/workflows/build-windows-branch.yaml b/.github/workflows/build-windows-branch.yaml deleted file mode 100644 index 655a18ec..00000000 --- a/.github/workflows/build-windows-branch.yaml +++ /dev/null @@ -1,85 +0,0 @@ -name: "Build Windows client" -on: - push: - branches: - - fix-blocking-calls - -jobs: - build-windows: - strategy: - fail-fast: false - matrix: - windows_runner: - - windows-latest - - windows-11-arm - include: - - windows_runner: windows-latest - cpu: x64 - - windows_runner: windows-11-arm - cpu: arm64 - runs-on: ${{ matrix.windows_runner }} - steps: - - uses: actions/checkout@v6 - with: - submodules: recursive - - uses: actions/setup-node@v6 - with: - node-version: "24" - - uses: pnpm/action-setup@v5 - with: - version: 10 - run_install: false - - name: Get pnpm store directory - shell: bash - run: echo "STORE_PATH=$(pnpm store path --silent)" >> ${GITHUB_ENV} - - uses: actions/cache@v5 - name: Setup pnpm cache - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-build-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-build-store- - - name: Install deps - run: pnpm install --frozen-lockfile - - uses: dtolnay/rust-toolchain@stable - - name: Install Protoc - uses: arduino/setup-protoc@v3 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Build packages - uses: tauri-apps/tauri-action@v0.5.23 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Upload unsigned bundle - uses: actions/upload-artifact@v4 - with: - name: unsigned-bundle-${{ matrix.cpu }} - path: src-tauri/target/release/bundle/msi/Defguard_1.6.8_${{ matrix.cpu }}_en-US.msi - - sign-bundle: - needs: - - build-windows - strategy: - fail-fast: false - matrix: - cpu: - - x64 - - arm64 - runs-on: - - self-hosted - - Linux - - X64 - steps: - - name: Download unsigned bundle - uses: actions/download-artifact@v4 - with: - name: unsigned-bundle-${{ matrix.cpu }} - - name: Sign bundle - run: osslsigncode sign -pkcs11module /srv/codesign/certum/sc30pkcs11-3.0.6.72-MS.so -pkcs11cert ${{ secrets.CODESIGN_KEYID }} -key ${{ secrets.CODESIGN_KEYID }} -pass ${{ secrets.CODESIGN_PIN }} -h sha256 -t http://time.certum.pl/ -in Defguard_1.6.8_${{ matrix.cpu }}_en-US.msi -out Defguard-signed.msi - - name: Rename signed bundle - run: mv Defguard-signed.msi Defguard_1.6.8_${{ matrix.cpu }}_en-US.msi - - name: Upload signed artifact - uses: actions/upload-artifact@v4 - with: - name: Defguard_1.6.8_${{ matrix.cpu }}_en-US - path: Defguard_1.6.8_${{ matrix.cpu }}_en-US.msi