go.crypto/ssh: close channel feeding tcpListener.

Close both on closing the listener, and on closing the
connection. Test the former case.

R=dave
CC=golang-dev
https://golang.org/cl/11349043
This commit is contained in:
Han-Wen Nienhuys
2013-07-22 21:50:13 +10:00
committed by Dave Cheney
parent 396847505b
commit 7f7cbbf18e
3 changed files with 68 additions and 8 deletions
+2 -1
View File
@@ -210,7 +210,8 @@ func (c *ClientConn) kexDH(group *dhGroup, hashFunc crypto.Hash, magics *handsha
func (c *ClientConn) mainLoop() { func (c *ClientConn) mainLoop() {
defer func() { defer func() {
c.Close() c.Close()
c.closeAll() c.chanList.closeAll()
c.forwardList.closeAll()
}() }()
for { for {
+13
View File
@@ -101,17 +101,30 @@ func (l *forwardList) add(addr net.TCPAddr) chan forward {
return f.c return f.c
} }
// remove removes the forward entry, and the channel feeding its
// listener.
func (l *forwardList) remove(addr net.TCPAddr) { func (l *forwardList) remove(addr net.TCPAddr) {
l.Lock() l.Lock()
defer l.Unlock() defer l.Unlock()
for i, f := range l.entries { for i, f := range l.entries {
if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port { if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port {
l.entries = append(l.entries[:i], l.entries[i+1:]...) l.entries = append(l.entries[:i], l.entries[i+1:]...)
close(f.c)
return return
} }
} }
} }
// closeAll closes and clears all forwards.
func (l *forwardList) closeAll() {
l.Lock()
defer l.Unlock()
for _, f := range l.entries {
close(f.c)
}
l.entries = nil
}
func (l *forwardList) lookup(addr net.TCPAddr) (chan forward, bool) { func (l *forwardList) lookup(addr net.TCPAddr) (chan forward, bool) {
l.Lock() l.Lock()
defer l.Unlock() defer l.Unlock()
+53 -7
View File
@@ -14,14 +14,12 @@ import (
"math/rand" "math/rand"
"net" "net"
"testing" "testing"
"time"
"code.google.com/p/go.crypto/ssh"
) )
func TestPortForward(t *testing.T) { func listenSSHAuto(conn *ssh.ClientConn) (net.Listener, error) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
defer conn.Close()
var sshListener net.Listener var sshListener net.Listener
var err error var err error
tries := 10 tries := 10
@@ -38,7 +36,21 @@ func TestPortForward(t *testing.T) {
} }
if err != nil { if err != nil {
t.Fatalf("conn.Listen failed: %v (after %d tries)", err, tries) return nil, fmt.Errorf("conn.Listen failed: %v (after %d tries)", err, tries)
}
return sshListener, nil
}
func TestPortForward(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
defer conn.Close()
sshListener, err := listenSSHAuto(conn)
if err != nil {
t.Fatal(err)
} }
go func() { go func() {
@@ -106,3 +118,37 @@ func TestPortForward(t *testing.T) {
t.Errorf("still listening to %s after closing", forwardedAddr) t.Errorf("still listening to %s after closing", forwardedAddr)
} }
} }
func TestAcceptClose(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
sshListener, err := listenSSHAuto(conn)
if err != nil {
t.Fatal(err)
}
quit := make(chan error, 1)
go func() {
for {
c, err := sshListener.Accept()
if err != nil {
quit <- err
break
}
c.Close()
}
}()
sshListener.Close()
select {
case <-time.After(1 * time.Second):
t.Errorf("timeout: listener did not close.")
case err := <-quit:
t.Logf("quit as expected (error %v)", err)
}
}
// TODO(hanwen): test that closing the connection also
// exits the listeners.