82 lines
2.6 KiB
Python
82 lines
2.6 KiB
Python
"""PAC 路由引擎使用的纯算法,不依赖文件、网络或 Qt。"""
|
|
|
|
import ipaddress
|
|
import re
|
|
|
|
|
|
def merge_ip_ranges(cidrs, version):
|
|
"""将指定 IP 版本的 CIDR 合并为整数闭区间,供二分查找。"""
|
|
ranges = []
|
|
for cidr in cidrs:
|
|
network = ipaddress.ip_network(str(cidr).strip(), strict=False)
|
|
if network.version == version:
|
|
ranges.append((int(network.network_address), int(network.broadcast_address)))
|
|
ranges.sort()
|
|
merged = []
|
|
for start, end in ranges:
|
|
if merged and start <= merged[-1][1] + 1:
|
|
if end > merged[-1][1]:
|
|
merged[-1] = (merged[-1][0], end)
|
|
else:
|
|
merged.append((start, end))
|
|
return merged
|
|
|
|
|
|
def build_suffix_trie(suffixes):
|
|
"""按反向域名标签建立 Trie,适合大规模后缀规则的 O(标签数) 匹配。"""
|
|
root = {}
|
|
for suffix in suffixes:
|
|
node = root
|
|
for label in reversed(suffix.split(".")):
|
|
node = node.setdefault(label, {})
|
|
node[""] = True
|
|
return root
|
|
|
|
|
|
def suffix_trie_matches(host: str, trie) -> bool:
|
|
"""判断主机名是否命中一个后缀 Trie。"""
|
|
node = trie
|
|
for label in reversed(host.rstrip(".").lower().split(".")):
|
|
node = node.get(label)
|
|
if node is None:
|
|
return False
|
|
if node.get(""):
|
|
return True
|
|
return bool(node and node.get(""))
|
|
|
|
|
|
def parse_domain_rules(raw_rules, seed_suffixes=()):
|
|
"""解析后缀、``full:`` 与 ``regexp:`` 三种 PAC 域名规则。
|
|
|
|
返回 ``(suffixes, exact, regexes, invalid_regex_count)``。调用方决定
|
|
这些规则是“强制代理”还是“明确直连”,从而避免解析层携带业务策略。
|
|
"""
|
|
suffixes = set(seed_suffixes)
|
|
exact = set()
|
|
regexes = []
|
|
invalid_regexes = 0
|
|
for raw in raw_rules or ():
|
|
raw_rule = str(raw).strip()
|
|
rule = raw_rule.lower()
|
|
if not raw_rule:
|
|
continue
|
|
if rule.startswith("full:"):
|
|
host = rule[len("full:"):].rstrip(".")
|
|
if host:
|
|
exact.add(host)
|
|
continue
|
|
if rule.startswith("regexp:"):
|
|
pattern = raw_rule[len("regexp:"):]
|
|
if not pattern:
|
|
invalid_regexes += 1
|
|
continue
|
|
try:
|
|
regexes.append(re.compile(pattern, re.IGNORECASE))
|
|
except re.error:
|
|
invalid_regexes += 1
|
|
continue
|
|
suffix = rule.lstrip("*.").rstrip(".")
|
|
if suffix:
|
|
suffixes.add(suffix)
|
|
return suffixes, frozenset(exact), tuple(regexes), invalid_regexes
|