diff --git a/client_test.go b/client_test.go index ac09cd5..92d4779 100644 --- a/client_test.go +++ b/client_test.go @@ -21,7 +21,7 @@ func TestConnEPSV(t *testing.T) { testConn(t, false) } -func testConn(t *testing.T, passive bool) { +func testConn(t *testing.T, disableEPSV bool) { if testing.Short() { t.Skip("skipping test in short mode.") } @@ -31,8 +31,9 @@ func testConn(t *testing.T, passive bool) { t.Fatal(err) } - if passive { + if disableEPSV { delete(c.features, "EPSV") + c.disableEPSV = true } err = c.Login("anonymous", "anonymous") diff --git a/ftp.go b/ftp.go index 306a3e5..cfa1711 100644 --- a/ftp.go +++ b/ftp.go @@ -24,10 +24,11 @@ const ( // ServerConn represents the connection to a remote FTP server. type ServerConn struct { - conn *textproto.Conn - host string - timeout time.Duration - features map[string]string + conn *textproto.Conn + host string + timeout time.Duration + features map[string]string + disableEPSV bool } // Entry describes a file and is returned by List(). @@ -219,17 +220,26 @@ func (c *ServerConn) pasv() (port int, err error) { return } +// getDataConnPort returns a port for a new data connection +// it uses the best available method to do so +func (c *ServerConn) getDataConnPort() (int, error) { + if !c.disableEPSV { + if port, err := c.epsv(); err == nil { + return port, nil + } + + // if there is an error, disable EPSV for the next attempts + c.disableEPSV = true + } + + return c.pasv() +} + // openDataConn creates a new FTP data connection. func (c *ServerConn) openDataConn() (net.Conn, error) { - var ( - port int - err error - ) - - if port, err = c.epsv(); err != nil { - if port, err = c.pasv(); err != nil { - return nil, err - } + port, err := c.getDataConnPort() + if err != nil { + return nil, err } return net.DialTimeout("tcp", net.JoinHostPort(c.host, strconv.Itoa(port)), c.timeout)