fix: Windows same-host mDNS discovery + surface received mDNS queries in diagnostics - #372
Merged
Merged
Conversation
Adds the "mDNS queries received" section to the diagnostics report, listing each query the responder saw with its source, the QU bit and whether the reply went out unicast, multicast, both or not at all. This is the missing half of diagnosing "the trainer app on this machine can't find BikeControl". A same-host querier appears with the advertised address as its source, so the report now distinguishes three very different situations: the app never queried us at all, it queried for the service (PTR/SRV — the browse step), or it queried for our hostname (A — the resolve step). Empty on iOS, where the OS responder handles queries and never reports them to us. Picks up the prop-side fix for the Windows shared-port QU delivery problem. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PanTG9kRBi4QTQ2psvpTxP
Contributor
There was a problem hiding this comment.
Pull request overview
Adds responder-side visibility into incoming mDNS queries to help diagnose same-host (Windows) discovery failures, and validates the new diagnostics output via tests.
Changes:
- Extend
DebugDiagnosticsto capture and render recent mDNS queries (source, QU/QM, questions, and reply mode). - Populate
recentQueriesfrom the responder backend during diagnostics gathering. - Add tests covering query rendering and the empty-log case.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| lib/services/debug_diagnostics.dart | Adds recentQueries to diagnostics, populates it when running the responder backend, and renders a new “mDNS queries received” section. |
| test/services/debug_diagnostics_test.dart | Adds unit tests ensuring the new diagnostics section renders query entries and handles empty logs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+180
to
+191
| b.writeln(' mDNS queries received:'); | ||
| if (recentQueries.isEmpty) { | ||
| b.writeln(' (none)'); | ||
| } else { | ||
| for (final q in recentQueries) { | ||
| final at = q.at.toIso8601String().split('T').last.split('.').first; | ||
| b.writeln( | ||
| ' $at ${q.source}:${q.sourcePort} ${q.wantsUnicast ? 'QU' : 'QM'} ' | ||
| '${q.questions.join(', ')} → ${q.reply}', | ||
| ); | ||
| } | ||
| } |
Comment on lines
+117
to
+122
| test('says so when nothing has queried us', () { | ||
| // The decisive line for "the trainer app on this machine cannot see | ||
| // BikeControl": if no query ever arrived, the problem is upstream of our | ||
| // responder, not in how we answer. | ||
| expect(withQueries(const []).toText(), contains('(none)')); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Trainer apps on the same Windows machine as BikeControl frequently never see the advertisement, while the identical advertisement is found without trouble from another machine. Roughly two dozen reports.
Root cause (prop side)
Our mDNS responder answered a QU (unicast-response) question by unicast only. On Windows both processes hold
0.0.0.0:5353throughSO_REUSEADDR, and Microsoft documents that delivery to such a shared port is non-deterministic — "the application will be unable to determine which of the two sockets received specific packets sent to the 'shared' port" — with one documented exception: "If two sockets are bound to the same interface and port and are members of the same multicast group, data will be delivered to both sockets."So the answer was a coin flip. macOS is unaffected because one daemon owns 5353 and fans answers out to every Bonjour client. It also explains the field workaround of running
tnc <host>.localfirst: that elicits a plain QM query, whose multicast answer every socket in the group receives.RFC 6762 §5.4 already prefers multicast here, so we were violating a SHOULD in the one way that is fatal on Windows.
Fix lives in OpenBikeControl/prop#1; this PR bumps the submodule.
This repo
Adds the "mDNS queries received" section to the diagnostics report, listing each query the responder saw with its source, the QU bit, and whether the reply went out unicast, multicast, both or not at all.
This is the missing half of diagnosing "the trainer app on this machine can't find BikeControl". A same-host querier shows up with the advertised address as its source, so the report now distinguishes three very different situations:
PTR/SRVqueriesA bikecontrol-xxxx.localqueriesEmpty on iOS, where the OS responder handles queries and never reports them to us.
Still unconfirmed
Whether the trainer app's Windows browse actually sets QU, and whether its failing step is the browse or the resolve. A separate report implicates Tailscale's DNS override, which breaks
.localthroughGetAddrInfo;INSTRUCTIONS_WINDOWS_IPV6.mdfits that side too. The new query log is what settles it — left that file untouched until a reporter's log comes back.Tests
flutter analyze lib/mdnsclean.screenshot_test.dartfails 26/32 in isolation, and the snapshot tests fail on the knownMissingPluginException ... com.llfbandit.app_links/eventsco-run flake. Neither references any changed code.Not included
No
CHANGELOG.mdentry — 6.3.0 is already cut and I didn't want to presume a 6.4 heading or a patch note.🤖 Generated with Claude Code
https://claude.ai/code/session_01PanTG9kRBi4QTQ2psvpTxP