@@ -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.
212243func (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