forked from AdguardTeam/AdGuardHome
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dns.go
764 lines (611 loc) · 19.9 KB
/
dns.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
package dnsforward
import (
"encoding/binary"
"net"
"net/netip"
"strings"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/miekg/dns"
)
// To transfer information between modules
type dnsContext struct {
proxyCtx *proxy.DNSContext
// setts are the filtering settings for the client.
setts *filtering.Settings
result *filtering.Result
// origResp is the response received from upstream. It is set when the
// response is modified by filters.
origResp *dns.Msg
// unreversedReqIP stores an IP address obtained from PTR request if it
// parsed successfully and belongs to one of locally-served IP ranges as per
// RFC 6303.
unreversedReqIP net.IP
// err is the error returned from a processing function.
err error
// clientID is the ClientID from DoH, DoQ, or DoT, if provided.
clientID string
// origQuestion is the question received from the client. It is set
// when the request is modified by rewrites.
origQuestion dns.Question
// startTime is the time at which the processing of the request has started.
startTime time.Time
// protectionEnabled shows if the filtering is enabled, and if the
// server's DNS filter is ready.
protectionEnabled bool
// responseFromUpstream shows if the response is received from the
// upstream servers.
responseFromUpstream bool
// responseAD shows if the response had the AD bit set.
responseAD bool
// isLocalClient shows if client's IP address is from locally-served
// network.
isLocalClient bool
}
// resultCode is the result of a request processing function.
type resultCode int
const (
// resultCodeSuccess is returned when a handler performed successfully,
// and the next handler must be called.
resultCodeSuccess resultCode = iota
// resultCodeFinish is returned when a handler performed successfully,
// and the processing of the request must be stopped.
resultCodeFinish
// resultCodeError is returned when a handler failed, and the processing
// of the request must be stopped.
resultCodeError
)
// ddrHostFQDN is the FQDN used in Discovery of Designated Resolvers (DDR) requests.
// See https://www.ietf.org/archive/id/draft-ietf-add-ddr-06.html.
const ddrHostFQDN = "_dns.resolver.arpa."
// handleDNSRequest filters the incoming DNS requests and writes them to the query log
func (s *Server) handleDNSRequest(_ *proxy.Proxy, pctx *proxy.DNSContext) error {
dctx := &dnsContext{
proxyCtx: pctx,
result: &filtering.Result{},
startTime: time.Now(),
}
type modProcessFunc func(ctx *dnsContext) (rc resultCode)
// Since (*dnsforward.Server).handleDNSRequest(...) is used as
// proxy.(Config).RequestHandler, there is no need for additional index
// out of range checking in any of the following functions, because the
// (*proxy.Proxy).handleDNSRequest method performs it before calling the
// appropriate handler.
mods := []modProcessFunc{
s.processRecursion,
s.processInitial,
s.processDDRQuery,
s.processDetermineLocal,
s.processDHCPHosts,
s.processRestrictLocal,
s.processDHCPAddrs,
s.processFilteringBeforeRequest,
s.processLocalPTR,
s.processUpstream,
s.processFilteringAfterResponse,
s.ipset.process,
s.processQueryLogsAndStats,
}
for _, process := range mods {
r := process(dctx)
switch r {
case resultCodeSuccess:
// continue: call the next filter
case resultCodeFinish:
return nil
case resultCodeError:
return dctx.err
}
}
if pctx.Res != nil {
// Some devices require DNS message compression.
pctx.Res.Compress = true
}
return nil
}
// processRecursion checks the incoming request and halts it's handling if s
// have tried to resolve it recently.
func (s *Server) processRecursion(dctx *dnsContext) (rc resultCode) {
pctx := dctx.proxyCtx
if msg := pctx.Req; msg != nil && s.recDetector.check(*msg) {
log.Debug("recursion detected resolving %q", msg.Question[0].Name)
pctx.Res = s.genNXDomain(pctx.Req)
return resultCodeFinish
}
return resultCodeSuccess
}
// processInitial terminates the following processing for some requests if
// needed and enriches the ctx with some client-specific information.
//
// TODO(e.burkov): Decompose into less general processors.
func (s *Server) processInitial(dctx *dnsContext) (rc resultCode) {
pctx := dctx.proxyCtx
q := pctx.Req.Question[0]
qt := q.Qtype
if s.conf.AAAADisabled && qt == dns.TypeAAAA {
_ = proxy.CheckDisabledAAAARequest(pctx, true)
return resultCodeFinish
}
if s.conf.OnDNSRequest != nil {
s.conf.OnDNSRequest(pctx)
}
// Disable Mozilla DoH.
//
// See https://support.mozilla.org/en-US/kb/canary-domain-use-application-dnsnet.
if (qt == dns.TypeA || qt == dns.TypeAAAA) && q.Name == "use-application-dns.net." {
pctx.Res = s.genNXDomain(pctx.Req)
return resultCodeFinish
}
// Get the ClientID, if any, before getting client-specific filtering
// settings.
var key [8]byte
binary.BigEndian.PutUint64(key[:], pctx.RequestID)
dctx.clientID = string(s.clientIDCache.Get(key[:]))
// Get the client-specific filtering settings.
dctx.protectionEnabled = s.conf.ProtectionEnabled
dctx.setts = s.getClientRequestFilteringSettings(dctx)
return resultCodeSuccess
}
func (s *Server) setTableHostToIP(t hostToIPTable) {
s.tableHostToIPLock.Lock()
defer s.tableHostToIPLock.Unlock()
s.tableHostToIP = t
}
func (s *Server) setTableIPToHost(t ipToHostTable) {
s.tableIPToHostLock.Lock()
defer s.tableIPToHostLock.Unlock()
s.tableIPToHost = t
}
func (s *Server) onDHCPLeaseChanged(flags int) {
switch flags {
case dhcpd.LeaseChangedAdded,
dhcpd.LeaseChangedAddedStatic,
dhcpd.LeaseChangedRemovedStatic:
// Go on.
case dhcpd.LeaseChangedRemovedAll:
s.setTableHostToIP(nil)
s.setTableIPToHost(nil)
return
default:
return
}
ll := s.dhcpServer.Leases(dhcpd.LeasesAll)
hostToIP := make(hostToIPTable, len(ll))
ipToHost := make(ipToHostTable, len(ll))
for _, l := range ll {
// TODO(a.garipov): Remove this after we're finished with the client
// hostname validations in the DHCP server code.
err := netutil.ValidateDomainName(l.Hostname)
if err != nil {
log.Debug("dnsforward: skipping invalid hostname %q from dhcp: %s", l.Hostname, err)
continue
}
lowhost := strings.ToLower(l.Hostname + "." + s.localDomainSuffix)
// Assume that we only process IPv4 now.
//
// TODO(a.garipov): Remove once we switch to netip.Addr more fully.
ip, err := netutil.IPToAddr(l.IP, netutil.AddrFamilyIPv4)
if err != nil {
log.Debug("dnsforward: skipping invalid ip %v from dhcp: %s", l.IP, err)
continue
}
ipToHost[ip] = lowhost
hostToIP[lowhost] = ip
}
s.setTableHostToIP(hostToIP)
s.setTableIPToHost(ipToHost)
log.Debug("dnsforward: added %d a and ptr entries from dhcp", len(ipToHost))
}
// processDDRQuery responds to Discovery of Designated Resolvers (DDR) SVCB
// queries. The response contains different types of encryption supported by
// current user configuration.
//
// See https://www.ietf.org/archive/id/draft-ietf-add-ddr-10.html.
func (s *Server) processDDRQuery(dctx *dnsContext) (rc resultCode) {
pctx := dctx.proxyCtx
q := pctx.Req.Question[0]
if !s.conf.HandleDDR {
return resultCodeSuccess
}
if q.Name == ddrHostFQDN {
if s.dnsProxy.TLSListenAddr == nil && s.conf.HTTPSListenAddrs == nil &&
s.dnsProxy.QUICListenAddr == nil || q.Qtype != dns.TypeSVCB {
pctx.Res = s.makeResponse(pctx.Req)
return resultCodeFinish
}
pctx.Res = s.makeDDRResponse(pctx.Req)
return resultCodeFinish
}
return resultCodeSuccess
}
// makeDDRResponse creates a DDR answer based on the server configuration. The
// constructed SVCB resource records have the priority of 1 for each entry,
// similar to examples provided by the [draft standard].
//
// TODO(a.meshkov): Consider setting the priority values based on the protocol.
//
// [draft standard]: https://www.ietf.org/archive/id/draft-ietf-add-ddr-10.html.
func (s *Server) makeDDRResponse(req *dns.Msg) (resp *dns.Msg) {
resp = s.makeResponse(req)
// TODO(e.burkov): Think about storing the FQDN version of the server's
// name somewhere.
domainName := dns.Fqdn(s.conf.ServerName)
for _, addr := range s.conf.HTTPSListenAddrs {
values := []dns.SVCBKeyValue{
&dns.SVCBAlpn{Alpn: []string{"h2"}},
&dns.SVCBPort{Port: uint16(addr.Port)},
&dns.SVCBDoHPath{Template: "/dns-query{?dns}"},
}
ans := &dns.SVCB{
Hdr: s.hdr(req, dns.TypeSVCB),
Priority: 1,
Target: domainName,
Value: values,
}
resp.Answer = append(resp.Answer, ans)
}
for _, addr := range s.dnsProxy.TLSListenAddr {
values := []dns.SVCBKeyValue{
&dns.SVCBAlpn{Alpn: []string{"dot"}},
&dns.SVCBPort{Port: uint16(addr.Port)},
}
ans := &dns.SVCB{
Hdr: s.hdr(req, dns.TypeSVCB),
Priority: 1,
Target: domainName,
Value: values,
}
resp.Answer = append(resp.Answer, ans)
}
for _, addr := range s.dnsProxy.QUICListenAddr {
values := []dns.SVCBKeyValue{
&dns.SVCBAlpn{Alpn: []string{"doq"}},
&dns.SVCBPort{Port: uint16(addr.Port)},
}
ans := &dns.SVCB{
Hdr: s.hdr(req, dns.TypeSVCB),
Priority: 1,
Target: domainName,
Value: values,
}
resp.Answer = append(resp.Answer, ans)
}
return resp
}
// processDetermineLocal determines if the client's IP address is from
// locally-served network and saves the result into the context.
func (s *Server) processDetermineLocal(dctx *dnsContext) (rc resultCode) {
rc = resultCodeSuccess
var ip net.IP
if ip, _ = netutil.IPAndPortFromAddr(dctx.proxyCtx.Addr); ip == nil {
return rc
}
dctx.isLocalClient = s.privateNets.Contains(ip)
return rc
}
// dhcpHostToIP tries to get an IP leased by DHCP and returns the copy of
// address since the data inside the internal table may be changed while request
// processing. It's safe for concurrent use.
func (s *Server) dhcpHostToIP(host string) (ip netip.Addr, ok bool) {
s.tableHostToIPLock.Lock()
defer s.tableHostToIPLock.Unlock()
ip, ok = s.tableHostToIP[host]
return ip, ok
}
// processDHCPHosts respond to A requests if the target hostname is known to
// the server.
//
// TODO(a.garipov): Adapt to AAAA as well.
func (s *Server) processDHCPHosts(dctx *dnsContext) (rc resultCode) {
pctx := dctx.proxyCtx
req := pctx.Req
q := req.Question[0]
reqHost, ok := s.isDHCPClientHostQ(q)
if !ok {
return resultCodeSuccess
}
if !dctx.isLocalClient {
log.Debug("dnsforward: %q requests for dhcp host %q", pctx.Addr, reqHost)
pctx.Res = s.genNXDomain(req)
// Do not even put into query log.
return resultCodeFinish
}
ip, ok := s.dhcpHostToIP(reqHost)
if !ok {
// Go on and process them with filters, including dnsrewrite ones, and
// possibly route them to a domain-specific upstream.
log.Debug("dnsforward: no dhcp record for %q", reqHost)
return resultCodeSuccess
}
log.Debug("dnsforward: dhcp record for %q is %s", reqHost, ip)
resp := s.makeResponse(req)
if q.Qtype == dns.TypeA {
a := &dns.A{
Hdr: s.hdr(req, dns.TypeA),
A: ip.AsSlice(),
}
resp.Answer = append(resp.Answer, a)
}
dctx.proxyCtx.Res = resp
return resultCodeSuccess
}
// processRestrictLocal responds with NXDOMAIN to PTR requests for IP addresses
// in locally-served network from external clients.
func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) {
pctx := dctx.proxyCtx
req := pctx.Req
q := req.Question[0]
if q.Qtype != dns.TypePTR {
// No need for restriction.
return resultCodeSuccess
}
ip, err := netutil.IPFromReversedAddr(q.Name)
if err != nil {
log.Debug("dnsforward: parsing reversed addr: %s", err)
// DNS-Based Service Discovery uses PTR records having not an ARPA
// format of the domain name in question. Those shouldn't be
// invalidated. See http://www.dns-sd.org/ServerStaticSetup.html and
// RFC 2782.
name := strings.TrimSuffix(q.Name, ".")
if err = netutil.ValidateSRVDomainName(name); err != nil {
log.Debug("dnsforward: validating service domain: %s", err)
return resultCodeError
}
log.Debug("dnsforward: request is for a service domain")
return resultCodeSuccess
}
// Restrict an access to local addresses for external clients. We also
// assume that all the DHCP leases we give are locally-served or at least
// don't need to be accessible externally.
if !s.privateNets.Contains(ip) {
log.Debug("dnsforward: addr %s is not from locally-served network", ip)
return resultCodeSuccess
}
if !dctx.isLocalClient {
log.Debug("dnsforward: %q requests an internal ip", pctx.Addr)
pctx.Res = s.genNXDomain(req)
// Do not even put into query log.
return resultCodeFinish
}
// Do not perform unreversing ever again.
dctx.unreversedReqIP = ip
// There is no need to filter request from external addresses since this
// code is only executed when the request is for locally-served ARPA
// hostname so disable redundant filters.
dctx.setts.ParentalEnabled = false
dctx.setts.SafeBrowsingEnabled = false
dctx.setts.SafeSearchEnabled = false
dctx.setts.ServicesRules = nil
// Nothing to restrict.
return resultCodeSuccess
}
// ipToDHCPHost tries to get a hostname leased by DHCP. It's safe for
// concurrent use.
func (s *Server) ipToDHCPHost(ip netip.Addr) (host string, ok bool) {
s.tableIPToHostLock.Lock()
defer s.tableIPToHostLock.Unlock()
host, ok = s.tableIPToHost[ip]
return host, ok
}
// processDHCPAddrs responds to PTR requests if the target IP is leased by the
// DHCP server.
func (s *Server) processDHCPAddrs(dctx *dnsContext) (rc resultCode) {
pctx := dctx.proxyCtx
if pctx.Res != nil {
return resultCodeSuccess
}
ip := dctx.unreversedReqIP
if ip == nil {
return resultCodeSuccess
}
// TODO(a.garipov): Remove once we switch to netip.Addr more fully.
ipAddr, err := netutil.IPToAddrNoMapped(ip)
if err != nil {
log.Debug("dnsforward: bad reverse ip %v from dhcp: %s", ip, err)
return resultCodeSuccess
}
host, ok := s.ipToDHCPHost(ipAddr)
if !ok {
return resultCodeSuccess
}
log.Debug("dnsforward: dhcp reverse record for %s is %q", ip, host)
req := pctx.Req
resp := s.makeResponse(req)
ptr := &dns.PTR{
Hdr: dns.RR_Header{
Name: req.Question[0].Name,
Rrtype: dns.TypePTR,
Ttl: s.conf.BlockedResponseTTL,
Class: dns.ClassINET,
},
Ptr: dns.Fqdn(host),
}
resp.Answer = append(resp.Answer, ptr)
pctx.Res = resp
return resultCodeSuccess
}
// processLocalPTR responds to PTR requests if the target IP is detected to be
// inside the local network and the query was not answered from DHCP.
func (s *Server) processLocalPTR(dctx *dnsContext) (rc resultCode) {
pctx := dctx.proxyCtx
if pctx.Res != nil {
return resultCodeSuccess
}
ip := dctx.unreversedReqIP
if ip == nil {
return resultCodeSuccess
}
s.serverLock.RLock()
defer s.serverLock.RUnlock()
if !s.privateNets.Contains(ip) {
return resultCodeSuccess
}
if s.conf.UsePrivateRDNS {
s.recDetector.add(*pctx.Req)
if err := s.localResolvers.Resolve(pctx); err != nil {
dctx.err = err
return resultCodeError
}
}
if pctx.Res == nil {
pctx.Res = s.genNXDomain(pctx.Req)
// Do not even put into query log.
return resultCodeFinish
}
return resultCodeSuccess
}
// Apply filtering logic
func (s *Server) processFilteringBeforeRequest(ctx *dnsContext) (rc resultCode) {
if ctx.proxyCtx.Res != nil {
// Go on since the response is already set.
return resultCodeSuccess
}
s.serverLock.RLock()
defer s.serverLock.RUnlock()
if s.dnsFilter == nil {
return resultCodeSuccess
}
var err error
if ctx.result, err = s.filterDNSRequest(ctx); err != nil {
ctx.err = err
return resultCodeError
}
return resultCodeSuccess
}
// ipStringFromAddr extracts an IP address string from net.Addr.
func ipStringFromAddr(addr net.Addr) (ipStr string) {
if ip, _ := netutil.IPAndPortFromAddr(addr); ip != nil {
return ip.String()
}
return ""
}
// processUpstream passes request to upstream servers and handles the response.
func (s *Server) processUpstream(dctx *dnsContext) (rc resultCode) {
pctx := dctx.proxyCtx
req := pctx.Req
q := req.Question[0]
if pctx.Res != nil {
// The response has already been set.
return resultCodeSuccess
} else if reqHost, ok := s.isDHCPClientHostQ(q); ok {
// A DHCP client hostname query that hasn't been handled or filtered.
// Respond with an NXDOMAIN.
//
// TODO(a.garipov): Route such queries to a custom upstream for the
// local domain name if there is one.
log.Debug("dnsforward: dhcp client hostname %q was not filtered", reqHost)
pctx.Res = s.genNXDomain(req)
return resultCodeFinish
}
s.setCustomUpstream(pctx, dctx.clientID)
origReqAD := false
if s.conf.EnableDNSSEC {
if req.AuthenticatedData {
origReqAD = true
} else {
req.AuthenticatedData = true
}
}
// Process the request further since it wasn't filtered.
prx := s.proxy()
if prx == nil {
dctx.err = srvClosedErr
return resultCodeError
}
if dctx.err = prx.Resolve(pctx); dctx.err != nil {
return resultCodeError
}
dctx.responseFromUpstream = true
dctx.responseAD = pctx.Res.AuthenticatedData
if s.conf.EnableDNSSEC && !origReqAD {
pctx.Req.AuthenticatedData = false
pctx.Res.AuthenticatedData = false
}
return resultCodeSuccess
}
// isDHCPClientHostQ returns true if q is from a request for a DHCP client
// hostname. If ok is true, reqHost contains the requested hostname.
func (s *Server) isDHCPClientHostQ(q dns.Question) (reqHost string, ok bool) {
if !s.dhcpServer.Enabled() {
return "", false
}
// Include AAAA here, because despite the fact that we don't support it yet,
// the expected behavior here is to respond with an empty answer and not
// NXDOMAIN.
if qt := q.Qtype; qt != dns.TypeA && qt != dns.TypeAAAA {
return "", false
}
reqHost = strings.ToLower(q.Name[:len(q.Name)-1])
if strings.HasSuffix(reqHost, s.localDomainSuffix) {
return reqHost, true
}
return "", false
}
// setCustomUpstream sets custom upstream settings in pctx, if necessary.
func (s *Server) setCustomUpstream(pctx *proxy.DNSContext, clientID string) {
customUpsByClient := s.conf.GetCustomUpstreamByClient
if pctx.Addr == nil || customUpsByClient == nil {
return
}
// Use the ClientID first, since it has a higher priority.
id := stringutil.Coalesce(clientID, ipStringFromAddr(pctx.Addr))
upsConf, err := customUpsByClient(id)
if err != nil {
log.Error("dnsforward: getting custom upstreams for client %s: %s", id, err)
return
}
if upsConf != nil {
log.Debug("dnsforward: using custom upstreams for client %s", id)
}
pctx.CustomUpstreamConfig = upsConf
}
// Apply filtering logic after we have received response from upstream servers
func (s *Server) processFilteringAfterResponse(dctx *dnsContext) (rc resultCode) {
pctx := dctx.proxyCtx
switch res := dctx.result; res.Reason {
case filtering.NotFilteredAllowList:
return resultCodeSuccess
case
filtering.Rewritten,
filtering.RewrittenRule:
if dctx.origQuestion.Name == "" {
// origQuestion is set in case we get only CNAME without IP from
// rewrites table.
return resultCodeSuccess
}
pctx.Req.Question[0], pctx.Res.Question[0] = dctx.origQuestion, dctx.origQuestion
if len(pctx.Res.Answer) > 0 {
rr := s.genAnswerCNAME(pctx.Req, res.CanonName)
answer := append([]dns.RR{rr}, pctx.Res.Answer...)
pctx.Res.Answer = answer
}
return resultCodeSuccess
default:
return s.filterAfterResponse(dctx, pctx)
}
}
// filterAfterResponse returns the result of filtering the response that wasn't
// explicitly allowed or rewritten.
func (s *Server) filterAfterResponse(dctx *dnsContext, pctx *proxy.DNSContext) (res resultCode) {
// Check the response only if it's from an upstream. Don't check the
// response if the protection is disabled since dnsrewrite rules aren't
// applied to it anyway.
if !dctx.protectionEnabled || !dctx.responseFromUpstream || s.dnsFilter == nil {
return resultCodeSuccess
}
result, err := s.filterDNSResponse(pctx, dctx.setts)
if err != nil {
dctx.err = err
return resultCodeError
}
if result != nil {
dctx.result = result
dctx.origResp = pctx.Res
}
return resultCodeSuccess
}