using System.Buffers.Binary; using System.Diagnostics; using System.Net; using System.Net.Security; using System.Net.Sockets; using System.IO.Pipes; using System.Security.AccessControl; using System.Security.Cryptography; using System.Security.Principal; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; using QVPN.Core; // All fixtures are generated locally. No real subscription, server credential, or key is in this file. return await Runner.MainAsync(args); static class Runner { private static int passed; private static int failed; private const int MaxWireSize = 256 * 1024; private static readonly string FakeUuid = new Guid(Enumerable.Range(1, 16).Select(x => (byte)x).ToArray()).ToString(); private static readonly string FakePublicKey = Convert.ToBase64String(Enumerable.Range(1, 32).Select(x => (byte)x).ToArray()).TrimEnd('=').Replace('+', '-').Replace('/', '_'); private static readonly string FakeUrl = "https://test.withblancvpn.online/s/" + new string('a', 32); public static async Task MainAsync(string[] args) { if (args.Length > 0) return await CommandAsync(args); await Test("Blanc URL accepted", () => { Check(Subscription.ValidateUrl(FakeUrl).Scheme == "https"); }); await Test("Foreign provider and suffix spoof rejected", () => { foreach (var url in new[] { "https://example.org/s/", "https://withblancvpn.online.example.org/s/", "https://evilwithblancvpn.online/s/" }) Throws(() => Subscription.ValidateUrl(url + new string('a', 32))); }); await Test("Unicode and punycode provider spoof rejected", () => { var spoof = new Uri("https://withblаncvpn.online/s/" + new string('a', 32)); // Cyrillic a, not the provider. Throws(() => Subscription.ValidateUrl(spoof.AbsoluteUri)); Throws(() => Subscription.ValidateUrl("https://" + spoof.IdnHost + spoof.AbsolutePath)); }); await Test("Unsafe URL components rejected", () => { foreach (var url in new[] { FakeUrl.Replace("https:", "http:"), FakeUrl.Replace("https://", "https://name:secret@"), FakeUrl + "?token=x", FakeUrl + "#fragment", FakeUrl.Replace(".online/", ".online:444/"), FakeUrl.Replace("/s/", "/other/"), FakeUrl.Replace("/s/", "/s/%2f"), FakeUrl[..^20] }) Throws(() => Subscription.ValidateUrl(url)); }); await Test("Realistic 50-node plain and base64 subscriptions", () => { var text = string.Join('\n', Enumerable.Range(0, 50).Select(i => Link("8.8.4.4", 10000 + i, "Location " + i))); Check(Subscription.Parse(text).Count == 50); Check(Subscription.Parse(Convert.ToBase64String(Encoding.UTF8.GetBytes(text))).Count == 50); Check(Subscription.Parse("\uFEFF\r\n " + text + "\r\n").Count == 50); }); await Test("Duplicate nodes deduplicated; names sanitized", () => { var first = Link(name: "Safe%0AName"); var nodes = Subscription.Parse(first + "\n" + Link(name: "Other name")); Check(nodes.Count == 1 && nodes[0].Name == "SafeName"); Check(Subscription.Parse(Link(name: new string('a', 200)))[0].Name.Length == 160); }); await Test("Unsupported protocols and malformed Reality rejected", () => { var valid = Link(); foreach (var bad in new[] { "not base64!", valid.Replace("vless://", "vmess://"), valid.Replace("security=reality", "security=tls"), valid.Replace("type=tcp", "type=ws"), valid.Replace("headerType=none", "headerType=http"), valid.Replace("encryption=none", "encryption=aes"), valid.Replace("pbk=" + FakePublicKey, "pbk=short"), valid.Replace("sid=abcd", "sid=abc"), valid.Replace("sni=example.com", "sni=bad%0Ahost"), valid.Replace("fp=chrome", "fp=unknown"), valid.Replace("flow=xtls-rprx-vision", "flow=unknown"), valid.Replace(FakeUuid, "not-a-uuid"), valid.Replace(":443?", ":0?") }) Throws(() => Subscription.Parse(bad)); Throws(() => Subscription.Parse("")); Throws(() => Subscription.Parse(valid + "\nnot a location")); Throws(() => Subscription.Parse(string.Join('\n', Enumerable.Range(0, 257).Select(i => Link(port: 10000 + i))))); }); await Test("Local/private IP server targets rejected", () => { foreach (var host in new[] { "127.0.0.1", "0.0.0.0", "10.0.0.1", "172.16.0.1", "172.31.0.1", "192.168.1.1", "169.254.1.1", "100.64.0.1", "198.18.0.1", "224.0.0.1", "255.255.255.255", "[::1]", "[::]", "[fc00::1]", "[fd12::1]", "[fe80::1]", "[ff02::1]", "[::ffff:127.0.0.1]", "[::ffff:192.168.1.1]", "localhost", "example.com" }) Throws(() => Subscription.Parse(Link(host))); }); await Test("DPAPI encrypted atomic store and protected ACL", StoreAsync); await Test("Wire request and status roundtrip", async () => { var request = new Request("SetRussianDirect", Enabled: false); using var buffer = new MemoryStream(); await Wire.WriteAsync(buffer, request, default); buffer.Position = 0; Check(await Wire.ReadAsync(buffer, default) == request); var status = new Status("Disconnected", "Ready", false, null, null, null, 0, null, true, true, "", null, [], []); buffer.SetLength(0); buffer.Position = 0; await Wire.WriteAsync(buffer, new Response(true, null, status), default); buffer.Position = 0; var read = await Wire.ReadAsync(buffer, default); Check(read.Ok && read.Status?.State == "Disconnected" && read.Status.Nodes.Count == 0); }); await Test("Wire invalid length and truncated payload rejected", async () => { foreach (var length in new[] { 0, -1, MaxWireSize + 1, int.MaxValue }) { var header = new byte[4]; BinaryPrimitives.WriteInt32LittleEndian(header, length); using var buffer = new MemoryStream(header); await ThrowsAsync(() => Wire.ReadAsync(buffer, default)); } using (var shortHeader = new MemoryStream(new byte[3])) await ThrowsAsync(() => Wire.ReadAsync(shortHeader, default)); var bytes = new byte[5]; BinaryPrimitives.WriteInt32LittleEndian(bytes, 20); using (var shortPayload = new MemoryStream(bytes)) await ThrowsAsync(() => Wire.ReadAsync(shortPayload, default)); using var nullPayload = new MemoryStream(); await Wire.WriteAsync(nullPayload, (Request?)null, default); nullPayload.Position = 0; await ThrowsAsync(() => Wire.ReadAsync(nullPayload, default)); var malformedJson = Encoding.UTF8.GetBytes("{not-json}"); var malformedFrame = new byte[4 + malformedJson.Length]; BinaryPrimitives.WriteInt32LittleEndian(malformedFrame, malformedJson.Length); malformedJson.CopyTo(malformedFrame, 4); using var malformedPayload = new MemoryStream(malformedFrame); await ThrowsAsync(() => Wire.ReadAsync(malformedPayload, default)); }); await Test("Wire outbound bound checked before any write", async () => { using var stream = new MemoryStream(); await ThrowsAsync(() => Wire.WriteAsync(stream, new Request("Import", new string('x', MaxWireSize)), default)); Check(stream.Length == 0); }); await Test("TUN strict routing, tunneled DNS and proxy health precedence", Config); await Test("Russian direct toggle and local rule-set wiring", () => { var directory = NewTemporaryDirectory(); try { File.WriteAllBytes(Path.Combine(directory, "geoip-ru.srs"), []); File.WriteAllBytes(Path.Combine(directory, "geosite-category-ru.srs"), []); var on = ConfigBuilder.Build(Subscription.Parse(Link())[0], false, true, directory); Check(on["inbounds"]!.AsArray().Count == 1); Check(on["route"]!["rule_set"]!.AsArray().Count == 2); Check(on["route"]!["rules"]!.AsArray().Count(r => r?["outbound"]?.GetValue() == "direct") == 4); var off = ConfigBuilder.Build(Subscription.Parse(Link())[0], true, false, directory); Check(off["route"]!["rule_set"] is null); Check(off["route"]!["rules"]!.AsArray().Count(r => r?["outbound"]?.GetValue() == "direct") == 1); } finally { RemoveTemporaryDirectory(directory); } }); await Test("Recovery requires three failures and healthy resets streak", () => { var p = new RecoveryPolicy(); Check(!p.HealthFailed()); Check(!p.HealthFailed()); p.Healthy(); Check(!p.HealthFailed()); Check(!p.HealthFailed()); Check(p.HealthFailed()); Check(p.ConsecutiveFailures == 3); p.Healthy(); Check(p.ConsecutiveFailures == 0); }); await Test("New endpoint fails quickly; established tunnel tolerates a transient miss", () => { var p = new RecoveryPolicy(); Check(!p.HealthFailed(false)); Check(p.HealthFailed(false)); p.Healthy(); Check(!p.HealthFailed()); Check(!p.HealthFailed()); Check(p.HealthFailed()); }); await Test("Fresh alternatives do not inherit exponential outage backoff", () => { var p = new RecoveryPolicy(); var now = DateTimeOffset.UtcNow; for (int i = 0; i < 20; i++) { p.Failed(now, true); Check(p.NextAttempt == now.AddMilliseconds(250)); Check(p.Attempts == 0); } p.Failed(now); Check(p.NextAttempt == now.AddSeconds(2)); p.Failed(now); Check(p.NextAttempt == now.AddSeconds(4)); p.Failed(now, true); Check(p.NextAttempt == now.AddMilliseconds(250)); p.Reset(); Check(p.CanAttempt(now) && p.Attempts == 0); }); await NodeSelectionTests.RunAsync((name, action) => Test(name, action)); await SubscriptionTransportTests.RunAsync((name, action) => Test(name, action)); await SystemTunnelProbeTests.RunAsync((name, action) => Test(name, action)); await Test("Subscription deadline covers headers and a stalled body", SubscriptionDownloadTests.HeadersDoNotEndBodyDeadlineAsync); await Test("Caller cancellation wins the subscription body deadline", SubscriptionDownloadTests.CallerCancellationWinsBodyDeadlineAsync); await Test("Bounded subscription download preserves provider and body validation", SubscriptionDownloadTests.CompletedBodyPreservesValidationAsync); await XrayConfigTests.RunAsync((name, action) => Test(name, action)); await SubscriptionRefreshPolicyTests.RunAsync((name, action) => Test(name, action)); await Test("System TUN routes all proxy traffic through local Xray without storing credentials", () => { var config = ConfigBuilder.BuildXrayBridge(true, true, Path.GetTempPath()); var proxy = config["outbounds"]!.AsArray().Single(n => n?["tag"]?.GetValue() == "proxy")!; Check(proxy["type"]!.GetValue() == "socks" && proxy["server"]!.GetValue() == "127.0.0.1"); Check(proxy["server_port"]!.GetValue() == Constants.XrayPort && proxy["version"]!.GetValue() == "5"); Check(!config.ToJsonString().Contains(FakeUuid) && !config.ToJsonString().Contains("public_key")); Check(config["inbounds"]!.AsArray().Any(n => n?["type"]?.GetValue() == "tun")); Check(config["dns"]!["servers"]!.AsArray().Single()!["detour"]!.GetValue() == "proxy"); }); await Test("Parallel health success cancels pending requests", HealthProbeTests.SuccessCancelsPendingAsync); await Test("Health requests share a bounded timeout", HealthProbeTests.SharedBudgetBoundsLatencyAsync); await Test("Health caller cancellation propagates", HealthProbeTests.CallerCancellationPropagatesAsync); await Test("Health rejects redirects, transport errors and HTTP 200", HealthProbeTests.FailuresDoNotProduceSuccessAsync); await Test("Third health endpoint tolerates throttled peers", HealthProbeTests.ThirdEndpointRecoversThrottledPeersAsync); await Test("Health opens fresh HTTPS sessions against a keep-alive server", HealthProbeTests.FreshConnectionsDoNotReuseSuccessfulSessionAsync); await Test("TUN health requires both proxy and system DNS/HTTPS paths", HealthProbeTests.TunRequiresBothPathsAsync); await Test("TUN paths share one bounded health deadline", HealthProbeTests.TunPathsShareBudgetAsync); await Test("Proxy success cannot mask a timed-out system tunnel", HealthProbeTests.ProxySuccessDoesNotMaskSystemTimeoutAsync); await Test("TUN health propagates caller cancellation", HealthProbeTests.TunCallerCancellationPropagatesAsync); await Test("SOCKS diagnostics skip the system tunnel check", HealthProbeTests.SocksModeSkipsSystemPathAsync); await Test("Recovery backoff bounded; one healthy probe does not clear attempts", () => { var p = new RecoveryPolicy(); var now = DateTimeOffset.UtcNow; foreach (var seconds in new[] { 2, 4, 8, 16, 32, 60, 60, 60, 60, 60, 60 }) { p.Failed(now); Check(p.NextAttempt == now.AddSeconds(seconds)); Check(!p.CanAttempt(p.NextAttempt.AddTicks(-1))); Check(p.CanAttempt(p.NextAttempt)); } Check(p.Attempts == 10); p.Healthy(); Check(p.Attempts == 10); p.Stable(); Check(p.Attempts == 0); p.Reset(); Check(p.CanAttempt(now)); }); Console.WriteLine($"RESULT passed={passed} failed={failed}"); return failed == 0 ? 0 : 1; } private static async Task CommandAsync(string[] args) { try { if (args.Length == 2 && args[0] == "--xray-check") { await XrayConfigTests.CheckEngineAsync(Path.GetFullPath(args[1])); return 0; } if (args.Length == 3 && args[0] == "--malformed-service" && args[1] == "--evidence-file") return await MalformedServiceAsync(args[2]); if (args.Length is 3 or 4 && args[0] == "--download" && args[1] == "--value-file" && (args.Length == 3 || args[3] == "--diagnose")) return await DownloadCommandAsync((await File.ReadAllTextAsync(args[2])).Trim(), args.Length == 4); if (args.Length == 2 && args[0] == "--live-parse") { var nodes = Subscription.Parse(await File.ReadAllTextAsync(args[1])); // Counts only: never echo UUIDs, links, hosts or node names from a real subscription. Console.WriteLine($"LIVE_PARSE nodes={nodes.Count}"); return 0; } if (args.Length == 2 && args[0] == "--engine-check") { var directory = NewTemporaryDirectory(); try { var node = Subscription.Parse(Link())[0]; foreach (var tun in new[] { false, true }) foreach (var russianDirect in new[] { false, true }) { var path = Path.Combine(directory, "test-config.json"); await File.WriteAllTextAsync(path, ConfigBuilder.Build(node, tun, russianDirect, directory).ToJsonString()); var start = new ProcessStartInfo(Path.GetFullPath(args[1])) { UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true }; start.ArgumentList.Add("check"); start.ArgumentList.Add("-c"); start.ArgumentList.Add(path); using var process = Process.Start(start) ?? throw new InvalidOperationException(); var stdout = process.StandardOutput.ReadToEndAsync(); var stderr = process.StandardError.ReadToEndAsync(); using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(15)); try { await process.WaitForExitAsync(timeout.Token); } catch { if (!process.HasExited) process.Kill(true); throw; } await Task.WhenAll(stdout, stderr); Console.WriteLine($"ENGINE_CHECK tun={tun} russianDirect={russianDirect} exit={process.ExitCode}"); if (process.ExitCode != 0) return 1; } return 0; } finally { RemoveTemporaryDirectory(directory); } } if (args.Length >= 2 && args[0] == "--client") { string? value = null; bool? enabled = null; for (int i = 2; i < args.Length; i += 2) { if (i + 1 >= args.Length) throw new ArgumentException(); if (args[i] == "--value-file") value = (await File.ReadAllTextAsync(args[i + 1])).Trim(); else if (args[i] == "--value") value = args[i + 1]; else if (args[i] == "--enabled") enabled = bool.Parse(args[i + 1]); else throw new ArgumentException(); } var response = await PipeClient.SendAsync(new Request(args[1], value, enabled)); // Status exposes only public presentation fields. Requests/credentials are never echoed. Console.WriteLine(JsonSerializer.Serialize(new { response.Ok, response.Error, response.Status }, Constants.Json)); return response.Ok ? 0 : 2; } Console.WriteLine("Usage: QVPN.Tests [--download --value-file private-file | --live-parse file | --engine-check executable | --client command [--value-file file] [--value public-node-id] [--enabled true/false]]"); return 2; } catch (Exception e) { Console.WriteLine("COMMAND_FAILED type=" + e.GetType().Name); return 1; } } private static async Task DownloadCommandAsync(string url, bool diagnose) { var uri = Subscription.ValidateUrl(url); var coreOk = false; try { var result = await Subscription.DownloadAsync(url, default); Console.WriteLine($"DOWNLOAD_CORE nodes={result.Nodes.Count}"); coreOk = true; if (!diagnose) return 0; } catch (Exception e) { DiagnosticException("DOWNLOAD_CORE", e); } try { var addresses = await Dns.GetHostAddressesAsync(uri.IdnHost); Console.WriteLine($"DOWNLOAD_DNS ipv4={addresses.Count(a => a.AddressFamily == AddressFamily.InterNetwork)} ipv6={addresses.Count(a => a.AddressFamily == AddressFamily.InterNetworkV6)} systemProxyBypassed={HttpClient.DefaultProxy.IsBypassed(uri)}"); } catch (Exception e) { DiagnosticException("DOWNLOAD_DNS", e); } foreach (var mode in new[] { "direct", "system-proxy", "ipv4-direct" }) { var errors = SslPolicyErrors.None; var chainFlags = new HashSet(); bool Validate(System.Security.Cryptography.X509Certificates.X509Certificate? certificate, System.Security.Cryptography.X509Certificates.X509Chain? chain, SslPolicyErrors policy) { errors = policy; if (chain is not null) foreach (var status in chain.ChainStatus) chainFlags.Add(status.Status.ToString()); return policy == SslPolicyErrors.None; // Diagnostic callback keeps strict certificate validation. } using var handler = new SocketsHttpHandler { AllowAutoRedirect = false, UseProxy = mode == "system-proxy", SslOptions = new System.Net.Security.SslClientAuthenticationOptions { RemoteCertificateValidationCallback = (_, c, ch, err) => Validate(c, ch, err) } }; if (mode == "ipv4-direct") handler.ConnectCallback = async (context, token) => { var addresses = await Dns.GetHostAddressesAsync(context.DnsEndPoint.Host, AddressFamily.InterNetwork, token); foreach (var address in addresses) { var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { await socket.ConnectAsync(address, context.DnsEndPoint.Port, token); return new NetworkStream(socket, true); } catch { socket.Dispose(); } } throw new SocketException((int)SocketError.HostUnreachable); }; using var http = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(12) }; http.DefaultRequestHeaders.UserAgent.ParseAdd("v2rayN/7.0 QVPN/1.0"); try { using var response = await http.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead); Console.WriteLine($"DOWNLOAD_TRANSPORT mode={mode} http={(int)response.StatusCode} tls={errors} chain={string.Join(',', chainFlags)}"); } catch (Exception e) { DiagnosticException("DOWNLOAD_TRANSPORT mode=" + mode, e); Console.WriteLine($"DOWNLOAD_TLS mode={mode} errors={errors} chain={string.Join(',', chainFlags)}"); } } return coreOk ? 0 : 1; } private static async Task MalformedServiceAsync(string evidencePath) { // Authorized diagnostic regression: invalid frames only, no control-state commands. using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(40)); var before = await PipeClient.SendAsync(new Request("status"), timeout.Token); var samples = new List(); var payload = Encoding.UTF8.GetBytes("{not-json}"); var frame = new byte[payload.Length + 4]; BinaryPrimitives.WriteInt32LittleEndian(frame, payload.Length); payload.CopyTo(frame, 4); for (var i = 0; i < 8; i++) { using (var pipe = new NamedPipeClientStream(".", Constants.PipeName, PipeDirection.InOut, PipeOptions.Asynchronous, TokenImpersonationLevel.Identification)) { await pipe.ConnectAsync(2500, timeout.Token); await pipe.WriteAsync(frame, timeout.Token); await pipe.FlushAsync(timeout.Token); var one = new byte[1]; var read = await pipe.ReadAsync(one, timeout.Token); Check(read == 0); // Malformed input is closed without logging or executing a command. } var status = await PipeClient.SendAsync(new Request("status"), timeout.Token); Check(status.Ok && status.Status is not null); samples.Add(new { Iteration = i + 1, StatusReachable = status.Ok, State = status.Status!.State, status.Status.EnginePid }); Console.WriteLine($"MALFORMED_SERVICE iteration={i + 1} status={status.Ok} state={status.Status.State}"); } var after = await PipeClient.SendAsync(new Request("status"), timeout.Token); Check(after.Ok && before.Status is not null && after.Status is not null); var evidence = new { CapturedUtc = DateTimeOffset.UtcNow, Mode = "existing diagnostic console, SOCKS only; no TUN/stop/restart commands", MalformedFrames = 8, IdentificationClient = true, StatusSurvived = true, Before = new { before.Status!.State, before.Status.EnginePid, before.Status.DesiredConnected, Nodes = before.Status.Nodes.Count }, After = new { after.Status!.State, after.Status.EnginePid, after.Status.DesiredConnected, Nodes = after.Status.Nodes.Count }, EngineUnchanged = before.Status.EnginePid == after.Status.EnginePid, Samples = samples }; await File.WriteAllTextAsync(Path.GetFullPath(evidencePath), JsonSerializer.Serialize(evidence, new JsonSerializerOptions { WriteIndented = true })); Console.WriteLine("MALFORMED_SERVICE passed=8 status-survived=True engine-unchanged=" + evidence.EngineUnchanged); return 0; } private static void DiagnosticException(string label, Exception exception) { for (Exception? e = exception; e is not null; e = e.InnerException) { // Fixed enums and numeric codes only; Message/ToString can contain the private URL. var code = e is HttpRequestException http ? " httpError=" + http.HttpRequestError + " status=" + http.StatusCode : e is SocketException socket ? " socketError=" + socket.SocketErrorCode : ""; Console.WriteLine($"{label} type={e.GetType().Name} hresult=0x{e.HResult:X8}{code}"); } } private static string Link(string host = "8.8.8.8", int port = 443, string name = "Test location") => $"vless://{FakeUuid}@{host}:{port}?security=reality&type=tcp&headerType=none&encryption=none&pbk={FakePublicKey}&sid=abcd&sni=example.com&fp=chrome&flow=xtls-rprx-vision#{name}"; private static Task StoreAsync() { var directory = NewTemporaryDirectory(); var stage = "acl"; try { var owner = WindowsIdentity.GetCurrent().User ?? throw new InvalidOperationException(); var allowed = new[] { owner, new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null), new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null) }; var acl = new DirectoryInfo(directory).GetAccessControl(); acl.SetAccessRuleProtection(true, false); foreach (FileSystemAccessRule inheritedRule in acl.GetAccessRules(true, false, typeof(SecurityIdentifier))) acl.RemoveAccessRuleSpecific(inheritedRule); foreach (var sid in allowed) acl.AddAccessRule(new FileSystemAccessRule(sid, FileSystemRights.FullControl, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow)); new DirectoryInfo(directory).SetAccessControl(acl); var store = new SecureStore(directory); Check(store.Load().Nodes.Count == 0); var settings = new Settings { SubscriptionUrl = FakeUrl, Nodes = Subscription.Parse(Link()), DesiredConnected = true, RussianDirect = false, SelectedId = "sentinel-test-id" }; stage = "first-save"; store.Save(settings); stage = "first-load"; var read = store.Load(); Check(read.SubscriptionUrl == FakeUrl && read.Nodes.Count == 1 && read.DesiredConnected && !read.RussianDirect); stage = "replace-save"; settings.AutoFailover = false; store.Save(settings); Check(!store.Load().AutoFailover); stage = "encrypted-acl-check"; Check(File.Exists(Path.Combine(directory, "settings.dpapi.backup")) && !File.Exists(Path.Combine(directory, "settings.dpapi.new"))); foreach (var file in Directory.GetFiles(directory)) { var bytes = File.ReadAllBytes(file); Check(!Encoding.UTF8.GetString(bytes).Contains(FakeUrl) && !Encoding.UTF8.GetString(bytes).Contains(FakeUuid)); foreach (FileSystemAccessRule rule in new FileInfo(file).GetAccessControl().GetAccessRules(true, true, typeof(SecurityIdentifier))) if (rule.AccessControlType == AccessControlType.Allow) Check(allowed.Any(sid => sid == (SecurityIdentifier)rule.IdentityReference)); } File.WriteAllBytes(Path.Combine(directory, "settings.dpapi"), new byte[32]); stage = "corruption-check"; Throws(() => store.Load()); } catch { Console.WriteLine("STORE_FAILED stage=" + stage); throw; } finally { RemoveTemporaryDirectory(directory); } return Task.CompletedTask; } private static void Config() { var node = Subscription.Parse(Link())[0]; var config = ConfigBuilder.Build(node, true, true, Path.GetTempPath(), 18999); var tun = config["inbounds"]!.AsArray().Single(n => n?["type"]?.GetValue() == "tun")!; Check(tun["auto_route"]!.GetValue() && tun["strict_route"]!.GetValue() && tun["dns_mode"]!.GetValue() == "hijack"); Check(tun["address"]!.AsArray().Count == 2); var health = config["inbounds"]!.AsArray().Single(n => n?["tag"]?.GetValue() == "health-in")!; Check(health["listen"]!.GetValue() == "127.0.0.1" && health["listen_port"]!.GetValue() == 18999); var dns = config["dns"]!; Check(dns["final"]!.GetValue() == "remote-dns"); var server = dns["servers"]!.AsArray().Single()!; Check(server["type"]!.GetValue() == "https" && server["detour"]!.GetValue() == "proxy" && server["tls"]!["enabled"]!.GetValue()); var route = config["route"]!; Check(route["final"]!.GetValue() == "proxy" && route["auto_detect_interface"]!.GetValue()); var rules = route["rules"]!.AsArray(); var healthRule = rules.Single(r => r?["inbound"]?.GetValue() == "health-in")!; Check(healthRule["outbound"]!.GetValue() == "proxy"); Check(rules.IndexOf(healthRule) < rules.Select((r, i) => (r, i)).Where(x => x.r?["outbound"]?.GetValue() == "direct").Min(x => x.i)); Check(rules.Any(r => r?["action"]?.GetValue() == "hijack-dns")); var proxy = config["outbounds"]!.AsArray().Single(n => n?["tag"]?.GetValue() == "proxy")!; Check(proxy["type"]!.GetValue() == "vless" && proxy["tls"]!["reality"]!["enabled"]!.GetValue()); Check(config["log"]!["level"]!.GetValue() == "error"); } private static string NewTemporaryDirectory() { var path = Path.Combine(Path.GetTempPath(), "QVPN.Tests-" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(path); return path; } private static void RemoveTemporaryDirectory(string path) { var absolute = Path.GetFullPath(path); var root = Path.GetFullPath(Path.GetTempPath()).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; if (!absolute.StartsWith(root, StringComparison.OrdinalIgnoreCase) || !Path.GetFileName(absolute).StartsWith("QVPN.Tests-", StringComparison.Ordinal)) throw new InvalidOperationException("Unsafe test cleanup target."); Directory.Delete(absolute, true); } private static Task Test(string name, Action action) => Test(name, () => { action(); return Task.CompletedTask; }); private static async Task Test(string name, Func action) { try { await action(); passed++; Console.WriteLine("PASS " + name); } catch (Exception e) { failed++; Console.WriteLine("FAIL " + name + " type=" + e.GetType().Name); } } private static void Check(bool condition) { if (!condition) throw new TestFailure(); } private static void Throws(Action action) where T : Exception { try { action(); } catch (T) { return; } throw new TestFailure(); } private static async Task ThrowsAsync(Func action) where T : Exception { try { await action(); } catch (T) { return; } throw new TestFailure(); } private sealed class TestFailure : Exception; }