initial commit
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
// Package models holds the plain data types shared between the storage layer,
|
||||
// the DNS engine and the HTTP layer. Nothing here depends on a database or on
|
||||
// the DNS library, which keeps the type definitions usable from every package.
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// ZoneKind distinguishes forward lookup zones from the two reverse families.
|
||||
type ZoneKind string
|
||||
|
||||
const (
|
||||
ZoneForward ZoneKind = "forward"
|
||||
ZoneReverse4 ZoneKind = "reverse4"
|
||||
ZoneReverse6 ZoneKind = "reverse6"
|
||||
)
|
||||
|
||||
// Valid reports whether k is a kind the application understands.
|
||||
func (k ZoneKind) Valid() bool {
|
||||
switch k {
|
||||
case ZoneForward, ZoneReverse4, ZoneReverse6:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Label returns a human readable name for the zone kind.
|
||||
func (k ZoneKind) Label() string {
|
||||
switch k {
|
||||
case ZoneReverse4:
|
||||
return "Reverse IPv4"
|
||||
case ZoneReverse6:
|
||||
return "Reverse IPv6"
|
||||
default:
|
||||
return "Forward"
|
||||
}
|
||||
}
|
||||
|
||||
// Zone is an authoritative DNS zone.
|
||||
type Zone struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"` // normalised FQDN, always ends in "."
|
||||
Kind ZoneKind `json:"kind"`
|
||||
Description string `json:"description"`
|
||||
Enabled bool `json:"enabled"`
|
||||
DefaultTTL uint32 `json:"default_ttl"`
|
||||
PrimaryNS string `json:"primary_ns"`
|
||||
AdminEmail string `json:"admin_email"`
|
||||
Serial uint32 `json:"serial"`
|
||||
Refresh uint32 `json:"refresh"`
|
||||
Retry uint32 `json:"retry"`
|
||||
Expire uint32 `json:"expire"`
|
||||
Minimum uint32 `json:"minimum"`
|
||||
AutoSerial bool `json:"auto_serial"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
// RecordCount is populated by list queries; it is not stored.
|
||||
RecordCount int `json:"record_count,omitempty"`
|
||||
}
|
||||
|
||||
// Record is a single resource record inside a zone. Name is stored relative to
|
||||
// the zone apex ("@" for the apex itself) and Data holds the rdata in the usual
|
||||
// zone-file presentation format.
|
||||
type Record struct {
|
||||
ID int64 `json:"id"`
|
||||
ZoneID int64 `json:"zone_id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Data string `json:"data"`
|
||||
TTL *uint32 `json:"ttl"` // nil means "inherit the zone default"
|
||||
Enabled bool `json:"enabled"`
|
||||
Comment string `json:"comment"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
// ZoneName is filled in by cross-zone queries for display purposes.
|
||||
ZoneName string `json:"zone_name,omitempty"`
|
||||
}
|
||||
|
||||
// EffectiveTTL resolves the record TTL against the zone default.
|
||||
func (r Record) EffectiveTTL(zoneDefault uint32) uint32 {
|
||||
if r.TTL != nil {
|
||||
return *r.TTL
|
||||
}
|
||||
return zoneDefault
|
||||
}
|
||||
|
||||
// BlockAction is what a policy does with a query that matched a blacklist.
|
||||
type BlockAction string
|
||||
|
||||
const (
|
||||
BlockNXDOMAIN BlockAction = "nxdomain"
|
||||
BlockRefused BlockAction = "refused"
|
||||
BlockSinkhole BlockAction = "sinkhole"
|
||||
)
|
||||
|
||||
// Valid reports whether a is a supported block action.
|
||||
func (a BlockAction) Valid() bool {
|
||||
switch a {
|
||||
case BlockNXDOMAIN, BlockRefused, BlockSinkhole:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Label returns a display name for the block action.
|
||||
func (a BlockAction) Label() string {
|
||||
switch a {
|
||||
case BlockRefused:
|
||||
return "REFUSED"
|
||||
case BlockSinkhole:
|
||||
return "Sinkhole"
|
||||
default:
|
||||
return "NXDOMAIN"
|
||||
}
|
||||
}
|
||||
|
||||
// Network is a client subnet that policies can be attached to.
|
||||
type Network struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
CIDR string `json:"cidr"`
|
||||
Description string `json:"description"`
|
||||
Enabled bool `json:"enabled"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
Policies []Policy `json:"policies,omitempty"`
|
||||
}
|
||||
|
||||
// Policy groups a set of blacklists and allowlists with a block action.
|
||||
type Policy struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Enabled bool `json:"enabled"`
|
||||
BlockAction BlockAction `json:"block_action"`
|
||||
SinkholeIPv4 string `json:"sinkhole_ipv4"`
|
||||
SinkholeIPv6 string `json:"sinkhole_ipv6"`
|
||||
BlockTTL uint32 `json:"block_ttl"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
BlacklistIDs []int64 `json:"blacklist_ids,omitempty"`
|
||||
AllowlistIDs []int64 `json:"allowlist_ids,omitempty"`
|
||||
NetworkCount int `json:"network_count,omitempty"`
|
||||
BlacklistName []string `json:"blacklist_names,omitempty"`
|
||||
AllowlistName []string `json:"allowlist_names,omitempty"`
|
||||
}
|
||||
|
||||
// DomainList is either a blacklist or an allowlist; the Kind field says which.
|
||||
type DomainList struct {
|
||||
ID int64 `json:"id"`
|
||||
Kind string `json:"kind"` // "blacklist" or "allowlist"
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Enabled bool `json:"enabled"`
|
||||
SourceURL string `json:"source_url"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
DomainCount int `json:"domain_count"`
|
||||
UsedBy []string `json:"used_by,omitempty"`
|
||||
}
|
||||
|
||||
// List kinds.
|
||||
const (
|
||||
KindBlacklist = "blacklist"
|
||||
KindAllowlist = "allowlist"
|
||||
)
|
||||
|
||||
// DomainEntry is one domain inside a DomainList.
|
||||
type DomainEntry struct {
|
||||
ID int64 `json:"id"`
|
||||
ListID int64 `json:"list_id"`
|
||||
Domain string `json:"domain"`
|
||||
MatchSubdomains bool `json:"match_subdomains"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Comment string `json:"comment"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// APIToken is a bearer credential for automation. The secret itself is never
|
||||
// stored: only a prefix (for lookup) and an Argon2id hash.
|
||||
type APIToken struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Prefix string `json:"prefix"`
|
||||
Enabled bool `json:"enabled"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
LastUsedAt *time.Time `json:"last_used_at"`
|
||||
|
||||
// Secret is populated only in the response that creates the token.
|
||||
Secret string `json:"secret,omitempty"`
|
||||
}
|
||||
|
||||
// Admin is the single administrator account.
|
||||
type Admin struct {
|
||||
Username string `json:"username"`
|
||||
PasswordHash string `json:"-"`
|
||||
MustChangePassword bool `json:"must_change_password"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
LastLoginAt *time.Time `json:"last_login_at"`
|
||||
}
|
||||
|
||||
// QueryLogEntry is one logged DNS query.
|
||||
type QueryLogEntry struct {
|
||||
ID int64 `json:"id"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
ClientIP string `json:"client_ip"`
|
||||
NetworkID *int64 `json:"network_id"`
|
||||
NetworkName string `json:"network_name"`
|
||||
QName string `json:"qname"`
|
||||
QType string `json:"qtype"`
|
||||
Rcode string `json:"rcode"`
|
||||
Source string `json:"source"`
|
||||
CacheHit bool `json:"cache_hit"`
|
||||
Blocked bool `json:"blocked"`
|
||||
PolicyID *int64 `json:"policy_id"`
|
||||
PolicyName string `json:"policy_name"`
|
||||
BlacklistID *int64 `json:"blacklist_id"`
|
||||
BlacklistName string `json:"blacklist_name"`
|
||||
MatchedRule string `json:"matched_rule"`
|
||||
Protocol string `json:"protocol"`
|
||||
DurationUS int64 `json:"duration_us"`
|
||||
AnswerCount int `json:"answer_count"`
|
||||
}
|
||||
|
||||
// DurationMS renders the query duration in milliseconds for the UI.
|
||||
func (q QueryLogEntry) DurationMS() float64 { return float64(q.DurationUS) / 1000.0 }
|
||||
|
||||
// Answer source values recorded in the query log and exported in metrics.
|
||||
const (
|
||||
SourceAuthoritative = "authoritative"
|
||||
SourceCache = "cache"
|
||||
SourceStale = "stale"
|
||||
SourceRecursive = "recursive"
|
||||
SourceBlocked = "blocked"
|
||||
SourceRefused = "refused"
|
||||
SourceRateLimited = "ratelimited"
|
||||
SourceError = "error"
|
||||
SourceLocal = "local"
|
||||
)
|
||||
|
||||
// AuditEntry records one administrative change.
|
||||
type AuditEntry struct {
|
||||
ID int64 `json:"id"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Actor string `json:"actor"`
|
||||
Source string `json:"source"` // web | api | cli | system
|
||||
ClientIP string `json:"client_ip"`
|
||||
Action string `json:"action"`
|
||||
ObjectType string `json:"object_type"`
|
||||
ObjectID string `json:"object_id"`
|
||||
ObjectName string `json:"object_name"`
|
||||
Details string `json:"details"`
|
||||
}
|
||||
|
||||
// ImportSummary reports the outcome of a bulk domain import.
|
||||
type ImportSummary struct {
|
||||
LinesProcessed int `json:"lines_processed"`
|
||||
Imported int `json:"imported"`
|
||||
Duplicates int `json:"duplicates"`
|
||||
Invalid int `json:"invalid"`
|
||||
Ignored int `json:"ignored"` // comments and blank lines
|
||||
InvalidSamples []string `json:"invalid_samples,omitempty"`
|
||||
}
|
||||
Reference in New Issue
Block a user