diff --git a/go.mod b/go.mod index 7ea1cd2bc..d0fe898db 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( ) require ( - charm.land/ssh v0.4.2 + charm.land/ssh v0.4.3 github.com/cilium/ebpf v0.22.0 github.com/gokrazy/tools v0.0.0-20260703063348-3fe400c13246 github.com/gokrazy/updater v0.0.0-20260620140544-0a84d8ab3878 diff --git a/go.sum b/go.sum index ada602166..8277f1f17 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ charm.land/lipgloss/v2 v2.0.5 h1:kbNxgeeUOYv5J0YdpxFjfvf3dFvqH8Aci4zB6xqFtrY= charm.land/lipgloss/v2 v2.0.5/go.mod h1:9oqhxt4yxIMe6q5A4kHr44DremZk7J9UNh74GlWa5nc= charm.land/log/v2 v2.0.0 h1:SY3Cey7ipx86/MBXQHwsguOT6X1exT94mmJRdzTNs+s= charm.land/log/v2 v2.0.0/go.mod h1:c3cZSRqm20qUVVAR1WmS/7ab8bgha3C6G7DjPcaVZz0= -charm.land/ssh v0.4.2 h1:mpJW8KuCQSu5mn4L9cRtDQpVtUNa/JwbqbOHyB/H1lI= -charm.land/ssh v0.4.2/go.mod h1:so/3IECPNlYZSnE7JKn7NFmcUyyxJqIAeM4TJy35qPk= +charm.land/ssh v0.4.3 h1:hr5cYmlUYsP+KxyG/ug7ZMzKKN9f6VOD9yPpcGqdzzM= +charm.land/ssh v0.4.3/go.mod h1:so/3IECPNlYZSnE7JKn7NFmcUyyxJqIAeM4TJy35qPk= charm.land/wish/v2 v2.0.3 h1:Xkgw31lEH9AJkPfgXYYvsgrskfDIY9ffHTxFRV4UT+4= charm.land/wish/v2 v2.0.3/go.mod h1:i8gFfXu+IyMcGpRh6D84Wa+mDGwjYCKWcA86R+IJf0c= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= diff --git a/vendor/charm.land/ssh/.golangci.yml b/vendor/charm.land/ssh/.golangci.yml index c3b50844d..c90f03161 100644 --- a/vendor/charm.land/ssh/.golangci.yml +++ b/vendor/charm.land/ssh/.golangci.yml @@ -30,33 +30,12 @@ linters: - text: '(slog|log)\.\w+' linters: - noctx - - text: "var-naming" - linters: - - revive - # This package is a thin wrapper around x/crypto/ssh and the stdlib. - # Errors are passed through as-is by design; wrapping them adds no - # value to the caller. - - text: "error returned from (external package|interface method)" - linters: - - wrapcheck - # Port numbers, terminal dimensions, and exit codes are all bounded - # by the SSH protocol and cannot overflow in practice. - - text: "G115: integer overflow conversion" - linters: - - gosec - # net.Listen in this package is for server-side sockets, not HTTP - # client requests. - - text: "net.Listen must not be called" - linters: - - noctx - # The accept loop backoff pattern matches net/http and is not - # improved by further extraction. - - text: "`if e != nil` has complex nested blocks" - linters: - - nestif generated: lax presets: - common-false-positives + settings: + exhaustive: + default-signifies-exhaustive: true issues: max-issues-per-linter: 0 max-same-issues: 0 diff --git a/vendor/charm.land/ssh/conn.go b/vendor/charm.land/ssh/conn.go index d8dca8dee..93cac4173 100644 --- a/vendor/charm.land/ssh/conn.go +++ b/vendor/charm.land/ssh/conn.go @@ -3,16 +3,39 @@ package ssh import ( "context" "net" + "sync" "time" ) type serverConn struct { net.Conn - idleTimeout time.Duration + idleTimeout time.Duration + maxDeadline time.Time + closeCanceler context.CancelFunc + + // handshakeDeadline is cleared once the handshake completes, which happens + // after gossh.NewServerConn has started goroutines that read it via + // updateDeadline. Access it only through the accessors below. + mu sync.Mutex handshakeDeadline time.Time - maxDeadline time.Time - closeCanceler context.CancelFunc +} + +// setHandshakeDeadline bounds how long the handshake may take. +func (c *serverConn) setHandshakeDeadline(t time.Time) { + c.mu.Lock() + c.handshakeDeadline = t + c.mu.Unlock() + c.updateDeadline() +} + +// clearHandshakeDeadline drops the handshake deadline once the handshake has +// completed, leaving the idle and max deadlines to govern the connection. +func (c *serverConn) clearHandshakeDeadline() { + c.mu.Lock() + c.handshakeDeadline = time.Time{} + c.mu.Unlock() + c.updateDeadline() } func (c *serverConn) Write(p []byte) (n int, err error) { @@ -46,10 +69,16 @@ func (c *serverConn) Close() (err error) { } func (c *serverConn) updateDeadline() { + c.mu.Lock() + handshakeDeadline := c.handshakeDeadline + c.mu.Unlock() + + // idleTimeout and maxDeadline are set before the handshake starts and never + // mutated afterwards, so they need no locking. deadline := c.maxDeadline - if !c.handshakeDeadline.IsZero() && (deadline.IsZero() || c.handshakeDeadline.Before(deadline)) { - deadline = c.handshakeDeadline + if !handshakeDeadline.IsZero() && (deadline.IsZero() || handshakeDeadline.Before(deadline)) { + deadline = handshakeDeadline } if c.idleTimeout > 0 { diff --git a/vendor/charm.land/ssh/server.go b/vendor/charm.land/ssh/server.go index 98d5e8512..ef7a081b8 100644 --- a/vendor/charm.land/ssh/server.go +++ b/vendor/charm.land/ssh/server.go @@ -415,7 +415,7 @@ func (srv *Server) handleConn(newConn net.Conn) { conn.maxDeadline = time.Now().Add(srv.MaxTimeout) } if srv.HandshakeTimeout > 0 { - conn.handshakeDeadline = time.Now().Add(srv.HandshakeTimeout) + conn.setHandshakeDeadline(time.Now().Add(srv.HandshakeTimeout)) } conn.updateDeadline() defer func() { _ = conn.Close() }() @@ -431,8 +431,7 @@ func (srv *Server) handleConn(newConn net.Conn) { } return } - conn.handshakeDeadline = time.Time{} - conn.updateDeadline() + conn.clearHandshakeDeadline() if err := extractPublicKeyFromPermissions(ctx, sshConn); err != nil { if srv.ConnectionFailedCallback != nil { diff --git a/vendor/modules.txt b/vendor/modules.txt index 07b1d8377..d3e480fc9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -14,7 +14,7 @@ charm.land/lipgloss/v2 # charm.land/log/v2 v2.0.0 ## explicit; go 1.25.8 charm.land/log/v2 -# charm.land/ssh v0.4.2 +# charm.land/ssh v0.4.3 ## explicit; go 1.25.0 charm.land/ssh # charm.land/wish/v2 v2.0.3