mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-23 11:41:53 +08:00
perf: 六项性能优化
- DNS 解析缓存:sync.Map 缓存避免重复系统调用 - 凭据测试 TCP 预检:不可达目标直接跳过全部凭据 - Web 探测 HTTP Client 复用:全局共享连接池 - 端口扫描 Bloom Filter 去重:替代 map 降低内存 - 进度条 atomic 累加 + 50ms 节流渲染:消除锁竞争 - 服务探针预解码:Init 时预编译,运行时零解码开销
This commit is contained in:
+1
-1
@@ -391,7 +391,7 @@ func RunIcmp1(hostslist []string, conn *icmp.PacketConn, chanHosts chan string,
|
||||
}
|
||||
packets := make([]icmpPacket, 0, len(hostslist))
|
||||
for _, host := range hostslist {
|
||||
dst, _ := net.ResolveIPAddr("ip", host)
|
||||
dst, _ := common.DNSCache.ResolveIP(host)
|
||||
packets = append(packets, icmpPacket{data: makemsg(host), dst: dst})
|
||||
}
|
||||
|
||||
|
||||
+18
-10
@@ -39,17 +39,22 @@ var resourceExhaustedPatterns = []string{
|
||||
}
|
||||
|
||||
// resultCollector 结果收集器,用于并发安全地收集扫描结果
|
||||
// 使用 map 实现:O(1) 的添加和删除,无顺序依赖问题
|
||||
// 使用 Bloom Filter 去重 + slice 存储,大规模扫描时内存更优
|
||||
type resultCollector struct {
|
||||
mu sync.Mutex
|
||||
addrs map[string]struct{}
|
||||
addrs []string
|
||||
bloom *BloomFilter
|
||||
stream chan<- string // 可选:流式通知 channel
|
||||
}
|
||||
|
||||
// newResultCollector 创建结果收集器
|
||||
func newResultCollector(stream chan<- string) *resultCollector {
|
||||
func newResultCollector(stream chan<- string, expectedSize int) *resultCollector {
|
||||
if expectedSize < 1024 {
|
||||
expectedSize = 1024
|
||||
}
|
||||
return &resultCollector{
|
||||
addrs: make(map[string]struct{}),
|
||||
addrs: make([]string, 0, expectedSize/10),
|
||||
bloom: NewBloomFilter(expectedSize, 0.001),
|
||||
stream: stream,
|
||||
}
|
||||
}
|
||||
@@ -57,7 +62,12 @@ func newResultCollector(stream chan<- string) *resultCollector {
|
||||
// Add 添加一个扫描结果
|
||||
func (c *resultCollector) Add(addr string) {
|
||||
c.mu.Lock()
|
||||
c.addrs[addr] = struct{}{}
|
||||
if c.bloom.Contains(addr) {
|
||||
c.mu.Unlock()
|
||||
return
|
||||
}
|
||||
c.bloom.Add(addr)
|
||||
c.addrs = append(c.addrs, addr)
|
||||
c.mu.Unlock()
|
||||
if c.stream != nil {
|
||||
c.stream <- addr
|
||||
@@ -67,10 +77,8 @@ func (c *resultCollector) Add(addr string) {
|
||||
// GetAll 获取所有结果
|
||||
func (c *resultCollector) GetAll() []string {
|
||||
c.mu.Lock()
|
||||
result := make([]string, 0, len(c.addrs))
|
||||
for addr := range c.addrs {
|
||||
result = append(result, addr)
|
||||
}
|
||||
result := make([]string, len(c.addrs))
|
||||
copy(result, c.addrs)
|
||||
c.mu.Unlock()
|
||||
return result
|
||||
}
|
||||
@@ -181,7 +189,7 @@ func EnhancedPortScan(ctx context.Context, hosts []string, ports string, timeout
|
||||
to := time.Duration(timeout) * time.Second
|
||||
adaptiveTO := NewAdaptiveTimeout(to)
|
||||
var count int64
|
||||
collector := newResultCollector(stream)
|
||||
collector := newResultCollector(stream, totalTasks)
|
||||
failedCollector := &failedPortCollector{}
|
||||
var wg sync.WaitGroup
|
||||
|
||||
|
||||
@@ -25,6 +25,23 @@ func (vs *VScan) Init() {
|
||||
vs.parseProbesToMapKName()
|
||||
vs.SetusedProbes()
|
||||
vs.compileFallbacks() // 编译 fallback 数组
|
||||
vs.preDecodeProbeData() // 预解码探针数据
|
||||
}
|
||||
|
||||
// preDecodeProbeData 预解码所有探针的 Data 字段,避免运行时重复解码
|
||||
func (vs *VScan) preDecodeProbeData() {
|
||||
for i := range vs.Probes {
|
||||
if vs.Probes[i].Data != "" {
|
||||
decoded, err := DecodeData(vs.Probes[i].Data)
|
||||
if err == nil {
|
||||
vs.Probes[i].DecodedData = decoded
|
||||
}
|
||||
}
|
||||
}
|
||||
// 同步到 map
|
||||
for i := range vs.Probes {
|
||||
vs.ProbesMapKName[vs.Probes[i].Name] = vs.Probes[i]
|
||||
}
|
||||
}
|
||||
|
||||
// compileFallbacks 编译所有探测器的 fallback 数组
|
||||
|
||||
@@ -20,6 +20,7 @@ const MaxFallbacks = 20
|
||||
type Probe struct {
|
||||
Name string // 探测器名称
|
||||
Data string // 探测数据
|
||||
DecodedData []byte // 预解码的探测数据
|
||||
Protocol string // 协议
|
||||
Ports string // 端口范围
|
||||
SSLPorts string // SSL端口范围
|
||||
|
||||
+26
-7
@@ -219,9 +219,14 @@ func (s *SmartPortInfoScanner) tryProbeList(probes []*Probe, usedProbes map[stri
|
||||
}
|
||||
usedProbes[probe.Name] = struct{}{}
|
||||
|
||||
probeData, err := DecodeData(probe.Data)
|
||||
if err != nil {
|
||||
continue
|
||||
// 优先使用预解码数据
|
||||
probeData := probe.DecodedData
|
||||
if probeData == nil {
|
||||
var err error
|
||||
probeData, err = DecodeData(probe.Data)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 使用 TotalWaitMS 设置动态超时
|
||||
@@ -282,8 +287,15 @@ func (s *SmartPortInfoScanner) performSSLSecondStage(serviceInfo *ServiceInfo) *
|
||||
continue
|
||||
}
|
||||
|
||||
probeData, err := DecodeData(probe.Data)
|
||||
if err != nil || len(probeData) == 0 {
|
||||
probeData := probe.DecodedData
|
||||
if probeData == nil {
|
||||
var decErr error
|
||||
probeData, decErr = DecodeData(probe.Data)
|
||||
if decErr != nil || len(probeData) == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(probeData) == 0 {
|
||||
continue
|
||||
}
|
||||
response := s.info.Connect(probeData)
|
||||
@@ -317,8 +329,15 @@ func (s *SmartPortInfoScanner) tryHTTPSProbe() *ServiceInfo {
|
||||
return nil
|
||||
}
|
||||
|
||||
probeData, err := DecodeData(probe.Data)
|
||||
if err != nil || len(probeData) == 0 {
|
||||
probeData := probe.DecodedData
|
||||
if probeData == nil {
|
||||
var decErr error
|
||||
probeData, decErr = DecodeData(probe.Data)
|
||||
if decErr != nil || len(probeData) == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if len(probeData) == 0 {
|
||||
return nil
|
||||
}
|
||||
response := s.info.Connect(probeData)
|
||||
|
||||
+21
-10
@@ -19,6 +19,25 @@ import (
|
||||
// Web服务检测
|
||||
// ===============================
|
||||
|
||||
// 全局共享 HTTP Client,复用连接池减少 TLS 握手和 TCP 建连开销
|
||||
var (
|
||||
sharedHTTPClientOnce sync.Once
|
||||
sharedHTTPClient *http.Client
|
||||
)
|
||||
|
||||
func getSharedHTTPClient(config *common.Config) *http.Client {
|
||||
sharedHTTPClientOnce.Do(func() {
|
||||
sharedHTTPClient = createHTTPClient(config)
|
||||
// 启用 keep-alive 复用连接
|
||||
if t, ok := sharedHTTPClient.Transport.(*http.Transport); ok {
|
||||
t.DisableKeepAlives = false
|
||||
t.MaxIdleConns = 100
|
||||
t.MaxIdleConnsPerHost = 2
|
||||
}
|
||||
})
|
||||
return sharedHTTPClient
|
||||
}
|
||||
|
||||
// WebPortDetector 简化的Web检测器 - 保持API兼容
|
||||
type WebPortDetector struct{}
|
||||
|
||||
@@ -59,15 +78,7 @@ func DetectHTTPScheme(host string, port int, config *common.Config, session *com
|
||||
// TLS握手失败,记录原因
|
||||
|
||||
// 第二步:尝试HTTP请求(回退检测HTTP)
|
||||
client := &http.Client{
|
||||
Timeout: timeout,
|
||||
Transport: &http.Transport{
|
||||
DisableKeepAlives: true,
|
||||
},
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse // 不跟随重定向
|
||||
},
|
||||
}
|
||||
client := getSharedHTTPClient(config)
|
||||
|
||||
// 使用HEAD请求(更轻量)
|
||||
httpURL := fmt.Sprintf("http://%s", addr)
|
||||
@@ -132,7 +143,7 @@ func (w *WebPortDetector) DetectHTTPServiceOnly(host string, port int, config *c
|
||||
return false
|
||||
}
|
||||
|
||||
client := createHTTPClient(config)
|
||||
client := getSharedHTTPClient(config)
|
||||
|
||||
// 尝试HTTP
|
||||
if w.tryHTTP(client, host, port, "http") {
|
||||
|
||||
Reference in New Issue
Block a user