"""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