diff --git a/ftp.go b/ftp.go index 62429f1..9746e05 100644 --- a/ftp.go +++ b/ftp.go @@ -54,6 +54,7 @@ type dialOptions struct { disableEPSV bool location *time.Location debugOutput io.Writer + dialFunc func(network, address string) (net.Conn, error) } // Entry describes a file and is returned by List(). @@ -84,17 +85,23 @@ func Dial(addr string, options ...DialOption) (*ServerConn, error) { tconn := do.conn if tconn == nil { - ctx := do.context + var err error - if ctx == nil { - ctx = context.Background() + if do.dialFunc != nil { + tconn, err = do.dialFunc("tcp", addr) + } else { + ctx := do.context + + if ctx == nil { + ctx = context.Background() + } + + tconn, err = do.dialer.DialContext(ctx, "tcp", addr) } - conn, err := do.dialer.DialContext(ctx, "tcp", addr) if err != nil { return nil, err } - tconn = conn } // Use the resolved IP address in case addr contains a domain name @@ -192,6 +199,18 @@ func DialWithDebugOutput(w io.Writer) DialOption { }} } +// DialWithDialFunc returns a DialOption that configures the ServerConn to use the +// specified function to establish both control and data connections +// +// If used together with the DialWithNetConn option, the DialWithNetConn +// takes precedence for the control connection, while data connections will +// be established using function specified with the DialWithDialFunc option +func DialWithDialFunc(f func(network, address string) (net.Conn, error)) DialOption { + return DialOption{func(do *dialOptions) { + do.dialFunc = f + }} +} + // Connect is an alias to Dial, for backward compatibility func Connect(addr string) (*ServerConn, error) { return Dial(addr) @@ -387,7 +406,12 @@ func (c *ServerConn) openDataConn() (net.Conn, error) { return nil, err } - return c.options.dialer.Dial("tcp", net.JoinHostPort(host, strconv.Itoa(port))) + addr := net.JoinHostPort(host, strconv.Itoa(port)) + if c.options.dialFunc != nil { + return c.options.dialFunc("tcp", addr) + } + + return c.options.dialer.Dial("tcp", addr) } // cmd is a helper function to execute a command and check for the expected FTP