feat: 实现进度条网络包统计功能,支持TCP成功失败分类

- 新增全局原子操作包计数器,支持TCP成功/失败、UDP分类统计
- 端口扫描TCP连接区分成功失败计数,提供准确的网络活动监控
- HTTP请求统一归类到TCP统计,简化显示逻辑
- 进度条实时显示发包统计:发包:总数[TCP:成功✓失败✗,UDP:数量]
- 服务插件TCP/UDP连接同步计数,确保统计完整性
- 使用原子操作保证高并发环境下计数准确性
This commit is contained in:
ZacharyZcR 2025-09-02 08:45:44 +00:00
parent a7ea2f5198
commit a36767c158
8 changed files with 176 additions and 5 deletions

View File

@ -2,6 +2,7 @@ package common
import ( import (
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/shadow1ng/fscan/common/config" "github.com/shadow1ng/fscan/common/config"
@ -101,6 +102,14 @@ var (
End int64 // 结束计数器 End int64 // 结束计数器
Num int64 // 数量计数器 Num int64 // 数量计数器
// 网络包计数器 - 使用原子操作确保线程安全
PacketCount int64 // 总发包数量
TCPPacketCount int64 // TCP包数量总计
TCPSuccessPacketCount int64 // TCP成功连接包数量
TCPFailedPacketCount int64 // TCP失败连接包数量
UDPPacketCount int64 // UDP包数量
HTTPPacketCount int64 // HTTP请求数量
// ============================================================================= // =============================================================================
// 初始化控制 // 初始化控制
// ============================================================================= // =============================================================================
@ -172,4 +181,79 @@ const (
) )
// LogWithProgress 已在progressmanager.go中定义此处不重复 // LogWithProgress 已在progressmanager.go中定义此处不重复
// =============================================================================
// 网络包计数器功能
// =============================================================================
// IncrementPacketCount 增加总包计数(原子操作)
func IncrementPacketCount() int64 {
return atomic.AddInt64(&PacketCount, 1)
}
// IncrementTCPSuccessPacketCount 增加TCP成功连接包计数原子操作
func IncrementTCPSuccessPacketCount() int64 {
atomic.AddInt64(&TCPSuccessPacketCount, 1)
atomic.AddInt64(&TCPPacketCount, 1)
return atomic.AddInt64(&PacketCount, 1)
}
// IncrementTCPFailedPacketCount 增加TCP失败连接包计数原子操作
func IncrementTCPFailedPacketCount() int64 {
atomic.AddInt64(&TCPFailedPacketCount, 1)
atomic.AddInt64(&TCPPacketCount, 1)
return atomic.AddInt64(&PacketCount, 1)
}
// IncrementUDPPacketCount 增加UDP包计数原子操作
func IncrementUDPPacketCount() int64 {
atomic.AddInt64(&UDPPacketCount, 1)
return atomic.AddInt64(&PacketCount, 1)
}
// IncrementHTTPPacketCount 增加HTTP包计数原子操作
func IncrementHTTPPacketCount() int64 {
atomic.AddInt64(&HTTPPacketCount, 1)
return atomic.AddInt64(&PacketCount, 1)
}
// GetPacketCount 获取总包计数(原子操作)
func GetPacketCount() int64 {
return atomic.LoadInt64(&PacketCount)
}
// GetTCPPacketCount 获取TCP包计数原子操作
func GetTCPPacketCount() int64 {
return atomic.LoadInt64(&TCPPacketCount)
}
// GetTCPSuccessPacketCount 获取TCP成功连接包计数原子操作
func GetTCPSuccessPacketCount() int64 {
return atomic.LoadInt64(&TCPSuccessPacketCount)
}
// GetTCPFailedPacketCount 获取TCP失败连接包计数原子操作
func GetTCPFailedPacketCount() int64 {
return atomic.LoadInt64(&TCPFailedPacketCount)
}
// GetUDPPacketCount 获取UDP包计数原子操作
func GetUDPPacketCount() int64 {
return atomic.LoadInt64(&UDPPacketCount)
}
// GetHTTPPacketCount 获取HTTP包计数原子操作
func GetHTTPPacketCount() int64 {
return atomic.LoadInt64(&HTTPPacketCount)
}
// ResetPacketCounters 重置所有包计数器(原子操作)
func ResetPacketCounters() {
atomic.StoreInt64(&PacketCount, 0)
atomic.StoreInt64(&TCPPacketCount, 0)
atomic.StoreInt64(&TCPSuccessPacketCount, 0)
atomic.StoreInt64(&TCPFailedPacketCount, 0)
atomic.StoreInt64(&UDPPacketCount, 0)
atomic.StoreInt64(&HTTPPacketCount, 0)
}

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"runtime" "runtime"
"strings"
"sync" "sync"
"time" "time"
@ -171,7 +172,32 @@ func (pm *ProgressManager) generateProgressBar() string {
if pm.total == 0 { if pm.total == 0 {
spinner := pm.getActivityIndicator() spinner := pm.getActivityIndicator()
memInfo := pm.getMemoryInfo() memInfo := pm.getMemoryInfo()
return fmt.Sprintf("%s %s 等待中... %s", pm.description, spinner, memInfo)
// 获取TCP包统计包含原HTTP请求
packetCount := GetPacketCount()
tcpSuccess := GetTCPSuccessPacketCount()
tcpFailed := GetTCPFailedPacketCount()
udpCount := GetUDPPacketCount()
packetInfo := ""
if packetCount > 0 {
// 构建简化的包统计信息只显示TCP和UDP
details := make([]string, 0, 2)
if tcpSuccess > 0 || tcpFailed > 0 {
details = append(details, fmt.Sprintf("TCP:%d✓%d✗", tcpSuccess, tcpFailed))
}
if udpCount > 0 {
details = append(details, fmt.Sprintf("UDP:%d", udpCount))
}
if len(details) > 0 {
packetInfo = fmt.Sprintf(" 发包:%d[%s]", packetCount, strings.Join(details, ","))
} else {
packetInfo = fmt.Sprintf(" 发包:%d", packetCount)
}
}
return fmt.Sprintf("%s %s 等待中...%s %s", pm.description, spinner, packetInfo, memInfo)
} }
percentage := float64(pm.current) / float64(pm.total) * 100 percentage := float64(pm.current) / float64(pm.total) * 100
@ -231,9 +257,33 @@ func (pm *ProgressManager) generateProgressBar() string {
// 生成活跃指示器 // 生成活跃指示器
spinner := pm.getActivityIndicator() spinner := pm.getActivityIndicator()
// 获取TCP包统计包含原HTTP请求
packetCount := GetPacketCount()
tcpSuccess := GetTCPSuccessPacketCount()
tcpFailed := GetTCPFailedPacketCount()
udpCount := GetUDPPacketCount()
packetInfo := ""
if packetCount > 0 {
// 构建简化的包统计信息只显示TCP和UDP
details := make([]string, 0, 2)
if tcpSuccess > 0 || tcpFailed > 0 {
details = append(details, fmt.Sprintf("TCP:%d✓%d✗", tcpSuccess, tcpFailed))
}
if udpCount > 0 {
details = append(details, fmt.Sprintf("UDP:%d", udpCount))
}
if len(details) > 0 {
packetInfo = fmt.Sprintf(" 发包:%d[%s]", packetCount, strings.Join(details, ","))
} else {
packetInfo = fmt.Sprintf(" 发包:%d", packetCount)
}
}
// 构建基础进度条 // 构建基础进度条
baseProgress := fmt.Sprintf("%s %s %6.1f%% %s (%d/%d)%s%s", baseProgress := fmt.Sprintf("%s %s %6.1f%% %s (%d/%d)%s%s%s",
pm.description, spinner, percentage, bar, pm.current, pm.total, speedStr, eta) pm.description, spinner, percentage, bar, pm.current, pm.total, speedStr, eta, packetInfo)
// 添加内存信息 // 添加内存信息
memInfo := pm.getMemoryInfo() memInfo := pm.getMemoryInfo()

View File

@ -79,10 +79,16 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
// 连接测试 - 支持SOCKS5代理 // 连接测试 - 支持SOCKS5代理
conn, err := common.WrapperTcpWithTimeout("tcp", addr, to) conn, err := common.WrapperTcpWithTimeout("tcp", addr, to)
if err != nil { if err != nil {
// 计数TCP连接失败包
common.IncrementTCPFailedPacketCount()
return nil return nil
} }
defer conn.Close() defer conn.Close()
// 计数TCP连接成功包
common.IncrementTCPSuccessPacketCount()
// 记录开放端口(先记录到内存,延迟输出直到服务识别完成) // 记录开放端口(先记录到内存,延迟输出直到服务识别完成)
atomic.AddInt64(&count, 1) atomic.AddInt64(&count, 1)

View File

@ -254,11 +254,17 @@ func (w *WebPortDetector) tryHTTPConnectionDirect(ctx context.Context, host stri
req.Header.Set("Accept", "*/*") req.Header.Set("Accept", "*/*")
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
// HTTP请求失败计为TCP失败
common.IncrementTCPFailedPacketCount()
// 检查错误类型某些错误也表明是HTTP服务 // 检查错误类型某些错误也表明是HTTP服务
return w.analyzeHTTPError(err) return w.analyzeHTTPError(err)
} }
defer resp.Body.Close() defer resp.Body.Close()
// HTTP请求成功计为TCP成功
common.IncrementTCPSuccessPacketCount()
// 分析HTTP响应 // 分析HTTP响应
return w.analyzeHTTPResponse(resp, url) return w.analyzeHTTPResponse(resp, url)

View File

@ -127,9 +127,14 @@ func (p *RedisPlugin) testCredential(ctx context.Context, info *common.HostInfo,
// 建立TCP连接 // 建立TCP连接
conn, err := net.DialTimeout("tcp", target, timeout) conn, err := net.DialTimeout("tcp", target, timeout)
if err != nil { if err != nil {
// 计数TCP连接失败包
common.IncrementTCPFailedPacketCount()
connChan <- connResult{nil, err} connChan <- connResult{nil, err}
return return
} }
// 计数TCP连接成功包
common.IncrementTCPSuccessPacketCount()
// 如果有密码,进行认证 // 如果有密码,进行认证
if cred.Password != "" { if cred.Password != "" {
@ -353,6 +358,8 @@ func (p *RedisPlugin) identifyService(ctx context.Context, info *common.HostInfo
// 尝试连接Redis服务 // 尝试连接Redis服务
conn, err := net.DialTimeout("tcp", target, timeout) conn, err := net.DialTimeout("tcp", target, timeout)
if err != nil { if err != nil {
// 计数TCP连接失败包
common.IncrementTCPFailedPacketCount()
return &ScanResult{ return &ScanResult{
Success: false, Success: false,
Service: "redis", Service: "redis",
@ -360,6 +367,9 @@ func (p *RedisPlugin) identifyService(ctx context.Context, info *common.HostInfo
} }
} }
defer conn.Close() defer conn.Close()
// 计数TCP连接成功包
common.IncrementTCPSuccessPacketCount()
// 发送PING命令识别 // 发送PING命令识别
pingCmd := "PING\r\n" pingCmd := "PING\r\n"

View File

@ -67,6 +67,9 @@ func (p *SNMPPlugin) testCredential(ctx context.Context, info *common.HostInfo,
return false return false
} }
defer conn.Close() defer conn.Close()
// 计数UDP连接包
common.IncrementUDPPacketCount()
packet := p.buildSNMPGetRequest(cred.Username, "1.3.6.1.2.1.1.1.0") packet := p.buildSNMPGetRequest(cred.Username, "1.3.6.1.2.1.1.1.0")
@ -129,6 +132,9 @@ func (p *SNMPPlugin) identifyService(ctx context.Context, info *common.HostInfo)
} }
} }
defer conn.Close() defer conn.Close()
// 计数UDP连接包
common.IncrementUDPPacketCount()
banner := "SNMP网络管理服务" banner := "SNMP网络管理服务"
common.LogSuccess(fmt.Sprintf("SNMP %s %s", target, banner)) common.LogSuccess(fmt.Sprintf("SNMP %s %s", target, banner))

View File

@ -173,8 +173,12 @@ func (p *SSHPlugin) testCredential(ctx context.Context, info *common.HostInfo, c
select { select {
case result := <-resultChan: case result := <-resultChan:
if result.err != nil { if result.err != nil {
// 计数TCP连接失败包
common.IncrementTCPFailedPacketCount()
return nil return nil
} }
// 计数TCP连接成功包
common.IncrementTCPSuccessPacketCount()
return result.client return result.client
case <-ctx.Done(): case <-ctx.Done():
return nil return nil

View File

@ -685,10 +685,15 @@ func DoRequest(req *http.Request, redirect bool) (*Response, error) {
} else { } else {
oResp, err = ClientNoRedirect.Do(req) oResp, err = ClientNoRedirect.Do(req)
} }
if err != nil { if err != nil {
// HTTP请求失败计为TCP失败
common.IncrementTCPFailedPacketCount()
return nil, fmt.Errorf("请求执行失败: %w", err) return nil, fmt.Errorf("请求执行失败: %w", err)
} }
// HTTP请求成功计为TCP成功
common.IncrementTCPSuccessPacketCount()
defer oResp.Body.Close() defer oResp.Body.Close()
// 解析响应 // 解析响应