generated from Dicken/dickendock
Commit 5: Scanner-Engine (DNS, Portscan, Titel/Favicon, Softwareerkennung, FritzBox TR-064)
This commit is contained in:
66
apps/backend/src/scanner/fritzbox.ts
Normal file
66
apps/backend/src/scanner/fritzbox.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { soapRequest, type DigestAuthOptions } from "./digestAuth.js";
|
||||
|
||||
const SERVICE_TYPE = "urn:dslforum-org:service:Hosts:1";
|
||||
|
||||
function actionEnvelope(action: string, params: Record<string, string | number> = {}): string {
|
||||
const args = Object.entries(params)
|
||||
.map(([key, value]) => `<${key}>${value}</${key}>`)
|
||||
.join("");
|
||||
|
||||
return (
|
||||
`<?xml version="1.0" encoding="utf-8"?>` +
|
||||
`<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" ` +
|
||||
`s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">` +
|
||||
`<s:Body><u:${action} xmlns:u="${SERVICE_TYPE}">${args}</u:${action}></s:Body>` +
|
||||
`</s:Envelope>`
|
||||
);
|
||||
}
|
||||
|
||||
function extractTag(xml: string, tag: string): string | null {
|
||||
const match = xml.match(new RegExp(`<${tag}>([^<]*)</${tag}>`, "i"));
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
export interface FritzBoxHost {
|
||||
ip: string;
|
||||
mac: string | null;
|
||||
hostname: string;
|
||||
online: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest die vollständige Geräteliste der FritzBox über die TR-064-Aktionen
|
||||
* GetHostNumberOfEntries + GetGenericHostEntry (urn:dslforum-org:service:Hosts:1).
|
||||
*/
|
||||
export async function fetchFritzBoxHosts(options: DigestAuthOptions): Promise<FritzBoxHost[]> {
|
||||
const countXml = await soapRequest(
|
||||
options,
|
||||
`${SERVICE_TYPE}#GetHostNumberOfEntries`,
|
||||
actionEnvelope("GetHostNumberOfEntries")
|
||||
);
|
||||
const countStr = extractTag(countXml, "NewHostNumberOfEntries");
|
||||
const count = countStr ? parseInt(countStr, 10) : 0;
|
||||
|
||||
const hosts: FritzBoxHost[] = [];
|
||||
|
||||
for (let index = 0; index < count; index++) {
|
||||
const entryXml = await soapRequest(
|
||||
options,
|
||||
`${SERVICE_TYPE}#GetGenericHostEntry`,
|
||||
actionEnvelope("GetGenericHostEntry", { NewIndex: index })
|
||||
);
|
||||
|
||||
const ip = extractTag(entryXml, "NewIPAddress");
|
||||
const hostname = extractTag(entryXml, "NewHostName");
|
||||
if (!ip || !hostname) continue; // inaktive/unvollständige Einträge überspringen
|
||||
|
||||
hosts.push({
|
||||
ip,
|
||||
mac: extractTag(entryXml, "NewMACAddress"),
|
||||
hostname,
|
||||
online: extractTag(entryXml, "NewActive") === "1",
|
||||
});
|
||||
}
|
||||
|
||||
return hosts;
|
||||
}
|
||||
Reference in New Issue
Block a user