v2.0.0
Breaking change. v1.0.0 implemented the spec wrong: it wrote a text file of ip:sha256hash pairs. The list "formatted as ip:hash" is an ipset set of type hash:ip — the kernel facility. v2.0.0 is that.
The app now writes no files at all. The kernel set is the storage.
How it works
ipset -exist restore <<< create dns_clients hash:ip family inet hashsize 1024 maxelem 65536 timeout 10800
add dns_clients 192.168.1.5 timeout 10800
- Every incoming DNS request on every transport protocol is intercepted through the official
IDnsRequestControllerinterface — UDP, TCP, DoT (Tls), DoH and DoH3 (Https), DoQ (Quic) and the PROXY protocol variants. - Each new query re-adds the client. Thanks to the global
-existflag, ipset does not error on a duplicate — it resets the timeout of the existing element. That is the refresh behaviour. - Idle clients are expired by the kernel itself. Entries live in the kernel, so restarting the DNS server does not lose them.
- Default timeout is 3 hours. Nothing is filtered out by default — every request from every client is tracked.
Highlights
- Batched writes. A process per DNS query is not viable, so pushes of the same client are coalesced (60 s window, far below the 3 h timeout) and a whole batch is fed to a single
ipset restoreprocess per tick. - IPv4 and IPv6 through separate sets (
dns_clients/dns_clients6), since an ipset set has a fixed address family.::ffff:a.b.c.dis unwrapped into plain IPv4; link local scope ids are stripped. - Resilient. If ipset is missing or the process lacks
CAP_NET_ADMIN, the error is logged once, entries stay queued, the setup is retried, and a failed batch is requeued rather than dropped. hash:netmode to aggregate clients into/24,/64, … instead of single addresses.dryRunlogs the exact ipset commands without executing them, so the config can be validated anywhere.- Never interferes. The app always returns
Allow; any internal failure is caught and cannot affect DNS resolution. - Set names, type, timeout, create parameters, batching, network and protocol filters and log verbosity are all in
dnsApp.config, and saving it hot reloads the app.
Requirements
- Technitium DNS Server v15.3 or later (app API
DnsServerCore.ApplicationCommon10.1, .NET 10). Verified against v15.4. - Linux with the
ipsetbinary installed (apt install ipset). - The DNS server process must be root or hold
CAP_NET_ADMIN. In Docker:--cap-add NET_ADMIN; note the official image does not ship theipsetbinary.
Surviving a reboot is out of scope for the app and is handled by ipset save / ipset restore (ipset-persistent, a systemd unit, etc.).
Install
- Download
IPCollectorApp-v2.0.0.zipbelow. - DNS server web console -> Apps -> Install, upload the zip.
- Open Config to adjust the settings, then Save.
- Verify on the host:
ipset list dns_clients.
Upgrading from v1.0.0
The config format changed completely; the old storage, hashing and output sections are gone. Delete the old dnsApp.config (or let the app rewrite it) and re-apply your settings. The old ip-list.txt and ip-state.json files in the app folder can be removed.