A Technitium DNS Server app that intercepts every incoming DNS request on
all transport protocols and adds the client IP address into a Linux kernel ipset of type hash:ip.
# ipset list dns_clients
Name: dns_clients
Type: hash:ip
Header: family inet hashsize 1024 maxelem 65536 timeout 10800
Members:
192.168.1.5 timeout 10794
10.0.0.14 timeout 10800
ipset -exist restore <<< create dns_clients hash:ip family inet ... timeout 10800
add dns_clients 192.168.1.5 timeout 10800
The set itself is the storage — the app keeps no list on disk. Every new query from a client re-adds it, and
because of the -exist flag ipset resets the timeout of the existing element instead of erroring. Clients
that go quiet are expired by the kernel. Entries live in the kernel, so restarting the DNS server does not lose
them; the startup create is a no-op when the set already exists.
Spawning a process per query is not viable, so pushes are coalesced (refreshIntervalSeconds, 60 s by default,
far below the 3 h timeout) and handed to a single ipset restore process per flush tick. If ipset is
unavailable, entries stay queued and the setup is retried — nothing is silently dropped.
- Every protocol. Uses the official
IDnsRequestControllerinterface, which the DNS server calls for every request before processing: UDP, TCP, DoT (Tls), DoH and DoH3 (Https), DoQ (Quic), plus the PROXY protocol variants. - Never interferes. The app always returns
Allow; any internal failure is caught and logged and cannot affect DNS resolution. - IPv4 and IPv6 through separate sets (an ipset set has a fixed address family), with
::ffff:unwrapping and link local scope id handling. - Tracks everything by default — no networks, families or protocols are filtered out unless you say so.
- Config driven. Set names, type, timeout, create parameters, batching, filters and log verbosity all live
in
dnsApp.config; saving it hot reloads the app. hash:netmode for per-subnet tracking (/24,/64, …) instead of per-address.dryRunmode that logs the exact ipset commands without executing them.
- Technitium DNS Server v15.3 or later (app API
DnsServerCore.ApplicationCommon10.1, .NET 10). - 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, and note that the official image does not ship theipsetbinary.
On other platforms the app loads, warns once and does nothing; use dryRun to validate the config there.
- Download
IPCollectorApp-X.Y.zipfrom the releases page (or fromrelease/in this repository — the package is committed, nothing needs to be compiled). - DNS server web console → Apps → Install, upload the zip, name it e.g.
DNS IP Collector. - Open Config on the app to adjust the settings, then Save.
- Verify on the host:
ipset list dns_clients.
Every option, its default and its meaning are documented in src/IPCollectorApp/README.md
— that file is also shipped inside the app package and shown in the DNS console.
| Section | What it controls |
|---|---|
enableCollector |
Master on/off switch. |
ipset |
Binary path, push mode and arguments, set names, set type, timeout, create parameters, retries, dry run. |
tracking |
Refresh interval, flush interval, batch size, queue cap, IPv4/IPv6, hash:net prefixes, network and protocol filters. |
logging |
Verbosity and per-event log switches. |
Defaults: sets dns_clients / dns_clients6, type hash:ip, timeout 3 hours, refresh every 60 s, nothing
filtered out.
Requires the .NET 10 SDK. build.ps1 downloads the Technitium DNS
Server portable package to extract the reference assemblies into lib/, builds the project and produces the
app package in release/.
pwsh ./build.ps1build.ps1 build + package script
lib/ Technitium reference assemblies (fetched by build.ps1, not committed)
release/ ready to use app package (committed)
src/IPCollectorApp/
App.cs IDnsApplication + IDnsRequestController entry point
IpSetCollector.cs filters, write coalescing, batching, retry/requeue, set creation
IpSetClient.cs runs the ipset binary (restore via stdin, direct add, version probe)
CollectorConfig.cs typed view over dnsApp.config
DefaultConfig.cs config written when none exists
IpNormalizer.cs mapped IPv4 / scope id / prefix masking
AppLogger.cs leveled logging into the DNS server log
JsonHelper.cs tolerant JSON readers
dnsApp.config shipped default config
README.md app documentation shown in the DNS console
GNU General Public License v3.0 — required for compatibility with the GPLv3 licensed Technitium DNS Server app API.