package common import ( "context" "fmt" "sync" "time" "github.com/shadow1ng/fscan/common" ) // DefaultScanner 默认并发扫描器实现 type DefaultScanner struct{} // NewScanner 创建新的扫描器 func NewScanner() *DefaultScanner { return &DefaultScanner{} } // Scan 执行并发扫描 func (s *DefaultScanner) Scan(ctx context.Context, target *TargetInfo, connector SmbConnector, credMgr CredentialManager, config *ScanConfig) (*ScanResult, error) { credentials := credMgr.GenerateCredentials() if len(credentials) == 0 { return nil, fmt.Errorf("没有可用的认证凭据") } common.LogDebug(fmt.Sprintf("开始%s扫描 %s:%d (总用户数: %d, 总组合数: %d)", connector.GetProtocolName(), target.Host, target.Port, len(common.Userdict["smb"]), len(credentials))) // 确定并发数 maxConcurrent := config.MaxConcurrent if maxConcurrent <= 0 { maxConcurrent = 10 } if maxConcurrent > len(credentials) { maxConcurrent = len(credentials) } // 创建工作池 var wg sync.WaitGroup resultChan := make(chan *ScanResult, 1) workChan := make(chan Credential, maxConcurrent) scanCtx, scanCancel := context.WithCancel(ctx) defer scanCancel() // 启动工作协程 for i := 0; i < maxConcurrent; i++ { wg.Add(1) go func() { defer wg.Done() for cred := range workChan { select { case <-scanCtx.Done(): return default: // 检查用户是否已锁定 if credMgr.IsUserLocked(cred.Username) { common.LogDebug(fmt.Sprintf("跳过已锁定用户: %s", cred.Username)) continue } // 尝试连接 result := s.tryCredential(scanCtx, target, connector, cred, config.Timeout) // 处理认证失败 if !result.Success && result.Error != nil { credMgr.HandleAuthFailure(cred.Username, result.Error) } // 如果成功,发送结果并取消其他工作 if result.Success { select { case resultChan <- result: scanCancel() default: } return } } } }() } // 发送工作 go func() { defer close(workChan) for i, cred := range credentials { select { case <-scanCtx.Done(): return default: // 跳过已锁定用户 if credMgr.IsUserLocked(cred.Username) { continue } // 记录尝试日志 s.logAttempt(connector.GetProtocolName(), i+1, len(credentials), cred) workChan <- cred } } }() // 等待结果或完成 go func() { wg.Wait() close(resultChan) }() // 获取结果 select { case result, ok := <-resultChan: if ok && result != nil && result.Success { return result, nil } return nil, nil case <-ctx.Done(): common.LogDebug(fmt.Sprintf("%s扫描全局超时", connector.GetProtocolName())) scanCancel() return nil, fmt.Errorf("全局超时") } } // tryCredential 尝试单个凭据 func (s *DefaultScanner) tryCredential(ctx context.Context, target *TargetInfo, connector SmbConnector, cred Credential, timeout time.Duration) *ScanResult { // 创建连接超时上下文 connCtx, cancel := context.WithTimeout(ctx, timeout) defer cancel() // 在协程中尝试连接 resultChan := make(chan *ConnectionResult, 1) go func() { result, err := connector.Connect(connCtx, target, &cred) if err != nil { result = &ConnectionResult{ Success: false, Error: err, } } select { case <-connCtx.Done(): case resultChan <- result: } }() // 等待结果或超时 select { case result := <-resultChan: if result.Success { return &ScanResult{ Success: true, Credential: cred, Shares: result.Shares, } } // 分类错误 classifiedError := ClassifySmbError(result.Error) return &ScanResult{ Success: false, Error: classifiedError, Credential: cred, Shares: result.Shares, } case <-connCtx.Done(): var err error if ctx.Err() != nil { err = ctx.Err() // 全局超时 } else { err = ErrTimeout // 连接超时 } return &ScanResult{ Success: false, Error: err, Credential: cred, } } } // logAttempt 记录尝试日志 func (s *DefaultScanner) logAttempt(protocol string, current, total int, cred Credential) { if cred.IsHash { common.LogDebug(fmt.Sprintf("[%d/%d] 尝试%s: %s Hash:%s", current, total, protocol, cred.Username, common.HashValue)) } else { common.LogDebug(fmt.Sprintf("[%d/%d] 尝试%s: %s:%s", current, total, protocol, cred.Username, cred.Password)) } }