Files
housecarl/internal/model/resolve.go
T
abrown38andClaude Opus 5.5 68ba2d06c5 Sets from Jarl: sets holding the addresses of names in Jarl
A set can take its addresses from Jarl: every A and AAAA record of its
names (NAME@VIEW), CNAMEs followed within Jarl's zones in the same view.
housecarld reads them every jarl.refresh and after each change, keeps
only the addresses within the zones and networks the set allows, and
keeps what it last read (saved in the database and the boot ruleset)
while Jarl can't be reached. A change of the addresses applies the
ruleset again without a history entry and is audited (set.jarl).

hc set add --jarl --within, hc set jarl; the kind "from Jarl" and
Addresses… on the Sets page; GET /sets/{name}/jarl and /jarl/refresh;
validation codes set_jarl_*, status warnings jarl_*; docs/jarl.md.
e2e phase with scripts/fakejarl.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 13:51:55 +01:00

97 lines
2.2 KiB
Go

package model
// FamilyRefs are a reference list's part for one address family: names of
// sets that have entries of the family (dynamic and Jarl sets always
// do), and
// literal entries.
type FamilyRefs struct {
Sets []string
Literals []string
}
// Empty reports whether nothing of the family was referenced.
func (f FamilyRefs) Empty() bool { return len(f.Sets) == 0 && len(f.Literals) == 0 }
// SplitRefs splits source or destination references by family. Unknown
// names are left out (validation reports them).
func (st *State) SplitRefs(refs []string) (v4, v6 FamilyRefs) {
for _, r := range refs {
if a, err := ParseAddress(r); err == nil {
if a.IsV6() {
v6.Literals = append(v6.Literals, a.String())
} else {
v4.Literals = append(v4.Literals, a.String())
}
continue
}
s := st.Set(r)
if s == nil {
continue
}
has4, has6 := s.Families()
if has4 {
v4.Sets = append(v4.Sets, s.Name)
}
if has6 {
v6.Sets = append(v6.Sets, s.Name)
}
}
return v4, v6
}
// Families reports which families a set holds: a dynamic or Jarl set may
// get either at run time.
func (s *AddressSet) Families() (v4, v6 bool) {
if s.Runtime() {
return true, true
}
for _, e := range s.Entries {
if a, err := ParseAddress(e); err == nil {
if a.IsV6() {
v6 = true
} else {
v4 = true
}
}
}
return v4, v6
}
// FamilyEntries returns a set's static entries of one family.
func (s *AddressSet) FamilyEntries(v6 bool) []string {
var out []string
for _, e := range s.Entries {
if a, err := ParseAddress(e); err == nil && a.IsV6() == v6 {
out = append(out, a.String())
}
}
return out
}
// ServiceEntries resolves service references (names and entries) to
// entries, without duplicates. Unknown names are left out.
func (st *State) ServiceEntries(refs []string) []ServiceEntry {
var out []ServiceEntry
seen := map[string]bool{}
add := func(s string) {
e, err := ParseServiceEntry(s)
if err != nil || seen[e.String()] {
return
}
seen[e.String()] = true
out = append(out, e)
}
for _, r := range refs {
if IsServiceEntry(r) {
add(r)
continue
}
if svc := st.Service(r); svc != nil {
for _, e := range svc.Entries {
add(e)
}
}
}
return out
}