generated from Dicken/dickendock
round55: DNS-Abfragen mit Timeout absichern, Thread-Pool-Groesse erhoehen (behebt Haenger nach masscan/httpx-Vorabscan), Live-Haekchen pro fertigem Geraet im Batch
This commit is contained in:
@@ -5,19 +5,57 @@ export interface DnsResolution {
|
||||
ip: string;
|
||||
}
|
||||
|
||||
// GEFUNDENER FLASCHENHALS (round55): dns.lookup()/dns.reverse() aus
|
||||
// node:dns/promises laufen NICHT direkt asynchron über den Netzwerk-Stack,
|
||||
// sondern über getaddrinfo() im Betriebssystem - Node schickt das über den
|
||||
// libuv-Threadpool ab, der standardmäßig nur 4 Threads hat
|
||||
// (UV_THREADPOOL_SIZE=4), UNABHÄNGIG von Node's eigentlich nicht-blockierendem
|
||||
// I/O-Modell. Ohne eigenen Timeout kann eine einzelne langsame/hängende
|
||||
// Abfrage (z. B. für Handys/Smart-Geräte mit FritzBox-Spitznamen wie
|
||||
// "S24-Ultra-von-Sebastian", die weder per normaler DNS noch per .home/.local
|
||||
// auflösbar sind, aber je nach Resolver/Musl-libc im Alpine-Image erst nach
|
||||
// langer Zeit "nicht gefunden" zurückmelden) einen der 4 Threads über
|
||||
// Sekunden bis Minuten blockieren. Bei bis zu 10 Geräten gleichzeitig (siehe
|
||||
// routes/scan.ts CONCURRENT_DEVICES) mit je bis zu 4 DNS-Abfragen (3x
|
||||
// resolveHostname-Varianten + 1x reverseLookup) stauen sich die restlichen
|
||||
// Abfragen dann VOR dem 4er-Nadelöhr - das erklärt einen Sammel-Scan, der
|
||||
// nach einem erfolgreichen masscan-/httpx-Vorabscan plötzlich minutenlang in
|
||||
// der eigentlichen Geräte-Schleife hängt, obwohl dort eigentlich nur noch
|
||||
// leichte HTTP-Detailabfragen laufen sollten (siehe Bugreport).
|
||||
const DNS_TIMEOUT_MS = 1500;
|
||||
|
||||
function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => reject(new Error("DNS-Zeitüberschreitung")), ms);
|
||||
promise.then(
|
||||
(value) => {
|
||||
clearTimeout(timer);
|
||||
resolve(value);
|
||||
},
|
||||
(err) => {
|
||||
clearTimeout(timer);
|
||||
reject(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Versucht der Spezifikation folgend: hostname, hostname.home, hostname.local.
|
||||
* Gibt die erste erfolgreich aufgelöste Variante zurück, sonst null.
|
||||
* Gibt die erste erfolgreich aufgelöste Variante zurück, sonst null. Jeder
|
||||
* einzelne Versuch ist auf DNS_TIMEOUT_MS begrenzt (siehe Kommentar oben) -
|
||||
* eine hängende Abfrage blockiert damit höchstens 3 × 1,5s statt potenziell
|
||||
* unbegrenzt.
|
||||
*/
|
||||
export async function resolveHostname(shortName: string): Promise<DnsResolution | null> {
|
||||
const candidates = [shortName, `${shortName}.home`, `${shortName}.local`];
|
||||
|
||||
for (const hostname of candidates) {
|
||||
try {
|
||||
const { address } = await lookup(hostname);
|
||||
const { address } = await withTimeout(lookup(hostname), DNS_TIMEOUT_MS);
|
||||
return { hostname, ip: address };
|
||||
} catch {
|
||||
// nächste Variante versuchen
|
||||
// nächste Variante versuchen (Fehler ODER Timeout)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +67,12 @@ export async function resolveHostname(shortName: string): Promise<DnsResolution
|
||||
* Viele Router/DHCP-Server (FritzBox, Pi-hole, AdGuard Home, ...) tragen hier
|
||||
* automatisch den vom Gerät gemeldeten DHCP-Hostnamen ein, auch wenn die
|
||||
* normale Vorwärtsauflösung (Name -> IP) nichts findet. Nützlich als
|
||||
* zusätzliche Quelle für Gerätenamen, siehe scanner/networkScanner.ts.
|
||||
* zusätzliche Quelle für Gerätenamen, siehe scanner/networkScanner.ts. Mit
|
||||
* demselben Timeout abgesichert wie resolveHostname() oben.
|
||||
*/
|
||||
export async function reverseLookup(ip: string): Promise<string | null> {
|
||||
try {
|
||||
const names = await reverse(ip);
|
||||
const names = await withTimeout(reverse(ip), DNS_TIMEOUT_MS);
|
||||
return names[0] ?? null;
|
||||
} catch {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user