@@ -27,6 +27,18 @@ type UpstreamConfig struct {
27
27
// SpecifiedDomainUpstreams maps the specific domain names to the upstreams.
28
28
SpecifiedDomainUpstreams map [string ][]upstream.Upstream
29
29
30
+ // SkippedDomains is a set of domains which will never be proxied to any
31
+ // upstream server. The dnsproxy will return NXDOMAIN for any of these
32
+ // domains or their subdomains.
33
+ SkippedDomains * container.MapSet [string ]
34
+
35
+ // SkippedDomainsExclusions is a set of domains which will be proxied to an
36
+ // upstream server, regardless of a match in SkippedDomains. This is mainly
37
+ // used when a wildcard subdomain is used in the upstream definition (e.g.
38
+ // *.my.domain), where my.domain would be proxied to an upstream, but any
39
+ // subdomain of it would be skipped.
40
+ SkippedDomainsExclusions * container.MapSet [string ]
41
+
30
42
// SubdomainExclusions is set of domains with subdomains exclusions.
31
43
SubdomainExclusions * container.MapSet [string ]
32
44
@@ -63,16 +75,24 @@ var _ io.Closer = (*UpstreamConfig)(nil)
63
75
//
64
76
// [/domain1/../domainN/]#
65
77
//
78
+ // Using a hyphen "-" as the <upstreamString> will ensure that the matched
79
+ // domain(s) will never be recursed to upstream nameservers. The dnsproxy will
80
+ // respond with NXDOMAIN for any matched domain or subdomain. For example:
81
+ //
82
+ // [/domain1/../domainN/]-
83
+ //
66
84
// So the following config:
67
85
//
68
86
// [/host.com/]1.2.3.4
69
87
// [/www.host.com/]2.3.4.5"
88
+ // [/domain.local/]-
70
89
// [/maps.host.com/news.host.com/]#
71
90
// 3.4.5.6
72
91
//
73
92
// will send queries for *.host.com to 1.2.3.4. Except for *.www.host.com,
74
- // which will go to 2.3.4.5. And *.maps.host.com or *.news.host.com, which
75
- // will go to default server 3.4.5.6 with all other domains.
93
+ // which will go to 2.3.4.5. Any requests to *.domain.local or domain.local
94
+ // will be answered with NXDOMAIN. And *.maps.host.com or *.news.host.com,
95
+ // which will go to default server 3.4.5.6 with all other domains.
76
96
//
77
97
// To exclude top level domain from reserved upstreams querying you could use
78
98
// the following:
@@ -108,6 +128,8 @@ func ParseUpstreamsConfig(
108
128
domainReservedUpstreams : map [string ][]upstream.Upstream {},
109
129
specifiedDomainUpstreams : map [string ][]upstream.Upstream {},
110
130
subdomainsOnlyUpstreams : map [string ][]upstream.Upstream {},
131
+ skippedDomains : container .NewMapSet [string ](),
132
+ skippedDomainsExclusions : container .NewMapSet [string ](),
111
133
subdomainsOnlyExclusions : container .NewMapSet [string ](),
112
134
}
113
135
@@ -161,6 +183,17 @@ type configParser struct {
161
183
// corresponding upstreams.
162
184
subdomainsOnlyUpstreams map [string ][]upstream.Upstream
163
185
186
+ // skippedDomains is a set of domains which should never be looked up on
187
+ // any upstream server.
188
+ skippedDomains * container.MapSet [string ]
189
+
190
+ // skippedDomainsExclusions is a set of domains which should be looked up on
191
+ // any upstream server, even though it matches an entry in skippedDomains.
192
+ // This is mainly used when a wildcard subdomain is used in the upstream
193
+ // definition (e.g. *.my.domain), where my.domain would be proxied to an
194
+ // upstream, but any subdomain of it would be skipped.
195
+ skippedDomainsExclusions * container.MapSet [string ]
196
+
164
197
// subdomainsOnlyExclusions is set of domains with subdomains exclusions.
165
198
subdomainsOnlyExclusions * container.MapSet [string ]
166
199
@@ -188,6 +221,8 @@ func (p *configParser) parse(lines []string) (c *UpstreamConfig, err error) {
188
221
DomainReservedUpstreams : p .domainReservedUpstreams ,
189
222
SpecifiedDomainUpstreams : p .specifiedDomainUpstreams ,
190
223
SubdomainExclusions : p .subdomainsOnlyExclusions ,
224
+ SkippedDomains : p .skippedDomains ,
225
+ SkippedDomainsExclusions : p .skippedDomainsExclusions ,
191
226
}, errors .Join (errs ... )
192
227
}
193
228
@@ -203,6 +238,12 @@ func (p *configParser) parseLine(idx int, confLine string) (err error) {
203
238
return err
204
239
}
205
240
241
+ if upstreams [0 ] == "-" && len (domains ) > 0 {
242
+ p .specifySkipped (domains )
243
+
244
+ return nil
245
+ }
246
+
206
247
if upstreams [0 ] == "#" && len (domains ) > 0 {
207
248
p .excludeFromReserved (domains )
208
249
@@ -253,6 +294,16 @@ func splitConfigLine(confLine string) (upstreams, domains []string, err error) {
253
294
return strings .Fields (upstreamsLine ), domains , nil
254
295
}
255
296
297
+ func (p * configParser ) specifySkipped (domains []string ) {
298
+ for _ , domain := range domains {
299
+ if strings .HasPrefix (domain , "*." ) {
300
+ domain = strings .TrimPrefix (domain , "*." )
301
+ p .skippedDomainsExclusions .Add (domain )
302
+ }
303
+ p .skippedDomains .Add (domain )
304
+ }
305
+ }
306
+
256
307
// specifyUpstream specifies the upstream for domains.
257
308
func (p * configParser ) specifyUpstream (domains []string , u string , idx int ) (err error ) {
258
309
dnsUpstream , ok := p .upstreamsIndex [u ]
@@ -373,7 +424,7 @@ func ValidatePrivateConfig(uc *UpstreamConfig, privateSubnets netutil.SubnetSet)
373
424
374
425
// getUpstreamsForDomain returns the upstreams specified for resolving fqdn. It
375
426
// always returns the default set of upstreams if the domain is not reserved for
376
- // any other upstreams.
427
+ // any other upstreams. If the domain is skipped, it returns nil.
377
428
//
378
429
// More specific domains take priority over less specific ones. For example, if
379
430
// the upstreams specified for the following domains:
@@ -384,6 +435,10 @@ func ValidatePrivateConfig(uc *UpstreamConfig, privateSubnets netutil.SubnetSet)
384
435
// The request for mail.host.com will be resolved using the upstreams specified
385
436
// for host.com.
386
437
func (uc * UpstreamConfig ) getUpstreamsForDomain (fqdn string ) (ups []upstream.Upstream ) {
438
+ if uc .checkSkipped (fqdn ) {
439
+ return nil
440
+ }
441
+
387
442
if len (uc .DomainReservedUpstreams ) == 0 {
388
443
return uc .Upstreams
389
444
}
@@ -413,6 +468,19 @@ func (uc *UpstreamConfig) getUpstreamsForDomain(fqdn string) (ups []upstream.Ups
413
468
return uc .Upstreams
414
469
}
415
470
471
+ func (uc * UpstreamConfig ) checkSkipped (host string ) bool {
472
+ if uc .SkippedDomainsExclusions .Has (host ) {
473
+ return false
474
+ }
475
+ for host != "" {
476
+ if uc .SkippedDomains .Has (host ) {
477
+ return true
478
+ }
479
+ _ , host , _ = strings .Cut (host , "." )
480
+ }
481
+ return false
482
+ }
483
+
416
484
// getUpstreamsForDS is like [getUpstreamsForDomain], but intended for DS
417
485
// queries only, so that it matches fqdn without the first label.
418
486
//
0 commit comments