Files
ykxmz/_build/fix_user_reports.py
T
xmz7891 b62ac865ea ykxmz 1.5.0:修复手表端 TLS 证书校验 + MTU 修复网络稳定性
被控端
- 修复 MTU 问题:夹 MSS + 路径 MTU 探测(公网可用率 48% -> 100%)
- 复用 HTTP 连接(免掉每次新建 TLS 的 30% 握手失败)
- 每分钟回头找局域网,自动切回快 400 倍的路径
- 画面心跳帧:静止时省流量,客户端仍知道流是活的
- 锁屏时给出可操作的提示

手表端
- 内置 GTS Root R4 / GlobalSign Root CA,修 SSLHandshakeException
- 连接错误翻译成人话(证书/时间/解析/超时分别提示)
- 经中转连上后用服务器报的地址刷新已配对记录
- 新增局域网连接页面、服务器延迟检测

服务器端
- 支持 chunked 请求体(修每 16 秒一次的连接错乱与大面积 502)
- 网络调优脚本 net-tune.sh(开机自动应用)
- 管理页:批量删除、网络体检、下载区
- 隧道改 HTTP/2 + 双连接

文档
- 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
2026-10-06 19:01:05 +08:00

209 lines
8.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""修用户报的 4 个问题。
【4|最要命】设完配对码之后配对被立刻关掉,手表永远连不上
run_setup() 最后调用 run_silent(store, None),而 None 表示
"不开配对窗口" → allow_new_pairing=False。
结果:用户刚设完码,被控端马上把门锁上,第一次配对必然失败。
改法:设完码先开一个 60 分钟的配对窗口;并且**第一台设备配对成功后
自动把窗口关上**(既安全又不用用户操心)。
【1】手表点「连不上」显示"已打开",电脑上其实什么都没有
静默模式下 open_diagnostics 只写日志,不弹窗口。
与其在电脑上弹窗口(用户要的是零窗口),不如**把诊断结果回传给手表**,
在手表上显示 —— 用户看的就是手表。
【3】程序不能自己退出
全局兜底后面在手表端处理;这里先把服务端的自检请求做得不会打断任何东西。
"""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\windows"
r"\lanremote")
DISCOVERY = ROOT / "discovery.py"
SERVER = ROOT / "server.py"
RUN = ROOT.parent / "run_agent.py"
FIXES = [
# ---------------- 4. 设完码要开配对窗口 ----------------
(RUN,
''' # 黑终端一闪:修防火墙
if result["firewall"]:
fix_firewall_with_flash(store)
return run_silent(store, None)''',
''' # 黑终端一闪:修防火墙
if result["firewall"]:
fix_firewall_with_flash(store)
# 关键:设完码必须开一个配对窗口。
# 之前这里传 None(= 不开窗口 → allow_new_pairing=False),
# 结果用户刚设完码,被控端立刻就把门锁上,手表第一次配对必然失败,
# 报"被控端已关闭新设备配对"。
# 第一台设备配对成功后服务端会自动把窗口关上。
store.set("close_pair_after_first", True)
return run_silent(store, SETUP_PAIR_MINUTES)'''),
(RUN,
'''# ======================================================================
# 黑终端一闪:提权修防火墙
# ======================================================================''',
'''# 设完配对码之后留多久给手表来配对
SETUP_PAIR_MINUTES = 60
# ======================================================================
# 黑终端一闪:提权修防火墙
# ======================================================================'''),
# ---------------- 1. 自检应答带上诊断结果 ----------------
(DISCOVERY,
'''def build_selfcheck_ack(name: str) -> bytes:
payload = {"lanremote": ACK_MAGIC, "name": name}
return json.dumps(payload, ensure_ascii=False).encode("utf-8")''',
'''def build_selfcheck_ack(name: str, note: str = "") -> bytes:
"""自检应答。
note 带上电脑这边的诊断结论 —— 手表拿到就直接显示,
不需要在被控端弹窗口(用户要的是全程无窗口)。
"""
payload = {"lanremote": ACK_MAGIC, "name": name, "note": note}
return json.dumps(payload, ensure_ascii=False).encode("utf-8")'''),
(DISCOVERY,
''' build_selfcheck_ack(str(self.store.get("server_name", ""))),''',
''' build_selfcheck_ack(
str(self.store.get("server_name", "")),
self._selfcheck_note),'''),
]
# on_selfcheck 回调允许返回一段说明
CALLBACK_OLD = ''' if self.on_selfcheck:
try:
self.on_selfcheck(addr[0])'''
CALLBACK_NEW = ''' if self.on_selfcheck:
try:
# 回调可以返回一段话,随 ACK 一起回给手表
produced = self.on_selfcheck(addr[0])
if isinstance(produced, str) and produced:
self._selfcheck_note = produced[:1500]'''
# 加字段
FIELD_OLD = " self.on_selfcheck = None"
FIELD_NEW = (" self.on_selfcheck = None\n"
" # 最近一次自检的结论,随 ACK 回给手表\n"
" self._selfcheck_note = \"\"")
def main() -> int:
applied, skipped = 0, []
for path, old, new in FIXES:
text = path.read_text(encoding="utf-8")
if old not in text:
skipped.append(old.splitlines()[0][:50])
print(f" [跳过] {path.name} :: {old.splitlines()[0][:50]}")
continue
path.write_text(text.replace(old, new, 1), encoding="utf-8")
applied += 1
print(f" {path.name}: OK")
# discovery 的回调与字段
text = DISCOVERY.read_text(encoding="utf-8")
for old, new, tag in ((CALLBACK_OLD, CALLBACK_NEW, "回调返回值"),
(FIELD_OLD, FIELD_NEW, "_selfcheck_note 字段")):
if old in text:
text = text.replace(old, new, 1)
applied += 1
print(f" discovery / {tag}: OK")
else:
skipped.append(tag)
print(f" [跳过] discovery / {tag}")
DISCOVERY.write_text(text, encoding="utf-8")
# ------------- server:自检回调返回诊断文本 + 配对后自动关窗口 -------------
stext = SERVER.read_text(encoding="utf-8")
old_self = ''' self._log(f"[自检] 收到来自 {from_ip} 的请求,正在打开网络自检")'''
new_self = ''' self._log(f"[自检] 收到来自 {from_ip} 的请求,正在生成诊断结论")
note = self._selfcheck_report(from_ip)'''
if old_self in stext:
stext = stext.replace(old_self, new_self, 1)
applied += 1
print(" server / 自检生成结论: OK")
# 在 _on_selfcheck_request 之前插入 _selfcheck_report
anchor = " def _on_selfcheck_request(self, from_ip: str) -> None:"
helper = ''' def _selfcheck_report(self, from_ip: str) -> str:
"""生成一段给手表看的诊断结论(不弹任何窗口)。"""
try:
from . import firewall
from .diagnostics import collect, verdict
data = collect(self, self.store)
level, text = verdict(data)
ips = data.get("ips") or []
port = self.port
blocked = data.get("firewall", {}).get("blocked_count", 0)
lines = [
f"电脑:{data.get('name') or '被控端'}",
f"本机地址:{', '.join(ips[:2]) if ips else '读不到'}",
f"监听端口:{port}",
f"收到过的连接:{data.get('tcp_connections', 0)} 次",
f"阻止规则:{blocked} 条",
"",
f"结论:{text}",
]
return "\\n".join(lines)
except Exception as exc: # noqa: BLE001
return f"电脑端诊断失败:{exc}"
'''
if anchor in stext and "_selfcheck_report" not in stext:
stext = stext.replace(anchor, helper + anchor, 1)
applied += 1
print(" server / _selfcheck_report: OK")
# 配对成功后自动关配对窗口
old_pair = ''' session.device_id = device_id
session.device_name = device_name
session.authed = True'''
new_pair = ''' session.device_id = device_id
session.device_name = device_name
session.authed = True
# 首次配对成功后自动把配对窗口关上 —— 用户设完码只需配一次,
# 之后不用再操心"要不要关配对"。
if self.store.get("close_pair_after_first"):
self.pairing_enabled = False
self.store.set("allow_new_pairing", False)
self.store.set("close_pair_after_first", False)
self._log("[配对] 首台设备已配对,配对窗口已自动关闭")'''
if old_pair in stext:
stext = stext.replace(old_pair, new_pair, 1)
applied += 1
print(" server / 首台配对后关窗口: OK")
SERVER.write_text(stext, encoding="utf-8")
print(f"\n 应用 {applied} 处")
if skipped:
print(f" 跳过: {skipped}")
import ast
ok = True
for label, path in (("discovery.py", DISCOVERY), ("server.py", SERVER),
("run_agent.py", RUN)):
try:
ast.parse(path.read_text(encoding="utf-8"))
print(f" {label}: 语法 OK")
except SyntaxError as exc:
print(f" {label}: 语法错误 {exc}")
ok = False
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())