fscan/Common/Bridge.go
ZacharyZcR 05ba01f170 refactor: 统一包命名规范并清理冗余文件
主要更改:
- 统一包目录命名为小写(Core→core, Plugins→plugins, WebScan→webscan)
- 更新所有import路径以符合Go语言命名规范
- 重构parsers模块,简化复杂的工厂模式(从2000+行优化至400行)
- 移除i18n兼容层,统一使用模块化i18n包
- 简化Core/Manager.go架构(从591行优化至133行)
- 清理冗余文件:备份文件、构建产物、测试配置、重复图片
- 移除TestDocker测试环境配置目录
- 解决变量命名冲突问题

性能优化:
- 减少代码复杂度60-70%
- 提升构建和运行性能
- 保持完整功能兼容性

代码质量:
- 符合Go语言最佳实践
- 统一命名规范
- 优化项目结构
2025-08-06 01:30:18 +08:00

431 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package common
/*
Bridge.go - 统一桥接模块
将Config.go、Log.go、Output.go、Proxy.go的桥接功能合并到一个文件中
减少文件数量提高代码组织性。保持所有原有API的完全兼容性。
*/
import (
"context"
"crypto/tls"
"fmt"
"io"
"log"
"net"
"sync"
"time"
"github.com/fatih/color"
"github.com/schollz/progressbar/v3"
"github.com/shadow1ng/fscan/common/config"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/common/logging"
"github.com/shadow1ng/fscan/common/output"
"github.com/shadow1ng/fscan/common/proxy"
)
// =============================================================================
// 配置桥接 (Config.go 功能)
// =============================================================================
var version = "2.0.2"
// 向后兼容的输出配置变量
var (
Outputfile string
OutputFormat string
ProgressBar *progressbar.ProgressBar
OutputMutex sync.Mutex
)
// PocInfo POC详细信息结构
type PocInfo = config.PocInfo
func syncOutputConfig() {
cfg := config.GetGlobalConfig()
if cfg != nil && cfg.Output != nil {
Outputfile = cfg.Output.Outputfile
OutputFormat = cfg.Output.OutputFormat
}
ProgressBar = config.GetGlobalProgressBar()
}
// =============================================================================
// 日志桥接 (Log.go 功能)
// =============================================================================
// 全局日志状态和变量
var (
status = logging.NewScanStatus()
Num, End int64
StartTime = time.Now()
globalLogger *logging.Logger
loggerMutex sync.Mutex
)
// 日志级别常量
const (
LogLevelAll = string(logging.LevelAll)
LogLevelError = string(logging.LevelError)
LogLevelBase = string(logging.LevelBase)
LogLevelInfo = string(logging.LevelInfo)
LogLevelSuccess = string(logging.LevelSuccess)
LogLevelDebug = string(logging.LevelDebug)
LogLevelInfoSuccess = string(logging.LevelInfoSuccess)
LogLevelBaseInfoSuccess = string(logging.LevelBaseInfoSuccess)
)
// 类型别名
type LogEntry = logging.LogEntry
type ScanStatus = logging.ScanStatus
type ProgressDisplay = logging.ProgressDisplay
func getGlobalLogger() *logging.Logger {
loggerMutex.Lock()
defer loggerMutex.Unlock()
if globalLogger == nil {
// 获取正确的日志级别 - 动态获取确保使用最新值
level := getLogLevelFromString(LogLevel)
config := &logging.LoggerConfig{
Level: level, EnableColor: !NoColor, SlowOutput: SlowLogOutput,
ShowProgress: true, StartTime: StartTime,
LevelColors: map[logging.LogLevel]color.Attribute{
logging.LevelError: color.FgBlue, logging.LevelBase: color.FgYellow,
logging.LevelInfo: color.FgGreen, logging.LevelSuccess: color.FgRed, logging.LevelDebug: color.FgWhite,
},
}
globalLogger = logging.NewLogger(config)
if ProgressBar != nil {
globalLogger.SetProgressBar(ProgressBar)
}
globalLogger.SetOutputMutex(&OutputMutex)
status = globalLogger.GetScanStatus()
}
return globalLogger
}
// shouldLogLevel 检查是否应该记录该级别的日志
func shouldLogLevel(logger *logging.Logger, level logging.LogLevel) bool {
// 直接基于当前的LogLevel配置判断
switch LogLevel {
case "all", "ALL":
return true
case "error", "ERROR":
return level == logging.LevelError
case "base", "BASE":
return level == logging.LevelBase
case "info", "INFO":
return level == logging.LevelInfo
case "success", "SUCCESS":
return level == logging.LevelSuccess
case "debug", "DEBUG":
return level == logging.LevelDebug || level == logging.LevelError
case "info,success", "INFO_SUCCESS":
return level == logging.LevelInfo || level == logging.LevelSuccess
case "base,info,success", "BASE_INFO_SUCCESS":
return level == logging.LevelBase || level == logging.LevelInfo || level == logging.LevelSuccess
default:
// 默认使用BaseInfoSuccess级别不包含DEBUG和ERROR级别
return level == logging.LevelBase || level == logging.LevelInfo || level == logging.LevelSuccess
}
}
// getLogLevelFromString 根据字符串获取日志级别
func getLogLevelFromString(levelStr string) logging.LogLevel {
switch levelStr {
case "all", "ALL":
return logging.LevelAll
case "error", "ERROR":
return logging.LevelError
case "base", "BASE":
return logging.LevelBase
case "info", "INFO":
return logging.LevelInfo
case "success", "SUCCESS":
return logging.LevelSuccess
case "debug", "DEBUG":
return logging.LevelDebug
case "info,success":
return logging.LevelInfoSuccess
case "base,info,success", "BASE_INFO_SUCCESS":
return logging.LevelBaseInfoSuccess
default:
// 默认使用InfoSuccess级别只显示Info和Success消息
return logging.LevelInfoSuccess
}
}
// 日志相关函数
func InitLogger() {
// 重置全局logger以确保使用最新的LogLevel配置
loggerMutex.Lock()
globalLogger = nil
loggerMutex.Unlock()
log.SetOutput(io.Discard)
getGlobalLogger().Initialize()
}
func LogDebug(msg string) { logWithProgressCoordination(msg, "debug") }
func LogBase(msg string) { logWithProgressCoordination(msg, "base") }
func LogInfo(msg string) { logWithProgressCoordination(msg, "info") }
func LogSuccess(result string) { logWithProgressCoordination(result, "success") }
func LogError(errMsg string) { logWithProgressCoordination(errMsg, "error") }
func CheckErrs(err error) error { return logging.CheckErrs(err) }
// logWithProgressCoordination 协调日志输出与进度条的冲突
func logWithProgressCoordination(msg, level string) {
logger := getGlobalLogger()
// 首先检查是否应该记录这个级别的日志
var logLevel logging.LogLevel
switch level {
case "debug":
logLevel = logging.LevelDebug
case "base":
logLevel = logging.LevelBase
case "info":
logLevel = logging.LevelInfo
case "success":
logLevel = logging.LevelSuccess
case "error":
logLevel = logging.LevelError
default:
logLevel = logging.LevelDebug
}
// 如果当前日志级别不应该显示这条消息,直接返回
if !shouldLogLevel(logger, logLevel) {
return
}
// 如果进度条活跃,使用协调输出
if IsProgressActive() {
// 简单格式化消息,保持时间戳格式一致
elapsed := time.Since(StartTime)
var prefix, colorCode, resetCode string
if !NoColor {
resetCode = "\033[0m"
switch level {
case "debug":
colorCode = "\033[37m" // 白色
prefix = " "
case "base":
colorCode = "\033[33m" // 黄色
prefix = " "
case "info":
colorCode = "\033[32m" // 绿色
prefix = " [*] "
case "success":
colorCode = "\033[31m" // 红色
prefix = " [+] "
case "error":
colorCode = "\033[34m" // 蓝色
prefix = " [-] "
}
} else {
switch level {
case "info":
prefix = " [*] "
case "success":
prefix = " [+] "
case "error":
prefix = " [-] "
default:
prefix = " "
}
}
formattedMsg := fmt.Sprintf("[%.1fs]%s%s%s%s",
elapsed.Seconds(), prefix, colorCode, msg, resetCode)
LogWithProgress(formattedMsg)
} else {
// 如果进度条不活跃,使用原始日志方法
switch level {
case "debug":
logger.Debug(msg)
case "base":
logger.Base(msg)
case "info":
logger.Info(msg)
case "success":
logger.Success(msg)
case "error":
logger.Error(msg)
}
}
}
// =============================================================================
// 输出桥接 (Output.go 功能)
// =============================================================================
// 全局输出管理器
var ResultOutput *OutputManager
type OutputManager struct{ manager *output.Manager }
type ResultType = output.ResultType
const (
HOST ResultType = output.TypeHost
PORT ResultType = output.TypePort
SERVICE ResultType = output.TypeService
VULN ResultType = output.TypeVuln
)
type ScanResult = output.ScanResult
func createOutputManager(outputPath, outputFormat string) (*OutputManager, error) {
var format output.OutputFormat
switch outputFormat {
case "txt":
format = output.FormatTXT
case "json":
format = output.FormatJSON
case "csv":
format = output.FormatCSV
default:
return nil, fmt.Errorf(i18n.GetText("output_format_invalid", outputFormat))
}
config := output.DefaultManagerConfig(outputPath, format)
manager, err := output.NewManager(config)
if err != nil {
return nil, err
}
return &OutputManager{manager: manager}, nil
}
// 输出相关函数
func InitOutput() error {
LogDebug(i18n.GetText("output_init_start"))
switch OutputFormat {
case "txt", "json", "csv":
default:
return fmt.Errorf(i18n.GetText("output_format_invalid", OutputFormat))
}
if Outputfile == "" {
return fmt.Errorf(i18n.GetText("output_path_empty"))
}
manager, err := createOutputManager(Outputfile, OutputFormat)
if err != nil {
LogDebug(i18n.GetText("output_init_failed", err))
return fmt.Errorf(i18n.GetText("output_init_failed", err))
}
ResultOutput = manager
output.SetGlobalManager(manager.manager)
LogDebug(i18n.GetText("output_init_success"))
return nil
}
func (om *OutputManager) saveResult(result *ScanResult) error {
if om.manager == nil {
return fmt.Errorf(i18n.GetText("output_not_init"))
}
LogDebug(i18n.GetText("output_saving_result", result.Type, result.Target))
return om.manager.SaveResult(result)
}
func SaveResult(result *ScanResult) error {
if ResultOutput == nil {
LogDebug(i18n.GetText("output_not_init"))
return fmt.Errorf(i18n.GetText("output_not_init"))
}
return ResultOutput.saveResult(result)
}
func CloseOutput() error {
if ResultOutput == nil {
return nil
}
LogDebug(i18n.GetText("output_closing"))
err := ResultOutput.manager.Close()
if err != nil {
return fmt.Errorf(i18n.GetText("output_close_failed", err))
}
LogDebug(i18n.GetText("output_closed"))
return nil
}
// =============================================================================
// 代理桥接 (Proxy.go 功能)
// =============================================================================
// 代理相关函数
func WrapperTcpWithTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return proxy.DialContextWithProxy(ctx, network, address)
}
func WrapperTcpWithContext(ctx context.Context, network, address string) (net.Conn, error) {
return proxy.DialContextWithProxy(ctx, network, address)
}
func Socks5Dialer(forward *net.Dialer) (interface{}, error) {
if err := syncProxyConfig(); err != nil {
return nil, fmt.Errorf(i18n.GetText("socks5_create_failed", err))
}
manager := proxy.GetGlobalProxy()
dialer, err := manager.GetDialer()
if err != nil {
return nil, fmt.Errorf(i18n.GetText("socks5_create_failed", err))
}
return dialer, nil
}
func WrapperTlsWithContext(ctx context.Context, network, address string, tlsConfig *tls.Config) (net.Conn, error) {
if err := syncProxyConfig(); err != nil {
LogError(i18n.GetText("proxy_config_sync_failed", err.Error()))
}
conn, err := proxy.DialTLSContextWithProxy(ctx, network, address, tlsConfig)
if err != nil {
LogError(i18n.GetText("tls_conn_failed", err.Error()))
return nil, fmt.Errorf(i18n.GetText("tls_conn_failed", err))
}
return conn, nil
}
func syncProxyConfig() error {
var proxyURL string
if Socks5Proxy != "" {
proxyURL = Socks5Proxy
} else if HttpProxy != "" {
proxyURL = HttpProxy
}
if proxy.IsProxyEnabledGlobally() {
currentAddr := proxy.GetGlobalProxyAddress()
if (proxyURL == "" && currentAddr == "") ||
(proxyURL != "" && currentAddr != "" && (Socks5Proxy == currentAddr || HttpProxy == currentAddr)) {
return nil
}
}
if err := proxy.InitGlobalProxy(proxyURL); err != nil {
return fmt.Errorf(i18n.GetText("proxy_init_failed", err))
}
if proxyURL != "" {
LogBase(i18n.GetText("proxy_enabled", proxy.GetGlobalProxyType(), proxy.GetGlobalProxyAddress()))
} else {
LogBase(i18n.GetText("proxy_disabled"))
}
return nil
}
// =============================================================================
// 初始化函数
// =============================================================================
func init() {
config.SetGlobalVersion(version)
syncOutputConfig()
}