milestone 25: client names learned over reverse dns
Gates / test (push) Successful in 2m58s
Gates / frontend (push) Successful in 3m57s
Gates / test-aarch64 (push) Successful in 8m20s
Gates / package (push) Successful in 7m27s
Gates / container (push) Successful in 17s
CI / gates (push) Successful in 19m9s

This commit is contained in:
2026-08-15 11:18:44 +02:00
parent c428bc2398
commit 3c794b645b
20 changed files with 2638 additions and 36 deletions
+38
View File
@@ -251,6 +251,34 @@ Known clients with a fixed group assignment.
Consumed by the filter engine's exact address-to-group lookup
(`src/filter/matcher.zig`).
#### Learned names
A client row that carries no name of its own can still show one. Every flush
pass the client tracker takes at most 16 unnamed rows that are due, builds each
address's reverse name (`192.168.1.10` becomes `10.1.168.192.in-addr.arpa`), and
matches it against the declared [`forward_zones`](#forward_zones). On a match it
sends one PTR query to that zone's resolver and stores the answer as the row's
*learned* name. A row becomes due again 24 hours after the resolver answered or
returned NXDOMAIN, and 1 hour after any other outcome — no covering zone, a
transport failure, or a reply nxdns rejected — so declaring the missing zone or
fixing the resolver shows names within the hour. This
requires a conditional forward zone that covers the LAN's reverse space — for
example `168.192.in-addr.arpa` pointed at the router. Without such a zone nxdns
sends the reverse name to nobody, and the row stays unnamed.
The PTR query goes to the resolver of the declared zone, wherever the operator
pointed it; nxdns does not second-guess that declaration, and it cannot stop a
LAN resolver from forwarding the query onward.
A learned name is runtime state, like `last_seen`:
- A hand-typed `name` always wins, and a named row is never asked about again.
- Learned names never appear in `nxdns export`, and `nxdns import` never sets
one.
- Each row refreshes once a day. A device rename or a DHCP lease change can
therefore display a stale name for up to 24 hours, until the next refresh
overwrites it or the router reports no name and nxdns clears it.
### client_prefixes
Group assignment by CIDR prefix, for clients without an exact entry.
@@ -395,6 +423,16 @@ name would be a bootstrap problem. Matching is longest suffix
(`src/local/forward_client.zig`) with `upstream.read_timeout_ms` as the read
deadline.
Reverse zones are declared the same way, and one is the prerequisite for
[learned client names](#learned-names):
```zig
.forward_zones = .{
.{ .zone = "lan", .resolver = "udp://192.168.1.1:53" },
.{ .zone = "168.192.in-addr.arpa", .resolver = "udp://192.168.1.1:53" },
},
```
## Password and hash
Both fields are optional, and the difference between *absent* and *empty* is the