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 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 Class
- 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 RData
- type RRType
- type RRsetKey
- type Record
- type RecordID
- 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 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 ( // 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 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 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 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 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/adr/0001-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 ADR 0001 asks for.
The parameter is the wire library's own type, which widens the dependency ADR 0005 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/adr/0003-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 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.md, D2.
func (*Serial) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler.
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.