223 lines
7.8 KiB
PHP
223 lines
7.8 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\NetworkDevice;
|
|
use App\Models\NetworkDeviceEvent;
|
|
use App\Models\NetworkHost;
|
|
use App\Models\NetworkScan;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class NetworkScanImporter
|
|
{
|
|
private NetworkScan $scan;
|
|
private int $newDevices = 0;
|
|
private int $changedDevices = 0;
|
|
private int $onlineHosts = 0;
|
|
|
|
/**
|
|
* Importiert eine Angry IP Scanner .txt Exportdatei.
|
|
*/
|
|
public function importAngryIpScannerFile(string $filePath, int $createdBy): NetworkScan
|
|
{
|
|
$lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
$scanner = '';
|
|
$subnet = '';
|
|
$headers = [];
|
|
$rows = [];
|
|
|
|
foreach ($lines as $line) {
|
|
if (str_starts_with($line, 'Erstellt von') || str_starts_with($line, 'Created by')) {
|
|
$scanner = trim(str_replace(['Erstellt von ', 'Created by '], '', $line));
|
|
continue;
|
|
}
|
|
if (str_starts_with($line, 'Gescannt') || str_starts_with($line, 'Scanned')) {
|
|
// "Gescannt 192.168.86.0 - 192.168.86.255"
|
|
preg_match('/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/', $line, $m);
|
|
$subnet = $m[1] ?? '0.0.0.0';
|
|
continue;
|
|
}
|
|
if (str_starts_with($line, 'http') || str_starts_with($line, 'https')) {
|
|
continue; // URL-Zeile überspringen
|
|
}
|
|
// Header-Zeile (beginnt mit "IP")
|
|
if (str_starts_with($line, 'IP') && empty($headers)) {
|
|
$headers = preg_split('/\t+/', $line);
|
|
$headers = array_map('trim', $headers);
|
|
continue;
|
|
}
|
|
// Datenzeile
|
|
if (!empty($headers) && str_starts_with(trim($line), '') && preg_match('/^\d{1,3}\./', trim($line))) {
|
|
$cols = preg_split('/\t+/', $line);
|
|
$row = [];
|
|
foreach ($headers as $i => $h) {
|
|
$row[$h] = trim($cols[$i] ?? '');
|
|
}
|
|
$rows[] = $row;
|
|
}
|
|
}
|
|
|
|
return DB::transaction(function () use ($rows, $scanner, $subnet, $createdBy) {
|
|
$this->scan = NetworkScan::create([
|
|
'subnet' => $subnet,
|
|
'source' => 'import',
|
|
'scanner' => $scanner,
|
|
'created_by' => $createdBy,
|
|
]);
|
|
|
|
foreach ($rows as $row) {
|
|
$this->processRow($row);
|
|
}
|
|
|
|
$this->scan->update([
|
|
'total_hosts' => count($rows),
|
|
'online_hosts' => $this->onlineHosts,
|
|
'new_devices' => $this->newDevices,
|
|
'changed_devices'=> $this->changedDevices,
|
|
]);
|
|
|
|
return $this->scan;
|
|
});
|
|
}
|
|
|
|
private function processRow(array $row): void
|
|
{
|
|
$ip = $this->extractColumn($row, ['IP']);
|
|
$ping = $this->extractColumn($row, ['Ping']);
|
|
$host = $this->extractColumn($row, ['Hostname']);
|
|
$mac = $this->normalizeMAC($this->extractColumn($row, ['MAC Addresse', 'MAC Address']));
|
|
$vendor = $this->extractColumn($row, ['MAC Hersteller', 'MAC Vendor']);
|
|
$ttl = (int) $this->extractColumn($row, ['TTL']) ?: null;
|
|
$ports = $this->extractColumn($row, ['Ports']);
|
|
$netbios= $this->extractColumn($row, ['NetBIOS Info']);
|
|
$http = $this->extractColumn($row, ['HTTP Sender']);
|
|
$web = $this->extractColumn($row, ['Web Erkennung', 'Web Detection']);
|
|
|
|
$pingMs = null;
|
|
$status = 'offline';
|
|
|
|
if (!empty($ping) && $ping !== 'n/a' && $ping !== '-') {
|
|
preg_match('/(\d+)/', $ping, $m);
|
|
$pingMs = isset($m[1]) ? (int)$m[1] : null;
|
|
$status = 'online';
|
|
$this->onlineHosts++;
|
|
}
|
|
|
|
// Host-Eintrag speichern
|
|
$host_record = NetworkHost::create([
|
|
'scan_id' => $this->scan->id,
|
|
'ip_address' => $ip,
|
|
'mac_address' => $mac ?: null,
|
|
'hostname' => $host ?: null,
|
|
'mac_vendor' => $vendor ?: null,
|
|
'status' => $status,
|
|
'ping_ms' => $pingMs,
|
|
'netbios_info' => $netbios ?: null,
|
|
'ttl' => $ttl,
|
|
'ports' => $ports ?: null,
|
|
'http_sender' => $http ?: null,
|
|
'web_detection' => $web ?: null,
|
|
]);
|
|
|
|
// Geräte-Master nur wenn MAC bekannt
|
|
if (!empty($mac)) {
|
|
$this->processDevice($host_record, $ip, $mac, $host, $vendor, $netbios, $ttl, $ports, $status);
|
|
}
|
|
}
|
|
|
|
private function processDevice(
|
|
NetworkHost $hostRecord,
|
|
string $ip, string $mac, ?string $hostname,
|
|
?string $vendor, ?string $netbios, ?int $ttl,
|
|
?string $ports, string $status
|
|
): void {
|
|
$device = NetworkDevice::firstOrNew(['mac_address' => $mac]);
|
|
$isNew = !$device->exists;
|
|
|
|
if ($isNew) {
|
|
$device->fill([
|
|
'current_ip' => $ip,
|
|
'hostname' => $hostname,
|
|
'mac_vendor' => $vendor,
|
|
'status' => $status,
|
|
'netbios_name' => $netbios,
|
|
'ttl' => $ttl,
|
|
'ports' => $ports,
|
|
'first_seen_at'=> now(),
|
|
'last_seen_at' => now(),
|
|
])->save();
|
|
|
|
NetworkDeviceEvent::create([
|
|
'device_id' => $device->id,
|
|
'scan_id' => $this->scan->id,
|
|
'event_type' => 'new_device',
|
|
'new_value' => $ip,
|
|
'description'=> "Erstes Erscheinen: {$ip} ({$vendor})",
|
|
]);
|
|
$this->newDevices++;
|
|
} else {
|
|
$events = [];
|
|
|
|
// IP-Änderung erkennen
|
|
if ($device->current_ip !== $ip) {
|
|
$events[] = [
|
|
'event_type' => 'ip_changed',
|
|
'old_value' => $device->current_ip,
|
|
'new_value' => $ip,
|
|
'description'=> "IP geändert von {$device->current_ip} zu {$ip}",
|
|
];
|
|
$this->changedDevices++;
|
|
}
|
|
|
|
// Online/Offline-Status
|
|
if ($device->status !== $status) {
|
|
$events[] = [
|
|
'event_type' => $status === 'online' ? 'came_online' : 'went_offline',
|
|
'old_value' => $device->status,
|
|
'new_value' => $status,
|
|
'description'=> $status === 'online'
|
|
? "Gerät wieder online ({$ip})"
|
|
: "Gerät offline ({$device->current_ip})",
|
|
];
|
|
}
|
|
|
|
$device->update([
|
|
'current_ip' => $ip,
|
|
'hostname' => $hostname ?? $device->hostname,
|
|
'status' => $status,
|
|
'last_seen_at' => now(),
|
|
]);
|
|
|
|
foreach ($events as $event) {
|
|
NetworkDeviceEvent::create(array_merge($event, [
|
|
'device_id' => $device->id,
|
|
'scan_id' => $this->scan->id,
|
|
]));
|
|
}
|
|
}
|
|
|
|
$hostRecord->update(['device_id' => $device->id]);
|
|
}
|
|
|
|
private function extractColumn(array $row, array $keys): string
|
|
{
|
|
foreach ($keys as $key) {
|
|
if (isset($row[$key]) && $row[$key] !== '') {
|
|
return $row[$key];
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
|
|
private function normalizeMAC(string $mac): string
|
|
{
|
|
// Verschiedene MAC-Formate normalisieren zu XX:XX:XX:XX:XX:XX
|
|
$clean = preg_replace('/[^a-fA-F0-9]/', '', $mac);
|
|
if (strlen($clean) !== 12) {
|
|
return '';
|
|
}
|
|
return strtoupper(implode(':', str_split($clean, 2)));
|
|
}
|
|
}
|