Skip to content

Commit 075bf64

Browse files
committed
fix: 全局超时改为动态估算,替代硬编码阈值表
根据 hostCount × portCount / threads 计算端口扫描耗时, 结合开放率估算插件扫描耗时,加 20% 余量,上限 2h。 新增 EstimateHostCount 快速统计 CIDR/range/文件中的主机数。
1 parent 1980504 commit 075bf64

2 files changed

Lines changed: 128 additions & 21 deletions

File tree

common/parsers/host_iterator.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,3 +488,90 @@ func ipToUint32(ip net.IP) (uint32, bool) {
488488
func uint32ToIP(v uint32) string {
489489
return fmt.Sprintf("%d.%d.%d.%d", byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
490490
}
491+
492+
// EstimateHostCount 快速估算主机总数(不消费 iterator)
493+
func EstimateHostCount(host string, filename string) int64 {
494+
var total int64
495+
496+
if filename != "" {
497+
if f, err := os.Open(filename); err == nil {
498+
scanner := bufio.NewScanner(f)
499+
for scanner.Scan() {
500+
line := strings.TrimSpace(scanner.Text())
501+
if line == "" || strings.HasPrefix(line, "#") {
502+
continue
503+
}
504+
total += estimateHostEntry(line)
505+
}
506+
_ = f.Close()
507+
}
508+
}
509+
510+
for _, h := range strings.Split(host, ",") {
511+
h = strings.TrimSpace(h)
512+
if h != "" {
513+
total += estimateHostEntry(h)
514+
}
515+
}
516+
517+
return total
518+
}
519+
520+
func estimateHostEntry(entry string) int64 {
521+
switch {
522+
case entry == "192":
523+
return 65536 // /16
524+
case entry == "172":
525+
return 1 << 20 // /12
526+
case entry == "10":
527+
return 1 << 24 // /8
528+
case strings.Contains(entry, "/"):
529+
_, ipNet, err := net.ParseCIDR(entry)
530+
if err != nil {
531+
return 1
532+
}
533+
ones, bits := ipNet.Mask.Size()
534+
if bits != 32 {
535+
return 1
536+
}
537+
size := int64(1) << uint(32-ones)
538+
if size > 2 {
539+
size -= 2
540+
}
541+
return size
542+
case strings.Contains(entry, "-") && !strings.Contains(entry, ":") && looksLikeIPRange(entry):
543+
parts := strings.SplitN(entry, "-", 2)
544+
startIP := net.ParseIP(strings.TrimSpace(parts[0]))
545+
if startIP == nil {
546+
return 1
547+
}
548+
startU, ok := ipToUint32(startIP)
549+
if !ok {
550+
return 1
551+
}
552+
endStr := strings.TrimSpace(parts[1])
553+
var endU uint32
554+
if len(endStr) < 4 || !strings.Contains(endStr, ".") {
555+
n, err := strconv.Atoi(endStr)
556+
if err != nil || n > 255 {
557+
return 1
558+
}
559+
endU = (startU & 0xFFFFFF00) | uint32(n)
560+
} else {
561+
endIP := net.ParseIP(endStr)
562+
if endIP == nil {
563+
return 1
564+
}
565+
endU, ok = ipToUint32(endIP)
566+
if !ok {
567+
return 1
568+
}
569+
}
570+
if endU < startU {
571+
return 1
572+
}
573+
return int64(endU-startU) + 1
574+
default:
575+
return 1
576+
}
577+
}

core/scanner.go

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -501,30 +501,50 @@ func addCommonDetails(result *plugins.Result, details map[string]interface{}) {
501501
}
502502

503503
func estimateGlobalTimeout(config *common.Config, session *common.ScanSession) time.Duration {
504-
portCount := len(parsers.ParsePort(config.Target.Ports))
504+
portCount := int64(len(parsers.ParsePort(config.Target.Ports)))
505505
if portCount == 0 {
506-
portCount = len(parsers.ParsePort("21,22,80,443,445,1433,3306,3389,6379,8080"))
506+
portCount = 10
507507
}
508508

509-
hasHostFile := session.Params != nil && session.Params.HostsFile != ""
509+
var hostFile string
510+
var hostStr string
511+
if session.Params != nil {
512+
hostFile = session.Params.HostsFile
513+
hostStr = session.Params.Host
514+
}
515+
hostCount := parsers.EstimateHostCount(hostStr, hostFile)
516+
if hostCount <= 0 {
517+
hostCount = 1
518+
}
510519

511-
// 启发式:端口数越多、有文件输入(目标可能很多),超时越大
512-
switch {
513-
case portCount > 10000 && hasHostFile:
514-
return 24 * time.Hour
515-
case portCount > 10000:
516-
return 6 * time.Hour
517-
case portCount > 1000 && hasHostFile:
518-
return 6 * time.Hour
519-
case portCount > 1000:
520-
return 1 * time.Hour
521-
case portCount > 100 && hasHostFile:
522-
return 1 * time.Hour
523-
case portCount > 100:
524-
return 30 * time.Minute
525-
case hasHostFile:
526-
return 30 * time.Minute
527-
default:
528-
return config.GlobalTimeout
520+
totalTasks := hostCount * portCount
521+
threads := int64(config.ThreadNum)
522+
if threads <= 0 {
523+
threads = 600
524+
}
525+
526+
// 端口扫描:平均每个任务约 50ms(大部分连接快速失败)
527+
portScanSec := float64(totalTasks) * 0.05 / float64(threads)
528+
529+
// 插件扫描:开放率随端口数下降(全端口约 0.1%,少量端口约 5%)
530+
openRate := 0.05
531+
if portCount > 1000 {
532+
openRate = 0.002
533+
} else if portCount > 100 {
534+
openRate = 0.01
535+
}
536+
moduleThreads := float64(config.ModuleThreadNum)
537+
if moduleThreads <= 0 {
538+
moduleThreads = 20
539+
}
540+
pluginSec := float64(totalTasks) * openRate * 2.0 / moduleThreads
541+
// 总估算 + 20% 余量
542+
estimatedSec := (portScanSec + pluginSec) * 1.2
543+
544+
const maxTimeout = 2 * time.Hour
545+
estimated := time.Duration(estimatedSec) * time.Second
546+
if estimated > maxTimeout {
547+
estimated = maxTimeout
529548
}
549+
return estimated
530550
}

0 commit comments

Comments
 (0)