Merge pull request #115 from ncw/fix-pasv

Parse IP address returned with PASV to fix load balanced FTP servers
This commit is contained in:
Julien Laffaye 2018-02-05 00:23:34 +01:00 committed by GitHub
commit 427467931c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

21
ftp.go
View File

@ -216,7 +216,7 @@ func (c *ServerConn) epsv() (port int, err error) {
} }
// pasv issues a "PASV" command to get a port number for a data connection. // pasv issues a "PASV" command to get a port number for a data connection.
func (c *ServerConn) pasv() (port int, err error) { func (c *ServerConn) pasv() (host string, port int, err error) {
_, line, err := c.cmd(StatusPassiveMode, "PASV") _, line, err := c.cmd(StatusPassiveMode, "PASV")
if err != nil { if err != nil {
return return
@ -226,14 +226,16 @@ func (c *ServerConn) pasv() (port int, err error) {
start := strings.Index(line, "(") start := strings.Index(line, "(")
end := strings.LastIndex(line, ")") end := strings.LastIndex(line, ")")
if start == -1 || end == -1 { if start == -1 || end == -1 {
return 0, errors.New("Invalid PASV response format") err = errors.New("Invalid PASV response format")
return
} }
// We have to split the response string // We have to split the response string
pasvData := strings.Split(line[start+1:end], ",") pasvData := strings.Split(line[start+1:end], ",")
if len(pasvData) < 6 { if len(pasvData) < 6 {
return 0, errors.New("Invalid PASV response format") err = errors.New("Invalid PASV response format")
return
} }
// Let's compute the port number // Let's compute the port number
@ -251,15 +253,18 @@ func (c *ServerConn) pasv() (port int, err error) {
// Recompose port // Recompose port
port = portPart1*256 + portPart2 port = portPart1*256 + portPart2
// Make the IP address to connect to
host = strings.Join(pasvData[0:4], ".")
return return
} }
// getDataConnPort returns a port for a new data connection // getDataConnPort returns a host, port for a new data connection
// it uses the best available method to do so // it uses the best available method to do so
func (c *ServerConn) getDataConnPort() (int, error) { func (c *ServerConn) getDataConnPort() (string, int, error) {
if !c.DisableEPSV { if !c.DisableEPSV {
if port, err := c.epsv(); err == nil { if port, err := c.epsv(); err == nil {
return port, nil return c.host, port, nil
} }
// if there is an error, disable EPSV for the next attempts // if there is an error, disable EPSV for the next attempts
@ -271,12 +276,12 @@ func (c *ServerConn) getDataConnPort() (int, error) {
// openDataConn creates a new FTP data connection. // openDataConn creates a new FTP data connection.
func (c *ServerConn) openDataConn() (net.Conn, error) { func (c *ServerConn) openDataConn() (net.Conn, error) {
port, err := c.getDataConnPort() host, port, err := c.getDataConnPort()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return net.DialTimeout("tcp", net.JoinHostPort(c.host, strconv.Itoa(port)), c.timeout) return net.DialTimeout("tcp", net.JoinHostPort(host, strconv.Itoa(port)), c.timeout)
} }
// cmd is a helper function to execute a command and check for the expected FTP // cmd is a helper function to execute a command and check for the expected FTP