A port of Apple's mDNSResponder + libdns_sd to FreeBSD, providing native Bonjour / zeroconf service discovery: .local hostname resolution, automatic printer / file-share discovery on the LAN, and the same dns-sd(1) + libdns_sd API surface that Apple-derived applications already speak. Companion to launchd, configd, kmodloader, asl, notifyd.
mdns/ at the top, alongside the other Apple-derived ports.dns_sd.h surface they expect.mDNSResponder-878.70.2 (latest tag at apple-oss-distributions/mDNSResponder, Apache 2.0). 261 source files, ~154k LOC. Surprise: zero <mach/> includes — mDNSResponder is already cross-platform via the mDNSPosix/ platform layer Apple maintains for Linux/FreeBSD/Solaris builds. The port is much closer to a configuration exercise than a port.mDNSMacOSX (Mach-tied, Darwin-only), mDNSPosix (BSD sockets + select/poll, builds on FreeBSD today), mDNSWindows. We use mDNSPosix as-is. Drop mDNSMacOSX + mDNSWindows on import.mDNSPosix's default event loop is select(2). We swap that for libdispatch sources to match the rest of our daemon stack — ~200 LOC change, optional polish.Provide a working mdnsd daemon and libdns_sd client library on FreeBSD so that:
DNSServiceRegister, DNSServiceBrowse, DNSServiceResolve, DNSServiceQueryRecord link unchanged..local hostnames resolve via mDNS (a gershwin desktop named "joe" appears as joe.local to other Bonjour-aware machines on the LAN).dns-sd(1) CLI works for service registration / browsing / lookup.mDNSResponder.proj uses macOS-private APIs (CFNetwork, SecurityFoundation, IOKit power-management hooks). Drop on import.libavahi-client.so stay on Avahi; apps that link libdns_sd.so get our port. The two libraries don't share a wire protocol nor an API.Monorepo. Source under mdns/ in freebsd-launchd:
freebsd-launchd/
├── src/ launchd
├── configd/ Apple configd
├── kmodloader/ clean-room kmodloader
├── asl/ Apple syslog
├── notifyd/ Apple Libnotify
├── mdns/ Apple mDNSResponder (this plan)
│ ├── scripts/import-source.sh
│ ├── Makefile
│ ├── compat/
│ └── src/ forked Apple mDNSResponder-878.70.2
│ ├── mDNSCore/ platform-agnostic core (kept as-is)
│ ├── mDNSPosix/ BSD/Linux/FreeBSD platform layer (used as-is)
│ ├── mDNSShared/ shared utilities, libdns_sd source
│ ├── Clients/ dns-sd CLI + sample apps
│ ├── DSO/ DNS Stateful Operations (RFC 8490)
│ └── ServiceRegistration/ mDNS service-registration daemon
└── make-mdnsresponder.sh STANDALONE — builds + installs
Apple has invested in non-Darwin builds because mDNSResponder is also shipped as a daemon on Linux distributions and Solaris (now defunct), and because various Apple-internal teams build for embedded/Wind-River targets that aren't macOS. The mDNSPosix/ directory contains:
mDNSPosix.c — platform-init: opens UDP sockets, queries interfaces via getifaddrs(3), hooks up SIGINT/SIGTERMmDNSUNP.{c,h} — Unix Network Programming helpers (interface enumeration that handles BSD's SIOCGIFCONF + Linux's netlink + Solaris's whatever, all gated by #ifdef)PosixDaemon.c — daemon main: loads the core, runs select(2) loop, handles client IPC over /var/run/mDNSRespondernss_mdns.{c,h} — nsswitch.conf module so gethostbyname("foo.local") resolves via mDNSThis already builds on FreeBSD as of recent tags. Verification: the net/mDNSResponder port in the FreeBSD ports tree uses exactly this layer.
The default PosixDaemon.c uses select(2). For consistency with the rest of our daemon stack (launchd, configd, asl, notifyd, kmodloader all use libdispatch), we replace the select loop with DISPATCH_SOURCE_TYPE_READ sources for: each multicast UDP socket, the client IPC socket, signals. ~200 LOC change in PosixDaemon.c; non-blocking optional polish — the select-based daemon works fine.
| Source type | Watches | Reaction |
|---|---|---|
DISPATCH_SOURCE_TYPE_READ | UDP/5353 multicast socket per active iface | parse mDNS message; update record cache; respond to queries |
DISPATCH_SOURCE_TYPE_READ | /var/run/mDNSResponder (Unix socket listener) | accept new client connections |
DISPATCH_SOURCE_TYPE_READ | each connected client fd | parse client IPC framed messages: register / browse / resolve / cancel |
DISPATCH_SOURCE_TYPE_VNODE | /etc/mdnsd.conf | reload config; reset interface filters |
DISPATCH_SOURCE_TYPE_SIGNAL | SIGTERM, SIGHUP, SIGUSR1 | SIGTERM: clean shutdown. SIGHUP: reload conf. SIGUSR1: dump cache. |
| configd subscription (DO) | network-state events from configd | iface up: open multicast socket; iface down: tear down. Replaces configd-internal polling that mDNSResponder.proj's macOSX layer does. |
| Artifact | Path | Why |
|---|---|---|
mdnsd binary | /usr/sbin/mdnsd | System daemon, admin-callable. Same path FreeBSD's net/mDNSResponder port uses. |
dns-sd CLI | /usr/bin/dns-sd | Service-registration / browsing tool. Apple's name; user-callable. |
libdns_sd.so | /System/Library/Libraries/libdns_sd.so | Client library. Apps link against this. |
| Header | /System/Library/Headers/dns_sd.h | Public API. Apps #include <dns_sd.h>. |
| nsswitch module | /usr/local/lib/nss_mdns.so.1 | nsswitch.conf integration: hosts: files mdns dns resolves .local via mDNS. |
| Daemon socket | /var/run/mDNSResponder | Conventional Apple path for client IPC. |
| launchd plist | /System/Library/LaunchDaemons/org.freebsd.mdnsd.plist | Project-shipped daemon. |
| Config | /etc/mdnsd.conf | Optional; interface filters and logging. |
| Decision | Choice |
|---|---|
| Source baseline | Apple mDNSResponder-878.70.2. Apache 2.0. Latest tag. |
| Platform layer | Use mDNSPosix/ as-is. Drop mDNSMacOSX/ and mDNSWindows/. |
| Mach IPC | None — mDNSResponder doesn't use Mach in non-Darwin builds. Zero <mach/> includes in the imported tree once mDNSMacOSX is dropped. |
| Client IPC mechanism | Existing libdns_sd.c Unix-socket protocol. Already there; framed messages over /var/run/mDNSResponder. |
| Event loop | Phase 1: stock select(2) loop in PosixDaemon.c. Phase 3: upgrade to libdispatch sources for consistency. |
| Coexistence with Avahi | None. Pick one per system. Apple-shaped installs default to this port. |
| nsswitch integration | Ship nss_mdns.so + edit /etc/nsswitch.conf overlay so .local hostname lookups resolve via mDNS. |
| License (top-level) | BSD-2-Clause. Apple's mDNS source retains Apache 2.0 per-file (mostly; a few BSD-licensed sub-files preserved as-is). |
mdns/src/)Imported source: Apple mDNSResponder-878.70.2. 261 files, ~154k LOC. Zero Mach-tied files (the Mach-using code is all under mDNSMacOSX/, which we drop wholesale).
mDNSMacOSX/ (entire directory) — Darwin-specific platform layer using IOKit, configd integration, SecurityFoundation, etc.mDNSWindows/ (entire directory) — Windows platform layermDNSResponder.proj/ — Xcode projectClients/PrinterSetupWizard/ — macOS-only printer setup UI (Carbon-era)Clients/Java/ — Java JNI wrapper; out of scopeClients/ExplorerPlugin/ — Windows Internet Explorer plugin| Directory / file | Apple LOC | Action |
|---|---|---|
mDNSCore/ | ~50k | Keep as-is. Platform-agnostic mDNS protocol implementation. No edits expected. |
mDNSPosix/ | ~8k | Keep mostly as-is. Phase 3 upgrades the daemon's event loop from select to libdispatch. |
mDNSShared/dnssd_clientstub.c + dnssd_ipc.{c,h} | ~5k | Keep. The libdns_sd client library + wire protocol. |
mDNSShared/dnssd_clientshim.c | ~700 | Keep. Embedded-build alternative to the daemon (for static linking; we don't use, but trivial to keep). |
mDNSShared/dnsextd* | ~10k | Drop — dnsextd is the wide-area Bonjour helper; we don't ship BTMM. |
Clients/dns-sd.c | ~3k | Keep. The dns-sd(1) CLI. |
Clients/SimpleChat/, SampleCode/, etc. | — | Keep selectively as documentation; don't ship binaries. |
DSO/ | ~3k | Keep. DNS Stateful Operations (RFC 8490) for long-running queries; modern mDNS uses this. |
ServiceRegistration/ | ~5k | Keep. Daemon for centralized service registration. |
Total post-Phase-2: roughly 80-85k LOC kept, ~70k LOC dropped (mostly the Darwin and Windows platform layers). Net: a clean cross-platform Bonjour stack.
| Feature | Apple's Darwin build | This port (FreeBSD-only) |
|---|---|---|
| Network state ingestion | configd's network-state plugin via Mach notifications | configd subscription via DO (per our configd port). Or stock mDNSPosix iface poll. |
| Power management hooks | IOKit power-management for sleep/wake | Skip in Phase 1. Phase 3+: subscribe to notifyd's org.freebsd.power.sleep-requested / ...wake; pause/resume mDNS state appropriately. |
| Sandboxing | sandbox(7) profile (mdnsd.sb) | Drop. Capsicum-based sandboxing later. |
| Wide-Area Bonjour | Integration with iCloud + Back to My Mac infrastructure | Dropped (non-goal). |
| Build-system gates | iOS / sim / catalyst #if | Delete. One target. |
Each gershwin machine on the LAN registers <hostname>.local via mDNS. Other machines (gershwin, macOS, Linux running Avahi, Windows Bonjour) resolve it without DNS server config. ssh joe.local, open http://joe.local:8080 Just Work.
| Concern | Bonjour service type | Effect |
|---|---|---|
| Printers | _ipp._tcp, _ipps._tcp, _pdl-datastream._tcp | Workspace's print dialog populates the printer list automatically. No manual IP / queue-name entry. |
| File shares (SMB) | _smb._tcp | Workspace's File Viewer "Network" sidebar shows other machines automatically. |
| SSH targets | _ssh._tcp | Terminal apps populate "known hosts" with discoverable SSH servers. |
| HTTP services | _http._tcp | "Network" browser shows web servers running on the LAN. |
| iTunes-style media sharing | _daap._tcp | Music apps auto-discover libraries on the network. (Useful if gershwin grows a Music app.) |
| Sleep / Wake announcements | integration with notifyd's power events | When a machine wakes, re-announce all registered services. When it sleeps, withdraw. |
Any app that calls DNSServiceRegister(), DNSServiceBrowse(), DNSServiceResolve(), etc., links unchanged. Specific examples:
swift package resolving local registry, etc.<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>Label</key> <string>org.freebsd.mdnsd</string>
<key>ProgramArguments</key> <array><string>/usr/sbin/mdnsd</string></array>
<key>RunAtLoad</key> <true/>
<key>KeepAlive</key> <true/>
<key>Sockets</key> <dict>
<key>Listeners</key> <dict>
<key>SockPathName</key> <string>/var/run/mDNSResponder</string>
<key>SockType</key> <string>stream</string>
<key>SockPathMode</key> <integer>438</integer> <!-- 0666 -->
</dict>
</dict>
</dict>
</plist>
mDNSResponder needs at least loopback up. configd's network-state events are nice-to-have for hot-iface integration but not blocking. Order: starts in parallel with the other system daemons; comes online faster on systems with no NICs (loopback only) or slower as it waits for first iface advertisement.
Apple's mDNSResponder is mostly Apache 2.0 (per the LICENSE file in the repo). Some sub-files use BSD-derived licenses (BSD-3-Clause, BSD-2-Clause, MIT) preserved per-file. Different from configd / asl / notifyd which are APSL.
| Source | License | How we handle it |
|---|---|---|
Apple mDNSResponder-878.70.2 (majority Apache 2.0) | Apache 2.0 (most files); BSD-2/3-Clause / MIT (some) | Per-file headers preserved verbatim. Edits inherit per-file license. |
| This repo's new code (FreeBSD shims, integration glue) | BSD-2-Clause | SPDX header on each new file. |
| libdispatch (linked, not in tree) | Apache 2.0 + Runtime Exception | Listed in NOTICE. |
mdns/ at the top of freebsd-launchd.mdns/scripts/import-source.sh that clones at mDNSResponder-878.70.2.mdns/Makefile + make-mdnsresponder.sh.mdnsd binary using mDNSPosix/PosixDaemon.c's default select-loop entrypoint. Uses getifaddrs(3) for iface enumeration; SIOCGIFCONF et al. for the Posix-specific bits.libdns_sd.so.dns-sd -B _services._dns-sd._udp, verify it returns at minimum the local registration.nss_mdns.so; ship /etc/nsswitch.conf overlay with hosts: files mdns dns./usr/bin/dns-sd.getent hosts <hostname>.local, verify it resolves.PosixDaemon.c's select loop with libdispatch sources. ~200 LOC. Optional polish for stack consistency.DNSServiceBrowse for SMB / file-share discovery.DNSServiceBrowse for _ipp._tcp / _ipps._tcp.net/avahi installed from FreeBSD pkg. Both daemons bind UDP/5353; one wins, the other fails to start. Decision: ship a build-time conflict in our pkg (if/when we package this) marking it as conflicting with avahi-daemon. Document in README.
_sleep-proxy._udp) that re-advertises sleeping clients. We don't have AppleTVs, but a user running gershwin-server-machine could optionally run our mdnsd in sleep-proxy mode. Phase 5+ feature; flag not enabled by default.
$(hostname).local at startup. If the host's hostname is "Amnesiac" (the FreeBSD kernel default), every system on a livecd LAN announces itself as Amnesiac.local — conflict storm. Decision: configd should set a unique hostname (UUID-prefixed, or based on the primary MAC) before mdnsd starts. Coordinate via launchd ordering.
mDNSResponder-878.70.2).net/mDNSResponder already exists and uses mDNSPosix; reference for build / packaging.