mDNS Peer#

The Mdns::Peer class is an abstract base class for participating in multicast DNS (mDNS / RFC 6762). It handles multicast group membership, message framing, service announcement, probing, browsing, goodbye, and unicast resolution. Two pure virtual methods must be implemented: onQuery (incoming queries) and onAnnouncement (incoming announcements from other peers).


Subclassing#

class MyMdnsPeer : public Mdns::Peer
{
public:
    explicit MyMdnsPeer (const std::string& interface, Reactor* reactor = nullptr)
    : Mdns::Peer (interface, reactor)
    {
    }

    // Called when another host sends an mDNS query
    void onQuery (const DnsPacket& query) override
    {
        for (const auto& q : query.questions)
        {
            if (q.host == "myhost.local" && q.type == DnsMessage::RecordType::A)
            {
                ResourceRecord rr;
                rr.host     = "myhost.local";
                rr.type     = DnsMessage::RecordType::A;
                rr.dnsclass = DnsMessage::RecordClass::IN;
                rr.ttl      = 4500;
                rr.addr     = "192.168.1.42";

                reply(query, {rr});
            }
        }
    }

    // Called when another host sends an mDNS announcement
    void onAnnouncement (const DnsPacket& packet) override
    {
        for (const auto& answer : packet.answers)
        {
            std::cout << "Announcement: " << answer.host
                      << " ttl=" << answer.ttl << "\n";
        }
    }
};

Construction#

// By interface name
MyMdnsPeer peer("eth0");

// By interface index
MyMdnsPeer peer(2);

// With custom reactor
MyMdnsPeer peer("eth0", &myReactor);

Lifecycle#

MyMdnsPeer peer("eth0");

// Join multicast group and register with reactor
if (peer.bind(AF_INET) == -1)
{
    std::cerr << lastError.message() << "\n";
}

// ... reactor runs ...

peer.close(); // leave multicast group, unregister

bind(family) opens the socket, joins the mDNS multicast group (224.0.0.251 for IPv4, ff02::fb for IPv6), enables port reuse, and registers with the reactor.


Service operations#

Probe#

Sends a DNS-SD probe to check whether a name is already in use before announcing:

ResourceRecord rr;
rr.host     = "myhost.local";
rr.type     = DnsMessage::RecordType::A;
rr.dnsclass = DnsMessage::RecordClass::IN;
rr.ttl      = 4500;
rr.addr     = "192.168.1.42";

peer.probe({rr});

Sends a multicast query with the records in the authority section and QU bit set. Responses arrive via onQuery.

Announce#

Sends an unsolicited multicast response advertising one or more records:

peer.announce({rr});

Sets QR and AA bits. ttl > 0 means the record is live.

Goodbye#

Sends an announcement with ttl = 0 to signal that records are being withdrawn:

peer.goodbye({rr});

The TTL is forced to 0 regardless of the value in the passed record.

Browse#

Sends a PTR query for a service type:

peer.browse("_http._tcp.local");

Responses arrive via onAnnouncement.


Unicast resolution#

Mdns::Peer can also send unicast mDNS queries and wait for a response. These are instance-only (no lookup* static methods):

// Resolve a hostname to its first IPv4 address
IpAddress ip = peer.resolveAddress("myhost.local", AF_INET);

// All addresses
IpAddressList all = peer.resolveAllAddress("myhost.local");
IpAddressList v6  = peer.resolveAllAddress("myhost.local", AF_INET6);

// Reverse lookup
std::string   name    = peer.resolveName("192.168.1.42");
AliasList     aliases = peer.resolveAllName("192.168.1.42");

// Custom timeout
IpAddress ip = peer.resolveAddress("myhost.local", AF_INET,
                                    std::chrono::milliseconds(2000));

Default timeout is 5 seconds.


Replying to queries#

Inherited from Dns::NameServer — same reply() signature:

reply(query, answers);
reply(query, answers, authorities, additionals, rcode);

For mDNS, the response is sent back to the multicast group unless the query had the QU bit set, in which case it is sent unicast to the originating client.


onQuery vs onAnnouncement#

CallbackTriggered by
onQueryIncoming mDNS query (QR bit = 0), including probes from other peers
onAnnouncementIncoming mDNS response (QR bit = 1) not matching a pending unicast query

Unicast query responses (from resolveAddress etc.) are handled internally and do not reach onAnnouncement.


Debug callbacks#

Same pattern as Dns::Resolver — pre-wired in DEBUG builds, nullptr in release:

peer.onSuccess = [](const DnsPacket& p) { /* ... */ };
peer.onFailure = [](const DnsPacket& p) { /* ... */ };

Error handling#

bind(), probe(), announce(), goodbye(), browse() return 0 on success, -1 on failure. Resolution methods return a wildcard address or empty list on failure. Check lastError:

CodeCause
Errc::InvalidParamEmpty records vector, empty service type, or empty hostname
Errc::TimedOutUnicast resolution timeout
Errc::MessageTooLongSerialized packet exceeds 8192 bytes

Protocol details#

  • Transport: UDP multicast, port 5353
  • IPv4 multicast group: 224.0.0.251
  • IPv6 multicast group: ff02::fb
  • Multicast loopback disabled in release builds (enabled in debug)
  • Max message size: 8192 bytes
  • Probe uses QU bit (class 0x8000) and authority section per RFC 6762 §8
  • Goodbye sets ttl = 0 per RFC 6762 §11
  • Transaction IDs for unicast queries are random 16-bit values; mDNS multicast uses id = 0

Summary#

FeatureSupported
Multicast announce
Probe (conflict detection)
Goodbye
Browse (PTR query)
Unicast resolution (A/AAAA)
Unicast reverse resolution (PTR)
onQuery callback✅ (pure virtual)
onAnnouncement callback✅ (pure virtual)
IPv4 multicast
IPv6 multicast
Custom reactor
Custom timeout
Static lookup* methods