From 55aa08130e6a3dc3c4078f2f939f415f7391c540 Mon Sep 17 00:00:00 2001 From: Dave Cheney Date: Tue, 22 May 2012 12:04:51 +1000 Subject: [PATCH] go.crypto/ssh: allow zero sized window adjustments The RFC doesn't prohibit zero sized window adjustments and there is evidence of well known clients using them in the wild. R=agl, kardianos, gustav.paul CC=golang-dev https://golang.org/cl/6209082 --- ssh/common.go | 3 ++- ssh/session_test.go | 53 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/ssh/common.go b/ssh/common.go index 1c07045..9cb740d 100644 --- a/ssh/common.go +++ b/ssh/common.go @@ -300,8 +300,9 @@ type window struct { // add adds win to the amount of window available // for consumers. func (w *window) add(win uint32) bool { + // a zero sized window adjust is a noop. if win == 0 { - return false + return true } w.L.Lock() if w.win+win < win { diff --git a/ssh/session_test.go b/ssh/session_test.go index 041da65..aaeae48 100644 --- a/ssh/session_test.go +++ b/ssh/session_test.go @@ -294,6 +294,50 @@ func TestInvalidServerMessage(t *testing.T) { defer session.Close() } +// In the wild some clients (and servers) send zero sized window updates. +// Test that the client can continue after receiving a zero sized update. +func TestClientZeroWindowAdjust(t *testing.T) { + conn := dial(sendZeroWindowAdjust, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %s", err) + } + defer session.Close() + + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %s", err) + } + err = session.Wait() + if err != nil { + t.Fatalf("expected nil but got %s", err) + } +} + +// In the wild some clients (and servers) send zero sized window updates. +// Test that the server can continue after receiving a zero size update. +func TestServerZeroWindowAdjust(t *testing.T) { + conn := dial(exitStatusZeroHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %s", err) + } + defer session.Close() + + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %s", err) + } + + // send a bogus zero sized window update + session.clientChan.sendWindowAdj(0) + + err = session.Wait() + if err != nil { + t.Fatalf("expected nil but got %s", err) + } +} + type exitStatusMsg struct { PeersId uint32 Request string @@ -403,3 +447,12 @@ func sendInvalidRecord(ch *serverChan) { ch.serverConn.writePacket(packet) } + +func sendZeroWindowAdjust(ch *serverChan) { + defer ch.Close() + // send a bogus zero sized window update + ch.sendWindowAdj(0) + shell := newServerShell(ch, "> ") + shell.ReadLine() + sendStatus(0, ch) +}