Skip to content

Commit

Permalink
Ensure that a client can only keep one passive listener open at a time (
Browse files Browse the repository at this point in the history
#412)

A client might send many PASV commands and never send a transfer command.
This will lead to passive ports exhaustion.
  • Loading branch information
drakkan authored Jul 19, 2023
1 parent 7bb35a8 commit 3fe6de2
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 4 deletions.
8 changes: 4 additions & 4 deletions transfer_pasv.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down
27 changes: 27 additions & 0 deletions transfer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit 3fe6de2

Please sign in to comment.