修复:PAC 规则解析与关键域名分流
This commit is contained in:
+66
-11
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys, os, subprocess, time, ctypes, threading, functools, base64, bisect
|
||||
import sys, os, subprocess, time, ctypes, threading, functools, base64, bisect, re
|
||||
from pathlib import Path
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
@@ -832,8 +832,17 @@ def _is_private_ip(host: str) -> bool:
|
||||
_CN_RANGES = None # 合并排序后的 (start, end) 整数段
|
||||
_CN_RANGES_LOADED = False
|
||||
_FORCE_PROXY_SUFFIXES = None
|
||||
_FORCE_PROXY_EXACT = None
|
||||
_FORCE_PROXY_REGEXES = None
|
||||
_FORCE_PROXY_LOADED = False
|
||||
|
||||
# PAC 数据由 CDN 独立下发;此处保留登录/AI 服务的关键后缀兜底。
|
||||
# 即使远端表暂时未更新或不可读,这些域名也不能因解析到中国 CDN IP 而直连。
|
||||
_CORE_FORCE_PROXY_SUFFIXES = frozenset((
|
||||
"anthropic.com", "chatgpt.com", "claude.ai", "googlecdn.com",
|
||||
"oaistatic.com", "oaiusercontent.com", "openai.com",
|
||||
))
|
||||
|
||||
def _data_path(name: str):
|
||||
"""数据文件路径:只取打包内置目录(config.json 随 exe 内置)。"""
|
||||
p = Path(BASE_DIR) / name
|
||||
@@ -1318,8 +1327,13 @@ def _load_cn_ranges():
|
||||
return _CN_RANGES
|
||||
|
||||
def _load_force_proxy_suffixes():
|
||||
"""加载 pac_domains.json:命中这些域名后缀的连接强制走隧道(优先于 IP 判定)。"""
|
||||
global _FORCE_PROXY_SUFFIXES, _FORCE_PROXY_LOADED
|
||||
"""加载 pac_domains.json 的后缀、精确和正则规则。
|
||||
|
||||
domain-list-community 的三种规则语义:普通域名匹配自身及子域;
|
||||
``full:`` 仅匹配该主机;``regexp:`` 按正则匹配。旧逻辑把后两类
|
||||
当成带冒号的普通后缀,导致它们永远不命中。
|
||||
"""
|
||||
global _FORCE_PROXY_SUFFIXES, _FORCE_PROXY_EXACT, _FORCE_PROXY_REGEXES, _FORCE_PROXY_LOADED
|
||||
if _FORCE_PROXY_LOADED:
|
||||
return _FORCE_PROXY_SUFFIXES
|
||||
_FORCE_PROXY_LOADED = True
|
||||
@@ -1327,14 +1341,51 @@ def _load_force_proxy_suffixes():
|
||||
import json
|
||||
path = _user_path("pac_domains.json")
|
||||
if path is None:
|
||||
return _FORCE_PROXY_SUFFIXES # 可选文件,缺失不影响 IP 分流
|
||||
_FORCE_PROXY_SUFFIXES = sorted(_CORE_FORCE_PROXY_SUFFIXES)
|
||||
_FORCE_PROXY_EXACT = frozenset()
|
||||
_FORCE_PROXY_REGEXES = ()
|
||||
log(f"未找到 pac_domains.json,使用 {len(_FORCE_PROXY_SUFFIXES)} 条关键域名兜底")
|
||||
return _FORCE_PROXY_SUFFIXES
|
||||
data = json.loads(Path(path).read_text("utf-8"))
|
||||
suffixes = sorted({str(d).strip().lower().lstrip("*.")
|
||||
for d in (data.get("domains") or []) if str(d).strip()})
|
||||
suffixes = set(_CORE_FORCE_PROXY_SUFFIXES)
|
||||
exact = set()
|
||||
regexes = []
|
||||
invalid_regexes = 0
|
||||
for raw in data.get("domains") 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。
|
||||
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
|
||||
suffixes.add(rule.lstrip("*.").rstrip("."))
|
||||
suffixes.discard("")
|
||||
_FORCE_PROXY_SUFFIXES = suffixes
|
||||
log(f"已加载强制代理域名: {len(suffixes)} 条 (pac_domains.json)")
|
||||
_FORCE_PROXY_EXACT = frozenset(exact)
|
||||
_FORCE_PROXY_REGEXES = tuple(regexes)
|
||||
log(f"已加载强制代理规则: 后缀 {len(suffixes)} 条、精确 {len(exact)} 条、"
|
||||
f"正则 {len(regexes)} 条 (pac_domains.json)" +
|
||||
(f",跳过 {invalid_regexes} 条非法正则" if invalid_regexes else ""))
|
||||
except Exception as e:
|
||||
log_red(f"pac_domains.json 加载失败: {e}")
|
||||
_FORCE_PROXY_SUFFIXES = sorted(_CORE_FORCE_PROXY_SUFFIXES)
|
||||
_FORCE_PROXY_EXACT = frozenset()
|
||||
_FORCE_PROXY_REGEXES = ()
|
||||
return _FORCE_PROXY_SUFFIXES
|
||||
|
||||
def _is_private_int(n: int) -> bool:
|
||||
@@ -1386,12 +1437,16 @@ def _is_china_ip(host: str):
|
||||
|
||||
@functools.lru_cache(maxsize=8192)
|
||||
def _is_force_proxy(host: str) -> bool:
|
||||
"""pac_domains.json 命中的域名强制走隧道(如 claude.ai:国内可连但显示拒绝页)。"""
|
||||
"""PAC 强制规则命中的域名走隧道(后缀 / full / regexp)。"""
|
||||
suffixes = _load_force_proxy_suffixes()
|
||||
if not suffixes:
|
||||
exact = _FORCE_PROXY_EXACT or ()
|
||||
regexes = _FORCE_PROXY_REGEXES or ()
|
||||
if not suffixes and not exact and not regexes:
|
||||
return False
|
||||
h = host.rstrip(".").lower()
|
||||
return any(h == s or h.endswith("." + s) for s in suffixes)
|
||||
return (any(h == s or h.endswith("." + s) for s in suffixes) or
|
||||
h in exact or
|
||||
any(pattern.search(h) is not None for pattern in regexes))
|
||||
|
||||
def _resolve_host_ips(host: str):
|
||||
"""解析域名的 IPv4/IPv6 地址集合(字面量 IP 返回自身);解析失败返回空集合。"""
|
||||
@@ -3092,4 +3147,4 @@ def main():
|
||||
os._exit(ret)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user