Skip to content

Commit 1579906

Browse files
committed
feat: specify local-only domains
1 parent f661fdc commit 1579906

File tree

2 files changed

+48
-2
lines changed

2 files changed

+48
-2
lines changed

proxy/proxy.go

+6
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,12 @@ func (p *Proxy) selectUpstreams(d *DNSContext) (upstreams []upstream.Upstream) {
530530
func (p *Proxy) replyFromUpstream(d *DNSContext) (ok bool, err error) {
531531
req := d.Req
532532

533+
if p.UpstreamConfig.checkLocalOnly(req.Question[0].Name) {
534+
resp := p.genWithRCode(req, dns.RcodeNameError)
535+
p.handleExchangeResult(d, req, resp, nil)
536+
return true, nil
537+
}
538+
533539
upstreams := p.selectUpstreams(d)
534540
if len(upstreams) == 0 {
535541
return false, fmt.Errorf("selecting general upstream: %w", upstream.ErrNoUpstreams)

proxy/upstreams.go

+42-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ type UpstreamConfig struct {
2424
// corresponding upstreams.
2525
SpecifiedDomainUpstreams map[string][]upstream.Upstream
2626

27+
// LocalOnlyDomains is a list of domains which should never be looked up on
28+
// any upstream server.
29+
LocalOnlyDomains map[string]bool
30+
2731
// SubdomainExclusions is set of domains with subdomains exclusions.
2832
SubdomainExclusions *stringutil.Set
2933

@@ -58,16 +62,23 @@ var _ io.Closer = (*UpstreamConfig)(nil)
5862
//
5963
// [/domain1/../domainN/]#
6064
//
65+
// To ensure domains will never be looked up on any upstream servers, and will
66+
// respond with NXDOMAIN if not resolved locally, use the following syntax:
67+
//
68+
// [/domain1/../domainN/]-
69+
//
6170
// So the following config:
6271
//
6372
// [/host.com/]1.2.3.4
6473
// [/www.host.com/]2.3.4.5"
74+
// [/domain.local/]-
6575
// [/maps.host.com/news.host.com/]#
6676
// 3.4.5.6
6777
//
6878
// will send queries for *.host.com to 1.2.3.4. Except for *.www.host.com,
69-
// which will go to 2.3.4.5. And *.maps.host.com or *.news.host.com, which
70-
// will go to default server 3.4.5.6 with all other domains.
79+
// which will go to 2.3.4.5. Any requests to *.domain.local or domain.local
80+
// will only be resolved locally. And *.maps.host.com or *.news.host.com,
81+
// which will go to default server 3.4.5.6 with all other domains.
7182
//
7283
// To exclude top level domain from reserved upstreams querying you could use
7384
// the following:
@@ -95,6 +106,7 @@ func ParseUpstreamsConfig(upstreamConfig []string, options *upstream.Options) (*
95106
domainReservedUpstreams: map[string][]upstream.Upstream{},
96107
specifiedDomainUpstreams: map[string][]upstream.Upstream{},
97108
subdomainsOnlyUpstreams: map[string][]upstream.Upstream{},
109+
localOnlyDomains: map[string]bool{},
98110
subdomainsOnlyExclusions: stringutil.NewSet(),
99111
}
100112

@@ -121,6 +133,10 @@ type configParser struct {
121133
// corresponding upstreams.
122134
subdomainsOnlyUpstreams map[string][]upstream.Upstream
123135

136+
// localOnlyDomains is a list of domains which should never be looked up on
137+
// any upstream server.
138+
localOnlyDomains map[string]bool
139+
124140
// subdomainsOnlyExclusions is set of domains with subdomains exclusions.
125141
subdomainsOnlyExclusions *stringutil.Set
126142

@@ -147,6 +163,7 @@ func (p *configParser) parse(conf []string) (c *UpstreamConfig, err error) {
147163
DomainReservedUpstreams: p.domainReservedUpstreams,
148164
SpecifiedDomainUpstreams: p.specifiedDomainUpstreams,
149165
SubdomainExclusions: p.subdomainsOnlyExclusions,
166+
LocalOnlyDomains: p.localOnlyDomains,
150167
}, nil
151168
}
152169

@@ -158,6 +175,12 @@ func (p *configParser) parseLine(idx int, confLine string) (err error) {
158175
return err
159176
}
160177

178+
if upstreams[0] == "-" && len(domains) > 0 {
179+
p.specifyLocalOnly(domains)
180+
181+
return nil
182+
}
183+
161184
if upstreams[0] == "#" && len(domains) > 0 {
162185
p.excludeFromReserved(domains)
163186

@@ -208,6 +231,12 @@ func splitConfigLine(idx int, confLine string) (upstreams, domains []string, err
208231
return strings.Fields(upstreamsLine), domains, nil
209232
}
210233

234+
func (p *configParser) specifyLocalOnly(domains []string) {
235+
for _, domain := range domains {
236+
p.localOnlyDomains[domain] = true
237+
}
238+
}
239+
211240
// specifyUpstream specifies the upstream for domains.
212241
func (p *configParser) specifyUpstream(
213242
domains []string,
@@ -296,6 +325,17 @@ func (uc *UpstreamConfig) validate() (err error) {
296325
}
297326
}
298327

328+
func (uc *UpstreamConfig) checkLocalOnly(host string) bool {
329+
for host != "" {
330+
var ok bool
331+
if _, ok = uc.LocalOnlyDomains[host]; ok {
332+
return true
333+
}
334+
_, host, _ = strings.Cut(host, ".")
335+
}
336+
return false
337+
}
338+
299339
// getUpstreamsForDomain looks for a domain in the reserved domains map and
300340
// returns a list of corresponding upstreams. It returns default upstreams list
301341
// if the domain was not found in the map. More specific domains take priority

0 commit comments

Comments
 (0)