mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:20:43 +00:00
fix(bonjour): truncate mDNS service name and hostname to 63-byte DNS label limit
When the system hostname exceeds 63 bytes (common with Kubernetes pod names), the @homebridge/ciao DNS label encoder throws an AssertionError that crashes the gateway on startup. Add truncateToDnsLabel() that safely truncates UTF-8 strings at byte boundaries, applied to both the service instance name and hostname before passing them to ciao. Closes #37705 AI-assisted (built with Hermes orchestration).
This commit is contained in:
committed by
Peter Steinberger
parent
f5b01c1e0e
commit
9ac0b7edbc
@@ -735,6 +735,54 @@ describe("gateway bonjour advertiser", () => {
|
||||
await started.stop();
|
||||
});
|
||||
|
||||
it("truncates service name exceeding 63-byte DNS label limit", async () => {
|
||||
const longHostname = "app-41627eae5842473f9e05f139ea307277-7f9477f4d6-lqqzf";
|
||||
enableAdvertiserUnitMode(longHostname);
|
||||
|
||||
const destroy = vi.fn().mockResolvedValue(undefined);
|
||||
const advertise = vi.fn().mockResolvedValue(undefined);
|
||||
mockCiaoService({ advertise, destroy });
|
||||
|
||||
const started = await startAdvertiser({
|
||||
gatewayPort: 18789,
|
||||
sshPort: 2222,
|
||||
});
|
||||
|
||||
const [gatewayCall] = createService.mock.calls as Array<[ServiceCall]>;
|
||||
const serviceName = gatewayCall?.[0]?.name as string;
|
||||
const hostname = gatewayCall?.[0]?.hostname as string;
|
||||
|
||||
// Both name and hostname must be within the 63-byte DNS label limit
|
||||
expect(new TextEncoder().encode(serviceName).byteLength).toBeLessThanOrEqual(63);
|
||||
expect(new TextEncoder().encode(hostname).byteLength).toBeLessThanOrEqual(63);
|
||||
|
||||
await started.stop();
|
||||
});
|
||||
|
||||
it("truncates multi-byte hostname within DNS label byte limit", async () => {
|
||||
// 21 CJK characters = 63 bytes in UTF-8, adding " (OpenClaw)" pushes over
|
||||
const cjkHostname = "你".repeat(21);
|
||||
enableAdvertiserUnitMode(cjkHostname);
|
||||
|
||||
const destroy = vi.fn().mockResolvedValue(undefined);
|
||||
const advertise = vi.fn().mockResolvedValue(undefined);
|
||||
mockCiaoService({ advertise, destroy });
|
||||
|
||||
const started = await startAdvertiser({
|
||||
gatewayPort: 18789,
|
||||
sshPort: 2222,
|
||||
});
|
||||
|
||||
const [gatewayCall] = createService.mock.calls as Array<[ServiceCall]>;
|
||||
const serviceName = gatewayCall?.[0]?.name as string;
|
||||
|
||||
expect(new TextEncoder().encode(serviceName).byteLength).toBeLessThanOrEqual(63);
|
||||
// Should not end with a replacement character from incomplete multi-byte truncation
|
||||
expect(serviceName).not.toMatch(/\uFFFD$/);
|
||||
|
||||
await started.stop();
|
||||
});
|
||||
|
||||
it("uses system hostname when OPENCLAW_MDNS_HOSTNAME is unset", async () => {
|
||||
// Allow advertiser to run in unit tests.
|
||||
delete process.env.VITEST;
|
||||
|
||||
@@ -183,9 +183,24 @@ function resolveSystemMdnsHostname(): string | null {
|
||||
return firstLabel;
|
||||
}
|
||||
|
||||
const MAX_DNS_LABEL_BYTES = 63;
|
||||
|
||||
function truncateToDnsLabel(name: string): string {
|
||||
const encoder = new TextEncoder();
|
||||
const encoded = encoder.encode(name);
|
||||
if (encoded.byteLength <= MAX_DNS_LABEL_BYTES) {
|
||||
return name;
|
||||
}
|
||||
// Truncate at byte boundary, then decode back (TextDecoder handles incomplete sequences)
|
||||
const truncated = encoded.slice(0, MAX_DNS_LABEL_BYTES);
|
||||
const decoded = new TextDecoder("utf-8", { fatal: false }).decode(truncated);
|
||||
// Strip any replacement character from incomplete multi-byte sequence at the end
|
||||
return decoded.replace(/\uFFFD$/, "").trim() || "OpenClaw";
|
||||
}
|
||||
|
||||
function safeServiceName(name: string) {
|
||||
const trimmed = name.trim();
|
||||
return trimmed.length > 0 ? trimmed : "OpenClaw";
|
||||
return trimmed.length > 0 ? truncateToDnsLabel(trimmed) : "OpenClaw";
|
||||
}
|
||||
|
||||
function prettifyInstanceName(name: string) {
|
||||
@@ -353,11 +368,12 @@ export async function startGatewayBonjourAdvertiser(
|
||||
|
||||
const hostnameRaw =
|
||||
process.env.OPENCLAW_MDNS_HOSTNAME?.trim() || resolveSystemMdnsHostname() || "openclaw";
|
||||
const hostname =
|
||||
const hostname = truncateToDnsLabel(
|
||||
hostnameRaw
|
||||
.replace(/\.local$/i, "")
|
||||
.split(".")[0]
|
||||
.trim() || "openclaw";
|
||||
.trim() || "openclaw",
|
||||
);
|
||||
const instanceName =
|
||||
typeof opts.instanceName === "string" && opts.instanceName.trim()
|
||||
? opts.instanceName.trim()
|
||||
|
||||
Reference in New Issue
Block a user