AdGuardDNS/internal/dnssvc/integration_test.go

445 lines
12 KiB
Go
Raw Normal View History

2022-08-26 14:18:35 +03:00
package dnssvc_test
import (
"context"
"net"
"net/http"
"net/netip"
2024-10-14 17:44:24 +03:00
"path"
2022-08-26 14:18:35 +03:00
"testing"
"time"
2024-01-04 19:22:32 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/access"
2022-08-26 14:18:35 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
2024-11-08 16:26:22 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/agdcache"
2024-06-07 14:27:46 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/agdpasswd"
2022-08-26 14:18:35 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver"
2023-06-11 12:58:40 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/dnsserver/dnsservertest"
2022-08-26 14:18:35 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/dnssvc"
2023-09-06 08:22:07 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/dnssvc/internal/dnssvctest"
2022-08-26 14:18:35 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/filter"
2024-11-08 16:26:22 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/filter/hashprefix"
2024-01-04 19:22:32 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/geoip"
2022-08-26 14:18:35 +03:00
"github.com/AdguardTeam/AdGuardDNS/internal/querylog"
2024-10-14 17:44:24 +03:00
"github.com/AdguardTeam/golibs/logutil/slogutil"
2022-11-07 10:21:24 +03:00
"github.com/AdguardTeam/golibs/netutil"
2022-08-26 14:18:35 +03:00
"github.com/AdguardTeam/golibs/testutil"
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
2023-06-11 12:58:40 +03:00
// newTestService creates a new [dnssvc.Service] for tests. The service built
// of stubs, that use the following data:
//
2024-01-04 19:22:32 +03:00
// - A filtering group containing a filter with [dnssvctest.FilterListID1] and
// enabled rule lists.
2023-09-06 08:22:07 +03:00
// - A device with [dnssvctest.DeviceID] and enabled filtering.
// - A profile with [dnssvctest.ProfileID] with enabled filtering and query
2023-06-11 12:58:40 +03:00
// logging, containing the device.
// - GeoIP database always returning [agd.CountryAD], [agd.ContinentEU], and
// ASN of 42.
// - A server with [testSrvName] under group with [testSrvGrpName], matching
2024-10-14 17:44:24 +03:00
// the DeviceID with [dnssvctest.DomainForDevices].
2023-06-11 12:58:40 +03:00
//
// Each stub also uses the corresponding channels to send the data it receives
2023-09-06 08:22:07 +03:00
// from the service. The channels must not be nil. Each sending to a channel
// wrapped with [testutil.RequireSend] using [dnssvctest.Timeout].
2023-06-11 12:58:40 +03:00
//
2024-11-08 16:26:22 +03:00
// It also uses the [dnsservertest.NewDefaultHandler] to create the DNS handler.
2023-06-11 12:58:40 +03:00
func newTestService(
t testing.TB,
flt filter.Interface,
errCollCh chan<- error,
profileDBCh chan<- agd.DeviceID,
querylogCh chan<- *querylog.Entry,
geoIPCh chan<- string,
dnsDBCh chan<- *agd.RequestInfo,
ruleStatCh chan<- agd.FilterRuleText,
) (svc *dnssvc.Service, srvAddr netip.AddrPort) {
t.Helper()
pt := testutil.PanicT{}
2022-08-26 14:18:35 +03:00
dev := &agd.Device{
2024-06-07 14:27:46 +03:00
Auth: &agd.AuthSettings{
Enabled: false,
PasswordHash: agdpasswd.AllowAuthenticator{},
},
2023-09-06 08:22:07 +03:00
ID: dnssvctest.DeviceID,
2022-08-26 14:18:35 +03:00
FilteringEnabled: true,
}
prof := &agd.Profile{
2024-01-04 19:22:32 +03:00
Access: access.EmptyProfile{},
2024-10-14 17:44:24 +03:00
BlockingMode: &dnsmsg.BlockingModeNullIP{},
2023-09-06 08:22:07 +03:00
ID: dnssvctest.ProfileID,
DeviceIDs: []agd.DeviceID{dnssvctest.DeviceID},
2024-01-04 19:22:32 +03:00
RuleListIDs: []agd.FilterListID{dnssvctest.FilterListID1},
2023-06-11 12:58:40 +03:00
FilteredResponseTTL: agdtest.FilteredResponseTTL,
2022-08-26 14:18:35 +03:00
FilteringEnabled: true,
QueryLogEnabled: true,
}
2024-11-08 16:26:22 +03:00
profDB := agdtest.NewProfileDB()
profDB.OnProfileByDeviceID = func(
_ context.Context,
id agd.DeviceID,
) (p *agd.Profile, d *agd.Device, err error) {
testutil.RequireSend(pt, profileDBCh, id, dnssvctest.Timeout)
2024-07-10 19:49:07 +03:00
2024-11-08 16:26:22 +03:00
return prof, dev, nil
2022-08-26 14:18:35 +03:00
}
2023-09-06 08:22:07 +03:00
accessManager := &agdtest.AccessManager{
OnIsBlockedHost: func(host string, qt uint16) (blocked bool) {
return false
},
2024-03-11 12:21:07 +03:00
OnIsBlockedIP: func(ip netip.Addr) (blocked bool) {
return false
2023-09-06 08:22:07 +03:00
},
}
2022-08-26 14:18:35 +03:00
// Make sure that any panics and errors within handlers are caught and
// that they fail the test by panicking.
errColl := &agdtest.ErrorCollector{
OnCollect: func(_ context.Context, err error) {
2023-09-06 08:22:07 +03:00
testutil.RequireSend(pt, errCollCh, err, dnssvctest.Timeout)
2022-08-26 14:18:35 +03:00
},
}
2024-01-04 19:22:32 +03:00
loc := &geoip.Location{
Country: geoip.CountryAD,
Continent: geoip.ContinentEU,
2023-06-11 12:58:40 +03:00
ASN: 42,
}
2022-08-26 14:18:35 +03:00
2024-11-08 16:26:22 +03:00
geoIP := agdtest.NewGeoIP()
geoIP.OnData = func(host string, _ netip.Addr) (l *geoip.Location, err error) {
testutil.RequireSend(pt, geoIPCh, host, dnssvctest.Timeout)
return loc, nil
2022-08-26 14:18:35 +03:00
}
fltStrg := &agdtest.FilterStorage{
OnFilterFromContext: func(_ context.Context, _ *agd.RequestInfo) (f filter.Interface) {
return flt
},
OnHasListID: func(_ agd.FilterListID) (ok bool) { panic("not implemented") },
}
var ql querylog.Interface = &agdtest.QueryLog{
OnWrite: func(_ context.Context, e *querylog.Entry) (err error) {
2023-09-06 08:22:07 +03:00
testutil.RequireSend(pt, querylogCh, e, dnssvctest.Timeout)
2022-08-26 14:18:35 +03:00
return nil
},
}
2023-06-11 12:58:40 +03:00
srvAddr = netip.MustParseAddrPort("94.149.14.14:853")
2024-01-04 19:22:32 +03:00
srv := dnssvctest.NewServer(dnssvctest.ServerName, agd.ProtoDoT, &agd.ServerBindData{
AddrPort: srvAddr,
})
2022-08-26 14:18:35 +03:00
tl := newTestListener()
tl.onStart = func(_ context.Context) (err error) { return nil }
tl.onShutdown = func(_ context.Context) (err error) { return nil }
dnsCk := &agdtest.DNSCheck{
OnCheck: func(
_ context.Context,
_ *dns.Msg,
_ *agd.RequestInfo,
) (resp *dns.Msg, err error) {
return nil, nil
},
}
dnsDB := &agdtest.DNSDB{
2023-06-11 12:58:40 +03:00
OnRecord: func(_ context.Context, _ *dns.Msg, ri *agd.RequestInfo) {
2023-09-06 08:22:07 +03:00
testutil.RequireSend(pt, dnsDBCh, ri, dnssvctest.Timeout)
2022-08-26 14:18:35 +03:00
},
}
ruleStat := &agdtest.RuleStat{
2023-06-11 12:58:40 +03:00
OnCollect: func(_ context.Context, _ agd.FilterListID, text agd.FilterRuleText) {
2023-09-06 08:22:07 +03:00
testutil.RequireSend(pt, ruleStatCh, text, dnssvctest.Timeout)
2022-08-26 14:18:35 +03:00
},
}
2024-11-08 16:26:22 +03:00
rl := agdtest.NewRateLimit()
rl.OnIsRateLimited = func(
_ context.Context,
_ *dns.Msg,
_ netip.Addr,
) (shouldDrop, isAllowlisted bool, err error) {
return true, false, nil
2022-08-26 14:18:35 +03:00
}
2024-11-08 16:26:22 +03:00
srvGrps := []*agd.ServerGroup{{
DDR: &agd.DDR{
Enabled: true,
},
TLS: &agd.TLS{
DeviceDomains: []string{dnssvctest.DomainForDevices},
},
Name: dnssvctest.ServerGroupName,
FilteringGroup: dnssvctest.FilteringGroupID,
Servers: []*agd.Server{srv},
ProfilesEnabled: true,
}}
hdlrConf := &dnssvc.HandlersConfig{
BaseLogger: slogutil.NewDiscardLogger(),
Cache: &dnssvc.CacheConfig{
Type: dnssvc.CacheTypeNone,
},
StructuredErrors: agdtest.NewSDEConfig(true),
Cloner: agdtest.NewCloner(),
HumanIDParser: agd.NewHumanIDParser(),
Messages: agdtest.NewConstructor(t),
AccessManager: accessManager,
2022-08-26 14:18:35 +03:00
BillStat: &agdtest.BillStatRecorder{
OnRecord: func(
_ context.Context,
_ agd.DeviceID,
2024-01-04 19:22:32 +03:00
_ geoip.Country,
_ geoip.ASN,
2022-08-26 14:18:35 +03:00
_ time.Time,
_ agd.Protocol,
) {
},
},
2024-11-08 16:26:22 +03:00
CacheManager: agdcache.EmptyManager{},
2024-10-14 17:44:24 +03:00
DNSCheck: dnsCk,
DNSDB: dnsDB,
ErrColl: errColl,
FilterStorage: fltStrg,
GeoIP: geoIP,
2024-11-08 16:26:22 +03:00
Handler: dnsservertest.NewDefaultHandler(),
HashMatcher: hashprefix.NewMatcher(nil),
ProfileDB: profDB,
PrometheusRegisterer: agdtest.NewTestPrometheusRegisterer(),
2024-10-14 17:44:24 +03:00
QueryLog: ql,
RateLimit: rl,
2024-11-08 16:26:22 +03:00
RuleStat: ruleStat,
2024-10-14 17:44:24 +03:00
MetricsNamespace: path.Base(t.Name()),
2022-08-26 14:18:35 +03:00
FilteringGroups: map[agd.FilteringGroupID]*agd.FilteringGroup{
2024-11-08 16:26:22 +03:00
dnssvctest.FilteringGroupID: {
ID: dnssvctest.FilteringGroupID,
2024-01-04 19:22:32 +03:00
RuleListIDs: []agd.FilterListID{dnssvctest.FilterListID1},
2022-08-26 14:18:35 +03:00
RuleListsEnabled: true,
},
},
2024-11-08 16:26:22 +03:00
ServerGroups: srvGrps,
EDEEnabled: true,
}
ctx := context.Background()
handlers, err := dnssvc.NewHandlers(ctx, hdlrConf)
require.NoError(t, err)
c := &dnssvc.Config{
Handlers: handlers,
NewListener: newTestListenerFunc(tl),
Cloner: agdtest.NewCloner(),
ErrColl: errColl,
NonDNS: http.NotFoundHandler(),
MetricsNamespace: path.Base(t.Name()),
ServerGroups: srvGrps,
HandleTimeout: dnssvctest.Timeout,
2022-08-26 14:18:35 +03:00
}
2024-11-08 16:26:22 +03:00
svc, err = dnssvc.New(c)
2022-08-26 14:18:35 +03:00
require.NoError(t, err)
require.NotNil(t, svc)
2023-06-11 12:58:40 +03:00
2024-06-07 14:27:46 +03:00
err = svc.Start(testutil.ContextWithTimeout(t, dnssvctest.Timeout))
2023-06-11 12:58:40 +03:00
require.NoError(t, err)
2022-08-26 14:18:35 +03:00
testutil.CleanupAndRequireSuccess(t, func() (err error) {
2024-06-07 14:27:46 +03:00
return svc.Shutdown(testutil.ContextWithTimeout(t, dnssvctest.Timeout))
2022-08-26 14:18:35 +03:00
})
2023-06-11 12:58:40 +03:00
return svc, srvAddr
}
2024-11-08 16:26:22 +03:00
// TODO(a.garipov): Refactor to test handlers separately from the service.
2023-06-11 12:58:40 +03:00
func TestService_Wrap(t *testing.T) {
profileDBCh := make(chan agd.DeviceID, 1)
querylogCh := make(chan *querylog.Entry, 1)
geoIPCh := make(chan string, 2)
dnsDBCh := make(chan *agd.RequestInfo, 1)
ruleStatCh := make(chan agd.FilterRuleText, 1)
errCollCh := make(chan error, 1)
go func() {
for err := range errCollCh {
require.NoError(t, err)
}
}()
2022-08-26 14:18:35 +03:00
reqType := dns.TypeA
2024-01-04 19:22:32 +03:00
req := dnsservertest.CreateMessage(dnssvctest.DomainFQDN, reqType)
2023-06-11 12:58:40 +03:00
clientAddr := &net.TCPAddr{IP: net.IP{1, 2, 3, 4}, Port: 12345}
2022-08-26 14:18:35 +03:00
ctx := context.Background()
2024-01-04 19:22:32 +03:00
ctx = dnsserver.ContextWithServerInfo(ctx, &dnsserver.ServerInfo{
2022-08-26 14:18:35 +03:00
Proto: agd.ProtoDoT,
})
2023-06-11 12:58:40 +03:00
t.Run("simple_success", func(t *testing.T) {
noMatch := func(
_ context.Context,
m *dns.Msg,
_ *agd.RequestInfo,
) (r filter.Result, err error) {
pt := testutil.PanicT{}
require.NotEmpty(pt, m.Question)
2024-01-04 19:22:32 +03:00
require.Equal(pt, dnssvctest.DomainFQDN, m.Question[0].Name)
2023-06-11 12:58:40 +03:00
return nil, nil
}
flt := &agdtest.Filter{
OnFilterRequest: noMatch,
OnFilterResponse: noMatch,
}
svc, srvAddr := newTestService(
t,
flt,
errCollCh,
profileDBCh,
querylogCh,
geoIPCh,
dnsDBCh,
ruleStatCh,
)
rw := dnsserver.NewNonWriterResponseWriter(
net.TCPAddrFromAddrPort(srvAddr),
clientAddr,
)
2022-08-26 14:18:35 +03:00
2024-01-04 19:22:32 +03:00
ctx = dnsserver.ContextWithRequestInfo(ctx, &dnsserver.RequestInfo{
StartTime: time.Now(),
2024-06-07 14:27:46 +03:00
TLSServerName: dnssvctest.DeviceIDSrvName,
2024-01-04 19:22:32 +03:00
})
2023-06-11 12:58:40 +03:00
2024-11-08 16:26:22 +03:00
err := svc.Handle(ctx, dnssvctest.ServerGroupName, dnssvctest.ServerName, rw, req)
2023-06-11 12:58:40 +03:00
require.NoError(t, err)
resp := rw.Msg()
dnsservertest.RequireResponse(t, req, resp, 1, dns.RcodeSuccess, false)
2023-09-06 08:22:07 +03:00
assert.Equal(t, dnssvctest.DeviceID, <-profileDBCh)
2023-06-11 12:58:40 +03:00
logEntry := <-querylogCh
2024-01-04 19:22:32 +03:00
assert.Equal(t, dnssvctest.DomainFQDN, logEntry.DomainFQDN)
2023-06-11 12:58:40 +03:00
assert.Equal(t, reqType, logEntry.RequestType)
assert.Equal(t, "", <-geoIPCh)
2024-01-04 19:22:32 +03:00
assert.Equal(t, dnssvctest.Domain, <-geoIPCh)
2023-06-11 12:58:40 +03:00
dnsDBReqInfo := <-dnsDBCh
assert.NotNil(t, dnsDBReqInfo)
assert.Equal(t, agd.FilterRuleText(""), <-ruleStatCh)
})
t.Run("request_cname", func(t *testing.T) {
const (
cname = "cname.example.org"
2024-01-04 19:22:32 +03:00
cnameRule agd.FilterRuleText = "||" + dnssvctest.Domain + "^$dnsrewrite=" + cname
2023-06-11 12:58:40 +03:00
)
cnameFQDN := dns.Fqdn(cname)
flt := &agdtest.Filter{
OnFilterRequest: func(
_ context.Context,
m *dns.Msg,
_ *agd.RequestInfo,
) (r filter.Result, err error) {
// Pretend a CNAME rewrite matched the request.
mod := dnsmsg.Clone(m)
mod.Question[0].Name = cnameFQDN
2024-06-07 14:27:46 +03:00
return &filter.ResultModifiedRequest{
2023-06-11 12:58:40 +03:00
Msg: mod,
2024-01-04 19:22:32 +03:00
List: dnssvctest.FilterListID1,
2023-06-11 12:58:40 +03:00
Rule: cnameRule,
}, nil
},
OnFilterResponse: func(
_ context.Context,
_ *dns.Msg,
_ *agd.RequestInfo,
) (filter.Result, error) {
panic("not implemented")
},
}
svc, srvAddr := newTestService(
t,
flt,
errCollCh,
profileDBCh,
querylogCh,
geoIPCh,
dnsDBCh,
ruleStatCh,
)
rw := dnsserver.NewNonWriterResponseWriter(
net.TCPAddrFromAddrPort(srvAddr),
clientAddr,
)
2024-01-04 19:22:32 +03:00
ctx = dnsserver.ContextWithRequestInfo(ctx, &dnsserver.RequestInfo{
StartTime: time.Now(),
2024-06-07 14:27:46 +03:00
TLSServerName: dnssvctest.DeviceIDSrvName,
2024-01-04 19:22:32 +03:00
})
2023-06-11 12:58:40 +03:00
2024-11-08 16:26:22 +03:00
err := svc.Handle(ctx, dnssvctest.ServerGroupName, dnssvctest.ServerName, rw, req)
2023-06-11 12:58:40 +03:00
require.NoError(t, err)
resp := rw.Msg()
require.NotNil(t, resp)
require.Len(t, resp.Answer, 2)
assert.Equal(t, []dns.RR{&dns.CNAME{
Hdr: dns.RR_Header{
2024-01-04 19:22:32 +03:00
Name: dnssvctest.DomainFQDN,
2023-06-11 12:58:40 +03:00
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
Ttl: uint32(agdtest.FilteredResponseTTL.Seconds()),
},
Target: cnameFQDN,
}, &dns.A{
Hdr: dns.RR_Header{
Name: cnameFQDN,
Rrtype: dns.TypeA,
Class: dns.ClassINET,
Ttl: uint32(dnsservertest.AnswerTTL.Seconds()),
},
A: netutil.IPv4Localhost().AsSlice(),
}}, resp.Answer)
2023-09-06 08:22:07 +03:00
assert.Equal(t, dnssvctest.DeviceID, <-profileDBCh)
2023-06-11 12:58:40 +03:00
logEntry := <-querylogCh
2024-01-04 19:22:32 +03:00
assert.Equal(t, dnssvctest.DomainFQDN, logEntry.DomainFQDN)
2023-06-11 12:58:40 +03:00
assert.Equal(t, reqType, logEntry.RequestType)
assert.Equal(t, "", <-geoIPCh)
assert.Equal(t, cname, <-geoIPCh)
dnsDBReqInfo := <-dnsDBCh
assert.Equal(t, cname, dnsDBReqInfo.Host)
assert.Equal(t, cnameRule, <-ruleStatCh)
})
2022-08-26 14:18:35 +03:00
}