Browse Source

go.crypto/ssh: support OpenSSH keepalives
Fixes golang/go#4552.

R=minux.ma, agl
CC=golang-dev
https://golang.org/cl/6948059

Eric Milliken 13 years ago
parent
commit
61ab4d36df
3 changed files with 46 additions and 1 deletions
  1. 6 0
      ssh/client.go
  2. 7 1
      ssh/session.go
  3. 33 0
      ssh/session_test.go

+ 6 - 0
ssh/client.go

@@ -309,6 +309,12 @@ func (c *ClientConn) mainLoop() {
 					// invalid window update
 					return
 				}
+			case *globalRequestMsg:
+				// This handles keepalive messages and matches
+				// the behaviour of OpenSSH.
+				if msg.WantReply {
+					c.writePacket(marshal(msgRequestFailure, globalRequestFailureMsg{}))
+				}
 			case *globalRequestSuccessMsg, *globalRequestFailureMsg:
 				c.globalRequest.response <- msg
 			case *disconnectMsg:

+ 7 - 1
ssh/session.go

@@ -404,7 +404,13 @@ func (s *Session) wait() error {
 				}
 				wm.lang = safeString(string(lang))
 			default:
-				return fmt.Errorf("wait: unexpected channel request: %v", msg)
+				// This handles keepalives and matches
+				// OpenSSH's behaviour.
+				if msg.WantReply {
+					s.writePacket(marshal(msgChannelFailure, channelRequestFailureMsg{
+						PeersId: s.remoteId,
+					}))
+				}
 			}
 		default:
 			return fmt.Errorf("wait: unexpected packet %T received: %v", msg, msg)

+ 33 - 0
ssh/session_test.go

@@ -498,6 +498,24 @@ func TestServerWindow(t *testing.T) {
 	}
 }
 
+// Verify the client can handle a keepalive packet from the server.
+func TestClientHandlesKeepalives(t *testing.T) {
+	conn := dial(channelKeepaliveSender, t)
+	defer conn.Close()
+	session, err := conn.NewSession()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer session.Close()
+	if err := session.Shell(); err != nil {
+		t.Fatalf("Unable to execute command: %v", err)
+	}
+	err = session.Wait()
+	if err != nil {
+		t.Fatalf("expected nil but got: %v", err)
+	}
+}
+
 type exitStatusMsg struct {
 	PeersId   uint32
 	Request   string
@@ -687,3 +705,18 @@ func copyNRandomly(title string, dst io.Writer, src io.Reader, n int) (int, erro
 	}
 	return written, nil
 }
+
+func channelKeepaliveSender(ch *serverChan, t *testing.T) {
+	defer ch.Close()
+	shell := newServerShell(ch, "> ")
+	readLine(shell, t)
+	msg := channelRequestMsg{
+		PeersId:   ch.remoteId,
+		Request:   "keepalive@openssh.com",
+		WantReply: true,
+	}
+	if err := ch.writePacket(marshal(msgChannelRequest, msg)); err != nil {
+		t.Errorf("unable to send channel keepalive request: %v", err)
+	}
+	sendStatus(0, ch, t)
+}