From d8aff325a7965a072bb145b31b339d5e51461008 Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Fri, 14 Jul 2023 16:48:23 +0200 Subject: [PATCH] ensure that a client can only keep one passive listener open at a time A client might send many PASV commands and never send a transfer command. This will lead to passive ports exhaustion. --- transfer_pasv.go | 8 ++++---- transfer_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/transfer_pasv.go b/transfer_pasv.go index 626ac9b1..a7e58521 100644 --- a/transfer_pasv.go +++ b/transfer_pasv.go @@ -119,10 +119,8 @@ func (c *clientHandler) findListenerWithinPortRange(portRange *PortRange) (*net. func (c *clientHandler) handlePASV(param string) error { command := c.GetLastCommand() addr, _ := net.ResolveTCPAddr("tcp", ":0") - var tcpListener *net.TCPListener var err error - portRange := c.server.settings.PassiveTransferPortRange if portRange != nil { @@ -137,10 +135,8 @@ func (c *clientHandler) handlePASV(param string) error { return nil } - // The listener will either be plain TCP or TLS var listener net.Listener - listener = tcpListener if wrapper, ok := c.server.driver.(MainDriverExtensionPassiveWrapper); ok { @@ -192,6 +188,10 @@ func (c *clientHandler) handlePASV(param string) error { } c.transferMu.Lock() + if c.transfer != nil { + c.transfer.Close() //nolint:errcheck,gosec + } + c.transfer = p c.transferMu.Unlock() c.setLastDataChannel(DataChannelPassive) diff --git a/transfer_test.go b/transfer_test.go index da390296..8e745e95 100644 --- a/transfer_test.go +++ b/transfer_test.go @@ -1096,6 +1096,33 @@ func TestPASVIPMatch(t *testing.T) { } } +func TestPassivePortExhaustion(t *testing.T) { + s := NewTestServer(t, false) + s.settings.PassiveTransferPortRange = &PortRange{ + Start: 40000, + End: 40005, + } + + c, err := goftp.DialConfig(goftp.Config{ + User: authUser, + Password: authPass, + }, s.Addr()) + require.NoError(t, err, "Couldn't connect") + + defer func() { panicOnError(c.Close()) }() + + raw, err := c.OpenRawConn() + require.NoError(t, err, "Couldn't open raw connection") + + defer func() { require.NoError(t, raw.Close()) }() + + for i := 0; i < 20; i++ { + rc, message, err := raw.SendCommand("PASV") + require.NoError(t, err) + require.Equal(t, StatusEnteringPASV, rc, message) + } +} + func loginConnection(t *testing.T, conn net.Conn) { buf := make([]byte, 1024) _, err := fmt.Fprintf(conn, "USER %v\r\n", authUser)