Skip to content

resolve hostname when bonjour-service returns empty addresses #40

@dxdc

Description

@dxdc

Analysis

When multiple Homebridge child bridges share the same hostname (e.g., pi4b.local), bonjour-service fails to populate device.addresses for some of them. This is due to mDNS response packet splitting - the A/AAAA records for the shared hostname are included in earlier response packets but suppressed in later ones per mDNS duplicate record guidelines. bonjour-service only looks for A/AAAA records within each individual packet (see buildServicesFor in browser.js), so services arriving in later packets get empty address arrays.

This causes startDiscovery() to skip the IP validation loop entirely for those instances, going straight from "Found" to "Could not register" with no "Testing" attempt.

Root cause

In bonjour-service/dist/lib/browser.js, buildServicesFor() (line 169-171):

records
    .filter((rr) => (rr.type === 'A' || rr.type === 'AAAA') && dnsEqual(rr.name, service.host))
    .forEach((rr) => service.addresses.push(rr.data));

records is scoped to a single mDNS response packet (packet.answers.concat(packet.additionals)). When multiple services share a hostname, the mDNS responder suppresses recently-sent A records in subsequent packets. Services in those later packets get addresses: [].

Expected Behavior

All Homebridge child bridges running on the same host are discovered and registered, regardless of how many share a hostname.

Steps To Reproduce

Set up Homebridge with 10+ child bridges on a single host
Use hap-client to discover instances
Observe discovery logs - some bridges show "Found" → "Testing" → "Success" → "Instance Registered", while others show "Found" → "Could not register" with no "Testing" line
The missing bridges have device.addresses: [] despite device.host containing a valid .local hostname

Logs

.

Configuration

13 child bridges, all on the same host
bonjour-service v1.3.0
avahi-daemon as mDNS responder
mDNS advertiser set to avahi in Homebridge config

Environment

  • OS: Raspian Trixie
  • Software: Homebridge 1.11.4
  • Node: 24
  • npm: 11

Process Supervisor

hb-service

Additional Context

The better fix is probably upstream in the bonjour-service, but this does seem to work.

Add a dns.lookup fallback in startDiscovery() when device.addresses is empty. dns.lookup uses the OS resolver (getaddrinfo), which respects /etc/nsswitch.conf and resolves .local hostnames via the system's mDNS stack (libnss-mdns/avahi).

if (device.addresses.length === 0 && device.host) {
    try {
        const dns = require('node:dns');
        const { promisify } = require('node:util');
        const lookup = promisify(dns.lookup);
        const result = await lookup(device.host, { family: 4 });
        device.addresses.push(result.address);
        this.debug(`[HapClient] Discovery :: Resolved ${device.host} to ${result.address} for ${instance.username}`);
    } catch (e) {
        this.debug(`[HapClient] Discovery :: Failed to resolve ${device.host} for ${instance.username}: ${e.message}`);
    }
}

This is inserted immediately before the for (const ip of device.addresses) loop.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions