Pull request: 6053-https-filtering
Updates #6053. Squashed commit of the following: commit b71957f87eca93e9827d027c246d2ca9d7a7f45a Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Aug 9 16:12:10 2023 +0300 all: docs commit 3e394fb2d723c4e305ea91f10fffc866f0b9948a Merge: f406a5ff4c47509fab
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Aug 9 15:15:37 2023 +0300 all: imp code commit f406a5ff4977acdcd19557969bd405747b84ebbc Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Aug 9 15:05:43 2023 +0300 all: imp code commit 0de1e0e8a9f0dfd3a0ff0c9e787d6e50cf2a1ee8 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Aug 9 14:45:21 2023 +0300 all: docs commit d98cbafe62edd77afcf6c760e28cb5e7632a993e Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Aug 9 11:54:39 2023 +0300 dnsforward: https blocked rcode commit c13ffda6182920f97fe8293a9c0b518bbf77956e Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Aug 9 10:45:27 2023 +0300 dnsforward: imp tests commit 9c5bc29b33d53ba82ca11f508391e5b5d534a834 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Wed Aug 9 10:08:06 2023 +0300 dnsforward: imp code commit d6ff28b9c277c24b4f273cd4b292543ead13d859 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Aug 8 16:00:15 2023 +0300 all: imp code commit 832b59965d1515badd0a0650f9753fc2985dff1c Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Aug 8 13:32:15 2023 +0300 dnsforward: https filtering commit 6a2bdd11331ffddb13bac4e05de85b6661360783 Merge: 257a1b6b854aee2272
Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Aug 8 11:44:12 2023 +0300 Merge remote-tracking branch 'origin/master' into 6053-https-filtering # Conflicts: # CHANGELOG.md commit 257a1b6b868826cb4112c1c88b177290242d3fdd Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Tue Aug 8 11:26:13 2023 +0300 dnsforward: imp tests commit edba217a72101b8b5a79e7b82614b3ea0e4c1f09 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Aug 4 15:03:02 2023 +0300 dnsforward: https filtering commit 4c93be3e0c7b98c1242b60ba5a3c45cea2775be4 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Aug 4 14:36:33 2023 +0300 docs: https filtering commit 1d2d1aa3b4ce7a994395fade2f87b2d88d68ac63 Author: Dimitry Kolyshev <dkolyshev@adguard.com> Date: Fri Aug 4 12:54:05 2023 +0300 all: https filtering hints
This commit is contained in:
parent
c47509fabc
commit
1e939703e5
|
@ -25,6 +25,7 @@ NOTE: Add new changes BELOW THIS COMMENT.
|
|||
|
||||
### Added
|
||||
|
||||
- The ability to filter DNS HTTPS records including IPv4/v6 hints. ([#6053]).
|
||||
- Two new metrics showing total number of responses from each upstream DNS
|
||||
server and their average processing time in the Web UI ([#1453]).
|
||||
- The ability to set the port for the `pprof` debug API, see configuration
|
||||
|
@ -32,6 +33,10 @@ NOTE: Add new changes BELOW THIS COMMENT.
|
|||
|
||||
### Changed
|
||||
|
||||
- For non-A and non-AAAA requests, which has been filtered, the NODATA response
|
||||
is returned if the blocking mode isn't set to `Null IP`. In previous versions
|
||||
it returned NXDOMAIN response in such cases.
|
||||
|
||||
#### Configuration Changes
|
||||
|
||||
In this release, the schema version has changed from 24 to 25.
|
||||
|
@ -63,6 +68,7 @@ In this release, the schema version has changed from 24 to 25.
|
|||
|
||||
[#1453]: https://github.com/AdguardTeam/AdGuardHome/issues/1453
|
||||
[#5948]: https://github.com/AdguardTeam/AdGuardHome/issues/5948
|
||||
[#6053]: https://github.com/AdguardTeam/AdGuardHome/issues/6053
|
||||
|
||||
<!--
|
||||
NOTE: Add new changes ABOVE THIS COMMENT.
|
||||
|
|
|
@ -10,6 +10,14 @@ import (
|
|||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// ReqHost is the common request host for filtering tests.
|
||||
ReqHost = "www.host.example"
|
||||
|
||||
// ReqFQDN is the common request FQDN for filtering tests.
|
||||
ReqFQDN = ReqHost + "."
|
||||
)
|
||||
|
||||
// ReplaceLogWriter moves logger output to w and uses Cleanup method of t to
|
||||
// revert changes.
|
||||
func ReplaceLogWriter(t testing.TB, w io.Writer) {
|
||||
|
|
|
@ -231,6 +231,17 @@ func createTestMessageWithType(host string, qtype uint16) *dns.Msg {
|
|||
return req
|
||||
}
|
||||
|
||||
// newResp returns the new DNS response with response code set to rcode, req
|
||||
// used as request, and rrs added.
|
||||
func newResp(rcode int, req *dns.Msg, ans []dns.RR) (resp *dns.Msg) {
|
||||
resp = (&dns.Msg{}).SetRcode(req, rcode)
|
||||
resp.RecursionAvailable = true
|
||||
resp.Compress = true
|
||||
resp.Answer = ans
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func assertGoogleAResponse(t *testing.T, reply *dns.Msg) {
|
||||
assertResponse(t, reply, net.IP{8, 8, 8, 8})
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package dnsforward
|
|||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
|
@ -172,19 +173,26 @@ func (s *Server) filterDNSResponse(
|
|||
case *dns.CNAME:
|
||||
host = strings.TrimSuffix(a.Target, ".")
|
||||
rrtype = dns.TypeCNAME
|
||||
|
||||
res, err = s.checkHostRules(host, rrtype, setts)
|
||||
case *dns.A:
|
||||
host = a.A.String()
|
||||
rrtype = dns.TypeA
|
||||
|
||||
res, err = s.checkHostRules(host, rrtype, setts)
|
||||
case *dns.AAAA:
|
||||
host = a.AAAA.String()
|
||||
rrtype = dns.TypeAAAA
|
||||
|
||||
res, err = s.checkHostRules(host, rrtype, setts)
|
||||
case *dns.HTTPS:
|
||||
res, err = s.filterHTTPSRecords(a, setts)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug("dnsforward: checking %s %s for %s", dns.Type(rrtype), host, a.Header().Name)
|
||||
log.Debug("dnsforward: checked %s %s for %s", dns.Type(rrtype), host, a.Header().Name)
|
||||
|
||||
res, err = s.checkHostRules(host, rrtype, setts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if res == nil {
|
||||
|
@ -199,3 +207,56 @@ func (s *Server) filterDNSResponse(
|
|||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// filterHTTPSRecords filters HTTPS answers information through all rule list
|
||||
// filters of the server filters.
|
||||
func (s *Server) filterHTTPSRecords(
|
||||
rr *dns.HTTPS,
|
||||
setts *filtering.Settings,
|
||||
) (r *filtering.Result, err error) {
|
||||
for _, kv := range rr.Value {
|
||||
var ips []net.IP
|
||||
switch hint := kv.(type) {
|
||||
case *dns.SVCBIPv4Hint:
|
||||
ips = hint.Hint
|
||||
case *dns.SVCBIPv6Hint:
|
||||
ips = hint.Hint
|
||||
default:
|
||||
// Go on.
|
||||
}
|
||||
|
||||
if len(ips) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
r, err = s.filterSVCBHint(ips, setts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("filtering svcb hints: %w", err)
|
||||
}
|
||||
|
||||
if r != nil {
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// filterSVCBHint filters SVCB hint information.
|
||||
func (s *Server) filterSVCBHint(
|
||||
hint []net.IP,
|
||||
setts *filtering.Settings,
|
||||
) (res *filtering.Result, err error) {
|
||||
for _, h := range hint {
|
||||
res, err = s.checkHostRules(h.String(), dns.TypeHTTPS, setts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("checking rules for %s: %w", h, err)
|
||||
}
|
||||
|
||||
if res != nil && res.IsFiltered {
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package dnsforward
|
|||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||
|
@ -14,7 +15,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
||||
func TestHandleDNSRequest_handleDNSRequest(t *testing.T) {
|
||||
rules := `
|
||||
||blocked.domain^
|
||||
@@||allowed.domain^
|
||||
|
@ -23,6 +24,7 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
|||
||::1^$dnstype=~AAAA
|
||||
0.0.0.0 duplicate.domain
|
||||
0.0.0.0 duplicate.domain
|
||||
0.0.0.0 blocked.by.hostrule
|
||||
`
|
||||
|
||||
forwardConf := ServerConfig{
|
||||
|
@ -75,10 +77,17 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
|||
testCases := []struct {
|
||||
req *dns.Msg
|
||||
name string
|
||||
wantRCode int
|
||||
wantAns []dns.RR
|
||||
}{{
|
||||
req: createTestMessage(aghtest.ReqFQDN),
|
||||
name: "pass",
|
||||
wantRCode: dns.RcodeNameError,
|
||||
wantAns: nil,
|
||||
}, {
|
||||
req: createTestMessage("cname.exception."),
|
||||
name: "cname_exception",
|
||||
wantRCode: dns.RcodeSuccess,
|
||||
wantAns: []dns.RR{&dns.CNAME{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: "cname.exception.",
|
||||
|
@ -89,6 +98,7 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
|||
}, {
|
||||
req: createTestMessage("should.block."),
|
||||
name: "blocked_by_cname",
|
||||
wantRCode: dns.RcodeSuccess,
|
||||
wantAns: []dns.RR{&dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: "should.block.",
|
||||
|
@ -100,6 +110,7 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
|||
}, {
|
||||
req: createTestMessage("a.exception."),
|
||||
name: "a_exception",
|
||||
wantRCode: dns.RcodeSuccess,
|
||||
wantAns: []dns.RR{&dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: "a.exception.",
|
||||
|
@ -110,6 +121,7 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
|||
}, {
|
||||
req: createTestMessageWithType("aaaa.exception.", dns.TypeAAAA),
|
||||
name: "aaaa_exception",
|
||||
wantRCode: dns.RcodeSuccess,
|
||||
wantAns: []dns.RR{&dns.AAAA{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: "aaaa.exception.",
|
||||
|
@ -120,6 +132,7 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
|||
}, {
|
||||
req: createTestMessage("allowed.first."),
|
||||
name: "allowed_first",
|
||||
wantRCode: dns.RcodeSuccess,
|
||||
wantAns: []dns.RR{&dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: "allowed.first.",
|
||||
|
@ -131,6 +144,7 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
|||
}, {
|
||||
req: createTestMessage("blocked.first."),
|
||||
name: "blocked_first",
|
||||
wantRCode: dns.RcodeSuccess,
|
||||
wantAns: []dns.RR{&dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: "blocked.first.",
|
||||
|
@ -142,6 +156,7 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
|||
}, {
|
||||
req: createTestMessage("duplicate.domain."),
|
||||
name: "duplicate_domain",
|
||||
wantRCode: dns.RcodeSuccess,
|
||||
wantAns: []dns.RR{&dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: "duplicate.domain.",
|
||||
|
@ -150,6 +165,16 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
|||
},
|
||||
A: netutil.IPv4Zero(),
|
||||
}},
|
||||
}, {
|
||||
req: createTestMessageWithType("blocked.domain.", dns.TypeHTTPS),
|
||||
name: "blocked_https_req",
|
||||
wantRCode: dns.RcodeSuccess,
|
||||
wantAns: nil,
|
||||
}, {
|
||||
req: createTestMessageWithType("blocked.by.hostrule.", dns.TypeHTTPS),
|
||||
name: "blocked_host_rule_https_req",
|
||||
wantRCode: dns.RcodeSuccess,
|
||||
wantAns: nil,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
@ -164,7 +189,175 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotNil(t, dctx.Res)
|
||||
|
||||
assert.Equal(t, tc.wantRCode, dctx.Res.Rcode)
|
||||
assert.Equal(t, tc.wantAns, dctx.Res.Answer)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
||||
const (
|
||||
passedIPv4Str = "1.1.1.1"
|
||||
blockedIPv4Str = "1.2.3.4"
|
||||
blockedIPv6Str = "1234::cdef"
|
||||
blockRules = blockedIPv4Str + "\n" + blockedIPv6Str + "\n"
|
||||
)
|
||||
|
||||
var (
|
||||
passedIPv4 net.IP = netip.MustParseAddr(passedIPv4Str).AsSlice()
|
||||
blockedIPv4 net.IP = netip.MustParseAddr(blockedIPv4Str).AsSlice()
|
||||
blockedIPv6 net.IP = netip.MustParseAddr(blockedIPv6Str).AsSlice()
|
||||
)
|
||||
|
||||
filters := []filtering.Filter{{
|
||||
ID: 0, Data: []byte(blockRules),
|
||||
}}
|
||||
|
||||
f, err := filtering.New(&filtering.Config{}, filters)
|
||||
require.NoError(t, err)
|
||||
|
||||
f.SetEnabled(true)
|
||||
|
||||
s, err := NewServer(DNSCreateParams{
|
||||
DHCPServer: testDHCP,
|
||||
DNSFilter: f,
|
||||
PrivateNets: netutil.SubnetSetFunc(netutil.IsLocallyServed),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
req *dns.Msg
|
||||
name string
|
||||
wantRule string
|
||||
respAns []dns.RR
|
||||
}{{
|
||||
name: "pass",
|
||||
req: createTestMessageWithType(aghtest.ReqFQDN, dns.TypeA),
|
||||
wantRule: "",
|
||||
respAns: []dns.RR{&dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: aghtest.ReqFQDN,
|
||||
Rrtype: dns.TypeA,
|
||||
Class: dns.ClassINET,
|
||||
},
|
||||
A: passedIPv4,
|
||||
}},
|
||||
}, {
|
||||
name: "ipv4",
|
||||
req: createTestMessageWithType(aghtest.ReqFQDN, dns.TypeA),
|
||||
wantRule: blockedIPv4Str,
|
||||
respAns: []dns.RR{&dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: aghtest.ReqFQDN,
|
||||
Rrtype: dns.TypeA,
|
||||
Class: dns.ClassINET,
|
||||
},
|
||||
A: blockedIPv4,
|
||||
}},
|
||||
}, {
|
||||
name: "ipv6",
|
||||
req: createTestMessageWithType(aghtest.ReqFQDN, dns.TypeAAAA),
|
||||
wantRule: blockedIPv6Str,
|
||||
respAns: []dns.RR{&dns.AAAA{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: aghtest.ReqFQDN,
|
||||
Rrtype: dns.TypeAAAA,
|
||||
Class: dns.ClassINET,
|
||||
},
|
||||
AAAA: blockedIPv6,
|
||||
}},
|
||||
}, {
|
||||
name: "ipv4hint",
|
||||
req: createTestMessageWithType(aghtest.ReqFQDN, dns.TypeHTTPS),
|
||||
wantRule: blockedIPv4Str,
|
||||
respAns: newSVCBHintsAnswer(
|
||||
aghtest.ReqFQDN,
|
||||
[]dns.SVCBKeyValue{
|
||||
&dns.SVCBIPv4Hint{Hint: []net.IP{blockedIPv4}},
|
||||
&dns.SVCBIPv6Hint{Hint: []net.IP{}},
|
||||
},
|
||||
),
|
||||
}, {
|
||||
name: "ipv6hint",
|
||||
req: createTestMessageWithType(aghtest.ReqFQDN, dns.TypeHTTPS),
|
||||
wantRule: blockedIPv6Str,
|
||||
respAns: newSVCBHintsAnswer(
|
||||
aghtest.ReqFQDN,
|
||||
[]dns.SVCBKeyValue{
|
||||
&dns.SVCBIPv4Hint{Hint: []net.IP{}},
|
||||
&dns.SVCBIPv6Hint{Hint: []net.IP{blockedIPv6}},
|
||||
},
|
||||
),
|
||||
}, {
|
||||
name: "ipv4_ipv6_hints",
|
||||
req: createTestMessageWithType(aghtest.ReqFQDN, dns.TypeHTTPS),
|
||||
wantRule: blockedIPv4Str,
|
||||
respAns: newSVCBHintsAnswer(
|
||||
aghtest.ReqFQDN,
|
||||
[]dns.SVCBKeyValue{
|
||||
&dns.SVCBIPv4Hint{Hint: []net.IP{blockedIPv4}},
|
||||
&dns.SVCBIPv6Hint{Hint: []net.IP{blockedIPv6}},
|
||||
},
|
||||
),
|
||||
}, {
|
||||
name: "pass_hints",
|
||||
req: createTestMessageWithType(aghtest.ReqFQDN, dns.TypeHTTPS),
|
||||
wantRule: "",
|
||||
respAns: newSVCBHintsAnswer(
|
||||
aghtest.ReqFQDN,
|
||||
[]dns.SVCBKeyValue{
|
||||
&dns.SVCBIPv4Hint{Hint: []net.IP{passedIPv4}},
|
||||
&dns.SVCBIPv6Hint{Hint: []net.IP{}},
|
||||
},
|
||||
),
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
resp := newResp(dns.RcodeSuccess, tc.req, tc.respAns)
|
||||
|
||||
pctx := &proxy.DNSContext{
|
||||
Proto: proxy.ProtoUDP,
|
||||
Req: tc.req,
|
||||
Res: resp,
|
||||
Addr: &net.UDPAddr{IP: net.IP{127, 0, 0, 1}, Port: 1},
|
||||
}
|
||||
|
||||
res, rErr := s.filterDNSResponse(pctx, &filtering.Settings{
|
||||
ProtectionEnabled: true,
|
||||
FilteringEnabled: true,
|
||||
})
|
||||
require.NoError(t, rErr)
|
||||
|
||||
if tc.wantRule == "" {
|
||||
assert.Nil(t, res)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
want := &filtering.Result{
|
||||
IsFiltered: true,
|
||||
Reason: filtering.FilteredBlockList,
|
||||
Rules: []*filtering.ResultRule{{
|
||||
Text: tc.wantRule,
|
||||
}},
|
||||
}
|
||||
assert.Equal(t, want, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// newSVCBHintsAnswer returns a test HTTPS answer RRs with SVCB hints.
|
||||
func newSVCBHintsAnswer(target string, hints []dns.SVCBKeyValue) (rrs []dns.RR) {
|
||||
return []dns.RR{&dns.HTTPS{
|
||||
SVCB: dns.SVCB{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: target,
|
||||
Rrtype: dns.TypeHTTPS,
|
||||
Class: dns.ClassINET,
|
||||
},
|
||||
Target: target,
|
||||
Value: hints,
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
|
|
@ -58,12 +58,13 @@ func (s *Server) genDNSFilterMessage(
|
|||
res *filtering.Result,
|
||||
) (resp *dns.Msg) {
|
||||
req := dctx.Req
|
||||
if qt := req.Question[0].Qtype; qt != dns.TypeA && qt != dns.TypeAAAA {
|
||||
qt := req.Question[0].Qtype
|
||||
if qt != dns.TypeA && qt != dns.TypeAAAA {
|
||||
if s.conf.BlockingMode == BlockingModeNullIP {
|
||||
return s.makeResponse(req)
|
||||
}
|
||||
|
||||
return s.genNXDomain(req)
|
||||
return s.newMsgNODATA(req)
|
||||
}
|
||||
|
||||
switch res.Reason {
|
||||
|
@ -314,6 +315,17 @@ func (s *Server) makeResponseREFUSED(request *dns.Msg) *dns.Msg {
|
|||
return &resp
|
||||
}
|
||||
|
||||
// newMsgNODATA returns a properly initialized NODATA response.
|
||||
//
|
||||
// See https://www.rfc-editor.org/rfc/rfc2308#section-2.2.
|
||||
func (s *Server) newMsgNODATA(req *dns.Msg) (resp *dns.Msg) {
|
||||
resp = (&dns.Msg{}).SetRcode(req, dns.RcodeSuccess)
|
||||
resp.RecursionAvailable = true
|
||||
resp.Ns = s.genSOA(req)
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
func (s *Server) genNXDomain(request *dns.Msg) *dns.Msg {
|
||||
resp := dns.Msg{}
|
||||
resp.SetRcode(request, dns.RcodeNameError)
|
||||
|
|
Loading…
Reference in New Issue