mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-02-09 02:09:17 +08:00
1. 端口优先级排序:高价值端口(80,443,22,3389等)优先扫描 - 用户能更快看到有意义的结果 - 不影响端口喷洒策略 2. TCP 补充探测:ICMP 响应率<10%时自动启用 - 对未响应主机用 TCP 80/443/22/445 补充探测 - 解决防火墙过滤 ICMP 导致漏检的问题
235 lines
5.8 KiB
Go
235 lines
5.8 KiB
Go
package core
|
||
|
||
import (
|
||
"fmt"
|
||
"sync"
|
||
"testing"
|
||
)
|
||
|
||
/*
|
||
socket_iterator_test.go - SocketIterator 高价值测试
|
||
|
||
测试重点:
|
||
1. 端口喷洒顺序 - 这是核心设计,顺序错误会导致单IP限速
|
||
2. 并发安全性 - 多worker并发调用Next()不丢失不重复
|
||
3. 边界情况 - 空输入、单元素
|
||
|
||
不测试:
|
||
- getter方法(Total)- 太简单
|
||
- 内部状态 - 只关心外部行为
|
||
*/
|
||
|
||
// TestSocketIterator_PortSprayOrder 验证端口喷洒顺序
|
||
//
|
||
// 这是最重要的测试:顺序必须是先遍历所有IP的同一端口,再换端口
|
||
// 正确顺序:Port1[IP1,IP2,IP3] → Port2[IP1,IP2,IP3]
|
||
// 错误顺序:IP1[Port1,Port2,Port3] → IP2[Port1,Port2,Port3]
|
||
func TestSocketIterator_PortSprayOrder(t *testing.T) {
|
||
hosts := []string{"192.168.1.1", "192.168.1.2", "192.168.1.3"}
|
||
ports := []int{80, 443}
|
||
|
||
it := NewSocketIterator(hosts, ports, nil)
|
||
|
||
// 期望的顺序:先所有IP的80端口,再所有IP的443端口
|
||
expected := []struct {
|
||
host string
|
||
port int
|
||
}{
|
||
{"192.168.1.1", 80},
|
||
{"192.168.1.2", 80},
|
||
{"192.168.1.3", 80},
|
||
{"192.168.1.1", 443},
|
||
{"192.168.1.2", 443},
|
||
{"192.168.1.3", 443},
|
||
}
|
||
|
||
for i, exp := range expected {
|
||
host, port, ok := it.Next()
|
||
if !ok {
|
||
t.Fatalf("第%d次迭代提前结束", i+1)
|
||
}
|
||
if host != exp.host || port != exp.port {
|
||
t.Errorf("第%d次迭代: 期望 %s:%d, 实际 %s:%d",
|
||
i+1, exp.host, exp.port, host, port)
|
||
}
|
||
}
|
||
|
||
// 验证迭代结束
|
||
_, _, ok := it.Next()
|
||
if ok {
|
||
t.Error("迭代应该已结束")
|
||
}
|
||
}
|
||
|
||
// TestSocketIterator_ConcurrentSafety 验证并发安全性
|
||
//
|
||
// 多个goroutine同时调用Next(),所有任务必须:
|
||
// 1. 不丢失 - 每个host:port组合只出现一次
|
||
// 2. 不重复 - 总数等于预期
|
||
func TestSocketIterator_ConcurrentSafety(t *testing.T) {
|
||
// 构造较大的测试集
|
||
hosts := make([]string, 100)
|
||
for i := range hosts {
|
||
hosts[i] = fmt.Sprintf("192.168.1.%d", i+1)
|
||
}
|
||
ports := []int{22, 80, 443, 3306, 6379}
|
||
|
||
it := NewSocketIterator(hosts, ports, nil)
|
||
expectedTotal := len(hosts) * len(ports)
|
||
|
||
// 记录所有结果
|
||
results := make(map[string]int)
|
||
var mu sync.Mutex
|
||
var wg sync.WaitGroup
|
||
|
||
// 启动10个并发worker
|
||
workers := 10
|
||
for i := 0; i < workers; i++ {
|
||
wg.Add(1)
|
||
go func() {
|
||
defer wg.Done()
|
||
for {
|
||
host, port, ok := it.Next()
|
||
if !ok {
|
||
return
|
||
}
|
||
key := fmt.Sprintf("%s:%d", host, port)
|
||
mu.Lock()
|
||
results[key]++
|
||
mu.Unlock()
|
||
}
|
||
}()
|
||
}
|
||
|
||
wg.Wait()
|
||
|
||
// 验证:每个组合只出现一次
|
||
if len(results) != expectedTotal {
|
||
t.Errorf("任务丢失或重复: 期望 %d 个唯一组合, 实际 %d", expectedTotal, len(results))
|
||
}
|
||
|
||
// 验证:没有重复
|
||
for key, count := range results {
|
||
if count != 1 {
|
||
t.Errorf("任务重复: %s 出现 %d 次", key, count)
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestSocketIterator_ExcludePorts 验证端口过滤
|
||
func TestSocketIterator_ExcludePorts(t *testing.T) {
|
||
hosts := []string{"192.168.1.1"}
|
||
ports := []int{22, 80, 443, 3306}
|
||
exclude := map[int]struct{}{
|
||
80: {},
|
||
3306: {},
|
||
}
|
||
|
||
it := NewSocketIterator(hosts, ports, exclude)
|
||
|
||
// 应该只有22和443,按优先级排序:443(优先级2) 在 22(优先级3) 之前
|
||
var gotPorts []int
|
||
for {
|
||
_, port, ok := it.Next()
|
||
if !ok {
|
||
break
|
||
}
|
||
gotPorts = append(gotPorts, port)
|
||
}
|
||
|
||
if len(gotPorts) != 2 {
|
||
t.Fatalf("期望2个端口, 实际 %d", len(gotPorts))
|
||
}
|
||
// 443优先级高于22,所以443在前
|
||
if gotPorts[0] != 443 || gotPorts[1] != 22 {
|
||
t.Errorf("期望 [443, 22] (按优先级排序), 实际 %v", gotPorts)
|
||
}
|
||
|
||
// 验证Total也正确
|
||
if it.Total() != 2 {
|
||
t.Errorf("Total() 应该是2, 实际 %d", it.Total())
|
||
}
|
||
}
|
||
|
||
// TestSocketIterator_EmptyInputs 验证边界情况
|
||
func TestSocketIterator_EmptyInputs(t *testing.T) {
|
||
t.Run("空hosts", func(t *testing.T) {
|
||
it := NewSocketIterator(nil, []int{80}, nil)
|
||
_, _, ok := it.Next()
|
||
if ok {
|
||
t.Error("空hosts应该立即返回false")
|
||
}
|
||
if it.Total() != 0 {
|
||
t.Errorf("Total() 应该是0, 实际 %d", it.Total())
|
||
}
|
||
})
|
||
|
||
t.Run("空ports", func(t *testing.T) {
|
||
it := NewSocketIterator([]string{"192.168.1.1"}, nil, nil)
|
||
_, _, ok := it.Next()
|
||
if ok {
|
||
t.Error("空ports应该立即返回false")
|
||
}
|
||
})
|
||
|
||
t.Run("全部被排除", func(t *testing.T) {
|
||
exclude := map[int]struct{}{80: {}, 443: {}}
|
||
it := NewSocketIterator([]string{"192.168.1.1"}, []int{80, 443}, exclude)
|
||
_, _, ok := it.Next()
|
||
if ok {
|
||
t.Error("全部端口被排除应该立即返回false")
|
||
}
|
||
})
|
||
}
|
||
|
||
// TestSocketIterator_PortPrioritySort 验证端口优先级排序
|
||
// 高价值端口(80, 443, 22等)应该排在前面
|
||
func TestSocketIterator_PortPrioritySort(t *testing.T) {
|
||
hosts := []string{"192.168.1.1"}
|
||
// 故意乱序输入,包含高优先级和普通端口
|
||
ports := []int{9999, 22, 8888, 80, 7777, 443, 3389, 1234}
|
||
|
||
it := NewSocketIterator(hosts, ports, nil)
|
||
|
||
var gotPorts []int
|
||
for {
|
||
_, port, ok := it.Next()
|
||
if !ok {
|
||
break
|
||
}
|
||
gotPorts = append(gotPorts, port)
|
||
}
|
||
|
||
// 期望顺序:高优先级端口按优先级排序,然后是普通端口按数字升序
|
||
// 80(优先级1), 443(2), 22(3), 3389(4), 然后 1234, 7777, 8888, 9999
|
||
expected := []int{80, 443, 22, 3389, 1234, 7777, 8888, 9999}
|
||
|
||
if len(gotPorts) != len(expected) {
|
||
t.Fatalf("端口数量不匹配: 期望 %d, 实际 %d", len(expected), len(gotPorts))
|
||
}
|
||
|
||
for i, exp := range expected {
|
||
if gotPorts[i] != exp {
|
||
t.Errorf("第%d个端口: 期望 %d, 实际 %d\n完整结果: %v", i, exp, gotPorts[i], gotPorts)
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestSocketIterator_SingleElements 验证单元素情况
|
||
func TestSocketIterator_SingleElements(t *testing.T) {
|
||
t.Run("单IP单端口", func(t *testing.T) {
|
||
it := NewSocketIterator([]string{"10.0.0.1"}, []int{8080}, nil)
|
||
|
||
host, port, ok := it.Next()
|
||
if !ok || host != "10.0.0.1" || port != 8080 {
|
||
t.Errorf("期望 10.0.0.1:8080, 实际 %s:%d, ok=%v", host, port, ok)
|
||
}
|
||
|
||
_, _, ok = it.Next()
|
||
if ok {
|
||
t.Error("应该只有一个元素")
|
||
}
|
||
})
|
||
}
|