重写为多协议管理器: 6 协议, CLI + curses TUI, 用户级 systemd

This commit is contained in:
typist committed 2026-11-01 06:38:56 +08:00
1 parent c43ab4a16e
commit 237b958e41
71 files changed
+4599 -1171

No files matched your search

+138
View File
@@ -0,0 +1,138 @@
import json
import unittest
from sbp.config import build_client_config, build_server_config
from sbp.errors import SbpError
from sbp.share import all_uris, share_uri
from tests.test_state import SERVER_SS_KEY, USER_SS_KEY, sample_state
class ServerConfigTest(unittest.TestCase):
def setUp(self) -> None:
self.state = sample_state()
self.config = build_server_config(self.state)
def test_all_protocols_present(self) -> None:
tags = {inbound["tag"] for inbound in self.config["inbounds"]}
self.assertEqual(
tags,
{"hysteria2-in", "vless-reality-in", "trojan-in", "shadowsocks-in", "tuic-in", "anytls-in"},
)
def test_disabled_protocol_is_skipped(self) -> None:
self.state.protocols["trojan"].enabled = False
tags = {inbound["tag"] for inbound in build_server_config(self.state)["inbounds"]}
self.assertNotIn("trojan-in", tags)
def test_hysteria2_tls_and_obfs(self) -> None:
inbound = next(i for i in self.config["inbounds"] if i["tag"] == "hysteria2-in")
self.assertEqual(inbound["tls"]["alpn"], ["h3"])
self.assertEqual(inbound["obfs"]["type"], "salamander")
self.assertEqual(inbound["users"][0]["password"], "PASSWORD")
def test_reality_uses_private_key(self) -> None:
inbound = next(i for i in self.config["inbounds"] if i["tag"] == "vless-reality-in")
self.assertEqual(inbound["tls"]["reality"]["private_key"], "PRIVATE")
self.assertEqual(inbound["tls"]["reality"]["handshake"]["server"], "www.bing.com")
def test_shadowsocks_multi_user(self) -> None:
inbound = next(i for i in self.config["inbounds"] if i["tag"] == "shadowsocks-in")
self.assertEqual(inbound["password"], SERVER_SS_KEY)
self.assertEqual(inbound["users"][0]["password"], USER_SS_KEY)
def test_shadowsocks_client_sends_identity_and_user_key(self) -> None:
config = build_client_config(self.state, "shadowsocks", self.state.users[0])
self.assertEqual(
config["outbounds"][0]["password"], f"{SERVER_SS_KEY}:{USER_SS_KEY}"
)
def test_config_is_json_serializable(self) -> None:
json.dumps(self.config)
def test_reality_rejects_ip_server_name(self) -> None:
self.state.protocols["vless-reality"].options["server_name"] = "203.0.113.10"
with self.assertRaises(SbpError):
build_server_config(self.state)
def test_reality_rejects_odd_length_short_id(self) -> None:
self.state.protocols["vless-reality"].options["short_id"] = "abc"
with self.assertRaises(SbpError):
build_server_config(self.state)
def test_reality_accepts_even_short_id(self) -> None:
self.state.protocols["vless-reality"].options["short_id"] = "ab12"
build_server_config(self.state)
class ClientConfigTest(unittest.TestCase):
def setUp(self) -> None:
self.state = sample_state()
self.user = self.state.users[0]
def test_disabled_protocol_raises_sbp_error(self) -> None:
self.state.protocols["tuic"].enabled = False
with self.assertRaises(SbpError):
build_client_config(self.state, "tuic", self.user)
with self.assertRaises(SbpError):
share_uri(self.state, "tuic", self.user)
def test_each_client_config_routes_to_outbound(self) -> None:
for key in self.state.protocols:
config = build_client_config(self.state, key, self.user)
self.assertEqual(config["route"]["final"], config["outbounds"][0]["tag"])
json.dumps(config)
def test_self_signed_pins_public_key(self) -> None:
config = build_client_config(self.state, "trojan", self.user)
self.assertEqual(
config["outbounds"][0]["tls"]["certificate_public_key_sha256"], ["SPKI"]
)
def test_reality_does_not_pin(self) -> None:
config = build_client_config(self.state, "vless-reality", self.user)
tls = config["outbounds"][0]["tls"]
self.assertNotIn("certificate_public_key_sha256", tls)
self.assertEqual(tls["reality"]["public_key"], "PUBLIC")
class ShareUriTest(unittest.TestCase):
def setUp(self) -> None:
self.state = sample_state()
self.user = self.state.users[0]
def test_uri_prefixes(self) -> None:
prefixes = {
"hysteria2": "hysteria2://",
"vless-reality": "vless://",
"trojan": "trojan://",
"shadowsocks": "ss://",
"tuic": "tuic://",
"anytls": "anytls://",
}
for key, prefix in prefixes.items():
self.assertTrue(share_uri(self.state, key, self.user).startswith(prefix), key)
def test_hysteria2_pin_and_obfs(self) -> None:
uri = share_uri(self.state, "hysteria2", self.user)
self.assertIn("pinSHA256=SHA256", uri)
self.assertIn("obfs=salamander", uri)
def test_vless_reality_parameters(self) -> None:
uri = share_uri(self.state, "vless-reality", self.user)
self.assertIn("security=reality", uri)
self.assertIn("pbk=PUBLIC", uri)
self.assertIn("flow=xtls-rprx-vision", uri)
def test_shadowsocks_uses_plain_userinfo(self) -> None:
from urllib.parse import unquote
uri = share_uri(self.state, "shadowsocks", self.user)
userinfo = unquote(uri.removeprefix("ss://").split("@", 1)[0])
self.assertEqual(userinfo, f"2022-blake3-aes-128-gcm:{SERVER_SS_KEY}:{USER_SS_KEY}")
def test_all_uris(self) -> None:
self.assertEqual(len(all_uris(self.state, self.user)), 6)
if __name__ == "__main__":
unittest.main()