# -*- coding: utf-8 -*- """三个修复: 1. 【关键】被控端发请求要带 User-Agent Cloudflare 的 Browser Integrity Check 把 Python 默认 UA (Python-urllib/3.11)当成可疑客户端,返回 403 + error code 1010。 实测 LanRemote/1.4.0 能过。 不修的话:固定域名上线后,被控端根本连不上。 (手表端 Java 已经带了 LanRemote-Watch/1.4,没问题) 2. setup_named_tunnel.sh 写的 public_url.txt 少了 https:// 导致 /url 接口和 status 显示"还没有地址"。 3. install.sh 里钉的 Cloudflare API IP 是错的 我之前猜的 104.16.132.229 返回的是错误页(error code 1034), 不是 API。真实地址要用公共 DNS 查(104.19.192.174 等)。 """ from __future__ import annotations import sys from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") RELAY = ROOT / "windows" / "lanremote" / "relay.py" SETUP = ROOT / "_build" / "setup_named_tunnel.sh" INSTALL = ROOT / "relay" / "install.sh" # ---------------- 1. relay.py 加 UA ---------------- UA_CONST_ANCHOR = '''# 私有网段 + 回环 + mDNS 名字''' UA_CONST = '''# 请求头。**必须带 User-Agent**: # Cloudflare 的 Browser Integrity Check 会把 Python 默认 UA # (Python-urllib/3.11)判为可疑客户端,返回 403 + error code 1010。 # 实测 LanRemote/1.4.0 能过,用自家标识比伪装浏览器合适。 USER_AGENT = "LanRemote/1.4.0" # 私有网段 + 回环 + mDNS 名字''' UA_PROBE_OLD = ''' request = urllib.request.Request(base + "/v1/health")''' UA_PROBE_NEW = ''' request = urllib.request.Request( base + "/v1/health", headers={"User-Agent": USER_AGENT})''' # ---------------- 2. setup_named_tunnel.sh 补 https:// ---------------- SETUP_OLD = '''echo "$HOSTNAME_FULL" > "$RELAY_DIR/public_url.txt"''' SETUP_NEW = '''# 一定要带 https:// —— public_url() 只认以 http 开头的, # 少了协议头 /url 接口会显示"还没有地址"。 echo "https://$HOSTNAME_FULL" > "$RELAY_DIR/public_url.txt"''' # ---------------- 3. install.sh 换真实 IP ---------------- INSTALL_OLD = ''' echo "104.16.132.229 api.cloudflare.com" echo "104.16.133.229 api.cloudflare.com"''' INSTALL_NEW = ''' # 这几个是用公共 DNS(1.1.1.1 / 8.8.8.8 / 223.5.5.5)查出来的 # 真实地址。之前随手挑的 104.16.132.229 是错的 —— # 它返回 Cloudflare 错误页(error code: 1034)而不是 API, # 会导致 cloudflared tunnel create 报 unauthorized。 echo "104.19.192.174 api.cloudflare.com" echo "104.19.192.175 api.cloudflare.com" echo "104.19.192.176 api.cloudflare.com" echo "104.19.192.177 api.cloudflare.com" echo "104.17.0.1 api.cloudflare.com" echo "104.18.0.1 api.cloudflare.com"''' # 检查逻辑也要跟着改:现在该看响应是不是 API JSON,不是只看状态码 INSTALL_CHECK_OLD = '''if is_real_cf "$CF_IP" && { [ "$CF_CODE" = "403" ] || [ "$CF_CODE" = "200" ]; }; then echo " ✓ 正常(没被劫持)"''' INSTALL_CHECK_NEW = '''CF_BODY=$(timeout 12 curl -4 -s https://api.cloudflare.com/client/v4/user 2>/dev/null | head -c 200) if is_real_cf "$CF_IP" && echo "$CF_BODY" | grep -q '"success"'; then echo " ✓ 正常(返回的是真正的 API JSON)"''' INSTALL_CHECK2_OLD = ''' if [ "$CF_CODE2" = "403" ] || [ "$CF_CODE2" = "200" ]; then echo " ✓ 修好了"''' INSTALL_CHECK2_NEW = ''' CF_BODY2=$(timeout 12 curl -4 -s https://api.cloudflare.com/client/v4/user 2>/dev/null | head -c 200) if echo "$CF_BODY2" | grep -q '"success"'; then echo " ✓ 修好了(返回的是真正的 API JSON)"''' def patch(path: Path, pairs, label: str) -> bool: text = path.read_text(encoding="utf-8") ok = True for old, new, tag in pairs: if old not in text: print(f" [{label}] [跳过] {tag} —— 锚点没找到") ok = False continue text = text.replace(old, new, 1) print(f" [{label}] {tag}") path.write_text(text, encoding="utf-8", newline="\n") raw = path.read_bytes() if b"\r\n" in raw and path.suffix == ".sh": path.write_bytes(raw.replace(b"\r\n", b"\n")) print(f" [{label}] 顺手修了 CRLF") return ok def main() -> int: ok = True ok &= patch(RELAY, [ (UA_CONST_ANCHOR, UA_CONST, "加 USER_AGENT 常量"), (UA_PROBE_OLD, UA_PROBE_NEW, "_probe 带 UA"), ], "relay.py") # _post 那处:把 headers 里补上 UA text = RELAY.read_text(encoding="utf-8") post_old = ''' request = urllib.request.Request(self.url + path, data=body,''' if post_old in text: # 找 _post 里构造 headers 的地方,统一并入 UA text = text.replace( post_old, ''' headers = dict(headers or {}) headers.setdefault("User-Agent", USER_AGENT) request = urllib.request.Request(self.url + path, data=body,''', 1) RELAY.write_text(text, encoding="utf-8", newline="\n") print(" [relay.py] _post 带 UA") else: print(" [relay.py] [跳过] _post 锚点没找到") ok &= patch(SETUP, [(SETUP_OLD, SETUP_NEW, "public_url.txt 补 https://")], "setup_named_tunnel.sh") ok &= patch(INSTALL, [ (INSTALL_OLD, INSTALL_NEW, "换成真实 IP"), (INSTALL_CHECK_OLD, INSTALL_CHECK_NEW, "判断改成看 JSON"), (INSTALL_CHECK2_OLD, INSTALL_CHECK2_NEW, "第二处判断"), ], "install.sh") import ast try: ast.parse(RELAY.read_text(encoding="utf-8")) print(" relay.py 语法 OK") except SyntaxError as exc: print(f" relay.py 语法错误: {exc}") ok = False return 0 if ok else 1 if __name__ == "__main__": sys.exit(main())