Skip to content

Commit 9461681

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

File tree

2 files changed

+52
-2
lines changed

2 files changed

+52
-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

+46-2
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 set 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,12 @@ type configParser struct {
121135
// corresponding upstreams.
122136
subdomainsOnlyUpstreams map[string][]upstream.Upstream
123137

138+
// localOnlyDomains is a set of domains which should never be looked up on
139+
// any upstream server. The value of the boolean doesn't matter, this is only
140+
// a map to make lookups quicker than if it was a slice. If the key exists,
141+
// it is treated as local only.
142+
localOnlyDomains map[string]bool
143+
124144
// subdomainsOnlyExclusions is set of domains with subdomains exclusions.
125145
subdomainsOnlyExclusions *stringutil.Set
126146

@@ -147,6 +167,7 @@ func (p *configParser) parse(conf []string) (c *UpstreamConfig, err error) {
147167
DomainReservedUpstreams: p.domainReservedUpstreams,
148168
SpecifiedDomainUpstreams: p.specifiedDomainUpstreams,
149169
SubdomainExclusions: p.subdomainsOnlyExclusions,
170+
LocalOnlyDomains: p.localOnlyDomains,
150171
}, nil
151172
}
152173

@@ -158,6 +179,12 @@ func (p *configParser) parseLine(idx int, confLine string) (err error) {
158179
return err
159180
}
160181

182+
if upstreams[0] == "-" && len(domains) > 0 {
183+
p.specifyLocalOnly(domains)
184+
185+
return nil
186+
}
187+
161188
if upstreams[0] == "#" && len(domains) > 0 {
162189
p.excludeFromReserved(domains)
163190

@@ -208,6 +235,12 @@ func splitConfigLine(idx int, confLine string) (upstreams, domains []string, err
208235
return strings.Fields(upstreamsLine), domains, nil
209236
}
210237

238+
func (p *configParser) specifyLocalOnly(domains []string) {
239+
for _, domain := range domains {
240+
p.localOnlyDomains[domain] = true
241+
}
242+
}
243+
211244
// specifyUpstream specifies the upstream for domains.
212245
func (p *configParser) specifyUpstream(
213246
domains []string,
@@ -296,6 +329,17 @@ func (uc *UpstreamConfig) validate() (err error) {
296329
}
297330
}
298331

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

0 commit comments

Comments
 (0)