Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9376e35025 | ||
|
|
110a526fc6 | ||
|
|
9b7b817cb7 | ||
|
|
a39647d46f |
@@ -1,9 +1,11 @@
|
|||||||
|
# Royirin Rosmontis Private Proxy
|
||||||
|
Supported by Global Rosmanka Cloud Nexus Inc. —— https://rosmanka.rrrosmontis.icu
|
||||||
## 警告
|
## 警告
|
||||||
- 包含 WireGuard 的 common 版本转发器永久不再维护,推荐使用 Lite 版本
|
- 包含 WireGuard 的 common 版本转发器永久不再维护,推荐使用 Lite 版本
|
||||||
> common 版本现在有许多未修复的 bug (如部分 UI 的显示问题) 、令人难以忍受的代码逻辑 (如 PAC 规则直接写在代码里)和安全性问题 (认证凭据的暴露与证书忽略),而 Lite 版本修复了这些问题,并会收到持续的更新
|
> common 版本现在有许多未修复的 bug (如部分 UI 的显示问题) 、令人难以忍受的代码逻辑 (如 PAC 规则直接写在代码里)和安全性问题 (认证凭据的暴露与证书忽略),而 Lite 版本修复了这些问题,并会收到持续的更新
|
||||||
- 本程序完全没有 Linux 与 CLI 功能兼容,默认构建为带有图形界面的 Windows .exe
|
- 本程序完全没有 Linux 与 CLI 功能兼容,默认构建为带有图形界面的 Windows .exe
|
||||||
|
|
||||||
## 如何区分 common 版本与 Lite 版本
|
### 如何区分 common 版本与 Lite 版本
|
||||||
- Lite 版本的版本号前四位固定为 9999,后续为从月份开始的构建时间戳,而 common 版本的版本号为包含年份的完整的构建时间戳
|
- Lite 版本的版本号前四位固定为 9999,后续为从月份开始的构建时间戳,而 common 版本的版本号为包含年份的完整的构建时间戳
|
||||||
|
|
||||||
## 服务器侧配置手册 (部分内容过时)
|
## 服务器侧配置手册 (部分内容过时)
|
||||||
|
|||||||
+66
-11
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- 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 pathlib import Path
|
||||||
|
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
@@ -832,8 +832,17 @@ def _is_private_ip(host: str) -> bool:
|
|||||||
_CN_RANGES = None # 合并排序后的 (start, end) 整数段
|
_CN_RANGES = None # 合并排序后的 (start, end) 整数段
|
||||||
_CN_RANGES_LOADED = False
|
_CN_RANGES_LOADED = False
|
||||||
_FORCE_PROXY_SUFFIXES = None
|
_FORCE_PROXY_SUFFIXES = None
|
||||||
|
_FORCE_PROXY_EXACT = None
|
||||||
|
_FORCE_PROXY_REGEXES = None
|
||||||
_FORCE_PROXY_LOADED = False
|
_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):
|
def _data_path(name: str):
|
||||||
"""数据文件路径:只取打包内置目录(config.json 随 exe 内置)。"""
|
"""数据文件路径:只取打包内置目录(config.json 随 exe 内置)。"""
|
||||||
p = Path(BASE_DIR) / name
|
p = Path(BASE_DIR) / name
|
||||||
@@ -1318,8 +1327,13 @@ def _load_cn_ranges():
|
|||||||
return _CN_RANGES
|
return _CN_RANGES
|
||||||
|
|
||||||
def _load_force_proxy_suffixes():
|
def _load_force_proxy_suffixes():
|
||||||
"""加载 pac_domains.json:命中这些域名后缀的连接强制走隧道(优先于 IP 判定)。"""
|
"""加载 pac_domains.json 的后缀、精确和正则规则。
|
||||||
global _FORCE_PROXY_SUFFIXES, _FORCE_PROXY_LOADED
|
|
||||||
|
domain-list-community 的三种规则语义:普通域名匹配自身及子域;
|
||||||
|
``full:`` 仅匹配该主机;``regexp:`` 按正则匹配。旧逻辑把后两类
|
||||||
|
当成带冒号的普通后缀,导致它们永远不命中。
|
||||||
|
"""
|
||||||
|
global _FORCE_PROXY_SUFFIXES, _FORCE_PROXY_EXACT, _FORCE_PROXY_REGEXES, _FORCE_PROXY_LOADED
|
||||||
if _FORCE_PROXY_LOADED:
|
if _FORCE_PROXY_LOADED:
|
||||||
return _FORCE_PROXY_SUFFIXES
|
return _FORCE_PROXY_SUFFIXES
|
||||||
_FORCE_PROXY_LOADED = True
|
_FORCE_PROXY_LOADED = True
|
||||||
@@ -1327,14 +1341,51 @@ def _load_force_proxy_suffixes():
|
|||||||
import json
|
import json
|
||||||
path = _user_path("pac_domains.json")
|
path = _user_path("pac_domains.json")
|
||||||
if path is None:
|
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"))
|
data = json.loads(Path(path).read_text("utf-8"))
|
||||||
suffixes = sorted({str(d).strip().lower().lstrip("*.")
|
suffixes = set(_CORE_FORCE_PROXY_SUFFIXES)
|
||||||
for d in (data.get("domains") or []) if str(d).strip()})
|
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
|
_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:
|
except Exception as e:
|
||||||
log_red(f"pac_domains.json 加载失败: {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
|
return _FORCE_PROXY_SUFFIXES
|
||||||
|
|
||||||
def _is_private_int(n: int) -> bool:
|
def _is_private_int(n: int) -> bool:
|
||||||
@@ -1386,12 +1437,16 @@ def _is_china_ip(host: str):
|
|||||||
|
|
||||||
@functools.lru_cache(maxsize=8192)
|
@functools.lru_cache(maxsize=8192)
|
||||||
def _is_force_proxy(host: str) -> bool:
|
def _is_force_proxy(host: str) -> bool:
|
||||||
"""pac_domains.json 命中的域名强制走隧道(如 claude.ai:国内可连但显示拒绝页)。"""
|
"""PAC 强制规则命中的域名走隧道(后缀 / full / regexp)。"""
|
||||||
suffixes = _load_force_proxy_suffixes()
|
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
|
return False
|
||||||
h = host.rstrip(".").lower()
|
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):
|
def _resolve_host_ips(host: str):
|
||||||
"""解析域名的 IPv4/IPv6 地址集合(字面量 IP 返回自身);解析失败返回空集合。"""
|
"""解析域名的 IPv4/IPv6 地址集合(字面量 IP 返回自身);解析失败返回空集合。"""
|
||||||
@@ -3092,4 +3147,4 @@ def main():
|
|||||||
os._exit(ret)
|
os._exit(ret)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<span class="code-label">Error code 200</span>
|
<span class="code-label">Error code 200</span>
|
||||||
</h1>
|
</h1>
|
||||||
<div>
|
<div>
|
||||||
Visit <a href="https://example.com" target="_blank" rel="noopener noreferrer">example.com</a> for more information.
|
Visit <a href="https://rrrosmontis.icu" target="_blank" rel="noopener noreferrer">rrrosmontis.icu</a> for more information.
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-3">2026-06-28 05:00:19 UTC</div>
|
<div class="mt-3">2026-06-28 05:00:19 UTC</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
<span class="cf-footer-separator sm:hidden">•</span>
|
<span class="cf-footer-separator sm:hidden">•</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="cf-footer-item sm:block sm:mb-1"><span>Performance & security by</span> <a rel="noopener noreferrer" href="https://example.com" id="brand_link" target="_blank">Example Site</a></span>
|
<span class="cf-footer-item sm:block sm:mb-1"><span>Performance & security by</span> <a rel="noopener noreferrer" href="https://rrrosmontis.icu" id="brand_link" target="_blank">Royirin Rosmontis</a></span>
|
||||||
</p>
|
</p>
|
||||||
</div><!-- /.error-footer -->
|
</div><!-- /.error-footer -->
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user