# DNS that works on any network (zev dns)

> zev dns finds which DNS resolvers really work from your network and how (encrypted, another port, TCP, through your proxy), throws away a censor's answers, checks DNSSEC signatures itself, and falls back to the network's own DNS so names never stop.

Source: https://zaryn.xyz/docs/en/dns

Every website starts with a DNS question: "what is the address of github.com?". On a filtered network that question goes wrong in quiet ways: the answer is a lie (one address, such as `10.10.34.36`, for every blocked site), encrypted DNS is cut, the network's own DNS is slow or down. Programs just say "can't connect".

`zev dns` measures every well-known resolver, every way it can be asked, from where you are; tells you what the network does to DNS; and, when you turn it on, answers this computer's names through the best few. It checks DNSSEC signatures itself, so a forged answer for a signed name is caught whichever way it came. If they all fail, it asks the network's own DNS: zev dns never leaves you without names.

```bash
zev dns check          # what this network does to DNS, and what gets around it
sudo zev dns on        # this computer's names through the best ways (Linux)
zev dns status         # what it uses now, and how it's doing
sudo zev dns off       # back exactly as it was
```

```diagram
type: flow
title: How zev dns finds a way, for every resolver
---
Encrypted | DNS over HTTPS and DNS over TLS, straight to the resolver's address (no name to look up first) | good | 🔒
~ the handshake is cut
Other names | The same resolver with every name its certificate has: a filter often knows only the main one | | 🔁
~ still cut
Split hello | The TLS hello in two records, in small pieces, so a filter can't read the resolver's name in it | | ✂️
~ still blocked
Another port | Plain DNS on ports some resolvers also answer on (OpenDNS 443, Quad9 9953), and over TCP | warn | 🚪
~ nothing direct works
Your proxy | DNS over HTTPS through zev proxy, when `zev proxy share` is on | accent | 🛰️
~ everything fails
The network's DNS | Asked last, as if zev dns weren't there: names keep working | dim | 🏠
```

## Start in one minute

```diagram
type: cards
title: Honest DNS in three commands
---
1️⃣ | Look | zev dns check: what this network does to DNS (lies, blocks) and which ways still tell the truth, in about 15 seconds
2️⃣ | On | sudo zev dns on: this computer's names through the best few, the network's DNS behind them, signatures checked
3️⃣ | See | zev dns status, and zev dns why <name> for any one name
```

## Every command

| Command | Example | What it does |
|---|---|---|
| `check` | `zev dns check` | what this network does to DNS, and what gets around it |
| `bench` | `zev dns bench` · `zev dns bench --for private` | every resolver, every way, measured from here; the best for each use |
| `why` | `zev dns why twitter.com` · `zev dns why example.com AAAA` | what each resolver says about one name, who lies, which are signed |
| `verify` | `zev dns verify www.wikipedia.org` · `zev dns verify example.com --json` | which resolvers gave the real site, proven by each address's HTTPS certificate |
| `on` | `sudo zev dns on` · `sudo zev dns on --for adblock` | this computer's names through zev dns (Linux), checked that the system really asks it |
| `off` | `sudo zev dns off` | back exactly as it was |
| `status` | `zev dns status` | what it asks now, how fast, the cache, lies dropped, forged answers |
| `log` | `sudo zev dns log` · `sudo zev dns log -f` · `sudo zev dns log github` · `--json` | the names this computer asked, newest last, and how each was answered |
| `top` | `sudo zev dns top` · `sudo zev dns top --json` | the most asked, how they were answered, which way answered, what failed, which devices asked |
| `share` | `sudo zev dns share on` · `sudo zev dns share off` · `zev dns share` | phones, TVs and consoles on this network use it too |
| `use` | `sudo zev dns use family` | another use while on: balanced, fast, private, secure, family, adblock, gaming |
| `rule` | `sudo zev dns rule add corp.example system` · `sudo zev dns rule country de` | some names through the network's DNS, blocked, or one resolver |
| `sanctions` | `sudo zev dns sanctions on` | sanctioned developer services through a local relay resolver |
| `set` | `sudo zev dns set strict=on` · `set dnssec=off` · `set log=off` | strict, proxy, rebinding, dnssec, log, share, listen |
| `ls` | `zev dns ls` | the resolvers it knows, and what each is good for |
| `restore` | `sudo zev dns restore` | the system's DNS as before (what systemd runs if zev dns dies) |

## What does this network do to DNS? zev dns check

```bash
zev dns check
```

It asks some 20 resolvers about 90 ways at once, takes a few seconds, and needs nothing installed. On a filtered network it looks like this:

```text
This network's DNS
  ! 192.168.1.1 answers in 4 ms, but gives the censor's address for 4 of 4 blocked sites

On the way out
  ✗ something on this network answers DNS questions meant for other servers (an address with no DNS server answered 10.10.34.36)
  ✗ plain DNS on port 53: 19 of 19 resolvers' answers were the censor's

What gets through
  ✓ DNS over HTTPS (encrypted): 1 work
    dnssb DoH as dot.sb
  ✗ DNS over TLS (encrypted): blocked: the handshake is cut when the filter reads the resolver's name (9 of 13)
  ✓ plain DNS on another port: 4 work
    quad9 UDP :9953, quad9-unfiltered UDP :9953, opendns UDP :443, opendns-family UDP :443
  ✓ plain DNS over TCP: 15 work
  ✗ plain DNS (UDP 53): blocked

What zev dns would use (balanced)
  1. dnssb DoH as dot.sb  110 ms
  2. quad9 UDP :9953  125 ms
  3. opendns UDP :443  140 ms
  then this network's own DNS, if all of those fail
```

What it found there, and how:

- **Someone on the way answers.** zev asks an address where no DNS server runs (192.0.2.53, reserved for examples). Silence is normal; an answer means something between you and the internet answers DNS itself. Whatever addresses it hands out are this network's lies, and zev throws them away from then on, from any resolver.
- **Lies on port 53.** Every resolver's plain answer for a blocked site was the censor's, because the filter answered first.
- **What still gets through.** Here: DNS.SB's encrypted DNS with the second name on its certificate, plain DNS on ports other than 53, and TCP.

`zev dns check --json` gives the same for scripts and agents.

## Turn it on: sudo zev dns on

```bash
sudo zev dns on                 # balanced: fast, honest, encrypted when it costs little
sudo zev dns on --for private   # or another use, below
```

It starts a small resolver at `127.0.0.153` (a system service that starts with the computer) and points the system at it:

- **With systemd-resolved** (Ubuntu, Fedora, most desktops): a setting in `/run/systemd/resolved.conf.d/zev-dns.conf`, and the network's links stop being the default route for names. Domains a link asks for itself, a work VPN's for example, still go to that link.
- **Without it:** `/etc/resolv.conf` points at zev dns; the old file is kept and put back.

Then it **checks the system really asks it**: it looks up a name only zev dns can answer, the way every program does. If that fails, everything goes back as it was and it tells you so. Nothing is left half-changed.

The first measurement takes a few seconds; until then the network's own DNS answers, so names work from the first second.

## Names never stop

```diagram
type: sequence
title: One name, when things go wrong
lanes: A program | zev dns | Best ways | Network's DNS
alt: A program asks zev dns; it answers from its cache when it can; else it asks the best way, and if that is slow the next one too, and takes the first honest answer; if a censor's answer comes it is thrown away; if none of the best ways answer, the network's DNS is asked; if nothing answers, an expired copy is given
---
A program -> zev dns : github.com?
note zev dns : kept and still fresh? answered at once | good
zev dns -> Best ways : the fastest first; the next joins if it's slow
Best ways --> zev dns : 10.10.34.36 (a censor's answer, from the way)
note zev dns : thrown away: not an answer | warn
Best ways --> zev dns : 140.82.121.3
zev dns --> A program : 140.82.121.3
== If none of the best ways answer ==
zev dns -> Network's DNS : github.com?
Network's DNS --> zev dns : whatever it says
== If nothing answers at all ==
zev dns --> A program : the last answer it had, kept for up to 3 days
```

- **A race, not a queue.** The best way is asked first. If it hasn't answered by the time it usually would have, the second joins, then the third; the first honest answer wins. One slow resolver never makes you wait.
- **Ways that fail rest.** After three failures in a row a way is skipped for a while (5 s, then longer, up to 2 minutes), then tried again.
- **Answers too big for UDP.** A name with many addresses, or a signed zone's keys, doesn't fit in a UDP answer: the resolver sends it cut, and zev dns asks again over TCP. Where the network blocks TCP to that resolver, zev dns keeps two other ways for exactly this (the best TCP, DoT or DoH ways it measured that work here, such as AdGuard's TCP on port 5353) and asks them. `zev dns status` shows them with ◇ and how many answers came whole through them.
- **The network's DNS behind them.** If none of the best ways answer within 2.5 seconds, the network's DNS is asked: exactly what would have happened without zev dns. If the best ways stop answering altogether (an outage, a new block), zev dns asks the network's DNS first for a while, measures again, and goes back to the best ways as soon as one answers.
- **Expired copies.** When nothing answers at all, a name it answered before is answered from its old copy (for up to 3 days, with a 30-second TTL, as RFC 8767 suggests). Sites you already use keep opening during an outage.
- **Kept answers.** Answers are kept as long as their TTL says. A name asked often is fetched again just before it expires, so it never waits.
- **A new network, measured again.** zev dns notices when you change network (another Wi-Fi, a phone hotspot) and measures again, starting from what worked there last time. It measures every half hour anyway, and every two minutes while the best ways are failing.

## The best for what you need

| Use | What it picks |
|---|---|
| `balanced` (the default) | Fast and honest; encrypted when it costs little more than plain. Can be the network's own DNS, if it told the truth and is fastest |
| `fast` | The fastest honest answer, encrypted or not |
| `private` | Encrypted only (DNS over HTTPS or TLS), to resolvers that keep no logs and don't pass on part of your address to websites (ECS) |
| `secure` | Resolvers that block malware and phishing (Quad9, Cloudflare 1.1.1.2, AdGuard) |
| `family` | Malware and adult sites blocked (Cloudflare 1.1.1.3, AdGuard Family, OpenDNS FamilyShield, CleanBrowsing) |
| `adblock` | Ads and trackers blocked (AdGuard, Mullvad adblock) |
| `gaming` | The steadiest: the least difference between its usual and its slow answers |

```bash
zev dns bench                 # every way, measured from here, and the best for each use
zev dns bench --for gaming    # sorted for one use
sudo zev dns use private      # change while it's on
```

```text
✓ quad9 UDP :9953        122 ms    168 ms    17/17
✓ dnssb DoH as dot.sb    110 ms    265 ms    17/17    handshake 380 ms
✗ cloudflare UDP          31 ms     48 ms    17/17    lied 4× (censor's address)
· google DoT                -         -        0/1    TLS handshake cut
```

A way only counts if it **never lied**: a censor's address, a private address for a public name, or an answer for a name that doesn't exist (some networks invent those to show ads) puts it out. Speed is the median and the slowest tenth of its answers, so a resolver that is fast on average but sometimes stalls doesn't win. Measurements are kept per network, so a network you return to starts with what worked there.

`zev dns ls` lists the resolvers zev knows, what each speaks and what it's good for.

## Why this answer? zev dns why

```bash
zev dns why twitter.com
```

```text
twitter.com A, asked 22 ways at once

  ✓ cloudflare TCP            162.159.140.229  273 ms
  ✓ quad9 UDP :9953           162.159.140.229  118 ms
  ✗ local-relay UDP           10.10.34.36  51 ms ← the censor's address, not the site's
  ✗ network DNS 192.168.1.1   10.10.34.36  4 ms ← the censor's address, not the site's

  zev dns answers: 162.159.140.229  2 ms
```

Every resolver, the way that works best for it from here, side by side: who tells the truth, who blocks the name on purpose (`⊘`), who lies, and what zev dns itself answers. Give a type for other records: `zev dns why example.com AAAA`.

For a signed name, each answer is also checked against the signatures, with the keys asked from that same resolver the same way. `signed ✓` means its answer is proven; `forged` means it doesn't match, even when the address isn't one the censor is known for. Real lines from a filtered network, and the last one from zev's lab test, where a resolver forges cloudflare.com's address under its real signature:

```text
cloudflare.com A, asked 25 ways at once

  ✓ cloudflare UDP            104.16.132.229 104.16.133.229  109 ms signed ✓
  ✓ adguard-family UDP :5353  104.16.132.229 104.16.133.229  82 ms signed ✓
  ✓ cleanbrowsing-family UDP  104.16.132.229 104.16.133.229  118 ms
  ✗ liar UDP                  198.51.100.66  40 ms ← forged: the signature on cloudflare.com doesn't match cloudflare.com's keys

  cloudflare.com is signed (DNSSEC): an answer whose signatures check out from the root's key is the real one
```

A line with no mark gave the right address but no signatures to check (some resolvers, CleanBrowsing among them, drop them), or its keys didn't come back.

## Is this really the site? zev dns verify

DNSSEC proves an answer only for names whose owner signed them, and most names aren't signed. `zev dns verify` proves answers another way, one that works for **every site that speaks HTTPS**: it asks every resolver, every way, then opens HTTPS to each address it was given and asks for that name's certificate. A forger can make up an address, but not the site's certificate.

```bash
zev dns verify www.wikipedia.org
```

```text
www.wikipedia.org  37 ways asked; each address checked by opening HTTPS to it and asking for www.wikipedia.org's certificate

  The addresses
    ✓ 185.15.59.224    the site: a valid certificate for www.wikipedia.org (740 ms)
    ✓ 208.80.153.224   the site: a valid certificate for www.wikipedia.org (1066 ms)
    ✓ 198.35.26.224    the site: a valid certificate for www.wikipedia.org (1112 ms)

  The resolvers
    ✓ cloudflare UDP                      185.15.59.224
    ✓ quad9 UDP                           185.15.59.224
    ✓ dnssb UDP                           198.35.26.224
    · google DoH                          TLS handshake cut
    ✓ network DNS 192.168.1.1             185.15.59.224

  Every address that could be checked is really www.wikipedia.org.
```

When an answer is forged, verify says which, and exits with code 1:

```text
  The addresses
    ✓ 104.16.123.96    the site: a valid certificate for www.cloudflare.com (3 ms)
    ✓ 104.16.124.96    the site: a valid certificate for www.cloudflare.com (2 ms)
    ✗ 198.51.100.66    not the site: its certificate isn't for www.cloudflare.com

  The resolvers
    ✓ honest UDP                          104.16.124.96 104.16.123.96
    ✗ liar UDP                            198.51.100.66  ← not the site
    ✗ network DNS 192.168.1.1             198.51.100.66  ← not the site

  2 answer(s) weren't www.cloudflare.com: forged by the resolver, or on the way to it (plain DNS can be
  answered by anything in between). zev dns check finds the ways that answer honestly from here,
  and sudo zev dns on uses only those.
```

```diagram
type: sequence
title: Proving an answer with the site's own certificate
lanes: zev dns verify | Resolvers | The address
alt: zev dns verify asks every resolver for the name's addresses, then opens HTTPS to each address with the name and checks the certificate it shows; an address that shows a valid certificate for the name is the site, one that shows another name's is not, and a resolver is honest when its addresses are the site
---
zev dns verify -> Resolvers : www.cloudflare.com? (every one, every way)
Resolvers --> zev dns verify : 104.16.123.96 · 198.51.100.66
zev dns verify -> The address : HTTPS to 104.16.123.96, for www.cloudflare.com
The address --> zev dns verify : a valid certificate for www.cloudflare.com | good
zev dns verify -> The address : HTTPS to 198.51.100.66, for www.cloudflare.com
The address --> zev dns verify : a certificate for another name | bad
note zev dns verify : the resolver that gave 198.51.100.66 isn't honest | warn
```

```diagram
type: cards
title: What each mark means
---
✓ | The site | it showed a valid certificate for the name, signed by an authority this computer trusts | good
✗ | Not the site | another name's certificate, or a private address for a public name | bad
? | Couldn't tell | it didn't answer HTTPS from here (blocked, cut, or not a web site). Not counted as a lie |
· | No answer | that resolver or way gave no address |
```

`--json` gives each address's proof (`site`, `wrong_certificate`, `private`, `unknown`) and each resolver's verdict, for scripts and monitoring.

## Signed names: DNSSEC, checked here

Many names are signed (DNSSEC): the zone's owner signs every answer, the zone above vouches for its key, and so on up to the root, whose key zev has built in. A censor can hand out any address it likes, but it can't make the signature fit. zev dns checks every answer itself, from the root's key down, so a forged answer for a signed name is caught **whichever way it came**: from an encrypted resolver, from plain DNS on another port, or from the network's own DNS when nothing else answered.

```diagram
type: flow
title: Why cloudflare.com's address can be trusted
---
The root's key | Built into zev (IANA's KSK-2017 and KSK-2024): the one thing taken on trust | good | 🔑
~ signs
.com's DS | The root says which key .com uses | | 📜
~ matches
.com's keys | They sign everything in .com | | 🔐
~ signs
cloudflare.com's DS | .com says which key cloudflare.com uses | | 📜
~ matches
cloudflare.com's keys | They sign cloudflare.com's answers | | 🔐
~ signs
The address | 104.16.132.229, with its signature: proven | good | ✅
```

Each answer ends up one of four things:

```diagram
type: cards
title: What zev dns makes of an answer
---
✅ | Proven | signed, and every signature checks out from the root's key. Answered, with the AD bit for programs that ask for it
📭 | Not signed | proven unsigned: a zone on the way has no key above it (most names, twitter.com among them). Answered as before
❔ | Couldn't check | the keys didn't come (too big for UDP where TCP is blocked, a resolver that drops signatures), or the clock is off. Answered as before: never worse than without DNSSEC
⛔ | Forged | it doesn't match the signatures. Thrown away; the next way is asked, up to three; if every one is forged, the program gets SERVFAIL with the reason (Extended DNS Error 6)
```

What it means in practice:

- **"Forged" only when something is there and is wrong.** An answer whose signature doesn't fit, or an unsigned answer for a zone proven signed, from a resolver that speaks DNSSEC. Anything just missing (keys that don't arrive, a resolver that doesn't pass signatures on) is "couldn't check", and the name is answered as before. zev dns never stops a name only because DNSSEC couldn't be checked.
- **The keys come from the best ways**, through the cache, and only answers that carry signatures count for them. Keys stay in the cache as long as their TTL says (usually a day or two), so checking adds a few round trips the first time a zone is seen, then nothing. A key lookup that fails isn't asked again for 5 minutes.
- **Keys ahead of time.** Right after measuring a network, zev dns fetches the root's keys and those of the busiest top-level names (.com, .net, .org, .io, .dev and others), so the first signed name doesn't wait for them. `zev dns status` says so under *lately*: `dnssec keys ready: the root's, and 17 of 17 top-level names'`.
- **Programs that check for themselves** (they set the CD bit) get the answer as it came, unchecked. Programs that ask with DO get the signatures too; the others get the answer without them.
- **Office and home names** aren't checked: names under `.lan`, `.home.arpa`, `.local`, `.internal`, `.corp`, private address ranges' reverse names, and any name under a top-level name that doesn't exist in the public DNS. Rules that send names to `system` aren't checked either.
- **The clock**: signatures are valid between two dates. With the computer's clock badly wrong, signed names are "couldn't check" (not forged), with an hour's slack.

On a real filtered network (2026-10-26) every honest resolver's answer for cloudflare.com, ed25519.nl, nlnetlabs.nl and nic.cz was proven, with ECDSA, Ed25519, RSA, NSEC and NSEC3. The censor's injected answer for torproject.org (signed) carries no signature. `.org`'s keys are too big for UDP and TCP was blocked, so `.org` names were "couldn't check" through every resolver except AdGuard on port 5353, which answers over TCP. zev dns now keeps a way like that for answers too big for UDP, and fetches such keys through it (see [Names never stop](#names-never-stop)).

`zev dns status` counts both:

```text
  dnssec        412 answers proven by their signatures, 3 forged ones thrown away
```

and lists each forged answer under *lately*, with the way it came from.

It lists the ways it asks too, with the ones kept for big answers:

```text
  asking
    ● quad9 UDP :9953  41 ms ±6, 1290 answered, 2 failed
    ● cloudflare UDP  48 ms ±9, 980 answered, 0 failed
    ◇ adguard TCP :5353 (for answers too big for UDP; 37 so far)
    ○ network DNS 192.168.1.1 (the network's DNS, behind them)
```

## What this computer asks: zev dns log and top

With `zev dns on`, every name it answers goes into a short list: the time, the name, how it was answered, which way answered it, how long it took, and which device asked. It's kept in memory and in `/run` (root only, gone at reboot), and holds the last 2,000 names.

```bash
sudo zev dns log              # the last 30, newest last
sudo zev dns log -f           # as they come, like tail -f
sudo zev dns log github       # only names with github in them
sudo zev dns log -n 200 --json
```

```text
Names this computer asked  newest last
  10:05:32  www.wikipedia.org                        A     asked         4 ms  quad9 TCP
  10:05:33  github.com                               A     asked         5 ms  quad9 TCP
  10:05:33  www.wikipedia.org                        A     cache         0 ms
  10:05:33  api.github.com                           A     asked         4 ms  quad9 TCP
  10:05:38  github.com                               A     cache         0 ms  from 192.168.1.20
```

```diagram
type: cards
title: How a name was answered
---
asked | Asked out | through the way named beside it | good
cache | From the cache | no question went out: no time, and nothing for anyone to see |
kept copy | An expired copy | nothing answered, so a recent answer kept the name working | warn
blocked | Blocked | by one of your rules, or the use you chose (family, adblock) | warn
forged / failed | Thrown away / no answer | a forged DNSSEC answer, or nothing answered at all | bad
```

`zev dns top` adds it up: how the names were answered (and how much the cache saved), the most asked sites, which way answered how many and how fast, what was blocked, forged or failed, and which devices asked.

```bash
sudo zev dns top
```

```text
The last 6 names (over 1 min)

  how they were answered
    asked            4   66%
    cache            2   33%
    a name asked out took 3 ms on average; one from the cache takes none

  most asked
    github.com                               3
    wikipedia.org                            2

  answered by
    quad9 TCP                                4  3 ms average

  asked from
    127.0.0.1                                5  (this computer)
    192.168.1.20                             1
```

Don't want any list kept? `sudo zev dns set log=off`, and the file is removed.

## For the whole home: zev dns share

Phones, TVs and consoles can't run zev, and they suffer the same lying or broken DNS. `sudo zev dns share on` lets them use this computer's zev dns: the honest resolvers, the cache, the fallback, your rules and DNSSEC checks, for every device on the network.

```bash
sudo zev dns share on
```

```text
● zev dns answers the local network too, on 192.168.1.34

  On a phone, TV or console: in its network settings, set DNS to 192.168.1.34 (manual).
  For every device at once: put 192.168.1.34 as the DNS server in the router's DHCP settings.
  Only private addresses (192.168.x, 10.x, 172.16–31.x) are answered, so it's never open to the internet.
  sudo zev dns share off stops it; zev dns top shows which devices asked.
```

```diagram
type: flow
title: The phone asks the laptop; the laptop asks the honest resolvers
---
Phone | DNS set to 192.168.1.34 | | 📱
~ a name
This computer | zev dns: cache, rules, DNSSEC, the honest ways | accent | 💻
~ only what isn't cached
Resolvers | the ways that answer honestly from this network | good | 🌍
```

It listens on every local network address this computer has (Wi-Fi, cable), not on Docker's or a VPN's, and follows them: join another Wi-Fi and it moves. Questions from anything but a private address get no answer at all, so it can't be used as an open resolver from the internet. `zev dns share` shows where it listens; `sudo zev dns share off` stops it. If a firewall runs here, allow port 53 from the local network.

## Rules: some names elsewhere

```bash
sudo zev dns rule add corp.example system     # a work domain through the network's DNS
sudo zev dns rule add ads.example block       # 0.0.0.0 for it and every name under it
sudo zev dns rule add example.org quad9       # one name through one resolver
sudo zev dns rule country de                  # a country's sites (.de) through the network's DNS
zev dns rule ls
sudo zev dns rule rm corp.example
```

A rule covers the name and everything under it; the longest matching rule wins. A country's own top-level name to `system` sends its sites to the network's own DNS: in that country they answer faster from inside, and some only answer inside.

### Sanctions resolvers

Some services refuse visitors from some countries (Docker Hub, parts of Google's developer sites, JetBrains, HashiCorp…). In those countries, local resolvers answer those names with the address of their own relay, which passes you on.

```bash
sudo zev dns sanctions on
```

sends only those services' names to the best of them; everything else stays on the best ways. Know what this means: the relay sees which of these services you use, and these resolvers are inside the country, so its filter's lies apply to them (`zev dns why` shows it). It is off until you turn it on.

## Settings

```bash
zev dns set                     # see them
sudo zev dns set strict=on      # never the network's DNS: no answer rather than an unencrypted one
sudo zev dns set proxy=off      # don't use zev proxy share for DNS
sudo zev dns set rebinding=on   # allow private addresses for public names
sudo zev dns set dnssec=off     # don't check signatures here
sudo zev dns set log=off        # keep no list of the names asked
sudo zev dns set share=on       # the same as sudo zev dns share on
sudo zev dns set listen=127.0.0.153:53
```

- **strict**: for privacy. With it, when the best ways fail, a name gets no answer instead of going to the network's DNS. Off by default, because names that stop are worse for most people.
- **dnssec** (on by default): signatures checked here, forged answers for signed names thrown away. See [Signed names](#signed-names-dnssec-checked-here).
- **rebinding protection** (on by default): an answer with a private address (192.168.x, 10.x…) for a public name is thrown away. That is either a lie or an attack on the devices in your home (DNS rebinding). Rules to `system` aren't affected, so office names keep working.
- **log** (on by default): the last 2,000 names asked, kept in memory and in `/run/zev-dns/queries.jsonl`, readable by root only and gone at reboot. Off, nothing is kept and the file is removed.
- **share** (off by default): see [For the whole home](#for-the-whole-home-zev-dns-share).
- Settings are picked up within seconds; nothing restarts.

## With zev proxy and zev vpn

- When `zev proxy share` is on, zev dns also tries DNS over HTTPS **through your proxy**: when nothing direct works honestly, that still does.
- When `sudo zev vpn on` is on, the VPN's own DNS goes through the tunnel; zev dns leaves the VPN's link alone and both answer honestly.

## Off, crashes and reboots

```bash
sudo zev dns off                          # stop it, and put back exactly what it changed
sudo zev dns restore                      # only the system's DNS settings back (what systemd runs if zev dns dies)
zev dns serve --listen 127.0.0.1:5353     # the same server in this terminal, on any port, changing nothing else
dig @127.0.0.1 -p 5353 example.com        # ask it
```

stops the service and puts back exactly what it changed: the resolved setting, the links it changed (and only those) or the old `/etc/resolv.conf`.

It is built so a failure can't leave the computer without DNS:

- If zev dns stops for any reason, even killed, systemd puts the system's DNS back right away (`zev dns restore`), then starts it again.
- The resolved setting lives in `/run`: a reboot always clears it, and zev dns sets it again when it starts.
- `sudo zev dns on` checks the system really gets names through it, and undoes everything if not.

## How it tells a lie

| Sign | What zev does |
|---|---|
| An address a censor hands out (such as `10.10.34.x`), or one an address with no DNS server answered with | The answer is thrown away; plain UDP keeps listening for the real one |
| A private address for a public name | Thrown away (rebinding protection) |
| An answer for a name that can't exist (a random name under example.com) | The resolver is not used |
| 0.0.0.0 for an ordinary name | The resolver blocks names on purpose: counted, fine for `adblock` and `family` only |
| The letters of the name in another case than asked (DNS 0x20) | A forged answer: thrown away, for resolvers known to keep the case |
| For a signed name, an answer that doesn't match its DNSSEC signatures | Thrown away, the next way asked; SERVFAIL if every way's answer is forged |

## What each way shows

- **DNS over HTTPS / TLS**: the network sees only that you talk to a resolver, not which names. zev pads questions to 128 bytes (RFC 8467) so their length doesn't give the name away.
- **Plain DNS, any port, UDP or TCP**: the network can see every name. It's honest (zev checks), not private.
- **The resolver** sees your names either way; the `private` use picks resolvers that keep no logs and don't pass on your address.

## Limits

- `sudo zev dns on` is for Linux for now. On macOS, run `sudo zev dns serve --listen 127.0.0.1:53` and set the system's DNS to 127.0.0.1. `check`, `bench` and `why` work on Linux and macOS.
- Resolvers are reached over IPv4; names with IPv6 addresses (AAAA) are answered normally.
- DNSSEC is checked for the algorithms in use today (RSA/SHA-1, RSA/SHA-256, RSA/SHA-512, ECDSA P-256 and P-384, Ed25519); a zone signed only with others (Ed448, the old DSA and GOST) counts as unsigned. NSEC3 with more than 150 iterations counts as unsigned too (RFC 9276).
- Plain DNS over TCP opens a connection per question, so TCP ways rank below the others.
