Skip to main content

DNS

A set decides which resolver answers the domains it targets. Everything else keeps going to the resolver the client already chose. This page describes what b4 intercepts, what a set does with a query it matched, and the global DNS settings that apply to every set.

What b4 intercepts

TransportHow it reaches b4When
UDP port 53Queue rules in PREROUTING and OUTPUT, for requests (dport 53) and for replies (sport 53)Always, while b4 is running
TCP port 53A nat REDIRECT into a local listener, port 5453 by defaultOnly while at least one enabled set has a DNS server or a DoH URL, and Intercept DNS over TCP is on

Both cases cover traffic forwarded from the network and queries the router makes for itself. b4's own lookups carry a firewall mark that skips these rules, so a query b4 sends on a client's behalf never re-enters the queue.

b4 only sees DNS that is not encrypted

Everything on this page works on DNS to port 53. When a device resolves over DoH, DoT or DoQ by itself, the query leaves as TLS or QUIC to port 443 or 853, and b4 cannot read it: the set's resolver, its pins and its blocking never apply to that name, and a routing set learns no addresses from those answers.

The usual sources are a browser with its own secure DNS turned on (Firefox, Chrome), Android Private DNS, and a resolver configured as DoH on the device itself. To work with DNS through b4, switch encrypted DNS off on those devices and let them use the router.

An encrypted forwarder on the router is a different matter and does not get in the way. Devices still ask the router on plain port 53, b4 intercepts there, and only what b4 passes through reaches the forwarder.

How a set handles a query

The query name is matched against the set targets exactly like an SNI is, so a domain entry covers its subdomains and regexp: entries are supported. Literal entries are tried before regular expressions: a domain listed in one set is not taken over by a regexp:.* catch-all in another.

For a matched query b4 works through these steps in order:

  1. Blocking. If the set's routing mode is block, the query is answered with NXDOMAIN or dropped, according to the block action. See Blocking.
  2. Pinned addresses. If the name has a pin, b4 answers from the pin and stops. This happens whether or not the redirect below is enabled.
  3. Resolver. If the redirect is enabled and a resolver is configured, b4 resolves the name itself and answers the client directly. Otherwise the query is forwarded unchanged.
  4. The answer. Addresses from the answer are remembered for the client, and written into the set's IP set when routing is on.

Resolver types

Set the resolver on Sets → a set → DNS & Routing → DNS Redirect.

ModeConfig fieldWhat b4 does
Plain DNS (UDP)dns.target_dnsSends the query to that IP itself, optionally fragmented (Fragment DNS Queries) to get past DPI that reads domain names out of queries
DNS-over-HTTPSdns.doh_urlSends the query as an encrypted HTTPS request, POST first and GET if the server rejects POST

A DoH URL takes precedence: when both fields are filled, the DoH URL is used and the IP is ignored. The URL has to start with https://, which is checked when the configuration is saved.

DNS redirect in DoH mode

Sending one service to a resolver of its own

A common reason to redirect a single set is a service that refuses to work from your region. Two different things hide behind that, and only one of them is DNS.

The name resolves to the wrong place. Which address you get for a name depends on who asked: geo-aware DNS hands out the front end nearest to the resolver, and a resolver elsewhere gets a different answer. Some public resolvers go further and run their own front ends for a fixed list of services, answering with the address of a relay they operate, which reads the SNI and forwards the connection on. Either way the connection ends up on a path the service accepts, and pointing one set at such a resolver changes nothing for the rest of your DNS.

The service checks the address you connect from. Nothing a resolver returns changes your own address, so a check that happens after the connection is up (an account, a payment, an API key tied to a region) is unaffected by DNS. That needs a different exit: see traffic routing.

For the first case, make a set that targets only that service's domains, enable the redirect, and enter the resolver's DoH URL. Keep those domains out of a network-wide catch-all set, since the point is that only that service is treated differently.

note

A resolver that answers with its own relay addresses sees which of those names you look up and carries the traffic for them afterwards. That is a real trust decision. Point only the domains that need it at such a resolver, and keep general browsing on a resolver you would pick on its own merits.

Pinned addresses

A pin replaces what DNS hands out for a name, without a hosts file on every device. The field takes hosts file order, one line per address:

157.240.0.174 www.instagram.com
157.240.205.63 scontent.cdninstagram.com scontent-a.cdninstagram.com
  • The address comes first, then the names it should answer for.
  • A pin covers each name and its subdomains, and the longest matching entry wins.
  • A pin only applies to a name the set already targets. Pinning a name that no target of the set covers does nothing, and the interface warns about it and offers to add the name to the set's domains.
  • Only A and AAAA queries are answered from pins, and only with addresses of the matching family. An AAAA query against a name pinned to one IPv4 address falls through to the resolver.
  • Pinned answers are handed out with a 60 second TTL.
  • Pins are read even when Enable DNS Redirect is off, so a set can pin a few names and leave everything else with the client's resolver.

In the configuration file the same data is stored the other way round, as dns.pins, a name mapped to its addresses.

When the resolver fails

A resolver that times out or errors produces SERVFAIL for that query. b4 does not fall back to plain DNS and does not let the original query continue to its destination. A name that b4 took over either resolves through the configured resolver or does not resolve at all.

Global DNS settings

Settings → Core → DNS holds what applies to every set: the DNS-over-TCP transport, and the timeouts.

DNS settings

Why DNS over TCP is here

DNS normally travels over UDP. A resolver that cannot fit an answer into a UDP packet marks it truncated, and the client asks again over TCP, which is what happens with long address lists, DNSSEC records and some zone transfers. A few stub resolvers prefer TCP outright, and a client that wants to get around something watching UDP can go to TCP on purpose.

Those queries are ordinary DNS, and without interception they reach the resolver the client chose, so the set's resolver, its pins and its blocking are all skipped for them. Intercept DNS over TCP closes that path by redirecting TCP port 53 into b4, which then applies the same set handling as it does for UDP.

SettingConfig fieldDefaultMeaning
Intercept DNS over TCPsystem.dns.tcp_disabledon (false)With this off, a client that falls back to TCP reaches the upstream resolver and the set's DNS server is skipped
Listener portsystem.dns.tcp_port5453b4 listens for DNS over TCP on this local port, and a firewall rule sends connections aimed at port 53 to it. Nothing outside the router uses this port, clients keep addressing port 53 as before. Change it only if another program already holds 5453
Query timeoutsystem.dns.query_timeout_sec5How long to wait for the set's resolver before answering SERVFAIL. Applies to UDP and TCP alike
Idle timeoutsystem.dns.tcp_idle_sec30How long an idle DNS-over-TCP connection is held open for further queries
Read/write timeoutsystem.dns.tcp_io_sec10Deadline for a single query or answer on an established connection
Forward timeoutsystem.dns.tcp_dial_sec5How long to wait when forwarding an unmatched TCP query to the resolver the client chose

The configuration file only stores values that differ from the defaults, so a system.dns block is usually absent until one of these is changed.

note

TCP interception needs REDIRECT in the nat table. Where the kernel does not provide it, b4 logs a warning and DNS over TCP stays with the client's resolver for that address family. UDP interception is unaffected.

Sending every DNS query to DoH

A set that targets every domain turns the per-set redirect into a network-wide one. Import this on Sets → Import/Export:

{
"b4_version": "dev",
"name": "all DOH",
"tcp": { "dport_filter": "53" },
"udp": { "dport_filter": "53" },
"fragmentation": { "strategy": "none" },
"faking": { "sni": false },
"targets": { "sni_domains": ["regexp:.*"] },
"enabled": true,
"dns": {
"enabled": true,
"doh_url": "https://wikimedia-dns.org/dns-query"
}
}

regexp:.* matches every name, so every query that no other set claims is resolved over DoH, for every device on the network and for the router itself. The bypass strategies are all turned off, because this set exists to answer DNS and not to modify traffic.

Domains listed literally in another set keep going to that set's resolver, since literal entries are matched before regular expressions. Order the catch-all set last so it is easy to see which sets take precedence over it.

About the two dport_filter fields

They do limit the set: with udp.dport_filter at 53, the set is not applied to QUIC on port 443, and every port any set lists is also added to what b4 pulls into the queue.

What they do not do is switch DNS handling on. Port 53 is dispatched before any port filter is read, so the redirect works with both fields empty. Keeping them here is still sensible, since it stops a regexp:.* set from claiming every TLS connection as well. The one cost is that tcp.dport_filter at 53 also pulls TCP port 53 into the queue for the bypass strategies, which this set does not use.

What to expect

  • Local names stop resolving. Router hostnames, .lan names and anything else the router's own resolver serves are answered by the public resolver instead, which does not know them. Pin the few names that matter, or target the catch-all at a narrower expression.
  • One resolver becomes a single point of failure. Resolution fails closed, so while the DoH server is unreachable, name lookups fail across the network. A resolver reachable from your connection matters more than its feature list.
  • DNS over TCP follows along. As soon as this set exists, TCP port 53 is redirected into b4 too, so a client that retries over TCP gets the same answer rather than slipping past.

Reading the result

Every decision b4 makes about a query is recorded on the Traffic page and in the logs, with the protocol, the set, the domain and the client.

ResultMeaning
dns-doh-><host>Resolved over DoH through that server
dns-forward-><ip>Resolved by b4 against that plain DNS server
dns-passthroughMatched a set, but the set has no resolver configured, so the query was forwarded unchanged
dns-pinAnswered from a pinned address
dns-healAn answer had unreachable addresses replaced, by the set's IP block detection
dns-sinkholeAnswered with NXDOMAIN by a blocking set
dns-blockDropped by a blocking set
dns-servfailThe set's resolver did not answer in time
dns-bad-targetThe set's DNS server field does not hold a valid IP address, so the query was forwarded unchanged
dns-ipv6-disabledAn IPv6 query matched a set while IPv6 is switched off, so it was forwarded unchanged
note

In TUN mode, requests to port 53 are captured, but replies are only seen when b4 carries the whole default route. Redirects and pins work either way, since b4 produces those answers itself.