diff --git a/android/app/src/main/java/de/data_it_solution/netdiag/NetDiagScannerPlugin.kt b/android/app/src/main/java/de/data_it_solution/netdiag/NetDiagScannerPlugin.kt index 272a6ab..8a75408 100644 --- a/android/app/src/main/java/de/data_it_solution/netdiag/NetDiagScannerPlugin.kt +++ b/android/app/src/main/java/de/data_it_solution/netdiag/NetDiagScannerPlugin.kt @@ -82,14 +82,18 @@ class NetDiagScannerPlugin : Plugin() { @PluginMethod fun ipScan(call: PluginCall) { val subnet = call.getString("subnet") ?: return call.reject("subnet fehlt") - val base = subnet.substringBeforeLast('.', "192.168.1") + val hosts = hostsInSubnet(subnet) + if (hosts.isEmpty()) { + return call.reject("Subnetz ungültig oder zu groß (max /16): $subnet") + } io.launch { try { - // Parallel-Ping über das gesamte /24 + // Parallel-Ping über ALLE Host-Adressen des Subnetzes — CIDR-genau, + // also exakt der Bereich, den die Netzmaske aufspannt (/24, /23, /22 …). val alive = withContext(Dispatchers.IO) { - (1..254).map { host -> + hosts.map { ipInt -> async { - val ip = "$base.$host" + val ip = intToIpv4(ipInt) if (InetAddress.getByName(ip).isReachable(350)) ip else null } }.awaitAll().filterNotNull() @@ -112,6 +116,51 @@ class NetDiagScannerPlugin : Plugin() { } } + /** + * Alle Host-IPs (als Int) eines CIDR-Subnetzes. + * "192.168.1.0/24" -> .1 bis .254, "10.0.0.0/22" -> 1022 Hosts usw. + * Ohne Praefix wird /24 angenommen. Netz- und Broadcast-Adresse sind + * ausgenommen (ausser /31, /32). Leer bei ungueltig oder > /16. + */ + private fun hostsInSubnet(cidr: String): List { + val parts = cidr.trim().split('/') + val ipInt = ipv4ToInt(parts[0].trim()) ?: return emptyList() + val prefix = if (parts.size > 1) (parts[1].trim().toIntOrNull() ?: 24) else 24 + if (prefix < 0 || prefix > 32) return emptyList() + val mask = if (prefix == 0) 0 else (-1 shl (32 - prefix)) + val network = ipInt and mask + val broadcast = network or mask.inv() + val out = ArrayList() + if (prefix >= 31) { + var i = network + while (true) { out.add(i); if (i == broadcast) break; i++ } + return out + } + val count = (broadcast.toLong() and 0xFFFFFFFFL) - (network.toLong() and 0xFFFFFFFFL) - 1L + if (count < 1L || count > 65534L) return emptyList() + var i = network + 1 + val last = broadcast - 1 + while (true) { out.add(i); if (i == last) break; i++ } + return out + } + + /** "192.168.1.50" -> 32-Bit-Int (big-endian), null bei ungueltig */ + private fun ipv4ToInt(s: String): Int? { + val o = s.split('.') + if (o.size != 4) return null + var v = 0 + for (part in o) { + val n = part.toIntOrNull() ?: return null + if (n < 0 || n > 255) return null + v = (v shl 8) or n + } + return v + } + + /** 32-Bit-Int (big-endian) -> "192.168.1.50" */ + private fun intToIpv4(i: Int): String = + "${(i shr 24) and 0xFF}.${(i shr 16) and 0xFF}.${(i shr 8) and 0xFF}.${i and 0xFF}" + /* --------------------------------------------------------------------- */ /* Port-Scan */ /* --------------------------------------------------------------------- */