Local names, caching and blocking
Every office machine now uses the router as its DNS server, and the router answers in three different ways.
Local names
fileserver.office.lan is the reservation's name; guest.office.lan is the name the guest's machine sent with its DHCP request. Both resolve without any zone file: dnsmasq builds them from its leases. Because the clients were given office.lan as their search domain, the short name fileserver works too.
This also means any VLAN can look up any other VLAN's names. The firewall still decides who can connect, so a guest knowing the file server's address doesn't let it reach the server; but if names alone are sensitive, give guests a different DNS server with a per-VLAN DHCP option.
Forwarding and caching
www.example.com isn't local, so the router forwards the query to the ISP's resolver, gets the answer with a TTL of 300 seconds, and caches it. Asked again five seconds later, the router answers from its cache with a TTL of 295: the answer has five seconds less to live. The ISP's resolver log shows a single query for the name, from the router's public address. Every office machine shares that one cache, which makes repeated lookups instant and cuts the queries leaving the office.
Blocking
ads.example.com gets NXDOMAIN ("no such name") straight from the router, and never reaches the ISP's resolver: its log has no query for it. Blocking at the router works for every device on every VLAN, with nothing installed on them.
And, finally, the names work for what people actually do: a browser fetching http://fileserver.office.lan/ and http://www.example.com/.
root@staff:~# dig +short fileserver.office.lan; dig +short guest.office.lan; dig +short fileserver
10.10.30.10
10.10.20.162
10.10.30.10
root@staff:~# dig www.example.com | grep -E "status:|^www|SERVER"; sleep 5; dig www.example.com | grep -E "^www"
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18740
www.example.com. 300 IN A 198.51.100.10
;; SERVER: 10.10.10.1#53(10.10.10.1) (UDP)
www.example.com. 295 IN A 198.51.100.10
root@staff:~# dig ads.example.com | grep -E "status:|SERVER"
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 6335
;; SERVER: 10.10.10.1#53(10.10.10.1) (UDP)
root@isp:~# grep -E "query\[A\]" /tmp/dns.log
Nov 4 22:07:02 dnsmasq[43]: query[A] www.example.com from 203.0.113.2
root@staff:~# curl -s -o /dev/null -w "%{http_code}\n" -m 2 http://fileserver.office.lan/; curl -s -o /dev/null -w "%{http_code}\n" -m 2 http://www.example.com/
200
200- Lines 1–4: Names from the reservation and from a guest's own DHCP request, and the short name with the search domain.
- Lines 7–10: Forwarded, then cached: the TTL counts down from 300 to 295 between the two queries.
- Lines 13–14: Blocked: NXDOMAIN, straight from the router.
- Lines 16–17: The ISP's resolver saw one query for www.example.com, and none for the blocked domain.
- Lines 20–21: And the names work in a browser.
Check your understanding
Why does the second query for www.example.com show a TTL of 295, not 300?
- The router answered from its cache, where the answer has 5 seconds less to live.
- The ISP's resolver lowered it.
- dig rounds it.
- The client cached it.