fscan/Plugins/base/scanner.go
ZacharyZcR f097d2812a refactor: 清理项目死代码和未使用函数
- 移除所有未使用的generateCredentials方法
- 删除插件适配器中的过时函数
- 清理MySQL连接器中的无用方法
- 移除Redis利用器中的未调用函数
- 删除遗留加密函数和基础扫描器无用方法
- 完全移除未注册的VNC插件
- 优化代码结构,提升项目可维护性

清理统计: 移除25+个死代码函数,减少400+行无用代码
2025-08-12 11:51:36 +08:00

267 lines
6.1 KiB
Go

package base
import (
"context"
"fmt"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"strings"
"sync"
"time"
)
// =============================================================================
// 通用扫描器基础实现
// =============================================================================
// BaseScanner 基础扫描器,提供通用的扫描逻辑
type BaseScanner struct {
Name string
metadata *PluginMetadata
capabilities []Capability
}
// NewBaseScanner 创建基础扫描器
func NewBaseScanner(name string, metadata *PluginMetadata) *BaseScanner {
return &BaseScanner{
Name: name,
metadata: metadata,
}
}
// GetName 获取扫描器名称
func (s *BaseScanner) GetName() string {
return s.Name
}
// GetCapabilities 获取扫描器支持的能力
func (s *BaseScanner) GetCapabilities() []Capability {
return s.capabilities
}
// SetCapabilities 设置扫描器能力
func (s *BaseScanner) SetCapabilities(caps []Capability) {
s.capabilities = caps
}
// GetMetadata 获取插件元数据
func (s *BaseScanner) GetMetadata() *PluginMetadata {
return s.metadata
}
// =============================================================================
// 通用并发扫描框架
// =============================================================================
// ConcurrentScanConfig 并发扫描配置
type ConcurrentScanConfig struct {
MaxConcurrent int // 最大并发数
Timeout time.Duration // 单次扫描超时
MaxRetries int // 最大重试次数
RetryDelay time.Duration // 重试延迟
}
// CredentialScanner 凭据扫描器接口
type CredentialScanner interface {
// ScanCredential 扫描单个凭据
ScanCredential(ctx context.Context, info *common.HostInfo, cred *Credential) (*ScanResult, error)
}
// ConcurrentCredentialScan 并发凭据扫描通用实现
func ConcurrentCredentialScan(
ctx context.Context,
scanner CredentialScanner,
info *common.HostInfo,
credentials []*Credential,
config *ConcurrentScanConfig,
) (*ScanResult, error) {
if len(credentials) == 0 {
return nil, fmt.Errorf("没有提供凭据")
}
// 设置默认配置
if config == nil {
config = &ConcurrentScanConfig{
MaxConcurrent: 10,
Timeout: time.Duration(common.Timeout) * time.Second,
MaxRetries: common.MaxRetries,
RetryDelay: 500 * time.Millisecond,
}
}
// 限制并发数
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 credential := range workChan {
select {
case <-scanCtx.Done():
return
default:
// 开始监控连接
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
monitor := common.GetConcurrencyMonitor()
monitor.StartConnection("credential", target)
result := scanCredentialWithRetry(scanCtx, scanner, info, credential, config)
// 完成连接监控
monitor.FinishConnection("credential", target)
if result != nil && result.Success {
select {
case resultChan <- result:
scanCancel() // 找到有效凭据,取消其他工作
default:
}
return
}
}
}
}()
}
// 发送工作
go func() {
for _, cred := range credentials {
select {
case <-scanCtx.Done():
break
default:
workChan <- cred
}
}
close(workChan)
}()
// 等待结果或完成
go func() {
wg.Wait()
close(resultChan)
}()
// 获取结果
select {
case result, ok := <-resultChan:
if ok && result != nil && result.Success {
return result, nil
}
return nil, fmt.Errorf(i18n.GetText("plugin_all_creds_failed"))
case <-ctx.Done():
scanCancel()
return nil, ctx.Err()
}
}
// scanCredentialWithRetry 带重试的单凭据扫描
func scanCredentialWithRetry(
ctx context.Context,
scanner CredentialScanner,
info *common.HostInfo,
cred *Credential,
config *ConcurrentScanConfig,
) *ScanResult {
for retry := 0; retry < config.MaxRetries; retry++ {
select {
case <-ctx.Done():
return &ScanResult{
Success: false,
Error: ctx.Err(),
}
default:
if retry > 0 {
time.Sleep(config.RetryDelay)
}
// 创建独立的超时上下文
connCtx, cancel := context.WithTimeout(ctx, config.Timeout)
result, err := scanner.ScanCredential(connCtx, info, cred)
cancel()
if result != nil && result.Success {
return result
}
// 检查是否需要重试
if err != nil && !shouldRetry(err) {
break
}
}
}
return &ScanResult{
Success: false,
Error: fmt.Errorf("重试次数耗尽"),
}
}
// shouldRetry 判断是否应该重试
func shouldRetry(err error) bool {
if err == nil {
return false
}
errStr := strings.ToLower(err.Error())
// 不需要重试的错误
noRetryErrors := []string{
"access denied",
"authentication failed",
"invalid credentials",
"permission denied",
"unauthorized",
}
for _, noRetry := range noRetryErrors {
if strings.Contains(errStr, noRetry) {
return false
}
}
return true
}
// =============================================================================
// 凭据生成工具
// =============================================================================
// GenerateCredentials 生成用户名密码组合的凭据列表
func GenerateCredentials(usernames []string, passwords []string) []*Credential {
var credentials []*Credential
for _, username := range usernames {
for _, password := range passwords {
// 支持 {user} 占位符替换
actualPassword := strings.ReplaceAll(password, "{user}", username)
credentials = append(credentials, &Credential{
Username: username,
Password: actualPassword,
Extra: make(map[string]string),
})
}
}
return credentials
}
// 已移除未使用的 GeneratePasswordOnlyCredentials 方法