pingScanner = $pingScanner; $this->tcpPortScanner = $tcpPortScanner; $this->arpTableReader = $arpTableReader; $this->fingerprintService = $fingerprintService; $this->discoveredHostRepository = $discoveredHostRepository; } /** * @return DiscoveredHost[] */ public function scan(NetworkRange $range): array { $ips = $this->enumerateIps($range->cidr); $aliveHosts = []; foreach ($ips as $ip) { // Step 1: Ping sweep if (!$this->pingScanner->ping($ip)) { continue; } $host = new DiscoveredHost(); $host->ipAddress = $ip; $host->firstSeen = new \DateTimeImmutable(); $host->lastSeen = new \DateTimeImmutable(); // Step 2: TCP port scan $openPorts = $this->tcpPortScanner->scan($ip, $this->defaultPorts, 200, 50); $host->openPorts = $openPorts; // Step 3: Reverse DNS $hostname = $this->resolveHostname($ip); $host->hostname = $hostname; // Step 4: Determine vendor by port patterns $host->vendor = $this->fingerprintService->guessVendor($openPorts, $hostname); // Step 5: Confidence score $host->confidence = $this->fingerprintService->calculateConfidence(count($openPorts), $hostname !== null); $aliveHosts[] = $host; } // Step 6: ARP table enrichment $arpEntries = $this->arpTableReader->read(); foreach ($aliveHosts as $host) { if (isset($arpEntries[$host->ipAddress])) { $host->macAddress = $arpEntries[$host->ipAddress]['mac'] ?? null; $host->vendor = $host->vendor ?: ($arpEntries[$host->ipAddress]['vendor'] ?? null); } $this->discoveredHostRepository->save($host); } return $aliveHosts; } /** * @return string[] */ private function enumerateIps(string $cidr): array { if (str_contains($cidr, '/32')) { return [explode('/', $cidr)[0]]; } // Only handle /24 or larger for MVP (to avoid scanning huge ranges) if (preg_match('#^(\d+\.\d+\.\d+)\.(\d+)/(\d+)$#', $cidr, $m)) { $prefix = $m[1]; $suffix = (int)$m[2]; $mask = (int)$m[3]; if ($mask > 24) { // Treat smaller than /24 as single IP return [$cidr]; } $offset = $mask === 24 ? 0 : $suffix; $count = 2 ** (24 - $mask); $ips = []; for ($i = 1; $i <= $count; $i++) { $ips[] = $prefix . '.' . ($offset + $i); } return $ips; } return []; } private function resolveHostname(string $ip): ?string { $hostname = @gethostbyaddr($ip); return ($hostname !== false && $hostname !== $ip) ? $hostname : null; } }