From 0a54df6fe0bd214504fa4c8fa1e3c73c7de4055f Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Wed, 10 Dec 2025 20:21:32 +0530 Subject: [PATCH 01/73] Fixed #32 parentheses validation added --- Windows/gui/ViewModels/ProxyRulesViewModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Windows/gui/ViewModels/ProxyRulesViewModel.cs b/Windows/gui/ViewModels/ProxyRulesViewModel.cs index 58e571e..f18170b 100644 --- a/Windows/gui/ViewModels/ProxyRulesViewModel.cs +++ b/Windows/gui/ViewModels/ProxyRulesViewModel.cs @@ -136,9 +136,9 @@ public ProxyRulesViewModel(ObservableCollection proxyRules, Action Date: Wed, 10 Dec 2025 20:40:25 +0530 Subject: [PATCH 02/73] added local workflow release , WIP build optimization - add strip, link optimization for C #35 --- .github/workflows/release-windows.yml | 53 +++++++++++++++++++++++++++ Windows/compile.ps1 | 15 ++++++-- 2 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/release-windows.yml diff --git a/.github/workflows/release-windows.yml b/.github/workflows/release-windows.yml new file mode 100644 index 0000000..f3b1dfc --- /dev/null +++ b/.github/workflows/release-windows.yml @@ -0,0 +1,53 @@ +name: Release ProxyBridge Windows + +on: + release: + types: [created] + +jobs: + build-and-release: + runs-on: self-hosted + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '9.0.x' + + - name: Verify WinDivert installation + run: | + if (Test-Path "C:\WinDivert-2.2.2-A") { + Write-Host "WinDivert found at C:\WinDivert-2.2.2-A" + } else { + Write-Error "WinDivert not found. Please install WinDivert 2.2.2-A at C:\WinDivert-2.2.2-A" + exit 1 + } + shell: pwsh + + - name: Build and sign project + run: .\Windows\compile.ps1 + shell: pwsh + + - name: List built files + run: | + Write-Host "`nBuild artifacts:" + Get-ChildItem Windows/output -Recurse | ForEach-Object { + $size = [math]::Round($_.Length/1MB, 2) + Write-Host " $($_.Name) - $size MB" + } + shell: pwsh + + - name: Upload installer to release + uses: softprops/action-gh-release@v1 + with: + files: Windows/output/ProxyBridge-Setup-*.exe + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: ProxyBridge-Release-${{ github.event.release.tag_name }} + path: Windows/output/ + retention-days: 90 diff --git a/Windows/compile.ps1 b/Windows/compile.ps1 index 1ed6f04..dd3a418 100644 --- a/Windows/compile.ps1 +++ b/Windows/compile.ps1 @@ -48,11 +48,11 @@ function Compile-MSVC { } } - $cmd = "cl.exe /nologo /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /DPROXYBRIDGE_EXPORTS " + + $cmd = "cl.exe /nologo /O2 /GL /W3 /D_CRT_SECURE_NO_WARNINGS /DPROXYBRIDGE_EXPORTS " + "/I`"$WinDivertPath\include`" " + "$SourcePath\$SourceFile " + "/LD " + - "/link /LIBPATH:`"$WinDivertPath\$Arch`" " + + "/link /LTCG /OPT:REF /OPT:ICF /DEBUG:NONE /LIBPATH:`"$WinDivertPath\$Arch`" " + "WinDivert.lib ws2_32.lib iphlpapi.lib " + "/OUT:$OutputDLL" @@ -77,7 +77,7 @@ function Compile-GCC { Write-Host "GCC found: $($gccVersion[0])" -ForegroundColor Cyan - $cmd = "gcc -shared -O2 -Wall -D_WIN32_WINNT=0x0601 -DPROXYBRIDGE_EXPORTS " + + $cmd = "gcc -shared -O2 -flto -s -Wall -D_WIN32_WINNT=0x0601 -DPROXYBRIDGE_EXPORTS " + "-I`"$WinDivertPath\include`" " + "$SourcePath\$SourceFile " + "-L`"$WinDivertPath\$Arch`" " + @@ -155,6 +155,15 @@ if ($Compiler -eq 'auto') { if ($success) { Write-Host "`nCompilation SUCCESSFUL!" -ForegroundColor Green + Write-Host "`nCleaning up intermediate files..." -ForegroundColor Yellow + $intermediateFiles = @("*.obj", "*.exp", "*.lib", "ProxyBridge.obj") + foreach ($pattern in $intermediateFiles) { + Get-ChildItem -Path . -Filter $pattern -ErrorAction SilentlyContinue | ForEach-Object { + Remove-Item $_.FullName -Force + Write-Host " Removed: $($_.Name)" -ForegroundColor Gray + } + } + Write-Host "`nMoving files to output directory..." -ForegroundColor Green Move-Item $OutputDLL -Destination $OutputDir -Force Write-Host " Moved: $OutputDLL -> $OutputDir\" -ForegroundColor Gray From c5495f4a88274be185e58b93822d53812f185c58 Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Sun, 14 Dec 2025 19:52:21 +0530 Subject: [PATCH 03/73] Fixed mac signing #29 --- .../extension/extensionRelease.entitlements | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 MacOS/ProxyBridge/extension/extensionRelease.entitlements diff --git a/MacOS/ProxyBridge/extension/extensionRelease.entitlements b/MacOS/ProxyBridge/extension/extensionRelease.entitlements new file mode 100644 index 0000000..7ad7f59 --- /dev/null +++ b/MacOS/ProxyBridge/extension/extensionRelease.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.developer.networking.networkextension + + app-proxy-provider-systemextension + + com.apple.security.application-groups + + group.com.interceptsuite.ProxyBridge + + + From 5a1a89fe2cf273397a3c06f8f9a66ef6d7ae733f Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Sun, 14 Dec 2025 19:52:29 +0530 Subject: [PATCH 04/73] #29 --- .gitignore | 1 + .../ProxyBridge.xcodeproj/project.pbxproj | 7 +- .../xcschemes/ProxyBridge.xcscheme | 78 +++++++++++++++++ .../ProxyBridgeRelease.entitlements | 4 + MacOS/ProxyBridge/build.sh | 86 ++++--------------- .../extension/extension.entitlements | 2 +- .../extension/extensionRelease.entitlements | 4 + 7 files changed, 111 insertions(+), 71 deletions(-) create mode 100644 MacOS/ProxyBridge/ProxyBridge.xcodeproj/xcshareddata/xcschemes/ProxyBridge.xcscheme diff --git a/.gitignore b/.gitignore index f5bc961..fe2b894 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ Windows/output/ProxyBridge_CLI.pdb MacOS/ProxyBridge/.env MacOS/ProxyBridge/output/ MacOS/ProxyBridge/build/ +MacOS/ProxyBridge/provision/** # Xcode xcuserdata/ diff --git a/MacOS/ProxyBridge/ProxyBridge.xcodeproj/project.pbxproj b/MacOS/ProxyBridge/ProxyBridge.xcodeproj/project.pbxproj index eebd57f..99ff2f9 100644 --- a/MacOS/ProxyBridge/ProxyBridge.xcodeproj/project.pbxproj +++ b/MacOS/ProxyBridge/ProxyBridge.xcodeproj/project.pbxproj @@ -300,7 +300,7 @@ MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; + ONLY_ACTIVE_ARCH = NO; SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -358,6 +358,7 @@ MACOSX_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; }; @@ -503,7 +504,8 @@ 8466A1F72EC63C5600A8C2E5 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_ENTITLEMENTS = extension/extension.entitlements; + CODE_SIGN_ENTITLEMENTS = extension/extensionRelease.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = L4HJT32Z59; @@ -531,6 +533,7 @@ MARKETING_VERSION = 3.0; PRODUCT_BUNDLE_IDENTIFIER = com.interceptsuite.ProxyBridge.extension; PRODUCT_NAME = "$(inherited)"; + PROVISIONING_PROFILE_SPECIFIER = ""; REGISTER_APP_GROUPS = YES; SKIP_INSTALL = YES; STRING_CATALOG_GENERATE_SYMBOLS = YES; diff --git a/MacOS/ProxyBridge/ProxyBridge.xcodeproj/xcshareddata/xcschemes/ProxyBridge.xcscheme b/MacOS/ProxyBridge/ProxyBridge.xcodeproj/xcshareddata/xcschemes/ProxyBridge.xcscheme new file mode 100644 index 0000000..71faa96 --- /dev/null +++ b/MacOS/ProxyBridge/ProxyBridge.xcodeproj/xcshareddata/xcschemes/ProxyBridge.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MacOS/ProxyBridge/ProxyBridge/ProxyBridgeRelease.entitlements b/MacOS/ProxyBridge/ProxyBridge/ProxyBridgeRelease.entitlements index 70d7710..cdc9491 100644 --- a/MacOS/ProxyBridge/ProxyBridge/ProxyBridgeRelease.entitlements +++ b/MacOS/ProxyBridge/ProxyBridge/ProxyBridgeRelease.entitlements @@ -4,6 +4,10 @@ com.apple.developer.networking.networkextension + dns-proxy + app-proxy-provider-systemextension + packet-tunnel-provider + content-filter-provider app-proxy-provider com.apple.developer.system-extension.install diff --git a/MacOS/ProxyBridge/build.sh b/MacOS/ProxyBridge/build.sh index ac62cf2..6f45111 100755 --- a/MacOS/ProxyBridge/build.sh +++ b/MacOS/ProxyBridge/build.sh @@ -13,90 +13,39 @@ SCHEME_NAME="ProxyBridge" OUTPUT_DIR="$SCRIPT_DIR/output" PKG_NAME="ProxyBridge-v3.0-Universal-Installer.pkg" PKG_PATH="$OUTPUT_DIR/$PKG_NAME" -ARCHIVE_PATH="$SCRIPT_DIR/build/${PROJECT_NAME}.xcarchive" -EXPORT_PATH="$SCRIPT_DIR/build/Export" APP_NAME="${PROJECT_NAME}.app" +APP_PATH="$OUTPUT_DIR/$APP_NAME" LICENSE_FILE="$SCRIPT_DIR/../../LICENSE" -SIGN_APP=${SIGN_APP:-""} SIGN_PKG=${SIGN_PKG:-""} NOTARIZE=${NOTARIZE:-""} APPLE_ID=${APPLE_ID:-""} TEAM_ID=${TEAM_ID:-""} APP_PASSWORD=${APP_PASSWORD:-""} -echo "Building ProxyBridge Universal Installer..." +echo "Creating PKG installer from notarized app..." -if [ ! -d "$OUTPUT_DIR" ]; then - mkdir -p "$OUTPUT_DIR" +# Check if app exists +if [ ! -d "$APP_PATH" ]; then + echo "Error: $APP_NAME not found in $OUTPUT_DIR" + echo "Please export the notarized app from Xcode to the output folder first." + exit 1 fi +# Remove old PKG if exists if [ -f "$PKG_PATH" ]; then + echo "Removing old PKG..." rm -f "$PKG_PATH" fi -echo "Cleaning build directory..." -rm -rf "$SCRIPT_DIR/build" -mkdir -p "$SCRIPT_DIR/build" - -echo "Building universal binary (ARM64 + x86_64)..." -xcodebuild archive \ - -project "${PROJECT_NAME}.xcodeproj" \ - -scheme "$SCHEME_NAME" \ - -configuration Release \ - -archivePath "$ARCHIVE_PATH" \ - -destination "generic/platform=macOS" \ - ARCHS="arm64 x86_64" \ - ONLY_ACTIVE_ARCH=NO \ - CODE_SIGN_IDENTITY="" \ - CODE_SIGNING_REQUIRED=NO \ - CODE_SIGNING_ALLOWED=NO - -if [ $? -ne 0 ]; then - echo "Build failed!" - exit 1 -fi - -echo "Build completed successfully!" - -echo "Exporting archive..." -xcodebuild -exportArchive \ - -archivePath "$ARCHIVE_PATH" \ - -exportPath "$EXPORT_PATH" \ - -exportOptionsPlist "${SCRIPT_DIR}/ExportOptions.plist" - -if [ $? -ne 0 ]; then - echo "Export failed, copying app manually..." - mkdir -p "$EXPORT_PATH" - cp -R "${ARCHIVE_PATH}/Products/Applications/${APP_NAME}" "$EXPORT_PATH/" -fi - -if [ ! -d "$EXPORT_PATH/$APP_NAME" ]; then - echo "App not found at $EXPORT_PATH/$APP_NAME" - exit 1 -fi +echo "Verifying app signature..." +codesign --verify --verbose=2 --deep --strict "$APP_PATH" echo "Verifying universal binary..." -lipo -info "$EXPORT_PATH/$APP_NAME/Contents/MacOS/$PROJECT_NAME" +lipo -info "$APP_PATH/Contents/MacOS/$PROJECT_NAME" -if [ -n "$SIGN_APP" ]; then - echo "Signing application..." - - codesign --force --sign "$SIGN_APP" \ - --timestamp \ - --options runtime \ - --entitlements "$SCRIPT_DIR/extension/extension.entitlements" \ - "$EXPORT_PATH/$APP_NAME/Contents/Library/SystemExtensions/com.interceptsuite.ProxyBridge.extension.systemextension" - - codesign --force --sign "$SIGN_APP" \ - --timestamp \ - --options runtime \ - --entitlements "$SCRIPT_DIR/ProxyBridge/ProxyBridgeRelease.entitlements" \ - "$EXPORT_PATH/$APP_NAME" - - echo "Verifying application signature..." - codesign --verify --verbose=2 "$EXPORT_PATH/$APP_NAME" -fi +echo "Verifying universal binary..." +lipo -info "$APP_PATH/Contents/MacOS/$PROJECT_NAME" echo "Creating PKG installer..." @@ -104,13 +53,14 @@ COMPONENT_DIR="$SCRIPT_DIR/build/component" TEMP_PKG="$SCRIPT_DIR/build/temp.pkg" DISTRIBUTION_FILE="$SCRIPT_DIR/build/distribution.xml" +mkdir -p "$SCRIPT_DIR/build" mkdir -p "$COMPONENT_DIR" -cp -R "$EXPORT_PATH/$APP_NAME" "$COMPONENT_DIR/" +cp -R "$APP_PATH" "$COMPONENT_DIR/" pkgbuild \ --root "$COMPONENT_DIR" \ --identifier "com.interceptsuite.${PROJECT_NAME}" \ - --version "1.0.0" \ + --version "3.0.0" \ --install-location "/Applications" \ "$TEMP_PKG" @@ -135,7 +85,7 @@ cat > "$DISTRIBUTION_FILE" << 'EOF' - temp.pkg + temp.pkg EOF diff --git a/MacOS/ProxyBridge/extension/extension.entitlements b/MacOS/ProxyBridge/extension/extension.entitlements index 2658e1b..7ad7f59 100644 --- a/MacOS/ProxyBridge/extension/extension.entitlements +++ b/MacOS/ProxyBridge/extension/extension.entitlements @@ -4,7 +4,7 @@ com.apple.developer.networking.networkextension - app-proxy-provider + app-proxy-provider-systemextension com.apple.security.application-groups diff --git a/MacOS/ProxyBridge/extension/extensionRelease.entitlements b/MacOS/ProxyBridge/extension/extensionRelease.entitlements index 7ad7f59..1274914 100644 --- a/MacOS/ProxyBridge/extension/extensionRelease.entitlements +++ b/MacOS/ProxyBridge/extension/extensionRelease.entitlements @@ -4,7 +4,11 @@ com.apple.developer.networking.networkextension + dns-proxy + content-filter-provider app-proxy-provider-systemextension + app-proxy-provider + packet-tunnel-provider com.apple.security.application-groups From c6b09d6f265757e25b701c343212dfc241cda19c Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Sun, 14 Dec 2025 21:01:14 +0530 Subject: [PATCH 05/73] fixed sign --- .../ProxyBridge.xcodeproj/project.pbxproj | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/MacOS/ProxyBridge/ProxyBridge.xcodeproj/project.pbxproj b/MacOS/ProxyBridge/ProxyBridge.xcodeproj/project.pbxproj index 99ff2f9..550d9b5 100644 --- a/MacOS/ProxyBridge/ProxyBridge.xcodeproj/project.pbxproj +++ b/MacOS/ProxyBridge/ProxyBridge.xcodeproj/project.pbxproj @@ -278,7 +278,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = L4HJT32Z59; + DEVELOPMENT_TEAM = ""; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -342,7 +342,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = L4HJT32Z59; + DEVELOPMENT_TEAM = ""; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -370,7 +370,6 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = ProxyBridge/ProxyBridge.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; @@ -418,11 +417,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = ProxyBridge/ProxyBridgeRelease.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = L4HJT32Z59; + DEVELOPMENT_TEAM = ""; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; ENABLE_INCOMING_NETWORK_CONNECTIONS = YES; @@ -466,7 +464,7 @@ CODE_SIGN_ENTITLEMENTS = extension/extension.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = L4HJT32Z59; + DEVELOPMENT_TEAM = ""; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; ENABLE_INCOMING_NETWORK_CONNECTIONS = YES; @@ -505,10 +503,9 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = extension/extensionRelease.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = L4HJT32Z59; + DEVELOPMENT_TEAM = ""; ENABLE_APP_SANDBOX = YES; ENABLE_HARDENED_RUNTIME = YES; ENABLE_INCOMING_NETWORK_CONNECTIONS = YES; From 3fe011ec667b11cefd7ad0834cff53ed0e492ed2 Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Sun, 14 Dec 2025 21:11:21 +0530 Subject: [PATCH 06/73] removed apple sign --- .github/workflows/build-mac.yml | 30 ++++--- MacOS/ProxyBridge/build.sh | 148 +++++++------------------------- 2 files changed, 53 insertions(+), 125 deletions(-) diff --git a/.github/workflows/build-mac.yml b/.github/workflows/build-mac.yml index fa13ee7..6156a3c 100644 --- a/.github/workflows/build-mac.yml +++ b/.github/workflows/build-mac.yml @@ -22,19 +22,29 @@ jobs: with: xcode-version: latest-stable - - name: Build ProxyBridge + - name: Build ProxyBridge (Universal Binary) run: | cd MacOS/ProxyBridge - chmod +x build.sh - ./build.sh - env: - SIGN_APP: "" - SIGN_PKG: "" - NOTARIZE: "" + xcodebuild \ + -project ProxyBridge.xcodeproj \ + -scheme ProxyBridge \ + -configuration Release \ + -derivedDataPath build/DerivedData \ + ARCHS="arm64 x86_64" \ + ONLY_ACTIVE_ARCH=NO \ + CODE_SIGN_IDENTITY="-" \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGNING_ALLOWED=NO \ + clean build - - name: Upload PKG artifact + - name: Verify Universal Binary + run: | + cd MacOS/ProxyBridge + lipo -info build/DerivedData/Build/Products/Release/ProxyBridge.app/Contents/MacOS/ProxyBridge + + - name: Upload App artifact uses: actions/upload-artifact@v4 with: - name: ProxyBridge-macOS - path: MacOS/ProxyBridge/output/ProxyBridge-Installer.pkg + name: ProxyBridge-macOS-Universal + path: MacOS/ProxyBridge/build/DerivedData/Build/Products/Release/ProxyBridge.app retention-days: 30 diff --git a/MacOS/ProxyBridge/build.sh b/MacOS/ProxyBridge/build.sh index 6f45111..8fd25d5 100755 --- a/MacOS/ProxyBridge/build.sh +++ b/MacOS/ProxyBridge/build.sh @@ -4,76 +4,37 @@ set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -if [ -f "$SCRIPT_DIR/.env" ]; then - source "$SCRIPT_DIR/.env" -fi - -PROJECT_NAME="ProxyBridge" -SCHEME_NAME="ProxyBridge" -OUTPUT_DIR="$SCRIPT_DIR/output" -PKG_NAME="ProxyBridge-v3.0-Universal-Installer.pkg" -PKG_PATH="$OUTPUT_DIR/$PKG_NAME" -APP_NAME="${PROJECT_NAME}.app" -APP_PATH="$OUTPUT_DIR/$APP_NAME" -LICENSE_FILE="$SCRIPT_DIR/../../LICENSE" - -SIGN_PKG=${SIGN_PKG:-""} -NOTARIZE=${NOTARIZE:-""} -APPLE_ID=${APPLE_ID:-""} -TEAM_ID=${TEAM_ID:-""} -APP_PASSWORD=${APP_PASSWORD:-""} - -echo "Creating PKG installer from notarized app..." - -# Check if app exists -if [ ! -d "$APP_PATH" ]; then - echo "Error: $APP_NAME not found in $OUTPUT_DIR" - echo "Please export the notarized app from Xcode to the output folder first." - exit 1 -fi - -# Remove old PKG if exists -if [ -f "$PKG_PATH" ]; then - echo "Removing old PKG..." - rm -f "$PKG_PATH" -fi - -echo "Verifying app signature..." -codesign --verify --verbose=2 --deep --strict "$APP_PATH" - -echo "Verifying universal binary..." -lipo -info "$APP_PATH/Contents/MacOS/$PROJECT_NAME" - -echo "Verifying universal binary..." -lipo -info "$APP_PATH/Contents/MacOS/$PROJECT_NAME" - -echo "Creating PKG installer..." - -COMPONENT_DIR="$SCRIPT_DIR/build/component" -TEMP_PKG="$SCRIPT_DIR/build/temp.pkg" -DISTRIBUTION_FILE="$SCRIPT_DIR/build/distribution.xml" - -mkdir -p "$SCRIPT_DIR/build" -mkdir -p "$COMPONENT_DIR" -cp -R "$APP_PATH" "$COMPONENT_DIR/" +rm -rf "$SCRIPT_DIR/output" +mkdir -p "$SCRIPT_DIR/output" + +xcodebuild \ + -project ProxyBridge.xcodeproj \ + -scheme ProxyBridge \ + -configuration Release \ + -derivedDataPath build/DerivedData \ + ARCHS="arm64 x86_64" \ + ONLY_ACTIVE_ARCH=NO \ + CODE_SIGN_IDENTITY="-" \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGNING_ALLOWED=NO \ + clean build + +cp -R build/DerivedData/Build/Products/Release/ProxyBridge.app output/ + +mkdir -p build/component +cp -R output/ProxyBridge.app build/component/ pkgbuild \ - --root "$COMPONENT_DIR" \ - --identifier "com.interceptsuite.${PROJECT_NAME}" \ - --version "3.0.0" \ - --install-location "/Applications" \ - "$TEMP_PKG" - -if [ $? -ne 0 ]; then - echo "PKG creation failed!" - exit 1 -fi + --root build/component \ + --identifier com.interceptsuite.ProxyBridge \ + --version 3.0.0 \ + --install-location /Applications \ + build/temp.pkg -cat > "$DISTRIBUTION_FILE" << 'EOF' +cat > build/distribution.xml << 'EOF' ProxyBridge - @@ -89,56 +50,13 @@ cat > "$DISTRIBUTION_FILE" << 'EOF' EOF -if [ -n "$SIGN_PKG" ]; then - echo "Signing PKG installer..." - productbuild \ - --distribution "$DISTRIBUTION_FILE" \ - --resources "$SCRIPT_DIR/../../" \ - --package-path "$SCRIPT_DIR/build" \ - --sign "$SIGN_PKG" \ - "$PKG_PATH" -else - echo "Creating unsigned PKG..." - productbuild \ - --distribution "$DISTRIBUTION_FILE" \ - --resources "$SCRIPT_DIR/../../" \ - --package-path "$SCRIPT_DIR/build" \ - "$PKG_PATH" -fi - -if [ $? -ne 0 ]; then - echo "Product PKG creation failed!" - exit 1 -fi - -echo "PKG installer created successfully!" -echo "PKG Location: $PKG_PATH" -echo "PKG Size: $(du -h "$PKG_PATH" | cut -f1)" - -if [ -n "$NOTARIZE" ] && [ -n "$APPLE_ID" ] && [ -n "$APP_PASSWORD" ] && [ -n "$TEAM_ID" ]; then - echo "Notarizing PKG..." - - xcrun notarytool submit "$PKG_PATH" \ - --apple-id "$APPLE_ID" \ - --password "$APP_PASSWORD" \ - --team-id "$TEAM_ID" \ - --wait - - if [ $? -eq 0 ]; then - echo "Stapling notarization ticket to PKG..." - xcrun stapler staple "$PKG_PATH" - - if [ $? -eq 0 ]; then - echo "PKG notarized and stapled successfully!" - else - echo "Warning: Stapling failed but notarization succeeded" - fi - else - echo "Warning: Notarization failed" - fi -fi +productbuild \ + --distribution build/distribution.xml \ + --package-path build \ + output/ProxyBridge-Installer.pkg -echo "Cleaning up build artifacts..." -rm -rf "$SCRIPT_DIR/build" +rm -rf build -echo "Build complete!" +echo "✓ Build complete" +echo " App: output/ProxyBridge.app" +echo " PKG: output/ProxyBridge-Installer.pkg" From 2696385cce3afca5679fce65409c8e4714db606b Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Sun, 14 Dec 2025 21:43:50 +0530 Subject: [PATCH 07/73] added action sign --- .github/workflows/build-mac.yml | 105 ++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-mac.yml b/.github/workflows/build-mac.yml index 6156a3c..c4c7a92 100644 --- a/.github/workflows/build-mac.yml +++ b/.github/workflows/build-mac.yml @@ -22,7 +22,29 @@ jobs: with: xcode-version: latest-stable - - name: Build ProxyBridge (Universal Binary) + - name: Import Code Signing Certificates + env: + DEVELOPER_ID_APP_CERT: ${{ secrets.DEVELOPER_ID_APP_CERT }} + DEVELOPER_ID_APP_CERT_PASSWORD: ${{ secrets.DEVELOPER_ID_APP_CERT_PASSWORD }} + DEVELOPER_ID_INSTALLER_CERT: ${{ secrets.DEVELOPER_ID_INSTALLER_CERT }} + DEVELOPER_ID_INSTALLER_CERT_PASSWORD: ${{ secrets.DEVELOPER_ID_INSTALLER_CERT_PASSWORD }} + run: | + security create-keychain -p actions build.keychain + security default-keychain -s build.keychain + security unlock-keychain -p actions build.keychain + security set-keychain-settings -t 3600 -u build.keychain + + echo "$DEVELOPER_ID_APP_CERT" | base64 --decode > app_cert.p12 + security import app_cert.p12 -k build.keychain -P "$DEVELOPER_ID_APP_CERT_PASSWORD" -T /usr/bin/codesign + + echo "$DEVELOPER_ID_INSTALLER_CERT" | base64 --decode > installer_cert.p12 + security import installer_cert.p12 -k build.keychain -P "$DEVELOPER_ID_INSTALLER_CERT_PASSWORD" -T /usr/bin/productsign + + security set-key-partition-list -S apple-tool:,apple: -s -k actions build.keychain + + rm app_cert.p12 installer_cert.p12 + + - name: Build Universal Binary run: | cd MacOS/ProxyBridge xcodebuild \ @@ -37,14 +59,85 @@ jobs: CODE_SIGNING_ALLOWED=NO \ clean build - - name: Verify Universal Binary + - name: Sign App and Extension + run: | + cd MacOS/ProxyBridge + + codesign --force --deep --sign "${{ secrets.DEVELOPER_ID_IDENTITY }}" \ + --entitlements extension/extensionRelease.entitlements \ + --options runtime \ + build/DerivedData/Build/Products/Release/ProxyBridge.app/Contents/Library/SystemExtensions/com.interceptsuite.ProxyBridge.extension.systemextension + + codesign --force --deep --sign "${{ secrets.DEVELOPER_ID_IDENTITY }}" \ + --entitlements ProxyBridge/ProxyBridgeRelease.entitlements \ + --options runtime \ + build/DerivedData/Build/Products/Release/ProxyBridge.app + + codesign --verify --verbose=2 --deep --strict build/DerivedData/Build/Products/Release/ProxyBridge.app + + - name: Create and Sign PKG run: | cd MacOS/ProxyBridge - lipo -info build/DerivedData/Build/Products/Release/ProxyBridge.app/Contents/MacOS/ProxyBridge + mkdir -p build/component output + cp -R build/DerivedData/Build/Products/Release/ProxyBridge.app build/component/ + + pkgbuild \ + --root build/component \ + --identifier com.interceptsuite.ProxyBridge \ + --version 3.0.0 \ + --install-location /Applications \ + build/temp.pkg + + cat > build/distribution.xml << 'EOF' + + + ProxyBridge + + + + + + + + + + + + temp.pkg + + EOF + + productbuild \ + --distribution build/distribution.xml \ + --package-path build \ + --sign "${{ secrets.DEVELOPER_ID_INSTALLER_IDENTITY }}" \ + output/ProxyBridge-Installer.pkg - - name: Upload App artifact + - name: Notarize PKG + env: + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} + TEAM_ID: ${{ secrets.TEAM_ID }} + run: | + cd MacOS/ProxyBridge + + xcrun notarytool submit output/ProxyBridge-Installer.pkg \ + --apple-id "$APPLE_ID" \ + --password "$APPLE_PASSWORD" \ + --team-id "$TEAM_ID" \ + --wait + + xcrun stapler staple output/ProxyBridge-Installer.pkg + + - name: Upload PKG uses: actions/upload-artifact@v4 with: - name: ProxyBridge-macOS-Universal - path: MacOS/ProxyBridge/build/DerivedData/Build/Products/Release/ProxyBridge.app + name: ProxyBridge-macOS-Signed + path: MacOS/ProxyBridge/output/ProxyBridge-Installer.pkg retention-days: 30 + + - name: Cleanup + if: always() + run: | + security delete-keychain build.keychain || true + security default-keychain -s login.keychain || true From 64257ed8a30758e7b6d4174190042fe4948d9da5 Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Sun, 14 Dec 2025 21:54:26 +0530 Subject: [PATCH 08/73] fixed sign workflow --- .github/workflows/build-mac.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build-mac.yml b/.github/workflows/build-mac.yml index c4c7a92..1cc6706 100644 --- a/.github/workflows/build-mac.yml +++ b/.github/workflows/build-mac.yml @@ -107,10 +107,13 @@ jobs: EOF + security unlock-keychain -p actions build.keychain + productbuild \ --distribution build/distribution.xml \ --package-path build \ --sign "${{ secrets.DEVELOPER_ID_INSTALLER_IDENTITY }}" \ + --timestamp \ output/ProxyBridge-Installer.pkg - name: Notarize PKG From 920bd0e27e306cdd79ef38db37ceec8fcbe10ee5 Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Sun, 14 Dec 2025 22:01:06 +0530 Subject: [PATCH 09/73] fixed workflow --- .github/workflows/build-mac.yml | 61 ++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build-mac.yml b/.github/workflows/build-mac.yml index 1cc6706..f008e28 100644 --- a/.github/workflows/build-mac.yml +++ b/.github/workflows/build-mac.yml @@ -28,22 +28,36 @@ jobs: DEVELOPER_ID_APP_CERT_PASSWORD: ${{ secrets.DEVELOPER_ID_APP_CERT_PASSWORD }} DEVELOPER_ID_INSTALLER_CERT: ${{ secrets.DEVELOPER_ID_INSTALLER_CERT }} DEVELOPER_ID_INSTALLER_CERT_PASSWORD: ${{ secrets.DEVELOPER_ID_INSTALLER_CERT_PASSWORD }} + TEAM_ID: ${{ secrets.TEAM_ID }} run: | + security list-keychains -d user -s login.keychain + security create-keychain -p actions build.keychain - security default-keychain -s build.keychain + security list-keychains -d user -s build.keychain $(security list-keychains -d user | sed s/\"//g) + security set-keychain-settings build.keychain security unlock-keychain -p actions build.keychain - security set-keychain-settings -t 3600 -u build.keychain echo "$DEVELOPER_ID_APP_CERT" | base64 --decode > app_cert.p12 - security import app_cert.p12 -k build.keychain -P "$DEVELOPER_ID_APP_CERT_PASSWORD" -T /usr/bin/codesign + security import app_cert.p12 -k build.keychain -P "$DEVELOPER_ID_APP_CERT_PASSWORD" -A -T /usr/bin/codesign -T /usr/bin/productsign echo "$DEVELOPER_ID_INSTALLER_CERT" | base64 --decode > installer_cert.p12 - security import installer_cert.p12 -k build.keychain -P "$DEVELOPER_ID_INSTALLER_CERT_PASSWORD" -T /usr/bin/productsign + security import installer_cert.p12 -k build.keychain -P "$DEVELOPER_ID_INSTALLER_CERT_PASSWORD" -A -T /usr/bin/codesign -T /usr/bin/productsign - security set-key-partition-list -S apple-tool:,apple: -s -k actions build.keychain + security set-key-partition-list -S apple-tool:,apple: -s -k actions -D "$TEAM_ID" -t private build.keychain rm app_cert.p12 installer_cert.p12 + - name: Setup Notarization Profile + env: + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} + TEAM_ID: ${{ secrets.TEAM_ID }} + run: | + xcrun notarytool store-credentials "notarytool-profile" \ + --apple-id "$APPLE_ID" \ + --team-id "$TEAM_ID" \ + --password "$APPLE_PASSWORD" + - name: Build Universal Binary run: | cd MacOS/ProxyBridge @@ -60,22 +74,28 @@ jobs: clean build - name: Sign App and Extension + env: + DEVELOPER_ID_IDENTITY: ${{ secrets.DEVELOPER_ID_IDENTITY }} run: | cd MacOS/ProxyBridge - codesign --force --deep --sign "${{ secrets.DEVELOPER_ID_IDENTITY }}" \ + codesign --force --options runtime --timestamp \ + --sign "$DEVELOPER_ID_IDENTITY" \ --entitlements extension/extensionRelease.entitlements \ - --options runtime \ build/DerivedData/Build/Products/Release/ProxyBridge.app/Contents/Library/SystemExtensions/com.interceptsuite.ProxyBridge.extension.systemextension - codesign --force --deep --sign "${{ secrets.DEVELOPER_ID_IDENTITY }}" \ + codesign --force --options runtime --timestamp \ + --sign "$DEVELOPER_ID_IDENTITY" \ --entitlements ProxyBridge/ProxyBridgeRelease.entitlements \ - --options runtime \ build/DerivedData/Build/Products/Release/ProxyBridge.app - codesign --verify --verbose=2 --deep --strict build/DerivedData/Build/Products/Release/ProxyBridge.app + codesign --verify --deep --strict build/DerivedData/Build/Products/Release/ProxyBridge.app - name: Create and Sign PKG + env: + TEAM_ID: ${{ secrets.TEAM_ID }} + run: | + DEVELOPER_ID_INSTALLER_IDENTITY: ${{ secrets.DEVELOPER_ID_INSTALLER_IDENTITY }} run: | cd MacOS/ProxyBridge mkdir -p build/component output @@ -107,30 +127,23 @@ jobs: EOF - security unlock-keychain -p actions build.keychain - - productbuild \ - --distribution build/distribution.xml \ - --package-path build \ - --sign "${{ secrets.DEVELOPER_ID_INSTALLER_IDENTITY }}" \ - --timestamp \ + productsign \ + --keychain build.keychain \ + --sign "$DEVELOPER_ID_INSTALLER output/ProxyBridge-Installer.pkg + + pkgutil --check-signature output/ProxyBridge-Installer.pkg - name: Notarize PKG - env: - APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} - TEAM_ID: ${{ secrets.TEAM_ID }} run: | cd MacOS/ProxyBridge xcrun notarytool submit output/ProxyBridge-Installer.pkg \ - --apple-id "$APPLE_ID" \ - --password "$APPLE_PASSWORD" \ - --team-id "$TEAM_ID" \ + --keychain-profile "notarytool-profile" \ --wait xcrun stapler staple output/ProxyBridge-Installer.pkg + xcrun stapler validate output/ProxyBridge-Installer.pkg - name: Upload PKG uses: actions/upload-artifact@v4 From 3fb1f063ac376ff1d2f4c25496358403fc875b3d Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Sun, 14 Dec 2025 22:02:26 +0530 Subject: [PATCH 10/73] fixed workflow --- .github/workflows/build-mac.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build-mac.yml b/.github/workflows/build-mac.yml index f008e28..3fdf8cc 100644 --- a/.github/workflows/build-mac.yml +++ b/.github/workflows/build-mac.yml @@ -93,8 +93,6 @@ jobs: - name: Create and Sign PKG env: - TEAM_ID: ${{ secrets.TEAM_ID }} - run: | DEVELOPER_ID_INSTALLER_IDENTITY: ${{ secrets.DEVELOPER_ID_INSTALLER_IDENTITY }} run: | cd MacOS/ProxyBridge From c675bf206ecdba495400b31872b0bda9efe54109 Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Sun, 14 Dec 2025 22:04:29 +0530 Subject: [PATCH 11/73] fixed workflow --- .github/workflows/build-mac.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-mac.yml b/.github/workflows/build-mac.yml index 3fdf8cc..5847ba8 100644 --- a/.github/workflows/build-mac.yml +++ b/.github/workflows/build-mac.yml @@ -127,7 +127,9 @@ jobs: productsign \ --keychain build.keychain \ - --sign "$DEVELOPER_ID_INSTALLER + --sign "$DEVELOPER_ID_INSTALLER_IDENTITY" \ + --distribution build/distribution.xml \ + --package-path build \ output/ProxyBridge-Installer.pkg pkgutil --check-signature output/ProxyBridge-Installer.pkg From 1206fc9533bfb2e1ea7199c5c4c9d39c87a0e820 Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Sun, 14 Dec 2025 22:07:22 +0530 Subject: [PATCH 12/73] fixed workflow --- .github/workflows/build-mac.yml | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/.github/workflows/build-mac.yml b/.github/workflows/build-mac.yml index 5847ba8..e183f96 100644 --- a/.github/workflows/build-mac.yml +++ b/.github/workflows/build-mac.yml @@ -106,30 +106,10 @@ jobs: --install-location /Applications \ build/temp.pkg - cat > build/distribution.xml << 'EOF' - - - ProxyBridge - - - - - - - - - - - - temp.pkg - - EOF - productsign \ --keychain build.keychain \ --sign "$DEVELOPER_ID_INSTALLER_IDENTITY" \ - --distribution build/distribution.xml \ - --package-path build \ + build/temp.pkg \ output/ProxyBridge-Installer.pkg pkgutil --check-signature output/ProxyBridge-Installer.pkg From 8da43e962c59a09ccbea06063c5cb1f7d9fb740e Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Sun, 14 Dec 2025 22:20:11 +0530 Subject: [PATCH 13/73] fixed action --- .github/workflows/build-mac.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-mac.yml b/.github/workflows/build-mac.yml index e183f96..37e3244 100644 --- a/.github/workflows/build-mac.yml +++ b/.github/workflows/build-mac.yml @@ -96,11 +96,10 @@ jobs: DEVELOPER_ID_INSTALLER_IDENTITY: ${{ secrets.DEVELOPER_ID_INSTALLER_IDENTITY }} run: | cd MacOS/ProxyBridge - mkdir -p build/component output - cp -R build/DerivedData/Build/Products/Release/ProxyBridge.app build/component/ + mkdir -p output pkgbuild \ - --root build/component \ + --root build/DerivedData/Build/Products/Release \ --identifier com.interceptsuite.ProxyBridge \ --version 3.0.0 \ --install-location /Applications \ From b5e401d9ba94a86dec581f910e1fe66db71613df Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Sun, 14 Dec 2025 23:13:00 +0530 Subject: [PATCH 14/73] fixed build --- .github/workflows/build-mac.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-mac.yml b/.github/workflows/build-mac.yml index 37e3244..e183f96 100644 --- a/.github/workflows/build-mac.yml +++ b/.github/workflows/build-mac.yml @@ -96,10 +96,11 @@ jobs: DEVELOPER_ID_INSTALLER_IDENTITY: ${{ secrets.DEVELOPER_ID_INSTALLER_IDENTITY }} run: | cd MacOS/ProxyBridge - mkdir -p output + mkdir -p build/component output + cp -R build/DerivedData/Build/Products/Release/ProxyBridge.app build/component/ pkgbuild \ - --root build/DerivedData/Build/Products/Release \ + --root build/component \ --identifier com.interceptsuite.ProxyBridge \ --version 3.0.0 \ --install-location /Applications \ From 044f2eb7eaa5299173c52fc93788031bb39f8990 Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Tue, 23 Dec 2025 18:55:46 +0530 Subject: [PATCH 15/73] WIP #41 - Added check for active rules (only process packet if at least 1 active rule) - Removed PID check duplication code on rules and logging - Added no proessing with no logging - CLI APP - Log Only TCP SYN Connections - Added PID Caching - Added multi Threads - Improved Packet Processing - Optimized packet lookup from O(n) to O(1) --- Windows/src/ProxyBridge.c | 248 ++++++++++++++++++++++++++++++++++---- 1 file changed, 223 insertions(+), 25 deletions(-) diff --git a/Windows/src/ProxyBridge.c b/Windows/src/ProxyBridge.c index d731c1d..d4dadf9 100644 --- a/Windows/src/ProxyBridge.c +++ b/Windows/src/ProxyBridge.c @@ -17,6 +17,9 @@ #define LOCAL_UDP_RELAY_PORT 34011 // its running UDP port still make sure to not run on same port as TCP, opening same port and tcp and udp cause issue and handling port at relay server response injection #define MAX_PROCESS_NAME 256 #define VERSION "3.0.0" +#define PID_CACHE_SIZE 1024 +#define PID_CACHE_TTL_MS 1000 +#define NUM_PACKET_THREADS 4 // Added Mlutiple threads typedef struct PROCESS_RULE { UINT32 rule_id; @@ -64,15 +67,27 @@ typedef struct LOGGED_CONNECTION { struct LOGGED_CONNECTION *next; } LOGGED_CONNECTION; +// Impoved slow speed due to PID checking // Added pid cache +typedef struct PID_CACHE_ENTRY { + UINT32 src_ip; + UINT16 src_port; + DWORD pid; + DWORD timestamp; + BOOL is_udp; + struct PID_CACHE_ENTRY *next; +} PID_CACHE_ENTRY; + static CONNECTION_INFO *connection_list = NULL; static LOGGED_CONNECTION *logged_connections = NULL; static PROCESS_RULE *rules_list = NULL; static UINT32 g_next_rule_id = 1; static HANDLE lock = NULL; static HANDLE windivert_handle = INVALID_HANDLE_VALUE; -static HANDLE packet_thread = NULL; +static HANDLE packet_thread[NUM_PACKET_THREADS] = {NULL}; static HANDLE proxy_thread = NULL; static HANDLE udp_relay_thread = NULL; +static PID_CACHE_ENTRY *pid_cache[PID_CACHE_SIZE] = {NULL}; +static BOOL g_has_active_rules = FALSE; static SOCKET udp_relay_socket = INVALID_SOCKET; static SOCKET socks5_udp_socket = INVALID_SOCKET; static SOCKET socks5_udp_send_socket = INVALID_SOCKET; @@ -134,7 +149,7 @@ static DWORD get_process_id_from_connection(UINT32 src_ip, UINT16 src_port); static DWORD get_process_id_from_udp_connection(UINT32 src_ip, UINT16 src_port); static BOOL get_process_name_from_pid(DWORD pid, char *name, DWORD name_size); static RuleAction match_rule(const char *process_name, UINT32 dest_ip, UINT16 dest_port, BOOL is_udp); -static RuleAction check_process_rule(UINT32 src_ip, UINT16 src_port, UINT32 dest_ip, UINT16 dest_port, BOOL is_udp); +static RuleAction check_process_rule(UINT32 src_ip, UINT16 src_port, UINT32 dest_ip, UINT16 dest_port, BOOL is_udp, DWORD *out_pid); static void add_connection(UINT16 src_port, UINT32 src_ip, UINT32 dest_ip, UINT16 dest_port); static BOOL get_connection(UINT16 src_port, UINT32 *dest_ip, UINT16 *dest_port); static BOOL is_connection_tracked(UINT16 src_port); @@ -143,6 +158,10 @@ static BOOL is_connection_already_logged(DWORD pid, UINT32 dest_ip, UINT16 dest_ static void add_logged_connection(DWORD pid, UINT32 dest_ip, UINT16 dest_port, RuleAction action); static void clear_logged_connections(void); static BOOL is_broadcast_or_multicast(UINT32 ip); +static DWORD get_cached_pid(UINT32 src_ip, UINT16 src_port, BOOL is_udp); +static void cache_pid(UINT32 src_ip, UINT16 src_port, DWORD pid, BOOL is_udp); +static void clear_pid_cache(void); +static void update_has_active_rules(void); static DWORD WINAPI packet_processor(LPVOID arg) @@ -203,11 +222,22 @@ static DWORD WINAPI packet_processor(LPVOID arg) UINT32 dest_ip = ip_header->DstAddr; UINT16 dest_port = ntohs(udp_header->DstPort); + // if no rule configuree all connection direct with no checks avoid unwanted memory and pocessing whcich could delay + if (!g_has_active_rules && g_connection_callback == NULL) + { + // No rules and no logging - pass through immediately + WinDivertHelperCalcChecksums(packet, packet_len, &addr, 0); + WinDivertSend(windivert_handle, packet, packet_len, NULL, &addr); + continue; + } + RuleAction action; + DWORD pid = 0; + if (dest_port == 53 && !g_dns_via_proxy) action = RULE_ACTION_DIRECT; else - action = check_process_rule(src_ip, src_port, dest_ip, dest_port, TRUE); + action = check_process_rule(src_ip, src_port, dest_ip, dest_port, TRUE, &pid); // Override PROXY to DIRECT for critical IPs and ports if (action == RULE_ACTION_PROXY && is_broadcast_or_multicast(dest_ip)) @@ -217,12 +247,12 @@ static DWORD WINAPI packet_processor(LPVOID arg) if (action == RULE_ACTION_PROXY && (dest_port == 67 || dest_port == 68)) action = RULE_ACTION_DIRECT; - if (g_connection_callback != NULL) + // only log if callback is set + // reuse pid from check_process_rule + // CLI use no log flag + if (g_connection_callback != NULL && pid > 0) { char process_name[MAX_PROCESS_NAME]; - DWORD pid = get_process_id_from_udp_connection(src_ip, src_port); - if (pid == 0) - pid = get_process_id_from_connection(src_ip, src_port); if (pid > 0 && get_process_name_from_pid(pid, process_name, sizeof(process_name))) { @@ -332,21 +362,29 @@ static DWORD WINAPI packet_processor(LPVOID arg) UINT32 orig_dest_ip = ip_header->DstAddr; UINT16 orig_dest_port = ntohs(tcp_header->DstPort); + // avoid rule pocess and packet process if no rules + if (!g_has_active_rules && g_connection_callback == NULL) + { + WinDivertSend(windivert_handle, packet, packet_len, NULL, &addr); + continue; + } + RuleAction action; + DWORD pid = 0; + if (orig_dest_port == 53 && !g_dns_via_proxy) action = RULE_ACTION_DIRECT; else - action = check_process_rule(src_ip, src_port, orig_dest_ip, orig_dest_port, FALSE); + action = check_process_rule(src_ip, src_port, orig_dest_ip, orig_dest_port, FALSE, &pid); // Override PROXY to DIRECT for criticl ips if (action == RULE_ACTION_PROXY && is_broadcast_or_multicast(orig_dest_ip)) action = RULE_ACTION_DIRECT; - // Log ALL connections (DIRECT, BLOCK, PROXY) - only ONCE per unique connection - if (g_connection_callback != NULL) + // only new TCP/SYN inital fist packet + if (g_connection_callback != NULL && tcp_header->Syn && !tcp_header->Ack && pid > 0) { char process_name[MAX_PROCESS_NAME]; - DWORD pid = get_process_id_from_connection(src_ip, src_port); if (pid > 0 && get_process_name_from_pid(pid, process_name, sizeof(process_name))) { if (!is_connection_already_logged(pid, orig_dest_ip, orig_dest_port, action)) @@ -475,6 +513,11 @@ static UINT32 resolve_hostname(const char *hostname) static DWORD get_process_id_from_connection(UINT32 src_ip, UINT16 src_port) { + // check cache first + DWORD cached_pid = get_cached_pid(src_ip, src_port, FALSE); + if (cached_pid != 0) + return cached_pid; + MIB_TCPTABLE_OWNER_PID *tcp_table = NULL; DWORD size = 0; DWORD pid = 0; @@ -511,12 +554,21 @@ static DWORD get_process_id_from_connection(UINT32 src_ip, UINT16 src_port) } free(tcp_table); + + // store cache the result + if (pid != 0) + cache_pid(src_ip, src_port, pid, FALSE); + return pid; } // Get process ID for UDP connection static DWORD get_process_id_from_udp_connection(UINT32 src_ip, UINT16 src_port) { + DWORD cached_pid = get_cached_pid(src_ip, src_port, TRUE); + if (cached_pid != 0) + return cached_pid; + MIB_UDPTABLE_OWNER_PID *udp_table = NULL; DWORD size = 0; DWORD pid = 0; @@ -571,6 +623,10 @@ static DWORD get_process_id_from_udp_connection(UINT32 src_ip, UINT16 src_port) } free(udp_table); + + if (pid != 0) + cache_pid(src_ip, src_port, pid, TRUE); + return pid; } @@ -981,7 +1037,7 @@ static RuleAction match_rule(const char *process_name, UINT32 dest_ip, UINT16 de return RULE_ACTION_DIRECT; } -static RuleAction check_process_rule(UINT32 src_ip, UINT16 src_port, UINT32 dest_ip, UINT16 dest_port, BOOL is_udp) +static RuleAction check_process_rule(UINT32 src_ip, UINT16 src_port, UINT32 dest_ip, UINT16 dest_port, BOOL is_udp, DWORD *out_pid) { DWORD pid; char process_name[MAX_PROCESS_NAME]; @@ -989,6 +1045,11 @@ static RuleAction check_process_rule(UINT32 src_ip, UINT16 src_port, UINT32 dest pid = is_udp ? get_process_id_from_udp_connection(src_ip, src_port) : get_process_id_from_connection(src_ip, src_port); if (pid == 0 && is_udp) pid = get_process_id_from_connection(src_ip, src_port); + + // this may cause issues - need to find alternative + if (out_pid != NULL) + *out_pid = pid; + if (pid == 0) return RULE_ACTION_DIRECT; @@ -2021,6 +2082,8 @@ PROXYBRIDGE_API UINT32 ProxyBridge_AddRule(const char* process_name, const char* rule->next = rules_list; rules_list = rule; + update_has_active_rules(); + return rule->rule_id; } @@ -2035,6 +2098,7 @@ PROXYBRIDGE_API BOOL ProxyBridge_EnableRule(UINT32 rule_id) if (rule->rule_id == rule_id) { rule->enabled = TRUE; + update_has_active_rules(); return TRUE; } rule = rule->next; @@ -2053,6 +2117,7 @@ PROXYBRIDGE_API BOOL ProxyBridge_DisableRule(UINT32 rule_id) if (rule->rule_id == rule_id) { rule->enabled = FALSE; + update_has_active_rules(); // Phase 1: Update fast-path flag return TRUE; } rule = rule->next; @@ -2083,6 +2148,7 @@ PROXYBRIDGE_API BOOL ProxyBridge_DeleteRule(UINT32 rule_id) free(rule->target_ports); free(rule); + update_has_active_rules(); log_message("Deleted rule ID: %u", rule_id); return TRUE; } @@ -2116,6 +2182,7 @@ PROXYBRIDGE_API BOOL ProxyBridge_EditRule(UINT32 rule_id, const char* process_na rule->protocol = protocol; rule->action = action; + update_has_active_rules(); log_message("Updated rule ID: %u", rule_id); return TRUE; } @@ -2234,6 +2301,117 @@ static void clear_logged_connections(void) ReleaseMutex(lock); } +// cache pid +// This can be imprroved +// Need to work on this before releease for potential collusion +// need to remove unwanted entires from table +static UINT32 pid_cache_hash(UINT32 src_ip, UINT16 src_port, BOOL is_udp) +{ + UINT32 hash = src_ip ^ ((UINT32)src_port << 16) ^ (is_udp ? 0x80000000 : 0); + return hash % PID_CACHE_SIZE; +} + +static DWORD get_cached_pid(UINT32 src_ip, UINT16 src_port, BOOL is_udp) +{ + UINT32 hash = pid_cache_hash(src_ip, src_port, is_udp); + DWORD current_time = GetTickCount(); + DWORD pid = 0; + + WaitForSingleObject(lock, INFINITE); + + PID_CACHE_ENTRY *entry = pid_cache[hash]; + while (entry != NULL) + { + if (entry->src_ip == src_ip && + entry->src_port == src_port && + entry->is_udp == is_udp) + { + if (current_time - entry->timestamp < PID_CACHE_TTL_MS) + { + pid = entry->pid; + break; + } + else + { + break; + } + } + entry = entry->next; + } + + ReleaseMutex(lock); + return pid; +} + +static void cache_pid(UINT32 src_ip, UINT16 src_port, DWORD pid, BOOL is_udp) +{ + UINT32 hash = pid_cache_hash(src_ip, src_port, is_udp); + DWORD current_time = GetTickCount(); + + WaitForSingleObject(lock, INFINITE); + + PID_CACHE_ENTRY *entry = pid_cache[hash]; + while (entry != NULL) + { + if (entry->src_ip == src_ip && + entry->src_port == src_port && + entry->is_udp == is_udp) + { + entry->pid = pid; + entry->timestamp = current_time; + ReleaseMutex(lock); + return; + } + entry = entry->next; + } + + PID_CACHE_ENTRY *new_entry = (PID_CACHE_ENTRY *)malloc(sizeof(PID_CACHE_ENTRY)); + if (new_entry != NULL) + { + new_entry->src_ip = src_ip; + new_entry->src_port = src_port; + new_entry->pid = pid; + new_entry->timestamp = current_time; + new_entry->is_udp = is_udp; + new_entry->next = pid_cache[hash]; + pid_cache[hash] = new_entry; + } + + ReleaseMutex(lock); +} + +static void clear_pid_cache(void) +{ + WaitForSingleObject(lock, INFINITE); + + for (int i = 0; i < PID_CACHE_SIZE; i++) + { + while (pid_cache[i] != NULL) + { + PID_CACHE_ENTRY *to_free = pid_cache[i]; + pid_cache[i] = pid_cache[i]->next; + free(to_free); + } + } + + ReleaseMutex(lock); +} + +static void update_has_active_rules(void) +{ + g_has_active_rules = FALSE; + PROCESS_RULE *rule = rules_list; + while (rule != NULL) + { + if (rule->enabled) + { + g_has_active_rules = TRUE; + break; + } + rule = rule->next; + } +} + PROXYBRIDGE_API BOOL ProxyBridge_Start(void) { char filter[512]; @@ -2291,18 +2469,32 @@ PROXYBRIDGE_API BOOL ProxyBridge_Start(void) WinDivertSetParam(windivert_handle, WINDIVERT_PARAM_QUEUE_LENGTH, 8192); WinDivertSetParam(windivert_handle, WINDIVERT_PARAM_QUEUE_TIME, 2000); - packet_thread = CreateThread(NULL, 1, packet_processor, NULL, 0, NULL); - if (packet_thread == NULL) + for (int i = 0; i < NUM_PACKET_THREADS; i++) { - WinDivertClose(windivert_handle); - windivert_handle = INVALID_HANDLE_VALUE; - running = FALSE; - WaitForSingleObject(proxy_thread, INFINITE); - CloseHandle(proxy_thread); - proxy_thread = NULL; - return FALSE; + packet_thread[i] = CreateThread(NULL, 0, packet_processor, NULL, 0, NULL); + if (packet_thread[i] == NULL) + { + running = FALSE; + for (int j = 0; j < i; j++) + { + if (packet_thread[j] != NULL) + { + WaitForSingleObject(packet_thread[j], 5000); + CloseHandle(packet_thread[j]); + packet_thread[j] = NULL; + } + } + WinDivertClose(windivert_handle); + windivert_handle = INVALID_HANDLE_VALUE; + WaitForSingleObject(proxy_thread, INFINITE); + CloseHandle(proxy_thread); + proxy_thread = NULL; + return FALSE; + } } + update_has_active_rules(); + log_message("ProxyBridge started"); log_message("Local relay: localhost:%d", g_local_relay_port); log_message("%s proxy: %s:%d", g_proxy_type == PROXY_TYPE_HTTP ? "HTTP" : "SOCKS5", g_proxy_host, g_proxy_port); @@ -2336,11 +2528,15 @@ PROXYBRIDGE_API BOOL ProxyBridge_Stop(void) windivert_handle = INVALID_HANDLE_VALUE; } - if (packet_thread != NULL) + // process alll packets before we stop, make sure packets are not dropped + for (int i = 0; i < NUM_PACKET_THREADS; i++) { - WaitForSingleObject(packet_thread, 5000); - CloseHandle(packet_thread); - packet_thread = NULL; + if (packet_thread[i] != NULL) + { + WaitForSingleObject(packet_thread[i], 5000); + CloseHandle(packet_thread[i]); + packet_thread[i] = NULL; + } } if (proxy_thread != NULL) @@ -2369,6 +2565,8 @@ PROXYBRIDGE_API BOOL ProxyBridge_Stop(void) // Clear logged connections list clear_logged_connections(); + clear_pid_cache(); + log_message("ProxyBridge stopped"); return TRUE; From 2b88cdad9694021139e431f5be455d05fe5d1c0b Mon Sep 17 00:00:00 2001 From: Sourav Kalal Date: Tue, 23 Dec 2025 23:29:36 +0530 Subject: [PATCH 16/73] Fixed #42 - GUI no longer handle the windivert drivers - GUI pass C Stop method to stop all drivers and network threads - ProxyBridge now validate the application already running - Fixed Driver load unload issues from GUI - GUI save local config real time instead of on exit - GUI now handle close and clean up on force stop from windows - GUI operate more smoothly --- Windows/gui/App.axaml.cs | 57 ++++++++++++----- Windows/gui/Program.cs | 34 +++++++++- Windows/gui/Services/ProxyBridgeService.cs | 22 +------ Windows/gui/ViewModels/MainWindowViewModel.cs | 64 ++++++++----------- Windows/gui/ViewModels/ProxyRulesViewModel.cs | 7 +- 5 files changed, 105 insertions(+), 79 deletions(-) diff --git a/Windows/gui/App.axaml.cs b/Windows/gui/App.axaml.cs index 4929a9d..6747264 100644 --- a/Windows/gui/App.axaml.cs +++ b/Windows/gui/App.axaml.cs @@ -5,38 +5,64 @@ using ProxyBridge.GUI.ViewModels; using ProxyBridge.GUI.Views; using System; +using System.Threading; +using System.Threading.Tasks; namespace ProxyBridge.GUI; public class App : Application { - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - } + private EventWaitHandle? _showWindowEvent; + private CancellationTokenSource? _eventListenerCts; + private const string EventName = "Global\\ProxyBridge_ShowWindow_Event_v3.0"; + + public override void Initialize() => AvaloniaXamlLoader.Load(this); public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - desktop.MainWindow = new MainWindow + desktop.MainWindow = new MainWindow { DataContext = new MainWindowViewModel() }; + + try { - DataContext = new MainWindowViewModel() - }; + _showWindowEvent = new EventWaitHandle(false, EventResetMode.AutoReset, EventName); + _eventListenerCts = new CancellationTokenSource(); + Task.Run(() => ListenForActivationSignal(_eventListenerCts.Token)); + } + catch { } - // save config during shutdown desktop.ShutdownRequested += (s, e) => { - if (desktop.MainWindow?.DataContext is MainWindowViewModel vm) - { - vm.Cleanup(); - } + _eventListenerCts?.Cancel(); + _showWindowEvent?.Dispose(); + (desktop.MainWindow?.DataContext as MainWindowViewModel)?.Cleanup(); }; + + desktop.ShutdownMode = Avalonia.Controls.ShutdownMode.OnMainWindowClose; } base.OnFrameworkInitializationCompleted(); } - // https://docs.avaloniaui.net/docs/reference/controls/tray-icon + + private async Task ListenForActivationSignal(CancellationToken token) + { + while (!token.IsCancellationRequested) + { + try + { + var signaled = await Task.Run(() => _showWindowEvent?.WaitOne(1000) ?? false, token); + if (signaled && !token.IsCancellationRequested) + { + await Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() => + TrayIcon_Show(null, EventArgs.Empty)); + } + } + catch (OperationCanceledException) { break; } + catch { } + } + } + public void TrayIcon_Show(object? sender, EventArgs e) { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) @@ -53,9 +79,6 @@ public void TrayIcon_Show(object? sender, EventArgs e) public void TrayIcon_Exit(object? sender, EventArgs e) { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - desktop.Shutdown(); - } + (ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.Shutdown(); } } diff --git a/Windows/gui/Program.cs b/Windows/gui/Program.cs index c41d535..255fe87 100644 --- a/Windows/gui/Program.cs +++ b/Windows/gui/Program.cs @@ -1,15 +1,45 @@ using Avalonia; using System; +using System.Threading; namespace ProxyBridge.GUI; class Program { + private static Mutex? _instanceMutex; + private const string MutexName = "Global\\ProxyBridge_SingleInstance_Mutex_v3.0"; + private const string EventName = "Global\\ProxyBridge_ShowWindow_Event_v3.0"; + [STAThread] public static void Main(string[] args) { - BuildAvaloniaApp() - .StartWithClassicDesktopLifetime(args); + _instanceMutex = new Mutex(true, MutexName, out bool isNewInstance); + + if (!isNewInstance) + { + SignalExistingInstance(); + return; + } + + try + { + BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + } + finally + { + _instanceMutex?.ReleaseMutex(); + _instanceMutex?.Dispose(); + } + } + + private static void SignalExistingInstance() + { + try + { + using var showEvent = EventWaitHandle.OpenExisting(EventName); + showEvent.Set(); + } + catch { } } public static AppBuilder BuildAvaloniaApp() diff --git a/Windows/gui/Services/ProxyBridgeService.cs b/Windows/gui/Services/ProxyBridgeService.cs index 13a277e..7c3532d 100644 --- a/Windows/gui/Services/ProxyBridgeService.cs +++ b/Windows/gui/Services/ProxyBridgeService.cs @@ -134,27 +134,7 @@ public void Dispose() { if (_isRunning) { - Stop(); - - // WinDivert takes few seconds to stop - System.Threading.Thread.Sleep(500); - - // STOP WinDivert kernel driver - try - { - var psi = new System.Diagnostics.ProcessStartInfo - { - FileName = "sc.exe", - Arguments = "stop WinDivert", - WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden, - CreateNoWindow = true, - UseShellExecute = false - }; - var process = System.Diagnostics.Process.Start(psi); - process?.WaitForExit(2000); - } - catch { - } + Stop(); // removing the threads, C code handle close no need to manually handle drives } GC.SuppressFinalize(this); } diff --git a/Windows/gui/ViewModels/MainWindowViewModel.cs b/Windows/gui/ViewModels/MainWindowViewModel.cs index ec40b04..c477bd3 100644 --- a/Windows/gui/ViewModels/MainWindowViewModel.cs +++ b/Windows/gui/ViewModels/MainWindowViewModel.cs @@ -29,6 +29,7 @@ public class MainWindowViewModel : ViewModelBase private string _newProxyAction = "PROXY"; private Window? _mainWindow; private ProxyBridgeService? _proxyService; + private bool _isServiceInitialized = false; private string _currentProxyType = "SOCKS5"; private string _currentProxyIp = ""; @@ -44,6 +45,11 @@ public class MainWindowViewModel : ViewModelBase public void SetMainWindow(Window window) { _mainWindow = window; + + if (_isServiceInitialized) + return; + + _isServiceInitialized = true; LoadConfiguration(); try @@ -257,6 +263,7 @@ public bool DnsViaProxy if (SetProperty(ref _dnsViaProxy, value)) { _proxyService?.SetDnsViaProxy(value); + SaveConfigurationInternal(); ActivityLog += $"[{DateTime.Now:HH:mm:ss}] DNS via Proxy: {(value ? "Enabled" : "Disabled")}\n"; } } @@ -327,6 +334,7 @@ public MainWindowViewModel() _currentProxyUsername = username; _currentProxyPassword = password; + SaveConfigurationInternal(); string authInfo = string.IsNullOrEmpty(username) ? "" : " (with auth)"; ActivityLog += $"[{DateTime.Now:HH:mm:ss}] Saved proxy settings: {type} {ip}:{port}{authInfo}\n"; } @@ -373,6 +381,7 @@ public MainWindowViewModel() rule.RuleId = ruleId; rule.Index = ProxyRules.Count + 1; ProxyRules.Add(rule); + SaveConfigurationInternal(); ActivityLog += $"[{DateTime.Now:HH:mm:ss}] Added rule: {rule.ProcessName} ({rule.TargetHosts}:{rule.TargetPorts} {rule.Protocol}) -> {rule.Action} (ID: {ruleId})\n"; } else @@ -385,7 +394,8 @@ public MainWindowViewModel() { window.Close(); }, - proxyService: _proxyService + proxyService: _proxyService, + onConfigChanged: SaveConfigurationInternal ); window.DataContext = viewModel; @@ -435,7 +445,7 @@ public MainWindowViewModel() ToggleCloseToTrayCommand = new RelayCommand(() => { CloseToTray = !CloseToTray; - SaveConfiguration(); + SaveConfigurationInternal(); }); CloseDialogCommand = new RelayCommand(CloseDialogs); @@ -481,6 +491,7 @@ public MainWindowViewModel() { rule.RuleId = ruleId; ProxyRules.Add(rule); + SaveConfigurationInternal(); ActivityLog += $"[{DateTime.Now:HH:mm:ss}] Added rule: {NewProcessName} -> {NewProxyAction}\n"; IsAddRuleViewOpen = false; NewProcessName = ""; @@ -579,36 +590,14 @@ private async Task CheckForUpdatesOnStartupAsync() public void Cleanup() { - try - { - var config = new AppConfig - { - ProxyType = _currentProxyType, - ProxyIp = _currentProxyIp, - ProxyPort = _currentProxyPort, - ProxyUsername = _currentProxyUsername, - ProxyPassword = _currentProxyPassword, - DnsViaProxy = _dnsViaProxy, - Language = _currentLanguage, - CloseToTray = _closeToTray, - ProxyRules = ProxyRules.Select(r => new ProxyRuleConfig - { - ProcessName = r.ProcessName, - TargetHosts = r.TargetHosts, - TargetPorts = r.TargetPorts, - Protocol = r.Protocol, - Action = r.Action, - IsEnabled = r.IsEnabled - }).ToList() - }; + // this something can be improved maybe config doesnt need save during cleanup + try { SaveConfigurationInternal(); } catch { } - ConfigManager.SaveConfig(config); - } + try { _proxyService?.Dispose(); _proxyService = null; } catch { } + } - _proxyService?.Dispose(); - _proxyService = null; - } private void LoadConfiguration() + private void LoadConfiguration() { try { @@ -648,7 +637,12 @@ public void Cleanup() } } - private void SaveConfiguration() + private void SaveConfigurationInternal() + { + Task.Run(() => SaveConfigurationInternalAsync()); + } + + private void SaveConfigurationInternalAsync() { try { @@ -673,15 +667,9 @@ private void SaveConfiguration() }).ToList() }; - if (ConfigManager.SaveConfig(config)) - { - ActivityLog += $"[{DateTime.Now:HH:mm:ss}] Configuration saved successfully\n"; - } - } - catch (Exception ex) - { - ActivityLog += $"[{DateTime.Now:HH:mm:ss}] Failed to save configuration: {ex.Message}\n"; + ConfigManager.SaveConfig(config); } + catch { } } } diff --git a/Windows/gui/ViewModels/ProxyRulesViewModel.cs b/Windows/gui/ViewModels/ProxyRulesViewModel.cs index f18170b..24e6520 100644 --- a/Windows/gui/ViewModels/ProxyRulesViewModel.cs +++ b/Windows/gui/ViewModels/ProxyRulesViewModel.cs @@ -24,6 +24,7 @@ public class ProxyRulesViewModel : ViewModelBase private string _processNameError = ""; private Action? _onAddRule; private Action? _onClose; + private Action? _onConfigChanged; private ProxyBridgeService? _proxyService; private Window? _window; @@ -94,12 +95,13 @@ public void SetWindow(Window window) _window = window; } - public ProxyRulesViewModel(ObservableCollection proxyRules, Action onAddRule, Action onClose, ProxyBridgeService? proxyService = null) + public ProxyRulesViewModel(ObservableCollection proxyRules, Action onAddRule, Action onClose, ProxyBridgeService? proxyService = null, Action? onConfigChanged = null) { ProxyRules = proxyRules; _onAddRule = onAddRule; _onClose = onClose; _proxyService = proxyService; + _onConfigChanged = onConfigChanged; foreach (var rule in ProxyRules) { @@ -167,6 +169,7 @@ public ProxyRulesViewModel(ObservableCollection proxyRules, Action proxyRules, Action Date: Tue, 23 Dec 2025 23:54:50 +0530 Subject: [PATCH 17/73] optimized search /filter - search is speedup with less memory usage --- Windows/gui/ViewModels/MainWindowViewModel.cs | 126 ++++++++++-------- Windows/gui/Views/MainWindow.axaml | 26 ++-- 2 files changed, 84 insertions(+), 68 deletions(-) diff --git a/Windows/gui/ViewModels/MainWindowViewModel.cs b/Windows/gui/ViewModels/MainWindowViewModel.cs index c477bd3..bba0c32 100644 --- a/Windows/gui/ViewModels/MainWindowViewModel.cs +++ b/Windows/gui/ViewModels/MainWindowViewModel.cs @@ -175,8 +175,11 @@ public string ConnectionsLog get => _connectionsLog; set { - SetProperty(ref _connectionsLog, value); - FilterConnectionsLog(); + if (SetProperty(ref _connectionsLog, value)) + { + if (string.IsNullOrWhiteSpace(_connectionsSearchText)) + FilteredConnectionsLog = _connectionsLog; + } } } @@ -185,29 +188,44 @@ public string ActivityLog get => _activityLog; set { - SetProperty(ref _activityLog, value); - FilterActivityLog(); + if (SetProperty(ref _activityLog, value)) + { + if (string.IsNullOrWhiteSpace(_activitySearchText)) + FilteredActivityLog = _activityLog; + } } } + + + public bool IsProxyRulesDialogOpen + { + get => _isProxyRulesDialogOpen; + set => SetProperty(ref _isProxyRulesDialogOpen, value); + } + + public bool IsProxySettingsDialogOpen + { + get => _isProxySettingsDialogOpen; + set => SetProperty(ref _isProxySettingsDialogOpen, value); + } + + public bool IsAddRuleViewOpen + { + get => _isAddRuleViewOpen; + set => SetProperty(ref _isAddRuleViewOpen, value); + } + public string ConnectionsSearchText { get => _connectionsSearchText; - set - { - SetProperty(ref _connectionsSearchText, value); - FilterConnectionsLog(); - } + set => SetProperty(ref _connectionsSearchText, value); } public string ActivitySearchText { get => _activitySearchText; - set - { - SetProperty(ref _activitySearchText, value); - FilterActivityLog(); - } + set => SetProperty(ref _activitySearchText, value); } public string FilteredConnectionsLog @@ -222,24 +240,6 @@ public string FilteredActivityLog set => SetProperty(ref _filteredActivityLog, value); } - public bool IsProxyRulesDialogOpen - { - get => _isProxyRulesDialogOpen; - set => SetProperty(ref _isProxyRulesDialogOpen, value); - } - - public bool IsProxySettingsDialogOpen - { - get => _isProxySettingsDialogOpen; - set => SetProperty(ref _isProxySettingsDialogOpen, value); - } - - public bool IsAddRuleViewOpen - { - get => _isAddRuleViewOpen; - set => SetProperty(ref _isAddRuleViewOpen, value); - } - public string NewProcessName { get => _newProcessName; @@ -305,6 +305,8 @@ public string ChineseCheckmark public ICommand CloseDialogCommand { get; } public ICommand ClearConnectionsLogCommand { get; } public ICommand ClearActivityLogCommand { get; } + public ICommand SearchConnectionsCommand { get; } + public ICommand SearchActivityCommand { get; } public ICommand AddRuleCommand { get; } public ICommand SaveNewRuleCommand { get; } public ICommand CancelAddRuleCommand { get; } @@ -453,11 +455,25 @@ public MainWindowViewModel() ClearConnectionsLogCommand = new RelayCommand(() => { ConnectionsLog = ""; + ConnectionsSearchText = ""; + FilteredConnectionsLog = ""; }); ClearActivityLogCommand = new RelayCommand(() => { ActivityLog = "ProxyBridge initialized successfully\n"; + ActivitySearchText = ""; + FilteredActivityLog = "ProxyBridge initialized successfully\n"; + }); + + SearchConnectionsCommand = new RelayCommand(() => + { + FilteredConnectionsLog = FilterLog(_connectionsLog, _connectionsSearchText); + }); + + SearchActivityCommand = new RelayCommand(() => + { + FilteredActivityLog = FilterLog(_activityLog, _activitySearchText); }); AddRuleCommand = new RelayCommand(() => @@ -527,33 +543,7 @@ public void ChangeLanguage(string languageCode) ActivityLog += $"[{DateTime.Now:HH:mm:ss}] {_loc.LogLanguageChanged}: {languageCode}\n"; } - private void FilterConnectionsLog() - { - if (string.IsNullOrWhiteSpace(_connectionsSearchText)) - { - FilteredConnectionsLog = _connectionsLog; - } - else - { - var lines = _connectionsLog.Split('\n', StringSplitOptions.RemoveEmptyEntries); - var filtered = lines.Where(line => line.Contains(_connectionsSearchText, StringComparison.OrdinalIgnoreCase)); - FilteredConnectionsLog = string.Join('\n', filtered) + (filtered.Any() ? "\n" : ""); - } - } - private void FilterActivityLog() - { - if (string.IsNullOrWhiteSpace(_activitySearchText)) - { - FilteredActivityLog = _activityLog; - } - else - { - var lines = _activityLog.Split('\n', StringSplitOptions.RemoveEmptyEntries); - var filtered = lines.Where(line => line.Contains(_activitySearchText, StringComparison.OrdinalIgnoreCase)); - FilteredActivityLog = string.Join('\n', filtered) + (filtered.Any() ? "\n" : ""); - } - } private void CloseDialogs() { @@ -597,6 +587,26 @@ public void Cleanup() catch { } } + private string FilterLog(string log, string searchText) + { + if (string.IsNullOrWhiteSpace(searchText)) + return log; + + var sb = new StringBuilder(log.Length / 2); + var lines = log.Split('\n'); + + foreach (var line in lines) + { + if (line.Contains(searchText, StringComparison.OrdinalIgnoreCase)) + { + sb.Append(line); + sb.Append('\n'); + } + } + + return sb.ToString(); + } + private void LoadConfiguration() { try diff --git a/Windows/gui/Views/MainWindow.axaml b/Windows/gui/Views/MainWindow.axaml index dc9b46b..e29cbdd 100644 --- a/Windows/gui/Views/MainWindow.axaml +++ b/Windows/gui/Views/MainWindow.axaml @@ -250,17 +250,20 @@ - + + Watermark="Search connections (press Enter)..." + Margin="0,0,8,0"> + + + +