diff --git a/ftp.go b/ftp.go index 9892882..02136ae 100644 --- a/ftp.go +++ b/ftp.go @@ -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) { diff --git a/ftp_test.go b/ftp_test.go new file mode 100644 index 0000000..c1ca5ad --- /dev/null +++ b/ftp_test.go @@ -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) + } + } +}