Skip to content

Commit

Permalink
Fix PASV address like lftp does (#349)
Browse files Browse the repository at this point in the history
Some behind-firewall-and-corporate-network FTP servers
responds their private-net address instead of the
publicly reachable.

This fix checks for such address and uses the
command channel's address instead.
  • Loading branch information
tgulacsi authored Nov 13, 2023
1 parent a58dc06 commit e44fc64
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 0 deletions.
17 changes: 17 additions & 0 deletions ftp.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,9 +528,26 @@ func (c *ServerConn) pasv() (host string, port int, err error) {

// Make the IP address to connect to
host = strings.Join(pasvData[0:4], ".")

if c.host != host {
if cmdIP := net.ParseIP(c.host); cmdIP != nil {
if dataIP := net.ParseIP(host); dataIP != nil {
if isBogusDataIP(cmdIP, dataIP) {
return c.host, port, nil
}
}
}
}
return host, port, nil
}

func isBogusDataIP(cmdIP, dataIP net.IP) bool {
// Logic stolen from lftp (https://github.com/lavv17/lftp/blob/d67fc14d085849a6b0418bb3e912fea2e94c18d1/src/ftpclass.cc#L769)
return dataIP.IsMulticast() ||
cmdIP.IsPrivate() != dataIP.IsPrivate() ||
cmdIP.IsLoopback() != dataIP.IsLoopback()
}

// getDataConnPort returns a host, port for a new data connection
// it uses the best available method to do so
func (c *ServerConn) getDataConnPort() (string, int, error) {
Expand Down
22 changes: 22 additions & 0 deletions ftp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ftp

import (
"net"
"testing"
)

func TestBogusDataIP(t *testing.T) {
for _, tC := range []struct {
cmd, data net.IP
bogus bool
}{
{net.IPv4(192, 168, 1, 1), net.IPv4(192, 168, 1, 1), false},
{net.IPv4(192, 168, 1, 1), net.IPv4(1, 1, 1, 1), true},
{net.IPv4(10, 65, 1, 1), net.IPv4(1, 1, 1, 1), true},
{net.IPv4(10, 65, 25, 1), net.IPv4(10, 65, 8, 1), false},
} {
if got, want := isBogusDataIP(tC.cmd, tC.data), tC.bogus; got != want {
t.Errorf("%s,%s got %t, wanted %t", tC.cmd, tC.data, got, want)
}
}
}

0 comments on commit e44fc64

Please sign in to comment.