fix(dialer): evict dead SSHD sessions before reuse to prevent channel-open failure (#382)
When an SSH session behind an SSHD dialer dies silently (NAT timeout, server disconnect, idle TCP drop), IsClosed() may still return false because the health-check goroutine hasn't detected the failure yet. Returning this dead cached session to the connector causes: ssh: unexpected packet in response to channel open: Add a Ping(req)-based liveness check before returning a cached session. If the ping fails, evict the session so the next Dial rebuilds a fresh connection. This matches the dead-session eviction pattern already used by the SSH, KCP, QUIC, and ICMP dialers. Also export the ping-as-health-check as Session.Ping(timeout) so it can be used by external health probes. Retain the existing ping() helper unmodified.
This commit is contained in:
@@ -63,6 +63,18 @@ func (d *sshdDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
||||
delete(d.sessions, addr) // session is dead
|
||||
ok = false
|
||||
}
|
||||
if ok {
|
||||
// Health check: verify the cached session is still alive before
|
||||
// returning it. A dead-but-not-yet-detected session (e.g. idle
|
||||
// connection silently dropped by NAT) would cause "ssh: unexpected
|
||||
// packet in response to channel open" later in the connector.
|
||||
if err := session.Ping(d.md.keepaliveTimeout); err != nil {
|
||||
d.options.Logger.Debugf("sshd session ping failed: %v, recreating", err)
|
||||
session.Close()
|
||||
delete(d.sessions, addr)
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
var options dialer.DialOptions
|
||||
for _, opt := range opts {
|
||||
|
||||
Reference in New Issue
Block a user