more config
build / build-push (push) Successful in 24s

This commit is contained in:
mats committed 2026-09-28 21:34:59 +02:00
1 parent 0b6dcbd547
commit d48c1863bf
9 files changed
+222 -7

No files matched your search

+43 -4
View File
@@ -255,8 +255,10 @@ func (u *Upstream) Stats() Stats {
// from, the upstreams, and the address index used for per-client upstream
// overrides. Readers take the pointer once and are then immune to reloads.
type poolState struct {
cfg config.Upstream
ups []*Upstream
cfg config.Upstream
ups []*Upstream
defaults []*Upstream
routes map[string][]*Upstream
// byAddr indexes the upstreams by every address they answer to, so an ACL
// group naming either the configured Addr or the resolved one works.
byAddr map[string][]*Upstream
@@ -344,6 +346,22 @@ func buildState(cfg config.Upstream, carry *poolState, lookup bool) *poolState {
}
st.ups = append(st.ups, up)
}
st.defaults = append([]*Upstream(nil), st.ups...)
st.routes = make(map[string][]*Upstream)
for _, z := range cfg.ForwardZones {
zone := strings.ToLower(dns.Fqdn(z.Zone))
for _, s := range z.Servers {
addr, resolved := resolveAddr(s, bootstraps, lookup)
up := &Upstream{cfg: s, addr: addr, resolved: resolved}
if old := prev[s.Addr+"/"+s.Proto]; old != nil {
up.stats = old.stats
} else {
up.stats = newUpstreamStats()
}
st.ups = append(st.ups, up)
st.routes[zone] = append(st.routes[zone], up)
}
}
st.byAddr = make(map[string][]*Upstream, len(st.ups)*2)
for _, up := range st.ups {
for _, a := range append([]string{up.cfg.Addr, up.addr}, up.resolved...) {
@@ -392,6 +410,9 @@ func (p *Pool) Config() config.Upstream {
// All returns every upstream.
func (p *Pool) All() []*Upstream { return p.state.Load().all() }
// Defaults returns the servers eligible for queries without a forwarding route.
func (p *Pool) Defaults() []*Upstream { return p.state.Load().defaults }
// Get returns an upstream by configured or resolved address, or nil.
func (p *Pool) Get(addr string) *Upstream {
for _, u := range p.All() {
@@ -457,8 +478,26 @@ func (p *Pool) clientFor(u *Upstream) *dns.Client {
// attempt.
func (p *Pool) Exchange(ctx context.Context, q *dns.Msg, want []string, timeout time.Duration) (*dns.Msg, string, error) {
st := p.state.Load()
candidates := st.all()
if len(want) > 0 {
candidates := st.defaults
// Walk complete labels, most specific first. A matched route never falls
// back to public resolvers, including when every target is unavailable.
matched := false
if len(q.Question) > 0 {
name := strings.ToLower(dns.Fqdn(q.Question[0].Name))
for name != "" {
if route, ok := st.routes[name]; ok {
candidates = route
matched = true
break
}
dot := strings.IndexByte(name, '.')
if dot < 0 {
break
}
name = name[dot+1:]
}
}
if !matched && len(want) > 0 {
candidates = p.restrict(st, want)
}
if len(candidates) == 0 {