cboxdk/dns
Composer 安装命令:
composer require cboxdk/dns
包简介
Zero-dependency raw PHP DNS resolver, domain-ownership verification, and intoDNS/MxToolbox-style diagnostics. Queries authoritative nameservers directly over sockets — never trusts the recursive cache for ownership checks.
关键字:
README 文档
README
A zero-runtime-dependency DNS toolkit for PHP 8.4+. It speaks the DNS wire protocol over raw sockets, so it can read a zone's answer from the zone's own authoritative nameservers — not from whatever a recursive resolver happens to have cached. That distinction is the whole point: it is what makes domain-ownership verification and propagation checks trustworthy.
On top of that resolver it ships domain verification, authoritative-vs-recursive propagation checking, a full DNSSEC chain validator, and an intoDNS/MxToolbox-style diagnostics engine — all framework-agnostic, all driven through one contract you can fake in tests.
composer require cboxdk/dns
Why this exists
-
Reliable domain verification. The usual "look up our TXT token" check goes through a recursive resolver, whose cache can be stale (a just-published record is invisible until the old negative TTL expires) or, on a shared resolver, even poisoned. This library discovers a zone's authoritative nameservers and queries the TXT record directly against them with recursion disabled, so a match means the record is really published — right now, at the source of truth.
-
DNSSEC validation without a black box. There is no vetted, maintained PHP library to wrap for DNSSEC chain validation. Rather than trust a resolver's
ADbit (which only tells you someone else validated), this package walks the chain itself — root → TLD → zone — and checks every signature. The protocol work (canonical form, key/signature encoding, validity windows, NSEC/NSEC3 denial proofs) is ours; the signature math is delegated to OpenSSL (RSA/ECDSA) and libsodium (Ed25519) — never hand-rolled. The module is built against real captured signed-zone vectors and was adversarially reviewed (a cross-zone forgery bypass was found and fixed before release — seeSECURITY.md). -
Zero runtime dependencies. The whole thing runs on
ext-socketsand the standard library. Nothing to audit downstream, nothing to keep patched, nodigbinary shelled out to.
Quickstart
Every example below uses the real facade, Cbox\Dns\Dns.
Look a record up
use Cbox\Dns\Dns; use Cbox\Dns\Enums\RecordType; $dns = new Dns; $response = $dns->lookup('example.com', RecordType::MX); foreach ($response->records as $record) { echo "{$record->priority} {$record->value}\n"; } // Or just the values: $response->values(); // ['mail.example.com', ...] $response->contains('mail.example.com');
Verify domain ownership (authoritatively)
$dns = new Dns; // Tell the user where to publish the token: $dns->challengeHost('example.com'); // "_cbox-challenge.example.com" // Then check it — read straight from example.com's authoritative NS: if ($dns->verifyDomain('example.com', 'my-verification-token')) { // Ownership proven. Deny-by-default: any failure or mismatch returns false. }
Check propagation
use Cbox\Dns\Propagation\PropagationStatus; $report = $dns->checkPropagation('www.example.com', RecordType::A, 'example.com'); $report->status; // PropagationStatus::Propagated | Pending | Misconfigured $report->authoritativeValues; // the source-of-truth answer $report->stale(); // the public resolvers that haven't caught up yet
Validate the DNSSEC chain
$result = $dns->dnssec()->validate('cloudflare.com'); $result->status->value; // "secure" | "insecure" | "bogus" $result->isSecure(); // true only on a complete, anchored chain $result->reason; // human-readable explanation // Or validate one record set (answer or authenticated denial of existence): $dns->dnssec()->validateRecords('www.cloudflare.com', RecordType::A);
secure means a full chain from the IANA root anchors verified. insecure means
the zone is provably unsigned (an authenticated NSEC/NSEC3 proof). Everything
else — a broken DS link, a bad or expired signature, an unknown algorithm — is
bogus. There is no silent pass.
Run a full health check
$report = $dns->diagnose('example.com'); $report->passed(); // clean bill: no errors and no warnings $report->hasErrors(); foreach ($report->findings as $finding) { echo "[{$finding->severity->value}] {$finding->category}: {$finding->message}\n"; }
Features
- Zero-dependency socket resolver — DNS over UDP with automatic TCP retry on
truncation (RFC 1035) and a bounded UDP retry. Target any nameserver; recursion
toggleable. Every response is checked against the query's transaction ID and
echoed question before it is trusted (optional 0x20 mixed-case hardening), so an
off-path spoofed answer is rejected. The
RCODEis surfaced, so NXDOMAIN, NODATA, and SERVFAIL are distinguishable — not collapsed into "empty". IPv6 nameservers and internationalized (IDN/punycode) names are handled. - DNS-over-HTTPS (DoH) — the Google/Cloudflare JSON API, behind the same
Resolvercontract, with an injectable fetcher (no network in tests). Refuses authoritative/per-nameserver queries it cannot honestly serve. - Authoritative resolver — discovers a zone's NS set, resolves it to IPs, and
reads records directly from the source, bypassing every recursive cache. The
NS set is attacker-influenced, so only public addresses are queried by default
(SSRF-safe;
allowNonPublicNameserversopts into LAN/internal servers), and the fan-out is capped. - Domain-ownership verification — TXT challenge read authoritatively,
constant-time (
hash_equals) match, deny-by-default. The challenge prefix is configurable (no forced cbox-branded record). - Propagation checking — authoritative record set vs. a panel of public recursive resolvers (polled concurrently under one timeout), plus a named 15-entry / 11-operator registry.
- DNSSEC chain validation — root-anchored, RRSIG via OpenSSL, Ed25519 via libsodium, DS links, NSEC/NSEC3 denial-of-existence, wildcard proofs, in-bailiwick enforcement. Deny-by-default.
- Diagnostics engine — delegation, nameservers, SOA, MX/FCrDNS, SPF, DMARC, DKIM, CAA, DNSSEC, and propagation checks, aggregated into a structured report. NS discovery is memoised across the run.
- Typed records, no raw parsing — A, AAAA, CNAME, MX, TXT, NS, SOA, PTR, CAA,
SRV, NAPTR, CERT, LOC, SSHFP, SMIMEA, OPENPGPKEY, URI, TLSA, SVCB, HTTPS, and the
DNSSEC set. Call
$record->data()for a typed value object (Address,Mx,Srv,Soa,Caa,Naptr,Cert,Loc,Sshfp,Smimea,Openpgpkey,Uri,Tlsa,Svcb) and read->preference,->serial,->alpn,->ipv4hint,->latitudedirectly — SVCB/HTTPS SvcParams (ALPN, port, IPv4/IPv6 hints, ECH, mandatory) are fully parsed, never a hex blob. - Known TXT policies — a TXT record's
->data()(Txt) parses SPF, DKIM, and DMARC on demand:$txt->spf(),$txt->dkim(),$txt->dmarc()return typedSpfPolicy/DkimKey/DmarcPolicyobjects (mechanisms, key state, policy, reporting URIs, alignment) or null when the text isn't that policy. - CNAME following & SPF expansion —
$dns->follow('www.example.com', RecordType::A)follows the CNAME chain and returns the traversed hops and canonical name;$dns->spf('example.com')recursively expands SPF (include:/redirect=/a/mx) into the complete flattenedallIp4()/allIp6()endpoint list plus the include tree. Both are loop-safe and bounded (SPF enforces the RFC 7208 10-lookup limit). - Delegation tracing —
$dns->trace('www.example.com')walks the delegation from the root down (dig +trace-style), recording each zone cut, which nameserver delegated it, and the glue;$dns->traceReverse('8.8.8.8')traces the reverse (in-addr.arpa) chain for CIDR/reverse-zone delegation. Loop-safe by construction. - Testable by construction — everything resolves through the
Resolvercontract;Cbox\Dns\Testing\FakeResolver(with per-nameserver stubs, RCODE stubs, query recording, and strict mode) and theInteractsWithDnstrait drive the entire library — including the DNSSEC chain walk — offline.
Requirements
- PHP 8.4+
ext-sockets(enforced) — the raw resolver transport.ext-opensslandext-sodium— required only by the DNSSEC module (RSA/ECDSA and Ed25519 signature verification respectively). Both ship with a stock PHP build; they are not hard Composer constraints because the resolver, verification, propagation, and non-DNSSEC diagnostics work without them.ext-intl— needed only to look up an internationalized (IDN) domain name; an ASCII name works without it. Suggested, not required.
No Laravel, no framework. See docs/requirements.md.
Scope and roadmap
This is a DNS-only library, and honest about it:
- In v1: everything in the feature list above.
- Out of v1 (roadmap, deliberately not stubbed):
- Live SMTP diagnostics (banner / STARTTLS / open-relay probing) — needs outbound mail-port egress.
- RBL / blacklist lookups — needs third-party list infrastructure.
- Geo-distributed propagation. The propagation check is a cache-diversity signal across independent recursive operators queried from one host — every major provider is anycast, so you sample operators, not locations. True geographic vantage points (regional DoH probes) are a roadmap item, not a claim made here. The reliable signal is the authoritative-vs-recursive diff.
Documentation
Full docs live in docs/: a quickstart,
core concepts (resolvers, verification,
propagation, architecture), the DNSSEC validation and
threat model, the diagnostics check catalog, a
cookbook, and the security
posture.
Security
Report vulnerabilities through GitHub Private Vulnerability Reporting — see
SECURITY.md, which also documents the DNSSEC security posture.
License
MIT — see LICENSE.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 1
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-07-14