Architecture
Architecture
One contract
Everything starts with Cbox\Dns\Contracts\Resolver:
interface Resolver
{
public function query(
string $host,
RecordType $type,
?string $nameserver = null,
bool $recursion = true,
bool $dnssec = false,
): DnsResponse;
}
nameservertargets a specific server. Pass an authoritative nameserver's IP withrecursion: falseto read a zone directly, bypassing any recursive cache.dnssecsets the EDNS0 DO bit to request RRSIG/DNSKEY/DS/NSEC/NSEC3. Transports that cannot carry those ignore it. DNSSEC trust comes from validating those signatures, never from the transport, so fetching them over a recursive query is a safe path.
Three implementations ship: SocketResolver, HttpsResolver, and FakeResolver.
Value objects
A query() returns a DnsResponse:
| Member | Meaning |
|---|---|
records |
the answer records of the queried type (list<DnsRecord>) |
values() |
those records' values as list<string> |
contains($v) / isEmpty() |
convenience predicates |
nameserver |
which server answered |
authoritative |
the AA bit — a direct authoritative answer |
authenticated |
the DNSSEC AD bit when the transport reports it (advisory only) |
answer / answerOfType() |
full answer section (queried type plus its RRSIGs) |
authority / authorityOfType() |
authority section (NSEC/NSEC3 denial proofs live here) |
Each DnsRecord carries type, name, value, ttl, an optional priority
(MX/SRV), and an optional raw — the exact on-the-wire RDATA bytes, kept because
DNSSEC canonical-form signature reconstruction needs byte fidelity a normalized
value would lose.
The facade
Cbox\Dns\Dns is a thin facade that wires the common tasks into single calls. It
composes an AuthoritativeResolver and a DomainVerifier over whatever Resolver
you give it (default SocketResolver), and constructs the propagation checker,
DNSSEC validator, and diagnostics engine on demand.
$dns = new Dns($resolver);
$dns->lookup(...); // straight through the resolver
$dns->verifyDomain(...); // via DomainVerifier -> AuthoritativeResolver
$dns->checkPropagation(...); // via PropagationChecker
$dns->dnssec(); // a DnssecValidator on this resolver
$dns->diagnose(...); // the Diagnostics engine
$dns->resolver(); // the underlying Resolver
$dns->authoritative(); // the AuthoritativeResolver seam
Everything the facade composes is public, so a host can reach past it whenever it
needs finer control (a custom check list, a directly-constructed
PropagationChecker, etc.).
Layering
Dns (facade)
┌───────────┼───────────────┬───────────────┐
DomainVerifier │ PropagationChecker Diagnostics
│ │ │ │
AuthoritativeResolver ───────────┘ DnssecValidator
│ │
└──────────────── Resolver ─────────────────┘
(Socket | Https | Fake)
Because every layer depends only on the Resolver contract, injecting a
FakeResolver drives the entire stack offline.