From cc0702396c0536ef9b0db574faf9344a5a9b5326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20Papp?= Date: Tue, 11 Aug 2026 16:50:22 +0200 Subject: [PATCH] [android] bound the SSH handshake with a deadline DialContext limited only the TCP establishment, so a peer that accepted the connection and then stayed silent left gossh.NewClientConn blocking forever and the terminal stuck on "Connecting". Set the socket deadline from the dial context before the handshake and clear it on success, so the handshake shares the dial timeout instead of being able to hang. Verified against a silent listener: the connect now returns i/o timeout instead of blocking. --- client/android/ssh_client.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/client/android/ssh_client.go b/client/android/ssh_client.go index f57a8f056..43c54d18a 100644 --- a/client/android/ssh_client.go +++ b/client/android/ssh_client.go @@ -435,6 +435,16 @@ func (s *SSHClient) dialAndHandshake(host string, port int, clientConfig *gossh. return fmt.Errorf("dial %s: %w", addr, err) } + // DialContext bounds only the TCP establishment; without a deadline on the + // socket a peer that accepts and then goes silent blocks the handshake + // forever. + if deadline, ok := ctx.Deadline(); ok { + if err := conn.SetDeadline(deadline); err != nil { + closeQuiet(conn, "conn after deadline error") + return fmt.Errorf("set handshake deadline: %w", err) + } + } + sshConn, chans, reqs, err := gossh.NewClientConn(conn, addr, clientConfig) if err != nil { if cerr := conn.Close(); cerr != nil { @@ -443,6 +453,11 @@ func (s *SSHClient) dialAndHandshake(host string, port int, clientConfig *gossh. return fmt.Errorf("ssh handshake: %w", err) } + if err := conn.SetDeadline(time.Time{}); err != nil { + closeQuiet(sshConn, "ssh conn after deadline clear error") + return fmt.Errorf("clear handshake deadline: %w", err) + } + s.mu.Lock() s.sshClient = gossh.NewClient(sshConn, chans, reqs) listener := s.listener