mirror of https://github.com/0xERR0R/blocky.git
refactor(resolvers): make `Bootstrap` implement `Resolver`
This commit is contained in:
parent
11543356b6
commit
659076dd7b
|
@ -271,6 +271,16 @@ type (
|
|||
bootstrapDNSConfig []BootstrappedUpstreamConfig
|
||||
)
|
||||
|
||||
func (b *BootstrapDNSConfig) IsEnabled() bool {
|
||||
return len(*b) != 0
|
||||
}
|
||||
|
||||
func (b *BootstrapDNSConfig) LogConfig(*logrus.Entry) {
|
||||
// This should not be called, at least for now:
|
||||
// The Boostrap resolver is not in the chain and thus its config is not logged
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// split in two types to avoid infinite recursion. See `BootstrappedUpstreamConfig.UnmarshalYAML`.
|
||||
type (
|
||||
BootstrappedUpstreamConfig bootstrappedUpstreamConfig
|
||||
|
|
|
@ -681,6 +681,31 @@ bootstrapDns:
|
|||
})
|
||||
})
|
||||
|
||||
Describe("BootstrapDNSConfig", func() {
|
||||
It("is not enabled when empty", func() {
|
||||
var sut BootstrapDNSConfig
|
||||
|
||||
Expect(sut.IsEnabled()).Should(BeFalse())
|
||||
})
|
||||
|
||||
It("is enabled if non empty", func() {
|
||||
sut := BootstrapDNSConfig{
|
||||
BootstrappedUpstreamConfig{},
|
||||
BootstrappedUpstreamConfig{},
|
||||
}
|
||||
|
||||
Expect(sut.IsEnabled()).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("LogConfig panics", func() {
|
||||
sut := BootstrapDNSConfig{}
|
||||
|
||||
Expect(func() {
|
||||
sut.LogConfig(logger)
|
||||
}).Should(Panic())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("SourceLoadingConfig", func() {
|
||||
var (
|
||||
ctx context.Context
|
||||
|
|
|
@ -26,7 +26,8 @@ const (
|
|||
|
||||
// Bootstrap allows resolving hostnames using the configured bootstrap DNS.
|
||||
type Bootstrap struct {
|
||||
log *logrus.Entry
|
||||
configurable[*config.BootstrapDNSConfig]
|
||||
typed
|
||||
|
||||
resolver Resolver
|
||||
bootstraped bootstrapedResolvers
|
||||
|
@ -44,8 +45,6 @@ type Bootstrap struct {
|
|||
// NewBootstrap creates and returns a new Bootstrap.
|
||||
// Internally, it uses a CachingResolver and an UpstreamResolver.
|
||||
func NewBootstrap(ctx context.Context, cfg *config.Config) (b *Bootstrap, err error) {
|
||||
logger := log.PrefixedLog("bootstrap")
|
||||
|
||||
timeout := defaultTimeout
|
||||
if cfg.Upstreams.Timeout.IsAboveZero() {
|
||||
timeout = cfg.Upstreams.Timeout.ToDuration()
|
||||
|
@ -55,7 +54,9 @@ func NewBootstrap(ctx context.Context, cfg *config.Config) (b *Bootstrap, err er
|
|||
// This also prevents the GC to clean up these two structs, but is not currently an
|
||||
// issue since they stay allocated until the process terminates
|
||||
b = &Bootstrap{
|
||||
log: logger,
|
||||
configurable: withConfig(&cfg.BootstrapDNS),
|
||||
typed: withType("bootstrap"),
|
||||
|
||||
connectIPVersion: cfg.ConnectIPVersion,
|
||||
|
||||
systemResolver: net.DefaultResolver,
|
||||
|
@ -71,7 +72,7 @@ func NewBootstrap(ctx context.Context, cfg *config.Config) (b *Bootstrap, err er
|
|||
}
|
||||
|
||||
if len(bootstraped) == 0 {
|
||||
logger.Infof("bootstrapDns is not configured, will use system resolver")
|
||||
b.log().Info("bootstrapDns is not configured, will use system resolver")
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
@ -103,6 +104,20 @@ func NewBootstrap(ctx context.Context, cfg *config.Config) (b *Bootstrap, err er
|
|||
return b, nil
|
||||
}
|
||||
|
||||
func (b *Bootstrap) Resolve(ctx context.Context, request *model.Request) (*model.Response, error) {
|
||||
if b.resolver == nil {
|
||||
// We could implement most queries using the `b.systemResolver.Lookup*` functions,
|
||||
// but that requires a lot of boilerplate to translate from `dns` to `net` and back.
|
||||
return nil, errors.New("cannot resolve arbitrary requests using the system resolver")
|
||||
}
|
||||
|
||||
// Add bootstrap prefix to all inner resolver logs
|
||||
req := *request
|
||||
req.Log = log.WithPrefix(req.Log, b.Type())
|
||||
|
||||
return b.resolver.Resolve(ctx, &req)
|
||||
}
|
||||
|
||||
func (b *Bootstrap) UpstreamIPs(ctx context.Context, r *UpstreamResolver) (*IPSet, error) {
|
||||
hostname := r.cfg.Host
|
||||
|
||||
|
@ -112,7 +127,7 @@ func (b *Bootstrap) UpstreamIPs(ctx context.Context, r *UpstreamResolver) (*IPSe
|
|||
|
||||
ips, err := b.resolveUpstream(ctx, r, hostname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not resolve IPs for upstream %s: %w", hostname, err)
|
||||
}
|
||||
|
||||
return newIPSet(ips), nil
|
||||
|
@ -149,7 +164,7 @@ func (b *Bootstrap) NewHTTPTransport() *http.Transport {
|
|||
}
|
||||
|
||||
func (b *Bootstrap) dialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
logger := b.log.WithField("network", network).WithField("addr", addr)
|
||||
logger := b.log().WithFields(logrus.Fields{"network": network, "addr": addr})
|
||||
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
|
@ -217,7 +232,7 @@ func (b *Bootstrap) resolveType(ctx context.Context, hostname string, qType dns.
|
|||
|
||||
req := model.Request{
|
||||
Req: util.NewMsgWithQuestion(hostname, qType),
|
||||
Log: b.log,
|
||||
Log: b.log(),
|
||||
}
|
||||
|
||||
rsp, err := b.resolver.Resolve(ctx, &req)
|
||||
|
@ -243,7 +258,7 @@ func (b *Bootstrap) resolveType(ctx context.Context, hostname string, qType dns.
|
|||
return ips, nil
|
||||
}
|
||||
|
||||
// map of bootstraped resolvers their hardcoded IPs
|
||||
// map of bootstraped resolvers to their hardcoded IPs
|
||||
type bootstrapedResolvers map[Resolver][]net.IP
|
||||
|
||||
func newBootstrapedResolvers(
|
||||
|
@ -267,7 +282,7 @@ func newBootstrapedResolvers(
|
|||
continue
|
||||
}
|
||||
|
||||
var ips []net.IP
|
||||
ips := make([]net.IP, 0, len(upstreamCfg.IPs)+1)
|
||||
|
||||
if ip := net.ParseIP(upstream.Host); ip != nil {
|
||||
ips = append(ips, ip)
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/0xERR0R/blocky/config"
|
||||
"github.com/0xERR0R/blocky/log"
|
||||
|
@ -24,7 +25,7 @@ import (
|
|||
var _ = Describe("Bootstrap", Label("bootstrap"), func() {
|
||||
var (
|
||||
sut *Bootstrap
|
||||
sutConfig *config.Config
|
||||
sutConfig config.Config
|
||||
ctx context.Context
|
||||
cancelFn context.CancelFunc
|
||||
|
||||
|
@ -32,7 +33,7 @@ var _ = Describe("Bootstrap", Label("bootstrap"), func() {
|
|||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
sutConfig = &config.Config{
|
||||
sutConfig = config.Config{
|
||||
BootstrapDNS: []config.BootstrappedUpstreamConfig{
|
||||
{
|
||||
Upstream: config.Upstream{
|
||||
|
@ -50,23 +51,23 @@ var _ = Describe("Bootstrap", Label("bootstrap"), func() {
|
|||
})
|
||||
|
||||
JustBeforeEach(func() {
|
||||
sut, err = NewBootstrap(ctx, sutConfig)
|
||||
sut, err = NewBootstrap(ctx, &sutConfig)
|
||||
Expect(err).Should(Succeed())
|
||||
})
|
||||
|
||||
Describe("configuration", func() {
|
||||
When("is not specified", func() {
|
||||
BeforeEach(func() {
|
||||
sutConfig = &config.Config{}
|
||||
sutConfig.BootstrapDNS = config.BootstrapDNSConfig{}
|
||||
})
|
||||
|
||||
It("should use the system resolver", func() {
|
||||
usedSystemResolver := make(chan bool, 100)
|
||||
var usedSystemResolver atomic.Bool
|
||||
|
||||
sut.systemResolver = &net.Resolver{
|
||||
PreferGo: true,
|
||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
usedSystemResolver <- true
|
||||
usedSystemResolver.Store(true)
|
||||
|
||||
return nil, errors.New("don't actually do anything")
|
||||
},
|
||||
|
@ -74,7 +75,7 @@ var _ = Describe("Bootstrap", Label("bootstrap"), func() {
|
|||
|
||||
_, err := sut.resolveUpstream(ctx, nil, "example.com")
|
||||
Expect(err).Should(HaveOccurred())
|
||||
Expect(usedSystemResolver).Should(Receive(BeTrue()))
|
||||
Expect(usedSystemResolver.Load()).Should(BeTrue())
|
||||
})
|
||||
|
||||
Describe("HTTP transport", func() {
|
||||
|
@ -113,7 +114,7 @@ var _ = Describe("Bootstrap", Label("bootstrap"), func() {
|
|||
Context("using TCP UDP", func() {
|
||||
When("hostname is an IP", func() {
|
||||
BeforeEach(func() {
|
||||
sutConfig = &config.Config{
|
||||
sutConfig = config.Config{
|
||||
BootstrapDNS: []config.BootstrappedUpstreamConfig{
|
||||
{
|
||||
Upstream: config.Upstream{
|
||||
|
@ -154,7 +155,7 @@ var _ = Describe("Bootstrap", Label("bootstrap"), func() {
|
|||
|
||||
When("extra IPs are configured", func() {
|
||||
BeforeEach(func() {
|
||||
sutConfig = &config.Config{
|
||||
sutConfig = config.Config{
|
||||
BootstrapDNS: []config.BootstrappedUpstreamConfig{
|
||||
{
|
||||
Upstream: config.Upstream{
|
||||
|
@ -334,6 +335,29 @@ var _ = Describe("Bootstrap", Label("bootstrap"), func() {
|
|||
Expect(rsp.Res.Question[0].Name).Should(Equal("example.com."))
|
||||
Expect(rsp.Res.Id).ShouldNot(Equal(bootstrapResponse.Id))
|
||||
})
|
||||
|
||||
Describe("Resolve", func() {
|
||||
It("calls the usptream resolver", func(ctx context.Context) {
|
||||
expected := new(model.Response)
|
||||
|
||||
bootstrapUpstream.On("Resolve", mock.Anything).Return(expected, nil)
|
||||
|
||||
Expect(sut.Resolve(ctx, newRequest("example.com.", A))).
|
||||
Should(BeIdenticalTo(expected))
|
||||
})
|
||||
|
||||
When("using the system resolver", func() {
|
||||
JustBeforeEach(func() {
|
||||
sut = systemResolverBootstrap
|
||||
})
|
||||
|
||||
It("can't resolve arbitrary requests", func(ctx context.Context) {
|
||||
_, err := sut.Resolve(ctx, newRequest("example.com.", A))
|
||||
Expect(err).
|
||||
Should(MatchError(errArbitrarySystemResolverRequest))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("HTTP Transport", func() {
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/0xERR0R/blocky/config"
|
||||
"github.com/0xERR0R/blocky/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/0xERR0R/blocky/model"
|
||||
|
||||
|
@ -119,13 +120,22 @@ func autoAnswer(qType dns.Type, qName string) (*dns.Msg, error) {
|
|||
|
||||
// newTestBootstrap creates a test Bootstrap
|
||||
func newTestBootstrap(ctx context.Context, response *dns.Msg) *Bootstrap {
|
||||
const cfgTxt = `
|
||||
upstream: https://mock
|
||||
ips:
|
||||
- 0.0.0.0
|
||||
`
|
||||
|
||||
bootstrapUpstream := &mockResolver{}
|
||||
|
||||
b, err := NewBootstrap(ctx, &config.Config{})
|
||||
var bCfg config.BootstrapDNSConfig
|
||||
err := yaml.UnmarshalStrict([]byte(cfgTxt), &bCfg)
|
||||
util.FatalOnError("test bootstrap config is broken, did you change the struct?", err)
|
||||
|
||||
b, err := NewBootstrap(ctx, &config.Config{BootstrapDNS: bCfg})
|
||||
util.FatalOnError("can't create bootstrap", err)
|
||||
|
||||
b.resolver = bootstrapUpstream
|
||||
b.bootstraped = bootstrapedResolvers{bootstrapUpstream: []net.IP{}}
|
||||
|
||||
if response != nil {
|
||||
bootstrapUpstream.
|
||||
|
|
|
@ -12,7 +12,10 @@ import (
|
|||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var systemResolverBootstrap = &Bootstrap{dialer: newMockDialer()}
|
||||
var systemResolverBootstrap = &Bootstrap{
|
||||
dialer: newMockDialer(),
|
||||
configurable: withConfig(newBootstrapConfig(&config.Config{Upstreams: defaultUpstreamsConfig})),
|
||||
}
|
||||
|
||||
var _ = Describe("Resolver", func() {
|
||||
Describe("Chains", func() {
|
||||
|
|
Loading…
Reference in New Issue