mirror of
https://github.com/YuanDevTeam/Vole.git
synced 2026-11-15 08:05:52 +00:00
* test: prepare protocol-completion validation * feat: support ShadowTLS v3 for Shadowsocks 2022 * feat: add UoT v2 to Shadowsocks 2022 * feat: support TUIC v5 TCP and UDP * feat: add HTTPUpgrade to VMess and Trojan Reuse the existing raw upgrade stream with typed normal/fast-open options and strict early-data configuration. Reject an expired setup before ready IO can write any prefix. Add isolated official-peer data, UDP, authentication, fingerprint, close and legacy-transport gates. * test: validate extended proxy chains and lifecycle * build: freeze release dependencies for protocol completion * fix: enforce HTTPUpgrade deadline on resumed IO Recheck the absolute setup deadline before each handshake poll and before accepting success, without retaining a timer on established streams. Cover ready late responses, blocked prefix writes, and post-setup IO in both normal and fast-open modes.
421 lines
16 KiB
Python
421 lines
16 KiB
Python
from __future__ import annotations
|
|
|
|
import copy
|
|
import io
|
|
import json
|
|
import tempfile
|
|
import unittest
|
|
from contextlib import redirect_stdout
|
|
from pathlib import Path
|
|
|
|
from vcore_scripts import cli
|
|
|
|
|
|
class IntegrationAcceptanceTest(unittest.TestCase):
|
|
def test_integration_quality_separates_release_source_policy(self):
|
|
from vcore_scripts.protocol_integration_local import commands
|
|
|
|
rows = commands("INTEGRATION-QUALITY")
|
|
self.assertNotIn(["vcore-scripts", "check", "tls-dependencies"], rows)
|
|
self.assertIn(["vcore-scripts", "check", "c-header"], rows)
|
|
self.assertIn(
|
|
[
|
|
"cargo",
|
|
"test",
|
|
"--locked",
|
|
"--manifest-path",
|
|
"crates/vcore-netstack/Cargo.toml",
|
|
"--all-targets",
|
|
],
|
|
rows,
|
|
)
|
|
self.assertIn(["vcore-scripts", "build", "apple"], rows)
|
|
self.assertIn(["vcore-scripts", "build", "android"], rows)
|
|
|
|
def test_native_source_identity_and_cleanup_are_required(self):
|
|
from vcore_scripts.protocol_integration_checks import envelope
|
|
|
|
identity = dict(
|
|
version="official-test",
|
|
binary_sha256="a" * 64,
|
|
archive_sha256="b" * 64,
|
|
source_url="https://github.com/MetaCubeX/mihomo/releases/latest/download/test",
|
|
)
|
|
source = {
|
|
k: "c" * 64
|
|
for k in (
|
|
"parent_commit",
|
|
"source_tree_sha256",
|
|
"dirty_patch_sha256",
|
|
"lock_sha256",
|
|
)
|
|
}
|
|
digest = "sha256:" + "d" * 64
|
|
report = dict(
|
|
status="PASS",
|
|
cleanup=True,
|
|
source_unchanged=True,
|
|
source=source,
|
|
peers={"M": identity},
|
|
isolation=dict(
|
|
network_mode="hostOnly",
|
|
host_servers=False,
|
|
guest_mtu=1500,
|
|
image_digest=digest,
|
|
peers=[dict(started=True, joined=True)],
|
|
),
|
|
)
|
|
self.assertTrue(envelope(report, {"M": identity}, source, digest))
|
|
for mutate in (
|
|
lambda d: d.update(cleanup=False),
|
|
lambda d: d.update(source_unchanged=False),
|
|
lambda d: d["source"].update(source_tree_sha256="e" * 64),
|
|
lambda d: d["peers"]["M"].update(binary_sha256="e" * 64),
|
|
lambda d: d["isolation"].update(host_servers=True),
|
|
lambda d: d["isolation"].update(guest_mtu=1200),
|
|
lambda d: d["isolation"]["peers"][0].update(joined=False),
|
|
lambda d: d["isolation"].update(peers=[]),
|
|
):
|
|
broken = copy.deepcopy(report)
|
|
mutate(broken)
|
|
self.assertFalse(envelope(broken, {"M": identity}, source, digest))
|
|
|
|
def test_pressure_evidence_is_recomputed_not_inferred(self):
|
|
from vcore_scripts.protocol_evidence import RESOURCE_KINDS
|
|
from vcore_scripts.protocol_integration_metrics import (
|
|
QUEUES,
|
|
lifetimes,
|
|
rebuild,
|
|
soak,
|
|
)
|
|
from vcore_scripts.protocol_integration_profiles import (
|
|
TCP_GROUPS,
|
|
UDP_GROUPS,
|
|
expected_flows,
|
|
)
|
|
|
|
def snapshot(current):
|
|
return dict(
|
|
counts=[
|
|
dict(kind=k, current=current, peak=1)
|
|
for k in sorted(RESOURCE_KINDS)
|
|
]
|
|
)
|
|
|
|
def sample(active=True):
|
|
return dict(
|
|
heap_in_use=2_000_000,
|
|
rss_kib=50_000,
|
|
fd=100 if active else 6,
|
|
resources=snapshot(int(active)),
|
|
queues=[
|
|
dict(kind=k, capacity=v, peak=int(v > 0)) for k, v in QUEUES.items()
|
|
],
|
|
)
|
|
|
|
event = dict(resources=snapshot(0))
|
|
stop = dict(
|
|
stop_ms=4,
|
|
after_stop=sample(False),
|
|
quiet_seconds=5.1,
|
|
quiet=sample(False),
|
|
ports_rebound=True,
|
|
)
|
|
rows = [
|
|
dict(
|
|
generation=i,
|
|
tcp=20,
|
|
udp=20,
|
|
per_group_tcp=5,
|
|
per_group_udp=5,
|
|
flow_profiles=expected_flows(i),
|
|
active=sample(),
|
|
setup_us=[1000] * 40,
|
|
fault="owned-peer-connections-closed",
|
|
new_clients=True,
|
|
after_clients=sample(),
|
|
)
|
|
for i in range(100)
|
|
]
|
|
value = dict(
|
|
count=100,
|
|
same_running_session=True,
|
|
tcp_groups=list(TCP_GROUPS),
|
|
udp_groups=list(UDP_GROUPS),
|
|
cold_fd=4,
|
|
baseline_fd=6,
|
|
cycles=rows,
|
|
stopped=stop,
|
|
)
|
|
self.assertTrue(rebuild(value, event))
|
|
for mutate in (
|
|
lambda d: d["cycles"].pop(),
|
|
lambda d: d.update(same_running_session=False),
|
|
lambda d: d["cycles"][0].update(tcp=0),
|
|
lambda d: d["cycles"][0].update(setup_us=[]),
|
|
lambda d: d["cycles"][0]["flow_profiles"]["tcp"].pop(),
|
|
lambda d: d["cycles"][1].update(flow_profiles=expected_flows(0)),
|
|
lambda d: d["stopped"].update(stop_ms=5001),
|
|
lambda d: d["stopped"].update(quiet_seconds=0),
|
|
lambda d: d["stopped"]["after_stop"].update(fd=7),
|
|
):
|
|
broken = copy.deepcopy(value)
|
|
mutate(broken)
|
|
self.assertFalse(rebuild(broken, event))
|
|
self.assertFalse(lifetimes(dict(count=99, cycles=[]), []))
|
|
points = [
|
|
dict(sample(), seconds=300 + i * 60, waves=i + 1, faults=5 + i)
|
|
for i in range(25)
|
|
]
|
|
value = dict(
|
|
requested_seconds=1800,
|
|
seconds=1800.2,
|
|
tcp_groups=list(TCP_GROUPS),
|
|
udp_groups=list(UDP_GROUPS),
|
|
tcp=20,
|
|
udp=20,
|
|
per_group_tcp=5,
|
|
per_group_udp=5,
|
|
first_profiles=expected_flows(0),
|
|
last_profiles=expected_flows(29),
|
|
waves=1000,
|
|
normal_verified_bytes=1000 * 20 * 2 * 2 * 257,
|
|
normal_corruption=0,
|
|
normal_misdirection=0,
|
|
normal_unexpected_loss=0,
|
|
switches=list(range(1, 1800)),
|
|
faults=[
|
|
dict(
|
|
start=60 * i,
|
|
end=60 * i + 0.5,
|
|
old_tcp_closed=20,
|
|
new_tcp=20,
|
|
new_udp=20,
|
|
replay=False,
|
|
)
|
|
for i in range(1, 30)
|
|
],
|
|
samples=points,
|
|
cold_fd=4,
|
|
baseline_fd=6,
|
|
stopped=stop,
|
|
active_end=sample(),
|
|
first_setup_us=[1000] * 40,
|
|
last_setup_us=[1100] * 40,
|
|
first_setup_median_us=1000,
|
|
last_setup_median_us=1100,
|
|
heap=dict(
|
|
early_median=2_000_000, late_median=2_000_000, allowed_growth=1048576
|
|
),
|
|
)
|
|
self.assertTrue(soak(value, event, points))
|
|
for mutate in (
|
|
lambda d: d.update(requested_seconds=65),
|
|
lambda d: d.update(seconds=1799),
|
|
lambda d: d["faults"].pop(),
|
|
lambda d: d.update(switches=[]),
|
|
lambda d: d["samples"][0].update(heap_in_use=0),
|
|
lambda d: d["samples"][-1]["queues"][0].update(peak=10000),
|
|
lambda d: d["heap"].update(late_median=4_000_000),
|
|
lambda d: d.update(last_setup_us=[3000] * 40, last_setup_median_us=3000),
|
|
lambda d: d.update(normal_unexpected_loss=1),
|
|
):
|
|
broken = copy.deepcopy(value)
|
|
mutate(broken)
|
|
self.assertFalse(soak(broken, event, broken["samples"]))
|
|
self.assertFalse(soak(value, event, points[:-1]))
|
|
|
|
def test_integration_consumer_results_keep_the_manifest_scope(self):
|
|
from vcore_scripts.protocol_evidence import load_manifest, validate_results
|
|
from vcore_scripts.protocol_security_acceptance import result
|
|
|
|
case = next(
|
|
c for c in load_manifest() if c["case_id"] == "INTEGRATION-PAIR-SS-SS"
|
|
)
|
|
validate_results([case], [result(case, True, True)])
|
|
|
|
def test_pair_evidence_requires_each_real_path_and_original_assertions(self):
|
|
from vcore_scripts.protocol_integration_acceptance import pair_pass
|
|
from vcore_scripts.protocol_integration_native import rust_command
|
|
|
|
identifier = "INTEGRATION-PAIR-SOCKS5-VLESS"
|
|
record = dict(
|
|
case_id=identifier,
|
|
command=rust_command(),
|
|
exit_code=0,
|
|
command_cleanup=True,
|
|
)
|
|
paths = [
|
|
dict(
|
|
outer_ipv6=v6,
|
|
nested_select=group,
|
|
tcp_target_kinds=3,
|
|
tcp_bytes_each_direction=31457280,
|
|
udp_target_kinds=3,
|
|
udp_packets=1200,
|
|
udp_sizes=[1, 64, 512, 1200],
|
|
server_first=True,
|
|
client_first=True,
|
|
ports_rebound=True,
|
|
)
|
|
for v6 in (False, True)
|
|
for group in (False, True)
|
|
]
|
|
observation = dict(
|
|
first="socks5",
|
|
last="vless",
|
|
paths=paths,
|
|
domain_native=[],
|
|
budgets=[
|
|
dict(
|
|
transmit=tx,
|
|
receive=rx,
|
|
roundtrip_size=min(tx, rx),
|
|
packets=100,
|
|
oversize_rejected_before_origin=True,
|
|
)
|
|
for tx, rx in ((128, 128), (512, 256))
|
|
],
|
|
routed_udp=dict(
|
|
upstream_business_udp_disabled=True,
|
|
carrier_tcp=True,
|
|
carrier_udp=True,
|
|
leaf_business_udp_rejected=True,
|
|
),
|
|
carrier_capability=dict(
|
|
upstream_datagrams="rejected",
|
|
tcp_allowed=True,
|
|
udp_allowed=True,
|
|
no_bypass=True,
|
|
),
|
|
)
|
|
events = [
|
|
dict(schema_version=1, suite=suite, assertion=assertion, status=status)
|
|
for suite, assertion, count in (
|
|
("INTEGRATION-PAIR", "ordered_pair", 1),
|
|
("INTEGRATION-BASE", "tcp_10mib_both_directions", 12),
|
|
("INTEGRATION-PAIR", "directional_budget", 1),
|
|
("INTEGRATION-PAIR", "routed_udp_permission", 1),
|
|
("INTEGRATION-PAIR", "carrier_capability", 1),
|
|
)
|
|
for _ in range(count)
|
|
for status in ("BEGIN", "PASS")
|
|
]
|
|
with tempfile.TemporaryDirectory() as temp:
|
|
root = Path(temp)
|
|
event_path = root / (identifier + "-events.jsonl")
|
|
data_path = root / (identifier + "-observations.json")
|
|
event_path.write_text("".join(json.dumps(e) + "\n" for e in events))
|
|
data_path.write_text(json.dumps(observation))
|
|
self.assertTrue(pair_pass(identifier, record, root))
|
|
for mutate in (
|
|
lambda d: d["paths"].pop(),
|
|
lambda d: d["paths"].append(d["paths"][0]),
|
|
lambda d: d["paths"][0].update(udp_packets=799),
|
|
lambda d: d["paths"][0].update(ports_rebound=False),
|
|
lambda d: d["paths"][0].update(server_first=False),
|
|
lambda d: d["paths"][0].update(
|
|
server_first_limitation="official-ss2022-empty-first-write"
|
|
),
|
|
lambda d: d.update(last="vmess"),
|
|
lambda d: d["budgets"][0].update(packets=99),
|
|
lambda d: d["carrier_capability"].update(no_bypass=False),
|
|
):
|
|
broken = copy.deepcopy(observation)
|
|
mutate(broken)
|
|
data_path.write_text(json.dumps(broken))
|
|
self.assertFalse(pair_pass(identifier, record, root))
|
|
data_path.write_text(json.dumps(observation))
|
|
event_path.write_text("".join(json.dumps(e) + "\n" for e in events[:-1]))
|
|
self.assertFalse(pair_pass(identifier, record, root))
|
|
identifier = "INTEGRATION-CHAIN-TUIC-QUIC-SS-V3-AES-256-GCM"
|
|
record["case_id"] = identifier
|
|
observation.update(
|
|
first="tuic",
|
|
last="ss",
|
|
variant=dict(udp_mode="quic", cipher="2022-blake3-aes-256-gcm"),
|
|
)
|
|
observation["carrier_capability"]["udp_allowed"] = False
|
|
for path in observation["paths"]:
|
|
path.update(
|
|
server_first=False,
|
|
server_first_limitation="official-ss2022-empty-first-write",
|
|
)
|
|
event_path = root / (identifier + "-events.jsonl")
|
|
data_path = root / (identifier + "-observations.json")
|
|
event_path.write_text("".join(json.dumps(e) + "\n" for e in events))
|
|
data_path.write_text(json.dumps(observation))
|
|
self.assertTrue(pair_pass(identifier, record, root))
|
|
for mutate in (
|
|
lambda d: d.pop("variant"),
|
|
lambda d: d["variant"].update(udp_mode="native"),
|
|
lambda d: d["variant"].update(cipher="2022-blake3-aes-128-gcm"),
|
|
lambda d: d["paths"][0].update(server_first=True),
|
|
lambda d: d["paths"][0].pop("server_first_limitation"),
|
|
lambda d: d["paths"][0].update(client_first=False),
|
|
lambda d: d["paths"][0].update(tcp_bytes_each_direction=1),
|
|
):
|
|
broken = copy.deepcopy(observation)
|
|
mutate(broken)
|
|
data_path.write_text(json.dumps(broken))
|
|
self.assertFalse(pair_pass(identifier, record, root))
|
|
|
|
def test_list_contains_all_ordered_pairs_and_remaining_gates(self):
|
|
output = io.StringIO()
|
|
with redirect_stdout(output):
|
|
self.assertEqual(
|
|
cli.main(
|
|
["check", "protocol-interop", "--suite", "integration", "--list"]
|
|
),
|
|
0,
|
|
)
|
|
identifiers = [line.split("\t")[0] for line in output.getvalue().splitlines()]
|
|
protocols = {
|
|
"SOCKS5",
|
|
"ANYTLS",
|
|
"SS",
|
|
"TROJAN",
|
|
"VMESS",
|
|
"VLESS",
|
|
"HYSTERIA2",
|
|
"TUIC",
|
|
}
|
|
expected = {
|
|
f"INTEGRATION-PAIR-{first}-{last}"
|
|
for first in protocols
|
|
for last in protocols
|
|
}
|
|
self.assertEqual(len(expected), 64)
|
|
self.assertEqual({key for key in identifiers if "-PAIR-" in key}, expected)
|
|
self.assertEqual(
|
|
{key for key in identifiers if "-CHAIN-" in key},
|
|
{
|
|
f"INTEGRATION-CHAIN-TUIC-{mode}-SS-V3-{cipher}"
|
|
for mode in ("NATIVE", "QUIC")
|
|
for cipher in ("AES-128-GCM", "AES-256-GCM", "CHACHA20-POLY1305")
|
|
},
|
|
)
|
|
self.assertEqual(len(identifiers), len(set(identifiers)))
|
|
self.assertTrue(
|
|
{
|
|
"INTEGRATION-ENTRYPOINTS",
|
|
"INTEGRATION-GRAPH",
|
|
"INTEGRATION-DNS-MEASURE",
|
|
"INTEGRATION-SS-ALGORITHMS",
|
|
"INTEGRATION-SS-EIH",
|
|
"INTEGRATION-LIFECYCLE",
|
|
"INTEGRATION-REBUILD",
|
|
"INTEGRATION-FAILURES",
|
|
"INTEGRATION-SOAK",
|
|
"INTEGRATION-HY2-HOP",
|
|
"INTEGRATION-DEBUG",
|
|
"INTEGRATION-RELEASE",
|
|
"INTEGRATION-QUALITY",
|
|
"INTEGRATION-FEATURES",
|
|
"INTEGRATION-SCRIPTS",
|
|
"INTEGRATION-SHARED",
|
|
"INTEGRATION-COUPLED",
|
|
}
|
|
<= set(identifiers)
|
|
)
|