diff --git a/lite/build_nuitka.bat b/lite/build_nuitka.bat index cfd384d..dcdc31d 100644 --- a/lite/build_nuitka.bat +++ b/lite/build_nuitka.bat @@ -26,6 +26,8 @@ call ..\.venv\Scripts\python.exe -m nuitka ^ --include-data-files=runtime/LICENSE.tun2socks.txt=LICENSE.tun2socks.txt ^ --include-data-files=runtime/LICENSE.wintun.txt=LICENSE.wintun.txt ^ --include-data-files=runtime/README.txt=README.txt ^ + --include-data-files=runtime/tun2socks.exe=tun2socks.exe ^ + --include-data-files=runtime/wintun.dll=wintun.dll ^ --windows-icon-from-ico=ico.ico ^ --windows-console-mode=disable ^ --company-name="Global Rosmanka Cloud Nexus Inc" ^ diff --git a/lite/main.py b/lite/main.py index fcb823a..e6478b3 100644 --- a/lite/main.py +++ b/lite/main.py @@ -1056,7 +1056,7 @@ def _verify_proxy_connectivity(timeout=10, opener=None, cancel_event=None): response.read(1) _raise_if_start_cancelled(cancel_event) results[name] = True - log(f" {name} 可达") + log_detail(f" {name} 可达") except _StartCancelled: raise except Exception as e: @@ -1064,7 +1064,7 @@ def _verify_proxy_connectivity(timeout=10, opener=None, cancel_event=None): # 这不是“开启失败”,也不能在稍后把界面重新切成已开启。 _raise_if_start_cancelled(cancel_event) results[name] = False - log(f" {name} 不可达: {e}") + log_detail(f" {name} 不可达: {e}") if required: raise RuntimeError(f"境外隧道验证失败({name}):{e}") from e return results @@ -1116,7 +1116,7 @@ def _tls_start(remote_host, remote_port=443, use_tls=True, bypass_lan=False, sma svr.close() svr = None if attempt < 2: - log(f"端口 {_TLS_LOCAL_PORT} 绑定失败: {e},1 秒后重试 ({attempt + 1}/3)") + log_detail(f"端口 {_TLS_LOCAL_PORT} 绑定失败: {e},1 秒后重试 ({attempt + 1}/3)") time.sleep(1) if svr is None: raise last_err @@ -2259,7 +2259,7 @@ def _connect_route(direct_target, allow_direct_fallback, remote_host, remote_por except OSError as e: if not allow_direct_fallback: raise - log(f"[转发器] 国内直连 {direct_target[0]} 失败,回退上游隧道: {e}") + log_detail(f"[转发器] 国内直连 {direct_target[0]} 失败,回退上游隧道: {e}") if jump_chain: if not use_tls: raise RuntimeError("跳板模式仅支持 TLS 上游") @@ -2381,7 +2381,7 @@ def _tls_relay(client, remote_host, remote_port, ctx, use_tls, bypass_lan=False, else: log_detail(f"[连接 #{connection_id}] 无法解析目标: {first_line or '空请求'},客户端={peer_text}") except Exception as e: - log(f"[转发器] 读取首包失败: {e}") + log_detail(f"[转发器] 读取首包失败: {e}") log_detail(f"[连接 #{connection_id}] 失败: 读取首包,目标={target_text},规则匹配={rule_match},错误={e}") client.close() return @@ -2414,7 +2414,7 @@ def _tls_relay(client, remote_host, remote_port, ctx, use_tls, bypass_lan=False, if via_tunnel: _traffic_add(up=len(buf)) except Exception as e: - log(f"[转发器] 连接目标失败: {e}") + log_detail(f"[转发器] 连接目标失败: {e}") log_detail(f"[连接 #{connection_id}] 连接失败: 目标={target_text},规则匹配={rule_match},路径={route_summary},错误={e}") try: client.close() @@ -2691,7 +2691,8 @@ def _make_tun_backend(emit): engine = Path(BASE_DIR) / "runtime" / "tun2socks.exe" if not engine.is_file() or not (engine.parent / "wintun.dll").is_file(): raise RuntimeError("单文件包中的 tun2socks.exe 或 wintun.dll 缺失") - return TunBackend(_USER_DIR, engine_path=engine, log=emit, detail_log=emit) + detail_emit = getattr(emit, "detail", emit) + return TunBackend(_USER_DIR, engine_path=engine, log=emit, detail_log=detail_emit) def _start_tun_backend(backend, payload): @@ -2783,7 +2784,7 @@ class MainWindow(QWidget): super().__init__() self._proxy_set_by_us = False # 系统代理是否由本程序开启(退出时只清理自己设置的) self._mode = _proxy_mode() - self._tun_controller = TunModeController(_tun_worker_command(), log) + self._tun_controller = TunModeController(_tun_worker_command(), log, log_detail) self._quitting = False self._tray = None self._tray_menu = None @@ -3224,11 +3225,11 @@ class MainWindow(QWidget): _traffic_reset() current_node = _current_node() jump_chain = _jump_chain(current_node) - log("TLS 多跳模式" if jump_chain else "TLS 直连模式") + log_detail("TLS 多跳模式" if jump_chain else "TLS 直连模式") ep = _endpoint_domain() - log(f" 目标域名: {ep}") + log_detail(f" 目标域名: {ep}") if jump_chain: - log(f" 跳板路径: {_jump_route_text(current_node)}") + log_detail(f" 跳板路径: {_jump_route_text(current_node)}") # PAC 模式:全部流量进转发器,按域名表/解析 IP 智能分流 use_internal_pac = _should_use_internal_pac(use_pac) if use_internal_pac: @@ -3249,7 +3250,7 @@ class MainWindow(QWidget): # 在内存中启动本地 PAC HTTP 服务,不往 EXE 目录写任何文件 pac_port = _start_pac_server(pac_content) pac_url = f"http://127.0.0.1:{pac_port}/proxy.pac" - log(f"使用 PAC: {pac_url}") + log_detail(f"使用 PAC: {pac_url}") with self._proxy_state_lock: _raise_if_start_cancelled(cancel_event) proxy_on(pac_url=pac_url) @@ -3260,10 +3261,10 @@ class MainWindow(QWidget): proxy_on(bypass_lan=use_bypass_lan) self._proxy_set_by_us = True if use_internal_pac: - log(f"使用内置 PAC:系统固定代理 127.0.0.1:{PROXY_PORT},转发器内部智能分流") + log_detail(f"使用内置 PAC:系统固定代理 127.0.0.1:{PROXY_PORT},转发器内部智能分流") _raise_if_start_cancelled(cancel_event) if use_bypass_lan: - log("不代理内网 IP:内网地址将直连") + log_detail("不代理内网 IP:内网地址将直连") log("验证代理连通性...") _verify_proxy_connectivity(cancel_event=cancel_event) except Exception: diff --git a/lite/rrpp_core/tun_mode.py b/lite/rrpp_core/tun_mode.py index dec494c..024757c 100644 --- a/lite/rrpp_core/tun_mode.py +++ b/lite/rrpp_core/tun_mode.py @@ -46,9 +46,11 @@ def _receive(reader) -> dict[str, Any]: class TunModeController: """普通权限 GUI 仅在首次开启 TUN 时启动管理员工作进程。""" - def __init__(self, command: list[str], log: Callable[[str], None]): + def __init__(self, command: list[str], log: Callable[[str], None], + detail_log: Callable[[str], None] | None = None): self.command = command self.log = log + self.detail_log = detail_log or log self._lock = threading.RLock() self._socket: socket.socket | None = None self._reader = None @@ -129,7 +131,8 @@ class TunModeController: while True: message = _receive(self._reader) if message.get("type") == "log": - self.log(str(message.get("message") or "")) + callback = self.detail_log if message.get("detail") else self.log + callback(str(message.get("message") or "")) continue if message.get("type") != "result": raise TunModeError("TUN 工作进程响应无效") @@ -192,12 +195,22 @@ def run_tun_worker(port: int, token: str, backend_factory, send_lock = threading.Lock() _send(connection, {"type": "hello", "token": token}, send_lock) - def emit(message: str) -> None: + def emit(message: str, detail: bool = False) -> None: try: - _send(connection, {"type": "log", "message": str(message)}, send_lock) + _send(connection, {"type": "log", "message": str(message), + "detail": bool(detail)}, send_lock) except OSError: pass + class _WorkerLogger: + def __call__(self, message: str) -> None: + emit(message) + + def detail(self, message: str) -> None: + emit(message, detail=True) + + logger = _WorkerLogger() + backend = None try: while True: @@ -213,7 +226,7 @@ def run_tun_worker(port: int, token: str, backend_factory, # 延迟创建后端,使初始化失败也能通过控制协议返回具体原因, # 避免 GUI 只看到 Windows 的 10053 连接中止错误。 if backend is None: - backend = backend_factory(emit) + backend = backend_factory(logger) start_backend(backend, request["payload"]) value = None elif command == "stop":