package ftp import ( "context" "fmt" "time" ftplib "github.com/jlaffaye/ftp" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/plugins/base" ) // FTPConnector FTP连接器实现 type FTPConnector struct { host string port string } // NewFTPConnector 创建FTP连接器 func NewFTPConnector() *FTPConnector { return &FTPConnector{} } // Connect 连接到FTP服务 func (c *FTPConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) { c.host = info.Host c.port = info.Ports // 构建连接地址 target := fmt.Sprintf("%s:%s", c.host, c.port) // 创建FTP连接配置 config := &FTPConfig{ Target: target, Timeout: time.Duration(common.Timeout) * time.Second, } return config, nil } // Authenticate 认证 func (c *FTPConnector) Authenticate(ctx context.Context, conn interface{}, cred *base.Credential) error { config, ok := conn.(*FTPConfig) if !ok { return fmt.Errorf("无效的连接类型") } // 在goroutine中建立FTP连接,支持Context取消 resultChan := make(chan struct { ftpConn *ftplib.ServerConn err error }, 1) go func() { ftpConn, err := ftplib.DialTimeout(config.Target, config.Timeout) select { case <-ctx.Done(): if ftpConn != nil { ftpConn.Quit() } case resultChan <- struct { ftpConn *ftplib.ServerConn err error }{ftpConn, err}: } }() // 等待连接结果或Context取消 var ftpConn *ftplib.ServerConn var err error select { case result := <-resultChan: ftpConn, err = result.ftpConn, result.err if err != nil { return fmt.Errorf("FTP连接失败: %v", err) } case <-ctx.Done(): return fmt.Errorf("FTP连接超时: %v", ctx.Err()) } defer ftpConn.Quit() // 在goroutine中进行登录认证 loginChan := make(chan error, 1) go func() { err := ftpConn.Login(cred.Username, cred.Password) select { case <-ctx.Done(): case loginChan <- err: } }() // 等待登录结果或Context取消 select { case err := <-loginChan: if err != nil { return fmt.Errorf("FTP认证失败: %v", err) } return nil case <-ctx.Done(): return fmt.Errorf("FTP认证超时: %v", ctx.Err()) } } // Close 关闭连接 func (c *FTPConnector) Close(conn interface{}) error { // FTP配置无需显式关闭 return nil } // FTPConfig FTP连接配置 type FTPConfig struct { Target string Timeout time.Duration }