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/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 loggerOnce sync.Once ) // 日志级别常量 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 { loggerOnce.Do(func() { config := &logging.LoggerConfig{ Level: logging.LevelBaseInfoSuccess, 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 } // 日志相关函数 func InitLogger() { log.SetOutput(io.Discard); getGlobalLogger().Initialize() } func LogDebug(msg string) { getGlobalLogger().Debug(msg) } func LogBase(msg string) { getGlobalLogger().Base(msg) } func LogInfo(msg string) { getGlobalLogger().Info(msg) } func LogSuccess(result string) { getGlobalLogger().Success(result) } func LogError(errMsg string) { getGlobalLogger().Error(errMsg) } func CheckErrs(err error) error { return logging.CheckErrs(err) } // ============================================================================= // 输出桥接 (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(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(GetText("output_init_start")) switch OutputFormat { case "txt", "json", "csv": default: return fmt.Errorf(GetText("output_format_invalid", OutputFormat)) } if Outputfile == "" { return fmt.Errorf(GetText("output_path_empty")) } manager, err := createOutputManager(Outputfile, OutputFormat) if err != nil { LogDebug(GetText("output_init_failed", err)) return fmt.Errorf(GetText("output_init_failed", err)) } ResultOutput = manager output.SetGlobalManager(manager.manager) LogDebug(GetText("output_init_success")) return nil } func (om *OutputManager) saveResult(result *ScanResult) error { if om.manager == nil { return fmt.Errorf(GetText("output_not_init")) } LogDebug(GetText("output_saving_result", result.Type, result.Target)) return om.manager.SaveResult(result) } func SaveResult(result *ScanResult) error { if ResultOutput == nil { LogDebug(GetText("output_not_init")) return fmt.Errorf(GetText("output_not_init")) } return ResultOutput.saveResult(result) } func CloseOutput() error { if ResultOutput == nil { return nil } LogDebug(GetText("output_closing")) err := ResultOutput.manager.Close() if err != nil { return fmt.Errorf(GetText("output_close_failed", err)) } LogDebug(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(GetText("socks5_create_failed", err)) } manager := proxy.GetGlobalProxy() dialer, err := manager.GetDialer() if err != nil { return nil, fmt.Errorf(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(GetText("proxy_config_sync_failed", err.Error())) } conn, err := proxy.DialTLSContextWithProxy(ctx, network, address, tlsConfig) if err != nil { LogError(GetText("tls_conn_failed", err.Error())) return nil, fmt.Errorf(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(GetText("proxy_init_failed", err)) } if proxyURL != "" { LogBase(GetText("proxy_enabled", proxy.GetGlobalProxyType(), proxy.GetGlobalProxyAddress())) } else { LogBase(GetText("proxy_disabled")) } return nil } // ============================================================================= // 初始化函数 // ============================================================================= func init() { config.SetGlobalVersion(version) syncOutputConfig() }