blocky/resolver/sudn_resolver_test.go

189 lines
5.6 KiB
Go
Raw Normal View History

2022-09-04 01:27:24 +02:00
package resolver
import (
"context"
"fmt"
"github.com/0xERR0R/blocky/config"
. "github.com/0xERR0R/blocky/helpertest"
2022-09-04 01:27:24 +02:00
. "github.com/0xERR0R/blocky/model"
"github.com/0xERR0R/blocky/util"
"github.com/miekg/dns"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/types"
2022-09-04 01:27:24 +02:00
"github.com/stretchr/testify/mock"
)
var _ = Describe("SudnResolver", Label("sudnResolver"), func() {
var (
sut *SpecialUseDomainNamesResolver
sutConfig config.SUDN
m *mockResolver
ctx context.Context
cancelFn context.CancelFunc
2022-09-04 01:27:24 +02:00
)
refactor: configuration rework (usage and printing) (#920) * refactor: make `config.Duration` a struct with `time.Duration` embed Allows directly calling `time.Duration` methods. * refactor(HostsFileResolver): don't copy individual config items The idea is to make adding configuration options easier, and searching for references straight forward. * refactor: move config printing to struct and use a logger Using a logger allows using multiple levels so the whole configuration can be printed in trace/verbose mode, but only important parts are shown by default. * squash: rename `Cast` to `ToDuration` * squash: revert `Duration` to a simple wrapper ("new type" pattern) * squash: `Duration.IsZero` tests * squash: refactor resolvers to rely on their config directly if possible * squash: implement `IsEnabled` and `LogValues` for all resolvers * refactor: use go-enum `--values` to simplify getting all log fields * refactor: simplify `QType` unmarshaling * squash: rename `ValueLogger` to `Configurable` * squash: rename `UpstreamConfig` to `ParallelBestConfig` * squash: rename `RewriteConfig` to `RewriterConfig` * squash: config tests * squash: resolver tests * squash: add `ForEach` test and improve `Chain` ones * squash: simplify implementing `config.Configurable` * squash: minor changes for better coverage * squash: more `UnmarshalYAML` -> `UnmarshalText` * refactor: move `config.Upstream` into own file * refactor: add `Resolver.Type` method * squash: add `log` method to `typed` to use `Resolover.Type` as prefix * squash: tweak startup config logging * squash: add `LogResolverConfig` tests * squash: make sure all options of type `Duration` use `%s`
2023-03-12 22:14:10 +01:00
Describe("Type", func() {
It("follows conventions", func() {
expectValidResolverType(sut)
})
})
2022-09-04 01:27:24 +02:00
BeforeEach(func() {
var err error
ctx, cancelFn = context.WithCancel(context.Background())
DeferCleanup(cancelFn)
sutConfig, err = config.WithDefaults[config.SUDN]()
Expect(err).Should(Succeed())
})
JustBeforeEach(func() {
mockAnswer, err := util.NewMsgWithAnswer("example.com.", 300, A, "123.145.123.145")
2022-09-04 01:27:24 +02:00
Expect(err).Should(Succeed())
m = &mockResolver{}
2022-09-04 01:27:24 +02:00
m.On("Resolve", mock.Anything).Return(&Response{Res: mockAnswer}, nil)
sut = NewSpecialUseDomainNamesResolver(sutConfig)
2022-09-04 01:27:24 +02:00
sut.Next(m)
})
Describe("handlers", func() {
It("should have correct response type", func() {
for domain, handler := range sudnHandlers {
resp, err := sut.Resolve(ctx, newRequest(domain, A))
Expect(err).Should(Succeed())
refactor: configuration rework (usage and printing) (#920) * refactor: make `config.Duration` a struct with `time.Duration` embed Allows directly calling `time.Duration` methods. * refactor(HostsFileResolver): don't copy individual config items The idea is to make adding configuration options easier, and searching for references straight forward. * refactor: move config printing to struct and use a logger Using a logger allows using multiple levels so the whole configuration can be printed in trace/verbose mode, but only important parts are shown by default. * squash: rename `Cast` to `ToDuration` * squash: revert `Duration` to a simple wrapper ("new type" pattern) * squash: `Duration.IsZero` tests * squash: refactor resolvers to rely on their config directly if possible * squash: implement `IsEnabled` and `LogValues` for all resolvers * refactor: use go-enum `--values` to simplify getting all log fields * refactor: simplify `QType` unmarshaling * squash: rename `ValueLogger` to `Configurable` * squash: rename `UpstreamConfig` to `ParallelBestConfig` * squash: rename `RewriteConfig` to `RewriterConfig` * squash: config tests * squash: resolver tests * squash: add `ForEach` test and improve `Chain` ones * squash: simplify implementing `config.Configurable` * squash: minor changes for better coverage * squash: more `UnmarshalYAML` -> `UnmarshalText` * refactor: move `config.Upstream` into own file * refactor: add `Resolver.Type` method * squash: add `log` method to `typed` to use `Resolover.Type` as prefix * squash: tweak startup config logging * squash: add `LogResolverConfig` tests * squash: make sure all options of type `Duration` use `%s`
2023-03-12 22:14:10 +01:00
if handler == nil {
Expect(resp).ShouldNot(HaveResponseType(ResponseTypeSPECIAL))
refactor: configuration rework (usage and printing) (#920) * refactor: make `config.Duration` a struct with `time.Duration` embed Allows directly calling `time.Duration` methods. * refactor(HostsFileResolver): don't copy individual config items The idea is to make adding configuration options easier, and searching for references straight forward. * refactor: move config printing to struct and use a logger Using a logger allows using multiple levels so the whole configuration can be printed in trace/verbose mode, but only important parts are shown by default. * squash: rename `Cast` to `ToDuration` * squash: revert `Duration` to a simple wrapper ("new type" pattern) * squash: `Duration.IsZero` tests * squash: refactor resolvers to rely on their config directly if possible * squash: implement `IsEnabled` and `LogValues` for all resolvers * refactor: use go-enum `--values` to simplify getting all log fields * refactor: simplify `QType` unmarshaling * squash: rename `ValueLogger` to `Configurable` * squash: rename `UpstreamConfig` to `ParallelBestConfig` * squash: rename `RewriteConfig` to `RewriterConfig` * squash: config tests * squash: resolver tests * squash: add `ForEach` test and improve `Chain` ones * squash: simplify implementing `config.Configurable` * squash: minor changes for better coverage * squash: more `UnmarshalYAML` -> `UnmarshalText` * refactor: move `config.Upstream` into own file * refactor: add `Resolver.Type` method * squash: add `log` method to `typed` to use `Resolover.Type` as prefix * squash: tweak startup config logging * squash: add `LogResolverConfig` tests * squash: make sure all options of type `Duration` use `%s`
2023-03-12 22:14:10 +01:00
continue
}
refactor: configuration rework (usage and printing) (#920) * refactor: make `config.Duration` a struct with `time.Duration` embed Allows directly calling `time.Duration` methods. * refactor(HostsFileResolver): don't copy individual config items The idea is to make adding configuration options easier, and searching for references straight forward. * refactor: move config printing to struct and use a logger Using a logger allows using multiple levels so the whole configuration can be printed in trace/verbose mode, but only important parts are shown by default. * squash: rename `Cast` to `ToDuration` * squash: revert `Duration` to a simple wrapper ("new type" pattern) * squash: `Duration.IsZero` tests * squash: refactor resolvers to rely on their config directly if possible * squash: implement `IsEnabled` and `LogValues` for all resolvers * refactor: use go-enum `--values` to simplify getting all log fields * refactor: simplify `QType` unmarshaling * squash: rename `ValueLogger` to `Configurable` * squash: rename `UpstreamConfig` to `ParallelBestConfig` * squash: rename `RewriteConfig` to `RewriterConfig` * squash: config tests * squash: resolver tests * squash: add `ForEach` test and improve `Chain` ones * squash: simplify implementing `config.Configurable` * squash: minor changes for better coverage * squash: more `UnmarshalYAML` -> `UnmarshalText` * refactor: move `config.Upstream` into own file * refactor: add `Resolver.Type` method * squash: add `log` method to `typed` to use `Resolover.Type` as prefix * squash: tweak startup config logging * squash: add `LogResolverConfig` tests * squash: make sure all options of type `Duration` use `%s`
2023-03-12 22:14:10 +01:00
Expect(resp).
Should(
SatisfyAll(
HaveResponseType(ResponseTypeSPECIAL),
HaveReason("Special-Use Domain Name"),
))
2022-09-04 01:27:24 +02:00
}
})
})
Describe("Resolve", func() {
//nolint:unparam // linter thinks `qName` is always `A` because of "RFC 6762 Appendix G" table
entry := func(qType dns.Type, qName string, expectedRCode int, extraMatchers ...any) TableEntry {
GinkgoHelper()
var verb string
switch expectedRCode {
case dns.RcodeSuccess:
verb = "resolve"
case dns.RcodeNameError:
verb = "block"
}
description := fmt.Sprintf("should %s %s IN %s", verb, qName, qType)
args := []any{qType, qName, expectedRCode}
args = append(args, extraMatchers...)
return Entry(description, args...)
}
DescribeTable("handled domains",
func(qType dns.Type, qName string, expectedRCode int, extraMatchers ...types.GomegaMatcher) {
resp, err := sut.Resolve(ctx, newRequest(qName, qType))
Expect(err).Should(Succeed())
Expect(resp).Should(SatisfyAll(
HaveResponseType(ResponseTypeSPECIAL),
HaveReason("Special-Use Domain Name"),
HaveReturnCode(expectedRCode),
))
switch expectedRCode {
case dns.RcodeSuccess:
Expect(resp).Should(HaveTTL(BeNumerically("==", 0)))
case dns.RcodeNameError:
Expect(resp).Should(HaveNoAnswer())
}
Expect(resp).Should(SatisfyAll(extraMatchers...))
},
entry(A, "1.0.0.10.in-addr.arpa.", dns.RcodeNameError),
entry(A, "something.test.", dns.RcodeNameError),
entry(A, "something.localhost.", dns.RcodeSuccess, BeDNSRecord("something.localhost.", A, loopbackV4.String())),
entry(AAAA, "thing.localhost.", dns.RcodeSuccess, BeDNSRecord("thing.localhost.", AAAA, loopbackV6.String())),
entry(HTTPS, "something.localhost.", dns.RcodeNameError),
entry(A, "something.invalid.", dns.RcodeNameError),
entry(A, "something.local.", dns.RcodeNameError),
entry(HTTPS, "something.local.", dns.RcodeNameError),
entry(A, "1.0.254.169.in-addr.arpa.", dns.RcodeNameError),
entry(A, "something.intranet.", dns.RcodeNameError),
entry(A, "something.internal.", dns.RcodeNameError),
entry(A, "something.private.", dns.RcodeNameError),
entry(A, "something.corp.", dns.RcodeNameError),
entry(A, "something.home.", dns.RcodeNameError),
entry(A, "something.lan.", dns.RcodeNameError),
entry(A, "something.onion.", dns.RcodeNameError),
)
When("RFC 6762 Appendix G is disabled", func() {
BeforeEach(func() {
sutConfig.RFC6762AppendixG = false
})
DescribeTable("",
func(qType dns.Type, qName string, expectedRCode int) {
resp, err := sut.Resolve(ctx, newRequest(qName, qType))
Expect(err).Should(Succeed())
Expect(resp).Should(HaveReturnCode(expectedRCode))
Expect(resp).ShouldNot(HaveResponseType(ResponseTypeSPECIAL))
},
entry(A, "something.intranet.", dns.RcodeSuccess),
entry(A, "something.intranet.", dns.RcodeSuccess),
entry(A, "something.internal.", dns.RcodeSuccess),
entry(A, "something.private.", dns.RcodeSuccess),
entry(A, "something.corp.", dns.RcodeSuccess),
entry(A, "something.home.", dns.RcodeSuccess),
entry(A, "something.lan.", dns.RcodeSuccess),
)
2022-09-04 01:27:24 +02:00
})
It("should forward example.com", func() {
Expect(sut.Resolve(ctx, newRequest("example.com", A))).
Should(
SatisfyAll(
BeDNSRecord("example.com.", A, "123.145.123.145"),
HaveTTL(BeNumerically("==", 300)),
HaveResponseType(ResponseTypeRESOLVED),
HaveReturnCode(dns.RcodeSuccess),
))
2022-09-13 19:56:50 +02:00
})
It("should forward home.arpa. IN DS", func() {
Expect(sut.Resolve(ctx, newRequest("something.home.arpa.", DS))).
Should(
SatisfyAll(
// setup code doesn't care about the question
BeDNSRecord("example.com.", A, "123.145.123.145"),
HaveTTL(BeNumerically("==", 300)),
HaveResponseType(ResponseTypeRESOLVED),
HaveReturnCode(dns.RcodeSuccess),
))
2022-09-04 01:27:24 +02:00
})
It("should forward non special use domains", func() {
resp, err := sut.Resolve(ctx, newRequest("something.not-special.", AAAA))
Expect(err).Should(Succeed())
Expect(resp).ShouldNot(HaveResponseType(ResponseTypeSPECIAL))
})
2022-09-04 01:27:24 +02:00
})
})