This project provides a DNS proxy service built with:
Meziantou.Framework.DnsServerto accept queries from clientsMeziantou.Framework.DnsFilterto apply filter listsMeziantou.Framework.DnsClientto forward queries to multiple upstream DNS servers
Pipeline:
client -> access policy -> rate limit -> custom records -> filter -> cache/forward to remotes (in priority order) -> response to client
Default configuration:
- DNS listeners: UDP/TCP on port
5053, bound to loopback only (BindAddresses=127.0.0.1,::1) - Allowed client networks: empty, meaning every client that can reach a listener is allowed (loopback is always allowed)
- Web diagnostics UI on port
5080 - DNS over HTTPS listener: disabled by default (
DnsOverHttpsPort=0; endpoint path/dns-query) - DNS over TLS listener: disabled by default (
DnsOverTlsPort=0) - DNS over QUIC listener: disabled by default (
DnsOverQuicPort=0) - Filter list refresh interval:
00:30:00 - Block list cache folder: local application data under
meziantou/dnsproxy/block-lists - DNS cache durations: positive
00:05:00, negative00:05:00, maximum01:00:00 - DNS cache size:
10000entries (MaxCacheEntries) - Per-client rate limit:
600queries per minute (MaxDnsQueriesPerClientPerMinute) - Filter list download:
00:02:00timeout,67108864bytes maximum per list - DNSSEC validation: disabled by default (
DnssecValidationMode=None; useLocalto enable local validation) - Bootstrap DNS servers: Quad9 (
9.9.9.9,149.112.112.112,2620:fe::fe,2620:fe::9) and Cloudflare (1.1.1.1,1.0.0.1,2606:4700:4700::1111,2606:4700:4700::1001) - Default filter lists:
- AdGuard DNS filter (
https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt) - StevenBlack hosts (
https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts)
- AdGuard DNS filter (
- Custom DNS records:
localhost A 127.0.0.1andlocalhost AAAA ::1 - Remote DNS servers: Cloudflare H3, NextDNS DoQ, Quad9 DoQ, Cloudflare DoH, NextDNS DoH, and Quad9 DoH
- In-memory diagnostics history size:
10000entries (the page renders the 200 most recent; use/?limit=Nfor more)
Enabling secure listeners (DoH/DoT/DoQ):
- Set one or more of:
DnsProxy__DnsOverHttpsPortDnsProxy__DnsOverTlsPortDnsProxy__DnsOverQuicPort
- Configure certificate:
DnsProxy__CertificatePathDnsProxy__CertificatePassword
- Optional DoH path override:
DnsProxy__DnsOverHttpsPath
Notes:
- When
DnsOverHttpsPortis enabled, the DoH endpoint is served over HTTPS on that port. - When disabled (
0), the existing HTTP endpoint remains available onHttpPortfor diagnostics/testing.
Serving other machines:
- The DNS listeners bind to loopback by default. To serve other machines on the network, set the bind addresses
explicitly and restrict which clients may query:
DnsProxy__BindAddresses__0=0.0.0.0DnsProxy__BindAddresses__1=::DnsProxy__AllowedClientNetworks__0=192.168.1.0/24
AllowedClientNetworksaccepts CIDR ranges or bare addresses. When the list is empty, every client that can reach a listener is allowed, so it should be set whenever the proxy is not bound to loopback. Loopback clients are always allowed so the diagnostics page and the local DNS over HTTPS endpoint keep working.- Queries from a client outside the allowed networks are answered with
Refused.
DNSSEC:
- The client's DNSSEC-OK (DO) bit is forwarded to the upstream server, and DNSSEC records (
RRSIG,NSEC,NSEC3,DS,DNSKEY) are returned to the client, so a validating client can verify answers itself. - The
ADbit is not forwarded: the proxy does not validate signatures unlessDnssecValidationModeisLocal, so it does not report answers as authenticated.
Upstream failover:
- Upstreams are tried in
Priorityorder. A transport failure, aServerFailureor aRefusedresponse moves on to the next upstream;NoErrorandNameError(NXDOMAIN) are real answers and are returned immediately. - When every upstream is unhealthy, the last
ServerFailure/Refusedresponse is returned to the client.
Diagnostics:
- Open
/in the browser to inspect recent DNS requests/responses and client information. - The diagnostics listener is bound to loopback,
AllowedHostsis set tolocalhostto block DNS rebinding, and the "disable filtering" action requires an antiforgery token.
Parallel instances:
- Override settings using environment variables such as:
DnsProxy__DnsPortDnsProxy__HttpPortDnsProxy__BindAddresses__0DnsProxy__AllowedClientNetworks__0DnsProxy__FilterRefreshIntervalDnsProxy__FilterDownloadTimeoutDnsProxy__MaxFilterListSizeInBytesDnsProxy__BlockListCacheFolderPathDnsProxy__PositiveCacheDurationDnsProxy__NegativeCacheDurationDnsProxy__MaximumCacheDurationDnsProxy__MaxCacheEntriesDnsProxy__MaxDnsQueriesPerClientPerMinuteDnsProxy__DnssecValidationModeDnsProxy__CustomRecords__0__DomainDnsProxy__CustomRecords__0__TypeDnsProxy__CustomRecords__0__ValueDnsProxy__BootstrapDnsServers__0DnsProxy__Upstreams__0__Url
Custom records:
- Custom records are answered before filtering, cache, and upstream forwarding.
- Block lists and temporary filtering pauses do not affect custom records.
- Use
Valuefor one answer orValuesfor multiple answers of the same type. - Supported formats:
A:192.168.1.11AAAA:::1CNAME,PTR,NS: domain nameTXT: textMX:10 mail.sample.localSRV:10 5 443 service.sample.localCAA:0 issue letsencrypt.org