-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathstructs.go
More file actions
153 lines (134 loc) · 4.58 KB
/
Copy pathstructs.go
File metadata and controls
153 lines (134 loc) · 4.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
* Copyright (c) 2024 Johan Stenstam, johan.stenstam@internetstiftelsen.se
*/
package main
import (
"log"
"sync"
"sync/atomic"
"time"
"github.com/dnstapir/tapir"
"github.com/miekg/dns"
)
type PopData struct {
mu sync.RWMutex
Lists map[string]map[string]*tapir.WBGlist
RpzRefreshCh chan RpzRefresh
RpzCommandCh chan RpzCmdData
TapirMqttEngineRunning bool
TapirMqttCmdCh chan tapir.MqttEngineCmd
// TapirMqttSubCh chan tapir.MqttPkg
TapirObservations chan tapir.MqttPkgIn
TapirMqttPubCh chan tapir.MqttPkgOut
ComponentStatusCh chan tapir.ComponentStatusUpdate
Logger *log.Logger
MqttLogger *log.Logger
DenylistedNames map[string]bool
DoubtlistedNames map[string]*tapir.TapirName
Policy PopPolicy
// snapshot is the immutable, currently-served RPZ zone. Readers (DNS
// AXFR/IXFR/SOA handlers, HTTP debug) do snapshot.Load() and read the
// returned *ZoneSnapshot lock-free; it is never mutated after publish.
// The RefreshEngine goroutine is the sole publisher (snapshot.Store()).
// See docs/2026-06-02-pop-149-snapshot-concurrency-design.md.
snapshot atomic.Pointer[ZoneSnapshot]
// Rpz holds engine-private working state used to BUILD snapshots
// (the canonical serial, the zone name). Only touched on the engine
// goroutine (plus the guarded startup window).
Rpz RpzData
RpzSources map[string]*tapir.ZoneData // input feeds (generic zones); engine-side
Downstreams map[string]RpzDownstream // map[ipaddr]RpzDownstream
downstreamSerials *downstreamTracker // SOA serials by downstream IP; own mutex
ReaperInterval time.Duration
MqttEngine *tapir.MqttEngine
Verbose bool
Debug bool
}
type RpzDownstream struct {
Address string
Port int
}
// RpzData is engine-private working state. The served zone lives in the
// published ZoneSnapshot, not here.
type RpzData struct {
CurrentSerial uint32
ZoneName string
}
type RpzIxfr struct {
FromSerial uint32
ToSerial uint32
Removed []*tapir.RpzName
Added []*tapir.RpzName
}
// ZoneSnapshot is the compiled RPZ zone exactly as served. It is built by the
// RefreshEngine and, once published via PopData.snapshot.Store, is NEVER
// mutated. Every served field is bundled here so a single atomic pointer load
// yields a mutually-consistent view. The IXFR FROM/TO inner SOAs are derived
// from this single SOA (copying it and overriding .Serial), so there is now
// exactly one authoritative SOA — no dual-SOA drift.
type ZoneSnapshot struct {
ZoneName string
Serial uint32
SOA dns.SOA
NSrrs []dns.RR
Data map[string]*tapir.RpzName // fresh map per publish
IxfrChain []RpzIxfr // fresh slice per publish; newest LAST; bounded
}
// maxIxfrChain bounds the in-memory IXFR delta chain. A downstream further
// behind than this is served a full AXFR instead. Prevents a slow/dead/spoofed
// downstream from pinning unbounded memory.
const maxIxfrChain = 1000
// downstreamTracker records the latest SOA serial each downstream claims to
// hold. It is written from DNS handler goroutines (so it has its own mutex —
// it is the one piece of shared state not owned by the engine goroutine) and
// read by the engine when pruning the IXFR chain.
type downstreamTracker struct {
mu sync.Mutex
serials map[string]uint32
}
func newDownstreamTracker() *downstreamTracker {
return &downstreamTracker{serials: map[string]uint32{}}
}
func (d *downstreamTracker) record(ip string, serial uint32) {
d.mu.Lock()
d.serials[ip] = serial
d.mu.Unlock()
}
// lowest returns the smallest serial any tracked downstream claims, and whether
// there were any downstreams at all. Used to decide how far the IXFR chain can
// be pruned (we must keep deltas back to the slowest downstream).
func (d *downstreamTracker) lowest() (uint32, bool) {
d.mu.Lock()
defer d.mu.Unlock()
if len(d.serials) == 0 {
return 0, false
}
var low uint32 = ^uint32(0)
for _, s := range d.serials {
if s < low {
low = s
}
}
return low, true
}
type PopPolicy struct {
Logger *log.Logger
AllowlistAction tapir.Action
DenylistAction tapir.Action
Doubtlist DoubtlistPolicy
}
type DoubtlistPolicy struct {
NumSources int
NumSourcesAction tapir.Action
NumTapirTags int
NumTapirTagsAction tapir.Action
DenyTapirTags tapir.TagMask
DenyTapirAction tapir.Action
}
// type WBGC map[string]*tapir.WBGlist
type SrcFoo struct {
Src struct {
Style string `yaml:"style"`
} `yaml:"src"`
Sources map[string]SourceConf `yaml:"sources"`
}