Asking DNS
packages/dns gathers evidence and never judges it. Here's who it asks, what it asks them, and how it knows who to ask.
Why ask two kinds of server?
- Asks each nameserver directly with its own Resolver and setServers([ip])
- So if a provider's servers disagree, we can see it
- IPv4 first, then IPv6. 2.5 seconds per address, 2 tries
- The answer is the truth, right now
What gets asked
| Name | Catches | Asked of |
|---|---|---|
| _prufy-challenge.mail.example.com | The record itself | Every server |
| _prufy-challenge.mail.example.com.example.com | The domain typed twice | Up to 2 authoritative |
| mail.example.com | The host field left blank | Up to 2 authoritative |
| example.com | The host field left blank | Up to 2 authoritative |
| prufy-challenge.mail.example.com | The underscore left off | Up to 2 authoritative |
| _prufy-challenge.example.com | The subdomain left off | Up to 2 authoritative |
Finding the zone
The engine needs to know which zone a name is in, so it can tell you what to type and who to ask. It walks up from the name until something answers with nameservers. This runs on every check, because nameservers change.
Who's the provider?
Nameserver hostnames give the provider away. The catalog maps those patterns to each provider's own field names and steps.
- Open Cloudflare and click your domain.
- In the menu, go to DNS, then Records.
- Click Add record and choose TXT.
A query never rejects
One timeout shouldn't sink a whole check, so every DnsSource returns its failure as data:
interface DnsAnswer {
rcode: DnsRcode | null // null means no response arrived
answers: readonly string[]
cnameChain: readonly string[]
ad: boolean | null // DNSSEC flag, null if the source can't say
serverIp: string | null
error: LookupErrorKind | null // timeout, servfail, refused, network...
}
A wrinkle found by looking at real data
Long TXT values get split into several strings of up to 255 characters, meant to be joined with nothing between them. The two public resolvers report this differently, which only showed up by recording real responses.
"data": "v=DKIM1; k=rsa; p=MIIB...GqMo..."
"data": "\"v=DKIM1; k=rsa; p=MIIB...\" \"GqMo...\""
A test checks that one real multi-string record reads identically through both parsers.
Faking DNS for tests and demos
Tokens are random, so a fixture can't hard-code a right answer. Record values are templates instead, filled in for each check. Set DNS_MODE=fixtures and every failure can be staged on demand.
| Placeholder | Renders as |
|---|---|
{{expected}} | The right value |
{{expected_lookalike}} | One character swapped for a lookalike |
{{expected_truncated}} | Cut off |
{{previous}} | An older value for this domain |
{{other}} | A different, well-formed code |
Records can also be limited by audience and by check number, which is how "propagating, then verified" is staged. See packages/dns/fixtures/scenarios.json.