/** * Brücke zum nativen Scan-Plugin `NetDiagScanner` (Kotlin). * * Der WebView kann keine Raw-Sockets/ICMP/ARP — die eigentliche Netzwerk- * Messung läuft im nativen Android-Plugin. Im Browser-Dev liefert ein Mock * Beispieldaten, damit die Oberfläche ohne Gerät entwickelt werden kann. */ import { Capacitor, registerPlugin } from '@capacitor/core'; /* --- Datentypen der Plugin-Antworten --- */ export interface ScannedDevice { ip: string; mac?: string; hostname?: string; vendor?: string; /** geschätzte Geräteart (Kamera, Drucker, Router …) */ deviceType?: string; /** NetBIOS-Name (UDP-137-Abfrage) */ netbiosName?: string; /** offene Ports aus der Quick-Port-Probe */ openPorts?: number[]; } /** Ein per mDNS/Bonjour gefundenes Gerät */ export interface MdnsDevice { ip: string; /** Bonjour-Anzeigename */ name: string; /** angebotene Diensttypen, z.B. ['_googlecast._tcp', '_printer._tcp'] */ services: string[]; } /** Ergebnis der IP-Konflikt-Prüfung */ export interface ConflictScanResult { /** IP-Adressen, die von mehr als einem Gerät benutzt werden */ conflicts: { ip: string; macs: string[] }[]; /** Anzahl der Adressen, für die überhaupt eine MAC gesehen wurde */ checked: number; rounds: number; /** false = ARP-Tabelle nicht lesbar (Android-Einschränkung, dann keine Aussage möglich) */ arpAvailable: boolean; } export interface OpenPort { port: number; service?: string; } export interface PingQuality { sent: number; received: number; lossPct: number; minMs: number; avgMs: number; maxMs: number; jitterMs: number; } export interface WifiNetwork { ssid: string; bssid: string; channel: number; rssi: number; band: string; } export interface DhcpLease { /** DHCP-Server, von dem das Gerät seine IP bezieht ('' wenn unbekannt) */ server: string; /** Lease-Dauer in Sekunden (0 wenn unbekannt) */ lease: number; /** Standard-Gateway */ gateway: string; /** zugewiesene DNS-Server */ dns: string[]; } export interface TracerouteHop { ttl: number; ip: string; ms: number; } export interface ThroughputResult { downMbps: number; upMbps: number; } /** Schnittstelle des nativen Plugins */ export interface NetDiagScannerPlugin { /** Aktuelles Subnetz des Geräts ermitteln (z.B. "192.168.1.0/24") */ getLocalSubnet(): Promise<{ subnet: string; ip: string; gateway: string }>; /** IP-Scan: Geräte im Subnetz finden (ARP + Ping-Sweep + Namensauflösung) */ ipScan(opts: { subnet: string }): Promise<{ devices: ScannedDevice[] }>; /** mDNS/Bonjour-Dienstsuche: Drucker, Kameras, Chromecast, AirPlay … */ mdnsScan(opts: { timeoutMs?: number }): Promise<{ devices: MdnsDevice[] }>; /** IP-Konflikt-Prüfung: findet IP-Adressen, die zwei Geräte gleichzeitig benutzen */ arpConflictScan(opts: { subnet: string; rounds?: number; delayMs?: number; }): Promise; /** Port-Scan eines Geräts */ portScan(opts: { ip: string; ports: number[] }): Promise<{ open: OpenPort[] }>; /** Ping-Qualität (Latenz, Jitter, Paketverlust) */ pingQuality(opts: { host: string; count: number }): Promise; /** WLAN-Scan: umliegende Netze, Kanäle, Signalstärke */ wifiScan(): Promise<{ networks: WifiNetwork[] }>; /** DHCP-Lease-Info des Geräts (Server, Lease-Dauer, Gateway, DNS) */ dhcpInfo(): Promise; /** SNMP v2c Abfrage (Switch: Link-Speed, Fehlerzähler) */ snmpGet(opts: { host: string; community: string; oids: string[] }): Promise<{ values: Record; }>; /** Traceroute zu einem Host */ traceroute(opts: { host: string }): Promise<{ hops: TracerouteHop[] }>; /** Durchsatztest gegen eine Gegenstelle (iperf-kompatibel) */ throughput(opts: { host: string; port: number; durationSec: number }): Promise; /** Dauer-/Stresstest starten (läuft als Foreground-Service) */ startStressTest(opts: { host: string; durationSec: number }): Promise<{ runId: string }>; /** Laufenden Stresstest beenden und Ergebnis holen */ stopStressTest(opts: { runId: string }): Promise<{ samples: number; lossPct: number; avgMs: number; maxMs: number; }>; } const native = registerPlugin('NetDiagScanner'); /* ----------------------------------------------------------------------- */ /* Mock für Browser-Entwicklung */ /* ----------------------------------------------------------------------- */ function rnd(min: number, max: number): number { return Math.round((min + Math.random() * (max - min)) * 10) / 10; } const mock: NetDiagScannerPlugin = { async getLocalSubnet() { return { subnet: '192.168.1.0/24', ip: '192.168.1.50', gateway: '192.168.1.1' }; }, async ipScan() { return { devices: [ { ip: '192.168.1.1', mac: 'AA:BB:CC:00:00:01', hostname: 'fritzbox', vendor: 'AVM', deviceType: 'Router', openPorts: [53, 80, 443], }, { ip: '192.168.1.10', mac: 'AA:BB:CC:00:00:0A', hostname: 'switch-keller', vendor: 'TP-Link', deviceType: 'Switch', openPorts: [80], }, { ip: '192.168.1.40', mac: 'AA:BB:CC:00:00:28', hostname: 'ipcam-hof', vendor: 'Hikvision', deviceType: 'Kamera', openPorts: [80, 554], }, { ip: '192.168.1.50', mac: 'AA:BB:CC:00:00:32', hostname: 'handy', vendor: 'Samsung', deviceType: '', openPorts: [], }, { ip: '192.168.1.77', mac: 'AA:BB:CC:00:00:4D', hostname: 'wallbox', vendor: '', netbiosName: 'WALLBOX', deviceType: 'Wallbox', openPorts: [80, 502], }, ], }; }, async mdnsScan() { return { devices: [ { ip: '192.168.1.20', name: 'Brother HL-L2350DW', services: ['_printer._tcp', '_ipp._tcp'] }, { ip: '192.168.1.30', name: 'Wohnzimmer-TV', services: ['_googlecast._tcp'] }, { ip: '192.168.1.40', name: 'IP-Kamera Hof', services: ['_rtsp._tcp'] }, ], }; }, async arpConflictScan() { return { conflicts: [{ ip: '192.168.1.40', macs: ['AA:BB:CC:00:00:28', 'AA:BB:CC:00:00:99'] }], checked: 12, rounds: 4, arpAvailable: true, }; }, async portScan(opts) { const all: OpenPort[] = [ { port: 80, service: 'http' }, { port: 443, service: 'https' }, { port: 22, service: 'ssh' }, ]; return { open: all.filter((p) => opts.ports.includes(p.port)) }; }, async pingQuality(opts) { const sent = opts.count; const received = sent - (Math.random() < 0.3 ? 1 : 0); return { sent, received, lossPct: Math.round(((sent - received) / sent) * 100), minMs: rnd(1, 4), avgMs: rnd(4, 12), maxMs: rnd(12, 40), jitterMs: rnd(0.5, 5), }; }, async wifiScan() { return { networks: [ { ssid: 'AllesWattLaeuft', bssid: 'AA:BB:CC:11:22:33', channel: 6, rssi: -52, band: '2.4 GHz' }, { ssid: 'Nachbar-WLAN', bssid: 'DD:EE:FF:44:55:66', channel: 11, rssi: -78, band: '2.4 GHz' }, { ssid: 'AllesWattLaeuft-5G', bssid: 'AA:BB:CC:11:22:34', channel: 36, rssi: -58, band: '5 GHz' }, ], }; }, async dhcpInfo() { return { server: '192.168.1.1', lease: 86400, gateway: '192.168.1.1', dns: ['192.168.1.1', '8.8.8.8'], }; }, async snmpGet(opts) { const values: Record = {}; for (const oid of opts.oids) values[oid] = String(Math.floor(Math.random() * 1000)); return { values }; }, async traceroute() { return { hops: [ { ttl: 1, ip: '192.168.1.1', ms: rnd(1, 3) }, { ttl: 2, ip: '10.0.0.1', ms: rnd(8, 15) }, { ttl: 3, ip: '8.8.8.8', ms: rnd(15, 30) }, ], }; }, async throughput() { return { downMbps: rnd(80, 940), upMbps: rnd(40, 500) }; }, async startStressTest() { return { runId: 'mock-run' }; }, async stopStressTest() { return { samples: 120, lossPct: rnd(0, 2), avgMs: rnd(3, 10), maxMs: rnd(20, 90) }; }, }; /** Aktive Scanner-Implementierung: nativ auf dem Gerät, Mock im Browser */ export const scanner: NetDiagScannerPlugin = Capacitor.isNativePlatform() ? native : mock;