User Tools

Site Tools


design:dns

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
design:dns [2026/08/27 23:15] – Figures re-check applied: drop the dnsdb.info A record (it rotated between two runs an hour apart) and correct the Rapid7 guard's tuple count. Authored by Claude. karel.kubicek.claudedesign:dns [2026/08/27 23:21] (current) – Generic (fable) review applied. Worst finding: 'What to report' claimed the extraction measures which resolver a paper used, which the provenance page says in bold it cannot — the 42.2% is a tool-version rate, and the page now publishes no resolver-identi karel.kubicek.claude
Line 4: Line 4:
  
 <WRAP important> <WRAP important>
-**A DNS answer is a property of the query, not only of the name.** It depends on which resolver you asked, where you asked from, what your source port happened to be, and what was already in a cache. In a run on 2026-08-27 from one machine, Google, Cloudflare and Quad9 **did not all return the same addresses for 29 of the 75** Tranco top-100 names that had an address at all (38.7%), and for **8** of those no two of the three shared a single address. Another **25 of the 100** had no A record at the apex at all. Details and the script are in [[#Measure your own resolver dependence]].+**A DNS answer is a property of the query, not only of the name.** It depends on which resolver you asked, where you asked from, what your source port happened to be, and what was already in a cache. In a run on 2026-08-27 from one machine, Google, Cloudflare and Quad9 **did not all return the same addresses for 29 of the 75** Tranco top-100 names that had an IPv4 address at all (38.7%), and for **10** of those no two of the three shared a single address. Another **25 of the 100** have no A record at the apex at all. Details and the script are in [[#Measure your own resolver dependence]].
 </WRAP> </WRAP>
  
-Everything below about "the literature" is a claim about **seven venues** — CCS, IMC, NDSS, PETS, USENIX Security, TheWebConf and IEEE S&P, 2010–2026, **5,859** papers with extracted full text. EuroS&P, ACSAC, RAID, AsiaCCS, CHI and SOUPS are not in it, and neither is the DNS-operations literature (DNS-OARC, RIPE, IETF). A DNS venue survey would look different; see [[Literature:Corpus]] for what this corpus is and is not.+Everything below about "the literature" is a claim about **seven venues** — CCS, IMC, NDSS, PETS, USENIX Security, TheWebConf and IEEE S&P, 2010–2026, **5,859** extracted papers (full text is readable for 5,855 of them; the other 4 count as negatives in every full-text probe below). EuroS&P, ACSAC, RAID, AsiaCCS, CHI and SOUPS are not in it, and neither is the DNS-operations literature (DNS-OARC, RIPE, IETF). A DNS venue survey would look different; see [[Literature:Corpus]] for what this corpus is and is not.
  
 ===== Is this your page? ===== ===== Is this your page? =====
Line 84: Line 84:
 | **Rapid7 Open Data** (Sonar FDNS / RDNS) | 8 | **Still live**, contrary to what a 2022-era memory will tell you: ''sonar.fdns_v2'' last updated 2026-08-23, ''sonar.rdns_v2'' 2026-08-26. The **host has moved**: every path under ''opendata.rapid7.com'' now 301s to ''sonardata.rapid7.com''. | Bulk forward and reverse DNS over the IPv4 space. | | **Rapid7 Open Data** (Sonar FDNS / RDNS) | 8 | **Still live**, contrary to what a 2022-era memory will tell you: ''sonar.fdns_v2'' last updated 2026-08-23, ''sonar.rdns_v2'' 2026-08-26. The **host has moved**: every path under ''opendata.rapid7.com'' now 301s to ''sonardata.rapid7.com''. | Bulk forward and reverse DNS over the IPv4 space. |
 | **ICANN CZDS** / zone files | 3 | Current. Per-TLD application. | The authoritative list of registered names in participating zones. It is the denominator a "we resolved the top 1M" study does not have. | | **ICANN CZDS** / zone files | 3 | Current. Per-TLD application. | The authoritative list of registered names in participating zones. It is the denominator a "we resolved the top 1M" study does not have. |
-| **Active DNS Project**; **360 / 114DNS**; **Spamhaus**; **DNS Observatory** | 2 / 4 / — / 1 | Various. | Regional and vendor feeds; the Chinese feeds are the only route to some of that namespace. |+| **Active DNS Project**; **360 / 114DNS**; **DNS Observatory** | 2 / 4 / 1 | Various. | Regional and vendor feeds; the Chinese feeds are the only route to some of that namespace. Spamhaus's passive-DNS API and seven other one-off feeds fold into the 8-paper "other passive-DNS feeds" row above rather than getting their own. |
  
 ==== Where you ask from ==== ==== Where you ask from ====
Line 96: Line 96:
 ==== 1. The answer depends on which resolver you asked ==== ==== 1. The answer depends on which resolver you asked ====
  
-This is the oldest finding on the page and it has never stopped being true. Ager et al. {[ager2010_comparing]} sent the same queries to the local resolver, Google and OpenDNS from 60-plus vantage points in 2010 and found that //"the answers to the DNS resolvers differ in terms of subnets for approximately 2,000 out of our 10,000 host names. In half of these cases, the returned IP addresses even belong to different ASs and countries."//((Quoted with whitespace normalised: both the .cols rendering and a pypdf re-extraction of the PDF render the figures as 2 , 000 and 10, 000, and hyphenate dif- fer across a line break. Checked 2026-08-27; see [[provenance:design:dns]].)) Sixteen years later the run in [[#Measure your own resolver dependence]] gets **38.7%** not-identical on a 75-name comparable set, with **10.7%** of them sharing no address between any pair of resolvers.+This is the oldest finding on the page and it has never stopped being true. Ager et al. {[ager2010_comparing]} sent the same queries to the local resolver, Google and OpenDNS from 60-plus vantage points in 2010 and found that //"the answers to the DNS resolvers differ in terms of subnets for approximately 2,000 out of our 10,000 host names. In half of these cases, the returned IP addresses even belong to different ASs and countries."//((Quoted with whitespace normalised: both the .cols rendering and a pypdf re-extraction of the PDF render the figures as 2 , 000 and 10, 000, and hyphenate dif- fer across a line break. Checked 2026-08-27; see [[provenance:design:dns]].)) Sixteen years later the run in [[#Measure your own resolver dependence]] gets **38.7%** not-identical on a 75-name comparable set, with **13.3%** of them sharing no address between any pair of resolvers.
  
 Two mechanisms do most of it. CDNs and GeoDNS answer according to where they think you are, and **EDNS Client Subnet** (RFC 7871 — note it is **Informational**, not a standard) is how a public resolver tells them. Al-Dalky et al. {[aldalky2019_look]} measured how resolvers actually use it and found the behaviour is not uniform: //"3382 out of 4147 resolvers in the CDN dataset send 100% of their A and AAAA queries with an ECS option"//, and that //"103 recursive resolvers, or over half of all recursive resolvers we could study"//, //"don't control caching based on scope at all"// — they reuse a cached response outside the prefix it was scoped for — a caching bug that hands the wrong client the wrong answer. Anycast does the rest; [[Design:IP classification]] covers why geolocating an anycast address is meaningless. Two mechanisms do most of it. CDNs and GeoDNS answer according to where they think you are, and **EDNS Client Subnet** (RFC 7871 — note it is **Informational**, not a standard) is how a public resolver tells them. Al-Dalky et al. {[aldalky2019_look]} measured how resolvers actually use it and found the behaviour is not uniform: //"3382 out of 4147 resolvers in the CDN dataset send 100% of their A and AAAA queries with an ECS option"//, and that //"103 recursive resolvers, or over half of all recursive resolvers we could study"//, //"don't control caching based on scope at all"// — they reuse a cached response outside the prefix it was scoped for — a caching bug that hands the wrong client the wrong answer. Anycast does the rest; [[Design:IP classification]] covers why geolocating an anycast address is meaningless.
Line 134: Line 134:
 | **DoH** — DNS over HTTPS | RFC 8484, October 2018, Proposed Standard | 0 / 0 / 23 / 16 / 9 | The one the browsers deployed. The most-measured of the encrypted transports and still the default assumption. | | **DoH** — DNS over HTTPS | RFC 8484, October 2018, Proposed Standard | 0 / 0 / 23 / 16 / 9 | The one the browsers deployed. The most-measured of the encrypted transports and still the default assumption. |
 | **DoQ** — DNS over QUIC | RFC 9250, May 2022, Proposed Standard | 0 / 0 / 4 / 1 / 1 | Standardised, barely measured: **6** of the 244 mention it at all (10 across the whole 5,859). An open area, not a settled method. | | **DoQ** — DNS over QUIC | RFC 9250, May 2022, Proposed Standard | 0 / 0 / 4 / 1 / 1 | Standardised, barely measured: **6** of the 244 mention it at all (10 across the whole 5,859). An open area, not a settled method. |
-| **DoC** — DNS over CoAP | RFC 9953, **March 2026**, Proposed Standard | 0 / 0 / 0 / 0 / 0 | Five months old at the time of writing and **not measured in this corpus at all**. It targets constrained IoT networks, so it is unlikely to matter for a web crawl — but it is on the transport list now, and page that omitted it would be describing 2024. |+| **DoC** — DNS over CoAP | RFC 9953, **March 2026**, Proposed Standard | 0 / 0 / 0 / 0 / 0 | Five months old at the time of writing. The zero is a **run probe, not an assumption**: the phrase "DNS over CoAP" appears in no paper in the corpus, and the 20 that mention CoAP at all mean the IoT protocol (a bare DoC matches two papers and both are homographs — a US government department and a table symbol). It targets constrained networks, so it is unlikely to reach a web crawlit is here because table headed "what is actually current" that omitted a five-month-old standardised transport would be describing 2024. |
 | **ODoH** — Oblivious DoH | RFC 9230, June 2022, **Experimental** | 0 / 0 / 5 / 4 / 2 | Experimental in the RFC sense as well as the deployment sense. Do not describe it as a deployed default. | | **ODoH** — Oblivious DoH | RFC 9230, June 2022, **Experimental** | 0 / 0 / 5 / 4 / 2 | Experimental in the RFC sense as well as the deployment sense. Do not describe it as a deployed default. |
 | **DDR** — Discovery of Designated Resolvers | RFC 9462, November 2023, Proposed Standard (with DNR, RFC 9463) | 0 / 0 / 0 / 0 / 1 | The frontier, and the answer to "how does a client find an encrypted resolver at all". **Exactly one paper in this corpus measures it.** | | **DDR** — Discovery of Designated Resolvers | RFC 9462, November 2023, Proposed Standard (with DNR, RFC 9463) | 0 / 0 / 0 / 0 / 1 | The frontier, and the answer to "how does a client find an encrypted resolver at all". **Exactly one paper in this corpus measures it.** |
Line 168: Line 168:
 ===== Active DNS measurement is scanning ===== ===== Active DNS measurement is scanning =====
  
-Sending queries to resolvers you do not own is a scan, and it has a failure mode the rest of the scan branch does not: **an open resolver will amplify whatever you send it at somebody else.** Kührer et al. {[khrer2015_going]} //"find up to 20 million open resolvers in the weekly IPv4 scans in 2015"//, and **58 of the 244** papers here match a full-text probe for amplification or reflection (**15** of them say so in the title). If your measurement queries open resolvers, you are one misconfigured source address away from being the attack.+Sending queries to resolvers you do not own is a scan, and it has a failure mode the rest of the scan branch does not: **an open resolver will amplify whatever you send it at somebody else.** Kührer et al. {[khrer2015_going]} //"find up to 20 million open resolvers in the weekly IPv4 scans in 2015"//, and **58 of the 244** papers here match a full-text probe for amplification or reflection (**15** of them say so in the title or the slug). If your measurement queries open resolvers, you are one misconfigured source address away from being the attack.
  
-The DNS population states more about ethics than the corpus average on three of four fields, and **less** on the one that names an ethics board — which is the pattern you would expect from work that mitigates harm operationally and does not go to an IRB because it has no human subjects:+The DNS population states more about ethics than the corpus average on three of four fields, and **less** on the one that names an ethics board. A plausible reading is that this is work which mitigates harm operationally and mostly has no human subjects to take to an IRB — but 16 of the 244 are tagged ''interview-or-survey'' and 11 ''user-study'', so that is a tendency here, not a property:
  
 ^ Field ^ DNS population (241 empirical of the 244) ^ Whole corpus (5,118 empirical) ^ ^ Field ^ DNS population (241 empirical of the 244) ^ Whole corpus (5,118 empirical) ^
Line 196: Line 196:
 </WRAP> </WRAP>
  
-A DNS methods section is complete when a replicator can answer all of these. The extraction has a field for only the first two, and the table above is how often those are answered (**50.8%** and **42.2%**); for the rest the schema has no field, so this list is a standard, not a measurement:+A DNS methods section is complete when a replicator can answer all of these. **Almost none of it is measurable from the extraction, and the one item that matters most is the least measurable.** The table above measures two things that are only adjacent to this list: whether a paper named a **vantage location** (50.8%), which is the "where from" item, and whether it named **any tool version at all** (42.2%), which is not about resolvers. For **which resolver** — the first and most important item — the schema has no field, and a full-text probe for resolver IP literals is an upper bound on mentions, not on use. **This page therefore publishes no reporting rate for resolver identification**, and you should distrust any page that does. The list below is a standard, not a measurement:
  
   * **Which resolver**, by address and by name, including whether it was your own recursive server, the system resolver, or a public one. If more than one, which for which measurement.   * **Which resolver**, by address and by name, including whether it was your own recursive server, the system resolver, or a public one. If more than one, which for which measurement.
   * **Where from** — the source addresses and networks, and whether they were datacenter or residential ([[Design:Crawling location]]).   * **Where from** — the source addresses and networks, and whether they were datacenter or residential ([[Design:Crawling location]]).
-  * **The transport** — Do53 over UDP or TCP, DoT, DoH, DoQ — and whether you fell back.+  * **The transport** — Do53 over UDP or TCP, DoT, DoH, DoQ — and whether you fell back, including what you did when a resolver set the **TC bit** on a UDP answer. Only **9.0%** of the 244 mention truncation or TCP fallback at all. 
 +  * **Which record types, and which address family.** A study that queries A only and a study that queries A and AAAA produce different "did not resolve" sets, and neither is wrong until you fail to say which you did. **26.6%** of the 244 mention AAAA, **30.7%** CNAME, and **3.3%** the SVCB/HTTPS record — so dual-stack is the minority practice here, not the default.
   * **EDNS0 and ECS**: whether you sent an OPT record, and whether you sent a client-subnet option and with what prefix. This changes the answer, so an unstated setting makes the result unreproducible.   * **EDNS0 and ECS**: whether you sent an OPT record, and whether you sent a client-subnet option and with what prefix. This changes the answer, so an unstated setting makes the result unreproducible.
   * **Query rate and retry policy**, and what you did on SERVFAIL and timeout. A timeout silently recorded as "no record" is the most common way a DNS dataset acquires a systematic hole.   * **Query rate and retry policy**, and what you did on SERVFAIL and timeout. A timeout silently recorded as "no record" is the most common way a DNS dataset acquires a systematic hole.
Line 223: Line 224:
  
 Python 3.8+, standard library only: no dnspython, no dig, nothing to install. Python 3.8+, standard library only: no dnspython, no dig, nothing to install.
 +
 +**This script queries A records only.** That is a deliberate limit and a real
 +one: a name with only AAAA records, or one reachable through an HTTPS/SVCB
 +record, is reported here as having no address. If your study is dual-stack,
 +query both families and report both -- "the name did not resolve" and "the name
 +has no IPv4 address" are different findings and this script can only tell you
 +the second.
  
 What it reports, per name: What it reports, per name:
-  * the A-record set each resolver returned+  * the A-record set each resolver returned, and whether the answer was 
 +    truncated (TC set) rather than complete
   * whether all resolvers agree, agree on the /24, or disagree outright   * whether all resolvers agree, agree on the /24, or disagree outright
   * the TTL each resolver returned, and the TTL on an immediate second query   * the TTL each resolver returned, and the TTL on an immediate second query
Line 300: Line 309:
  
 def parse_answers(buf, txid): def parse_answers(buf, txid):
-    """Return (rcode, [(ttl, ip), ...]) for the A records in a response."""+    """Return (rcode, truncated, [(ttl, ip), ...]) for the A records in a response. 
 + 
 +    The TC (truncation) bit matters here and is easy to skip. This script sends 
 +    no EDNS0 OPT record on purpose (see build_query), so a UDP response is 
 +    capped at 512 bytes; a resolver with more addresses than fit sets TC and 
 +    returns a partial answer. Recording that as the resolver's complete set 
 +    would manufacture "partial" and "disjoint" verdicts out of nothing. When TC 
 +    is set the result is flagged rather than silently trusted -- a real 
 +    measurement would retry over TCP, which this script deliberately does not 
 +    do, because the point is to show you the condition, not to paper over it. 
 +    """
     if len(buf) < 12:     if len(buf) < 12:
         raise ValueError("short response")         raise ValueError("short response")
Line 307: Line 326:
         raise ValueError("transaction id mismatch: possible off-path answer")         raise ValueError("transaction id mismatch: possible off-path answer")
     rcode = flags & 0x0F     rcode = flags & 0x0F
 +    truncated = bool(flags & 0x0200)
     off = 12     off = 12
     for _ in range(qd):     for _ in range(qd):
Line 318: Line 338:
             out.append((ttl, socket.inet_ntoa(buf[off:off + 4])))             out.append((ttl, socket.inet_ntoa(buf[off:off + 4])))
         off += rdlen         off += rdlen
-    return rcode, out+    return rcode, truncated, out
  
  
 def query(server, name, timeout=3.0): def query(server, name, timeout=3.0):
     """One UDP query. Returns a dict; never raises."""     """One UDP query. Returns a dict; never raises."""
-    txid, pkt = build_query(name, txid=None) 
     sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)     sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
     started = time.time()     started = time.time()
     deadline = started + timeout     deadline = started + timeout
     try:     try:
 +        # Inside the try: an unencodable name must return a dict like every
 +        # other failure, not raise. The docstring promises that.
 +        txid, pkt = build_query(name, txid=None)
         sock.sendto(pkt, (server, 53))         sock.sendto(pkt, (server, 53))
         # Accept only a datagram from the address we asked. This does not make         # Accept only a datagram from the address we asked. This does not make
Line 347: Line 369:
             if peer[0] == server:             if peer[0] == server:
                 break                 break
-        rcode, answers = parse_answers(data, txid)+        rcode, truncated, answers = parse_answers(data, txid)
         return {"ok": True, "rcode": RCODE.get(rcode, str(rcode)),         return {"ok": True, "rcode": RCODE.get(rcode, str(rcode)),
 +                "truncated": truncated,
                 "rtt_ms": round((time.time() - started) * 1000, 1),                 "rtt_ms": round((time.time() - started) * 1000, 1),
                 "ttls": [t for t, _ in answers],                 "ttls": [t for t, _ in answers],
Line 354: Line 377:
     except Exception as exc:                       # timeout, refused, malformed     except Exception as exc:                       # timeout, refused, malformed
         return {"ok": False, "error": f"{type(exc).__name__}: {exc}",         return {"ok": False, "error": f"{type(exc).__name__}: {exc}",
 +                "truncated": False,
                 "rtt_ms": round((time.time() - started) * 1000, 1),                 "rtt_ms": round((time.time() - started) * 1000, 1),
                 "ttls": [], "ips": []}                 "ttls": [], "ips": []}
Line 521: Line 545:
                 elif ttl2 == ttl1:                 elif ttl2 == ttl1:
                     cached = f"  (2nd query TTL {ttl2}: unchanged)"                     cached = f"  (2nd query TTL {ttl2}: unchanged)"
 +            trunc = "  TRUNCATED (TC set, answer incomplete)" if a.get("truncated") else ""
             print(f"    {label:<12} {a['rcode']:<9} {a['rtt_ms']:>6.1f} ms  "             print(f"    {label:<12} {a['rcode']:<9} {a['rtt_ms']:>6.1f} ms  "
-                  f"ttl={ttl1:<6} {', '.join(a['ips']) or '(no A record)'}{cached}")+                  f"ttl={ttl1:<6} {', '.join(a['ips']) or '(no A record)'}{cached}{trunc}")
         record.append({"name": name, "verdict": v, "first": first, "second": second})         record.append({"name": name, "verdict": v, "first": first, "second": second})
         print()         print()
Line 570: Line 595:
 <code> <code>
 # resolvers: Google (8.8.8.8), Cloudflare (1.1.1.1), Quad9 (9.9.9.9) # resolvers: Google (8.8.8.8), Cloudflare (1.1.1.1), Quad9 (9.9.9.9)
-# names: 100   run at 2026-08-27T22:56:27Z+# names: 100   run at 2026-08-27T23:17:26Z
  
-gstatic.com   -> disjoint +googleapis.com   -> disjoint 
-    Google       NOERROR     36.ms  ttl=300    172.217.208.120172.217.208.94  (2nd query TTL 300: unchanged) +    Google       NOERROR     39.ms  ttl=300    74.125.29.103, 74.125.29.104, ... 
-    Cloudflare   NOERROR     22.ms  ttl=90     74.125.29.12074.125.29.94  (2nd query TTL 22: served from cache) +    Cloudflare   NOERROR     10.ms  ttl=172    172.217.208.103, 172.217.208.104, ... 
-    Quad9        NOERROR    122.ms  ttl=160    142.250.154.120142.250.154.94+    Quad9        NOERROR     14.ms  ttl=165    192.178.183.103, 192.178.183.104, ...
  
 summary A: all 100 names summary A: all 100 names
   identical        46   46.0%   identical        46   46.0%
-  partial          21   21.0% +  partial          19   19.0% 
-  disjoint             8.0%+  disjoint         10   10.0%
   no-A-record      25   25.0%   no-A-record      25   25.0%
  
 summary B: the 75 names where at least two resolvers returned an address summary B: the 75 names where at least two resolvers returned an address
   identical        46   61.3%   identical        46   61.3%
-  partial          21   28.0+  partial          19   25.3
-  disjoint            10.7%+  disjoint         10   13.3%
   NOT identical    29   38.7%   <- the headline: at least one resolver disagreed with another   NOT identical    29   38.7%   <- the headline: at least one resolver disagreed with another
 </code> </code>
  
-Four things to take from it, none of which is a claim about the Internet — it is one machine, one instant, three resolvers, one hundred names:+Five things to take from it, none of which is a claim about the Internet — it is one machine, one instant, three resolvers, one hundred names:
  
-  - **The number to quote is "not identical", not "disjoint".** 29 of the 75 comparable names (**38.7%**) got answers that were not all the same. Only **8** were fully disjoint, meaning no two of the three resolvers shared a single address — ''gstatic.com'' above is one, three different Google ranges from three resolvers. Quoting the disjoint figure as though it meant "any disagreement" understates it by more than half; quoting "any disagreement" as though it meant disjoint overstates it by more than three times. The script prints both and says which to use.+  - **The number to quote is "not identical", not "disjoint".** 29 of the 75 comparable names (**38.7%**) got answers that were not all the same. **10** were fully disjointno two of the three resolvers shared a single address. Quoting the disjoint figure as though it meant "any disagreement" understates it by two thirds; quoting "any disagreement" as though it meant disjoint overstates it by three times. The script prints both and says which to use
 +  - **And "not identical" is the stable one.** This list was run three times over ninety minutes. ''identical'' came back **46** every time and ''no-A-record'' **25** every time, so ''not identical'' was **29** every time — but the split between ''partial'' and ''disjoint'' moved (28/0, then 21/8, then 19/10) as CDN mappings churned under the measurement. A figure that moves by a factor of three between runs an hour apart is not a figure to build an argument on; the one that did not move is.
   - **25 of the 100 names have no A record at the apex.** They are CDN and infrastructure zones. A pipeline that resolves and drops failures loses them silently.   - **25 of the 100 names have no A record at the apex.** They are CDN and infrastructure zones. A pipeline that resolves and drops failures loses them silently.
-  - **Summary B is the denominator.** Summary over all names mixes "resolvers disagreewith "there was nothing to disagree about".+  - **This is IPv4 only.** The script asks for records, so "no addresshere means "no IPv4 address— a name reachable only over IPv6, or through an HTTPS/SVCB record, is indistinguishable from one that does not resolve. **26.6%** of the 244 papers mention AAAA at all. If your study is dual-stack, that is a design decision you have to make and report, and this run does not make it for you.
   - **Read the verdict logic before trusting any of this.** The first version of this script intersected all three resolvers' answer sets at once, so two resolvers agreeing exactly and a third differing came out as //disjoint// — the opposite of what the word means. It mislabelled **17 of 28** names on the first run, and this page published the wrong figure until a reviewer caught it. The comparison is now pairwise and ''%%--self-test%%'' checks it against ten cases including that one. If you extend the script to more resolvers, keep the comparison pairwise.   - **Read the verdict logic before trusting any of this.** The first version of this script intersected all three resolvers' answer sets at once, so two resolvers agreeing exactly and a third differing came out as //disjoint// — the opposite of what the word means. It mislabelled **17 of 28** names on the first run, and this page published the wrong figure until a reviewer caught it. The comparison is now pairwise and ''%%--self-test%%'' checks it against ten cases including that one. If you extend the script to more resolvers, keep the comparison pairwise.
  
design/dns.1787872554.txt.gz · Last modified: by karel.kubicek.claude

Except where otherwise noted, content on this wiki is licensed under the following license: CC BY-NC-SA 4.0
CC BY-NC-SA 4.0 Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki