tdns

package module
v2.0.0-...-279a53c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 29, 2026 License: BSD-2-Clause Imports: 63 Imported by: 3

Documentation

Overview

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2026 Johan Stenstam, johani@johani.org

* Copyright (c) 2026 Johan Stenstam, johani@johani.org * * DBDelegationBackend stores child delegation data in the ChildDelegationData * SQLite table. Extracts and replaces the existing ApplyChildUpdateToDB logic.

* Copyright (c) 2026 Johan Stenstam, johani@johani.org * * DirectDelegationBackend applies child UPDATEs directly to in-memory zone data. * This is the original behavior for Primary zones.

* Copyright (c) 2026 Johan Stenstam, johani@johani.org * * ZonefileDelegationBackend writes per-child delegation data as DNS zone file * fragments. Each child zone gets its own file that can be $INCLUDEd into the * parent zone file. Files are written atomically (write-to-temp + rename).

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) 2026 Johan Stenstam, johani@johani.org * * In-memory error journal for the combiner/receiver. Records errors that occur * during HandleChunkNotify processing, indexed by distribution ID for targeted * diagnostic queries.

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) 2024 Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) Johan Stenstam, <johani@johani.org>

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) Johan Stenstam, johani@johani.org

* Copyright (c) Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) Johan Stenstam, johani@johani.org

* Copyright (c) Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) 2024 Johan Stenstam

* Copyright (c) Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) 2026 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) 2024 Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Copyright (c) 2025 Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) 2024 Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

* Transport signal synthesis (SVCB / TSYNC)

* Copyright (c) 2025 Johan Stenstam

* Copyright (c) DNS TAPIR

* Copyright (c) 2024 Johan Stenstam, johan.stenstam@internetstiftelsen.se

* Copyright (c) 2024 Johan Stenstam, johani@johani.org

Index

Constants

View Source
const (
	MsgAccept               dns.MsgAcceptAction = iota // Accept the message
	MsgReject                                          // Reject the message with a RcodeFormatError
	MsgIgnore                                          // Ignore the error and send nothing back.
	MsgRejectNotImplemented                            // Reject the message with a RcodeNotImplemented
)

Allowed returned values from a MsgAcceptFunc.

View Source
const (
	// DNS limitations
	MaxDomainLength = 253 // Maximum length of a domain name (RFC 1035)
	MaxLabelLength  = 63  // Maximum length of a single label (RFC 1035)

	// Default cookie for chunk identification
	DefaultCookie = "c0"

	// Sequence number format (e.g., "00-")
	SequenceFormat = "%02d-"
	SequenceLength = 3 // XX- is 3 characters
)
View Source
const (
	DogCfgFile = "/etc/axfr.net/dog.yaml"

	// Legacy constants for backward compatibility
	// New code should use GetDefaultConfigFile() instead
	DefaultCliCfgFile = "/etc/tdns/tdns-cli.yaml"
	DefaultImrCfgFile = "/etc/tdns/tdns-imr.yaml"

	DefaultAuthCfgFile = "/etc/tdns/tdns-auth.yaml"

	DefaultAgentCfgFile    = "/etc/tdns/tdns-agent.yaml"
	DefaultCombinerCfgFile = "/etc/tdns/tdns-combiner.yaml"
	DefaultReporterCfgFile = "/etc/tdns/tdns-reporter.yaml"
	DefaultScannerCfgFile  = "/etc/tdns/tdns-scanner.yaml"
	DefaultKdcCfgFile      = "/etc/tdns/tdns-kdc.yaml"
	DefaultKrsCfgFile      = "/etc/tdns/tdns-krs.yaml"
)
View Source
const (
	UpdateModeReplace = "replace" // Replace all existing delegation data with new data
	UpdateModeDelta   = "delta"   // Use incremental adds/removes (delta updates)
)

Update mode constants for parent delegation updates

View Source
const (
	OutboundSoaSerialKeep     = "keep"     // outbound = inbound serial (default)
	OutboundSoaSerialUnixtime = "unixtime" // outbound = time.Now().Unix()
	OutboundSoaSerialPersist  = "persist"  // outbound = previous outbound serial; bumps written to OutgoingSerials
)

outbound_soa_serial mode values for DnsEngine.OutboundSoaSerial.

View Source
const (
	DnssecPolicyModeKSKZSK = "ksk-zsk"
	DnssecPolicyModeCSK    = "csk"
)
View Source
const (
	Sig0StateCreated       string = "created"
	Sig0StatePublished     string = "published"
	Sig0StateActive        string = "active"
	Sig0StateRetired       string = "retired"
	DnskeyStateCreated     string = "created"
	DnskeyStatePublished   string = "published"
	DnskeyStateDsPublished string = "ds-published"
	DnskeyStateStandby     string = "standby"
	DnskeyStateActive      string = "active"
	DnskeyStateRetired     string = "retired"
	DnskeyStateRemoved     string = "removed"
)
View Source
const (
	SvcbTransportKey uint16 = 65280
	SvcbTLSAKey      uint16 = 65281
	SvcbBootstrapKey uint16 = 65282 // "bootstrap" SvcParamKey per draft-ietf-dnsop-delegation-mgmt-via-ddns-01
)

Private-use SVCB key codes for TDNS. RFC 9460 reserves 65280–65534 for local assignments.

View Source
const DefaultDnskeyTTL = 3600 * time.Second

DefaultDnskeyTTL is the assumed DNSKEY RRset TTL when the actual TTL is not readily available.

View Source
const (
	TimeLayout = "2006-01-02 15:04:05"
)

Variables

View Source
var AgentOptionToString = map[AgentOption]string{}
View Source
var AppTypeToString = map[AppType]string{
	AppTypeAuth:  "auth",
	AppTypeAgent: "agent",

	AppTypeImr:        "imr",
	AppTypeCli:        "cli",
	AppTypeReporter:   "reporter",
	AppTypeScanner:    "scanner",
	AppTypeKdc:        "kdc",
	AppTypeKrs:        "krs",
	AppTypeEdgeSigner: "edgeSigner",
	AppTypeMPSigner:   "mpsigner",
	AppTypeMPAgent:    "mpagent",
	AppTypeMPCombiner: "mpcombiner",
	AppTypeMPAuditor:  "mpauditor",
}
View Source
var AuthOptionToString = map[AuthOption]string{
	AuthOptParentUpdate: "parent-update",
}
View Source
var CombinerOptionToString = map[CombinerOption]string{
	CombinerOptAddSignature: "add-signature",
}
View Source
var DefaultTables = map[string]string{

	"ChildDnskeys": `CREATE TABLE IF NOT EXISTS 'ChildDnskeys' (
id		  INTEGER PRIMARY KEY,
parent	  TEXT,
child	  TEXT,
keyid	  INTEGER,
trusted	  INTEGER,
keyrr	  TEXT,
comment	  TEXT,
UNIQUE (parent, child, keyid)
)`,

	"ChildDelegationData": `CREATE TABLE IF NOT EXISTS 'ChildDelegationData' (
id		  INTEGER PRIMARY KEY,
parent	  TEXT,
child	  TEXT,
owner	  TEXT,
rrtype	  TEXT,
rr		  TEXT,
UNIQUE (owner,rr)
)`,

	"Sig0TrustStore": `CREATE TABLE IF NOT EXISTS 'Sig0TrustStore' (
id		  		  INTEGER PRIMARY KEY,
zonename	  	  TEXT,
keyid		      INTEGER,
validated	      INTEGER DEFAULT 0,
trusted		      INTEGER DEFAULT 0,
dnssecvalidated	  INTEGER DEFAULT 0,
source		      TEXT,
keyrr		      TEXT,
comment		      TEXT,
UNIQUE (zonename, keyid)
)`,

	"Sig0KeyStore": `CREATE TABLE IF NOT EXISTS 'Sig0KeyStore' (
id		  INTEGER PRIMARY KEY,
zonename	  TEXT,
state		  TEXT,
keyid		  INTEGER,
algorithm	  TEXT,
creator	  	  TEXT,
privatekey	  TEXT,
keyrr		  TEXT,
comment		  TEXT,
parent_state	  INTEGER DEFAULT 0,
UNIQUE (zonename, keyid)
)`,

	"DnssecKeyStore": `CREATE TABLE IF NOT EXISTS 'DnssecKeyStore' (
id		  INTEGER PRIMARY KEY,
zonename	  TEXT,
state		  TEXT,
keyid		  INTEGER,
flags		  INTEGER,
algorithm	  TEXT,
creator	  	  TEXT,
privatekey	  TEXT,
keyrr		  TEXT,
comment		  TEXT,
published_at              TEXT DEFAULT '',
retired_at                TEXT DEFAULT '',
UNIQUE (zonename, keyid)
)`,

	"OutgoingSerials": `CREATE TABLE IF NOT EXISTS 'OutgoingSerials' (
		zone       TEXT NOT NULL PRIMARY KEY,
		serial     INTEGER NOT NULL,
		updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
	)`,

	"RolloverKeyState": `CREATE TABLE IF NOT EXISTS 'RolloverKeyState' (
		zone                 TEXT NOT NULL,
		keyid                INTEGER NOT NULL,
		rollover_index       INTEGER NOT NULL,
		rollover_method      TEXT,
		rollover_state_at    TEXT,
		ds_submitted_at      TEXT,
		ds_observed_at       TEXT,
		standby_at           TEXT,
		active_at            TEXT,
		active_seq           INTEGER,
		last_rollover_error  TEXT,
		PRIMARY KEY (zone, keyid)
	)`,

	"RolloverZoneState": `CREATE TABLE IF NOT EXISTS 'RolloverZoneState' (
		zone                           TEXT NOT NULL PRIMARY KEY,
		last_ds_submitted_index_low    INTEGER,
		last_ds_submitted_index_high    INTEGER,
		last_ds_submitted_at           TEXT,
		last_ds_confirmed_index_low    INTEGER,
		last_ds_confirmed_index_high    INTEGER,
		last_ds_confirmed_at           TEXT,
		rollover_phase                 TEXT NOT NULL DEFAULT 'idle',
		rollover_phase_at              TEXT,
		rollover_in_progress           INTEGER NOT NULL DEFAULT 0,
		next_rollover_index            INTEGER NOT NULL DEFAULT 0,
		manual_rollover_requested_at   TEXT,
		manual_rollover_earliest       TEXT,
		observe_started_at             TEXT,
		observe_next_poll_at           TEXT,
		observe_backoff_seconds        INTEGER
	)`,

	"ZoneSigningState": `CREATE TABLE IF NOT EXISTS 'ZoneSigningState' (
		zone              TEXT NOT NULL PRIMARY KEY,
		max_observed_ttl  INTEGER NOT NULL DEFAULT 0,
		updated_at        TEXT
	)`,
}
View Source
var ErrNotHandled = errors.New("query not handled by this handler")

ErrNotHandled is returned by query/notify handlers to indicate they don't handle this request. TDNS will try the next handler or fall back to the default handler.

View Source
var ErrZoneNotReady = errors.New("zone data is not yet ready")

ErrZoneNotReady is returned by GetOwner/GetRRset when the zone data has not been loaded yet (zd.Ready == false). Callers that need to handle initial-load gracefully can check with errors.Is.

View Source
var ErrorTypeToString = map[ErrorType]string{
	ConfigError:  "config",
	RefreshError: "refresh",
	AgentError:   "agent",
	DnssecError:  "DNSSEC",
}
View Source
var Globals = GlobalStuff{

	Verbose:    false,
	Debug:      false,
	ApiClients: map[string]*ApiClient{},
}
View Source
var ImrOptionToString = map[ImrOption]string{
	ImrOptRevalidateNS:            "revalidate-ns",
	ImrOptQueryForTransport:       "query-for-transport",
	ImrOptAlwaysQueryForTransport: "always-query-for-transport",
	ImrOptTransportSignalType:     "transport-signal-type",
	ImrOptQueryForTransportTLSA:   "query-for-transport-tlsa",
	ImrOptUseTransportSignals:     "use-transport-signals",
}
View Source
var KnownCsyncMinSOAs = map[string]uint32{}

Check the minsoa in this CSYNC against the minsoa in the possible already stored CSYNC in the CsyncStatus table. If not found or old min_soa is lower, then update table.

View Source
var ScanTypeToString = map[ScanType]string{
	ScanRRtype: "rrtype",
	ScanCDS:    "cds",
	ScanCSYNC:  "csync",
	ScanDNSKEY: "dnskey",
}
View Source
var ServerName string = "PLACEHOLDER"
View Source
var SignerOptionToString = map[SignerOption]string{}
View Source
var StringToAgentOption = map[string]AgentOption{}
View Source
var StringToAppType = map[string]AppType{
	"auth":  AppTypeAuth,
	"agent": AppTypeAgent,

	"imr":        AppTypeImr,
	"cli":        AppTypeCli,
	"reporter":   AppTypeReporter,
	"scanner":    AppTypeScanner,
	"kdc":        AppTypeKdc,
	"krs":        AppTypeKrs,
	"edgeSigner": AppTypeEdgeSigner,
	"mpsigner":   AppTypeMPSigner,
	"mpagent":    AppTypeMPAgent,
	"mpcombiner": AppTypeMPCombiner,
	"mpauditor":  AppTypeMPAuditor,
}
View Source
var StringToAuthOption = map[string]AuthOption{
	"parent-update": AuthOptParentUpdate,
}
View Source
var StringToCombinerOption = map[string]CombinerOption{
	"add-signature": CombinerOptAddSignature,
}
View Source
var StringToImrOption = map[string]ImrOption{
	"revalidate-ns":              ImrOptRevalidateNS,
	"query-for-transport":        ImrOptQueryForTransport,
	"always-query-for-transport": ImrOptAlwaysQueryForTransport,
	"transport-signal-type":      ImrOptTransportSignalType,
	"query-for-transport-tlsa":   ImrOptQueryForTransportTLSA,
	"use-transport-signals":      ImrOptUseTransportSignals,
}
View Source
var StringToScanType = map[string]ScanType{
	"rrtype": ScanRRtype,
	"cds":    ScanCDS,
	"csync":  ScanCSYNC,
	"dnskey": ScanDNSKEY,
}
View Source
var StringToSignerOption = map[string]SignerOption{}
View Source
var StringToZoneOption = map[string]ZoneOption{
	"delegation-sync-parent":     OptDelSyncParent,
	"delegation-sync-child":      OptDelSyncChild,
	"allow-updates":              OptAllowUpdates,
	"allow-child-updates":        OptAllowChildUpdates,
	"allow-edits":                OptAllowEdits,
	"fold-case":                  OptFoldCase,
	"black-lies":                 OptBlackLies,
	"dont-publish-key":           OptDontPublishKey,
	"dont-publish-jwk":           OptDontPublishJWK,
	"online-signing":             OptOnlineSigning,
	"inline-signing":             OptInlineSigning,
	"multi-provider":             OptMultiProvider,
	"dirty":                      OptDirty,
	"frozen":                     OptFrozen,
	"automatic-zone":             OptAutomaticZone,
	"add-transport-signal":       OptAddTransportSignal,
	"catalog-zone":               OptCatalogZone,
	"catalog-member-auto-create": OptCatalogMemberAutoCreate,
	"catalog-member-auto-delete": OptCatalogMemberAutoDelete,
	"mp-manual-approval":         OptMPManualApproval,
	"multi-signer":               OptMultiSigner,
	"mp-not-listed-error":        OptMPNotListedErr,
	"mp-disallow-edits":          OptMPDisallowEdits,
}
View Source
var Templates = make(map[string]ZoneConf)

Add near the top of the file with other vars

View Source
var ZoneOptionToString = map[ZoneOption]string{
	OptDelSyncParent:     "delegation-sync-parent",
	OptDelSyncChild:      "delegation-sync-child",
	OptAllowUpdates:      "allow-updates",
	OptAllowChildUpdates: "allow-child-updates",
	OptAllowEdits:        "allow-edits",
	OptFoldCase:          "fold-case",
	OptBlackLies:         "black-lies",
	OptDontPublishKey:    "dont-publish-key",
	OptDontPublishJWK:    "dont-publish-jwk",
	OptOnlineSigning:     "online-signing",
	OptInlineSigning:     "inline-signing",
	OptMultiProvider:     "multi-provider",
	OptDirty:             "dirty",
	OptFrozen:            "frozen",
	OptAutomaticZone:     "automatic-zone",

	OptAddTransportSignal:      "add-transport-signal",
	OptCatalogZone:             "catalog-zone",
	OptCatalogMemberAutoCreate: "catalog-member-auto-create",
	OptCatalogMemberAutoDelete: "catalog-member-auto-delete",
	OptMPManualApproval:        "mp-manual-approval",
	OptMultiSigner:             "multi-signer",
	OptMPNotListedErr:          "mp-not-listed-error",
	OptMPDisallowEdits:         "mp-disallow-edits",
}
View Source
var ZoneStoreToString = map[ZoneStore]string{
	XfrZone:   "XfrZone",
	MapZone:   "MapZone",
	SliceZone: "SliceZone",
}
View Source
var ZoneTypeToString = map[ZoneType]string{
	Primary:   "primary",
	Secondary: "secondary",
}
View Source
var Zones = core.NewCmap[*ZoneData]()

Functions

func APICatalog

func APICatalog(app *AppDetails) func(w http.ResponseWriter, r *http.Request)

APICatalog handles catalog zone management operations

func APIcommand

func APIcommand(conf *Config, rtr *mux.Router) func(w http.ResponseWriter, r *http.Request)

func APIcommand(stopCh chan struct{}) func(w http.ResponseWriter, r *http.Request) {

func APIconfig

func APIconfig(conf *Config) func(w http.ResponseWriter, r *http.Request)

func APIdebug

func APIdebug(conf *Config) func(w http.ResponseWriter, r *http.Request)

func APIdelegation

func APIdelegation(delsyncq chan DelegationSyncRequest) func(w http.ResponseWriter, r *http.Request)

func APIdispatcher

func APIdispatcher(conf *Config, router *mux.Router, done <-chan struct{}) error

func APIdispatcherNG

func APIdispatcherNG(conf *Config, router *mux.Router, addrs []string, certFile string, keyFile string, done <-chan struct{}) error

APIdispatcherNG differs from APIdispatcher in that it allows for a different set of addresses and certificate files to be used for the API server.

func APIping

func APIping(conf *Config) func(w http.ResponseWriter, r *http.Request)

func APIscanner

func APIscanner(conf *Config, app *AppDetails, scannerq chan ScanRequest, kdb *KeyDB) func(w http.ResponseWriter, r *http.Request)

func APIscannerDelete

func APIscannerDelete(conf *Config) func(w http.ResponseWriter, r *http.Request)

APIscannerDelete handles DELETE requests for scan job deletion

func APIscannerStatus

func APIscannerStatus(conf *Config) func(w http.ResponseWriter, r *http.Request)

APIscannerStatus handles GET requests for scan job status

func APIzone

func APIzone(app *AppDetails, refreshq chan ZoneRefresher, kdb *KeyDB) func(w http.ResponseWriter, r *http.Request)

func APIzoneDsync

func APIzoneDsync(ctx context.Context, app *AppDetails, refreshq chan ZoneRefresher, kdb *KeyDB) func(w http.ResponseWriter, r *http.Request)

func AtomicRollover

func AtomicRollover(conf *Config, kdb *KeyDB, zone string) (oldKid, newKid uint16, err error)

AtomicRollover performs the active→retired / standby→active swap for a zone's KSK in a single transaction. Implements the §3.4 invariant: at any instant a zone has exactly one active KSK.

For multi-ds the new KSK is collapsed published→active in this same TX (§8.8: pending-child-publish covers the visibility wait). The double-signature method is not handled here — that's 4E.

Atomically:

  • selects KSK_old (the active SEP)
  • selects KSK_new (standby SEP with oldest standby_at; tie-break by rollover_index, then by keytag)
  • KSK_old: active → retired (DnssecKeyStore: state, retired_at; RolloverKeyState: rollover_state_at)
  • KSK_new: standby → active (DnssecKeyStore: state; RolloverKeyState: rollover_state_at, active_at)
  • sets RolloverZoneState.rollover_in_progress = TRUE
  • sets rollover_phase = pending-child-publish (with rollover_phase_at)

On commit, fires triggerResign so the zone re-signs with KSK_new (and without KSK_old).

rollover_due is the caller's responsibility; AtomicRollover assumes the trigger has already fired and there is a usable standby. Returns an error (no state change) if no standby SEP key exists.

func AuthDNSQuery

func AuthDNSQuery(qname string, lg *log.Logger, nameservers []string,
	rrtype uint16, verbose bool) (*core.RRset, int, error)

XXX: Do we still use this anywhere?

func AuthQuery

func AuthQuery(qname, ns string, rrtype uint16) ([]dns.RR, error)

func AuthQueryEngine

func AuthQueryEngine(ctx context.Context, requests chan AuthQueryRequest)

func AutoConfigureZonesFromCatalog

func AutoConfigureZonesFromCatalog(ctx context.Context, update *CatalogZoneUpdate, conf *Config) error

AutoConfigureZonesFromCatalog auto-configures zones based on catalog and meta groups

func BailiwickNS

func BailiwickNS(zonename string, nsrrs []dns.RR) ([]string, error)

Return the names of NS RRs that are in bailiwick for the zone.

func Base32Decode

func Base32Decode(data string) ([]byte, error)

Base32Decode decodes base32 data

func Base32Encode

func Base32Encode(data []byte) string

Base32Encode encodes data to base32

func BootstrapWithParent

func BootstrapWithParent(zone ZoneName, keyName string, algorithm uint8) error

BootstrapWithParent sends a self-signed UPDATE to the parent to bootstrap trust for the child's SIG(0) key.

func BuildChildWholeDSUpdate

func BuildChildWholeDSUpdate(parent, child string, newDS []dns.RR) (*dns.Msg, error)

BuildChildWholeDSUpdate builds a DNS UPDATE for the parent zone that replaces the child's entire DS RRset (DEL ANY DS at the delegation owner, then ADD the given set).

func CaseFoldContains

func CaseFoldContains(slice []string, str string) bool

func ChildDelegationDataUnsynched

func ChildDelegationDataUnsynched(zone, pzone, childpri, parpri string) (bool, []dns.RR, []dns.RR, error)

func ChildGlueRRsToAddrs

func ChildGlueRRsToAddrs(v4glue, v6glue []dns.RR) ([]string, error)

func ChildGlueRRsetsToAddrs

func ChildGlueRRsetsToAddrs(v4glue, v6glue []*core.RRset) ([]string, error)

func Chomp

func Chomp(s string) string

func ChunkBase32Data

func ChunkBase32Data(base32Data string, cookie string) []string

ChunkBase32Data splits base32 data into chunks of maximum size and adds cookie and sequence prefixes

func ChunksToDomains

func ChunksToDomains(chunks []string, domainSuffix string) []string

ChunksToDomains combines chunks into domain names It dynamically calculates how many chunks can fit in each domain

func ClampMetrics

func ClampMetrics() (steps, clamped, unclamped, violations uint64)

ClampMetrics returns a snapshot of the clamp telemetry counters.

func ClampedDuration

func ClampedDuration(configured, R, margin time.Duration) time.Duration

ClampedDuration returns min(configured, R+margin) per automated KSK rollover spec §5.2. If configured or margin is zero, configured is returned unchanged (caller disables clamp or omits policy).

func ClearLastRolloverError

func ClearLastRolloverError(kdb *KeyDB, zone string, keyid uint16) error

ClearLastRolloverError zeroes last_rollover_error for one key. Used by the `rollover reset` CLI to unstick a hard-failed key after operator action.

func ClearManualRolloverRequest

func ClearManualRolloverRequest(kdb *KeyDB, zone string) error

ClearManualRolloverRequest nulls both manual_rollover_* columns. Called by `rollover cancel` and after a manual-ASAP rollover fires.

func ComputeBailiwickNS

func ComputeBailiwickNS(childpri, parpri, owner string) ([]string, []string, error)

XXX: Should be replaced by four calls: one per child and parent primary to get

the NS RRsets and one to new ComputeBailiwickNS() that takes a []dns.RR + zone name

func ComputeDo53Remainder

func ComputeDo53Remainder(pct map[string]uint8) uint8

ComputeDo53Remainder returns the implicit remainder for do53 (clip at 0).

func ComputeRRDiff

func ComputeRRDiff(childpri, parpri, owner string, rrtype uint16) (bool, []dns.RR, []dns.RR, error)

Only used in the CLI version

func ComputeTargetDSSetForZone

func ComputeTargetDSSetForZone(kdb *KeyDB, childZone string, digest uint8) (ds []dns.RR, indexLow, indexHigh int, indexRangeKnown bool, err error)

ComputeTargetDSSetForZone returns the DS RRset the parent should publish for this child, per §6.1: one DS per KSK (SEP) in states created, ds-published, standby, published, active, retired (created included for multi-DS pre-publish DS at parent). Digest is SHA-256 only in this phase. DS owner names use child as FQDN. indexLow/indexHigh are min/max rollover_index when every contributing key has a RolloverKeyState row; otherwise indexRangeKnown is false and callers must not treat the indices as authoritative for RolloverZoneState.

func CopyFile

func CopyFile(src, dst string) (int64, error)

func CountKskInRolloverPipeline

func CountKskInRolloverPipeline(kdb *KeyDB, zone string) (int, error)

CountKskInRolloverPipeline counts SEP keys in pre-terminal rollover pipeline states.

func CreateChildReplaceUpdate

func CreateChildReplaceUpdate(parent, child string, newNS, newA, newAAAA, newDS []dns.RR) (*dns.Msg, error)

CreateChildReplaceUpdate creates a DNS UPDATE message that replaces all delegation data CreateChildReplaceUpdate creates a DNS UPDATE message for parent that replaces the delegation for child. It removes all existing NS records for the child and deletes A/AAAA glue for any in-bailiwick nameservers discovered among the provided new NS, A, and AAAA records, then inserts the new NS and glue RRs. Returns an error if parent or child is empty or equal to ".".

func CreateChildUpdate

func CreateChildUpdate(parent, child string, adds, removes []dns.RR) (*dns.Msg, error)

Parent is the zone to apply the update to. XXX: This is to focused on creating updates for child delegation info. Need a more general CreateChildUpdate constructs a DNS UPDATE message for the given parent zone that applies the provided additions and removals for a child delegation.

If any removed RR is an NS whose target name is within the child zone, the function also removes A and AAAA glue RRsets for that NS name. It validates that parent and child are non-empty and not ".", returning an error when validation fails. When Globals.Debug is set, the resulting message is printed.

It returns the constructed DNS UPDATE message, or an error if validation fails.

func CreateUpdate

func CreateUpdate(zone string, adds, removes []dns.RR) (*dns.Msg, error)

CreateUpdate creates a DNS UPDATE message for the given zone, applies the provided removals and additions, and enables EDNS0 (payload 1232 with the DO bit set) so that EDNS0 Extended DNS Error (EDE) information can be returned. It returns the constructed *dns.Msg, or an error if the zone is empty or ".".

func DefaultQueryHandler

func DefaultQueryHandler(ctx context.Context, req *DnsQueryRequest) error

DefaultQueryHandler handles all other queries using zone-based query handling. This is registered with qtype=0 to catch all query types that aren't handled by other handlers. Exported so apps (like KDC) can register it even when no zones are in config.

func DnsDoHEngine

func DnsDoHEngine(ctx context.Context, conf *Config, dohaddrs []string, certFile, keyFile string,
	ourDNSHandler func(w dns.ResponseWriter, r *dns.Msg)) error

func DnsDoQEngine

func DnsDoQEngine(ctx context.Context, conf *Config, doqaddrs []string, cert *tls.Certificate,
	ourDNSHandler func(w dns.ResponseWriter, r *dns.Msg)) error

func DnsDoTEngine

func DnsDoTEngine(ctx context.Context, conf *Config, dotaddrs []string, cert *tls.Certificate,
	ourDNSHandler func(w dns.ResponseWriter, r *dns.Msg)) error

func DnsEngine

func DnsEngine(ctx context.Context, conf *Config) error

func DomainsToBase32

func DomainsToBase32(domains []string, cookie string) (string, error)

DomainsToBase32 extracts and combines base32 data from domain names

func DomainsToJson

func DomainsToJson(domains []string, cookie string) ([]byte, error)

DomainsToJson converts domain names back to JSON

func DomainsToStruct

func DomainsToStruct(domains []string, cookie string, result interface{}) error

DomainsToStruct converts domain names back to a struct

func DotServerQnameResponse

func DotServerQnameResponse(qname string, w dns.ResponseWriter, r *dns.Msg)

func DsyncUpdateTargetName

func DsyncUpdateTargetName(zonename string) string

DsyncUpdateTargetName computes the DSYNC UPDATE target name for a parent zone from the global config. Returns empty string if not configured.

func EffectiveMarginForZone

func EffectiveMarginForZone(kdb *KeyDB, zone string, pol *DnssecPolicy) (time.Duration, error)

EffectiveMarginForZone is the exported alias used by the auto-rollover status CLI to estimate the next pending-child-withdraw transition.

func EnsureRolloverZoneRow

func EnsureRolloverZoneRow(kdb *KeyDB, zone string) error

func ExpirationFromTtl

func ExpirationFromTtl(addedAt time.Time, ttl uint32) time.Time

ExpirationFromTtl converts an insertion time and TTL seconds to an expiration time. This is only used for formatting and display, not for cache logic.

func ExportDelegationData

func ExportDelegationData(backend DelegationBackend, parentZone, outfile string, defaultTTL uint32) error

ExportDelegationData writes all delegation data for a parent zone to a file in DNS zone file format. Called from the /delegation API handler. defaultTTL is applied to any RR with TTL=0.

func Fatal

func Fatal(msg string, args ...any)

Fatal logs at Error level and exits. slog has no built-in Fatal.

func FindSoaRefresh

func FindSoaRefresh(zd *ZoneData) (uint32, error)

func FinishDnssecPolicy

func FinishDnssecPolicy(policyName string, conf *DnssecPolicyConf, out *DnssecPolicy) error

FinishDnssecPolicy fills Mode, Rollover, TTLS, and Clamping on out from conf and KSK/ZSK lifetimes. out must already carry Name, Algorithm, KSK, ZSK, CSK from the caller.

func GenerateAndStageKey

func GenerateAndStageKey(kdb *KeyDB, zone, creator string, alg uint8, keytype string) (uint16, error)

GenerateAndStageKey generates a new DNSSEC key and stages it (created → published).

func GenerateJobID

func GenerateJobID() (string, error)

GenerateJobID generates a unique job ID using crypto/rand.

func GenerateKskRolloverCreated

func GenerateKskRolloverCreated(kdb *KeyDB, zone, creator string, alg uint8, method RolloverMethod) (keyid uint16, rolloverIndex int, err error)

GenerateKskRolloverCreated inserts a new KSK in state created with RolloverKeyState (multi-ds / double-signature pipeline).

func GetAlpn

func GetAlpn(svcb *dns.SVCB) []string

GetAlpn extracts the ALPN protocols from an SVCB RR (from SVCBAlpn param).

func GetDefaultConfigFile

func GetDefaultConfigFile() string

GetDefaultConfigFile returns the default config file path based on Globals.App.Name. The path is constructed as /etc/tdns/{app-name}.yaml. If Globals.App.Name is empty, it returns an empty string.

func GetDynamicZoneFilePath

func GetDynamicZoneFilePath(zoneName, zoneDirectory string) string

GetDynamicZoneFilePath returns the expected file path for a dynamic zone file This is useful for checking if a file exists before attempting to load it

func GetNameservers

func GetNameservers(KeyName string, zd *ZoneData) ([]string, error)

func GetTransportParam

func GetTransportParam(svcb *dns.SVCB) (map[string]uint8, bool, error)

GetTransportParam fetches and parses the transport parameter from the SVCB RR, if present.

func InBailiwick

func InBailiwick(zone string, ns *dns.NS) bool

func IsIxfr

func IsIxfr(rrs []dns.RR) bool

func IsPEMFormat

func IsPEMFormat(keyData string) bool

IsPEMFormat detects if a stored private key is in PKCS#8 PEM format (new format). It returns true if the data decodes to a PEM block of type "PRIVATE KEY"; otherwise false.

func JsonToBase32Domains

func JsonToBase32Domains(jsonData []byte, domainSuffix string, cookie string) ([]string, error)

JsonToBase32Domains converts JSON data to a set of domain names Each domain name contains chunks of base32-encoded data with maximized label sizes

func KeyStateWorker

func KeyStateWorker(ctx context.Context, conf *Config) error

KeyStateWorker runs periodic checks on DNSSEC key states and performs automatic transitions and standby key maintenance.

func LoadLastRolloverError

func LoadLastRolloverError(kdb *KeyDB, zone string, keyid uint16) (string, error)

LoadLastRolloverError returns last_rollover_error for one key (empty string if unset or the row doesn't exist). Used by the `auto-rollover status` CLI for read-only inspection.

func LoadZoneSigningMaxTTL

func LoadZoneSigningMaxTTL(kdb *KeyDB, zone string) (uint32, error)

LoadZoneSigningMaxTTL returns the most recently persisted max_observed_ttl for the zone, or 0 if no row exists yet (e.g. zone has never completed a full sign pass since the column was introduced).

func Logger

func Logger(subsystem string) *slog.Logger

Logger returns a *slog.Logger for the given subsystem. The subsystem gets its own level (defaulting to the global level). Call it freely — if the subsystem doesn't exist yet, it's created on first use.

func LookupChildKeyAtApex

func LookupChildKeyAtApex(ctx context.Context, childZone string, imr *Imr) ([]dns.RR, bool, error)

LookupChildKeyAtApex queries the child zone apex for KEY records via the IMR engine. Returns the KEY RRs found, whether the response was DNSSEC- validated, and any error.

func LookupChildKeyAtSignal

func LookupChildKeyAtSignal(ctx context.Context, childZone string, imr *Imr) ([]dns.RR, bool, error)

LookupChildKeyAtSignal queries _sig0key.<childzone>._signal.<ns>. for KEY records for each NS serving the child zone. Returns the union of KEY RRs found, whether all responses were DNSSEC-validated, and any error.

func LookupSVCB

func LookupSVCB(name string) (*core.RRset, error)

func LookupTlsaRR

func LookupTlsaRR(name string) (*core.RRset, error)

func MarshalTLSAToString

func MarshalTLSAToString(tlsa *dns.TLSA) (string, error)

MarshalTLSAToString renders a TLSA record to the "usage selector matching data" representation.

func MarshalTransport

func MarshalTransport(transports map[string]uint8) string

MarshalTransport converts a transport map back to a canonical string (sorted by key).

func MsgAcceptFunc

func MsgAcceptFunc(dh dns.Header) dns.MsgAcceptAction

func MsgPrint

func MsgPrint(m *dns.Msg, server string, elapsed time.Duration, short bool, options map[string]string)

func NSInBailiwick

func NSInBailiwick(zone string, ns *dns.NS) bool

func NeedsResigning

func NeedsResigning(rrsig *dns.RRSIG) bool

XXX: Perhaps a working algorithm woul be to test for the remaining signature lifetime to be something like

less than 3 x resigning interval?

func NormalizeAddress

func NormalizeAddress(addr string) string

NormalizeAddress ensures an address has a port number. If the address doesn't have a port, ":53" is appended. This allows users to specify addresses as either "IP" or "IP:port" in config. Returns empty string if input is empty.

func NormalizeAddresses

func NormalizeAddresses(addresses []string) []string

NormalizeAddresses ensures all addresses have a port number. If an address doesn't have a port, ":53" is appended. This allows users to specify addresses as either "IP" or "IP:port" in config.

func NormalizeParentAgentAddr

func NormalizeParentAgentAddr(raw string) (string, error)

NormalizeParentAgentAddr parses rollover.parent-agent or CLI --parent-agent into host:port (default port 53).

func Notifier

func Notifier(ctx context.Context, notifyreqQ chan NotifyRequest) error

XXX: The whole point with the NotifierEngine is to be able to control the max rate of send notifications per zone. This is not yet implemented, but this is where to do it.

func NotifyCatalogZoneUpdate

func NotifyCatalogZoneUpdate(update *CatalogZoneUpdate) error

NotifyCatalogZoneUpdate invokes all registered callbacks

func NotifyHandler

func NotifyHandler(ctx context.Context, conf *Config) error

func NotifyHandlerWithCallback

func NotifyHandlerWithCallback(ctx context.Context, conf *Config, handlerFunc func(context.Context, *DnsNotifyRequest) error) error

NotifyHandlerWithCallback consumes DnsNotifyRequest messages from the DnsNotifyQ channel and calls the provided handler function. This allows custom NOTIFY handlers (like KDC for confirmation NOTIFYs) to process NOTIFYs via channels. handlerFunc: Function that processes a DnsNotifyRequest

func NotifyReporter

func NotifyReporter(conf *Config, tsigSecrets map[string]string, addr string) (stop func(context.Context) error, err error)

func NotifyResponder

func NotifyResponder(ctx context.Context, dnr *DnsNotifyRequest, zonech chan ZoneRefresher, scannerq chan ScanRequest) error

TODO: Add per-source rate limiting for NOTIFY messages. An attacker could flood the server with NOTIFY messages to trigger excessive zone refreshes and scanner scans. Consider a token bucket or sliding window rate limiter keyed by source IP.

func ObservedDSSetMatchesExpected

func ObservedDSSetMatchesExpected(observed, expected []dns.RR) bool

ObservedDSSetMatchesExpected implements the match rule in design §7.5 (normative).

func PEMToPrivateKey

func PEMToPrivateKey(pemData string) (crypto.PrivateKey, error)

PEMToPrivateKey parses pemData as a PKCS#8 PEM-encoded private key and returns it as a crypto.PrivateKey.

The input must contain a PEM block of type "PRIVATE KEY" encoded in PKCS#8. Returns an error if the input PEMToPrivateKey parses a PKCS#8 PEM-encoded private key and returns the corresponding crypto.PrivateKey. It returns an error if pemData is empty, if no PEM block can be decoded, if the PEM block type is not "PRIVATE KEY", or if PKCS#8 parsing fails.

func ParseLogLevel

func ParseLogLevel(s string) slog.Level

ParseLogLevel converts a string like "debug", "info", "warn", "error" to an slog.Level. Defaults to Info for unrecognized strings.

func ParsePrivateKeyFromDB

func ParsePrivateKeyFromDB(privatekey, algorithm, keyrrstr string) (crypto.PrivateKey, uint8, string, error)

ParsePrivateKeyFromDB parses a private key from the database, detecting whether it's in old BIND format or new PEM format, and returns a crypto.PrivateKey. ParsePrivateKeyFromDB parses a private key stored in the database, accepting either PKCS#8 PEM (new) or legacy BIND private-key formats.

It returns the parsed crypto.PrivateKey, the DNSSEC algorithm numeric code, and a BIND-format private-key string suitable for backward compatibility. An error is ParsePrivateKeyFromDB parses a stored private key (either PKCS#8 PEM or legacy BIND format) and returns the crypto.PrivateKey, the DNSSEC algorithm numeric code, and a BIND-format private-key string suitable for use with PrepareKeyCache.

If the input `privatekey` is detected as a PKCS#8 PEM, the function parses it and derives a BIND-format private-key string from `keyrrstr` (a DNSKEY/KEY RR string) for compatibility. If `privatekey` is not PEM, it is treated as the legacy BIND private-key material and is wrapped into a full BIND-format string. The `algorithm` parameter is validated and converted to the corresponding DNSSEC algorithm code.

Returns an error if the algorithm is unknown, PEM decoding/parsing fails, the public key RR cannot be parsed, conversion to BIND format fails, or the legacy BIND parsing logic fails.

func ParseTLSAFromSvcbLocal

func ParseTLSAFromSvcbLocal(local *dns.SVCBLocal) (*dns.TLSA, error)

ParseTLSAFromSvcbLocal parses a private SVCB TLSA key into a dns.TLSA.

func ParseTLSAString

func ParseTLSAString(s string) (*dns.TLSA, error)

ParseTLSAString parses a TLSA RDATA string of the form "usage selector matching data".

func ParseTsigKeys

func ParseTsigKeys(keyconf *KeyConf) (int, map[string]string)

ParseTsigKeys parses the TSIG keys from the configuration and returns the number of keys and the secrets in the format expected by the dns.Server and the dns.Client. It also stores the keys with more details in the tdns.Globals struct.

func PollParentDSUntilMatch

func PollParentDSUntilMatch(ctx context.Context, childZone string, expected []dns.RR, agentAddr string, initialWait, pollMax, confirmTimeout time.Duration) (matched bool, err error)

PollParentDSUntilMatch queries the parent-agent until the answer matches expected (§7.2, §7.5) or confirmTimeout elapses. Backoff between attempts: next = min(2*previous, pollMax), with the first pre-query wait equal to initialWait.

func PrintDsRR

func PrintDsRR(rr dns.RR, leftpad, rightmargin int)

func PrintGenericRR

func PrintGenericRR(rr dns.RR, leftpad, rightmargin int)

func PrintJwkRR

func PrintJwkRR(rr dns.RR, leftpad, rightmargin int)

func PrintKeyRR

func PrintKeyRR(rr dns.RR, rrtype, ktype string, keyid uint16, leftpad, rightmargin int)

leftpad = amount of white space instead of the domain name on continuation lines during multiline output

func PrintMsgFull

func PrintMsgFull(m *dns.Msg, width int) string

func PrintMsgSection

func PrintMsgSection(header string, section []dns.RR, width int) string

func PrintRR

func PrintRR(rr dns.RR, leftpad int, options map[string]string) error

func PrintRrsigRR

func PrintRrsigRR(rr dns.RR, leftpad, rightmargin int)

func PrintSoaRR

func PrintSoaRR(rr dns.RR, leftpad, rightmargin int)

func PrintSvcbRR

func PrintSvcbRR(rr dns.RR, leftpad, rightmargin int)

func PrivKeyToBindFormat

func PrivKeyToBindFormat(privkey, algorithm string) (string, error)

func PrivateKeyToPEM

func PrivateKeyToPEM(privkey crypto.PrivateKey) (string, error)

PrivateKeyToPEM converts a crypto.PrivateKey to PKCS#8 PEM format. PrivateKeyToPEM converts a crypto.PrivateKey to a PKCS#8 PEM-encoded string. PrivateKeyToPEM converts a crypto.PrivateKey into a PKCS#8 PEM-encoded string.

It returns the PEM-formatted private key. An error is returned if the provided private key is nil or if marshaling the key to PKCS#8 DER fails.

func PromoteStandbyKskIfNoActive

func PromoteStandbyKskIfNoActive(conf *Config, kdb *KeyDB, zone string)

PromoteStandbyKskIfNoActive activates one standby KSK when the zone has none (bootstrap).

func QueryHandler

func QueryHandler(ctx context.Context, conf *Config, handlerFunc func(context.Context, *DnsQueryRequest) error) error

QueryHandler consumes DnsQueryRequest messages from the DnsQueryQ channel and calls the provided handler function. This allows custom query handlers (like KDC for KMREQ queries) to process queries via channels. handlerFunc: Function that processes a DnsQueryRequest

func QueryParentAgentDS

func QueryParentAgentDS(ctx context.Context, childZone, agentAddr string) ([]dns.RR, error)

QueryParentAgentDS asks the configured parent-agent (addr:port) for child DS over TCP.

func QueryParentKeyState

func QueryParentKeyState(kdb *KeyDB, imr *Imr, keyName string, keyid uint16) (uint8, error)

QueryParentKeyState sends a KeyState EDNS(0) inquiry to the parent and returns the parent's reported state for the key.

func QueryParentKeyStateDetailed

func QueryParentKeyStateDetailed(kdb *KeyDB, imr *Imr, keyName string, keyid uint16) (uint8, string, error)

QueryParentKeyStateDetailed is like QueryParentKeyState but also returns the ExtraText from the KeyState response, for display purposes.

func RRsetToString

func RRsetToString(rrset *core.RRset) string

func ReadPubKey

func ReadPubKey(filename string) (dns.RR, uint16, uint8, error)

func ReadPubKeys

func ReadPubKeys(keydir string) (map[string]dns.KEY, error)

ReadPubKeys reads all ".key" public key files in the given directory and returns a mapping from each key's owner name to its dns.KEY record.

If keydir is empty, the function returns an error. Only files with the ".key" suffix are processed; other files are ignored. Each processed file is parsed as a DNS RR and must be of type KEY; parse failures, unexpected RR ReadPubKeys reads all files with the ".key" suffix in the provided directory, parses each as a DNS KEY RR, and returns a map from the RR owner name to the dns.KEY value.

The keydir parameter is the path to the directory containing public key files. Non-".key" files are ignored. If any filesystem operation fails or a file cannot be parsed as a DNS KEY RR, an error is returned.

func RecursiveDNSQuery

func RecursiveDNSQuery(server, qname string, qtype uint16, timeout time.Duration, retries int) (*core.RRset, error)

func RecursiveDNSQueryWithConfig

func RecursiveDNSQueryWithConfig(qname string, qtype uint16, timeout time.Duration, retries int) (*core.RRset, error)

func RecursiveDNSQueryWithResolvConf

func RecursiveDNSQueryWithResolvConf(qname string, qtype uint16, timeout time.Duration, retries int) (*core.RRset, error)

func RecursiveDNSQueryWithServers

func RecursiveDNSQueryWithServers(qname string, qtype uint16, timeout time.Duration,
	retries int, resolvers []string) (*core.RRset, error)

func RefreshEngine

func RefreshEngine(ctx context.Context, conf *Config)

func RegisterAPIRoute

func RegisterAPIRoute(routeFunc APIRouteFunc) error

RegisterAPIRoute registers a function that will add API routes to the router. IMPORTANT: The route registration function is called DURING SetupAPIRouter(), so routes must be registered BEFORE calling SetupAPIRouter(). For apps that call SetupAPIRouter() before initializing their subsystems, routes should be registered directly on the router after SetupAPIRouter() returns, rather than using RegisterAPIRoute().

Example usage:

tdns.RegisterAPIRoute(func(router *mux.Router) error {
    router.PathPrefix("/api/v1/kdc").HandlerFunc(kdc.APIKdcZone)
    return nil
})

func RegisterBootstrapActiveKSK

func RegisterBootstrapActiveKSK(kdb *KeyDB, zone string, keyid uint16, method RolloverMethod, alg uint8) error

RegisterBootstrapActiveKSK records a KSK that was generated directly into state=active (e.g. by EnsureActiveDnssecKeys when no KSK existed yet) into RolloverKeyState, assigning it the next rollover_index for the zone and stamping active_at = now. Without this, rolloverDue cannot fire scheduled rollovers (active_at is unset) and the K-step clamp scheduler refuses to run (tNextRoll returns ok=false).

Idempotent: if a row already exists for (zone, keyid), only active_at is updated. Skips when method == RolloverMethodNone (zone has no rollover policy).

func RegisterCatalogZoneCallback

func RegisterCatalogZoneCallback(callback CatalogZoneCallback)

RegisterCatalogZoneCallback registers a callback for catalog zone updates

func RegisterDebugNotifyHandler

func RegisterDebugNotifyHandler() error

RegisterDebugNotifyHandler registers a debug handler that logs all DNS NOTIFY messages. The handler logs NOTIFY details and always returns ErrNotHandled to pass through to the next handler. This is useful for debugging and monitoring.

The handler is registered with qtype=0, meaning it will be called for ALL NOTIFYs before any specific qtype handlers. It should be registered first (before other handlers).

Example usage:

tdns.RegisterDebugNotifyHandler()
tdns.RegisterNotifyHandler(core.TypeCHUNK, myHandler)

func RegisterDebugQueryHandler

func RegisterDebugQueryHandler() error

RegisterDebugQueryHandler registers a debug handler that logs all DNS queries. The handler logs query details and always returns ErrNotHandled to pass through to the next handler. This is useful for debugging and monitoring.

The handler is registered with qtype=0, meaning it will be called for ALL queries before any specific qtype handlers. It should be registered first (before other handlers).

Example usage:

tdns.RegisterDebugQueryHandler()
tdns.RegisterQueryHandler(hpke.TypeKMREQ, myHandler)

func RegisterDefaultQueryHandlers

func RegisterDefaultQueryHandlers(conf *Config) error

RegisterDefaultQueryHandlers registers the default zone-based query handler. This is called automatically during TDNS initialization. The default handler is registered if (a) zones are configured in the config, or (b) app type is Agent (agent gets an autozone from SetupAgent, needed for SOA/AXFR). Apps that need .server. query support should register ServerQueryHandler themselves.

func RegisterEngine

func RegisterEngine(name string, engine EngineFunc) error

RegisterEngine registers a long-running engine that will be started by TDNS. Engines are started as goroutines and run until the context is cancelled. They are started after TDNS initialization is complete.

Example usage:

tdns.RegisterEngine("KeyStateWorker", func(ctx context.Context) error {
    return kdc.KeyStateWorker(ctx, kdcDB, &kdcConf)
})

func RegisterImrClientQueryHook

func RegisterImrClientQueryHook(hook ImrClientQueryHookFunc) error

RegisterImrClientQueryHook registers a hook that is called when an external client query arrives at the IMR listener. Multiple hooks can be registered and are called in registration order.

func RegisterImrOutboundQueryHook

func RegisterImrOutboundQueryHook(hook ImrOutboundQueryHookFunc) error

RegisterImrOutboundQueryHook registers a hook that is called before the IMR sends an iterative query to an authoritative server. Multiple hooks can be registered and are called in registration order.

func RegisterImrResponseHook

func RegisterImrResponseHook(hook ImrResponseHookFunc) error

RegisterImrResponseHook registers a hook that is called after the IMR receives a response from an authoritative server. Multiple hooks can be registered and are called in registration order.

func RegisterNotifyHandler

func RegisterNotifyHandler(qtype uint16, handler NotifyHandlerFunc) error

RegisterNotifyHandler registers a handler for DNS NOTIFY messages. Multiple handlers can be registered for the same qtype - they will be called in registration order. If a handler returns ErrNotHandled, TDNS will try the next handler or fall back to default. If qtype is 0, handler is called for ALL NOTIFYs (use with caution, e.g., for debug handlers). Handlers registered with qtype=0 are called before handlers registered for specific qtypes.

This function can be called before TDNS is initialized (uses global storage), or after initialization (uses conf.Internal.NotifyHandlers). During NOTIFY processing, TDNS checks both locations.

func RegisterQueryHandler

func RegisterQueryHandler(qtype uint16, handler QueryHandlerFunc) error

RegisterQueryHandler registers a handler for a specific query type. Multiple handlers can be registered for the same qtype - they will be called in registration order. If a handler returns ErrNotHandled, TDNS will try the next handler or fall back to default. If qtype is 0, handler is called for ALL query types (use with caution, e.g., for debug handlers). Handlers registered with qtype=0 are called before handlers registered for specific qtypes.

This function can be called before TDNS is initialized (uses global storage), or after initialization (uses conf.Internal.QueryHandlers). During query processing, TDNS checks both locations.

func RegisterUpdateHandler

func RegisterUpdateHandler(matcher UpdateMatcherFunc, handler UpdateHandlerFunc) error

RegisterUpdateHandler registers a handler for DNS UPDATE messages. The matcher function determines which UPDATEs should be handled by this handler. Multiple handlers can be registered - they will be called in registration order. If a handler returns ErrNotHandled, TDNS will try the next handler or fall back to default.

This function can be called before TDNS is initialized (uses global storage), or after initialization (uses conf.Internal.UpdateHandlers). During UPDATE processing, TDNS checks both locations.

Example usage:

// Match bootstrap UPDATEs (name pattern _bootstrap.*)
tdns.RegisterUpdateHandler(
	func(req *tdns.DnsUpdateRequest) bool {
		for _, rr := range req.Msg.Ns {
			if strings.HasPrefix(rr.Header().Name, "_bootstrap.") {
				return true
			}
		}
		return false
	},
	func(ctx context.Context, req *tdns.DnsUpdateRequest) error {
		return kdc.HandleBootstrapUpdate(ctx, req, kdcDB, &kdcConf)
	},
)

func RegisterZoneOptionHandler

func RegisterZoneOptionHandler(opt ZoneOption, handler ZoneOptionHandler)

RegisterZoneOptionHandler registers a callback for a zone option. Multiple handlers can be registered for the same option. Handlers fire synchronously during ParseZones, in registration order.

func RegisterZoneOptionValidator

func RegisterZoneOptionValidator(opt ZoneOption, validator ZoneOptionValidator)

RegisterZoneOptionValidator registers a validator for a zone option. Only one validator per option is supported; a second registration for the same option replaces the first.

Validators run during parseZoneOptions (inside the switch) and can reject the option by returning false. They should call zd.SetError(ConfigError, ...) to record the reason for rejection.

Register validators before ParseZones runs (e.g. from MainInit before calling the parent MainInit).

func ResignerEngine

func ResignerEngine(ctx context.Context, zoneresignch chan *ZoneData)

func ResignerEngine(zoneresignch chan ZoneRefresher, stopch chan struct{}) {

func RolloverAutomatedTick

func RolloverAutomatedTick(ctx context.Context, conf *Config, kdb *KeyDB, imr *Imr, zd *ZoneData, propagationDelay time.Duration, now time.Time) error

RolloverAutomatedTick runs one slice of automated KSK rollover for multi-ds (pipeline fill, DS push / observe phase machine). double-signature is not implemented yet. propagationDelay is the kasp.propagation_delay used by pending-child-publish.

func RolloverIndexForKey

func RolloverIndexForKey(kdb *KeyDB, zone string, keyid uint16) (int, bool, error)

func RolloverKeyActiveAt

func RolloverKeyActiveAt(kdb *KeyDB, zone string, keyid uint16) (*time.Time, error)

RolloverKeyActiveAt returns the active_at timestamp for one key, or nil if unset. Used by rollover_due to determine whether the active KSK has lived past policy.ksk.lifetime, and by the auto-rollover status CLI.

func RolloverKeyActiveSeq

func RolloverKeyActiveSeq(kdb *KeyDB, zone string, keyid uint16) (int, error)

RolloverKeyActiveSeq returns the active_seq for one key, or (-1, nil) if the key has never been active (the column is NULL). Used by the auto-rollover status CLI for read-only display.

func RolloverKeyDsObservedAt

func RolloverKeyDsObservedAt(kdb *KeyDB, zone string, keyid uint16) (*time.Time, error)

RolloverKeyDsObservedAt returns the ds_observed_at timestamp, or nil if unset.

func RolloverKeyStandbyAt

func RolloverKeyStandbyAt(kdb *KeyDB, zone string, keyid uint16) (*time.Time, error)

RolloverKeyStandbyAt returns the standby_at timestamp, or nil if unset.

func RolloverKeyStateAt

func RolloverKeyStateAt(kdb *KeyDB, zone string, keyid uint16) (*time.Time, error)

RolloverKeyStateAt returns the rollover_state_at timestamp, or nil if unset. This is the most recent transition time recorded by the rollover machinery for this key — useful as a fallback "current state since" reading.

func RolloverKeyidsByIndexRange

func RolloverKeyidsByIndexRange(kdb *KeyDB, zone string, low, high int64) ([]uint16, error)

RolloverKeyidsByIndexRange returns the keyids whose rollover_index lies in [low, high] (inclusive), ordered by rollover_index ascending. Used by the auto-rollover status CLI to translate the submitted/confirmed index ranges into operator-meaningful keyids that match the per-key table.

func RunKeysCmd

func RunKeysCmd(conf *Config, appType AppType, args []string) error

RunKeysCmd runs the "keys" subcommand (generate | show). Used by tdns-cli agent keys / tdns-cli combiner keys. conf must be loaded from the server's config file.

func SanitizeForJSON

func SanitizeForJSON(v interface{}) interface{}

SanitizeForJSON is a wrapper function that sanitizes a struct for JSON serialization

func ScannerEngine

func ScannerEngine(ctx context.Context, conf *Config) error

func SendUnixPing

func SendUnixPing(target string, dieOnError bool) (bool, error)

func ServerQueryHandler

func ServerQueryHandler(ctx context.Context, req *DnsQueryRequest) error

ServerQueryHandler handles queries for qnames ending in ".server." with ClassCHAOS. NOTE: This function is now optional. .server. queries are automatically handled by createAuthDnsHandler() in do53.go as a fallback before returning REFUSED. This exported function is kept for backward compatibility or for apps that want to handle .server. queries earlier in the handler chain.

func SetManualRolloverRequest

func SetManualRolloverRequest(kdb *KeyDB, zone string, requestedAt, earliest time.Time) error

SetManualRolloverRequest stamps manual_rollover_requested_at = now and manual_rollover_earliest = earliest. Called by `rollover asap` after ComputeEarliestRollover succeeds.

func SetRolloverPhase

func SetRolloverPhase(kdb *KeyDB, zone, phase string) error

func SetSubsystemLevel

func SetSubsystemLevel(name string, level slog.Level)

SetSubsystemLevel sets or updates the log level for a subsystem. Goroutine-safe (uses atomic LevelVar).

func SetupCliLogging

func SetupCliLogging()

SetupCliLogging sets up logging for CLI commands. Default: no source info, no timestamps. Verbose/Debug: adds source info.

func SetupLogging

func SetupLogging(logfile string, logConf LogConf) error

SetupLogging configures the slog-based logging system with lumberjack rotation. It also bridges the old log package so that existing log.Printf calls flow through slog to the same output.

func ShowAPI

func ShowAPI(rtr *mux.Router) ([]string, error)

Stolen from labstuff:apihandler_funcs.go

func Shutdowner

func Shutdowner(conf *Config, msg string)

func SignMsg

func SignMsg(m dns.Msg, signer string, sak *Sig0ActiveKeys) (*dns.Msg, error)

func SignerStaleRRSIGsDropped

func SignerStaleRRSIGsDropped() uint64

SignerStaleRRSIGsDropped returns a snapshot of the stale-RRSIG-drop counter. Used by tests and a future observability surface; per-zone breakdown can be added later if needed.

func SprintUpdates

func SprintUpdates(actions []dns.RR) string

func StartEngine

func StartEngine(app *AppDetails, name string, engineFunc func() error)

startEngine wraps engine functions in a goroutine with error handling. It logs errors if the engine function returns an error, preventing silent failures.

func StartEngineNoError

func StartEngineNoError(app *AppDetails, name string, engineFunc func())

startEngineNoError wraps engine functions that don't return errors. This is for engines that handle errors internally or never fail during startup.

func StartRegisteredEngines

func StartRegisteredEngines(ctx context.Context)

StartRegisteredEngines starts all registered engines as goroutines. This should be called after TDNS initialization is complete. Engines run until the context is cancelled.

func StripKeyFileComments

func StripKeyFileComments(data []byte) []byte

StripKeyFileComments removes lines that are empty or start with '#' (after trim), so JWK/key files with comment headers (e.g. KDC/KRS-style) parse as valid JSON. Preserves line breaks and indentation of kept lines.

func StructToBase32Domains

func StructToBase32Domains(data interface{}, domainSuffix string, cookie string) ([]string, error)

StructToBase32Domains converts a Go struct to a set of domain names Each domain name contains chunks of base32-encoded data with maximized label sizes

func TLSAToSvcbLocal

func TLSAToSvcbLocal(tlsa *dns.TLSA) (*dns.SVCBLocal, error)

TLSAToSvcbLocal marshals a TLSA record into a private SVCB key/value pair.

func TransitionRolloverKskDsPublishedToStandby

func TransitionRolloverKskDsPublishedToStandby(conf *Config, kdb *KeyDB, now time.Time, propagationDelay time.Duration)

TransitionRolloverKskDsPublishedToStandby moves SEP keys from ds-published to standby after propagation delay.

func TtlPrint

func TtlPrint(expiration time.Time) string

TtlPrint returns a human-friendly TTL remaining until expiration. If the expiration time has passed, it returns "expired".

func TtyIntQuestion

func TtyIntQuestion(query string, oldval int, force bool) int

func TtyQuestion

func TtyQuestion(query, oldval string, force bool) string

func TtyRadioButtonQ

func TtyRadioButtonQ(query, defval string, choices []string) string

func TtyYesNo

func TtyYesNo(query, defval string) string

func TypeBitMapToString

func TypeBitMapToString(tbm []uint16) string

func UnstickRollover

func UnstickRollover(kdb *KeyDB, zone string) (int, error)

UnstickRollover clears the persisted DS-submitted range on the zone row and last_rollover_error on every key row for the zone, in a single transaction. Returns the number of key rows whose last_rollover_error was cleared.

Why: observeHardFail leaves last_ds_submitted_index_low/high populated when it returns the zone to idle. The idle branch's kskIndexPushNeeded then sees the target DS set unchanged from what was last submitted and never re-arms a push, leaving the zone permanently stuck even after the operator has fixed whatever caused the original observation timeout. UnstickRollover is the operator nudge: drop the submitted range so the next idle tick re-arms pending-parent-push, and clear stale per-key errors so status output isn't misleading.

func UpdateDnssecKeyState

func UpdateDnssecKeyState(kdb *KeyDB, zonename string, keyid uint16, newstate string) (err error)

UpdateDnssecKeyState transitions a DNSSEC key to a new state and sets the appropriate lifecycle timestamp. When transitioning to "published", sets published_at. When transitioning to "retired", sets retired_at. Invalidates the cache for both old and new states.

func UpdateDnssecKeyStateTx

func UpdateDnssecKeyStateTx(tx *Tx, kdb *KeyDB, zonename string, keyid uint16, newstate string) error

UpdateDnssecKeyStateTx updates a DNSKEY's state on an existing transaction. Used by callers that need to wrap the state change together with other rollover bookkeeping in a single TX (§9.4 two-store consistency).

func UpdateHandler

func UpdateHandler(ctx context.Context, conf *Config) error

func UpdateParentState

func UpdateParentState(kdb *KeyDB, keyName string, keyid uint16, parentState uint8)

UpdateParentState persists the parent's KeyState response in the local keystore.

func UpdateResponder

func UpdateResponder(dur *DnsUpdateRequest, updateq chan UpdateRequest) error

func UpsertZoneSigningMaxTTL

func UpsertZoneSigningMaxTTL(kdb *KeyDB, zone string, maxTTL uint32) error

UpsertZoneSigningMaxTTL records the maximum RRset TTL observed during a full zone-sign pass. Called at end-of-pass from SignZone. The value is reset (not accumulated) per pass so a TTL reduction in the zone takes effect after one complete sign cycle. Read by the rollover worker's pending-child-withdraw phase to bound wait time by the longest-lived RRSIG that could still be cached at resolvers.

func ValidateBySection

func ValidateBySection(config *Config, configsections map[string]interface{}, cfgfile string) (string, error)

func ValidateCertAndKeyFiles

func ValidateCertAndKeyFiles(fl validator.FieldLevel) bool

validateCertAndKeyFiles is the custom validation function

func ValidateConfig

func ValidateConfig(v *viper.Viper, cfgfile string) error

func ValidateConfigWithCustomValidator

func ValidateConfigWithCustomValidator(v *viper.Viper, cfgfile string) error

ValidateConfigWithCustomValidator validates the config using the custom validator XXX: Not used at the moment.

func ValidateDatabaseFile

func ValidateDatabaseFile(config *Config) error

ValidateDatabaseFile checks that db.file is set to a non-empty path.

func ValidateDnssecPoliciesFromFile

func ValidateDnssecPoliciesFromFile(path string) error

ValidateDnssecPoliciesFromFile parses a YAML file with a top-level dnssecpolicies: map and validates every policy the same way as runtime config loading.

func ValidateExplicitServerSVCB

func ValidateExplicitServerSVCB(svcb *dns.SVCB) error

ValidateExplicitServerSVCB validates an explicit SVCB RR for server use. It checks transport weights against the ALPN list, if present. If transport is absent, the record is accepted as-is. If transport is present but alpn is missing, it's invalid.

func ValidateZones

func ValidateZones(c *Config, cfgfile string) error

func ValidatorEngine

func ValidatorEngine(ctx context.Context, conf *Config)

func VerifyCertAgainstTlsaRR

func VerifyCertAgainstTlsaRR(tlsarr *dns.TLSA, rawcert []byte) error

func VerifyChildKey

func VerifyChildKey(ctx context.Context, childZone string, keyRR string, imr *Imr) (verified bool, dnssecValidated bool)

VerifyChildKey checks whether a child's KEY (identified by keyRR string) can be found via the configured verification mechanisms (at-apex, at-ns). Returns true if any mechanism succeeds (key found + optionally DNSSEC-validated).

func VerifyKey

func VerifyKey(KeyName string, key string, keyid uint16, zd *ZoneData, updatetrustq chan<- KeyBootstrapperRequest)

func WalkRoutes

func WalkRoutes(router *mux.Router, address string)

func WildcardReplace

func WildcardReplace(rrs []dns.RR, qname, origqname string) []dns.RR

func ZoneHasParentSyncAgent

func ZoneHasParentSyncAgent(zone ZoneName) bool

ZoneHasParentSyncAgent checks whether the zone's HSYNCPARAM record has parentsync=agent. Returns false if HSYNCPARAM is absent or parentsync=owner.

func ZoneIsReady

func ZoneIsReady(zonename string) func() bool

ZoneIsReady returns a function that can be used as a PreCondition for a DeferredUpdate. The returned function will return true if the zone exists and is ready, otherwise false.

func ZoneTransferPrint

func ZoneTransferPrint(zname, upstream string, serial uint32, ttype uint16, options map[string]string) error

Types

type APIRouteFunc

type APIRouteFunc func(router *mux.Router) error

APIRouteFunc is the function signature for API route registration. The function receives the API router and should register routes on it.

type APIRouteRegistration

type APIRouteRegistration struct {
	RouteFunc APIRouteFunc
}

APIRouteRegistration stores an API route registration

type AgentId

type AgentId string

func (AgentId) String

func (id AgentId) String() string

type AgentMgmtPost

type AgentMgmtPost struct {
	Command    string   `json:"command"`
	Zone       ZoneName `json:"zone"`
	AgentId    AgentId  `json:"agent_id"`
	RRType     uint16
	RR         string
	RRs        []string
	AddedRRs   []string // for update-local-zonedata
	RemovedRRs []string // for update-local-zonedata
	Upstream   AgentId
	Downstream AgentId
	Data       map[string]interface{} `json:"data,omitempty"` // Generic data field for custom parameters
	Response   chan *AgentMgmtResponse
}

AgentMgmt{Post,Response} are used in the mgmt API

type AgentMgmtResponse

type AgentMgmtResponse struct {
	Identity    AgentId
	Status      string
	Time        time.Time
	AgentConfig MultiProviderConf
	Msg         string
	Error       bool
	ErrorMsg    string
	Data        interface{} `json:"data,omitempty"` // Generic data field for custom responses
}

type AgentOption

type AgentOption uint8

type ApiClient

type ApiClient struct {
	Name      string
	Client    *http.Client
	BaseUrl   string
	Addresses []string // if non-empty, replace the host part of the BaseUrl with each of these

	AuthMethod string
	UseTLS     bool
	Verbose    bool
	Debug      bool

	// deSEC stuff (from MUSIC)
	Email    string
	Password string
	TokViper *viper.Viper
	// contains filtered or unexported fields
}

func NewClient

func NewClient(name, baseurl, apikey, authmethod, rootcafile string) *ApiClient

func (*ApiClient) Delete

func (api *ApiClient) Delete(endpoint string) (int, []byte, error)

api Delete (not tested)

func (*ApiClient) Get

func (api *ApiClient) Get(endpoint string) (int, []byte, error)

api Get (not tested)

func (*ApiClient) Post

func (api *ApiClient) Post(endpoint string, data []byte) (int, []byte, error)

func (*ApiClient) Put

func (api *ApiClient) Put(endpoint string, data []byte) (int, []byte, error)

api Put

func (*ApiClient) RequestNG

func (api *ApiClient) RequestNG(method, endpoint string, data interface{}, dieOnError bool) (int, []byte, error)

func (*ApiClient) RequestNGWithContext

func (api *ApiClient) RequestNGWithContext(ctx context.Context, method, endpoint string, data interface{}, dieOnError bool) (int, []byte, error)

func (*ApiClient) SendPing

func (api *ApiClient) SendPing(pingcount int, dieOnError bool) (PingResponse, error)

func (*ApiClient) ShowApi

func (api *ApiClient) ShowApi()

func (*ApiClient) StartDaemon

func (api *ApiClient) StartDaemon(maxwait int, slurp bool, command string, daemonFlags []string)

slurp means that we'll connect to stdout and stderr on the underlying daemon to check for possible error messages (if it dies somehow). The problem is that connecting to stdout doesn't work well, it kills the daemon after a while. So we only want to slurp when explicitly checking for errors. command is an optional parameter specifying the daemon binary path. If empty, it falls back to viper.GetString("common.command") for backward compatibility. daemonFlags are additional command-line flags to pass to the daemon (e.g., --config, --debug, -v)

func (*ApiClient) StopDaemon

func (api *ApiClient) StopDaemon()

func (*ApiClient) UpdateDaemon

func (api *ApiClient) UpdateDaemon(data CommandPost, dieOnError bool) (int, CommandResponse, error)

func (*ApiClient) UrlReport

func (api *ApiClient) UrlReport(method, endpoint string, data []byte)

func (*ApiClient) UrlReportNG

func (api *ApiClient) UrlReportNG(method, fullurl string, data []byte)

type ApiServerAppConf

type ApiServerAppConf struct {
	Addresses []string
	ApiKey    SensitiveString
}

type ApiServerConf

type ApiServerConf struct {
	Addresses []string        `validate:"required"` // Must be in addr:port format
	ApiKey    SensitiveString `validate:"required"`
	CertFile  string          `validate:"required,file,certkey"`
	KeyFile   string          `validate:"required,file"`
	UseTLS    bool
	Server    ApiServerAppConf
	Agent     ApiServerAppConf
	// MSA       ApiServerAppConf
	Combiner ApiServerAppConf
}

type AppDetails

type AppDetails struct {
	Name             string
	Version          string
	Type             AppType
	Date             string
	ServerBootTime   time.Time
	ServerConfigTime time.Time
}

type AppType

type AppType uint8
const (
	AppTypeAuth AppType = iota + 1
	AppTypeAgent
	// AppTypeCombiner
	AppTypeImr // simplified recursor
	AppTypeCli
	AppTypeReporter
	AppTypeScanner
	AppTypeKdc        // Key Distribution Center
	AppTypeKrs        // Key Receiving Service (edge receiver)
	AppTypeEdgeSigner // NYI
	AppTypeMPSigner   // MP signer (tdns-mp): DNS infra from tdns, MP wiring from tdns-mp
	AppTypeMPAgent    // MP agent (tdns-mp): DNS infra from tdns, MP wiring from tdns-mp
	AppTypeMPCombiner // MP combiner (tdns-mp): DNS infra from tdns, MP wiring from tdns-mp
	AppTypeMPAuditor  // MP auditor (tdns-mp): read-only observer, no zone contributions
)

type AuthOption

type AuthOption uint8
const (
	AuthOptParentUpdate AuthOption = iota + 1
)

type AuthQueryRequest

type AuthQueryRequest struct {
	// contains filtered or unexported fields
}

AuthQueryNG is the same as AuthQuery, but returns an RRset instead of a []dns.RR to be able to keep any RRSIGs. AuthQuery should be phased out. ns must be in addr:port format

type AuthQueryResponse

type AuthQueryResponse struct {
	// contains filtered or unexported fields
}

type BindPrivateKey

type BindPrivateKey struct {
	Private_Key_Format string `yaml:"Private-key-format"`
	Algorithm          string `yaml:"Algorithm"`
	PrivateKey         string `yaml:"PrivateKey"`
}

type BumperData

type BumperData struct {
	Zone   string
	Result chan BumperResponse
}

type BumperResponse

type BumperResponse struct {
	Time      time.Time
	Zone      string
	Msg       string
	OldSerial uint32
	NewSerial uint32
	Error     bool
	ErrorMsg  string
	Status    bool
}

type CatalogConf

type CatalogConf struct {
	GroupPrefixes GroupPrefixesConf             `yaml:"group_prefixes" mapstructure:"group_prefixes"`
	Policy        CatalogPolicy                 `yaml:"policy" mapstructure:"policy"` // Deprecated, kept for backward compatibility
	ConfigGroups  map[string]*ConfigGroupConfig `yaml:"config_groups" mapstructure:"config_groups"`
	MetaGroups    map[string]*ConfigGroupConfig `yaml:"meta_groups" mapstructure:"meta_groups"` // Deprecated, kept for backward compatibility
	SigningGroups map[string]*SigningGroupInfo  `yaml:"signing_groups" mapstructure:"signing_groups"`
}

CatalogConf defines configuration for catalog zone support (RFC 9432)

type CatalogMemberZone

type CatalogMemberZone struct {
	ZoneName     string
	Hash         string    // SHA256 hash of zone name (first 16 chars)
	Groups       []string  // List of group names associated with this zone (RFC 9432 terminology)
	DiscoveredAt time.Time // Timestamp when the zone was first added to the catalog
}

CatalogMemberZone represents a member zone in the catalog

type CatalogMembership

type CatalogMembership struct {
	CatalogZoneName string
	MemberZones     map[string]*CatalogMemberZone // zonename -> member data
	AvailableGroups []string                      // List of all defined groups (RFC 9432 terminology)
	// contains filtered or unexported fields
}

CatalogMembership manages the membership data for a catalog zone

func GetOrCreateCatalogMembership

func GetOrCreateCatalogMembership(catalogZoneName string) *CatalogMembership

GetOrCreateCatalogMembership returns the membership for a catalog zone, creating if needed

func (*CatalogMembership) AddGroup

func (cm *CatalogMembership) AddGroup(group string) error

AddGroup adds a group to the available groups list

func (*CatalogMembership) AddMemberZone

func (cm *CatalogMembership) AddMemberZone(zoneName string) error

AddMemberZone adds a zone to the catalog

func (*CatalogMembership) AddZoneGroup

func (cm *CatalogMembership) AddZoneGroup(zoneName, group string) error

AddZoneGroup associates a group with a zone

func (*CatalogMembership) GetGroups

func (cm *CatalogMembership) GetGroups() []string

GetGroups returns all available groups (thread-safe copy)

func (*CatalogMembership) GetMemberZones

func (cm *CatalogMembership) GetMemberZones() map[string]*MemberZone

GetMemberZones returns all member zones (thread-safe copy)

func (*CatalogMembership) RemoveGroup

func (cm *CatalogMembership) RemoveGroup(group string) error

RemoveGroup removes a group from the available groups list

func (*CatalogMembership) RemoveMemberZone

func (cm *CatalogMembership) RemoveMemberZone(zoneName string) error

RemoveMemberZone removes a zone from the catalog

func (*CatalogMembership) RemoveZoneGroup

func (cm *CatalogMembership) RemoveZoneGroup(zoneName, group string) error

RemoveZoneGroup disassociates a group from a zone

type CatalogPolicy

type CatalogPolicy struct {
	Zones struct {
		Add    string `yaml:"add" mapstructure:"add" validate:"omitempty,oneof=auto manual"`       // "auto" or "manual"
		Remove string `yaml:"remove" mapstructure:"remove" validate:"omitempty,oneof=auto manual"` // "auto" or "manual"
	} `yaml:"zones" mapstructure:"zones"`
}

CatalogPolicy defines policy for how catalog zones are processed

type CatalogPost

type CatalogPost struct {
	Command     string   `json:"command"`      // "create" | "zone-add" | "zone-delete" | "zone-list" | "group-add" | "group-delete" | "group-list" | "zone-group-add" | "zone-group-delete" | "notify-add" | "notify-remove" | "notify-list"
	CatalogZone string   `json:"catalog_zone"` // Name of the catalog zone
	Zone        string   `json:"zone"`         // Member zone name
	Group       string   `json:"group"`        // Group name (RFC 9432 terminology)
	Groups      []string `json:"groups"`       // Multiple groups (for zone-add with --groups flag)
	Address     string   `json:"address"`      // Notify address (IP:port) for notify-add/notify-remove
}

CatalogPost represents a request to manage catalog zones

type CatalogResponse

type CatalogResponse struct {
	Time            time.Time              `json:"time"`
	Error           bool                   `json:"error"`
	ErrorMsg        string                 `json:"error_msg,omitempty"`
	Msg             string                 `json:"msg,omitempty"`
	Zones           map[string]*MemberZone `json:"zones,omitempty"`            // For zone-list command
	Groups          []string               `json:"groups,omitempty"`           // For group-list command
	NotifyAddresses []string               `json:"notify_addresses,omitempty"` // For notify-list command
}

CatalogResponse represents the response from catalog operations

type CatalogZoneCallback

type CatalogZoneCallback func(update *CatalogZoneUpdate) error

CatalogZoneCallback is called when a catalog zone is updated

type CatalogZoneUpdate

type CatalogZoneUpdate struct {
	CatalogZone string                 `json:"catalog_zone"`
	MemberZones map[string]*MemberZone `json:"member_zones"`
	Serial      uint32                 `json:"serial"`
	UpdateTime  time.Time              `json:"update_time"`
}

CatalogZoneUpdate contains information about catalog zone changes

func ParseCatalogZone

func ParseCatalogZone(zd *ZoneData) (*CatalogZoneUpdate, error)

ParseCatalogZone parses a catalog zone and extracts member zones and groups

type ChildDelegationData

type ChildDelegationData struct {
	DelHasChanged    bool      // When returned from a scanner, this indicates that a change has been detected
	ParentSerial     uint32    // The parent serial that this data was correct for
	Timestamp        time.Time // Time at which this data was fetched
	ChildName        string
	RRsets           map[string]map[uint16]core.RRset // map[ownername]map[rrtype]RRset
	NS_rrs           []dns.RR
	A_glue           []dns.RR
	A_glue_rrsigs    []dns.RR
	AAAA_glue        []dns.RR
	AAAA_glue_rrsigs []dns.RR
	NS_rrset         *core.RRset
	DS_rrset         *core.RRset
	A_rrsets         []*core.RRset
	AAAA_rrsets      []*core.RRset
}

type ClampParams

type ClampParams struct {
	K            int
	Margin       time.Duration
	TRoll        time.Time // for the validity invariant check
	MaxServedTTL uint32    // 0 = no steady-state ceiling
}

ClampParams carries the current TTL-clamp inputs into SignRRset. A non-nil value means at least one clamp is active for this sign pass:

  • K-step rollover clamp (K, Margin, TRoll): active near a scheduled rollover; ceiling = K * Margin.
  • Steady-state TTL ceiling (MaxServedTTL): always-on cap from policy ttls.max_served; ceiling = MaxServedTTL.

SignRRset takes the minimum of (UnclampedTTL, K*Margin if K>0, MaxServedTTL if >0) and writes that to every RR header TTL before generating the RRSIG.

nil means no clamp at all (zone has clamping.enabled: false AND no max_served set AND no rollover scheduled).

func ClampParamsForZone

func ClampParamsForZone(kdb *KeyDB, zone string, pol *DnssecPolicy, now time.Time) (*ClampParams, error)

ClampParamsForZone builds a *ClampParams for the zone at `now`, or nil if no clamp is active (neither rollover K-step nor steady-state max_served). Called by SignZone before each sign pass on a clamping zone.

func (*ClampParams) CeilingTTL

func (c *ClampParams) CeilingTTL() uint32

CeilingTTL returns the K * margin ceiling in seconds.

type ClampingPolicy

type ClampingPolicy struct {
	Enabled bool
	Margin  time.Duration
}

type CombinerOption

type CombinerOption uint8
const (
	CombinerOptAddSignature CombinerOption = iota + 1
)

type CommandPost

type CommandPost struct {
	Command    string
	SubCommand string
	Zone       string
	Force      bool
}

type CommandResponse

type CommandResponse struct {
	AppName      string
	Time         time.Time
	Status       string
	Zone         string
	Names        []string
	Zones        map[string]ZoneConf
	Msg          string
	ApiEndpoints []string
	Error        bool
	ErrorMsg     string
}

type Config

type Config struct {
	Service        ServiceConf
	DnsEngine      DnsEngineConf
	Imr            ImrEngineConf `yaml:"imrengine" mapstructure:"imrengine"`
	ApiServer      ApiServerConf
	DnssecPolicies map[string]DnssecPolicyConf
	MultiSigner    map[string]MultiSignerConf `yaml:"multisigner"`
	MultiProvider  *MultiProviderConf         `yaml:"multi-provider" mapstructure:"multi-provider"`
	Catalog        *CatalogConf               `yaml:"catalog" mapstructure:"catalog"`
	DynamicZones   DynamicZonesConf           `yaml:"dynamiczones" mapstructure:"dynamiczones"`
	Zones          []ZoneConf                 `yaml:"zones"`
	Templates      []ZoneConf                 `yaml:"templates"`
	Kasp           KaspConf                   `yaml:"kasp" mapstructure:"kasp"`
	Keys           KeyConf
	Db             DbConf
	Registrars     map[string][]string
	Log            LogConf
	Internal       InternalConf
}
var Conf Config

func LoadConfigForKeys

func LoadConfigForKeys(path string) (*Config, error)

LoadConfigForKeys reads the given YAML config file and decodes it into Config. Used by tdns-cli agent/combiner keys to get long_term_jose_priv_key path. Does not process includes or run full ParseConfig.

func (*Config) APIagent

func (conf *Config) APIagent(refreshZoneCh chan<- ZoneRefresher, kdb *KeyDB) func(w http.ResponseWriter, r *http.Request)

APIagent handles the /agent management API endpoint. After MP migration, only IMR commands remain here. All MP commands (config, add-rr, resync, discover, etc.) are now handled by tdns-mp's APIagent.

func (*Config) AddDynamicZoneToConfig

func (conf *Config) AddDynamicZoneToConfig(zd *ZoneData) error

AddDynamicZoneToConfig adds or updates a zone in the dynamic config file

func (*Config) CheckDynamicConfigFileIncluded

func (conf *Config) CheckDynamicConfigFileIncluded(includedFiles []string) bool

CheckDynamicConfigFileIncluded checks if the dynamic config file is included in the main config Returns true if included, false otherwise (logs warning if not included)

func (*Config) FindDnsEngineAddrs

func (conf *Config) FindDnsEngineAddrs() ([]string, error)

Extract the addresses we listen on from the dnsengine configuration. Exclude localhost and non-standard ports.

func (*Config) ImrEngine

func (conf *Config) ImrEngine(ctx context.Context, quiet bool) error

func (*Config) InitImrEngine

func (conf *Config) InitImrEngine(quiet bool) error

InitImrEngine creates and initializes the Imr (cache, stubs, debug logging) and wires it into conf.Internal.ImrEngine and Globals.ImrEngine. This is safe to call synchronously before other engines start, guaranteeing that the Imr is available by the time transport bridges and agent registries are created.

Apps that need the IMR available early (e.g. mpagent) call this before StartEngine("ImrEngine", ...). Apps that don't need early availability can just call ImrEngine() which calls InitImrEngine() internally if needed.

Idempotent: if conf.Internal.ImrEngine is already set, returns nil without touching any state. The IMR is a process singleton by intent — its priming cache, root hints, and validation state are expensive to construct and would diverge between two instances. The guard at the top of this function makes that singleton property hold by construction, regardless of which application initialises the IMR or in what order. First-init wins; the `quiet` parameter on subsequent calls is ignored.

IMPORTANT: tdns-mp depends on this split. The mpagent calls InitImrEngine() synchronously at startup so that conf.Internal.ImrEngine is guaranteed non-nil before transport bridges and agent registries are created. Without this, the *tdnsmp.Imr embedding wraps a nil *tdns.Imr and promoted method calls panic. Do not fold InitImrEngine back into ImrEngine without updating tdns-mp/v2/start_agent.go.

func (*Config) InitializeKeyDB

func (conf *Config) InitializeKeyDB() error

func (*Config) LoadDynamicZoneFiles

func (conf *Config) LoadDynamicZoneFiles(ctx context.Context) error

LoadDynamicZoneFiles loads dynamic zones from the dynamic config file on startup This should be called after ParseZones() but before engines start It loads zones that were persisted in previous runs

func (*Config) LocalIdentity

func (conf *Config) LocalIdentity() string

LocalIdentity returns the local node's identity string, regardless of role. Used by sync API handlers (APIhello, APIbeat, APIsyncPing) so they work on agent, combiner, and signer without referencing conf.Agent.Identity directly.

func (*Config) MainInit

func (conf *Config) MainInit(ctx context.Context, defaultcfg string) error

func (*Config) MainLoop

func (conf *Config) MainLoop(ctx context.Context, cancel context.CancelFunc)

func (*Config) ParentSyncAfterKeyPublication

func (conf *Config) ParentSyncAfterKeyPublication(zone ZoneName, keyName string, keyid uint16, algorithm uint8)

ParentSyncAfterKeyPublication is called asynchronously after onLeaderElected publishes the SIG(0) KEY to the combiner. It queries the parent via KeyState EDNS(0) to determine if bootstrap is needed, and if so, sends the bootstrap UPDATE.

Flow:

  1. Check HSYNCPARAM parentsync=agent (caller already verified OptDelSyncChild)
  2. Query parent via KeyState EDNS(0) inquiry
  3. KeyStateTrusted → done
  4. KeyStateUnknown → bootstrap
  5. KeyStateBootstrapAutoOngoing → poll
  6. Query failure → retry with backoff

func (*Config) ParseAuthOptions

func (conf *Config) ParseAuthOptions()

func (*Config) ParseConfig

func (conf *Config) ParseConfig(reload bool) error

func (*Config) ParseZones

func (conf *Config) ParseZones(ctx context.Context, reload bool) ([]string, error)

func ParseZones(zones map[string]tdns.ZoneConf, zrch chan tdns.ZoneRefresher) error {

func (*Config) ReloadConfig

func (conf *Config) ReloadConfig() (string, error)

NOTE: MsgQs and its associated message types (KeystateInventoryMsg, KeystateSignalMsg, EditsResponseMsg, ConfigResponseMsg, AuditResponseMsg, StatusUpdateMsg) live in tdns-mp/v2/config.go. They are MP-only and were removed from tdns during the tdns-mp extraction.

func (*Config) ReloadZoneConfig

func (conf *Config) ReloadZoneConfig(ctx context.Context) (string, error)

func (*Config) RemoveDynamicZoneFromConfig

func (conf *Config) RemoveDynamicZoneFromConfig(zoneName string) error

RemoveDynamicZoneFromConfig removes a zone from the dynamic config file

func (*Config) SetupAPIRouter

func (conf *Config) SetupAPIRouter(ctx context.Context) (*mux.Router, error)

func (*Config) SetupSimpleAPIRouter

func (conf *Config) SetupSimpleAPIRouter(ctx context.Context) (*mux.Router, error)

The simple API router is sufficient for tdns-imr, tdns-scanner and tdns-reporter.

func (*Config) ShouldPersistZone

func (conf *Config) ShouldPersistZone(zd *ZoneData) bool

ShouldPersistZone checks if a zone should be persisted based on configuration Returns true if the zone should be written to disk

func (*Config) StartAgent

func (conf *Config) StartAgent(ctx context.Context, apirouter *mux.Router) error

StartAgent starts subsystems for tdns-agent

func (*Config) StartAuth

func (conf *Config) StartAuth(ctx context.Context, apirouter *mux.Router) error

StartAuth starts subsystems for tdns-auth

func (*Config) StartImr

func (conf *Config) StartImr(ctx context.Context, apirouter *mux.Router) error

StartImr starts subsystems for tdns-imr

func (*Config) StartScanner

func (conf *Config) StartScanner(ctx context.Context, apirouter *mux.Router) error

StartScanner starts subsystems for tdns-scanner

func (*Config) WriteDynamicConfigFile

func (conf *Config) WriteDynamicConfigFile() error

WriteDynamicConfigFile writes the current dynamic zones to the config file This should be called whenever a dynamic zone is created, updated, or deleted

type ConfigEntry

type ConfigEntry struct {
	Key   string
	Value interface{}
}

OrderedConfig preserves the order of configuration entries

type ConfigGroupConfig

type ConfigGroupConfig struct {
	Name     string   `yaml:"-" mapstructure:"-"` // Populated from map key
	Upstream string   `yaml:"upstream" mapstructure:"upstream"`
	TsigKey  string   `yaml:"tsig_key" mapstructure:"tsig_key"`
	Store    string   `yaml:"store" mapstructure:"store"`
	Options  []string `yaml:"options" mapstructure:"options"`
}

ConfigGroupConfig provides configuration for zone transfers from catalog config groups (RFC 9432 terminology)

type ConfigPost

type ConfigPost struct {
	Command string // status | sync | ...
}

type ConfigResponse

type ConfigResponse struct {
	AppName         string
	Time            time.Time
	DnsEngine       DnsEngineConf
	ApiServer       ApiServerConf
	Identities      []string
	CombinerOptions map[CombinerOption]bool
	DBFile          string
	Msg             string
	Error           bool
	ErrorMsg        string
}

type CurrentScanData

type CurrentScanData struct {
	RRset  *core.RRset `json:"-"` // Current RRset for ScanRRtype test, nil if no data exists (not JSON serialized)
	CDS    *core.RRset `json:"-"` // Current CDS RRset for ScanCDS test, nil if no data exists (not JSON serialized)
	DS     *core.RRset `json:"-"` // Current DS RRset from delegation backend (not JSON serialized)
	CSYNC  *core.RRset `json:"-"` // Current CSYNC RRset for ScanCSYNC test, nil if no data exists (not JSON serialized)
	DNSKEY *core.RRset `json:"-"` // Current DNSKEY RRset for ScanDNSKEY test, nil if no data exists (not JSON serialized)
}

ScanCurrentData holds the current data for a scan test For ScanRRtype tests, it contains the current RRset for the queried RRtype at the name Note: This struct is not JSON-serializable directly. Use ToJSON() to convert to CurrentScanDataJSON.

func (*CurrentScanData) ToJSON

func (csd *CurrentScanData) ToJSON() CurrentScanDataJSON

ToJSON converts CurrentScanData to a JSON-serializable format

type CurrentScanDataJSON

type CurrentScanDataJSON struct {
	RRset  *core.RRsetString `json:"rrset,omitempty"`
	CDS    *core.RRsetString `json:"cds,omitempty"`
	DS     *core.RRsetString `json:"ds,omitempty"`
	CSYNC  *core.RRsetString `json:"csync,omitempty"`
	DNSKEY *core.RRsetString `json:"dnskey,omitempty"`
}

CurrentScanDataJSON is the JSON-serializable version of CurrentScanData

type CustomValidator

type CustomValidator struct {
	*validator.Validate
}

CustomValidator is a struct that embeds the validator.Validate type

func NewCustomValidator

func NewCustomValidator() (*CustomValidator, error)

NewCustomValidator creates a new instance of CustomValidator

type DBDelegationBackend

type DBDelegationBackend struct {
	// contains filtered or unexported fields
}

func (*DBDelegationBackend) ApplyChildUpdate

func (b *DBDelegationBackend) ApplyChildUpdate(parentZone string, ur UpdateRequest) error

func (*DBDelegationBackend) GetDelegationData

func (b *DBDelegationBackend) GetDelegationData(parentZone, childZone string) (map[string]map[uint16][]dns.RR, error)

func (*DBDelegationBackend) ListChildren

func (b *DBDelegationBackend) ListChildren(parentZone string) ([]string, error)

func (*DBDelegationBackend) Name

func (b *DBDelegationBackend) Name() string

type DbConf

type DbConf struct {
	File string // `validate:"required"`
}

type DebugPost

type DebugPost struct {
	Command string
	Zone    string
	Qname   string
	Qtype   uint16
	Verbose bool
}

type DebugResponse

type DebugResponse struct {
	AppName    string
	Time       time.Time
	Status     string
	Zone       string
	OwnerIndex map[string]int
	RRset      core.RRset
	//	TrustedDnskeys	map[string]dns.DNSKEY
	//	TrustedSig0keys	map[string]dns.KEY
	TrustedDnskeys  []cache.CachedDnskeyRRset
	TrustedSig0keys map[string]Sig0Key
	CachedRRsets    []cache.CachedRRset
	Validated       bool
	Msg             string
	Error           bool
	ErrorMsg        string
}

type DeferredUpdate

type DeferredUpdate struct {
	Cmd          string
	ZoneName     string
	AddTime      time.Time
	Description  string
	PreCondition func() bool
	Action       func() error
}

type DelegationBackend

type DelegationBackend interface {
	// ApplyChildUpdate processes an approved child UPDATE.
	// Actions are ClassINET (add), ClassNONE (delete-RR), ClassANY (delete-RRset).
	ApplyChildUpdate(parentZone string, ur UpdateRequest) error

	// GetDelegationData returns current delegation RRs for a child zone,
	// grouped by owner name and RR type.
	GetDelegationData(parentZone, childZone string) (map[string]map[uint16][]dns.RR, error)

	// ListChildren returns all child zones with stored delegation data.
	ListChildren(parentZone string) ([]string, error)

	// Name returns the backend name for logging.
	Name() string
}

DelegationBackend is the interface for storing delegation data received from child zones via DNS UPDATE. Implementations persist the data in different ways (in-memory zone, database, zone files, etc.).

func LookupDelegationBackend

func LookupDelegationBackend(name string, kdb *KeyDB, zd *ZoneData) (DelegationBackend, error)

LookupDelegationBackend resolves a backend name to a DelegationBackend.

Predefined names:

  • "db" → DBDelegationBackend (uses the zone's existing KeyDB)
  • "direct" → DirectDelegationBackend (modifies in-memory zone data)

Any other name is looked up in the "delegation-backends" config list.

type DelegationBackendConf

type DelegationBackendConf struct {
	Name          string `yaml:"name"`
	Type          string `yaml:"type"`
	Directory     string `yaml:"directory"`      // zonefile backend
	NotifyCommand string `yaml:"notify-command"` // zonefile backend
}

DelegationBackendConf is a named backend definition from the config file. Predefined backends ("db", "direct") need no config entry. Named backends are only needed for types that require parameters (e.g. "zonefile").

type DelegationData

type DelegationData struct {
	CurrentNS *core.RRset
	AddedNS   *core.RRset
	RemovedNS *core.RRset

	BailiwickNS []string
	A_glue      map[string]*core.RRset // map[nsname]
	AAAA_glue   map[string]*core.RRset // map[nsname]
	Actions     []dns.RR               // actions are DNS UPDATE actions that modify delegation data
	Time        time.Time
}

type DelegationPost

type DelegationPost struct {
	Command string // status | sync | export | ...
	Scheme  uint8  // 1=notify | 2=update
	Zone    string
	Force   bool
	Outfile string `json:"outfile,omitempty"` // for "export": destination file path
}

type DelegationResponse

type DelegationResponse struct {
	AppName    string
	Time       time.Time
	Zone       string
	SyncStatus DelegationSyncStatus
	Msg        string
	Error      bool
	ErrorMsg   string
}

type DelegationSyncRequest

type DelegationSyncRequest struct {
	Command      string
	ZoneName     string
	ZoneData     *ZoneData
	SyncStatus   DelegationSyncStatus
	OldDnskeys   *core.RRset
	NewDnskeys   *core.RRset
	MsignerGroup *core.RRset
	Response     chan DelegationSyncStatus // used for API-based requests
}

type DelegationSyncStatus

type DelegationSyncStatus struct {
	ZoneName      string
	Parent        string // use zd.Parent instead
	Time          time.Time
	InSync        bool
	Status        string
	Msg           string
	Rcode         uint8
	Adds          []dns.RR `json:"-"`
	Removes       []dns.RR `json:"-"`
	NsAdds        []dns.RR `json:"-"`
	NsRemoves     []dns.RR `json:"-"`
	AAdds         []dns.RR `json:"-"`
	ARemoves      []dns.RR `json:"-"`
	AAAAAdds      []dns.RR `json:"-"`
	AAAARemoves   []dns.RR `json:"-"`
	DNSKEYAdds    []dns.RR `json:"-"`
	DNSKEYRemoves []dns.RR `json:"-"`
	DSAdds        []dns.RR `json:"-"`
	DSRemoves     []dns.RR `json:"-"`
	Error         bool
	ErrorMsg      string
	UpdateResult  UpdateResult // Experimental
	// Complete new delegation data for replace mode
	NewNS   []dns.RR `json:"-"`
	NewA    []dns.RR `json:"-"`
	NewAAAA []dns.RR `json:"-"`
	NewDS   []dns.RR `json:"-"`
	// String representations for JSON serialization (populated by ToStrings())
	NsAddsStr      []string `json:"NsAdds,omitempty"`
	NsRemovesStr   []string `json:"NsRemoves,omitempty"`
	AAddsStr       []string `json:"AAdds,omitempty"`
	ARemovesStr    []string `json:"ARemoves,omitempty"`
	AAAAAddsStr    []string `json:"AAAAAdds,omitempty"`
	AAAARemovesStr []string `json:"AAAARemoves,omitempty"`
	DSAddsStr      []string `json:"DSAdds,omitempty"`
	DSRemovesStr   []string `json:"DSRemoves,omitempty"`
}

func (*DelegationSyncStatus) ToStrings

func (dss *DelegationSyncStatus) ToStrings()

type DirectDelegationBackend

type DirectDelegationBackend struct {
	// contains filtered or unexported fields
}

func (*DirectDelegationBackend) ApplyChildUpdate

func (b *DirectDelegationBackend) ApplyChildUpdate(parentZone string, ur UpdateRequest) error

func (*DirectDelegationBackend) GetDelegationData

func (b *DirectDelegationBackend) GetDelegationData(parentZone, childZone string) (map[string]map[uint16][]dns.RR, error)

func (*DirectDelegationBackend) ListChildren

func (b *DirectDelegationBackend) ListChildren(parentZone string) ([]string, error)

func (*DirectDelegationBackend) Name

func (b *DirectDelegationBackend) Name() string

type DnsEngineConf

type DnsEngineConf struct {
	Addresses   []string              `yaml:"addresses" validate:"required"`
	CertFile    string                `yaml:"certfile,omitempty"`
	KeyFile     string                `yaml:"keyfile,omitempty"`
	Transports  []string              `yaml:"transports" validate:"required,min=1,dive,oneof=do53 dot doh doq"` // "do53", "dot", "doh", "doq"
	OptionsStrs []string              `yaml:"options" mapstructure:"options"`
	Options     map[AuthOption]string `yaml:"-" mapstructure:"-"`
	// OutboundSoaSerial controls the SOA serial advertised on outbound zone
	// transfers and NOTIFYs. One of:
	//   keep     — outbound = inbound serial (default; current behavior).
	//   unixtime — outbound = time.Now().Unix() at parse.
	//   persist  — outbound = previously-saved outbound serial (from the
	//              OutgoingSerials DB table). Every BumpSerial writes the
	//              new value back. On clean restart with no zone change,
	//              the serial stays put — secondaries don't see a regression
	//              and don't trigger an unnecessary AXFR.
	OutboundSoaSerial string `yaml:"outbound_soa_serial,omitempty" mapstructure:"outbound_soa_serial" validate:"omitempty,oneof=keep unixtime persist"`
}

type DnsHandlerRequest

type DnsHandlerRequest struct {
	ResponseWriter dns.ResponseWriter
	Msg            *dns.Msg
	Qname          string
}

type DnsNotifyRequest

type DnsNotifyRequest struct {
	ResponseWriter dns.ResponseWriter
	Msg            *dns.Msg
	Qname          string
	Options        *edns0.MsgOptions
	Status         *NotifyStatus
}

type DnsQueryRequest

type DnsQueryRequest struct {
	ResponseWriter dns.ResponseWriter
	Msg            *dns.Msg
	Qname          string
	Qtype          uint16
	Options        *edns0.MsgOptions
}

type DnsUpdateRequest

type DnsUpdateRequest struct {
	ResponseWriter dns.ResponseWriter
	Msg            *dns.Msg
	Qname          string
	Options        *edns0.MsgOptions
	Status         *UpdateStatus
}

type DnskeyStatus

type DnskeyStatus struct {
	Time             time.Time
	ZoneName         string
	LocalAdds        []dns.RR // Local DNSKEYs added since last check
	LocalRemoves     []dns.RR // Local DNSKEYs removed since last check
	CurrentLocalKeys []dns.RR // Complete current set of local DNSKEYs (for replace operations)
}

DnskeyStatus holds the result of DNSKEY change detection (local keys only). 20260415 johani: somewhat unclear if we still need this.

type DnssecKey

type DnssecKey struct {
	Name                   string
	State                  string
	Keyid                  uint16
	Flags                  uint16
	Algorithm              string
	Creator                string
	PrivateKey             string //
	Key                    dns.DNSKEY
	Keystr                 string
	PropagationConfirmed   bool      // True when all remote providers confirmed this key
	PropagationConfirmedAt time.Time // When propagation was confirmed (zero if not confirmed)
}

type DnssecKeyWithTimestamps

type DnssecKeyWithTimestamps struct {
	ZoneName    string
	KeyTag      uint16
	Algorithm   uint8
	Flags       uint16
	State       string
	KeyRR       string
	PublishedAt *time.Time
	RetiredAt   *time.Time
}

DnssecKeyWithTimestamps extends KeyInventoryItem with lifecycle timestamps. Used by the KeyStateWorker for time-based state transitions.

func GetDnssecKeysByState

func GetDnssecKeysByState(kdb *KeyDB, zone string, state string) ([]DnssecKeyWithTimestamps, error)

GetDnssecKeysByState returns all DNSSEC keys in a given state, with lifecycle timestamps. If zone is empty, returns keys across all zones.

type DnssecKeys

type DnssecKeys struct {
	KSKs []*PrivateKeyCache
	ZSKs []*PrivateKeyCache
}

type DnssecPolicy

type DnssecPolicy struct {
	Name      string
	Algorithm uint8
	Mode      string

	KSK KeyLifetime
	ZSK KeyLifetime
	CSK KeyLifetime

	Rollover RolloverPolicy
	TTLS     DnssecPolicyTTLS
	Clamping ClampingPolicy
}

DnssecPolicy is what is actually used; it is created from the corresponding DnssecPolicyConf

func BuiltinDefaultDnssecPolicy

func BuiltinDefaultDnssecPolicy() DnssecPolicy

builtinDefaultDnssecPolicy returns the built-in "default" DNSSEC policy used when no dnssecpolicies.default is defined in config (e.g. for agent autozone). An explicit dnssecpolicies.default in YAML overrides this. No automatic key rollovers.

type DnssecPolicyClampingConf

type DnssecPolicyClampingConf struct {
	Enabled bool   `yaml:"enabled" mapstructure:"enabled"`
	Margin  string `yaml:"margin" mapstructure:"margin"`
}

DnssecPolicyClampingConf is the YAML `clamping:` subtree under a DNSSEC policy.

type DnssecPolicyConf

type DnssecPolicyConf struct {
	Name      string
	Algorithm string
	Mode      string `yaml:"mode" mapstructure:"mode"`

	KSK struct {
		Lifetime    string
		SigValidity string
	}
	ZSK struct {
		Lifetime    string
		SigValidity string
	}
	CSK struct {
		Lifetime    string
		SigValidity string
	}

	Rollover DnssecPolicyRolloverConf `yaml:"rollover" mapstructure:"rollover"`
	Ttls     DnssecPolicyTtlsConf     `yaml:"ttls" mapstructure:"ttls"`
	Clamping DnssecPolicyClampingConf `yaml:"clamping" mapstructure:"clamping"`
}

DnssecPolicyConf should match the configuration

type DnssecPolicyRolloverConf

type DnssecPolicyRolloverConf struct {
	Method             string `yaml:"method" mapstructure:"method"`
	NumDS              int    `yaml:"num-ds" mapstructure:"num-ds"`
	ParentAgent        string `yaml:"parent-agent" mapstructure:"parent-agent"`
	ConfirmInitialWait string `yaml:"confirm-initial-wait" mapstructure:"confirm-initial-wait"`
	ConfirmPollMax     string `yaml:"confirm-poll-max" mapstructure:"confirm-poll-max"`
	ConfirmTimeout     string `yaml:"confirm-timeout" mapstructure:"confirm-timeout"`
	DsyncRequired      *bool  `yaml:"dsync-required" mapstructure:"dsync-required"`
}

DnssecPolicyRolloverConf is the YAML `rollover:` subtree (KSK automated rollover; Phase 1+).

type DnssecPolicyTTLS

type DnssecPolicyTTLS struct {
	DNSKEY uint32
	// MaxServed is a steady-state ceiling on the TTL of every RRset served
	// by this zone. When non-zero, SignRRset clamps Header().Ttl down to
	// min(operator_ttl, MaxServed) regardless of rollover proximity. Use
	// to enforce low TTLs on zones whose source data has high TTLs that
	// the operator can't directly edit (e.g. inbound zone transfers).
	// Zero means no ceiling. Validation: must be >= 60s when set.
	MaxServed uint32
}

DnssecPolicyTTLS holds steady-state TTL hints from policy (seconds). Zero means unset.

type DnssecPolicyTtlsConf

type DnssecPolicyTtlsConf struct {
	DNSKEY    string `yaml:"dnskey" mapstructure:"dnskey"`
	MaxServed string `yaml:"max_served" mapstructure:"max_served"`
}

DnssecPolicyTtlsConf is the YAML `ttls:` subtree under a DNSSEC policy.

type DsyncResult

type DsyncResult struct {
	Qname  string
	Rdata  []*core.DSYNC
	Parent string
	Error  error
}

type DsyncTarget

type DsyncTarget struct {
	Name      string
	Scheme    core.DsyncScheme
	Port      uint16
	Addresses []string // in addr:port format
	RR        *core.DSYNC
}

type DynamicCatalogMemberConf

type DynamicCatalogMemberConf struct {
	Allowed bool   `yaml:"allowed" mapstructure:"allowed"`                                              // Whether catalog member zones are allowed
	Storage string `yaml:"storage" mapstructure:"storage" validate:"omitempty,oneof=memory persistent"` // "memory" or "persistent"
	Add     string `yaml:"add" mapstructure:"add" validate:"omitempty,oneof=auto manual"`               // "auto" or "manual" - Enable auto-configuration from catalog
	Remove  string `yaml:"remove" mapstructure:"remove" validate:"omitempty,oneof=auto manual"`         // "auto" or "manual" - Whether to remove zones when deleted from catalog
}

DynamicCatalogMemberConf defines configuration for catalog member zones (includes add/remove policy)

type DynamicConfigFile

type DynamicConfigFile struct {
	Zones []ZoneConf `yaml:"zones"`
}

DynamicConfigFile represents the structure of the dynamic zones config file

type DynamicZoneTypeConf

type DynamicZoneTypeConf struct {
	Allowed bool   `yaml:"allowed" mapstructure:"allowed"`                                              // Whether this type of zone is allowed
	Storage string `yaml:"storage" mapstructure:"storage" validate:"omitempty,oneof=memory persistent"` // "memory" or "persistent"
}

DynamicZoneTypeConf defines configuration for a type of dynamic zone

type DynamicZonesConf

type DynamicZonesConf struct {
	ConfigFile     string                   `yaml:"configfile" mapstructure:"configfile"`           // Absolute path to dynamic config file
	ZoneDirectory  string                   `yaml:"zonedirectory" mapstructure:"zonedirectory"`     // Absolute path to zone file directory
	CatalogZones   DynamicZoneTypeConf      `yaml:"catalog_zones" mapstructure:"catalog_zones"`     // Configuration for catalog zones
	CatalogMembers DynamicCatalogMemberConf `yaml:"catalog_members" mapstructure:"catalog_members"` // Configuration for catalog member zones
	Dynamic        DynamicZoneTypeConf      `yaml:"dynamic" mapstructure:"dynamic"`                 // Configuration for direct API-created zones (future)
}

DynamicZonesConf defines configuration for dynamically created zones (catalog zones, catalog members, etc.)

type EarliestRolloverGate

type EarliestRolloverGate struct {
	Name string
	At   time.Time
}

EarliestRolloverGate names a constraint that contributed to t_earliest. One of: "now", "max-ttl-expiry", "max-rrsig-validity", "ds-ready".

type EarliestRolloverResult

type EarliestRolloverResult struct {
	Earliest time.Time
	FromIdx  int
	ToIdx    int
	Gates    []EarliestRolloverGate
}

EarliestRolloverResult is the output of ComputeEarliestRollover.

func ComputeEarliestRollover

func ComputeEarliestRollover(kdb *KeyDB, zone string, pol *DnssecPolicy, now time.Time) (*EarliestRolloverResult, error)

ComputeEarliestRollover returns the earliest moment a rollover can safely fire for the zone, the rollover_index of the active KSK and its scheduled successor, and the constraints that produced the result. Side-effect free.

Per §8.5:

t_earliest = max(now, max_ttl_expiry, max_sig_expiry, ds_ready_at)

where:

  • max_ttl_expiry = now + max_published_ttl_in_zone(z) - margin
  • max_sig_expiry = now + max_published_rrsig_validity(z) - margin
  • ds_ready_at = 0 if next_ksk is in standby, else next_ksk.ds_observed_at + ds_ttl + margin

Returns an error if a rollover cannot be scheduled (rollover already in progress, no active KSK, or no standby SEP key).

Implementation notes for v1:

  • max_published_rrsig_validity uses the policy's max SigValidity across KSK/ZSK/CSK as a conservative upper bound on currently published RRSIG validity. A future enhancement could track observed validity in ZoneSigningState alongside max_observed_ttl.
  • ds_ready_at uses next_ksk's standby state as the "ready now" signal. The non-standby branch is exercised by manual-ASAP only when the pipeline isn't fully primed yet, which the gate set will surface.

type EngineFunc

type EngineFunc func(ctx context.Context) error

EngineFunc is the function signature for registered engines. Engines are long-running goroutines that run until the context is cancelled. They should return nil when the context is cancelled, or an error if they fail.

type EngineRegistration

type EngineRegistration struct {
	Name   string
	Engine EngineFunc
}

EngineRegistration stores an engine registration

type ErrorJournal

type ErrorJournal struct {
	// contains filtered or unexported fields
}

ErrorJournal is a bounded, time-windowed in-memory ring buffer of errors. Thread-safe. No persistence — this is for operational debugging.

func NewErrorJournal

func NewErrorJournal(maxCount int, maxAge time.Duration) *ErrorJournal

NewErrorJournal creates a new error journal with the given retention limits.

func (*ErrorJournal) ListSince

func (ej *ErrorJournal) ListSince(duration time.Duration) []ErrorJournalEntry

ListSince returns all errors within the given duration from now.

func (*ErrorJournal) LookupByDistID

func (ej *ErrorJournal) LookupByDistID(distID string) (*ErrorJournalEntry, bool)

LookupByDistID returns the error entry for a specific distribution ID, if any.

func (*ErrorJournal) Record

func (ej *ErrorJournal) Record(entry ErrorJournalEntry)

Record adds a new error entry to the journal, evicting old entries as needed.

type ErrorJournalEntry

type ErrorJournalEntry struct {
	DistributionID string    `json:"distribution_id"`
	Sender         string    `json:"sender"`       // Sender identity (extracted from QNAME control zone)
	MessageType    string    `json:"message_type"` // "ping", "beat", "sync", "update", or "unknown"
	ErrorMsg       string    `json:"error_msg"`
	QNAME          string    `json:"qname"` // Original NOTIFY qname
	Timestamp      time.Time `json:"timestamp"`
}

ErrorJournalEntry records a single error that occurred during CHUNK NOTIFY processing.

type ErrorType

type ErrorType uint8
const (
	NoError ErrorType = iota
	ConfigError
	RefreshError
	AgentError
	DnssecError
)

type GlobalStuff

type GlobalStuff struct {
	// IMR         string  // trying to get rid of this, use Imr instead
	ImrEngine   *Imr
	Verbose     bool
	Debug       bool
	Zonename    string
	AgentId     AgentId
	ParentZone  string
	Sig0Keyfile string
	// Api is the legacy single-ApiClient convenience slot. Nothing in
	// tdns/v2 itself reads it any more; the tdns/v2/cli factories all
	// resolve clients via cli.GetApiClient(role, …) against ApiClients
	// below. The field is kept as a shim for external consumers that
	// have not yet migrated:
	//   - tdns-es/es/cli (es_cmds.go, cmd/es-cli/main.go)
	//   - tdns-nm/cmd/{kdc-cli,krs-cli}/main.go, tdns-nm/tnm/cli
	//   - tdns/music/ (music cli commands — legacy)
	// Delete once those are converted to cli.GetApiClient.
	Api         *ApiClient
	ApiClients  map[string]*ApiClient
	PingCount   int
	Slurp       bool
	Algorithm   string
	Rrtype      string
	ShowHeaders bool // -H in various CLI commands
	BaseUri     string
	Port        uint16
	Address     string
	App         AppDetails
	ServerSVCB  *dns.SVCB // ALPN for DoH/DoQ
	TsigKeys    map[string]*TsigDetails
}

func (*GlobalStuff) Validate

func (gs *GlobalStuff) Validate() error

type GroupPrefixesConf

type GroupPrefixesConf struct {
	Config  string `yaml:"config" mapstructure:"config" validate:"required"`   // Prefix for config/transfer groups, or "none" to disable
	Signing string `yaml:"signing" mapstructure:"signing" validate:"required"` // Prefix for signing groups, or "none" to disable
}

GroupPrefixesConf defines prefixes that identify special group types in catalog zones

type HsyncAgentStatus

type HsyncAgentStatus struct {
	Identity    string
	LastContact time.Time
	State       string   // "discovered", "contact_attempted", "connected", "failed"
	LastError   string   // If state is "failed"
	Endpoints   []string // Discovered API/DNS endpoints
}

HsyncAgentStatus represents the current status of a remote agent

type HsyncStatus

type HsyncStatus struct {
	Time         time.Time
	ZoneName     string
	Command      string
	Status       bool
	Error        bool
	ErrorMsg     string
	Msg          string
	HsyncAdds    []dns.RR // Changed from Adds
	HsyncRemoves []dns.RR // Changed from Removes
}

type Imr

type Imr struct {
	Cache       *cache.RRsetCacheT
	DnskeyCache *cache.DnskeyCacheT
	Options     map[ImrOption]string
	LineWidth   int // used to truncate long lines in logging and output (eg. DNSKEYs and RRSIGs)
	Verbose     bool
	Debug       bool
	Quiet       bool        // if true, suppress informational logging (useful for CLI tools)
	DebugLog    *log.Logger // non-nil when imr debug logging is enabled
	// RequireDnssecValidation: when true, security-sensitive lookups (TLSA, etc.) require
	// a secure DNSSEC validation state. Default true; set false in lab environments where
	// the full DNSSEC chain is not yet established.
	RequireDnssecValidation bool
}

func (*Imr) AuthDNSQuery

func (imr *Imr) AuthDNSQuery(ctx context.Context, qname string, qtype uint16, nameservers []string,
	lg *log.Logger, verbose bool) (*core.RRset, int, cache.CacheContext, error)

func (*Imr) CollectNSAddresses

func (imr *Imr) CollectNSAddresses(ctx context.Context, rrset *core.RRset, respch chan *ImrResponse) error

CollectNSAddresses - given an NS RRset, chase down the A and AAAA records corresponding to each nsname

func (*Imr) DefaultDNSKEYFetcher

func (imr *Imr) DefaultDNSKEYFetcher(ctx context.Context, name string) (*core.RRset, error)

func (*Imr) DefaultRRsetFetcher

func (imr *Imr) DefaultRRsetFetcher(ctx context.Context, qname string, qtype uint16) (*core.RRset, error)

func (*Imr) DsyncDiscovery

func (imr *Imr) DsyncDiscovery(ctx context.Context, child string, verbose bool) (DsyncResult, error)

func (*Imr) ImrQuery

func (imr *Imr) ImrQuery(ctx context.Context, qname string, qtype uint16, qclass uint16, respch chan *ImrResponse) (*ImrResponse, error)

func (*Imr) ImrResponder

func (imr *Imr) ImrResponder(ctx context.Context, w dns.ResponseWriter, r *dns.Msg, qname string, qtype uint16, msgoptions *edns0.MsgOptions)

func (*Imr) IterativeDNSQuery

func (imr *Imr) IterativeDNSQuery(ctx context.Context, qname string, qtype uint16, serverMap map[string]*cache.AuthServer, force bool, requireEncrypted bool) (*core.RRset, int, cache.CacheContext, core.Transport, error)

force is true if we should force a lookup even if the answer is in the cache visitedZones tracks which zones we've been referred to for this qname to prevent referral loops requireEncrypted is true if PR flag is set and only encrypted transports should be used Returns: rrset, rcode, context, transport, error

func (*Imr) IterativeDNSQueryFetcher

func (imr *Imr) IterativeDNSQueryFetcher() cache.RRsetFetcher

IterativeDNSQueryFetcher adapts IterativeDNSQuery to the RRsetFetcher interface. It discards the rcode and CacheContext return values, only returning the RRset and error.

func (*Imr) IterativeDNSQueryWithLoopDetection

func (imr *Imr) IterativeDNSQueryWithLoopDetection(ctx context.Context, qname string, qtype uint16, serverMap map[string]*cache.AuthServer, force bool, visitedZones map[string]bool, requireEncrypted bool) (*core.RRset, int, cache.CacheContext, core.Transport, error)

IterativeDNSQueryWithLoopDetection is the internal implementation with loop detection visitedZones tracks which zones we've been referred to for this qname (format: "qname:zone") requireEncrypted is true if PR flag is set and only encrypted transports should be used Returns: rrset, rcode, context, transport, error

func (*Imr) LookupDSYNCTarget

func (imr *Imr) LookupDSYNCTarget(ctx context.Context, childzone string, dtype uint16, scheme core.DsyncScheme) (*DsyncTarget, error)

dtype = the type of DSYNC RR to look for (dns.TypeCDS, dns.TypeCSYNC, dns.TypeANY, ...) scheme = the DSYNC scheme (SchemeNotify | SchemeUpdate)

func (*Imr) ParentZone

func (imr *Imr) ParentZone(z string) (string, error)

func (*Imr) ParseAdditionalForNSAddrs

func (imr *Imr) ParseAdditionalForNSAddrs(ctx context.Context, src string, nsrrset *core.RRset, zonename string,
	nsMap map[string]bool, r *dns.Msg) (map[string]*cache.AuthServer, error)

func (*Imr) ProcessAuthDNSResponse

func (imr *Imr) ProcessAuthDNSResponse(ctx context.Context, qname string, qtype uint16, rrset *core.RRset, rcode int, context cache.CacheContext, msgoptions *edns0.MsgOptions, m *dns.Msg, w dns.ResponseWriter, r *dns.Msg, transport core.Transport) (bool, error)

returns true if we have a response (i.e. we're done), false if we have an error all errors are treated as "done"

func (*Imr) SendRfc9567ErrorReport

func (imr *Imr) SendRfc9567ErrorReport(ctx context.Context, qname string, qtype uint16, ede_code uint16, msgoptions *edns0.MsgOptions) error

func (*Imr) StartImrEngineListeners

func (imr *Imr) StartImrEngineListeners(ctx context.Context, conf *Config) error

func (*Imr) TransportSignalCached

func (imr *Imr) TransportSignalCached(owner string) bool

func (*Imr) TransportSignalRRType

func (imr *Imr) TransportSignalRRType() uint16

type ImrClientQueryHookFunc

type ImrClientQueryHookFunc func(ctx context.Context, w dns.ResponseWriter,
	r *dns.Msg, qname string, qtype uint16,
	msgoptions *edns0.MsgOptions) (context.Context, *dns.Msg)

ImrClientQueryHookFunc is called when an external client query arrives at the IMR listener. Return nil ctx to keep the original context, or a new context to enrich it (e.g. to carry a parent query ID through the resolution chain). Return nil *dns.Msg to proceed with normal resolution. Return a non-nil *dns.Msg to short-circuit: the msg is sent as the response and resolution is skipped.

type ImrEngineConf

type ImrEngineConf struct {
	Active      *bool                `yaml:"active" mapstructure:"active"`         // If nil or true, IMR is active. Only false explicitly disables it.
	RootHints   string               `yaml:"root-hints" mapstructure:"root-hints"` // Path to root hints file. If empty, uses compiled-in hints.
	Addresses   []string             `yaml:"addresses" mapstructure:"addresses" validate:"required"`
	CertFile    string               `yaml:"certfile" mapstructure:"certfile"`
	KeyFile     string               `yaml:"keyfile" mapstructure:"keyfile"`
	Transports  []string             `yaml:"transports" mapstructure:"transports" validate:"required"` // "do53", "dot", "doh", "doq"
	Stubs       []ImrStubConf        `yaml:"stubs"`
	OptionsStrs []string             `yaml:"options" mapstructure:"options"`
	Options     map[ImrOption]string `yaml:"-" mapstructure:"-"`
	// Trust anchors for recursive validation. Provide either DS or DNSKEY as
	// full RR text (zonefile format). DS is preferred as it is more convenient.
	TrustAnchorDS     string `yaml:"trust_anchor_ds"`
	TrustAnchorDNSKEY string `yaml:"trust_anchor_dnskey"`
	// Unbound-style file with one RR per line (DNSKEY and/or DS). Absolute path.
	TrustAnchorFile string `yaml:"trust-anchor-file"`
	Verbose         bool
	Debug           bool
	Logging         ImrLoggingConf `yaml:"logging" mapstructure:"logging"`
	// RequireDnssecValidation: when true (default), TLSA and other security-sensitive
	// records must have a secure DNSSEC validation state. Set to false to allow
	// indeterminate/insecure records during lab/development when the full DNSSEC
	// chain is not yet established.
	RequireDnssecValidation *bool `yaml:"require_dnssec_validation" mapstructure:"require_dnssec_validation"`
}

type ImrLoggingConf

type ImrLoggingConf struct {
	Enabled bool   `yaml:"enabled" mapstructure:"enabled"`
	File    string `yaml:"file" mapstructure:"file"`
}

type ImrOption

type ImrOption uint8
const (
	ImrOptRevalidateNS ImrOption = iota + 1
	ImrOptQueryForTransport
	ImrOptAlwaysQueryForTransport
	ImrOptTransportSignalType
	ImrOptQueryForTransportTLSA
	ImrOptUseTransportSignals
)

type ImrOutboundQueryHookFunc

type ImrOutboundQueryHookFunc func(ctx context.Context, qname string,
	qtype uint16, serverName string, serverAddr string,
	transport core.Transport) error

ImrOutboundQueryHookFunc is called before the IMR sends an iterative query to an authoritative server. Return nil to proceed with the query. Return a non-nil error to skip this server (behaves as if the server didn't respond).

type ImrRequest

type ImrRequest struct {
	Qname      string
	Qtype      uint16
	Qclass     uint16
	ResponseCh chan ImrResponse
}

type ImrResponse

type ImrResponse struct {
	RRset     *core.RRset
	Validated bool
	Error     bool
	ErrorMsg  string
	Msg       string
}

type ImrResponseHookFunc

type ImrResponseHookFunc func(ctx context.Context, qname string, qtype uint16,
	serverName string, serverAddr string, transport core.Transport,
	response *dns.Msg, rcode int)

ImrResponseHookFunc is called after the IMR receives a response from an authoritative server. Observe-only — return value is ignored.

type ImrStubConf

type ImrStubConf struct {
	Zone string `validate:"required"`
	// Servers []StubServerConf `validate:"required"`
	Servers []cache.AuthServer `validate:"required"`
}

type InternalConf

type InternalConf struct {
	InternalDnsConf

	// PostParseZonesHook is called after ParseZones completes during
	// reload (SIGHUP or "config reload-zones"). Set by MP apps to
	// register tdns-mp callbacks on newly added zones.
	PostParseZonesHook func()

	// PostValidateConfigHook is called at the end of ValidateConfig.
	// Set by MP apps (before calling parent MainInit) to run
	// MP-specific config validators alongside the built-in ones.
	PostValidateConfigHook func(conf *Config) error
}

InternalConf holds DNS-internal state (channels, engine references). MP state has moved to tdns-mp's own InternalMpConf.

type InternalDnsConf

type InternalDnsConf struct {
	CfgFile             string //
	DebugMode           bool   // if true, may activate dangerous tests
	ZonesCfgFile        string //
	CertData            string // PEM encoded certificate
	KeyData             string // PEM encoded key
	KeyDB               *KeyDB
	AllZones            []string
	DnssecPolicies      map[string]DnssecPolicy
	StopCh              chan struct{}
	APIStopCh           chan struct{}
	StopOnce            sync.Once
	RefreshZoneCh       chan ZoneRefresher
	BumpZoneCh          chan BumperData
	ValidatorCh         chan ValidatorRequest
	RecursorCh          chan ImrRequest
	ScannerQ            chan ScanRequest
	UpdateQ             chan UpdateRequest
	DeferredUpdateQ     chan DeferredUpdate
	DnsUpdateQ          chan DnsUpdateRequest
	DnsNotifyQ          chan DnsNotifyRequest
	DnsQueryQ           chan DnsQueryRequest           // Optional: if nil, queries use direct call to QueryResponder
	QueryHandlers       map[uint16][]QueryHandlerFunc  // qtype -> list of handlers (registered via RegisterQueryHandler)
	QueryHandlersMutex  sync.RWMutex                   // protects QueryHandlers map
	NotifyHandlers      map[uint16][]NotifyHandlerFunc // qtype -> list of handlers (registered via RegisterNotifyHandler, 0 = all NOTIFYs)
	NotifyHandlersMutex sync.RWMutex                   // protects NotifyHandlers map
	UpdateHandlers      []UpdateHandlerRegistration    // UPDATE handlers (registered via RegisterUpdateHandler)
	UpdateHandlersMutex sync.RWMutex                   // protects UpdateHandlers slice
	DelegationSyncQ     chan DelegationSyncRequest
	NotifyQ             chan NotifyRequest
	AuthQueryQ          chan AuthQueryRequest
	ResignQ             chan *ZoneData     // the names of zones that should be kept re-signed should be sent into this channel
	RRsetCache          *cache.RRsetCacheT // ConcurrentMap of cached RRsets from queries
	ImrEngine           *Imr
	Scanner             *Scanner // Scanner instance for async job tracking
}

InternalDnsConf holds DNS-specific internal state: channels, handlers, caches, and engine references. Stays in tdns after repo split.

type Ixfr

type Ixfr struct {
	FromSerial uint32
	ToSerial   uint32
	Removed    []core.RRset
	Added      []core.RRset
}

type KSKDSPushResult

type KSKDSPushResult struct {
	Rcode        int
	UpdateResult UpdateResult
}

KSKDSPushResult is the outcome of PushWholeDSRRset (rcode and wire diagnostics).

func PushWholeDSRRset

func PushWholeDSRRset(ctx context.Context, zd *ZoneData, kdb *KeyDB, imr *Imr) (KSKDSPushResult, error)

PushWholeDSRRset computes the target DS RRset from the keystore, builds a whole-RRset replacement UPDATE to the parent, signs with the child's active SIG(0) key, and sends it. On rcode NOERROR, updates last_ds_submitted_index_* when indexRangeKnown from ComputeTargetDSSetForZone.

type KaspConf

type KaspConf struct {
	// PropagationDelay is how long to wait for DNSKEY RRsets to propagate
	// through all caches before allowing state transitions.
	// Used for published→standby and retired→removed transitions.
	// Accepts Go duration strings: "1h", "3600s", "90m".
	// Default: "1h".
	PropagationDelay string `yaml:"propagation_delay" mapstructure:"propagation_delay"`

	// StandbyZskCount is the number of standby ZSKs to maintain per zone.
	// When the count drops below this, the KeyStateWorker generates new ZSKs.
	// Default: 1. A value of 0 (or omitted) means use the default.
	StandbyZskCount int `yaml:"standby_zsk_count" mapstructure:"standby_zsk_count"`

	// StandbyKskCount is the number of standby KSKs to maintain per zone.
	// When the count drops below this, the KeyStateWorker generates new KSKs.
	// Default: 0 (no standby KSKs). Set to 1+ to enable standby KSK maintenance.
	StandbyKskCount int `yaml:"standby_ksk_count" mapstructure:"standby_ksk_count"`

	// CheckInterval is how often the KeyStateWorker runs its checks.
	// Accepts Go duration strings: "1m", "60s", "5m".
	// Default: "1m".
	CheckInterval string `yaml:"check_interval" mapstructure:"check_interval"`
}

KaspConf holds Key and Signing Policy parameters for the signer. Controls the KeyStateWorker's automatic key state transitions and standby key maintenance. YAML key: "kasp:"

Example:

kasp:
    propagation_delay: 1h
    standby_zsk_count: 1
    standby_ksk_count: 0
    check_interval: 1m

type KeyBootstrapperRequest

type KeyBootstrapperRequest struct {
	Cmd          string
	KeyName      string
	ZoneName     string
	ZoneData     *ZoneData
	Key          string
	Verified     bool
	Keyid        uint16
	Algorithm    uint8
	Imr          *Imr
	ResponseChan chan *VerificationInfo
}

type KeyConf

type KeyConf struct {
	Tsig []TsigDetails
}

type KeyDB

type KeyDB struct {
	DB *sql.DB

	// Sig0Cache   map[string]*Sig0KeyCache
	KeystoreSig0Cache   map[string]*Sig0ActiveKeys
	TruststoreSig0Cache *Sig0StoreT            // was *Sig0StoreT
	KeystoreDnskeyCache map[string]*DnssecKeys // map[zonename]*DnssecActiveKeys
	Ctx                 string
	UpdateQ             chan UpdateRequest
	DeferredUpdateQ     chan DeferredUpdate
	KeyBootstrapperQ    chan KeyBootstrapperRequest
	Options             map[AuthOption]string
	// OutboundSoaSerial is the resolved mode for outbound SOA serials:
	// OutboundSoaSerialKeep / OutboundSoaSerialUnixtime / OutboundSoaSerialPersist.
	// Sourced from DnsEngineConf.OutboundSoaSerial at parse, defaulted to
	// OutboundSoaSerialKeep if unset.
	OutboundSoaSerial string
	// contains filtered or unexported fields
}

func NewKeyDB

func NewKeyDB(dbfile string, force bool, options map[AuthOption]string) (*KeyDB, error)

NewKeyDB creates and initializes a KeyDB backed by the sqlite3 file at dbfile. It validates that dbfile is provided, ensures the file is writable, opens the sqlite3 database, and sets up required tables. If force is true, existing default tables are dropped before setup. On success it returns a KeyDB with caches, an update channel, and Options set to the provided map; on failure it returns an error describing the problem.

func (*KeyDB) APIkeystore

func (kdb *KeyDB) APIkeystore(conf *Config) func(w http.ResponseWriter, r *http.Request)

func (*KeyDB) APItruststore

func (kdb *KeyDB) APItruststore() func(w http.ResponseWriter, r *http.Request)

func (*KeyDB) ApplyChildUpdateToDB

func (kdb *KeyDB) ApplyChildUpdateToDB(ur UpdateRequest) error

func (*KeyDB) ApplyZoneUpdateToDB

func (kdb *KeyDB) ApplyZoneUpdateToDB(ur UpdateRequest) error

func (*KeyDB) Begin

func (db *KeyDB) Begin(context string) (*Tx, error)

func (*KeyDB) Close

func (db *KeyDB) Close() error

func (*KeyDB) CreateAutoZone

func (kdb *KeyDB) CreateAutoZone(zonename string, addrs []string, nsNames []string) (*ZoneData, error)

func (*KeyDB) DeferredUpdaterEngine

func (kdb *KeyDB) DeferredUpdaterEngine(ctx context.Context) error

func (*KeyDB) DelegationSyncher

func (kdb *KeyDB) DelegationSyncher(ctx context.Context, delsyncq chan DelegationSyncRequest, notifyq chan NotifyRequest, conf *Config) error

func (*KeyDB) DnssecKeyMgmt

func (kdb *KeyDB) DnssecKeyMgmt(tx *Tx, kp KeystorePost) (*KeystoreResponse, error)

func (*KeyDB) Exec

func (db *KeyDB) Exec(query string, args ...interface{}) (sql.Result, error)

func (*KeyDB) GenerateKeypair

func (kdb *KeyDB) GenerateKeypair(owner, creator, state string, rrtype uint16, alg uint8, keytype string, tx *Tx) (*PrivateKeyCache, string, error)

XXX: FIXME: This is not yet ready to generate DNSSEC keys, because in the DNSSEC case we also need the

flags field, which is not yet set here.

func (*KeyDB) GetDnssecKeys

func (kdb *KeyDB) GetDnssecKeys(zonename, state string) (*DnssecKeys, error)

func (*KeyDB) GetKeyStatus

func (kdb *KeyDB) GetKeyStatus(zonename string, keyID uint16) (*edns0.KeyStateOption, error)

func (*KeyDB) GetSig0KeyRaw

func (kdb *KeyDB) GetSig0KeyRaw(zonename, state string) (algorithm, privatekey, keyrr string, found bool, err error)

GetSig0KeyRaw returns the raw database column values for a SIG(0) key. Used for transferring key material between agents via RFI CONFIG.

func (*KeyDB) GetSig0Keys

func (kdb *KeyDB) GetSig0Keys(zonename, state string) (*Sig0ActiveKeys, error)

func (*KeyDB) HandleKeyStateOption

func (kdb *KeyDB) HandleKeyStateOption(opt *dns.OPT, zonename string) (*edns0.KeyStateOption, error)

func (*KeyDB) KeyBootstrapper

func (kdb *KeyDB) KeyBootstrapper(ctx context.Context) error

func (*KeyDB) LoadDnskeyTrustAnchors

func (kdb *KeyDB) LoadDnskeyTrustAnchors() error

XXX: This should die now that we have a real IMR

func (*KeyDB) LoadOutgoingSerial

func (kdb *KeyDB) LoadOutgoingSerial(zone string) (uint32, error)

func (*KeyDB) LoadSig0ChildKeys

func (kdb *KeyDB) LoadSig0ChildKeys() error

func (*KeyDB) Lock

func (kdb *KeyDB) Lock()

Lock and Unlock expose the mutex for code that moves to tdns-mp and can no longer access the unexported kdb.mu.

func (*KeyDB) Prepare

func (db *KeyDB) Prepare(q string) (*sql.Stmt, error)

func (*KeyDB) ProcessKeyState

func (kdb *KeyDB) ProcessKeyState(ks *edns0.KeyStateOption, zonename string) (*edns0.KeyStateOption, error)

func (*KeyDB) PromoteDnssecKey

func (kdb *KeyDB) PromoteDnssecKey(zonename string, keyid uint16, oldstate, newstate string) (err error)

func (*KeyDB) Query

func (db *KeyDB) Query(query string, args ...interface{}) (*sql.Rows, error)

func (*KeyDB) QueryRow

func (db *KeyDB) QueryRow(query string, args ...interface{}) *sql.Row

func (*KeyDB) RolloverKey

func (kdb *KeyDB) RolloverKey(zonename string, keytype string, tx *Tx) (uint16, uint16, error)

RolloverKey performs a manual key rollover for the specified zone and key type. It swaps the oldest standby key to active and the current active key to retired. Returns the old active keyid and the new active keyid. If tx is non-nil, uses the existing transaction; otherwise begins its own.

func (*KeyDB) SaveOutgoingSerial

func (kdb *KeyDB) SaveOutgoingSerial(zone string, serial uint32) error

func (*KeyDB) SendSig0KeyUpdate

func (kdb *KeyDB) SendSig0KeyUpdate(ctx context.Context, childpri, parpri string, gennewkey bool) error

XXX: FIXME: This is only used from the CLI. It should change into code used by TDNS-SERVER and

accessed via API. The code should store the newly generated key in the keystore.

func (*KeyDB) Sig0KeyMgmt

func (kdb *KeyDB) Sig0KeyMgmt(tx *Tx, kp KeystorePost) (*KeystoreResponse, error)

func (*KeyDB) Sig0TrustMgmt

func (kdb *KeyDB) Sig0TrustMgmt(tx *Tx, tp TruststorePost) (*TruststoreResponse, error)

func (*KeyDB) TriggerChildKeyVerification

func (kdb *KeyDB) TriggerChildKeyVerification(childZone string, keyid uint16, keyRR string)

TriggerChildKeyVerification starts an async verification of a child KEY that was just stored in the TrustStore. It uses the KeyBootstrapper's retry pattern: verify via DNS lookup, retry with backoff, then trust.

func (*KeyDB) Unlock

func (kdb *KeyDB) Unlock()

func (*KeyDB) UpdateKeyState

func (kdb *KeyDB) UpdateKeyState(ctx context.Context, keyName string, keyid uint16, imr *Imr, algorithm uint8) error

UpdateKeyState sends a KeyState inquiry to the DSYNC target for the given key and updates the local key store with the parent's response. If the parent reports the key as unknown, triggers a bootstrap.

func (*KeyDB) ZoneUpdaterEngine

func (kdb *KeyDB) ZoneUpdaterEngine(ctx context.Context) error

type KeyInventoryItem

type KeyInventoryItem struct {
	KeyTag    uint16
	Algorithm uint8
	Flags     uint16
	State     string // "created","published","standby","active","retired","removed"
	KeyRR     string // Full DNSKEY RR string (public key data, no private key)
}

KeyInventoryItem is a lightweight DNSKEY entry for inventory responses. Does not include private key material — only the metadata needed for key classification.

func GetKeyInventory

func GetKeyInventory(kdb *KeyDB, zonename string) ([]KeyInventoryItem, error)

GetKeyInventory returns the complete DNSKEY inventory for a zone — all keys across all states. Used by the signer to respond to RFI KEYSTATE requests. Returns lightweight entries (keytag, algorithm, flags, state, keyrr) without private keys.

type KeyLifetime

type KeyLifetime struct {
	Lifetime    uint32
	SigValidity uint32
}

func GenKeyLifetime

func GenKeyLifetime(lifetime, sigvalidity string) KeyLifetime

type KeystorePost

type KeystorePost struct {
	Command         string // "sig0"
	SubCommand      string // "list" | "add" | "delete" | ...
	Zone            string
	Keyname         string
	Keyid           uint16
	Flags           uint16
	KeyType         string
	Algorithm       uint8 // RSASHA256 | ED25519 | etc.
	PrivateKey      string
	KeyRR           string
	DnskeyRR        string
	PrivateKeyCache *PrivateKeyCache
	State           string
	ParentState     uint8
	Creator         string
}

type KeystoreResponse

type KeystoreResponse struct {
	AppName  string
	Time     time.Time
	Status   string
	Zone     string
	Dnskeys  map[string]DnssecKey // TrustAnchor
	Sig0keys map[string]Sig0Key
	Msg      string
	Error    bool
	ErrorMsg string
}

type LocalAgentApiConf

type LocalAgentApiConf struct {
	Addresses struct {
		Publish []string
		Listen  []string
	}
	BaseUrl  string
	Port     uint16
	CertFile string
	KeyFile  string
	CertData string
	KeyData  string
}

type LocalAgentDnsConf

type LocalAgentDnsConf struct {
	Addresses struct {
		Publish []string
		Listen  []string
	}
	BaseUrl     string
	Port        uint16
	ControlZone string `yaml:"control_zone" mapstructure:"control_zone"` // Zone used for NOTIFY(CHUNK) QNAMEs in DNS mode (default: agent identity)
	// Chunk config (same key names as combiner for consistency)
	ChunkMode          string `yaml:"chunk_mode" mapstructure:"chunk_mode"`                     // "edns0" | "query"; query = store payload, receiver fetches via CHUNK query (default: edns0)
	ChunkQueryEndpoint string `yaml:"chunk_query_endpoint" mapstructure:"chunk_query_endpoint"` // "include" | "none"; required when chunk_mode=query. include = signal in NOTIFY (EDNS0); none = receiver uses combiner.agents[].address
	// ChunkMaxSize: maximum size (bytes) of each data chunk when fragmenting payloads via PrepareDistributionChunks.
	// 0 = default (60000). Useful for testing fragmentation with small values (e.g. 500).
	ChunkMaxSize int `yaml:"chunk_max_size" mapstructure:"chunk_max_size"`
	// Message retention times for CHUNK distributions (in seconds)
	MessageRetention MessageRetentionConf `yaml:"message_retention" mapstructure:"message_retention"`
}

type LogConf

type LogConf struct {
	File       string            `yaml:"file" validate:"required"`
	Level      string            `yaml:"level"`      // "debug"|"info"|"warn"|"error"; default "info"
	Subsystems map[string]string `yaml:"subsystems"` // per-subsystem level overrides
}

type MSCAPIConf

type MSCAPIConf struct {
	BaseURL    string `validate:"required,url"`
	ApiKey     string `validate:"required_if=AuthMethod apikey"`
	AuthMethod string `validate:"required,oneof=none apikey"`
	UseTLS     bool   `validate:"required"`
}

type MSCNotifyConf

type MSCNotifyConf struct {
	Addresses []string `validate:"required"` // XXX: must not be in addr:port format
	Port      string   `validate:"required"`
	Targets   []string
}

type MemberZone

type MemberZone struct {
	ZoneName      string    `json:"zone_name"`
	Hash          string    `json:"hash"`
	ServiceGroups []string  `json:"service_groups"` // Groups associated with this zone (RFC 9432 terminology)
	SigningGroup  string    `json:"signing_group"`  // Signing group for this zone
	MetaGroup     string    `json:"meta_group"`     // Meta group for this zone
	DiscoveredAt  time.Time `json:"discovered_at"`
}

MemberZone represents a zone discovered in a catalog zone

type MessageRetentionConf

type MessageRetentionConf struct {
	Beat     int `yaml:"beat" mapstructure:"beat"`         // Beat message retention (default: 30s)
	Ping     int `yaml:"ping" mapstructure:"ping"`         // Ping message retention (default: 30s)
	Hello    int `yaml:"hello" mapstructure:"hello"`       // Hello message retention (default: 300s)
	Sync     int `yaml:"sync" mapstructure:"sync"`         // Sync message retention (default: 300s)
	Relocate int `yaml:"relocate" mapstructure:"relocate"` // Relocate message retention (default: 300s)
	Default  int `yaml:"default" mapstructure:"default"`   // Default retention for other types (default: 300s)
}

MessageRetentionConf defines retention times for different message types in CHUNK distributions. Times are in seconds. Beat and ping messages expire quickly to reduce clutter, while other message types are kept longer for debugging purposes.

func (*MessageRetentionConf) GetRetentionForMessageType

func (m *MessageRetentionConf) GetRetentionForMessageType(messageType string) int

GetRetentionForMessageType returns the retention time in seconds for a given message type. Returns the configured value if set, otherwise returns the appropriate default.

type MetaGroupConfig

type MetaGroupConfig = ConfigGroupConfig

MetaGroupConfig is deprecated, use ConfigGroupConfig instead

type MultiProviderConf

type MultiProviderConf struct {

	// Role: "agent", "combiner", or "signer". Determines which fields are active.
	Role string `yaml:"role"`
	// Active: master switch for multi-provider mode.
	// Must be true AND zone must have options: [multi-provider] for MP behavior.
	Active bool `yaml:"active"`
	// Identity: this node's identity (FQDN) for transport protocol.
	Identity string `yaml:"identity"`
	// LongTermJosePrivKey: path to JOSE private key for secure CHUNK.
	LongTermJosePrivKey string `yaml:"long_term_jose_priv_key"`
	// ChunkMode: "edns0" | "query" for outbound NOTIFY(CHUNK).
	ChunkMode string `yaml:"chunk_mode" mapstructure:"chunk_mode"`
	// ChunkMaxSize: maximum size (bytes) of each data chunk when fragmenting payloads.
	// 0 = default (60000). Useful for testing fragmentation with small values.
	ChunkMaxSize int `yaml:"chunk_max_size" mapstructure:"chunk_max_size"`
	// Agents: the agent peers (address, JOSE public key, optional API URL).
	// Used by signer and combiner roles.
	Agents []*PeerConf `yaml:"agents"`
	// SyncApi: sync API server config for inbound HELLO/BEAT/PING over HTTPS.
	// Used by signer and combiner roles.
	SyncApi struct {
		Addresses struct {
			Listen []string
		}
		CertFile string `yaml:"cert_file" mapstructure:"cert_file"`
		KeyFile  string `yaml:"key_file" mapstructure:"key_file"`
	} `yaml:"sync_api" mapstructure:"sync_api"`

	// CombinerOptions: list of combiner-specific option strings parsed at startup.
	// Known options: "add-signature".
	CombinerOptionsStrs []string                `yaml:"combiner-options" mapstructure:"combiner-options"`
	CombinerOptions     map[CombinerOption]bool `yaml:"-" mapstructure:"-"`

	// ChunkQueryEndpoint: "include" | "none"; required when chunk_mode=query (combiner role).
	ChunkQueryEndpoint string `yaml:"chunk_query_endpoint" mapstructure:"chunk_query_endpoint"`
	// Signature: template string for a TXT record injected into combined zones (demo feature).
	// Supports {identity} and {zone} placeholders.
	Signature    string `yaml:"signature"`
	AddSignature bool   `yaml:"add-signature" mapstructure:"add-signature"` // DEPRECATED: use combiner-options: [add-signature]
	// ProtectedNamespaces: list of domain suffixes that belong to this provider.
	// NS records from remote agents whose targets fall within any of these namespaces
	// are rejected (prevents namespace intrusion).
	ProtectedNamespaces []string `yaml:"protected-namespaces" mapstructure:"protected-namespaces"`
	// ProviderZones: zones owned by the provider where agents may make targeted edits
	// (e.g. _signal KEY records). Unlike MP zones, these use config-driven RRtype
	// restrictions and allow non-apex owners.
	ProviderZones []ProviderZoneConf `yaml:"provider-zones" mapstructure:"provider-zones"`

	// SignerOptions: list of signer-specific option strings parsed at startup.
	SignerOptionsStrs []string              `yaml:"signer-options" mapstructure:"signer-options"`
	SignerOptions     map[SignerOption]bool `yaml:"-" mapstructure:"-"`

	// AgentOptions: list of agent-specific option strings parsed at startup.
	AgentOptionsStrs []string             `yaml:"agent-options" mapstructure:"agent-options"`
	AgentOptions     map[AgentOption]bool `yaml:"-" mapstructure:"-"`
	// SupportedMechanisms: List of active transport mechanisms (default: ["api", "dns"] if both configured)
	SupportedMechanisms []string `yaml:"supported_mechanisms" mapstructure:"supported_mechanisms"`
	Local               struct {
		Notify      []string
		Nameservers []string `yaml:"nameservers,omitempty"`
	}
	Remote struct {
		LocateInterval int
		BeatInterval   uint32
	}
	Syncengine struct {
		Intervals struct {
			HelloRetry int
		}
	}
	Api LocalAgentApiConf
	Dns LocalAgentDnsConf
	// Combiner peer (agent only): address and combiner's JOSE public key path for secure CHUNK
	Combiner *PeerConf `yaml:"combiner"`
	// Signer peer (agent only): address and JOSE public key path for KEYSTATE signaling
	Signer *PeerConf `yaml:"signer"`
	// AuthorizedPeers: List of agent identities authorized to communicate
	AuthorizedPeers []string `yaml:"authorized_peers"`
	// Peers (DEPRECATED): Old format with embedded addresses/keys - use authorized_peers instead
	Peers map[string]*PeerConf `yaml:"peers"`
	Xfr   struct {
		Outgoing struct {
			Addresses []string `yaml:"addresses,omitempty"`
			Auth      []string `yaml:"auth,omitempty"`
		}
		Incoming struct {
			Addresses []string `yaml:"addresses,omitempty"`
			Auth      []string `yaml:"auth,omitempty"`
		}
	}
}

MultiProviderConf holds config for multi-provider DNSSEC (RFC 8901). Used by all three MP roles: agent, combiner, and signer. The Role field determines which role-specific fields are relevant. YAML key: "multi-provider:"

func (*MultiProviderConf) FindAgent

func (c *MultiProviderConf) FindAgent(identity string) *PeerConf

FindAgent returns the PeerConf for the agent with the given identity, or nil if not found.

type MultiSignerConf

type MultiSignerConf struct {
	Name       string
	Controller MultiSignerController
}

type MultiSignerController

type MultiSignerController struct {
	Name   string
	Notify MSCNotifyConf
	API    MSCAPIConf
}

type MultiSignerPost

type MultiSignerPost struct {
	Command string // "fetch-rrset" | "update" | "remove-rrset"
	Zone    string
	Name    string
	Type    uint16
}

type MultiSignerResponse

type MultiSignerResponse struct {
	AppName  string
	Time     time.Time
	RRset    core.RRset
	Msg      string
	Error    bool
	ErrorMsg string
}

type MusicSyncRequest

type MusicSyncRequest struct {
	Command         string
	ZoneName        string
	ZoneData        *ZoneData
	OldDnskeys      *core.RRset
	NewDnskeys      *core.RRset
	MusicSyncStatus *MusicSyncStatus
	Response        chan MusicSyncStatus // used for API-based requests
}

type MusicSyncStatus

type MusicSyncStatus struct {
	ZoneName       string
	MsignerAdds    []dns.RR
	MsignerRemoves []dns.RR
	DnskeyAdds     []dns.RR
	DnskeyRemoves  []dns.RR
	Msg            string
	Error          bool
	ErrorMsg       string
	Status         bool
}

type NotifyHandlerFunc

type NotifyHandlerFunc func(ctx context.Context, req *DnsNotifyRequest) error

NotifyHandlerFunc is the function signature for registered NOTIFY handlers. Returns ErrNotHandled if the handler doesn't handle this NOTIFY (allows fallthrough). Returns nil if the handler successfully handled the NOTIFY. Returns other error if handler attempted to handle but encountered an error.

type NotifyRequest

type NotifyRequest struct {
	ZoneName string
	ZoneData *ZoneData
	RRtype   uint16
	Targets  []string // []addr:port
	Urgent   bool
	Response chan NotifyResponse
}

type NotifyResponse

type NotifyResponse struct {
	Msg      string
	Rcode    int
	Error    bool
	ErrorMsg string
}

type NotifyStatus

type NotifyStatus struct {
	Zone          string // zone that the update applies to
	ChildZone     string // zone that the update applies to
	Type          uint16 // CDS | CSYNC | DNSKEY | DELEG
	ScanStatus    string // "ok" | "changed" | "failed"
	SafetyChecked bool   // true if the update has been safety checked
	PolicyChecked bool   // true if the update has been policy checked
	Approved      bool   // true if the update has been approved
	Msg           string
	Error         bool
	ErrorMsg      string
	Status        bool
}

type OwnerData

type OwnerData struct {
	Name    string
	RRtypes *RRTypeStore
}

func NewOwnerData

func NewOwnerData(name string) *OwnerData

type Owners

type Owners []OwnerData

func (Owners) Len

func (owners Owners) Len() int

func (Owners) Less

func (owners Owners) Less(i, j int) bool

func (Owners) Swap

func (owners Owners) Swap(i, j int)

type PeerConf

type PeerConf struct {
	Address            string `yaml:"address"`
	LongTermJosePubKey string `yaml:"long_term_jose_pub_key"`
	ApiBaseUrl         string `yaml:"api_base_url,omitempty"` // Optional: for API transport (e.g. https://combiner:8085/api/v1)
	Identity           string `yaml:"identity"`               // Peer identity (FQDN); required for combiner agents, optional for agent combiner
}

PeerConf holds address and public key path for the other party (agent or combiner).

type PingPost

type PingPost struct {
	Msg   string
	Pings int
}

type PingResponse

type PingResponse struct {
	Time       time.Time
	Client     string
	BootTime   time.Time
	Version    string
	ServerHost string // "master.dnslab"
	Daemon     string // "tdnsd"
	Msg        string
	Pings      int
	Pongs      int
}

type PrivateKeyCache

type PrivateKeyCache struct {
	K          crypto.PrivateKey
	PrivateKey string // This is only used when reading from file with ReadKeyNG()
	CS         crypto.Signer
	RR         dns.RR
	KeyType    uint16
	Algorithm  uint8
	KeyId      uint16
	KeyRR      dns.KEY
	DnskeyRR   dns.DNSKEY
}

Migrating all DB access to own interface to be able to have local receiver functions.

func GenerateKeyMaterial

func GenerateKeyMaterial(owner string, rrtype uint16, alg uint8, keytype string) (*PrivateKeyCache, error)

GenerateKeyMaterial builds private/public key material for KEY or DNSKEY without touching the DB.

func LoadSig0SigningKey

func LoadSig0SigningKey(keyfile string) (*PrivateKeyCache, error)

func PrepareKeyCache

func PrepareKeyCache(privkey, pubkey string) (*PrivateKeyCache, error)

func ReadPrivateKey

func ReadPrivateKey(filename string) (*PrivateKeyCache, error)

type ProviderZoneConf

type ProviderZoneConf struct {
	Zone           string   `yaml:"zone"`
	AllowedRRtypes []string `yaml:"allowed-rrtypes" mapstructure:"allowed-rrtypes"`
}

ProviderZoneConf configures a provider-owned zone that the combiner manages. Unlike MP zones (hardcoded RRtype whitelist, apex-only), provider zones use config-driven RRtype restrictions and allow non-apex record owners.

type QueryHandlerFunc

type QueryHandlerFunc func(ctx context.Context, req *DnsQueryRequest) error

QueryHandlerFunc is the function signature for registered query handlers. Returns ErrNotHandled if the handler doesn't handle this query (allows fallthrough). Returns nil if the handler successfully handled the query. Returns other error if handler attempted to handle but encountered an error.

type RRTypeStore

type RRTypeStore struct {
	// contains filtered or unexported fields
}

func NewRRTypeStore

func NewRRTypeStore() *RRTypeStore

func (*RRTypeStore) Count

func (s *RRTypeStore) Count() int

func (*RRTypeStore) Delete

func (s *RRTypeStore) Delete(key uint16)

func (*RRTypeStore) Get

func (s *RRTypeStore) Get(key uint16) (core.RRset, bool)

func (*RRTypeStore) GetOnlyRRSet

func (s *RRTypeStore) GetOnlyRRSet(key uint16) core.RRset

GetOnlyRRSet returns the RRset for key, or zero-value RRset if not found.

func (*RRTypeStore) Keys

func (s *RRTypeStore) Keys() []uint16

func (*RRTypeStore) Set

func (s *RRTypeStore) Set(key uint16, value core.RRset)

type RRsetString

type RRsetString struct {
	Name   string   `json:"name"`
	RRtype uint16   `json:"rrtype"`
	RRs    []string `json:"rrs"`
	RRSIGs []string `json:"rrsigs,omitempty"`
}

String-based versions of RRset for JSON marshaling

type RefreshCounter

type RefreshCounter struct {
	Name           string
	SOARefresh     uint32
	CurRefresh     uint32
	IncomingSerial uint32
	Upstream       string
	Downstreams    []string
	Zonefile       string
}

type RefresherResponse

type RefresherResponse struct {
	Time     time.Time
	Zone     string
	Msg      string
	Error    bool
	ErrorMsg string
}

type RolloverMethod

type RolloverMethod int
const (
	RolloverMethodNone RolloverMethod = iota
	RolloverMethodMultiDS
	RolloverMethodDoubleSignature
)

type RolloverPolicy

type RolloverPolicy struct {
	Method             RolloverMethod
	NumDS              int
	ParentAgent        string
	ConfirmInitialWait time.Duration
	ConfirmPollMax     time.Duration
	ConfirmTimeout     time.Duration
	DsyncRequired      bool
}

type RolloverZoneRow

type RolloverZoneRow struct {
	Zone                      string
	LastSubmittedLow          sql.NullInt64
	LastSubmittedHigh         sql.NullInt64
	LastConfirmedLow          sql.NullInt64
	LastConfirmedHigh         sql.NullInt64
	RolloverPhase             string
	RolloverPhaseAt           sql.NullString
	RolloverInProgress        bool
	ManualRolloverRequestedAt sql.NullString
	ManualRolloverEarliest    sql.NullString
	ObserveStartedAt          sql.NullString
	ObserveNextPollAt         sql.NullString
	ObserveBackoffSecs        sql.NullInt64
}

RolloverZoneRow is persisted rollover coordination for one zone (RolloverZoneState).

func LoadRolloverZoneRow

func LoadRolloverZoneRow(kdb *KeyDB, zone string) (*RolloverZoneRow, error)

type ScanJobStatus

type ScanJobStatus struct {
	JobID           string              `json:"job_id"`
	Status          string              `json:"status"` // "queued", "processing", "completed", "failed"
	CreatedAt       time.Time           `json:"created_at"`
	StartedAt       *time.Time          `json:"started_at,omitempty"`
	CompletedAt     *time.Time          `json:"completed_at,omitempty"`
	TotalTuples     int                 `json:"total_tuples"`
	IgnoredTuples   int                 `json:"ignored_tuples"`
	ErrorTuples     int                 `json:"error_tuples"`
	ProcessedTuples int                 `json:"processed_tuples"`
	Responses       []ScanTupleResponse `json:"responses,omitempty"`
	Error           bool                `json:"error,omitempty"`
	ErrorMsg        string              `json:"error_msg,omitempty"`
}

ScanJobStatus represents the status of a scan job

type ScanRequest

type ScanRequest struct {
	Cmd              string
	ParentZone       string
	ScanZones        []string
	ScanType         ScanType // "cds" | "csync" | "dnskey"
	ScanTuples       []ScanTuple
	ChildZone        string
	CurrentChildData ChildDelegationData // Current parent-side delegation data for child
	ZoneData         *ZoneData
	RRtype           uint16
	Edns0Options     *edns0.MsgOptions
	Response         chan ScanResponse
	JobID            string // Job ID for async processing
}

type ScanResponse

type ScanResponse struct {
	Time     time.Time
	Zone     string
	RRtype   uint16
	RRset    core.RRset
	Msg      string
	Error    bool
	ErrorMsg string
}

type ScanTuple

type ScanTuple struct {
	Zone string // zone to scan	//
	// Type ScanType	// type of test to perform	// ScanRRtype | ScanCSYNC | ScanDNSKEY
	CurrentData CurrentScanData // current data for the test
	Options     []string        // "all-ns", ...
}

type ScanTupleResponse

type ScanTupleResponse struct {
	Qname       string              // The qname that was queried
	ScanType    ScanType            // The type of scan performed
	Options     []string            // Options that were used (e.g., "all-ns")
	NewData     CurrentScanDataJSON // The new data retrieved from the scan (JSON-serializable)
	DataChanged bool                // Whether the new data differs from the old data (from ScanTuple.CurrentData)
	AllNSInSync bool                // If "all-ns" option was set, whether all NS were in sync (false if not applicable)
	DSAdds      []dns.RR            // DS records to add to parent (from CDS→DS conversion)
	DSRemoves   []dns.RR            // DS records to remove from parent
	NSAdds      []dns.RR            // NS records to add at child apex (from CSYNC)
	NSRemoves   []dns.RR            // NS records to remove from child apex (from CSYNC)
	GlueAdds    []dns.RR            // A/AAAA glue records to add (owner in RR header)
	GlueRemoves []dns.RR            // A/AAAA glue records to remove
	Error       bool                // Whether an error occurred
	ErrorMsg    string              // Error message if Error is true
}

ScanTupleResponse contains the result of scanning a single ScanTuple

type ScanType

type ScanType uint8

ScanType represents the type of test to perform during scanning

const (
	ScanRRtype ScanType = iota + 1
	ScanCDS
	ScanCSYNC
	ScanDNSKEY
)

type Scanner

type Scanner struct {
	AuthQueryQ         chan AuthQueryRequest
	ImrEngine          *Imr
	Options            []string
	AtApexChecks       int
	AtApexInterval     time.Duration
	OnDelegationChange func(parentZone string, zd *ZoneData, resp ScanTupleResponse)
	LogFile            string
	LogTemplate        string
	Log                map[string]*log.Logger
	Verbose            bool
	Debug              bool
	Jobs               map[string]*ScanJobStatus
	JobsMutex          sync.RWMutex
}

func NewScanner

func NewScanner(authqueryq chan AuthQueryRequest, verbose, debug bool) *Scanner

func (*Scanner) AddLogger

func (scanner *Scanner) AddLogger(rrtype string) error

func (*Scanner) AuthQueryNG

func (scanner *Scanner) AuthQueryNG(qname, ns string, rrtype uint16, transport string) (*core.RRset, error)

func (*Scanner) CheckCDS

func (scanner *Scanner) CheckCDS(ctx context.Context, tuple ScanTuple, scanType ScanType, options *edns0.MsgOptions, responseCh chan<- ScanTupleResponse)

func (*Scanner) CheckCSYNC

func (scanner *Scanner) CheckCSYNC(sr ScanRequest, cdd *ChildDelegationData) (*ChildDelegationData, error)

func (*Scanner) CheckDNSKEY

func (scanner *Scanner) CheckDNSKEY(ctx context.Context, tuple ScanTuple, scanType ScanType, options *edns0.MsgOptions, responseCh chan<- ScanTupleResponse)

func (*Scanner) CsyncAnalyzeA

func (scanner *Scanner) CsyncAnalyzeA(zone string, new_nsrrs []*dns.NS, cdd *ChildDelegationData) ([]dns.RR, bool, error)

func (*Scanner) CsyncAnalyzeAAAA

func (scanner *Scanner) CsyncAnalyzeAAAA(zone string, new_nsrrs []*dns.NS, cdd *ChildDelegationData) ([]dns.RR, bool, error)

func (*Scanner) CsyncAnalyzeNS

func (scanner *Scanner) CsyncAnalyzeNS(zone string, cdd *ChildDelegationData) ([]dns.RR, bool, error)

Returns: new_rrs, changed=true, error

func (*Scanner) HasOption

func (scanner *Scanner) HasOption(name string) bool

func (*Scanner) ProcessCDSNotify

func (scanner *Scanner) ProcessCDSNotify(ctx context.Context, tuple ScanTuple, parentZD *ZoneData, scanType ScanType, options *edns0.MsgOptions, responseCh chan<- ScanTupleResponse)

ProcessCDSNotify handles a CDS NOTIFY by querying CDS from child nameservers, converting CDS→DS, and diffing against current DS. The scanner is read-only: results (DSAdds/DSRemoves) are returned in the ScanTupleResponse for the caller to act on.

func (*Scanner) ProcessCSYNCNotify

func (scanner *Scanner) ProcessCSYNCNotify(ctx context.Context, tuple ScanTuple, parentZD *ZoneData, scanType ScanType, options *edns0.MsgOptions, responseCh chan<- ScanTupleResponse)

ProcessCSYNCNotify handles a CSYNC NOTIFY by querying CSYNC, NS, and glue from child nameservers, diffing against current delegation data, and reporting NS/glue adds/removes in the ScanTupleResponse. The scanner is read-only: results are returned for the caller to act on. Follows RFC 7477 processing algorithm.

func (*Scanner) UpdateCsyncStatus

func (scanner *Scanner) UpdateCsyncStatus(zone string, csyncrr *dns.CSYNC) error

func (*Scanner) ZoneCSYNCKnown

func (scanner *Scanner) ZoneCSYNCKnown(zone string, csyncrr *dns.CSYNC) bool

type ScannerPost

type ScannerPost struct {
	Command    string      // "scan" | "status"
	ParentZone string      // Legacy field
	ScanZones  []string    // Legacy field
	ScanType   ScanType    // Legacy field: "cds" | "csync" | "dnskey"
	ScanTuples []ScanTuple // New field: list of scan tuples for scan requests
}

type ScannerResponse

type ScannerResponse struct {
	AppName  string
	Time     time.Time
	Status   string
	Msg      string
	Error    bool
	ErrorMsg string
	JobID    string `json:"job_id,omitempty"` // Job ID for async scan requests
}

type SensitiveString

type SensitiveString string

SensitiveString wraps a string that should not appear in logs. Use .Value() to get the actual string; String() returns a redacted form.

func (SensitiveString) String

func (s SensitiveString) String() string

String returns a redacted representation for safe logging.

func (SensitiveString) Value

func (s SensitiveString) Value() string

Value returns the actual string value.

type ServerAddrTuple

type ServerAddrTuple struct {
	Server *cache.AuthServer
	Addr   string
	NSName string
}

ServerAddrTuple represents a (server, address) pair for prioritization

type ServiceConf

type ServiceConf struct {
	Name       string `validate:"required"`
	Debug      *bool
	Verbose    *bool
	Identities []string      // this is a strawman attempt at deciding on what name to publish the ALPN
	Transport  TransportConf `yaml:"transport"`
}

type ShowAPIresponse

type ShowAPIresponse struct {
	Status int
	Msg    string
	Data   []string
}

type Sig0ActiveKeys

type Sig0ActiveKeys struct {
	Keys []*PrivateKeyCache
}

type Sig0Key

type Sig0Key struct {
	Name            string
	State           string
	Keyid           uint16
	Algorithm       string
	Creator         string
	Validated       bool   // has this key been validated
	PublishedInDNS  bool   // is this key published in DNS (as a KEY RR)
	DnssecValidated bool   // has this key been DNSSEC validated
	Trusted         bool   // is this key trusted
	Source          string // "dns" | "file" | "keystore" | "child-update"
	PrivateKey      string //
	Key             dns.KEY
	Keystr          string
}

type Sig0StoreT

type Sig0StoreT struct {
	Map *core.ConcurrentMap[string, Sig0Key]
}

func NewSig0StoreT

func NewSig0StoreT() *Sig0StoreT

type Sig0UpdateSigner

type Sig0UpdateSigner struct {
	Name      string   // from the SIG
	KeyId     uint16   // from the SIG
	Sig0Key   *Sig0Key // a key that matches the signer name and keyid
	Validated bool     // true if this key validated the update
}

A Signer is a struct where we keep track of the signer name and keyid for a DNS UPDATE message.

type Sig0tmp

type Sig0tmp map[string]TmpSig0Key

type SignerOption

type SignerOption uint8

type SigningGroupInfo

type SigningGroupInfo struct {
	Description string `yaml:"description" mapstructure:"description"`
}

SigningGroupInfo provides documentation for signing groups (RFC 9432 terminology)

type TAtmp

type TAtmp map[string]TmpAnchor

XXX: These should die

type TargetUpdateStatus

type TargetUpdateStatus struct {
	Sender     string
	Rcode      int
	Error      bool
	ErrorMsg   string
	EDEFound   bool
	EDECode    uint16
	EDEMessage string
}

type TemplateConf

type TemplateConf struct {
	Name         string `validate:"required"`
	Zonefile     string
	Type         string
	Store        string
	Primary      string // upstream, for secondary zones
	Notify       []string
	OptionsStrs  []string `yaml:"options" mapstructure:"options"`
	UpdatePolicy UpdatePolicyConf
	DnssecPolicy string `yaml:"dnssecpolicy" mapstructure:"dnssecpolicy"`
	MultiSigner  string `yaml:"multisigner" mapstructure:"multisigner"`
}

type TmpAnchor

type TmpAnchor struct {
	Name   string
	Dnskey string
}

type TmpSig0Key

type TmpSig0Key struct {
	Name string
	Key  string
}

type TransportConf

type TransportConf struct {
	Type   string `yaml:"type" validate:"omitempty,oneof=tsync svcb none"`
	Signal string `yaml:"signal"`
}

type TruststorePost

type TruststorePost struct {
	Command         string // "sig0"
	SubCommand      string // "list-child-keys" | "trust-child-key" | "untrust-child-key"
	Zone            string
	Keyname         string
	Keyid           int
	Validated       bool
	DnssecValidated bool
	Trusted         bool
	Src             string // "dns" | "file"
	KeyRR           string // RR string for key
}

type TruststoreResponse

type TruststoreResponse struct {
	AppName       string
	Time          time.Time
	Status        string
	Zone          string
	ChildDnskeys  map[string]cache.CachedDnskeyRRset
	ChildSig0keys map[string]Sig0Key
	Msg           string
	Error         bool
	ErrorMsg      string
}

type TsigDetails

type TsigDetails struct {
	Name      string `validate:"required" yaml:"name"`
	Algorithm string `validate:"required" yaml:"algorithm"`
	Secret    string `validate:"required" yaml:"secret"`
}

type Tx

type Tx struct {
	*sql.Tx
	KeyDB *KeyDB
	// contains filtered or unexported fields
}

func (*Tx) Commit

func (tx *Tx) Commit() error

func (*Tx) Exec

func (tx *Tx) Exec(query string, args ...interface{}) (sql.Result, error)

func (*Tx) Query

func (tx *Tx) Query(query string, args ...interface{}) (*sql.Rows, error)

func (*Tx) QueryRow

func (tx *Tx) QueryRow(query string, args ...interface{}) *sql.Row

func (*Tx) Rollback

func (tx *Tx) Rollback() error

type UpdateHandlerFunc

type UpdateHandlerFunc func(ctx context.Context, req *DnsUpdateRequest) error

UpdateHandlerFunc is the function signature for registered UPDATE handlers. Returns ErrNotHandled if the handler doesn't handle this UPDATE (allows fallthrough). Returns nil if the handler successfully handled the UPDATE. Returns other error if handler attempted to handle but encountered an error.

type UpdateHandlerRegistration

type UpdateHandlerRegistration struct {
	Matcher UpdateMatcherFunc
	Handler UpdateHandlerFunc
}

UpdateHandlerRegistration stores an UPDATE handler registration

type UpdateMatcherFunc

type UpdateMatcherFunc func(req *DnsUpdateRequest) bool

UpdateMatcherFunc is the function signature for matching UPDATE messages. Returns true if the UPDATE should be handled by the associated handler. The matcher receives the DnsUpdateRequest and can inspect: - req.Qname (zone name from question section) - req.Msg.Ns (update section RRs) - req.Msg.Extra (additional section, e.g., SIG(0)) - req.Options (EDNS0 options)

type UpdatePolicy

type UpdatePolicy struct {
	Child    UpdatePolicyDetail
	Zone     UpdatePolicyDetail
	Validate bool
}

type UpdatePolicyConf

type UpdatePolicyConf struct {
	Child struct {
		Type         string // selfsub | self | sub | none
		RRtypes      []string
		KeyBootstrap []string // manual | dnssec-validated | consistent-lookup
		KeyUpload    string
		TTL          uint32 `yaml:"ttl"`
	}
	Zone struct {
		Type    string // "selfsub" | "self" | "sub" | ...
		RRtypes []string
		TTL     uint32 `yaml:"ttl"`
	}
	Validate bool
}

type UpdatePolicyDetail

type UpdatePolicyDetail struct {
	Type         string // "selfsub" | "self"
	RRtypes      map[uint16]bool
	KeyBootstrap []string
	KeyUpload    string
	TTL          uint32
}

type UpdateRequest

type UpdateRequest struct {
	Cmd            string
	UpdateType     string // "DSYNC", "KEY", ...
	ZoneName       string
	Adds           []dns.RR
	Removes        []dns.RR
	Actions        []dns.RR // The Update section from the dns.Msg
	Validated      bool     // Signature over update msg is validated
	Trusted        bool     // Content of update is trusted (via validation or policy)
	InternalUpdate bool     // Internal update, not a DNS UPDATE from the outside
	Status         *UpdateStatus
	Description    string
	PreCondition   func() bool
	Action         func() error
}

type UpdateResult

type UpdateResult struct {
	EDEFound     bool
	EDECode      uint16
	EDEMessage   string
	EDESender    string
	Rcode        int
	TargetStatus map[string]TargetUpdateStatus
}

This is only called from the CLI command "tdns-cli ddns sync" and uses a SIG(0) key from the command line rather than the one in the keystore. Not to be used by TDNS-SERVER.

func SendUpdate

func SendUpdate(msg *dns.Msg, zonename string, addrs []string) (int, UpdateResult, error)

Note: the target.Addresses must already be in addr:port format. func SendUpdate(msg *dns.Msg, zonename string, target *DsyncTarget) (int, error) { func SendUpdate(msg *dns.Msg, zonename string, addrs []string) (int, error, UpdateResult) {

type UpdateStatus

type UpdateStatus struct {
	Zone                  string             // zone that the update applies to
	ChildZone             string             // zone that the update applies to
	Type                  string             // auth | child
	Data                  string             // auth | delegation | key
	ValidatorKey          *Sig0Key           // key that validated the update
	Signers               []Sig0UpdateSigner // possible validators
	SignerName            string             // name of the key that signed the update
	SignatureType         string             // by-trusted | by-known | self-signed
	ValidationRcode       uint8              // Rcode from the validation process
	Validated             bool               // true if the update has passed validation
	ValidatedByTrustedKey bool               // true if the update has passed validation by a trusted key
	SafetyChecked         bool               // true if the update has been safety checked
	PolicyChecked         bool               // true if the update has been policy checked
	Approved              bool               // true if the update has been approved
	Msg                   string
	Error                 bool
	ErrorMsg              string
	Status                bool
}

type ValidatorRequest

type ValidatorRequest struct {
	Qname    string
	RRset    *core.RRset
	Response chan ValidatorResponse
}

type ValidatorResponse

type ValidatorResponse struct {
	Validated bool
	Msg       string
}

type VerificationInfo

type VerificationInfo struct {
	KeyName        string
	Key            string
	ZoneName       string
	AttemptsLeft   int
	NextCheckTime  time.Time
	ZoneData       *ZoneData
	Keyid          uint16
	FailedAttempts int
}

type ZoneConf

type ZoneConf struct {
	Name              string `validate:"required"`
	Zonefile          string
	Type              string `validate:"required"`
	Store             string // xfr | map | slice | reg (defaults to "map" if not specified)
	Primary           string // upstream, for secondary zones
	Notify            []string
	Downstreams       []string
	OptionsStrs       []string     `yaml:"options" mapstructure:"options"`
	Options           []ZoneOption `yaml:"-" mapstructure:"-"` // Ignore during both yaml and mapstructure decoding
	Frozen            bool         // true if zone is frozen; not a config param
	Dirty             bool         // true if zone has been modified; not a config param
	UpdatePolicy      UpdatePolicyConf
	DelegationBackend string    `yaml:"delegation-backend" mapstructure:"delegation-backend"` // named backend for child delegation data
	DnssecPolicy      string    `yaml:"dnssecpolicy" mapstructure:"dnssecpolicy"`
	Template          string    `yaml:"template" mapstructure:"template"`
	MultiSigner       string    `yaml:"multisigner" mapstructure:"multisigner"`
	Error             bool      // zone is broken and cannot be used
	ErrorType         ErrorType // "config" | "refresh" | "agent" | "DNSSEC"
	ErrorMsg          string    // reason for the error (if known)
	RefreshCount      int       // number of times the zone has been sucessfully refreshed (used to determine if we have zonedata)
	SourceCatalog     string    // if auto-configured, which catalog zone created this zone
}

ZoneConf represents the external config for a zone; it contains no zone data

func ExpandTemplate

func ExpandTemplate(zconf ZoneConf, tmpl *ZoneConf, appMode AppType) (ZoneConf, error)

type ZoneData

type ZoneData struct {
	ZoneName   string
	ZoneStore  ZoneStore // 1 = "xfr", 2 = "map", 3 = "slice". An xfr zone only supports xfr related ops
	ZoneType   ZoneType
	Owners     Owners
	OwnerIndex *core.ConcurrentMap[string, int]
	ApexLen    int
	//	RRs            RRArray
	Data *core.ConcurrentMap[string, OwnerData]
	// 20260415 johani: MP    *ZoneMPExtension // Multi-provider state; nil for non-MP zones
	Ready bool // true if zd.Data has been populated (from file or upstream)

	XfrType string // axfr | ixfr
	Logger  *log.Logger
	// ZoneFile           string // TODO: Remove this
	IncomingSerial     uint32 // SOA serial that we got from upstream
	CurrentSerial      uint32 // SOA serial after local bumping
	FirstZoneLoad      bool   // true until first zone data has been loaded
	Verbose            bool
	Debug              bool
	IxfrChain          []Ixfr
	Upstream           string   // primary from where zone is xfrred
	Downstreams        []string // secondaries that we notify
	Zonefile           string
	DelegationSyncQ    chan DelegationSyncRequest
	Parent             string   // name of parentzone (if filled in)
	ParentNS           []string // names of parent nameservers
	ParentServers      []string // addresses of parent nameservers
	Children           map[string]*ChildDelegationData
	DelegationBackend  DelegationBackend // parent-side: backend for storing child delegation data
	Options            map[ZoneOption]bool
	UpdatePolicy       UpdatePolicy
	DnssecPolicy       *DnssecPolicy
	DnssecPolicyName   string // name of currently-applied policy; used to detect config-reload-driven changes
	MultiSigner        *MultiSignerConf
	KeyDB              *KeyDB
	AppType            AppType
	Error              bool        // zone is broken and cannot be used
	ErrorType          ErrorType   // "config" | "refresh" | "notify" | "update"
	ErrorMsg           string      // reason for the error (if known)
	LatestError        time.Time   // time of latest error
	RefreshCount       int         // number of times the zone has been sucessfully refreshed (used to determine if we have zonedata)
	LatestRefresh      time.Time   // time of latest successful refresh
	SourceCatalog      string      // if auto-configured, which catalog zone created this zone
	TransportSignal    *core.RRset // transport signal RRset (SVCB or TSYNC)
	AddTransportSignal bool        // whether to attach TransportSignal in responses

	// OnFirstLoad holds one-shot callbacks executed after the zone's first successful load.
	// Apps register these before RefreshEngine starts, and RefreshEngine clears the slice
	// after executing them. Protected by zd.mu.
	OnFirstLoad []func(*ZoneData)

	// OnZonePreRefresh callbacks run BEFORE the hard flip in FetchFromFile/FetchFromUpstream.
	// They receive both old (zd, current, still served) and new (new_zd, incoming, not yet served)
	// zone data. Used for: analysis (compare old vs new for HSYNC/DNSKEY/delegation changes),
	// modification of new_zd (combiner contributions, signature TXT, MP data population),
	// and agent RFIs (RequestAndWaitForKeyInventory). Options map is shared between zd and new_zd.
	OnZonePreRefresh []func(zd, new_zd *ZoneData)

	// OnZonePostRefresh callbacks run AFTER the hard flip (and after RepopulateDynamicRRs).
	// They receive zd which now serves the new data. Used for: queue sends (SyncQ,
	// DelegationSyncQ) that need the live zone pointer, and any post-flip notifications.
	OnZonePostRefresh []func(zd *ZoneData)
	// contains filtered or unexported fields
}

func FindZone

func FindZone(qname string) (*ZoneData, bool)

Find the closest enclosing auth zone that has qname below it (qname is either auth data in the zone or located further down in a child zone that we are not auth for). Return zone, case fold used to match

func FindZoneNG

func FindZoneNG(qname string) *ZoneData

func (*ZoneData) AddOwner

func (zd *ZoneData) AddOwner(owner *OwnerData)

XXX: This MUST ONLY be called from the ZoneUpdater, due to locking issues

func (*ZoneData) AnalyseZoneDelegation

func (zd *ZoneData) AnalyseZoneDelegation(imr *Imr) (DelegationSyncStatus, error)

Return insync (bool), adds, removes ([]dns.RR) and error

func (*ZoneData) ApplyChildUpdateToZoneData

func (zd *ZoneData) ApplyChildUpdateToZoneData(ur UpdateRequest, kdb *KeyDB) (bool, error)

func (*ZoneData) ApplyZoneUpdateToZoneData

func (zd *ZoneData) ApplyZoneUpdateToZoneData(ur UpdateRequest, kdb *KeyDB) (bool, error)

func (*ZoneData) ApproveAuthUpdate

func (zd *ZoneData) ApproveAuthUpdate(zone string, us *UpdateStatus, r *dns.Msg) (bool, bool, error)

Updates to auth data must be validated.

func (*ZoneData) ApproveChildUpdate

func (zd *ZoneData) ApproveChildUpdate(zone string, us *UpdateStatus, r *dns.Msg) (bool, bool, error)

Child updates are either validated updates for child delegation data, or unvalidated key upload requests. Returns approved, updatezone, error

func (*ZoneData) ApproveTrustUpdate

func (zd *ZoneData) ApproveTrustUpdate(zone string, us *UpdateStatus, r *dns.Msg) (bool, bool, error)

Trust updates are either validated updates (signed by already trusted key) or unvalidated (selfsigned initial uploads of key). In both cases the update section must only contain a single KEY RR. Returns approved, updatezone, error

func (*ZoneData) ApproveUpdate

func (zd *ZoneData) ApproveUpdate(zone string, us *UpdateStatus, r *dns.Msg) (bool, bool, error)

Returns approved, updatezone, error

func (*ZoneData) BestSyncScheme

func (zd *ZoneData) BestSyncScheme(ctx context.Context, imr *Imr) (string, *DsyncTarget, error)

Find the best scheme (from the POV of the child) to sync the deletation with the parent

func (*ZoneData) BootstrapSig0KeyWithParent

func (zd *ZoneData) BootstrapSig0KeyWithParent(ctx context.Context, alg uint8) (string, UpdateResult, error)

func (*ZoneData) BumpSerial

func (zd *ZoneData) BumpSerial() (BumperResponse, error)

func (*ZoneData) BumpSerialOnly

func (zd *ZoneData) BumpSerialOnly() (BumperResponse, error)

BumpSerialOnly advances the SOA serial per the configured outbound_soa_serial mode and rewrites the apex SOA RR (and its RRSIG, when the zone is signed). Does not notify downstreams. Use when the caller will handle notification separately or when notification is not appropriate (e.g. inside a NOTIFY handler where triggering downstream NOTIFYs could cause side effects).

func (*ZoneData) CollectDynamicRRs

func (zd *ZoneData) CollectDynamicRRs(conf *Config) []*core.RRset

CollectDynamicRRs collects all dynamically generated RRsets for a zone that need to be repopulated after refresh. These RRs are stored outside ZoneData (in database or generated from config) and will be lost when the zone is reloaded.

Returns a slice of RRsets that should be repopulated into the zone after refresh: - DNSKEY records (from DnssecKeyStore database, if online-signing enabled) - SIG(0) KEY records (from Sig0KeyStore database, if needed) - Transport signals (SVCB/TSYNC) - if Config provided and add-transport-signal enabled

func (*ZoneData) ComputeIndices

func (zd *ZoneData) ComputeIndices()

func (*ZoneData) CreateTransportSignalRRs

func (zd *ZoneData) CreateTransportSignalRRs(conf *Config) error

CreateTransportSignalRRs orchestrates construction of a transport signal RRset for this zone. It delegates to the chosen mechanism (svcb|tsync) and assigns zd.TransportSignal and zd.AddTransportSignal when successful.

func (*ZoneData) DelegationData

func (zd *ZoneData) DelegationData() (*DelegationData, error)

func (*ZoneData) DelegationDataChangedNG

func (zd *ZoneData) DelegationDataChangedNG(newzd *ZoneData) (bool, DelegationSyncStatus, error)

func (*ZoneData) DelegationSyncSetup

func (zd *ZoneData) DelegationSyncSetup(ctx context.Context, kdb *KeyDB) error

func (*ZoneData) DnskeysChanged

func (zd *ZoneData) DnskeysChanged(newzd *ZoneData) (bool, DelegationSyncStatus, error)

func (*ZoneData) DnskeysChangedNG

func (zd *ZoneData) DnskeysChangedNG(newzd *ZoneData) (bool, error)

func (*ZoneData) DoTransfer

func (zd *ZoneData) DoTransfer() (bool, uint32, error)

Return shouldTransfer, new upstream serial, error

func (*ZoneData) EnsureActiveDnssecKeys

func (zd *ZoneData) EnsureActiveDnssecKeys(kdb *KeyDB) (*DnssecKeys, error)

EnsureActiveDnssecKeys ensures that a zone has active DNSSEC keys. If no active keys exist, it will: 1. Try to promote published keys to active (if available) 2. Generate new KSK and ZSK keys if needed Returns the active DNSSEC keys or an error if key generation fails.

func (*ZoneData) FetchChildDelegationData

func (zd *ZoneData) FetchChildDelegationData(childname string) (*ChildDelegationData, error)

func (*ZoneData) FetchFromFile

func (zd *ZoneData) FetchFromFile(verbose, debug, force bool, dynamicRRs []*core.RRset) (bool, error)

Return updated, error

func (*ZoneData) FetchFromUpstream

func (zd *ZoneData) FetchFromUpstream(verbose, debug bool, dynamicRRs []*core.RRset) (bool, error)

Return updated, err

func (*ZoneData) FetchParentData

func (zd *ZoneData) FetchParentData(imr *Imr) error

func (*ZoneData) FindDelegation

func (zd *ZoneData) FindDelegation(qname string, dnssec_ok bool) *ChildDelegationData

XXX: This should be merged with the FetchChildDelegationData() function Returns [] NS RRs + [] v4glue RRs + [] v6glue RRs

func (*ZoneData) FindDnskey

func (zd *ZoneData) FindDnskey(signer string, keyid uint16) (*cache.CachedDnskeyRRset, error)

If key not found *CachedDnskeyRRset is returned with nil value

func (*ZoneData) FindGlue

func (zd *ZoneData) FindGlue(nsrrs core.RRset, dnssec_ok bool) (*core.RRset, *core.RRset)

Returns two RRsets with A glue and AAAA glue. Each RRset may be nil. XXX: This is wrong. The v4 (and v6) glue is not an *RRset, but a []*RRset

func (*ZoneData) FindGlueSimple

func (zd *ZoneData) FindGlueSimple(nsrrs core.RRset, dnssec_ok bool) ([]dns.RR, []dns.RR, []dns.RR, []dns.RR)

func (*ZoneData) FindSig0KeyViaDNS

func (zd *ZoneData) FindSig0KeyViaDNS(signer string, keyid uint16) (*Sig0Key, error)

func (*ZoneData) FindSig0TrustedKey

func (zd *ZoneData) FindSig0TrustedKey(signer string, keyid uint16) (*Sig0Key, error)

This is about locating a SIG(0) key that is trusted, i.e. that is present in the TrustStore. It is not about looking in the Keystore, nor looking in the DNS. If key not found *TrustAnchor is nil

func (*ZoneData) GenerateNsecChain

func (zd *ZoneData) GenerateNsecChain(kdb *KeyDB) error

func (*ZoneData) GenerateNsecChainWithDak

func (zd *ZoneData) GenerateNsecChainWithDak(dak *DnssecKeys) error

GenerateNsecChainWithDak builds or refreshes the NSEC chain using the given active DNSSEC keys.

func (*ZoneData) GetOwner

func (zd *ZoneData) GetOwner(qname string) (*OwnerData, error)

func (*ZoneData) GetOwnerNames

func (zd *ZoneData) GetOwnerNames() ([]string, error)

func (*ZoneData) GetRRset

func (zd *ZoneData) GetRRset(qname string, rrtype uint16) (*core.RRset, error)

func (*ZoneData) GetSOA

func (zd *ZoneData) GetSOA() (*dns.SOA, error)

func (*ZoneData) IsChildDelegation

func (zd *ZoneData) IsChildDelegation(qname string) bool

XXX: Is qname the name of a zone cut for a child zone?

func (*ZoneData) LoadDynamicZoneFile

func (zd *ZoneData) LoadDynamicZoneFile(zoneDirectory string) (bool, uint32, error)

LoadDynamicZoneFile loads a zone from a file in the dynamic zones directory Returns true if zone was updated, the serial number, and any error If the file is corrupted, creates the zone but sets an error state

func (*ZoneData) Lock

func (zd *ZoneData) Lock()

Lock and Unlock expose the mutex for code that moves to tdns-mp and can no longer access the unexported zd.mu.

func (*ZoneData) LookupAndValidateRRset

func (zd *ZoneData) LookupAndValidateRRset(qname string, qtype uint16,
	verbose bool) (*core.RRset, bool, error)

func (*ZoneData) LookupChildRRset

func (zd *ZoneData) LookupChildRRset(qname string, qtype uint16,
	v4glue, v6glue *core.RRset, verbose bool) (*core.RRset, error)

XXX: This should die in favor of LookupChildRRsetNG

func (*ZoneData) LookupChildRRsetNG

func (zd *ZoneData) LookupChildRRsetNG(qname string, qtype uint16,
	addrs []string, verbose bool) (*core.RRset, error)

func (*ZoneData) LookupRRset

func (zd *ZoneData) LookupRRset(qname string, qtype uint16, verbose bool) (*core.RRset, error)

func (*ZoneData) MusicSig0KeyPrep

func (zd *ZoneData) MusicSig0KeyPrep(name string, kdb *KeyDB) error

MusicSig0KeyPrep and ParentSig0KeyPrep are identical except for the source of the keygen algorithm which is specified in the relevant section of the configuration file.

func (*ZoneData) NameExists

func (zd *ZoneData) NameExists(qname string) bool

func (*ZoneData) NotifyDownstreams

func (zd *ZoneData) NotifyDownstreams() error

func (*ZoneData) ParentSig0KeyPrep

func (zd *ZoneData) ParentSig0KeyPrep(name string, kdb *KeyDB) error

func (*ZoneData) ParseZoneFromReader

func (zd *ZoneData) ParseZoneFromReader(r io.Reader, force bool, filename string) (bool, uint32, error)

func (*ZoneData) PrintOwners

func (zd *ZoneData) PrintOwners()

func (*ZoneData) PublishAddrRR

func (zd *ZoneData) PublishAddrRR(name, addr string) error

func (*ZoneData) PublishCdsRRs

func (zd *ZoneData) PublishCdsRRs() error

func (*ZoneData) PublishCsyncRR

func (zd *ZoneData) PublishCsyncRR() error

func (*ZoneData) PublishDnskeyRRs

func (zd *ZoneData) PublishDnskeyRRs(dak *DnssecKeys) error

func (*ZoneData) PublishDsyncRRs

func (zd *ZoneData) PublishDsyncRRs() error

func (*ZoneData) PublishJWKFromKeyRR

func (zd *ZoneData) PublishJWKFromKeyRR(owner string, keyRR *dns.KEY) error

PublishJWKFromKeyRR publishes a JWK record by extracting the public key from a KEY RR. This is useful for migrating existing KEY records to JWK format.

Parameters:

  • owner: The DNS name for the JWK record
  • keyRR: The existing KEY record containing the public key

Returns error if key extraction or encoding fails.

func (*ZoneData) PublishJWKRR

func (zd *ZoneData) PublishJWKRR(owner string, publicKey crypto.PublicKey, use string) error

PublishJWKRR publishes a JWK record for the specified owner and public key. The public key is encoded to RFC 7517 JWK format and published as a JWK RR.

Parameters:

  • owner: The DNS name for the JWK record (typically the agent identity)
  • publicKey: The crypto.PublicKey to encode and publish
  • use: The intended use ("" to omit, "sig" for signing, "enc" for encryption)

Returns error if encoding fails or zone update fails.

func (*ZoneData) PublishKeyRRs

func (zd *ZoneData) PublishKeyRRs(sak *Sig0ActiveKeys) error

func (*ZoneData) PublishSvcbRR

func (zd *ZoneData) PublishSvcbRR(name string, port uint16, value []dns.SVCBKeyValue) error

func (*ZoneData) PublishTlsaRR

func (zd *ZoneData) PublishTlsaRR(name string, port uint16, certPEM string) error

func (*ZoneData) PublishUriRR

func (zd *ZoneData) PublishUriRR(owner, target, baseurl string, port uint16) error

Example: target: ms1.music.axfr.net baseurl: https://{TARGET}/api/v1 port: 443

func (*ZoneData) QueryResponder

func (zd *ZoneData) QueryResponder(ctx context.Context, w dns.ResponseWriter, r *dns.Msg,
	qname string, qtype uint16, msgoptions *edns0.MsgOptions, kdb *KeyDB, imr *Imr) error

func (*ZoneData) ReadZoneData

func (zd *ZoneData) ReadZoneData(zoneData string, force bool) (bool, uint32, error)

func (*ZoneData) ReadZoneFile

func (zd *ZoneData) ReadZoneFile(filename string, force bool) (bool, uint32, error)

func (*ZoneData) Refresh

func (zd *ZoneData) Refresh(verbose, debug, force bool, conf *Config) (bool, error)

func (*ZoneData) ReloadZone

func (zd *ZoneData) ReloadZone(refreshCh chan<- ZoneRefresher, force bool, wait bool, timeoutStr string) (string, error)

func (*ZoneData) RepopulateDynamicRRs

func (zd *ZoneData) RepopulateDynamicRRs(dynamicRRs []*core.RRset)

RepopulateDynamicRRs repopulates dynamically generated RRsets into the zone data after refresh. The RRsets are passed in from RefreshEngine which collected them before the refresh.

func (*ZoneData) RolloverSig0KeyWithParent

func (zd *ZoneData) RolloverSig0KeyWithParent(ctx context.Context, alg uint8, action string) (string, uint16, uint16, UpdateResult, error)

Returns msg, old keyid, new keyid, error, UpdateResult

func (*ZoneData) SendNotify

func (zd *ZoneData) SendNotify(ntype uint16, targets []string) (int, error)

func (*ZoneData) SetError

func (zd *ZoneData) SetError(errtype ErrorType, errmsg string, args ...interface{})

func (*ZoneData) SetOption

func (zd *ZoneData) SetOption(option ZoneOption, value bool)

func (*ZoneData) SetupZoneSigning

func (zd *ZoneData) SetupZoneSigning(resignq chan<- *ZoneData) error

func (*ZoneData) SetupZoneSync

func (zd *ZoneData) SetupZoneSync(delsyncq chan<- DelegationSyncRequest) error

func (*ZoneData) ShowNsecChain

func (zd *ZoneData) ShowNsecChain() ([]string, error)

func (*ZoneData) Sig0KeyPreparation

func (zd *ZoneData) Sig0KeyPreparation(name string, alg uint8, kdb *KeyDB) error

func (*ZoneData) SignRRset

func (zd *ZoneData) SignRRset(rrset *core.RRset, name string, dak *DnssecKeys, force bool, clamp *ClampParams) (bool, error)

SignRRset signs an RRset with the zone's active KSK or ZSK keys, regenerating any RRSIGs that NeedsResigning indicates are stale. When clamp != nil (zone has clamping.enabled and a rollover is scheduled), the RR header TTLs are first clamped to min(rrset.UnclampedTTL, K * margin) and then signed — so the resulting RRSIG.OrigTtl matches the served TTL. See §5.2 of the automated KSK rollover design.

clamp == nil disables clamping entirely (no behavior change from the pre-4D signature). Most callers pass nil; SignZone builds a *ClampParams once per pass for clamping zones and threads it down.

func (*ZoneData) SignZone

func (zd *ZoneData) SignZone(kdb *KeyDB, force bool) (int, error)

XXX: MaybesignRRset should report on whether it actually signed anything At the end, is anything hass been signed, then we must end by bumping the SOA Serial and resigning the SOA.

func (*ZoneData) SortFunc

func (zd *ZoneData) SortFunc(rr dns.RR, firstSoaSeen bool) bool

func (*ZoneData) SyncZoneDelegation

func (zd *ZoneData) SyncZoneDelegation(ctx context.Context, kdb *KeyDB, notifyq chan NotifyRequest, syncstate DelegationSyncStatus, imr *Imr) (string, uint8, UpdateResult, error)

SyncZoneDelegation() is used for delegation synchronization request via API.

func (*ZoneData) SyncZoneDelegationViaNotify

func (zd *ZoneData) SyncZoneDelegationViaNotify(kdb *KeyDB, notifyq chan NotifyRequest, syncstate DelegationSyncStatus,
	dsynctarget *DsyncTarget) (string, uint8, error)

func (*ZoneData) SyncZoneDelegationViaUpdate

func (zd *ZoneData) SyncZoneDelegationViaUpdate(kdb *KeyDB, syncstate DelegationSyncStatus,
	dsynctarget *DsyncTarget) (string, uint8, UpdateResult, error)

func (*ZoneData) SynthesizeCdsRRs

func (zd *ZoneData) SynthesizeCdsRRs() ([]dns.RR, error)

PublishCdsRRs synthesizes CDS records from the current KSK DNSKEYs in the zone and publishes them at the zone apex. CDS records signal to the parent that the child wants to update its DS records (RFC 7344).

Only KSK DNSKEYs (flags & 0x0001 == SEP bit set) are used for CDS synthesis. We publish CDS with digest type SHA-256 (2) which is the mandatory-to-implement algorithm per RFC 8624. synthesizeCdsRRs creates CDS records from the current KSK DNSKEYs in the zone. Returns the CDS RRs without publishing them — caller decides how to apply.

func (*ZoneData) TrustUpdate

func (zd *ZoneData) TrustUpdate(r *dns.Msg, us *UpdateStatus) error

BERRA TODO kolla om man kan förbättra detta, så man kan skicka en EDE Evaluate the keys that signed the update and determine the trust status of the update.

func (*ZoneData) Unlock

func (zd *ZoneData) Unlock()

func (*ZoneData) UnpublishAddrRR

func (zd *ZoneData) UnpublishAddrRR(name, addr string) error

func (*ZoneData) UnpublishCdsRRs

func (zd *ZoneData) UnpublishCdsRRs() error

UnpublishCdsRRs removes the CDS RRset from the zone apex.

func (*ZoneData) UnpublishCsyncRR

func (zd *ZoneData) UnpublishCsyncRR() error

func (*ZoneData) UnpublishDsyncRRs

func (zd *ZoneData) UnpublishDsyncRRs() error

func (*ZoneData) UnpublishKeyRRs

func (zd *ZoneData) UnpublishKeyRRs() error

func (*ZoneData) UnpublishSvcbRR

func (zd *ZoneData) UnpublishSvcbRR(name string) error

func (*ZoneData) UnpublishTlsaRR

func (zd *ZoneData) UnpublishTlsaRR(port uint16) error

func (*ZoneData) UnpublishUriRR

func (zd *ZoneData) UnpublishUriRR(owner, target string) error

func (*ZoneData) ValidateChildDnskeys

func (zd *ZoneData) ValidateChildDnskeys(cdd *ChildDelegationData, verbose bool) (bool, error)

ValidateChildDnskeys: we have the ChildDelegationData for the child zone, containing both the NS RRset and the DS RRset. 1. Fetch the child DNSKEY RRset from one of the child nameservers 2. Verify the child KSK against the DS that we have 3. Verify the child DNSKEY RRset against the verified KSK 4. Store the child DNSKEY RRset in the TrustAnchor store 5. Return true if the child DNSKEY RRset is validated

func (*ZoneData) ValidateRRset

func (zd *ZoneData) ValidateRRset(rrset *core.RRset, verbose bool) (bool, error)

XXX: This should not be a method of ZoneData, but rather a function.

func (*ZoneData) ValidateUpdate

func (zd *ZoneData) ValidateUpdate(r *dns.Msg, us *UpdateStatus) error

XXX: This should perhaps not be a method of ZoneData, but rather of KeyDB.

func (*ZoneData) VerifyPublishedKeyRRs

func (zd *ZoneData) VerifyPublishedKeyRRs() error

func (*ZoneData) WriteDynamicZoneFile

func (zd *ZoneData) WriteDynamicZoneFile(zoneDirectory string) (string, error)

WriteDynamicZoneFile writes a zone file to the dynamic zones directory using atomic writes Returns the full path to the written file, or an error

func (*ZoneData) WriteFile

func (zd *ZoneData) WriteFile(filename string) (string, error)

func (*ZoneData) WriteTmpFile

func (zd *ZoneData) WriteTmpFile(lg *log.Logger) (string, error)

func (*ZoneData) WriteZone

func (zd *ZoneData) WriteZone(tosource bool, force bool) (string, error)

func (*ZoneData) WriteZoneToFile

func (zd *ZoneData) WriteZoneToFile(f *os.File) error

func (*ZoneData) XXfindServerTSYNCRRset

func (zd *ZoneData) XXfindServerTSYNCRRset() *core.RRset

findServerTSYNCRRset returns a TSYNC RRset from any owner under this zone that starts with _dns.

func (*ZoneData) ZoneFileName

func (zd *ZoneData) ZoneFileName() (string, error)

ZoneFileName returns the path to use for this zone's file. Only zones with zonefile: set (in config) are written to disk; autozones and secondaries without zonefile are not persisted.

func (*ZoneData) ZoneTransferIn

func (zd *ZoneData) ZoneTransferIn(upstream string, serial uint32, ttype string) (uint32, error)

func (*ZoneData) ZoneTransferOut

func (zd *ZoneData) ZoneTransferOut(w dns.ResponseWriter, r *dns.Msg) (int, error)

func (*ZoneData) ZoneUpdateChangesDelegationDataNG

func (zd *ZoneData) ZoneUpdateChangesDelegationDataNG(ur UpdateRequest) (DelegationSyncStatus, error)

type ZoneDsyncPost

type ZoneDsyncPost struct {
	Command   string // status | bootstrap | ...
	Zone      string
	Algorithm uint8
	Action    string
	OldKeyID  uint16
	NewKeyID  uint16
}

type ZoneDsyncResponse

type ZoneDsyncResponse struct {
	AppName      string
	Time         time.Time
	Status       string
	Zone         string
	Functions    map[string]string
	Todo         []string
	Msg          string
	OldKeyID     uint16
	NewKeyID     uint16
	Error        bool
	ErrorMsg     string
	UpdateResult UpdateResult
}

type ZoneName

type ZoneName string

type ZoneOption

type ZoneOption uint8
const (
	OptDelSyncParent ZoneOption = iota + 1
	OptDelSyncChild
	OptAllowUpdates
	OptAllowChildUpdates
	OptAllowEdits // Dynamically et if app=combiner and zone contains a HSYNC RRset
	OptFoldCase
	OptBlackLies
	OptDontPublishKey
	OptDontPublishJWK
	OptOnlineSigning
	OptInlineSigning
	OptMultiProvider
	OptDirty
	OptFrozen
	OptAutomaticZone
	// OptServerSvcb
	OptAddTransportSignal
	OptCatalogZone
	OptCatalogMemberAutoCreate
	OptCatalogMemberAutoDelete
	OptMPManualApproval
	OptMultiSigner     // Dynamically set by signer when HSYNC shows multiple signers
	OptMPNotListedErr  // Warning: zone has HSYNC3 but we are not listed as a provider
	OptMPDisallowEdits // Zone is signed but we are not a signer: no edits allowed
)

type ZoneOptionHandler

type ZoneOptionHandler func(zname string, options map[ZoneOption]bool)

ZoneOptionHandler is a callback invoked during ParseZones when a zone has a specific option set. Handlers are registered before ParseZones runs and fire synchronously during parsing.

Parameters:

  • zname: the FQDN zone name
  • options: all parsed options for this zone (read-only)

type ZoneOptionValidator

type ZoneOptionValidator func(conf *Config, zname string, zd *ZoneData, options map[ZoneOption]bool) bool

ZoneOptionValidator is a callback invoked during parseZoneOptions when a specific zone option is encountered. Unlike handlers, validators run *during* the switch (not after) and can reject the option.

Return true to accept the option, false to reject it. On rejection, the validator should call zd.SetError() to record a ConfigError explaining the rejection. The caller will skip this option and continue parsing the rest.

type ZonePost

type ZonePost struct {
	Command    string
	SubCommand string
	Zone       string
	Force      bool
	Wait       bool
	Timeout    string
}

type ZoneRefreshAnalysis

type ZoneRefreshAnalysis struct {
	DelegationChanged bool
	DelegationStatus  DelegationSyncStatus
	//	HsyncChanged      bool
	//	HsyncStatus       *HsyncStatus
	DnskeyChanged bool
	DnskeyStatus  *DnskeyStatus
}

ZoneRefreshAnalysis carries analysis results from OnZonePreRefresh to OnZonePostRefresh. Set before the hard flip, consumed after.

type ZoneRefresher

type ZoneRefresher struct {
	Name         string
	ZoneType     ZoneType // primary | secondary
	Primary      string
	Notify       []string
	ZoneStore    ZoneStore // 1=xfr, 2=map, 3=slice
	Zonefile     string
	Options      map[ZoneOption]bool
	Edns0Options *edns0.MsgOptions
	UpdatePolicy UpdatePolicy
	DnssecPolicy string
	MultiSigner  string
	Force        bool // force refresh, ignoring SOA serial
	Wait         bool // wait for refresh to complete before responding
	Response     chan RefresherResponse
}

type ZoneResponse

type ZoneResponse struct {
	AppName  string
	Time     time.Time
	Status   string
	Zone     string
	Names    []string
	Zones    map[string]ZoneConf
	Msg      string
	Error    bool
	ErrorMsg string
}

type ZoneStore

type ZoneStore uint8
const (
	XfrZone ZoneStore = iota + 1
	MapZone
	SliceZone
)

type ZoneType

type ZoneType uint8
const (
	Primary ZoneType = iota + 1
	Secondary
)

type ZonefileDelegationBackend

type ZonefileDelegationBackend struct {
	// contains filtered or unexported fields
}

func (*ZonefileDelegationBackend) ApplyChildUpdate

func (b *ZonefileDelegationBackend) ApplyChildUpdate(parentZone string, ur UpdateRequest) error

ApplyChildUpdate persists the update to the DB (as source of truth) and then regenerates the child's zone file fragment from the DB state.

func (*ZonefileDelegationBackend) GetDelegationData

func (b *ZonefileDelegationBackend) GetDelegationData(parentZone, childZone string) (map[string]map[uint16][]dns.RR, error)

func (*ZonefileDelegationBackend) ListChildren

func (b *ZonefileDelegationBackend) ListChildren(parentZone string) ([]string, error)

func (*ZonefileDelegationBackend) Name

Source Files

Directories

Path Synopsis
cache module
cli module
core module
crypto module
edns0 module
hpke module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL