Skip to content

Commit

Permalink
Merge pull request #32 from mfreeman451/31-network-scanningrecon-ability
Browse files Browse the repository at this point in the history
scanner stuff bubbling up to UI now
  • Loading branch information
mfreeman451 authored Jan 18, 2025
2 parents 3662887 + 51ae1e1 commit 4e8e0da
Show file tree
Hide file tree
Showing 9 changed files with 1,025 additions and 220 deletions.
137 changes: 117 additions & 20 deletions pkg/agent/sweep_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,27 @@ type SweepService struct {

// NewSweepService creates a new sweep service.
func NewSweepService(config sweeper.Config) (*SweepService, error) {
// Ensure we have default sweep modes if none specified
if len(config.SweepModes) == 0 {
config.SweepModes = []sweeper.SweepMode{
sweeper.ModeTCP, // Always enable TCP scanning
sweeper.ModeICMP, // Enable ICMP for host discovery
}
}

// Ensure reasonable defaults
if config.Timeout == 0 {
config.Timeout = 2 * time.Second
}
if config.Concurrency == 0 {
config.Concurrency = 100
}
if config.ICMPCount == 0 {
config.ICMPCount = 3 // Default to 3 ICMP attempts
}

log.Printf("Creating sweep service with config: %+v", config)

// Create network sweeper instance
sw := sweeper.NewNetworkSweeper(config)

Expand All @@ -44,7 +65,32 @@ func (s *SweepService) Stop() error {
return s.sweeper.Stop()
}

// In pkg/agent/sweep_service.go
// identifyService maps common port numbers to service names
func identifyService(port int) string {
commonPorts := map[int]string{
21: "FTP",
22: "SSH",
23: "Telnet",
25: "SMTP",
53: "DNS",
80: "HTTP",
110: "POP3",
143: "IMAP",
443: "HTTPS",
3306: "MySQL",
5432: "PostgreSQL",
6379: "Redis",
8080: "HTTP-Alt",
8443: "HTTPS-Alt",
9000: "Kadcast", // Dusk network port
}

if service, ok := commonPorts[port]; ok {
return service
}
return fmt.Sprintf("Port-%d", port)
}

func (s *SweepService) GetStatus(ctx context.Context) (*proto.StatusResponse, error) {
if s == nil {
log.Printf("Warning: Sweep service not initialized")
Expand All @@ -56,7 +102,7 @@ func (s *SweepService) GetStatus(ctx context.Context) (*proto.StatusResponse, er
}, nil
}

// Get latest results
// Get latest results and log them
results, err := s.sweeper.GetResults(ctx, &sweeper.ResultFilter{
StartTime: time.Now().Add(-s.config.Interval),
})
Expand All @@ -65,44 +111,95 @@ func (s *SweepService) GetStatus(ctx context.Context) (*proto.StatusResponse, er
return nil, fmt.Errorf("failed to get sweep results: %w", err)
}

// Aggregate results
log.Printf("Processing %d sweep results", len(results))

// Aggregate results by host
hostMap := make(map[string]*sweeper.HostResult)
portCounts := make(map[int]int)
hostsSeen := make(map[string]bool)
hostsAvailable := make(map[string]bool)
totalHosts := 0

for _, result := range results {
hostsSeen[result.Target.Host] = true
log.Printf("Processing result for host %s (port %d): available=%v time=%v",
result.Target.Host, result.Target.Port, result.Available, result.RespTime)

// Update port counts
if result.Available {
hostsAvailable[result.Target.Host] = true
portCounts[result.Target.Port]++
}

// Get or create host result
host, exists := hostMap[result.Target.Host]
if !exists {
totalHosts++
host = &sweeper.HostResult{
Host: result.Target.Host,
FirstSeen: result.FirstSeen,
LastSeen: result.LastSeen,
Available: false,
PortResults: make([]*sweeper.PortResult, 0),
}
hostMap[result.Target.Host] = host
}

// Update host details
if result.Available {
host.Available = true
if result.Target.Mode == sweeper.ModeTCP {
portResult := &sweeper.PortResult{
Port: result.Target.Port,
Available: true,
RespTime: result.RespTime,
Service: identifyService(result.Target.Port),
}
host.PortResults = append(host.PortResults, portResult)
log.Printf("Found open port %d on host %s (%s)",
result.Target.Port, host.Host, portResult.Service)
}
}
}

// Create sweep status
status := map[string]interface{}{
"network": s.config.Networks[0],
"total_hosts": len(hostsSeen),
"available_hosts": len(hostsAvailable),
"last_sweep": time.Now().Unix(),
"ports": make([]map[string]interface{}, 0, len(portCounts)),
// Create the summary
hosts := make([]sweeper.HostResult, 0, len(hostMap))
availableHosts := 0
for _, host := range hostMap {
if host.Available {
availableHosts++
}
hosts = append(hosts, *host)
}

now := time.Now()
summary := sweeper.SweepSummary{
Network: s.config.Networks[0],
TotalHosts: totalHosts,
AvailableHosts: availableHosts,
LastSweep: now,
Hosts: hosts,
Ports: make([]sweeper.PortCount, 0, len(portCounts)),
}

// Add port statistics
for port, count := range portCounts {
status["ports"] = append(status["ports"].([]map[string]interface{}), map[string]interface{}{
"port": port,
"available": count,
summary.Ports = append(summary.Ports, sweeper.PortCount{
Port: port,
Available: count,
})
}

// Log the final summary
log.Printf("Sweep summary: %d total hosts, %d available, %d ports scanned",
summary.TotalHosts, summary.AvailableHosts, len(summary.Ports))
for _, port := range summary.Ports {
log.Printf("Port %d: %d hosts available", port.Port, port.Available)
}

// Convert to JSON for the message field
statusJSON, err := json.Marshal(status)
statusJSON, err := json.Marshal(summary)
if err != nil {
log.Printf("Error marshaling sweep status: %v", err)
return nil, fmt.Errorf("failed to marshal sweep status: %w", err)
}

log.Printf("Sending sweep status: %s", string(statusJSON))

return &proto.StatusResponse{
Available: true,
Message: string(statusJSON),
Expand Down
102 changes: 102 additions & 0 deletions pkg/sweeper/combined_scanner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Package sweeper pkg/sweeper/combined_scanner.go

package sweeper

import (
"context"
"log"
"sync"
"time"
)

type CombinedScanner struct {
tcpScanner *TCPScanner
icmpScanner *ICMPScanner
done chan struct{}
}

func NewCombinedScanner(timeout time.Duration, concurrency, icmpCount int) *CombinedScanner {
return &CombinedScanner{
tcpScanner: NewTCPScanner(timeout, concurrency),
icmpScanner: NewICMPScanner(timeout, concurrency, icmpCount),
done: make(chan struct{}),
}
}

func (s *CombinedScanner) Stop() error {
close(s.done)
_ = s.tcpScanner.Stop()
_ = s.icmpScanner.Stop()
return nil
}

func (s *CombinedScanner) Scan(ctx context.Context, targets []Target) (<-chan Result, error) {
results := make(chan Result)

// Separate targets by mode
var tcpTargets, icmpTargets []Target
for _, target := range targets {
switch target.Mode {
case ModeTCP:
tcpTargets = append(tcpTargets, target)
case ModeICMP:
icmpTargets = append(icmpTargets, target)
default:
log.Printf("Unknown scan mode for target %v: %v", target, target.Mode)
}
}

var wg sync.WaitGroup

// Start TCP scanner if we have TCP targets
if len(tcpTargets) > 0 {
wg.Add(1)
go func() {
defer wg.Done()
tcpResults, err := s.tcpScanner.Scan(ctx, tcpTargets)
if err != nil {
log.Printf("TCP scan error: %v", err)
return
}
for result := range tcpResults {
select {
case <-ctx.Done():
return
case <-s.done:
return
case results <- result:
}
}
}()
}

// Start ICMP scanner if we have ICMP targets
if len(icmpTargets) > 0 {
wg.Add(1)
go func() {
defer wg.Done()
icmpResults, err := s.icmpScanner.Scan(ctx, icmpTargets)
if err != nil {
log.Printf("ICMP scan error: %v", err)
return
}
for result := range icmpResults {
select {
case <-ctx.Done():
return
case <-s.done:
return
case results <- result:
}
}
}()
}

// Close results when all scans complete
go func() {
wg.Wait()
close(results)
}()

return results, nil
}
Loading

0 comments on commit 4e8e0da

Please sign in to comment.