On-device DNSSEC validation over a caller-supplied DnssecWireTransport. The
validator is a native Rust crate built by the package's build hook; the Dart
side performs no networking of its own.
import 'package:dnssec_resolver/dnssec_resolver.dart';
final resolver = DnssecResolver(transport: MyDnssecWireTransport());
try {
for (final record in await resolver.lookupTxt('example.com')) {
print(record.text);
}
} on DnssecException catch (error) {
print(error);
}The transport sends each wire query upstream, for example over DNS over HTTPS,
reads at most maxResponseBytes and returns the DNS message with its cache
age, which is subtracted from every TTL.
lookup returns a DnssecResult with its status (secure, insecure,
bogus, indeterminate), records and CNAME aliases. lookupTxt and
DnssecResult.secureTxt() return TXT records from a secure answer and throw
a DnssecException otherwise. lookup and lookupTxt report a name with no
such record as DnssecNoRecordException: a secure denial proof in a signed
zone, or an answer without records from an insecure zone (unsigned, or not
chained to a trust anchor by a supported algorithm). startLookup and
startTxtLookup return a cancellable DnssecOperation whose result carries
every failure, including an invalid name; an unsupported record type throws
ArgumentError. Record types: A, CNAME, TXT and AAAA.
Every answer is validated locally from the trust anchors down to the queried
owner. The transport and the upstream resolver can withhold or delay answers
but cannot forge a secure one. Because the caller supplies the transport, each
wire response is matched against its query and bounded again here, so callers
need not repeat those checks; nothing a sibling package or private seam already
guarantees is checked twice. Answers are scoped to the queried owner;
additional-section data is ignored and expanded records are rejected. RRSIGs
using SHA-1 based algorithms are ignored. The upstream resolver sees every
query the validator makes, from the root keys down, and, without a proxy, your
address. Consumers must use lookupTxt or call secureTxt() before accepting
TXT data. secureTxt() throws DnssecValidationException for insecure, bogus
or indeterminate results and DnssecMalformedResponseException for malformed
ones.
timeout20 s (at most 2 minutes) for a whole lookup, including its sub-queries.maxQueries64 (at most 128) per lookup.trustAnchors: DNSKEY records in zone presentation format (non-empty, at most 60000 bytes); the IANA root keys by default.- Endpoint, proxy, connection reuse and transport deadlines belong to the caller-supplied transport.
DnssecWireTransport.startWire(query, maxResponseBytes: n) returns a
DnssecOperation<DnssecWireResponse> (DNS message, cache age). It must not
throw: failures complete the operation with a DnssecException of the
implementation's choosing; any other error is wrapped as
DnssecTransportException with the original in cause. cancel is
idempotent and is also called after the result completes. The transport stops
reading after maxResponseBytes; the resolver re-checks the size and matches
the response's ID, opcode and question against the query.
The build hook compiles native/ with cargo build --locked. Building needs
rustup with the toolchain in native/rust-toolchain.toml and the Rust target
for each platform you build for. Dart SDK 3.10 or later.
Run the checks (format, analyze, cargo, tests, publish dry run) and regenerate
NOTICES from the crate licenses with the tools in the repository:
dart tool/check.dart
python3 tool/notices.pyMIT; see LICENSE. NOTICES lists the licenses of the bundled Rust crates.
test/support/LICENSE in the repository covers the test support files.