feat: SDK agent integration + UDP plugin framework + SNMP plugin

SDK enhancements for endpoint agent embedding:
- ScanWithController for pause/resume and live stats
- OnProgress callback for periodic progress reporting
- TaskID injection into every scan result
- ScanController with goroutine-safe pause/resume/stats
- Multi-target stats aggregation (race-free)

UDP plugin infrastructure:
- PluginTypeUDP registry with dedicated dispatch path
- DialUDP on ScanSession with rate limiting and packet counting
- UDP plugins bypass TCP port scan, probe targets directly
- FilterService excludes UDP plugins from TCP port matching

SNMP plugin (first UDP plugin):
- SNMPv2c GetRequest probe for sysDescr detection
- Community string brute force (public/private/community/etc)
- Pure stdlib implementation (encoding/asn1)
- Registered as safe default plugin on port 161/UDP

Tests: 95.7% SDK coverage, race-free, 50+ new test cases
This commit is contained in:
ZacharyZcR
2026-05-18 23:11:39 +08:00
parent eb4fa38fea
commit a1588a321f
16 changed files with 1881 additions and 72 deletions
+61
View File
@@ -0,0 +1,61 @@
package main
import (
"context"
"fmt"
"time"
fscan "github.com/shadow1ng/fscan/pkg/fscan"
)
func main() {
scanner := fscan.NewScanner(fscan.Config{
TaskID: "task-001",
Timeout: 3 * time.Second,
Threads: 64,
DisablePing: true,
DisableBrute: true,
Plugins: []string{"ssh", "mysql", "redis", "ftp"},
OnProgress: func(p fscan.ScanProgress) {
fmt.Printf("[progress] %d/%d tasks, %d packets, paused=%v, elapsed=%s\n",
p.TasksCompleted, p.TasksTotal, p.Packets, p.Paused, p.Duration.Round(time.Millisecond))
},
})
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
ctrl, reportCh, errCh := scanner.ScanWithController(ctx,
fscan.Target{Host: "127.0.0.1", Ports: []int{21, 22, 3306, 6379}},
)
// Simulate a pause command from control plane after 1 second.
go func() {
time.Sleep(1 * time.Second)
fmt.Println("[agent] pausing scan...")
ctrl.Pause()
// Check live stats while paused.
stats := ctrl.Stats()
fmt.Printf("[agent] stats while paused: completed=%d, packets=%d\n",
stats.TasksCompleted, stats.Packets)
time.Sleep(2 * time.Second)
fmt.Println("[agent] resuming scan...")
ctrl.Resume()
}()
report := <-reportCh
if err := <-errCh; err != nil {
fmt.Printf("[agent] scan error: %v\n", err)
return
}
fmt.Printf("\n[agent] scan complete: %d results, %d vulns, %d services\n",
report.Summary.Total, report.Summary.Vulns, report.Summary.Services)
for _, result := range report.Results {
taskID, _ := result.DetailString("task_id")
fmt.Printf(" [%s] %s %s (task=%s)\n", result.Type, result.Target, result.Status, taskID)
}
}