From 61f161d8a6dffc3aae60e94bab8de3095b47a9e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?lollipopkit=F0=9F=8F=B3=EF=B8=8F=E2=80=8D=E2=9A=A7?= =?UTF-8?q?=EF=B8=8F?= <10864310+lollipopkit@users.noreply.github.com> Date: Thu, 15 Jan 2026 20:52:11 +0800 Subject: [PATCH] opt. --- lib/core/utils/server.dart | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/core/utils/server.dart b/lib/core/utils/server.dart index 47896deb..7462cb11 100644 --- a/lib/core/utils/server.dart +++ b/lib/core/utils/server.dart @@ -177,8 +177,8 @@ Future _genClientInternal( String? alterUser; - final socket = await () async { - if (socketOverride != null) return socketOverride; + final (socket, hopClients) = await () async { + if (socketOverride != null) return (socketOverride, []); if (followJumpConfig) { final injectedSpiMap = {}; @@ -269,7 +269,8 @@ Future _genClientInternal( createdClients.add(currentClient); } - return await currentClient!.forwardLocal(spi.ip, spi.port); + final forwardedSocket = await currentClient!.forwardLocal(spi.ip, spi.port); + return (forwardedSocket, createdClients); } catch (e) { // Close all created clients on error to avoid leaks for (final client in createdClients) { @@ -288,14 +289,14 @@ Future _genClientInternal( // Direct try { - return await SSHSocket.connect(spi.ip, spi.port, timeout: timeout); + return (await SSHSocket.connect(spi.ip, spi.port, timeout: timeout), []); } catch (e) { Loggers.app.warning('genClient', e); if (spi.alterUrl == null) rethrow; try { final res = spi.parseAlterUrl(); alterUser = res.$2; - return await SSHSocket.connect(res.$1, res.$3, timeout: timeout); + return (await SSHSocket.connect(res.$1, res.$3, timeout: timeout), []); } catch (e) { Loggers.app.warning('genClient alterUrl', e); rethrow; @@ -339,7 +340,23 @@ Future _genClientInternal( ); } - return await buildClient(socket); + final client = await buildClient(socket); + + // Tie hop clients' lifetime to the final client: close all hop clients + // when the target client disconnects to avoid leaking SSH connections. + if (hopClients.isNotEmpty) { + client.done.whenComplete(() { + for (final hopClient in hopClients) { + try { + hopClient.close(); + } catch (_) { + // Ignore close errors during cleanup + } + } + }); + } + + return client; } typedef _HostKeyPersistCallback = void Function(String storageKey, String fingerprintHex);