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
|
### 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
|
- Two new metrics showing total number of responses from each upstream DNS
|
||||||
server and their average processing time in the Web UI ([#1453]).
|
server and their average processing time in the Web UI ([#1453]).
|
||||||
- The ability to set the port for the `pprof` debug API, see configuration
|
- 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
|
### 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
|
#### Configuration Changes
|
||||||
|
|
||||||
In this release, the schema version has changed from 24 to 25.
|
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
|
[#1453]: https://github.com/AdguardTeam/AdGuardHome/issues/1453
|
||||||
[#5948]: https://github.com/AdguardTeam/AdGuardHome/issues/5948
|
[#5948]: https://github.com/AdguardTeam/AdGuardHome/issues/5948
|
||||||
|
[#6053]: https://github.com/AdguardTeam/AdGuardHome/issues/6053
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
NOTE: Add new changes ABOVE THIS COMMENT.
|
NOTE: Add new changes ABOVE THIS COMMENT.
|
||||||
|
|
|
@ -10,6 +10,14 @@ import (
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"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
|
// ReplaceLogWriter moves logger output to w and uses Cleanup method of t to
|
||||||
// revert changes.
|
// revert changes.
|
||||||
func ReplaceLogWriter(t testing.TB, w io.Writer) {
|
func ReplaceLogWriter(t testing.TB, w io.Writer) {
|
||||||
|
|
|
@ -231,6 +231,17 @@ func createTestMessageWithType(host string, qtype uint16) *dns.Msg {
|
||||||
return req
|
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) {
|
func assertGoogleAResponse(t *testing.T, reply *dns.Msg) {
|
||||||
assertResponse(t, reply, net.IP{8, 8, 8, 8})
|
assertResponse(t, reply, net.IP{8, 8, 8, 8})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package dnsforward
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
|
@ -172,19 +173,26 @@ func (s *Server) filterDNSResponse(
|
||||||
case *dns.CNAME:
|
case *dns.CNAME:
|
||||||
host = strings.TrimSuffix(a.Target, ".")
|
host = strings.TrimSuffix(a.Target, ".")
|
||||||
rrtype = dns.TypeCNAME
|
rrtype = dns.TypeCNAME
|
||||||
|
|
||||||
|
res, err = s.checkHostRules(host, rrtype, setts)
|
||||||
case *dns.A:
|
case *dns.A:
|
||||||
host = a.A.String()
|
host = a.A.String()
|
||||||
rrtype = dns.TypeA
|
rrtype = dns.TypeA
|
||||||
|
|
||||||
|
res, err = s.checkHostRules(host, rrtype, setts)
|
||||||
case *dns.AAAA:
|
case *dns.AAAA:
|
||||||
host = a.AAAA.String()
|
host = a.AAAA.String()
|
||||||
rrtype = dns.TypeAAAA
|
rrtype = dns.TypeAAAA
|
||||||
|
|
||||||
|
res, err = s.checkHostRules(host, rrtype, setts)
|
||||||
|
case *dns.HTTPS:
|
||||||
|
res, err = s.filterHTTPSRecords(a, setts)
|
||||||
default:
|
default:
|
||||||
continue
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if res == nil {
|
} else if res == nil {
|
||||||
|
@ -199,3 +207,56 @@ func (s *Server) filterDNSResponse(
|
||||||
|
|
||||||
return nil, nil
|
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 (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"net/netip"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||||
|
@ -14,7 +15,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
func TestHandleDNSRequest_handleDNSRequest(t *testing.T) {
|
||||||
rules := `
|
rules := `
|
||||||
||blocked.domain^
|
||blocked.domain^
|
||||||
@@||allowed.domain^
|
@@||allowed.domain^
|
||||||
|
@ -23,6 +24,7 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
||||||
||::1^$dnstype=~AAAA
|
||::1^$dnstype=~AAAA
|
||||||
0.0.0.0 duplicate.domain
|
0.0.0.0 duplicate.domain
|
||||||
0.0.0.0 duplicate.domain
|
0.0.0.0 duplicate.domain
|
||||||
|
0.0.0.0 blocked.by.hostrule
|
||||||
`
|
`
|
||||||
|
|
||||||
forwardConf := ServerConfig{
|
forwardConf := ServerConfig{
|
||||||
|
@ -73,12 +75,19 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
||||||
startDeferStop(t, s)
|
startDeferStop(t, s)
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
req *dns.Msg
|
req *dns.Msg
|
||||||
name string
|
name string
|
||||||
wantAns []dns.RR
|
wantRCode int
|
||||||
|
wantAns []dns.RR
|
||||||
}{{
|
}{{
|
||||||
req: createTestMessage("cname.exception."),
|
req: createTestMessage(aghtest.ReqFQDN),
|
||||||
name: "cname_exception",
|
name: "pass",
|
||||||
|
wantRCode: dns.RcodeNameError,
|
||||||
|
wantAns: nil,
|
||||||
|
}, {
|
||||||
|
req: createTestMessage("cname.exception."),
|
||||||
|
name: "cname_exception",
|
||||||
|
wantRCode: dns.RcodeSuccess,
|
||||||
wantAns: []dns.RR{&dns.CNAME{
|
wantAns: []dns.RR{&dns.CNAME{
|
||||||
Hdr: dns.RR_Header{
|
Hdr: dns.RR_Header{
|
||||||
Name: "cname.exception.",
|
Name: "cname.exception.",
|
||||||
|
@ -87,8 +96,9 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
||||||
Target: "cname.specific.",
|
Target: "cname.specific.",
|
||||||
}},
|
}},
|
||||||
}, {
|
}, {
|
||||||
req: createTestMessage("should.block."),
|
req: createTestMessage("should.block."),
|
||||||
name: "blocked_by_cname",
|
name: "blocked_by_cname",
|
||||||
|
wantRCode: dns.RcodeSuccess,
|
||||||
wantAns: []dns.RR{&dns.A{
|
wantAns: []dns.RR{&dns.A{
|
||||||
Hdr: dns.RR_Header{
|
Hdr: dns.RR_Header{
|
||||||
Name: "should.block.",
|
Name: "should.block.",
|
||||||
|
@ -98,8 +108,9 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
||||||
A: netutil.IPv4Zero(),
|
A: netutil.IPv4Zero(),
|
||||||
}},
|
}},
|
||||||
}, {
|
}, {
|
||||||
req: createTestMessage("a.exception."),
|
req: createTestMessage("a.exception."),
|
||||||
name: "a_exception",
|
name: "a_exception",
|
||||||
|
wantRCode: dns.RcodeSuccess,
|
||||||
wantAns: []dns.RR{&dns.A{
|
wantAns: []dns.RR{&dns.A{
|
||||||
Hdr: dns.RR_Header{
|
Hdr: dns.RR_Header{
|
||||||
Name: "a.exception.",
|
Name: "a.exception.",
|
||||||
|
@ -108,8 +119,9 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
||||||
A: net.IP{0, 0, 0, 1},
|
A: net.IP{0, 0, 0, 1},
|
||||||
}},
|
}},
|
||||||
}, {
|
}, {
|
||||||
req: createTestMessageWithType("aaaa.exception.", dns.TypeAAAA),
|
req: createTestMessageWithType("aaaa.exception.", dns.TypeAAAA),
|
||||||
name: "aaaa_exception",
|
name: "aaaa_exception",
|
||||||
|
wantRCode: dns.RcodeSuccess,
|
||||||
wantAns: []dns.RR{&dns.AAAA{
|
wantAns: []dns.RR{&dns.AAAA{
|
||||||
Hdr: dns.RR_Header{
|
Hdr: dns.RR_Header{
|
||||||
Name: "aaaa.exception.",
|
Name: "aaaa.exception.",
|
||||||
|
@ -118,8 +130,9 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
||||||
AAAA: net.ParseIP("::1"),
|
AAAA: net.ParseIP("::1"),
|
||||||
}},
|
}},
|
||||||
}, {
|
}, {
|
||||||
req: createTestMessage("allowed.first."),
|
req: createTestMessage("allowed.first."),
|
||||||
name: "allowed_first",
|
name: "allowed_first",
|
||||||
|
wantRCode: dns.RcodeSuccess,
|
||||||
wantAns: []dns.RR{&dns.A{
|
wantAns: []dns.RR{&dns.A{
|
||||||
Hdr: dns.RR_Header{
|
Hdr: dns.RR_Header{
|
||||||
Name: "allowed.first.",
|
Name: "allowed.first.",
|
||||||
|
@ -129,8 +142,9 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
||||||
A: netutil.IPv4Zero(),
|
A: netutil.IPv4Zero(),
|
||||||
}},
|
}},
|
||||||
}, {
|
}, {
|
||||||
req: createTestMessage("blocked.first."),
|
req: createTestMessage("blocked.first."),
|
||||||
name: "blocked_first",
|
name: "blocked_first",
|
||||||
|
wantRCode: dns.RcodeSuccess,
|
||||||
wantAns: []dns.RR{&dns.A{
|
wantAns: []dns.RR{&dns.A{
|
||||||
Hdr: dns.RR_Header{
|
Hdr: dns.RR_Header{
|
||||||
Name: "blocked.first.",
|
Name: "blocked.first.",
|
||||||
|
@ -140,8 +154,9 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
||||||
A: netutil.IPv4Zero(),
|
A: netutil.IPv4Zero(),
|
||||||
}},
|
}},
|
||||||
}, {
|
}, {
|
||||||
req: createTestMessage("duplicate.domain."),
|
req: createTestMessage("duplicate.domain."),
|
||||||
name: "duplicate_domain",
|
name: "duplicate_domain",
|
||||||
|
wantRCode: dns.RcodeSuccess,
|
||||||
wantAns: []dns.RR{&dns.A{
|
wantAns: []dns.RR{&dns.A{
|
||||||
Hdr: dns.RR_Header{
|
Hdr: dns.RR_Header{
|
||||||
Name: "duplicate.domain.",
|
Name: "duplicate.domain.",
|
||||||
|
@ -150,6 +165,16 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
||||||
},
|
},
|
||||||
A: netutil.IPv4Zero(),
|
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 {
|
for _, tc := range testCases {
|
||||||
|
@ -164,7 +189,175 @@ func TestHandleDNSRequest_filterDNSResponse(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, dctx.Res)
|
require.NotNil(t, dctx.Res)
|
||||||
|
|
||||||
|
assert.Equal(t, tc.wantRCode, dctx.Res.Rcode)
|
||||||
assert.Equal(t, tc.wantAns, dctx.Res.Answer)
|
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,
|
res *filtering.Result,
|
||||||
) (resp *dns.Msg) {
|
) (resp *dns.Msg) {
|
||||||
req := dctx.Req
|
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 {
|
if s.conf.BlockingMode == BlockingModeNullIP {
|
||||||
return s.makeResponse(req)
|
return s.makeResponse(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.genNXDomain(req)
|
return s.newMsgNODATA(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch res.Reason {
|
switch res.Reason {
|
||||||
|
@ -314,6 +315,17 @@ func (s *Server) makeResponseREFUSED(request *dns.Msg) *dns.Msg {
|
||||||
return &resp
|
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 {
|
func (s *Server) genNXDomain(request *dns.Msg) *dns.Msg {
|
||||||
resp := dns.Msg{}
|
resp := dns.Msg{}
|
||||||
resp.SetRcode(request, dns.RcodeNameError)
|
resp.SetRcode(request, dns.RcodeNameError)
|
||||||
|
|
Loading…
Reference in New Issue