Skip to content

Commit d50e047

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

File tree

2 files changed

+50
-2
lines changed

2 files changed

+50
-2
lines changed

proxy/proxy.go

Lines changed: 6 additions & 0 deletions
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

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ 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. The value of the boolean doesn't matter, this is only
29+
// a map to make lookups quicker than if it was a slice. If the key exists,
30+
// it is treated as local only.
31+
LocalOnlyDomains map[string]bool
32+
2733
// SubdomainExclusions is set of domains with subdomains exclusions.
2834
SubdomainExclusions *stringutil.Set
2935

@@ -58,16 +64,23 @@ var _ io.Closer = (*UpstreamConfig)(nil)
5864
//
5965
// [/domain1/../domainN/]#
6066
//
67+
// To ensure domains will never be looked up on any upstream servers, and will
68+
// respond with NXDOMAIN if not resolved locally, use the following syntax:
69+
//
70+
// [/domain1/../domainN/]-
71+
//
6172
// So the following config:
6273
//
6374
// [/host.com/]1.2.3.4
6475
// [/www.host.com/]2.3.4.5"
76+
// [/domain.local/]-
6577
// [/maps.host.com/news.host.com/]#
6678
// 3.4.5.6
6779
//
6880
// 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.
81+
// which will go to 2.3.4.5. Any requests to *.domain.local or domain.local
82+
// will only be resolved locally. And *.maps.host.com or *.news.host.com,
83+
// which will go to default server 3.4.5.6 with all other domains.
7184
//
7285
// To exclude top level domain from reserved upstreams querying you could use
7386
// the following:
@@ -95,6 +108,7 @@ func ParseUpstreamsConfig(upstreamConfig []string, options *upstream.Options) (*
95108
domainReservedUpstreams: map[string][]upstream.Upstream{},
96109
specifiedDomainUpstreams: map[string][]upstream.Upstream{},
97110
subdomainsOnlyUpstreams: map[string][]upstream.Upstream{},
111+
localOnlyDomains: map[string]bool{},
98112
subdomainsOnlyExclusions: stringutil.NewSet(),
99113
}
100114

@@ -121,6 +135,10 @@ type configParser struct {
121135
// corresponding upstreams.
122136
subdomainsOnlyUpstreams map[string][]upstream.Upstream
123137

138+
// localOnlyDomains is a list of domains which should never be looked up on
139+
// any upstream server.
140+
localOnlyDomains map[string]bool
141+
124142
// subdomainsOnlyExclusions is set of domains with subdomains exclusions.
125143
subdomainsOnlyExclusions *stringutil.Set
126144

@@ -147,6 +165,7 @@ func (p *configParser) parse(conf []string) (c *UpstreamConfig, err error) {
147165
DomainReservedUpstreams: p.domainReservedUpstreams,
148166
SpecifiedDomainUpstreams: p.specifiedDomainUpstreams,
149167
SubdomainExclusions: p.subdomainsOnlyExclusions,
168+
LocalOnlyDomains: p.localOnlyDomains,
150169
}, nil
151170
}
152171

@@ -158,6 +177,12 @@ func (p *configParser) parseLine(idx int, confLine string) (err error) {
158177
return err
159178
}
160179

180+
if upstreams[0] == "-" && len(domains) > 0 {
181+
p.specifyLocalOnly(domains)
182+
183+
return nil
184+
}
185+
161186
if upstreams[0] == "#" && len(domains) > 0 {
162187
p.excludeFromReserved(domains)
163188

@@ -208,6 +233,12 @@ func splitConfigLine(idx int, confLine string) (upstreams, domains []string, err
208233
return strings.Fields(upstreamsLine), domains, nil
209234
}
210235

236+
func (p *configParser) specifyLocalOnly(domains []string) {
237+
for _, domain := range domains {
238+
p.localOnlyDomains[domain] = true
239+
}
240+
}
241+
211242
// specifyUpstream specifies the upstream for domains.
212243
func (p *configParser) specifyUpstream(
213244
domains []string,
@@ -296,6 +327,17 @@ func (uc *UpstreamConfig) validate() (err error) {
296327
}
297328
}
298329

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

0 commit comments

Comments
 (0)