From 19805040076d0f648def02edcb03d880151c4aac Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Sat, 27 Jun 2026 16:42:32 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=A4=A7=E8=A7=84=E6=A8=A1=E6=89=AB?= =?UTF-8?q?=E6=8F=8F=E5=85=A8=E5=B1=80=E8=B6=85=E6=97=B6=E8=BF=87=E7=9F=AD?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E6=8F=90=E5=89=8D=E7=BB=88=E6=AD=A2=20(#588)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4 万 IP 全端口扫描默认 -gt 180s 完全不够用,3 分钟后报 "解析目标失败: context deadline exceeded" 误导用户。 1. 自适应全局超时:用户未显式指定 -gt 时,根据端口数和是否有 hosts 文件自动调大超时(最高 24h),并输出调整日志 2. 修正超时错误信息:context deadline exceeded 不再包装为 "解析目标失败",改为提示用户调大 -gt 或设为 0 禁用 --- common/config_struct.go | 3 ++- common/flag.go | 2 ++ common/flag_config.go | 4 +++- common/i18n/locales/en.yaml | 4 ++++ common/i18n/locales/zh.yaml | 4 ++++ core/alive_scanner.go | 5 +++++ core/scanner.go | 39 +++++++++++++++++++++++++++++++++++++ core/service_scanner.go | 5 +++++ 8 files changed, 64 insertions(+), 2 deletions(-) diff --git a/common/config_struct.go b/common/config_struct.go index e40323c..f7f0b08 100644 --- a/common/config_struct.go +++ b/common/config_struct.go @@ -63,7 +63,8 @@ type Config struct { Target TargetConfig // 扫描目标配置 // 全局超时 - GlobalTimeout time.Duration + GlobalTimeout time.Duration + GlobalTimeoutExplicit bool // SOCKS5代理端口配置 Socks5ProxyPort int // SOCKS5代理端口 diff --git a/common/flag.go b/common/flag.go index a3f24d7..85ef4fa 100644 --- a/common/flag.go +++ b/common/flag.go @@ -226,6 +226,8 @@ func Flag(Info *HostInfo) error { fv.ModuleThreadNumExplicit = true case "retry": fv.MaxRetriesExplicit = true + case "gt": + fv.GlobalTimeoutExplicit = true case "icmp-rate": fv.ICMPRateExplicit = true case "num": diff --git a/common/flag_config.go b/common/flag_config.go index 79c23da..160e3df 100644 --- a/common/flag_config.go +++ b/common/flag_config.go @@ -39,6 +39,7 @@ type FlagVars struct { TimeoutSec int64 // 秒,需转换为 time.Duration TimeoutExplicit bool GlobalTimeout int64 + GlobalTimeoutExplicit bool DisablePing bool DisableTcpProbe bool DisableSubnetProbe bool @@ -171,7 +172,8 @@ func BuildConfigFromFlags(fv *FlagVars) *Config { DefaultMap: cloneStringSlice(config.DefaultProbeMap), // 全局超时 - GlobalTimeout: time.Duration(fv.GlobalTimeout) * time.Second, + GlobalTimeout: time.Duration(fv.GlobalTimeout) * time.Second, + GlobalTimeoutExplicit: fv.GlobalTimeoutExplicit, // SOCKS5代理端口 Socks5ProxyPort: fv.Socks5ProxyPort, diff --git a/common/i18n/locales/en.yaml b/common/i18n/locales/en.yaml index 8916b6b..7e7d2ac 100644 --- a/common/i18n/locales/en.yaml +++ b/common/i18n/locales/en.yaml @@ -26,6 +26,10 @@ flag_module_thread_num: other: "Module thread count" flag_global_timeout: other: "Global timeout" +global_timeout_adjusted: + other: "Large scan detected, global timeout adjusted from {{.V0}}s to {{.V1}}s (use -gt to override)" +global_timeout_exceeded: + other: "Global timeout reached (-gt {{.V0}}s), scan aborted. Use -gt to increase or set to 0 to disable" flag_disable_ping: other: "Disable ping detection" flag_disable_tcp_probe: diff --git a/common/i18n/locales/zh.yaml b/common/i18n/locales/zh.yaml index c4ee421..9e40f41 100644 --- a/common/i18n/locales/zh.yaml +++ b/common/i18n/locales/zh.yaml @@ -26,6 +26,10 @@ flag_module_thread_num: other: "模块线程数" flag_global_timeout: other: "全局超时时间" +global_timeout_adjusted: + other: "扫描规模较大,全局超时从 {{.V0}}s 自动调整为 {{.V1}}s(可用 -gt 手动指定)" +global_timeout_exceeded: + other: "全局超时已到(-gt {{.V0}}s),扫描被终止。大规模扫描请用 -gt 调大超时或设为 0 禁用" flag_disable_ping: other: "禁用ping探测" flag_disable_tcp_probe: diff --git a/core/alive_scanner.go b/core/alive_scanner.go index 1d2f250..6ad8a99 100644 --- a/core/alive_scanner.go +++ b/core/alive_scanner.go @@ -88,6 +88,11 @@ func (s *AliveScanStrategy) performAliveScan(ctx context.Context, info common.Ho for { hosts, err := iter.NextBatch(ctx, targetHostBatchSize(session.Config)) if err != nil { + if ctx.Err() != nil { + session.LogError(i18n.Tr("global_timeout_exceeded", + int(session.Config.GlobalTimeout.Seconds()))) + return + } session.LogError(i18n.Tr("parse_target_failed", err)) return } diff --git a/core/scanner.go b/core/scanner.go index dd1bc9a..4f1e76f 100644 --- a/core/scanner.go +++ b/core/scanner.go @@ -13,6 +13,7 @@ import ( "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common/i18n" "github.com/shadow1ng/fscan/common/output" + "github.com/shadow1ng/fscan/common/parsers" "github.com/shadow1ng/fscan/plugins" "github.com/shadow1ng/fscan/webscan/lib" ) @@ -96,6 +97,15 @@ func RunScan(ctx context.Context, info common.HostInfo, session *common.ScanSess start := time.Now() config := session.Config + // 全局超时自适应:用户未显式指定 -gt 时,根据扫描规模自动调大 + if !config.GlobalTimeoutExplicit && config.GlobalTimeout > 0 { + if adjusted := estimateGlobalTimeout(config, session); adjusted > config.GlobalTimeout { + session.LogInfo(i18n.Tr("global_timeout_adjusted", + int(config.GlobalTimeout.Seconds()), int(adjusted.Seconds()))) + config.GlobalTimeout = adjusted + } + } + // 全局超时:-gt 参数设置整个扫描的硬性截止时间 var cancel context.CancelFunc if config.GlobalTimeout > 0 { @@ -489,3 +499,32 @@ func addCommonDetails(result *plugins.Result, details map[string]interface{}) { details["server"] = result.Server } } + +func estimateGlobalTimeout(config *common.Config, session *common.ScanSession) time.Duration { + portCount := len(parsers.ParsePort(config.Target.Ports)) + if portCount == 0 { + portCount = len(parsers.ParsePort("21,22,80,443,445,1433,3306,3389,6379,8080")) + } + + hasHostFile := session.Params != nil && session.Params.HostsFile != "" + + // 启发式:端口数越多、有文件输入(目标可能很多),超时越大 + switch { + case portCount > 10000 && hasHostFile: + return 24 * time.Hour + case portCount > 10000: + return 6 * time.Hour + case portCount > 1000 && hasHostFile: + return 6 * time.Hour + case portCount > 1000: + return 1 * time.Hour + case portCount > 100 && hasHostFile: + return 1 * time.Hour + case portCount > 100: + return 30 * time.Minute + case hasHostFile: + return 30 * time.Minute + default: + return config.GlobalTimeout + } +} diff --git a/core/service_scanner.go b/core/service_scanner.go index 320f271..4c62c92 100644 --- a/core/service_scanner.go +++ b/core/service_scanner.go @@ -161,6 +161,11 @@ func (s *ServiceScanStrategy) performHostScan(ctx context.Context, session *comm for { hosts, err := iter.NextBatch(ctx, targetHostBatchSize(config)) if err != nil { + if ctx.Err() != nil { + session.LogError(i18n.Tr("global_timeout_exceeded", + int(config.GlobalTimeout.Seconds()))) + return + } session.LogError(fmt.Sprintf("%s: %v", i18n.GetText("parse_target_failed"), err)) return }