fix(bonjour): normalize hostnames for beacons

This commit is contained in:
Peter Steinberger
2025-12-13 11:14:05 +00:00
parent cc3d0d1ef7
commit 7e7e348a14
2 changed files with 40 additions and 1 deletions

View File

@@ -18,6 +18,11 @@ vi.mock("@homebridge/ciao", () => {
const { startGatewayBonjourAdvertiser } = await import("./bonjour.js");
describe("gateway bonjour advertiser", () => {
type ServiceCall = {
name?: unknown;
txt?: unknown;
};
const prevEnv = { ...process.env };
afterEach(() => {
@@ -84,4 +89,30 @@ describe("gateway bonjour advertiser", () => {
expect(destroy).toHaveBeenCalledTimes(2);
expect(shutdown).toHaveBeenCalledTimes(1);
});
it("normalizes hostnames with domains for service names", async () => {
// Allow advertiser to run in unit tests.
delete process.env.VITEST;
process.env.NODE_ENV = "development";
vi.spyOn(os, "hostname").mockReturnValue("Mac.localdomain");
const destroy = vi.fn().mockResolvedValue(undefined);
const advertise = vi.fn().mockResolvedValue(undefined);
createService.mockReturnValue({ advertise, destroy });
const started = await startGatewayBonjourAdvertiser({
gatewayPort: 18789,
sshPort: 2222,
bridgePort: 18790,
});
const [masterCall] = createService.mock.calls as Array<[ServiceCall]>;
expect(masterCall?.[0]?.name).toBe("Mac (Clawdis)");
expect((masterCall?.[0]?.txt as Record<string, string>)?.lanHost).toBe(
"Mac.local",
);
await started.stop();
});
});

View File

@@ -39,7 +39,15 @@ export async function startGatewayBonjourAdvertiser(
const { getResponder, Protocol } = await import("@homebridge/ciao");
const responder = getResponder();
const hostname = os.hostname().replace(/\.local$/i, "");
// mDNS service instance names are single DNS labels; dots in hostnames (like
// `Mac.localdomain`) can confuse some resolvers/browsers and break discovery.
// Keep only the first label and normalize away a trailing `.local`.
const hostname =
os
.hostname()
.replace(/\.local$/i, "")
.split(".")[0]
.trim() || "clawdis";
const instanceName =
typeof opts.instanceName === "string" && opts.instanceName.trim()
? opts.instanceName.trim()