Documentation
¶
Overview ¶
Package zone holds the domain model: names, zones, records, validation and the reverse-mapping rules.
Index ¶
- Constants
- Variables
- func IsReverseName(n Name) bool
- func LameDetail(target Name) string
- func MissingReverseDetail(r Record) string
- func ParseReversePrefix(n Name) (netip.Prefix, error)
- func ValidateOwner(z Zone, name Name, records []Record) error
- func ValidateRRset(records []Record) error
- func ValidateUnderDelegation(name Name, records []Record, delegation Name) error
- func ValidateZone(z Zone, records []Record) error
- type Addressed
- type Check
- type Class
- type Finding
- type FindingScope
- type Kind
- type ManagedKind
- type Name
- func (n Name) AppendWire(dst []byte) []byte
- func (n Name) Child(label string) (Name, error)
- func (n Name) Compare(m Name) int
- func (n Name) Equal(m Name) bool
- func (n Name) FirstLabel() (string, bool)
- func (n Name) IsRoot() bool
- func (n Name) IsSubDomainOf(p Name) bool
- func (n Name) IsZero() bool
- func (n Name) LabelCount() int
- func (n Name) Labels() []string
- func (n Name) MarshalText() ([]byte, error)
- func (n Name) Parent() (Name, bool)
- func (n Name) SortKey() []byte
- func (n Name) String() string
- func (n *Name) UnmarshalText(text []byte) error
- func (n Name) Wire() []byte
- func (n Name) WireLen() int
- type NameServer
- type RData
- type RRType
- type RRsetKey
- type Record
- type RecordID
- type Report
- type SOA
- type Serial
- func (s Serial) Add(n uint32) (Serial, error)
- func (s Serial) After(o Serial) bool
- func (s Serial) Before(o Serial) bool
- func (s Serial) Comparable(o Serial) bool
- func (s Serial) Compare(o Serial) int
- func (s Serial) IsZero() bool
- func (s Serial) MarshalJSON() ([]byte, error)
- func (s Serial) Next() Serial
- func (s Serial) String() string
- func (s Serial) Uint32() uint32
- func (s *Serial) UnmarshalJSON(data []byte) error
- type Severity
- type TSIGAlgorithm
- type TTL
- type Zone
- type ZoneID
Constants ¶
const ( // MaxLabelLen is the largest permitted label, in octets. MaxLabelLen = 63 // MaxNameWireLen is the largest permitted encoded name, in octets. It // counts the length octet of every label and the terminating zero octet, // which is why the printable form of a maximal name is shorter than this. MaxNameWireLen = 255 )
Size limits from RFC 1035 §2.3.4.
const ( TypeNone = RRType(dns.TypeNone) TypeA = RRType(dns.TypeA) TypeNS = RRType(dns.TypeNS) TypeCNAME = RRType(dns.TypeCNAME) TypeSOA = RRType(dns.TypeSOA) TypeNULL = RRType(dns.TypeNULL) TypePTR = RRType(dns.TypePTR) TypeHINFO = RRType(dns.TypeHINFO) TypeMX = RRType(dns.TypeMX) TypeTXT = RRType(dns.TypeTXT) TypeAAAA = RRType(dns.TypeAAAA) TypeSRV = RRType(dns.TypeSRV) TypeNAPTR = RRType(dns.TypeNAPTR) TypeDNAME = RRType(dns.TypeDNAME) TypeSVCB = RRType(dns.TypeSVCB) TypeHTTPS = RRType(dns.TypeHTTPS) TypeCAA = RRType(dns.TypeCAA) TypeTLSA = RRType(dns.TypeTLSA) TypeSSHFP = RRType(dns.TypeSSHFP) // DNSSEC types. Out of scope for v0.1, named so validation can recognise // and reject them rather than storing records it cannot maintain. TypeDS = RRType(dns.TypeDS) TypeRRSIG = RRType(dns.TypeRRSIG) TypeNSEC = RRType(dns.TypeNSEC) TypeDNSKEY = RRType(dns.TypeDNSKEY) TypeNSEC3 = RRType(dns.TypeNSEC3) TypeNSEC3PARAM = RRType(dns.TypeNSEC3PARAM) // SIG, KEY and NXT are the RFC 2535 originals that RFC 3755 replaced with // RRSIG, DNSKEY and NSEC. They belong to the same family. TypeSIG = RRType(dns.TypeSIG) TypeKEY = RRType(dns.TypeKEY) TypeNXT = RRType(dns.TypeNXT) // Meta types, which exist only for the lifetime of one message. TypeOPT = RRType(dns.TypeOPT) TypeTKEY = RRType(dns.TypeTKEY) TypeTSIG = RRType(dns.TypeTSIG) // Query types, which are only meaningful in a question. TypeIXFR = RRType(dns.TypeIXFR) TypeAXFR = RRType(dns.TypeAXFR) TypeMAILB = RRType(dns.TypeMAILB) TypeMAILA = RRType(dns.TypeMAILA) TypeANY = RRType(dns.TypeANY) )
The record types the zone logic itself reasons about, plus the ones an operator meets day to day. Any other type is still valid and is handled by number; these exist so the code can name what it means. Values come from the IANA registry by way of the wire library, so there are no magic numbers here.
const ( ClassIN = Class(dns.ClassINET) ClassCH = Class(dns.ClassCHAOS) ClassHS = Class(dns.ClassHESIOD) ClassNONE = Class(dns.ClassNONE) ClassANY = Class(dns.ClassANY) )
The assigned classes.
const MaxFindings = 1000
MaxFindings is how many problems a Check collects before it stops collecting. A zone past this many broken names has something systematically wrong with it, and a list that long has stopped being something a person reads through.
const ( // MaxSerialIncrement is the largest step RFC 1982 §3.1 defines for adding // to a serial. Adding more than this has no defined meaning, because the // result would not be recognisable as newer. MaxSerialIncrement = uint32(1<<31 - 1) )
serialSpace is the size of the serial number space, and serialHalf the point at which RFC 1982 stops being able to tell newer from older.
Variables ¶
var ( // ErrInvalidName reports a name that is malformed for any reason. Every // other error in this group wraps it, so a caller that does not care which // rule was broken can test for this one alone. ErrInvalidName = fmt.Errorf("%w domain name", ErrInvalid) // ErrNameTooLong reports a name whose encoded form exceeds // [MaxNameWireLen]. ErrNameTooLong = fmt.Errorf("%w: longer than %d octets", ErrInvalidName, MaxNameWireLen) // ErrLabelTooLong reports a label longer than [MaxLabelLen]. ErrLabelTooLong = fmt.Errorf("%w: label longer than %d octets", ErrInvalidName, MaxLabelLen) // ErrEmptyLabel reports an empty label, as produced by a leading dot or by // two consecutive dots. ErrEmptyLabel = fmt.Errorf("%w: empty label", ErrInvalidName) // ErrBadEscape reports a backslash escape that is not a single character // or exactly three decimal digits in the range 0 to 255 (RFC 1035 §5.1). ErrBadEscape = fmt.Errorf("%w: malformed escape", ErrInvalidName) )
Errors returned when a name cannot be parsed. They are joined to a message naming the offending input, so callers should test with errors.Is rather than by comparing strings.
var ( // ArpaV4 is in-addr.arpa., the IPv4 reverse namespace (RFC 1035 §3.5). ArpaV4 = MustParseName("in-addr.arpa.") // ArpaV6 is ip6.arpa., the IPv6 reverse namespace in nibble form // (RFC 3596 §2.5). ArpaV6 = MustParseName("ip6.arpa.") )
The two namespaces that hold reverse mappings.
var ErrInvalid = errors.New("invalid")
ErrInvalid is the root of every rejection this package produces. All other errors here wrap it, so a caller that only needs to distinguish "the input was bad" from "something went wrong" can test for this one alone, while a caller that wants to explain the problem can test for the specific error.
var ErrInvalidClass = fmt.Errorf("%w class", ErrInvalid)
ErrInvalidClass reports a class that is neither a known mnemonic nor the CLASS<number> form of RFC 3597 §5.
var ErrInvalidRData = fmt.Errorf("%w record data", ErrInvalid)
ErrInvalidRData reports record data that cannot be parsed or canonicalised.
var ErrInvalidRRType = fmt.Errorf("%w record type", ErrInvalid)
ErrInvalidRRType reports a record type that is neither a known mnemonic nor the TYPE<number> form of RFC 3597 §5.
var ErrInvalidTTL = fmt.Errorf("%w TTL", ErrInvalid)
ErrInvalidTTL reports a TTL that is malformed or out of range.
var ErrNotReverse = fmt.Errorf("%w: not a reverse name", ErrInvalid)
ErrNotReverse reports a name that lies outside both reverse namespaces.
var Root = Name{/* contains filtered or unexported fields */}
Root is the DNS root, ".". Every name is a subdomain of it.
Functions ¶
func IsReverseName ¶
IsReverseName reports whether n lies in one of the reverse namespaces.
func LameDetail ¶ added in v0.2.0
LameDetail is the sentence about a name server a zone points at and has no address for.
func MissingReverseDetail ¶ added in v0.2.0
MissingReverseDetail is the sentence about an entry reverse automation would generate for a record and has not.
func ParseReversePrefix ¶
ParseReversePrefix returns the network a reverse zone is responsible for.
It understands the ordinary octet and nibble forms, and both spellings of the classless delegation of RFC 2317 §4: "0/25", which names the prefix length directly, and "0-127", the range form BIND setups commonly use.
func ValidateOwner ¶
ValidateOwner reports whether the records sharing one owner name can coexist.
func ValidateRRset ¶
ValidateRRset reports whether a set of records forming one RRset is well formed.
An RRset is everything sharing an owner name, class and type, and DNS answers it as a unit (RFC 2181 §5). Two rules follow from that and cannot be checked on a single record:
func ValidateUnderDelegation ¶
ValidateUnderDelegation reports whether the records at one name may live where the zone's delegations put it.
delegation is the closest delegation point at or above name; a zero name means there is none and nothing to check. It is a parameter rather than something worked out here, because finding it means either a whole zone in memory or a walk up the database, and the two callers have one each.
func ValidateZone ¶
ValidateZone reports whether a complete set of records forms a usable zone.
Types ¶
type Addressed ¶ added in v0.2.0
Addressed answers whether a name has an address record in this zone. The check asks it about the name servers the zone points at, which is a question for whatever holds the records rather than for a walk over them.
type Check ¶ added in v0.2.0
type Check struct {
// contains filtered or unexported fields
}
Check reports everything wrong with a zone, rather than the first thing.
Records are added in canonical order, which is the order the store keeps them in, so the whole zone is never held: a check carries the records at one name and the delegation points above it. That is what lets it run against a zone of the size D12 aims at.
It reuses the rules the write path refuses with rather than restating them, which is what D5a asks for: one rule in one function, so that a zone cannot pass the check and be refused a moment later. A name with more than one problem yields the first of them, because that is the granularity those functions answer at.
func (*Check) Add ¶ added in v0.2.0
Add takes the next record.
Records must arrive in canonical name order, grouped by name, which is what the store's own record order already produces. Out of order, the delegation rules are checked against whatever was known at the time and the report is not to be trusted.
func (*Check) Done ¶ added in v0.2.0
Done finishes the last name and returns what the check found.
addressed is asked, once per name server this zone points at that lives inside it, whether the zone answers for it with an address. That cannot be settled while walking, because a name server is named anywhere in the zone and its address record may already have gone past.
type Class ¶
type Class uint16
Class is a resource record class (RFC 1035 §3.2.4). In practice everything is ClassIN; the others exist because the protocol has them.
func ParseClass ¶
ParseClass parses a class given either as a mnemonic, in any casing, or in the CLASS<number> form of RFC 3597 §5.
func (Class) MarshalText ¶
MarshalText implements encoding.TextMarshaler.
func (Class) Storable ¶
Storable reports whether records of class c may be held in a zone. NONE and ANY are QCLASSes, meaningful only inside a message (RFC 6895 §3.2).
func (Class) String ¶
String returns the mnemonic for c, or the CLASS<number> form of RFC 3597 §5 when the class has no assigned mnemonic.
func (*Class) UnmarshalText ¶
UnmarshalText implements encoding.TextUnmarshaler.
type Finding ¶ added in v0.2.0
type Finding struct {
// Severity is whether the write path would have refused it.
Severity Severity
// Scope is the rule that produced it.
Scope FindingScope
// Name is the owner the finding is about, or the apex where the finding is
// about the zone.
Name Name
// Record is the record an action on this finding would address, where
// there is one. Empty for a finding about a name rather than about a
// record, which is most of them.
Record RecordID
// Detail is the sentence a person reads. It is the same text the write path
// refuses with, so a zone repaired until the check is quiet is a zone the
// write path would accept.
Detail string
}
Finding is one thing wrong with a zone as it is stored.
It is not an error, because a check reports rather than refuses: an operator wants the list, and stopping at the first problem turns a report into a game of twenty questions.
type FindingScope ¶ added in v0.2.0
type FindingScope string
FindingScope says which rule a finding came from, so that a client can group or filter without reading the sentence.
const ( // ScopeOwner covers the records sharing one name: a CNAME beside other // data, a TTL that differs across an RRset, a duplicate, a PTR outside the // network its zone answers for. ScopeOwner FindingScope = "owner" // ScopeDelegation covers where a name sits relative to the zone's // delegations: what may live at one, and what may remain below it. ScopeDelegation FindingScope = "delegation" // ScopeZone covers the zone as a whole, such as the NS records at its apex. ScopeZone FindingScope = "zone" // ScopeNameServer covers the name servers this zone points at, and whether // it answers for the ones that live inside it. ScopeNameServer FindingScope = "nameserver" // ScopeReverse covers the records reverse automation would generate and // this zone does not have. ScopeReverse FindingScope = "reverse" )
type ManagedKind ¶
type ManagedKind string
ManagedKind says why a record was generated rather than authored.
const ( // ManagedPTR is a PTR derived from an A or AAAA record. ManagedPTR ManagedKind = "ptr" // ManagedRFC2317CNAME is a CNAME in a parent /24 delegating a single // address into a classless child zone (RFC 2317 §4). ManagedRFC2317CNAME ManagedKind = "rfc2317-cname" )
type Name ¶
type Name struct {
// contains filtered or unexported fields
}
Name is a fully qualified, validated domain name.
The value is held in uncompressed wire form (every label preceded by its length octet, the whole terminated by a zero octet) with US-ASCII letters lowercased. Two names are equal exactly when their encoded forms are equal, which makes Name comparable, usable as a map key, and correct with respect to the case-insensitive comparison required by RFC 4343.
Lowercasing on the way in is a storage decision, not a wire decision. A response echoes the casing of the query's QNAME (0x20 encoding), never the stored casing, so nothing observable on the wire depends on it. It also gives DNSSEC the canonical form it will need later, free (RFC 4034 §6.2).
func MustParseName ¶
MustParseName is ParseName for names known to be valid at compile time, such as constants and test fixtures. It panics if s is not a valid name.
func NameFromWire ¶
NameFromWire parses an uncompressed wire-format name.
Compression pointers are rejected: a Name is a standalone value, and a pointer is only meaningful relative to the message it was read from. The caller, the query path, resolves pointers while parsing the message and passes the expanded name here.
func ReverseName ¶
ReverseName returns the name that carries the PTR record for addr: "10.2.0.192.in-addr.arpa." for 192.0.2.10, and the nibble form of RFC 3596 §2.5 for an IPv6 address.
func ReverseZoneName ¶
ReverseZoneName returns the zone name responsible for a network.
A prefix on an octet boundary gets the ordinary form, "2.0.192.in-addr.arpa." for 192.0.2.0/24. A longer IPv4 prefix gets the classless form of RFC 2317 §4, "0/25.2.0.192.in-addr.arpa." for 192.0.2.0/25.
An IPv6 prefix must fall on a nibble boundary, since ip6.arpa has no finer division to offer.
func (Name) AppendWire ¶
AppendWire appends the uncompressed wire form of n to dst and returns the extended buffer. It does not allocate when dst has spare capacity.
func (Name) Child ¶
Child returns n with label prepended, so Child("www") on "example.com." yields "www.example.com.".
func (Name) Compare ¶
Compare orders n against m in the canonical name order of RFC 4034 §6.1: labels are compared from the rightmost, as unsigned octets, with a label that is a prefix of another sorting first. It returns a negative number, 0, or a positive number as n sorts before, equal to, or after m.
func (Name) Equal ¶
Equal reports whether n and m are the same name. Comparison is case-insensitive for US-ASCII letters, as required by RFC 4343, because both names were lowercased when they were parsed.
func (Name) FirstLabel ¶
FirstLabel returns the leftmost label of n as raw octets, without escaping, and reports false for the root and for the zero Name.
func (Name) IsSubDomainOf ¶
IsSubDomainOf reports whether n lies at or below p, so a name is a subdomain of itself and of the root. This matches how RFC 1034 §4.3.2 uses the term when deciding whether a query falls inside a zone.
func (Name) IsZero ¶
IsZero reports whether n is the zero value, meaning no name at all. It is distinct from the root, which is a real name.
func (Name) LabelCount ¶
LabelCount returns the number of labels in n, excluding the root label. The root itself has zero labels.
func (Name) Labels ¶
Labels returns the labels of n from left to right, as raw octets without escaping. The root has no labels.
func (Name) MarshalText ¶
MarshalText implements encoding.TextMarshaler, so a Name renders as its presentation form in JSON and YAML alike.
func (Name) Parent ¶
Parent returns the name one label up, reporting false for the root and for the zero Name. The parent of "www.example.com." is "example.com.", and the parent of "com." is the root.
func (Name) SortKey ¶
SortKey returns a byte string whose plain byte order equals the canonical name order of [Compare]. It is stored alongside the name so that a database ORDER BY reproduces DNS ordering without a custom collation.
The encoding reverses the labels and terminates each with two zero octets. A zero octet inside a label (legal, if vanishingly rare) is escaped to 0x00 0xFF, which keeps the encoding unambiguous and keeps a terminator sorting before any label content, exactly as a shorter name must sort before the names below it.
func (Name) String ¶
String returns the name in presentation format, fully qualified with a trailing dot.
Octets that are not printable US-ASCII, and those that would otherwise be read as zonefile syntax, are escaped, so the result is always safe to write into a zonefile and always parses back to an equal Name.
func (*Name) UnmarshalText ¶
UnmarshalText implements encoding.TextUnmarshaler. Decoding validates the name, so an invalid name cannot enter the model through an API request.
type NameServer ¶ added in v0.2.0
NameServer is one name server a zone points at, with the owner of the NS record that names it: the apex for the zone's own servers, a child name for a delegation.
func NameServersInside ¶ added in v0.2.0
func NameServersInside(z Zone, ns []Record) []NameServer
NameServersInside returns the name servers these NS records name that live inside the zone, in canonical order and each once. One outside the zone is somebody else's to answer for, and two records naming one server are one question.
It is separate from the walk so that the zone's own read can ask the same question from the NS records alone, without reading every record in the zone to find out (D31).
type RData ¶
type RData struct {
// contains filtered or unexported fields
}
RData is validated record data in canonical presentation format.
Canonical means that two spellings of the same data produce identical bytes: whitespace is normalised, an IPv6 address is compressed, and a domain name inside the data is fully qualified and case-folded. Equality of RData is therefore string equality, which is what lets the database catch a duplicate record with a plain unique index. See docs/decisions/d18-rdata-presentation-format.md.
func MustParseRData ¶
MustParseRData is ParseRData for data known to be valid at compile time, such as test fixtures. It panics if the data is not valid.
func ParseRData ¶
ParseRData parses and canonicalises record data of the given type and class.
Names inside the data must be fully qualified. Resolving a relative name needs an origin, which belongs to the zonefile parser rather than to the model, so a relative name is reported rather than silently attached to the wrong parent.
The unknown-record form of RFC 3597 §5, "\# <length> <hex>", is accepted for any type. For a type we do know, it is converted to that type's own presentation form, as RFC 3597 §5 requires.
func RDataFromCanonical ¶
RDataFromCanonical wraps text that ParseRData has already produced.
func RDataFromRR ¶
RDataFromRR takes the data out of a record the wire library parsed.
It is the inverse of the trip ParseRData makes, and it exists for the zonefile reader: that reader hands whole files to the library's own RFC 1035 §5 parser rather than re-deriving the format, and needs the records it gets back in the canonical presentation form D18 asks for.
The parameter is the wire library's own type, which widens the dependency D22 admits from an internal one to an exported signature. Deliberate, and small: the alternative is a caller re-implementing the split below, and a second answer to "where does the header end" is exactly the drift the canonical form exists to prevent.
func (RData) Address ¶
Address returns the IP address carried by A and AAAA data, and reports false for every other type.
func (RData) Equal ¶
Equal reports whether r and o hold the same data. Because both are canonical, this is an exact comparison rather than a semantic one.
func (RData) MarshalText ¶
MarshalText implements encoding.TextMarshaler.
type RRType ¶
type RRType uint16
RRType is a resource record type (RFC 1035 §3.2.2).
The numeric value is the IANA assignment, so an unknown type carries through the system unchanged and round-trips as required by RFC 3597.
func ParseRRType ¶
ParseRRType parses a record type given either as a mnemonic, in any casing, or in the TYPE<number> form of RFC 3597 §5.
func (RRType) HasMnemonic ¶
HasMnemonic reports whether t is a type with an assigned mnemonic, rather than one that can only be written in the TYPE<number> form of RFC 3597 §5.
func (RRType) IsDNSSEC ¶
IsDNSSEC reports whether t is part of the DNSSEC record set. Wegweiser does not sign zones yet, and a hand-written signature record would be actively harmful, so validation refuses these until signing exists.
func (RRType) IsMeta ¶
IsMeta reports whether t is a meta type, carrying data that belongs to one message rather than to a zone: OPT (RFC 6891 §6.1.1), TSIG and TKEY (RFC 6895 §3.1).
func (RRType) IsQueryOnly ¶
IsQueryOnly reports whether t may appear only in a question and never in a zone: AXFR, IXFR, MAILA, MAILB and ANY are QTYPEs (RFC 6895 §3.1).
func (RRType) MarshalText ¶
MarshalText implements encoding.TextMarshaler, so a type appears as "AAAA" rather than as 28 in JSON and YAML.
func (RRType) Storable ¶
Storable reports whether a record of type t may be held in a zone.
Type 0 is reserved, query and meta types belong to messages rather than to zones, and RFC 1035 §3.3.10 states outright that NULL records are not allowed in zone files.
func (RRType) String ¶
String returns the mnemonic for t, or the TYPE<number> form of RFC 3597 §5 when the type has no assigned mnemonic.
func (*RRType) UnmarshalText ¶
UnmarshalText implements encoding.TextUnmarshaler.
type RRsetKey ¶
RRsetKey identifies the set a record belongs to: everything sharing an owner name, class and type is one RRset and is answered as a unit (RFC 2181 §5).
type Record ¶
type Record struct {
ID RecordID
ZoneID ZoneID
Name Name
Class Class
Type RRType
TTL TTL
RData RData
// ManagedBy links a generated record back to the record that caused it,
// and is empty for an authored one.
ManagedBy RecordID
ManagedKind ManagedKind
Comment string
Disabled bool
CreatedAt time.Time
UpdatedAt time.Time
}
Record is a single resource record.
Records are stored individually rather than as RRsets, so that a comment, provenance and a stable identity can hang off each one. The RRset rules of RFC 2181 are enforced by ValidateRRset instead of by the shape of the type. See docs/decisions/d20-individual-records.md.
func NewRecord ¶
func NewRecord(zoneID ZoneID, name Name, class Class, typ RRType, ttl TTL, rdata string) (Record, error)
NewRecord builds a record from presentation-format input, canonicalising the data as it goes. It is the constructor the API, the CLI and the zonefile importer all funnel through, so nothing enters the model unvalidated.
func (Record) Address ¶
Address returns the IP address of an A or AAAA record, and reports false for every other type.
func (Record) Compare ¶
Compare orders records the way a zone is exported and the way the GUI walks the name tree: by owner name in the canonical order of RFC 4034 §6.1, then by type, then by data.
func (Record) IsManaged ¶
IsManaged reports whether the record was generated from another one rather than authored. A generated record is not edited directly; its source is.
func (Record) Validate ¶
Validate reports whether the record is well formed on its own.
Rules that need the rest of the zone (the CNAME restrictions of RFC 2181 §10.1, uniform TTLs within an RRset, what a delegation permits below it) belong to ValidateRRset and [ValidateZoneRecords], because a single record cannot see them.
type RecordID ¶
type RecordID string
RecordID identifies a record. Like ZoneID it is a ULID, so a record has a stable identity a diff line can anchor to and the API can address.
type Report ¶ added in v0.2.0
type Report struct {
// Findings are the problems, in the order the names were read.
Findings []Finding
// Truncated is true when [MaxFindings] was reached and the check stopped
// collecting. What is in the report is still true; it is not everything.
Truncated bool
// Records is how many records were read, whether or not they were sound.
Records int
}
Report is what a check found.
type SOA ¶
type SOA struct {
// NS is the MNAME field: the primary server for the zone.
NS Name
// Mbox is the RNAME field: the zone administrator's mailbox, written as a
// domain name, so hostmaster@example.com is hostmaster.example.com.
Mbox Name
Serial Serial
// Refresh, Retry and Expire pace a secondary's transfers. They are 32-bit
// second counts on the wire, the same domain as a TTL, so they share the
// type; the field names carry the meaning that the type does not.
Refresh TTL
Retry TTL
Expire TTL
// Minimum is the negative-caching TTL (RFC 2308 §4), not a floor on record
// TTLs, despite what the name suggests.
Minimum TTL
// TTL is the time to live of the SOA record itself.
TTL TTL
}
SOA holds the start-of-authority parameters of a zone (RFC 1035 §3.3.13).
It is modelled as fields on the zone rather than as an ordinary record. The serial belongs to the journal, not to the user: one commit advances it by exactly one, and that is the invariant IXFR replay depends on. As an editable record it would be one careless edit away from breaking every secondary. See data model §4.1.
func DefaultSOA ¶
DefaultSOA supplies the timers for a newly created zone.
The values follow the recommendations in RFC 1912 §2.2: a refresh interval short enough that a secondary notices a change within an hour even if NOTIFY is lost, a retry well below it, an expiry long enough to survive a weekend outage of the primary, and a negative-cache TTL kept short so a mistake can be corrected quickly.
func ParseSOAData ¶
ParseSOAData reads the presentation form of an SOA's record data back into the parameters it came from.
func (SOA) NegativeTTL ¶
NegativeTTL returns the TTL to put on a negative answer for this zone.
RFC 2308 §3 and §5 make it the lesser of the SOA record's own TTL and the SOA MINIMUM field, so that shortening either one takes effect.
func (SOA) RData ¶
RData returns the SOA in the presentation form of a record's data, which is what a zonefile writes and what the snapshot builder hands to the wire.
func (SOA) Validate ¶
Validate reports whether the SOA parameters are usable.
It enforces the rules that would produce a broken zone, not the style recommendations of RFC 1912: a secondary that cannot refresh, or an expiry that fires before a retry has had a chance, is a fault rather than a matter of taste.
type Serial ¶
type Serial struct {
// contains filtered or unexported fields
}
Serial is a DNS zone serial number, with the arithmetic of RFC 1982.
It is a struct rather than a bare uint32 so that "<" and ">" do not compile. Serials wrap: 4294967295 is older than 0, and comparing them as plain integers is the classic way to make a secondary refuse a transfer forever. Use Serial.After, Serial.Before or Serial.Compare. Equality with "==" is fine and is exactly what RFC 1982 §3.2 defines it to be.
func (Serial) Add ¶
Add returns the serial n steps on, wrapping past the end of the space.
RFC 1982 §3.1 defines addition only for n up to MaxSerialIncrement; adding more would produce a value that is not recognisably newer, so it is refused rather than silently wrapping into the past.
func (Serial) Comparable ¶
Comparable reports whether RFC 1982 §3.2 defines an ordering for s and o.
func (Serial) Compare ¶
Compare orders s against o by the rules of RFC 1982 §3.2, returning a negative number, zero, or a positive number as s is older than, the same as, or newer than o.
When the two are exactly half the space apart, RFC 1982 leaves the relation undefined. Compare still answers, falling back to the raw numeric order so that it stays antisymmetric and callers get a stable answer rather than one that depends on argument order. Serial.Comparable reports that case, and a caller that must not guess should ask first.
func (Serial) IsZero ¶
IsZero reports whether s is the zero serial. Zero is a legal serial value, so this is only useful for spotting a field that was never set.
func (Serial) MarshalJSON ¶
MarshalJSON implements json.Marshaler, writing the serial as a number.
func (Serial) Next ¶
Next returns the following serial, wrapping past the end of the space.
This is the only increment the journal uses: one commit advances a zone by exactly one, which is what lets IXFR replay commits directly. See docs/decisions/, D2.
func (*Serial) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler.
type Severity ¶ added in v0.2.0
type Severity string
Severity says whether the write path would refuse a finding. It is the only distinction a check draws, and it is not a scale: anything wanting finer grading is asking for a different report (D31).
const ( // SeverityError is a finding the write path refuses. A zone holding one // holds data that is unanswerable or contradictory, and this server would // not have let anybody build it, so something reached the database another // way. SeverityError Severity = "error" // SeverityWarning is a finding the write path accepts and would accept // again: correct DNS that is probably not what somebody meant, and may be // exactly what they meant. SeverityWarning Severity = "warning" )
type TSIGAlgorithm ¶ added in v0.2.0
type TSIGAlgorithm string
TSIGAlgorithm names the keyed hash a TSIG key signs with, written the way RFC 8945 §4.2 puts it on the wire: in domain name syntax, with the trailing dot.
const ( HMACSHA256 TSIGAlgorithm = "hmac-sha256." HMACSHA384 TSIGAlgorithm = "hmac-sha384." HMACSHA512 TSIGAlgorithm = "hmac-sha512." )
The algorithms this server offers.
RFC 8945 §6 also makes hmac-sha1 MUST-implement and lists HMAC-MD5 as MAY. Neither is here: the same table calls hmac-sha1 NOT RECOMMENDED and forbids using HMAC-MD5, and offering an algorithm alongside advice not to use it is a footgun with a manual page. See docs/decisions/d28-tsig.md, which argues the departure.
func ParseTSIGAlgorithm ¶ added in v0.2.0
func ParseTSIGAlgorithm(s string) (TSIGAlgorithm, error)
ParseTSIGAlgorithm reads an algorithm name, with or without its trailing dot and in any case: RFC 8945 §9 compares these as domain names.
func TSIGAlgorithms ¶ added in v0.2.0
func TSIGAlgorithms() []TSIGAlgorithm
TSIGAlgorithms are the ones this server signs and verifies with, in the order a client choosing between them should read.
func (TSIGAlgorithm) SecretBytes ¶ added in v0.2.0
func (a TSIGAlgorithm) SecretBytes() int
SecretBytes is how long a secret for this algorithm should be. RFC 8945 §8 asks for at least the length of the keyed hash output, and there is no reason to generate less.
func (TSIGAlgorithm) String ¶ added in v0.2.0
func (a TSIGAlgorithm) String() string
String returns the algorithm as it goes on the wire.
func (TSIGAlgorithm) Valid ¶ added in v0.2.0
func (a TSIGAlgorithm) Valid() bool
Valid reports whether a is one this server will use.
type TTL ¶
type TTL uint32
TTL is a resource record time to live, in seconds.
It is seconds rather than a time.Duration because DNS TTLs are integral seconds on the wire and in a zonefile. A Duration would admit values such as 1500ms that have no representation, and would have to be rounded somewhere — silently, and in more than one place.
const MaxTTL TTL = 1<<31 - 1
MaxTTL is the largest permitted TTL.
RFC 2181 §8 defines the TTL field as an unsigned 32-bit number whose top bit must be zero, and requires a receiver to treat a value with that bit set as zero. Accepting such a value on input would therefore store something that resolvers read as "do not cache", so it is refused instead.
func ParseTTL ¶
ParseTTL parses a TTL given either as a plain number of seconds or in the suffixed form BIND uses, so a value pasted out of an existing zonefile is accepted as typed.
func (TTL) Duration ¶
Duration returns the TTL as a time.Duration, for arithmetic against clocks and timers.
func (TTL) MarshalJSON ¶
MarshalJSON implements json.Marshaler, writing the TTL as a number so that a client receives 3600 rather than "3600".
func (TTL) String ¶
String returns the TTL as a plain number of seconds, which is the form a zonefile and the wire both use.
func (*TTL) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler. It accepts both a number of seconds and a suffixed string such as "1h", so a hand-written YAML file can use whichever is clearer.
type Zone ¶
type Zone struct {
ID ZoneID
Name Name
Kind Kind
// Prefix is the network a reverse zone answers for, derived from its name
// when the zone is created. It is the zero value for a forward zone.
Prefix netip.Prefix
SOA SOA
// DefaultTTL is applied to a record added without one.
DefaultTTL TTL
// AutoReverse enables PTR generation for records in this zone. Nil inherits
// the global setting.
AutoReverse *bool
Disabled bool
Comment string
CreatedAt time.Time
UpdatedAt time.Time
}
Zone is a namespace this server is authoritative for.
func NewZone ¶
NewZone builds a zone for the given apex, deriving its kind and, for a reverse zone, the network it is responsible for.
func (Zone) Contains ¶
Contains reports whether n lies at or below the zone apex. It does not account for delegations: a name below an NS record inside this zone is still within the zone's namespace, just not answered from it.
func (Zone) Covers ¶
Covers reports whether addr falls inside the network a reverse zone answers for. It is always false for a forward zone.
func (Zone) IsApex ¶
IsApex reports whether n is the zone apex, where the SOA and the zone's own NS records live.
func (Zone) ReverseOwner ¶
ReverseOwner returns the owner name a PTR for addr takes inside z.
Usually that is simply the reverse name of the address, because a reverse zone on an octet or nibble boundary is an ancestor of every name below it. The exception is RFC 2317: a classless child such as "0/25.2.0.192.in-addr.arpa." is *not* an ancestor of "10.2.0.192.in-addr.arpa.", even though it answers for that address. The host part is re-attached under the child's own apex instead, giving "10.0/25.2.0.192.in-addr.arpa.", which is the name the parent zone's generated CNAME points at.
func (Zone) Validate ¶
Validate reports whether the zone itself is well formed. It says nothing about the records in it; that is ValidateRRset and the applier's job.