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() }