Compare commits
No commits in common. "46226f0d780100e44c3b675682a3004b5d84ba7d" and "27581cd080c73f0175514e61eb9bd55c9d6b0a3f" have entirely different histories.
46226f0d78
...
27581cd080
|
|
@ -82,18 +82,14 @@ class NetDiagScannerPlugin : Plugin() {
|
||||||
@PluginMethod
|
@PluginMethod
|
||||||
fun ipScan(call: PluginCall) {
|
fun ipScan(call: PluginCall) {
|
||||||
val subnet = call.getString("subnet") ?: return call.reject("subnet fehlt")
|
val subnet = call.getString("subnet") ?: return call.reject("subnet fehlt")
|
||||||
val hosts = hostsInSubnet(subnet)
|
val base = subnet.substringBeforeLast('.', "192.168.1")
|
||||||
if (hosts.isEmpty()) {
|
|
||||||
return call.reject("Subnetz ungültig oder zu groß (max /16): $subnet")
|
|
||||||
}
|
|
||||||
io.launch {
|
io.launch {
|
||||||
try {
|
try {
|
||||||
// Parallel-Ping über ALLE Host-Adressen des Subnetzes — CIDR-genau,
|
// Parallel-Ping über das gesamte /24
|
||||||
// also exakt der Bereich, den die Netzmaske aufspannt (/24, /23, /22 …).
|
|
||||||
val alive = withContext(Dispatchers.IO) {
|
val alive = withContext(Dispatchers.IO) {
|
||||||
hosts.map { ipInt ->
|
(1..254).map { host ->
|
||||||
async {
|
async {
|
||||||
val ip = intToIpv4(ipInt)
|
val ip = "$base.$host"
|
||||||
if (InetAddress.getByName(ip).isReachable(350)) ip else null
|
if (InetAddress.getByName(ip).isReachable(350)) ip else null
|
||||||
}
|
}
|
||||||
}.awaitAll().filterNotNull()
|
}.awaitAll().filterNotNull()
|
||||||
|
|
@ -116,51 +112,6 @@ 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<Int> {
|
|
||||||
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<Int>()
|
|
||||||
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 */
|
/* Port-Scan */
|
||||||
/* --------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------- */
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 16 KiB |
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="ic_launcher_background">#0d1117</color>
|
<color name="ic_launcher_background">#FFFFFF</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -15,10 +15,11 @@ body {
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sichere Bereiche (Notch / Statusleiste): enthalten bewusst den
|
/* Sichere Bereiche (Notch / Statusleiste).
|
||||||
Basis-Innenabstand 0.75rem, sonst ueberschreibt diese Klasse das
|
Enthalten bewusst den Basis-Innenabstand (0.75rem) — sonst überschreibt
|
||||||
Tailwind-Padding und der Inhalt klebt an der Statusleiste. Elemente
|
diese Klasse das padding-top/-bottom von Tailwind py-* (unlayered CSS
|
||||||
mit pb- und px- kombinieren, nicht mit py-. Siehe KB 551. */
|
schlägt @layer utilities) und der Inhalt klebt an Statusleiste/Notch.
|
||||||
|
Elemente daher mit pb-*/px-* statt py-* kombinieren. Siehe KB #551. */
|
||||||
.safe-top {
|
.safe-top {
|
||||||
padding-top: calc(0.75rem + env(safe-area-inset-top));
|
padding-top: calc(0.75rem + env(safe-area-inset-top));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { scanner } from '../../scanner';
|
import { scanner } from '../../scanner';
|
||||||
import { debugLog } from '../../debuglog.svelte';
|
|
||||||
import type { Tool } from '../types';
|
import type { Tool } from '../types';
|
||||||
|
|
||||||
export const ipScanTool: Tool = {
|
export const ipScanTool: Tool = {
|
||||||
|
|
@ -59,14 +58,7 @@ export const ipScanTool: Tool = {
|
||||||
ctx.protocol.subnet = subnet;
|
ctx.protocol.subnet = subnet;
|
||||||
}
|
}
|
||||||
|
|
||||||
debugLog.add(
|
|
||||||
'info',
|
|
||||||
`IP-Scan: Dialog-Eingabe="${String(ctx.params.subnet ?? '')}", ` +
|
|
||||||
`Protokoll-Subnetz="${String(ctx.protocol.subnet ?? '')}" → ` +
|
|
||||||
`gescannt wird "${subnet}" (Quelle: ${source})`,
|
|
||||||
);
|
|
||||||
const { devices } = await scanner.ipScan({ subnet });
|
const { devices } = await scanner.ipScan({ subnet });
|
||||||
debugLog.add('info', `IP-Scan Ergebnis: ${devices.length} Geräte in ${subnet}`);
|
|
||||||
const via = source === 'adapter' ? ' (Adapter erkannt)' : '';
|
const via = source === 'adapter' ? ' (Adapter erkannt)' : '';
|
||||||
return {
|
return {
|
||||||
label: `${devices.length} Geräte im Netz ${subnet}${via}`,
|
label: `${devices.length} Geräte im Netz ${subnet}${via}`,
|
||||||
|
|
|
||||||