mirror of https://github.com/0xERR0R/blocky.git
324 lines
9.0 KiB
Go
324 lines
9.0 KiB
Go
package server
|
|
|
|
import (
|
|
"blocky/config"
|
|
"blocky/resolver"
|
|
"blocky/util"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/miekg/dns"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
var mockClientName string
|
|
|
|
// test case definition
|
|
var tests = []struct {
|
|
name string
|
|
request *dns.Msg
|
|
mockClientName string
|
|
respValidator func(*testing.T, *dns.Msg)
|
|
}{
|
|
{
|
|
// resolve query via external dns
|
|
name: "resolveWithUpstream",
|
|
request: util.NewMsgWithQuestion("google.de.", dns.TypeA),
|
|
respValidator: func(t *testing.T, resp *dns.Msg) {
|
|
assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
|
|
assert.Equal(t, "google.de.\t250\tIN\tA\t123.124.122.122", resp.Answer[0].String())
|
|
},
|
|
},
|
|
{
|
|
// custom dnd entry with exact match
|
|
name: "customDns",
|
|
request: util.NewMsgWithQuestion("custom.lan.", dns.TypeA),
|
|
respValidator: func(t *testing.T, resp *dns.Msg) {
|
|
assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
|
|
assert.Equal(t, "custom.lan.\t3600\tIN\tA\t192.168.178.55", resp.Answer[0].String())
|
|
},
|
|
},
|
|
{
|
|
// sub domain custom dns
|
|
name: "customDnsWithSubdomain",
|
|
request: util.NewMsgWithQuestion("host.lan.home.", dns.TypeA),
|
|
respValidator: func(t *testing.T, resp *dns.Msg) {
|
|
assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
|
|
assert.Equal(t, "host.lan.home.\t3600\tIN\tA\t192.168.178.56", resp.Answer[0].String())
|
|
},
|
|
},
|
|
{
|
|
// delegate to special dns upstream
|
|
name: "conditional",
|
|
request: util.NewMsgWithQuestion("host.fritz.box.", dns.TypeA),
|
|
respValidator: func(t *testing.T, resp *dns.Msg) {
|
|
assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
|
|
assert.Equal(t, "host.fritz.box.\t3600\tIN\tA\t192.168.178.2", resp.Answer[0].String())
|
|
},
|
|
},
|
|
{
|
|
// blocking default group
|
|
name: "blockDefault",
|
|
request: util.NewMsgWithQuestion("doubleclick.net.", dns.TypeA),
|
|
respValidator: func(t *testing.T, resp *dns.Msg) {
|
|
assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
|
|
assert.Equal(t, "doubleclick.net.\t21600\tIN\tA\t0.0.0.0", resp.Answer[0].String())
|
|
},
|
|
},
|
|
{
|
|
// blocking default group with sub domain
|
|
name: "blockDefaultWithSubdomain",
|
|
request: util.NewMsgWithQuestion("www.bild.de.", dns.TypeA),
|
|
respValidator: func(t *testing.T, resp *dns.Msg) {
|
|
assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
|
|
assert.Equal(t, "www.bild.de.\t21600\tIN\tA\t0.0.0.0", resp.Answer[0].String())
|
|
},
|
|
},
|
|
{
|
|
// no blocking default group with sub domain
|
|
name: "noBlockDefaultWithSubdomain",
|
|
request: util.NewMsgWithQuestion("bild.de.", dns.TypeA),
|
|
respValidator: func(t *testing.T, resp *dns.Msg) {
|
|
assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
|
|
assert.Equal(t, "bild.de.\t250\tIN\tA\t123.124.122.122", resp.Answer[0].String())
|
|
},
|
|
},
|
|
{
|
|
// white and block default group
|
|
name: "whiteBlackDefault",
|
|
request: util.NewMsgWithQuestion("heise.de.", dns.TypeA),
|
|
respValidator: func(t *testing.T, resp *dns.Msg) {
|
|
assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
|
|
assert.Equal(t, "heise.de.\t250\tIN\tA\t123.124.122.122", resp.Answer[0].String())
|
|
},
|
|
},
|
|
{
|
|
// no block client whitelist only
|
|
name: "noBlockWhitelistOnly",
|
|
mockClientName: "clWhitelistOnly",
|
|
request: util.NewMsgWithQuestion("heise.de.", dns.TypeA),
|
|
respValidator: func(t *testing.T, resp *dns.Msg) {
|
|
assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
|
|
assert.Equal(t, "123.124.122.122", resp.Answer[0].(*dns.A).A.String())
|
|
},
|
|
},
|
|
{
|
|
// block client whitelist only
|
|
name: "blockWhitelistOnly",
|
|
mockClientName: "clWhitelistOnly",
|
|
request: util.NewMsgWithQuestion("google.de.", dns.TypeA),
|
|
respValidator: func(t *testing.T, resp *dns.Msg) {
|
|
assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
|
|
assert.Equal(t, "0.0.0.0", resp.Answer[0].(*dns.A).A.String())
|
|
},
|
|
},
|
|
{
|
|
// block client with 2 groups
|
|
name: "block2groups1",
|
|
mockClientName: "clAdsAndYoutube",
|
|
request: util.NewMsgWithQuestion("www.bild.de.", dns.TypeA),
|
|
respValidator: func(t *testing.T, resp *dns.Msg) {
|
|
assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
|
|
assert.Equal(t, "0.0.0.0", resp.Answer[0].(*dns.A).A.String())
|
|
},
|
|
},
|
|
{
|
|
// block client with 2 groups
|
|
name: "block2groups2",
|
|
mockClientName: "clAdsAndYoutube",
|
|
request: util.NewMsgWithQuestion("youtube.com.", dns.TypeA),
|
|
respValidator: func(t *testing.T, resp *dns.Msg) {
|
|
assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
|
|
assert.Equal(t, "0.0.0.0", resp.Answer[0].(*dns.A).A.String())
|
|
},
|
|
},
|
|
{
|
|
// lient with 1 group: no block if domain in other group
|
|
name: "noBlockBlacklistOtherGroup",
|
|
mockClientName: "clYoutubeOnly",
|
|
request: util.NewMsgWithQuestion("www.bild.de.", dns.TypeA),
|
|
respValidator: func(t *testing.T, resp *dns.Msg) {
|
|
assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
|
|
assert.Equal(t, "123.124.122.122", resp.Answer[0].(*dns.A).A.String())
|
|
},
|
|
},
|
|
{
|
|
// block client with 1 group
|
|
name: "blockBlacklist",
|
|
mockClientName: "clYoutubeOnly",
|
|
request: util.NewMsgWithQuestion("youtube.com.", dns.TypeA),
|
|
respValidator: func(t *testing.T, resp *dns.Msg) {
|
|
assert.Equal(t, dns.RcodeSuccess, resp.Rcode)
|
|
assert.Equal(t, "0.0.0.0", resp.Answer[0].(*dns.A).A.String())
|
|
},
|
|
},
|
|
}
|
|
|
|
//nolint:funlen
|
|
func TestDnsRequest(t *testing.T) {
|
|
upstreamGoogle := resolver.TestUDPUpstream(func(request *dns.Msg) *dns.Msg {
|
|
response, err := util.NewMsgWithAnswer(fmt.Sprintf("%s %d %s %s %s",
|
|
util.ExtractDomain(request.Question[0]), 123, "IN", "A", "123.124.122.122"))
|
|
|
|
assert.NoError(t, err)
|
|
return response
|
|
})
|
|
upstreamFritzbox := resolver.TestUDPUpstream(func(request *dns.Msg) *dns.Msg {
|
|
response, err := util.NewMsgWithAnswer(fmt.Sprintf("%s %d %s %s %s",
|
|
util.ExtractDomain(request.Question[0]), 3600, "IN", "A", "192.168.178.2"))
|
|
|
|
assert.NoError(t, err)
|
|
return response
|
|
})
|
|
|
|
upstreamClient := resolver.TestUDPUpstream(func(request *dns.Msg) *dns.Msg {
|
|
response, err := util.NewMsgWithAnswer(fmt.Sprintf("%s %d %s %s %s",
|
|
util.ExtractDomain(request.Question[0]), 3600, "IN", "PTR", mockClientName))
|
|
|
|
assert.NoError(t, err)
|
|
return response
|
|
})
|
|
|
|
// create server
|
|
server, err := NewServer(&config.Config{
|
|
CustomDNS: config.CustomDNSConfig{
|
|
Mapping: map[string]net.IP{
|
|
"custom.lan": net.ParseIP("192.168.178.55"),
|
|
"lan.home": net.ParseIP("192.168.178.56"),
|
|
},
|
|
},
|
|
Conditional: config.ConditionalUpstreamConfig{
|
|
Mapping: map[string]config.Upstream{"fritz.box": upstreamFritzbox},
|
|
},
|
|
Blocking: config.BlockingConfig{
|
|
BlackLists: map[string][]string{
|
|
"ads": {
|
|
"../testdata/doubleclick.net.txt",
|
|
"../testdata/www.bild.de.txt",
|
|
"../testdata/heise.de.txt"},
|
|
"youtube": {"../testdata/youtube.com.txt"}},
|
|
WhiteLists: map[string][]string{
|
|
"ads": {"../testdata/heise.de.txt"},
|
|
"whitelist": {"../testdata/heise.de.txt"},
|
|
},
|
|
ClientGroupsBlock: map[string][]string{
|
|
"default": {"ads"},
|
|
"clWhitelistOnly": {"whitelist"},
|
|
"clAdsAndYoutube": {"ads", "youtube"},
|
|
"clYoutubeOnly": {"youtube"},
|
|
},
|
|
},
|
|
Upstream: config.UpstreamConfig{
|
|
ExternalResolvers: []config.Upstream{upstreamGoogle},
|
|
},
|
|
ClientLookup: config.ClientLookupConfig{
|
|
Upstream: upstreamClient,
|
|
},
|
|
|
|
Port: 55555,
|
|
})
|
|
|
|
assert.NoError(t, err)
|
|
|
|
// start server
|
|
go func() {
|
|
server.Start()
|
|
}()
|
|
|
|
defer server.Stop()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
for _, tt := range tests {
|
|
tst := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
res := server.queryResolver
|
|
for res != nil {
|
|
if t, ok := res.(*resolver.ClientNamesResolver); ok {
|
|
t.FlushCache()
|
|
break
|
|
}
|
|
if c, ok := res.(resolver.ChainedResolver); ok {
|
|
res = c.GetNext()
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
mockClientName = tst.mockClientName
|
|
response := requestServer(tst.request)
|
|
|
|
tst.respValidator(t, response)
|
|
})
|
|
}
|
|
}
|
|
|
|
// 26106 43273 ns/op 12518 B/op 137 allocs/op
|
|
func BenchmarkServerExternalResolver(b *testing.B) {
|
|
msg, _ := util.NewMsgWithAnswer(fmt.Sprintf("example.com IN A 123.124.122.122"))
|
|
upstreamExternal := resolver.TestUDPUpstreamWithResponse(msg)
|
|
|
|
// create server
|
|
server, err := NewServer(&config.Config{
|
|
Upstream: config.UpstreamConfig{
|
|
ExternalResolvers: []config.Upstream{upstreamExternal},
|
|
},
|
|
Port: 55555,
|
|
})
|
|
|
|
assert.NoError(b, err)
|
|
|
|
// start server
|
|
go func() {
|
|
server.Start()
|
|
}()
|
|
|
|
defer server.Stop()
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_ = requestServer(util.NewMsgWithQuestion("google.de.", dns.TypeA))
|
|
}
|
|
})
|
|
}
|
|
|
|
func requestServer(request *dns.Msg) *dns.Msg {
|
|
conn, err := net.Dial("udp", ":55555")
|
|
if err != nil {
|
|
log.Fatal("could not connect to server: ", err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
msg, err := request.Pack()
|
|
if err != nil {
|
|
log.Fatal("can't pack request: ", err)
|
|
}
|
|
|
|
_, err = conn.Write(msg)
|
|
if err != nil {
|
|
log.Fatal("can't send request to server: ", err)
|
|
}
|
|
|
|
out := make([]byte, 1024)
|
|
|
|
if _, err := conn.Read(out); err == nil {
|
|
response := new(dns.Msg)
|
|
err := response.Unpack(out)
|
|
|
|
if err != nil {
|
|
log.Fatal("can't unpack response: ", err)
|
|
}
|
|
|
|
return response
|
|
}
|
|
|
|
log.Fatal("could not read from connection", err)
|
|
|
|
return nil
|
|
}
|