Problem
dialSSH uses DialContext for TCP, but the following ssh.NewClientConn handshake has no context cancellation hook. A peer that accepts TCP without completing SSH leaves a canceled request blocked. Setting the original context deadline on the socket does not handle cancellation before that deadline or a context without a deadline.
Reproduction
Base: a1e6561e366455756c45a16a46647f882b3b6a1c.
Use the existing isolated SSH test fixture and point its alias at a loopback listener that accepts TCP but never sends its SSH identification. Begin dialSSH with context.WithCancel, read the client's SSH- identification at the peer to prove the handshake has started, and cancel. The dial remains blocked until the peer is closed. The same happens when explicitly canceling a context whose deadline is still 30 seconds away.
Expected behavior
Cancellation promptly interrupts the in-progress handshake and returns an error wrapping context.Canceled. After a successful handshake, canceling the original dial context must not close the established shared SSH client or force later requests to reconnect.
Proposed fix and validation
Close the raw connection via a context callback during the handshake, then stop/join the callback before publishing a successful shared client. Regression tests cover cancellation without a deadline, cancellation before a deadline, and a real SSH echo round-trip proving the completed shared tunnel outlives the dial context.
I would like to take this focused lifecycle fix because canceled diagnostics should not remain stuck in SSH setup. Host-key verification, authentication policy and server-side DNS behavior from #47 remain unchanged.
Problem
dialSSHusesDialContextfor TCP, but the followingssh.NewClientConnhandshake has no context cancellation hook. A peer that accepts TCP without completing SSH leaves a canceled request blocked. Setting the original context deadline on the socket does not handle cancellation before that deadline or a context without a deadline.Reproduction
Base:
a1e6561e366455756c45a16a46647f882b3b6a1c.Use the existing isolated SSH test fixture and point its alias at a loopback listener that accepts TCP but never sends its SSH identification. Begin
dialSSHwithcontext.WithCancel, read the client'sSSH-identification at the peer to prove the handshake has started, and cancel. The dial remains blocked until the peer is closed. The same happens when explicitly canceling a context whose deadline is still 30 seconds away.Expected behavior
Cancellation promptly interrupts the in-progress handshake and returns an error wrapping
context.Canceled. After a successful handshake, canceling the original dial context must not close the established shared SSH client or force later requests to reconnect.Proposed fix and validation
Close the raw connection via a context callback during the handshake, then stop/join the callback before publishing a successful shared client. Regression tests cover cancellation without a deadline, cancellation before a deadline, and a real SSH echo round-trip proving the completed shared tunnel outlives the dial context.
I would like to take this focused lifecycle fix because canceled diagnostics should not remain stuck in SSH setup. Host-key verification, authentication policy and server-side DNS behavior from #47 remain unchanged.