$ErrorActionPreference = 'Continue' $testLog = 'C:\Apache24\htdocs\.codex_tmp\airmax_gateway_ab_test.log' $protonClient = 'C:\Program Files\Proton\VPN\v5.1.5\ProtonVPN.Client.exe' $protonDirectory = Split-Path -Parent $protonClient $interfaceIndex = 14 $temporaryPrefixes = @('10.30.99.22/32', '10.30.99.25/32') function Write-TestLog([string]$message) { Add-Content -LiteralPath $testLog -Value (('{0} {1}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss.fff'), $message)) -Encoding UTF8 } function Test-DevicePair([string]$label) { $timeoutMs = 20000 $started = [System.Diagnostics.Stopwatch]::StartNew() $checks = @( [pscustomobject]@{ Host = '10.30.99.22'; Port = 80; Client = [System.Net.Sockets.TcpClient]::new(); Async = $null }, [pscustomobject]@{ Host = '10.30.99.25'; Port = 443; Client = [System.Net.Sockets.TcpClient]::new(); Async = $null } ) foreach ($check in $checks) { $check.Async = $check.Client.BeginConnect($check.Host, $check.Port, $null, $null) } foreach ($check in $checks) { $connected = $false $errorText = '' try { $remaining = [Math]::Max(0, $timeoutMs - [int]$started.ElapsedMilliseconds) if ($check.Async.AsyncWaitHandle.WaitOne($remaining)) { $check.Client.EndConnect($check.Async) $connected = $check.Client.Connected } else { $errorText = 'timeout' } } catch { $errorText = $_.Exception.GetBaseException().Message } finally { $check.Client.Close() } Write-TestLog ("TCP $label host=$($check.Host) port=$($check.Port) connected=$connected elapsed_ms=$($started.ElapsedMilliseconds) error=$errorText") } $started.Stop() } Set-Content -LiteralPath $testLog -Value '' -Encoding UTF8 Write-TestLog 'BEGIN gateway A/B test' try { Get-Process -Name 'ProtonVPN.Client' -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue Start-Sleep -Milliseconds 750 Stop-Service -Name 'ProtonVPN Service' -Force -ErrorAction Continue Start-Sleep -Seconds 2 $service = Get-Service -Name 'ProtonVPN Service' -ErrorAction SilentlyContinue $protonDefault = Get-NetRoute -InterfaceAlias 'ProTUN' -DestinationPrefix '0.0.0.0/0' -ErrorAction SilentlyContinue Write-TestLog "PROTON service=$($service.Status) default_route=$([bool]$protonDefault)" $baseRoute = Get-NetRoute -DestinationPrefix '10.30.99.0/24' -InterfaceIndex $interfaceIndex -ErrorAction SilentlyContinue Write-TestLog ("BASE_ROUTE next_hop=" + (($baseRoute.NextHop | Select-Object -Unique) -join ',')) Test-DevicePair 'via-51' foreach ($prefix in $temporaryPrefixes) { New-NetRoute -DestinationPrefix $prefix -InterfaceIndex $interfaceIndex -NextHop '10.30.98.50' -RouteMetric 1 -PolicyStore ActiveStore -ErrorAction Stop | Out-Null } Write-TestLog 'TEMP_ROUTES via 10.30.98.50 installed' Test-DevicePair 'via-50' } catch { Write-TestLog ("ERROR " + $_.Exception.GetBaseException().Message) } finally { foreach ($prefix in $temporaryPrefixes) { Get-NetRoute -DestinationPrefix $prefix -InterfaceIndex $interfaceIndex -ErrorAction SilentlyContinue | Where-Object { $_.NextHop -eq '10.30.98.50' } | Remove-NetRoute -Confirm:$false -ErrorAction SilentlyContinue } Write-TestLog 'TEMP_ROUTES removed' Start-Service -Name 'ProtonVPN Service' -ErrorAction Continue Start-Sleep -Seconds 2 if (Test-Path -LiteralPath $protonClient) { Start-Process -FilePath $protonClient -WorkingDirectory $protonDirectory -WindowStyle Hidden } Start-Sleep -Seconds 12 $finalService = Get-Service -Name 'ProtonVPN Service' -ErrorAction SilentlyContinue $finalClient = Get-Process -Name 'ProtonVPN.Client' -ErrorAction SilentlyContinue Write-TestLog "RESTORED service=$($finalService.Status) client=$([bool]$finalClient)" Write-TestLog 'END gateway A/B test' }