Repository navigation
Expand file tree
/
Copy pathfreebsd-mdnsresponder-plan.html
More file actions
449 lines (388 loc) · 31.3 KB
/
Copy pathfreebsd-mdnsresponder-plan.html
File metadata and controls
449 lines (388 loc) · 31.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>FreeBSD mDNSResponder — porting plan</title>
<style>
:root {
--bg: #fbfbf8;
--fg: #1a1a1a;
--muted: #555;
--accent: #b03000;
--accent2: #0a4d68;
--ok: #1f7a1f;
--warn: #b06800;
--bad: #b00020;
--code-bg: #f0ece4;
--rule: #d6cfc0;
--card: #fff;
}
html { -webkit-text-size-adjust: 100%; }
body { margin: 0 auto; max-width: 980px; padding: 2.5rem 1.5rem 6rem;
font: 16px/1.55 -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
color: var(--fg); background: var(--bg); }
h1 { font-size: 2rem; line-height: 1.2; margin: 0 0 .25rem; }
h2 { font-size: 1.4rem; margin: 2.5rem 0 .75rem; padding-bottom: .25rem; border-bottom: 2px solid var(--rule); }
h3 { font-size: 1.15rem; margin: 1.75rem 0 .5rem; color: var(--accent2); }
h4 { margin: 1.25rem 0 .35rem; }
.subtitle { color: var(--muted); font-size: 1.05rem; margin: 0 0 2rem; }
code, pre, kbd { font-family: "SF Mono", Menlo, Consolas, monospace; }
code { background: var(--code-bg); padding: 1px 5px; border-radius: 3px; font-size: .9em; }
pre { background: var(--code-bg); padding: .85rem 1rem; border-radius: 6px;
overflow-x: auto; font-size: .82rem; line-height: 1.45;
border-left: 3px solid var(--accent2); }
pre code { background: none; padding: 0; }
pre.shell { border-left-color: var(--ok); }
pre.plist { border-left-color: var(--accent); }
a { color: var(--accent2); }
a:hover { color: var(--accent); }
.tldr { background: var(--card); border: 1px solid var(--rule); border-left: 4px solid var(--accent2);
padding: 1rem 1.25rem; border-radius: 6px; margin-bottom: 2rem; }
.tldr h3 { margin-top: 0; color: var(--accent2); }
.pill { display: inline-block; font-size: .72rem; padding: 1px 8px; border-radius: 999px;
background: #eee; color: #333; margin-left: .35rem; vertical-align: middle;
font-weight: 600; letter-spacing: .02em; }
.pill.ok { background: #d8efd8; color: var(--ok); }
.pill.warn { background: #f6e4cb; color: var(--warn); }
.pill.bad { background: #f5d0d6; color: var(--bad); }
.pill.info { background: #d6e6f3; color: var(--accent2); }
.phase { background: var(--card); border: 1px solid var(--rule); border-radius: 6px;
padding: 1.2rem 1.4rem; margin: 1rem 0; }
.phase h3 { margin-top: 0; }
table { border-collapse: collapse; width: 100%; margin: 1rem 0; font-size: .92rem; }
th, td { text-align: left; padding: .5rem .65rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
th { background: #eee5d6; }
tr:nth-child(even) td { background: #faf6ed; }
.nav { position: sticky; top: 0; background: var(--bg); margin: -2.5rem -1.5rem 2rem;
padding: .75rem 1.5rem; border-bottom: 1px solid var(--rule);
font-size: .88rem; z-index: 10; }
.nav a { margin-right: .9rem; text-decoration: none; }
.verdict { font-weight: 600; }
.verdict.go { color: var(--ok); }
.verdict.maybe { color: var(--warn); }
.verdict.no { color: var(--bad); }
.ascii-diagram { font-family: "SF Mono", Menlo, Consolas, monospace; font-size: .82rem; line-height: 1.3; white-space: pre; background: var(--code-bg); padding: 1rem; border-radius: 6px; overflow-x: auto; }
.open-q { background: #fff8d6; border: 1px solid #e5d76b; padding: .8rem 1rem; margin: 1rem 0; border-radius: 6px; font-size: .92rem; }
.open-q strong { color: #7a5e00; }
.resolved { background: #ecf7ec; border-left: 4px solid var(--ok); padding: .8rem 1rem; margin: 1rem 0; border-radius: 0 6px 6px 0; }
</style>
</head>
<body>
<nav class="nav">
<a href="#tldr">Status</a>
<a href="#goal">Goal</a>
<a href="#repo">Repo</a>
<a href="#arch">Architecture</a>
<a href="#paths">Install paths</a>
<a href="#decisions">Decisions</a>
<a href="#files">File-by-file</a>
<a href="#bsd-wins">FreeBSD wins</a>
<a href="#usecases">Use cases</a>
<a href="#integration">launchd integration</a>
<a href="#license">License</a>
<a href="#phases">Phases</a>
<a href="#open">Open questions</a>
</nav>
<h1>FreeBSD mDNSResponder — porting plan <span class="pill info">freebsd-launchd-mach (v2) effort</span></h1>
<p class="subtitle">A port of Apple's mDNSResponder + libdns_sd to FreeBSD, providing native Bonjour / zeroconf service discovery: <code>.local</code> hostname resolution, automatic printer / file-share discovery on the LAN, and the same <code>dns-sd(1)</code> + <code>libdns_sd</code> API surface that Apple-derived applications already speak. Companion to <a href="freebsd-launchd-plan.html">launchd</a>, <a href="nextbsd-configd-plan.html">configd</a>, <a href="freebsd-kmodloader-plan.html">kmodloader</a>, <a href="freebsd-asl-plan.html">asl</a>, <a href="freebsd-notifyd-plan.html">notifyd</a>.</p>
<div class="resolved">
<strong>Revision 2026-05-23.</strong> Filed under the <code>freebsd-launchd-mach</code> (v2) Mach-IPC track effort. Strategy: keep Apple's cross-platform <code>mDNSPosix/</code> layer as the protocol engine (zero Mach surface — kqueue + sockets); add a Mach service layer mirroring Apple's <code>dnssd.defs</code> MIG IDL so <code>DNSServiceBrowse</code> / <code>DNSServiceRegister</code> / <code>DNSServiceResolve</code> clients get Apple-shape <code>DNSServiceRef</code> behavior over <code>mach_msg</code>. Network-state events sourced via Mach RPC from the in-repo MIG-served configd (<code>com.apple.SystemConfiguration</code>), NOT via GNUstep DO.
</div>
<section id="tldr" class="tldr">
<h3>Status: planning <span class="pill info">v0</span> — deferred</h3>
<ul>
<li><strong>Repo:</strong> <a href="https://github.com/pkgdemon/freebsd-launchd">github.com/pkgdemon/freebsd-launchd</a> — <strong>monorepo</strong>. mDNSResponder source under <code>mdns/</code> at the top, alongside the other Apple-derived ports.</li>
<li><strong>Mission:</strong> ship Bonjour-shaped service discovery on FreeBSD with API-level compatibility for Apple-derived applications. Replaces or coexists with Avahi (the FreeBSD-pkg alternative) by giving apps the exact <code>dns_sd.h</code> surface they expect.</li>
<li><strong>Source:</strong> Apple <code>mDNSResponder-878.70.2</code> (latest tag at <code>apple-oss-distributions/mDNSResponder</code>, Apache 2.0). 261 source files, ~154k LOC. <strong>Surprise:</strong> zero <code><mach/></code> includes — mDNSResponder is already cross-platform via the <code>mDNSPosix/</code> platform layer Apple maintains for Linux/FreeBSD/Solaris builds. The port is much closer to a configuration exercise than a port.</li>
<li><strong>The pre-existing mDNSPosix layer is the entire trick.</strong> Apple ships separate platform shims: <code>mDNSMacOSX</code> (Mach-tied, Darwin-only), <code>mDNSPosix</code> (BSD sockets + select/poll, builds on FreeBSD today), <code>mDNSWindows</code>. We use <code>mDNSPosix</code> as-is. Drop <code>mDNSMacOSX</code> + <code>mDNSWindows</code> on import.</li>
<li><strong>libdispatch upgrade.</strong> <code>mDNSPosix</code>'s default event loop is <code>select(2)</code>. We swap that for <code>libdispatch</code> sources to match the rest of our daemon stack — ~200 LOC change, optional polish.</li>
<li><strong>Why this is deferred:</strong> nothing in launchd / configd / kmodloader Phases 0-5 needs mDNSResponder. Promotion happens once gershwin's networked desktop UX (printer discovery, file sharing, .local browsing) becomes a priority. <strong>Phase 7+ work, parallel to notifyd in priority.</strong></li>
<li><strong>Coexistence with Avahi:</strong> not in the same boot. Avahi binds the same UDP/5353 socket. Either ship Avahi (FreeBSD-pkg) or this port; users pick. Default for Apple-shaped systems is this port.</li>
<li><strong>Licensing:</strong> majority of the source is <strong>Apache 2.0</strong> (per the LICENSE file in the Apple repo). A few sub-files use BSD-derived licenses; per-file headers preserved. Top-level repo stays BSD-2-Clause.</li>
</ul>
</section>
<h2 id="goal">1. Goal & non-goals</h2>
<h3>1.1 Goal</h3>
<p>Provide a working <code>mdnsd</code> daemon and <code>libdns_sd</code> client library on FreeBSD so that:</p>
<ul>
<li>Apps calling <code>DNSServiceRegister</code>, <code>DNSServiceBrowse</code>, <code>DNSServiceResolve</code>, <code>DNSServiceQueryRecord</code> link unchanged.</li>
<li><code>.local</code> hostnames resolve via mDNS (a gershwin desktop named "joe" appears as <code>joe.local</code> to other Bonjour-aware machines on the LAN).</li>
<li><code>dns-sd(1)</code> CLI works for service registration / browsing / lookup.</li>
<li>Printer discovery, file-share discovery, AirPlay-receiver-style discovery surface in gershwin without manual configuration.</li>
</ul>
<h3>1.2 Non-goals (this iteration)</h3>
<ul>
<li><strong>No Apple-private extensions.</strong> Some functionality in <code>mDNSResponder.proj</code> uses macOS-private APIs (CFNetwork, SecurityFoundation, IOKit power-management hooks). Drop on import.</li>
<li><strong>No Wide-Area Bonjour (BTMM).</strong> Apple's "Back to My Mac" wide-area Bonjour requires iCloud / DNS-SD-over-TLS hooks not relevant outside Apple's identity infrastructure.</li>
<li><strong>No mDNSWindows.</strong> Drop the entire dir on import.</li>
<li><strong>No Avahi compatibility.</strong> Apps that link <code>libavahi-client.so</code> stay on Avahi; apps that link <code>libdns_sd.so</code> get our port. The two libraries don't share a wire protocol nor an API.</li>
<li><strong>No coexistence with Avahi at runtime.</strong> Both bind UDP/5353; pick one per system.</li>
</ul>
<h2 id="repo">2. Repository</h2>
<p>Monorepo. Source under <code>mdns/</code> in <code>freebsd-launchd</code>:</p>
<pre><code>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
</code></pre>
<h2 id="arch">3. Architecture</h2>
<div class="ascii-diagram"> +-------------------------+
| /etc/mdnsd.conf |
| (interface filters, |
| logging level, etc.) |
+-----------+-------------+
| (vnode source: live reload)
v
+---------------------+ +-------+--------+ +----------------------+
| UDP/5353 multicast |-->| mdnsd |<-->| /var/run/mDNSResponder|
| (224.0.0.251) | | (mDNSCore + | | (Unix socket; client |
| READ source per | | mDNSPosix + | | IPC; libdispatch |
| iface that's up | | libdispatch) | | READ source) |
+---------------------+ +-------+--------+ +----------------------+
|
+--------+--------+
| record cache |
| (in-memory |
| authoritative |
| + cached) |
+-----------------+
Client side: app links libdns_sd.so -- DNSServiceRegister(...), etc.
Library opens /var/run/mDNSResponder, sends framed
messages, receives callbacks via the socket fd
registered with the app's runloop / dispatch queue.</div>
<h3>3.1 Why mDNSPosix already works</h3>
<p>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 <code>mDNSPosix/</code> directory contains:</p>
<ul>
<li><code>mDNSPosix.c</code> — platform-init: opens UDP sockets, queries interfaces via <code>getifaddrs(3)</code>, hooks up <code>SIGINT</code>/<code>SIGTERM</code></li>
<li><code>mDNSUNP.{c,h}</code> — Unix Network Programming helpers (interface enumeration that handles BSD's <code>SIOCGIFCONF</code> + Linux's netlink + Solaris's whatever, all gated by <code>#ifdef</code>)</li>
<li><code>PosixDaemon.c</code> — daemon main: loads the core, runs <code>select(2)</code> loop, handles client IPC over <code>/var/run/mDNSResponder</code></li>
<li><code>nss_mdns.{c,h}</code> — nsswitch.conf module so <code>gethostbyname("foo.local")</code> resolves via mDNS</li>
</ul>
<p>This already builds on FreeBSD as of recent tags. Verification: the <code>net/mDNSResponder</code> port in the FreeBSD ports tree uses exactly this layer.</p>
<h3>3.2 libdispatch upgrade (optional)</h3>
<p>The default <code>PosixDaemon.c</code> uses <code>select(2)</code>. For consistency with the rest of our daemon stack (launchd, configd, asl, notifyd, kmodloader all use libdispatch), we replace the select loop with <code>DISPATCH_SOURCE_TYPE_READ</code> sources for: each multicast UDP socket, the client IPC socket, signals. ~200 LOC change in <code>PosixDaemon.c</code>; non-blocking optional polish — the select-based daemon works fine.</p>
<h3>3.3 Event sources (libdispatch, post-Phase-3)</h3>
<table>
<thead><tr><th>Source type</th><th>Watches</th><th>Reaction</th></tr></thead>
<tbody>
<tr><td><code>DISPATCH_SOURCE_TYPE_READ</code></td><td>UDP/5353 multicast socket per active iface</td><td>parse mDNS message; update record cache; respond to queries</td></tr>
<tr><td><code>DISPATCH_SOURCE_TYPE_READ</code></td><td><code>/var/run/mDNSResponder</code> (Unix socket listener)</td><td>accept new client connections</td></tr>
<tr><td><code>DISPATCH_SOURCE_TYPE_READ</code></td><td>each connected client fd</td><td>parse client IPC framed messages: register / browse / resolve / cancel</td></tr>
<tr><td><code>DISPATCH_SOURCE_TYPE_VNODE</code></td><td><code>/etc/mdnsd.conf</code></td><td>reload config; reset interface filters</td></tr>
<tr><td><code>DISPATCH_SOURCE_TYPE_SIGNAL</code></td><td>SIGTERM, SIGHUP, SIGUSR1</td><td>SIGTERM: clean shutdown. SIGHUP: reload conf. SIGUSR1: dump cache.</td></tr>
<tr><td>configd subscription via Mach RPC (SCDynamicStoreCreate + SCDynamicStoreSetNotificationKeys against <code>com.apple.SystemConfiguration</code>)</td><td>network-state events from configd</td><td>iface up: open multicast socket; iface down: tear down. Replaces configd-internal polling that mDNSResponder.proj's macOSX layer does.</td></tr>
</tbody>
</table>
<h2 id="paths">4. Install paths</h2>
<table>
<thead><tr><th>Artifact</th><th>Path</th><th>Why</th></tr></thead>
<tbody>
<tr><td><code>mdnsd</code> binary</td><td><code>/usr/sbin/mdnsd</code></td><td>System daemon, admin-callable. Same path FreeBSD's <code>net/mDNSResponder</code> port uses.</td></tr>
<tr><td><code>dns-sd</code> CLI</td><td><code>/usr/bin/dns-sd</code></td><td>Service-registration / browsing tool. Apple's name; user-callable.</td></tr>
<tr><td><code>libdns_sd.so</code></td><td><code>/System/Library/Libraries/libdns_sd.so</code></td><td>Client library. Apps link against this.</td></tr>
<tr><td>Header</td><td><code>/System/Library/Headers/dns_sd.h</code></td><td>Public API. Apps <code>#include <dns_sd.h></code>.</td></tr>
<tr><td>nsswitch module</td><td><code>/usr/local/lib/nss_mdns.so.1</code></td><td>nsswitch.conf integration: <code>hosts: files mdns dns</code> resolves <code>.local</code> via mDNS.</td></tr>
<tr><td>Daemon socket</td><td><code>/var/run/mDNSResponder</code></td><td>Conventional Apple path for client IPC.</td></tr>
<tr><td>launchd plist</td><td><code>/System/Library/LaunchDaemons/org.freebsd.mdnsd.plist</code></td><td>Project-shipped daemon.</td></tr>
<tr><td>Config</td><td><code>/etc/mdnsd.conf</code></td><td>Optional; interface filters and logging.</td></tr>
</tbody>
</table>
<h2 id="decisions">5. Locked architectural decisions</h2>
<table>
<thead><tr><th>Decision</th><th>Choice</th></tr></thead>
<tbody>
<tr><td>Source baseline</td><td>Apple <code>mDNSResponder-878.70.2</code>. Apache 2.0. Latest tag.</td></tr>
<tr><td>Platform layer</td><td>Use <code>mDNSPosix/</code> as-is. Drop <code>mDNSMacOSX/</code> and <code>mDNSWindows/</code>.</td></tr>
<tr><td>Mach IPC</td><td>None — mDNSResponder doesn't use Mach in non-Darwin builds. Zero <code><mach/></code> includes in the imported tree once <code>mDNSMacOSX</code> is dropped.</td></tr>
<tr><td>Client IPC mechanism</td><td>Existing <code>libdns_sd.c</code> Unix-socket protocol. Already there; framed messages over <code>/var/run/mDNSResponder</code>.</td></tr>
<tr><td>Event loop</td><td>Phase 1: stock <code>select(2)</code> loop in <code>PosixDaemon.c</code>. Phase 3: upgrade to libdispatch sources for consistency.</td></tr>
<tr><td>Coexistence with Avahi</td><td>None. Pick one per system. Apple-shaped installs default to this port.</td></tr>
<tr><td>nsswitch integration</td><td>Ship <code>nss_mdns.so</code> + edit <code>/etc/nsswitch.conf</code> overlay so <code>.local</code> hostname lookups resolve via mDNS.</td></tr>
<tr><td>License (top-level)</td><td>BSD-2-Clause. Apple's mDNS source retains Apache 2.0 per-file (mostly; a few BSD-licensed sub-files preserved as-is).</td></tr>
</tbody>
</table>
<h2 id="files">6. File-by-file plan (<code>mdns/src/</code>)</h2>
<p>Imported source: Apple <code>mDNSResponder-878.70.2</code>. 261 files, ~154k LOC. Zero Mach-tied files (the Mach-using code is all under <code>mDNSMacOSX/</code>, which we drop wholesale).</p>
<h3>6.1 Deleted on import</h3>
<ul>
<li><code>mDNSMacOSX/</code> (entire directory) — Darwin-specific platform layer using IOKit, configd integration, SecurityFoundation, etc.</li>
<li><code>mDNSWindows/</code> (entire directory) — Windows platform layer</li>
<li><code>mDNSResponder.proj/</code> — Xcode project</li>
<li><code>Clients/PrinterSetupWizard/</code> — macOS-only printer setup UI (Carbon-era)</li>
<li><code>Clients/Java/</code> — Java JNI wrapper; out of scope</li>
<li><code>Clients/ExplorerPlugin/</code> — Windows Internet Explorer plugin</li>
</ul>
<h3>6.2 Retained — Phase 2 fate</h3>
<table>
<thead>
<tr><th>Directory / file</th><th>Apple LOC</th><th>Action</th></tr>
</thead>
<tbody>
<tr><td><code>mDNSCore/</code></td><td>~50k</td><td>Keep as-is. Platform-agnostic mDNS protocol implementation. <strong>No edits expected.</strong></td></tr>
<tr><td><code>mDNSPosix/</code></td><td>~8k</td><td>Keep mostly as-is. Phase 3 upgrades the daemon's event loop from select to libdispatch.</td></tr>
<tr><td><code>mDNSShared/dnssd_clientstub.c</code> + <code>dnssd_ipc.{c,h}</code></td><td>~5k</td><td>Keep. The libdns_sd client library + wire protocol.</td></tr>
<tr><td><code>mDNSShared/dnssd_clientshim.c</code></td><td>~700</td><td>Keep. Embedded-build alternative to the daemon (for static linking; we don't use, but trivial to keep).</td></tr>
<tr><td><code>mDNSShared/dnsextd*</code></td><td>~10k</td><td>Drop — dnsextd is the wide-area Bonjour helper; we don't ship BTMM.</td></tr>
<tr><td><code>Clients/dns-sd.c</code></td><td>~3k</td><td>Keep. The <code>dns-sd(1)</code> CLI.</td></tr>
<tr><td><code>Clients/SimpleChat/</code>, <code>SampleCode/</code>, etc.</td><td>—</td><td>Keep selectively as documentation; don't ship binaries.</td></tr>
<tr><td><code>DSO/</code></td><td>~3k</td><td>Keep. DNS Stateful Operations (RFC 8490) for long-running queries; modern mDNS uses this.</td></tr>
<tr><td><code>ServiceRegistration/</code></td><td>~5k</td><td>Keep. Daemon for centralized service registration.</td></tr>
</tbody>
</table>
<p>Total post-Phase-2: roughly <strong>80-85k LOC</strong> kept, ~70k LOC dropped (mostly the Darwin and Windows platform layers). Net: a clean cross-platform Bonjour stack.</p>
<h2 id="bsd-wins">7. FreeBSD-only wins (vs Apple's Darwin-tied build)</h2>
<table>
<thead><tr><th>Feature</th><th>Apple's Darwin build</th><th>This port (FreeBSD-only)</th></tr></thead>
<tbody>
<tr><td>Network state ingestion</td><td>configd's network-state plugin via Mach notifications</td><td>configd subscription via Mach RPC (the in-repo MIG-served configd at <code>com.apple.SystemConfiguration</code>). Or stock mDNSPosix iface poll.</td></tr>
<tr><td>Power management hooks</td><td>IOKit power-management for sleep/wake</td><td>Skip in Phase 1. Phase 3+: subscribe to notifyd's <code>org.freebsd.power.sleep-requested</code> / <code>...wake</code>; pause/resume mDNS state appropriately.</td></tr>
<tr><td>Sandboxing</td><td>sandbox(7) profile (<code>mdnsd.sb</code>)</td><td>Drop. Capsicum-based sandboxing later.</td></tr>
<tr><td>Wide-Area Bonjour</td><td>Integration with iCloud + Back to My Mac infrastructure</td><td>Dropped (non-goal).</td></tr>
<tr><td>Build-system gates</td><td>iOS / sim / catalyst <code>#if</code></td><td>Delete. One target.</td></tr>
</tbody>
</table>
<h2 id="usecases">8. Use cases for gershwin</h2>
<h3>8.1 .local hostname resolution</h3>
<p>Each gershwin machine on the LAN registers <code><hostname>.local</code> via mDNS. Other machines (gershwin, macOS, Linux running Avahi, Windows Bonjour) resolve it without DNS server config. <code>ssh joe.local</code>, <code>open http://joe.local:8080</code> Just Work.</p>
<h3>8.2 Service discovery for the desktop</h3>
<table>
<thead><tr><th>Concern</th><th>Bonjour service type</th><th>Effect</th></tr></thead>
<tbody>
<tr><td>Printers</td><td><code>_ipp._tcp</code>, <code>_ipps._tcp</code>, <code>_pdl-datastream._tcp</code></td><td>Workspace's print dialog populates the printer list automatically. No manual IP / queue-name entry.</td></tr>
<tr><td>File shares (SMB)</td><td><code>_smb._tcp</code></td><td>Workspace's File Viewer "Network" sidebar shows other machines automatically.</td></tr>
<tr><td>SSH targets</td><td><code>_ssh._tcp</code></td><td>Terminal apps populate "known hosts" with discoverable SSH servers.</td></tr>
<tr><td>HTTP services</td><td><code>_http._tcp</code></td><td>"Network" browser shows web servers running on the LAN.</td></tr>
<tr><td>iTunes-style media sharing</td><td><code>_daap._tcp</code></td><td>Music apps auto-discover libraries on the network. (Useful if gershwin grows a Music app.)</td></tr>
<tr><td>Sleep / Wake announcements</td><td>integration with <code>notifyd</code>'s power events</td><td>When a machine wakes, re-announce all registered services. When it sleeps, withdraw.</td></tr>
</tbody>
</table>
<h3>8.3 Apps that "just work"</h3>
<p>Any app that calls <code>DNSServiceRegister()</code>, <code>DNSServiceBrowse()</code>, <code>DNSServiceResolve()</code>, etc., links unchanged. Specific examples:</p>
<ul>
<li>iTunes / Music-library apps registering their library service</li>
<li>SSH GUI clients like Royal TSX populating known hosts</li>
<li>Web servers announcing their port for "Network → look at sites running on the LAN"</li>
<li>AirPlay receivers (if anyone ships one)</li>
<li>Apple's own developer tools: <code>swift package</code> resolving local registry, etc.</li>
</ul>
<h2 id="integration">9. launchd integration</h2>
<h3>9.1 The plist</h3>
<pre class="plist"><code><?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></code></pre>
<h3>9.2 Boot ordering</h3>
<p>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.</p>
<h2 id="license">10. Licensing</h2>
<p>Apple's mDNSResponder is mostly <strong>Apache 2.0</strong> (per the <code>LICENSE</code> 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.</p>
<table>
<thead><tr><th>Source</th><th>License</th><th>How we handle it</th></tr></thead>
<tbody>
<tr><td>Apple <code>mDNSResponder-878.70.2</code> (majority Apache 2.0)</td><td>Apache 2.0 (most files); BSD-2/3-Clause / MIT (some)</td><td>Per-file headers preserved verbatim. Edits inherit per-file license.</td></tr>
<tr><td>This repo's new code (FreeBSD shims, integration glue)</td><td>BSD-2-Clause</td><td>SPDX header on each new file.</td></tr>
<tr><td>libdispatch (linked, not in tree)</td><td>Apache 2.0 + Runtime Exception</td><td>Listed in NOTICE.</td></tr>
</tbody>
</table>
<h2 id="phases">11. Phased delivery</h2>
<div class="phase">
<h3>Phase 0 — repo scaffold + import</h3>
<ul>
<li>Create <code>mdns/</code> at the top of <code>freebsd-launchd</code>.</li>
<li>Add <code>mdns/scripts/import-source.sh</code> that clones at <code>mDNSResponder-878.70.2</code>.</li>
<li>One-shot import; follow-up amputation per §6.1 (Darwin + Windows + Xcode out).</li>
<li>No build wired up; ISO unchanged.</li>
</ul>
</div>
<div class="phase">
<h3>Phase 1 — build + run mdnsd via mDNSPosix</h3>
<ul>
<li>Top-level <code>mdns/Makefile</code> + <code>make-mdnsresponder.sh</code>.</li>
<li>Build <code>mdnsd</code> binary using <code>mDNSPosix/PosixDaemon.c</code>'s default select-loop entrypoint. Uses <code>getifaddrs(3)</code> for iface enumeration; <code>SIOCGIFCONF</code> et al. for the Posix-specific bits.</li>
<li>Build <code>libdns_sd.so</code>.</li>
<li>org.freebsd.mdnsd.plist shipped.</li>
<li>Boot-test extension: ssh in, run <code>dns-sd -B _services._dns-sd._udp</code>, verify it returns at minimum the local registration.</li>
</ul>
</div>
<div class="phase">
<h3>Phase 2 — nsswitch + dns-sd CLI</h3>
<ul>
<li>Build + install <code>nss_mdns.so</code>; ship <code>/etc/nsswitch.conf</code> overlay with <code>hosts: files mdns dns</code>.</li>
<li>Build + install <code>/usr/bin/dns-sd</code>.</li>
<li>Boot test: ssh in, run <code>getent hosts <hostname>.local</code>, verify it resolves.</li>
</ul>
</div>
<div class="phase">
<h3>Phase 3 — libdispatch event loop (polish)</h3>
<ul>
<li>Replace <code>PosixDaemon.c</code>'s select loop with libdispatch sources. ~200 LOC. Optional polish for stack consistency.</li>
<li>Subscribe to configd's network events via SCDynamicStore (Mach RPC) so iface attach/detach events propagate without polling. Alternatively, subscribe directly to <code>hwregd</code>'s <code>IFNET/ATTACH</code> Mach notifications for a more direct path.</li>
</ul>
</div>
<div class="phase">
<h3>Phase 4 — gershwin integration</h3>
<ul>
<li>Workspace's File Viewer "Network" sidebar uses <code>DNSServiceBrowse</code> for SMB / file-share discovery.</li>
<li>Print dialog uses <code>DNSServiceBrowse</code> for <code>_ipp._tcp</code> / <code>_ipps._tcp</code>.</li>
<li>Service-announcement plists for any project-shipped daemon that wants to publish itself (sshd, web servers, etc.).</li>
</ul>
</div>
<div class="phase">
<h3>Phase 5+ — optional</h3>
<ul>
<li>notifyd integration for sleep/wake announcement re-runs.</li>
<li>Capsicum-based sandboxing.</li>
<li>DSO (DNS Stateful Operations) full validation pass.</li>
</ul>
</div>
<h2 id="open">12. Open questions</h2>
<div class="open-q">
<strong>Q1. Avahi coexistence.</strong> A user might have <code>net/avahi</code> 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 <code>avahi-daemon</code>. Document in README.
</div>
<div class="open-q">
<strong>Q2. Proxy mDNS for reliability.</strong> A laptop sleeping disappears from .local. Apple's macOS resolution: AirPort base stations / AppleTVs run a "Sleep Proxy Service" (<code>_sleep-proxy._udp</code>) 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.
</div>
<div class="open-q">
<strong>Q3. Default hostname registration.</strong> mdnsd announces <code>$(hostname).local</code> 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.
</div>
<div class="open-q">
<strong>Q4. Wide-area integration.</strong> Apple's wide-area Bonjour (BTMM) requires a DNS-SD-over-TLS uplink to a controlled-by-Apple resolver. We drop wide-area entirely. If users want Wide-Area Bonjour later (hosting their own DNS server with the right SRV records), they configure the daemon with custom upstream. Out of scope for this iteration.
</div>
<h2 id="refs">13. References</h2>
<ul>
<li>Apple source: <a href="https://github.com/apple-oss-distributions/mDNSResponder">github.com/apple-oss-distributions/mDNSResponder</a> (latest tag <code>mDNSResponder-878.70.2</code>).</li>
<li>FreeBSD ports: <code>net/mDNSResponder</code> already exists and uses <code>mDNSPosix</code>; reference for build / packaging.</li>
<li>Companion plans: <a href="freebsd-launchd-plan.html">launchd</a>, <a href="nextbsd-configd-plan.html">configd</a>, <a href="freebsd-asl-plan.html">asl</a>, <a href="freebsd-notifyd-plan.html">notifyd</a>, <a href="freebsd-disk-arbitration-plan.html">DiskArbitration</a>.</li>
<li>RFC 6762 (mDNS), RFC 6763 (DNS-SD), RFC 8490 (DSO).</li>
<li>Apache License 2.0: <a href="https://www.apache.org/licenses/LICENSE-2.0">apache.org/licenses/LICENSE-2.0</a>.</li>
</ul>
<hr style="margin-top: 3rem; border: none; border-top: 1px solid var(--rule);">
<p style="color: var(--muted); font-size: .88rem; margin-top: 1rem;"><strong>Revised 2026-05-23.</strong> Refiled under the <code>freebsd-launchd-mach</code> (v2) Mach-IPC track: <code>mDNSPosix</code> remains the kqueue+sockets protocol engine; client API surface mirrors Apple's <code>dnssd.defs</code> MIG IDL; network-state events sourced via Mach RPC from the in-repo MIG-served configd at <code>com.apple.SystemConfiguration</code>.</p>
</body>
</html>