mirror of
https://github.com/folbricht/routedns.git
synced 2025-12-31 14:40:24 -06:00
462 lines
11 KiB
Go
462 lines
11 KiB
Go
package rdns
|
|
|
|
import (
|
|
"net"
|
|
"testing"
|
|
|
|
"github.com/miekg/dns"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestLuaSimplePassthrough(t *testing.T) {
|
|
opt := LuaOptions{
|
|
Script: `
|
|
function Resolve(msg, ci)
|
|
local resolver = Resolvers[1]
|
|
local answer, err = resolver:resolve(msg, ci)
|
|
if err ~= nil then
|
|
return nil, err
|
|
end
|
|
return answer, nil
|
|
end`,
|
|
}
|
|
|
|
var ci ClientInfo
|
|
resolver := new(TestResolver)
|
|
|
|
r, err := NewLua("test-lua", opt, resolver)
|
|
require.NoError(t, err)
|
|
|
|
q := new(dns.Msg)
|
|
q.SetQuestion("example.com.", dns.TypeA)
|
|
|
|
_, err = r.Resolve(q, ci)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, resolver.HitCount())
|
|
}
|
|
|
|
func TestLuaMissingResolveFunc(t *testing.T) {
|
|
opt := LuaOptions{
|
|
Script: `function test() return nil, nil end`,
|
|
}
|
|
|
|
resolver := new(TestResolver)
|
|
|
|
_, err := NewLua("test-lua", opt, resolver)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestLuaResolveError(t *testing.T) {
|
|
opt := LuaOptions{
|
|
Script: `
|
|
function Resolve(msg, ci)
|
|
return nil, Error.new("no bueno")
|
|
end`,
|
|
}
|
|
|
|
var ci ClientInfo
|
|
resolver := new(TestResolver)
|
|
|
|
r, err := NewLua("test-lua", opt, resolver)
|
|
require.NoError(t, err)
|
|
|
|
q := new(dns.Msg)
|
|
q.SetQuestion("example.com.", dns.TypeA)
|
|
|
|
_, err = r.Resolve(q, ci)
|
|
require.Error(t, err)
|
|
require.Zero(t, resolver.HitCount())
|
|
}
|
|
|
|
func TestLuaStaticAnswer(t *testing.T) {
|
|
tests := map[string]LuaOptions{
|
|
"set_questions": {
|
|
Script: `
|
|
function Resolve(msg, ci)
|
|
local question = Question.new("example.com.", TypeA)
|
|
local answer = Message.new()
|
|
answer.id = msg.id
|
|
answer.questions = { question }
|
|
answer.response = true
|
|
answer.rcode = RcodeNXDOMAIN
|
|
return answer, nil
|
|
end`,
|
|
},
|
|
"set_question": {
|
|
Script: `
|
|
function Resolve(msg, ci)
|
|
local answer = Message.new()
|
|
answer:set_question("example.com.", TypeA)
|
|
answer.id = msg.id
|
|
answer.response = true
|
|
answer.rcode = RcodeNXDOMAIN
|
|
return answer, nil
|
|
end`,
|
|
},
|
|
"set_reply": {
|
|
Script: `
|
|
function Resolve(msg, ci)
|
|
local answer = Message.new()
|
|
answer:set_reply(msg)
|
|
answer.rcode = RcodeNXDOMAIN
|
|
return answer, nil
|
|
end`,
|
|
},
|
|
}
|
|
|
|
for name, opt := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
var ci ClientInfo
|
|
resolver := new(TestResolver)
|
|
|
|
r, err := NewLua("test-lua", opt, resolver)
|
|
require.NoError(t, err)
|
|
|
|
q := new(dns.Msg)
|
|
q.SetQuestion("example.com.", dns.TypeA)
|
|
q.Id = 1234
|
|
|
|
answer, err := r.Resolve(q, ci)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, resolver.HitCount())
|
|
require.Equal(t, "example.com.", answer.Question[0].Name)
|
|
require.Equal(t, dns.TypeA, answer.Question[0].Qtype)
|
|
require.Equal(t, uint16(1234), answer.Id)
|
|
require.Equal(t, dns.RcodeNameError, answer.Rcode)
|
|
require.True(t, answer.Response)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLuaQuestionOperations(t *testing.T) {
|
|
opt := LuaOptions{
|
|
Script: `
|
|
function Resolve(msg, ci)
|
|
-- Create a new Question record and test value set/get operations
|
|
local question = Question.new("example.com.", TypeA)
|
|
if question.name ~= "example.com." or question.qtype ~= TypeA then
|
|
return nil, Error.new("unexpected name value")
|
|
end
|
|
question.name = "testing."
|
|
if question.name ~= "testing." then
|
|
return nil, Error.new("unexpected name value")
|
|
end
|
|
return nil, nil
|
|
end`,
|
|
}
|
|
|
|
var ci ClientInfo
|
|
resolver := new(TestResolver)
|
|
|
|
r, err := NewLua("test-lua", opt, resolver)
|
|
require.NoError(t, err)
|
|
|
|
q := new(dns.Msg)
|
|
q.SetQuestion("example.com.", dns.TypeMX)
|
|
|
|
_, err = r.Resolve(q, ci)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestLuaRROperations(t *testing.T) {
|
|
opt := LuaOptions{
|
|
Script: `
|
|
function Resolve(msg, ci)
|
|
-- Create a new TXT record and test value set/get operations
|
|
rr = RR.new({rtype=TypeTXT, name="example.com.", class=ClassIN, ttl=60, txt={"hello", "world"}})
|
|
if rr.txt[1] ~= "hello" then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
rr.txt = {"bla"}
|
|
if rr.txt[1] ~= "bla" then
|
|
return nil, Error.new("unexpected value in txt")
|
|
end
|
|
|
|
-- Create a new A record and test value set/get operations
|
|
rr = RR.new({rtype=TypeA, name="example.com.", class=ClassIN, ttl=60, a="1.2.3.4"})
|
|
if rr.rtype ~= TypeA or rr.name ~= "example.com." then
|
|
return nil, Error.new("unexpected name value")
|
|
end
|
|
rr.a = "1.1.1.1"
|
|
if rr.a ~= "1.1.1.1" then
|
|
return nil, Error.new("unexpected ip value")
|
|
end
|
|
return nil, nil
|
|
end`,
|
|
}
|
|
|
|
var ci ClientInfo
|
|
resolver := new(TestResolver)
|
|
|
|
r, err := NewLua("test-lua", opt, resolver)
|
|
require.NoError(t, err)
|
|
|
|
q := new(dns.Msg)
|
|
q.SetQuestion("example.com.", dns.TypeMX)
|
|
|
|
_, err = r.Resolve(q, ci)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestLuaEDNS0Operations(t *testing.T) {
|
|
opt := LuaOptions{
|
|
Script: `
|
|
function Resolve(msg, ci)
|
|
-- Create a new EDNS0 COOKIE and test value set/get operations
|
|
edns0 = EDNS0_COOKIE.new("24a5ac1a012345ff")
|
|
if edns0.cookie ~= "24a5ac1a012345ff" then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
edns0.cookie = "bla"
|
|
if edns0.cookie ~= "bla" then
|
|
return nil, Error.new("unexpected value in edns0 cookie")
|
|
end
|
|
|
|
-- Create a new EDNS0 DAU and test value set/get operations
|
|
edns0 = EDNS0_DAU.new({ 1, 2, 3 })
|
|
if edns0.algcode[1] ~= 1 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
edns0.algcode = { 0 }
|
|
if edns0.algcode[1] ~= 0 then
|
|
return nil, Error.new("unexpected value in edns0 dau")
|
|
end
|
|
|
|
-- Create a new EDNS0 DHU and test value set/get operations
|
|
edns0 = EDNS0_DHU.new({ 1, 2, 3 })
|
|
if edns0.algcode[1] ~= 1 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
edns0.algcode = { 0 }
|
|
if edns0.algcode[1] ~= 0 then
|
|
return nil, Error.new("unexpected value in edns0 dhu")
|
|
end
|
|
|
|
-- Create a new EDNS0 EDE and test value set/get operations
|
|
edns0 = EDNS0_EDE.new(15, "domain blocked")
|
|
if edns0.infocode ~= 15 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
edns0.extratext = "testing"
|
|
if edns0.extratext ~= "testing" then
|
|
return nil, Error.new("unexpected value in edns0 ede")
|
|
end
|
|
|
|
-- Create a new EDNS0 ESU and test value set/get operations
|
|
edns0 = EDNS0_ESU.new("http://example.com")
|
|
if edns0.uri ~= "http://example.com" then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
edns0.uri = "http://example.org"
|
|
if edns0.uri ~= "http://example.org" then
|
|
return nil, Error.new("unexpected value in edns0 ede")
|
|
end
|
|
|
|
-- Create a new EDNS0 EXPIRE and test value set/get operations
|
|
edns0 = EDNS0_EXPIRE.new(123)
|
|
if edns0.expire ~= 123 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
edns0.expire = 124
|
|
if edns0.expire ~= 124 then
|
|
return nil, Error.new("unexpected value in edns0 expire")
|
|
end
|
|
|
|
-- Create a new EDNS0 LLQ and test value set/get operations
|
|
edns0 = EDNS0_LLQ.new(1, 16, 0, 1234, 4321)
|
|
if edns0.version ~= 1 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
if edns0.opcode ~= 16 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
if edns0.error ~= 0 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
if edns0.id ~= 1234 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
if edns0.leaselife ~= 4321 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
edns0.error = 1
|
|
if edns0.error ~= 1 then
|
|
return nil, Error.new("unexpected value in edns0 llq")
|
|
end
|
|
|
|
-- Create a new EDNS0 LOCAL and test value set/get operations
|
|
edns0 = EDNS0_LOCAL.new(65001, "somedata")
|
|
if edns0.code ~= 65001 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
edns0.data = "otherdata"
|
|
if edns0.data ~= "otherdata" then
|
|
return nil, Error.new("unexpected value in edns0 local")
|
|
end
|
|
|
|
-- Create a new EDNS0 N3U and test value set/get operations
|
|
edns0 = EDNS0_N3U.new({ 1, 2, 3 })
|
|
if edns0.algcode[1] ~= 1 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
edns0.algcode = { 0 }
|
|
if edns0.algcode[1] ~= 0 then
|
|
return nil, Error.new("unexpected value in edns0 n3u")
|
|
end
|
|
|
|
-- Create a new EDNS0 NSID and test value set/get operations
|
|
edns0 = EDNS0_NSID.new("someid")
|
|
if edns0.nsid ~= "someid" then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
edns0.nsid = "otherid"
|
|
if edns0.nsid ~= "otherid" then
|
|
return nil, Error.new("unexpected value in edns0 nsid")
|
|
end
|
|
|
|
-- Create a new EDNS0 PADDING and test value set/get operations
|
|
edns0 = EDNS0_PADDING.new("somepadding")
|
|
if edns0.padding ~= "somepadding" then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
edns0.padding = "otherpadding"
|
|
if edns0.padding ~= "otherpadding" then
|
|
return nil, Error.new("unexpected value in edns0 padding")
|
|
end
|
|
|
|
-- Create a new EDNS0 SUBNET and test value set/get operations
|
|
edns0 = EDNS0_SUBNET.new(1, 32, 0, "192.168.0.0")
|
|
if edns0.family ~= 1 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
if edns0.sourcenetmask ~= 32 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
if edns0.sourcescope ~= 0 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
if edns0.address ~= "192.168.0.0" then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
edns0.address = "172.16.0.0"
|
|
if edns0.address ~= "172.16.0.0" then
|
|
return nil, Error.new("unexpected value in edns0 subnet")
|
|
end
|
|
|
|
-- Create a new EDNS0 TCP_KEEPALIVE and test value set/get operations
|
|
edns0 = EDNS0_TCP_KEEPALIVE.new(1)
|
|
if edns0.timeout ~= 1 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
edns0.timeout = 2
|
|
if edns0.timeout ~= 2 then
|
|
return nil, Error.new("unexpected value in edns0 tcp keepalive")
|
|
end
|
|
|
|
-- Create a new EDNS0 UL and test value set/get operations
|
|
edns0 = EDNS0_UL.new(1, 2)
|
|
if edns0.lease ~= 1 then
|
|
return nil, Error.new("unexpected value")
|
|
end
|
|
edns0.keylease = 3
|
|
if edns0.keylease ~= 3 then
|
|
return nil, Error.new("unexpected value in edns0 ul")
|
|
end
|
|
|
|
return nil, nil
|
|
end`,
|
|
}
|
|
|
|
var ci ClientInfo
|
|
resolver := new(TestResolver)
|
|
|
|
r, err := NewLua("test-lua", opt, resolver)
|
|
require.NoError(t, err)
|
|
|
|
q := new(dns.Msg)
|
|
q.SetQuestion("example.com.", dns.TypeMX)
|
|
|
|
_, err = r.Resolve(q, ci)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestLuaRREDNS0(t *testing.T) {
|
|
opt := LuaOptions{
|
|
Script: `
|
|
function Resolve(msg, ci)
|
|
-- Read the EDNS0 options
|
|
opt = msg:is_edns0()
|
|
if opt == nil then
|
|
return nil, Error.new("no edns0")
|
|
end
|
|
|
|
-- Grab the options
|
|
options = opt.option
|
|
|
|
-- The first one should be a cookie
|
|
if options[1].option ~= EDNS0COOKIE then
|
|
return nil, Error.new("unexpected subnet option value in cookie option")
|
|
end
|
|
if options[1].cookie ~= "testing" then
|
|
return nil, Error.new("unexpected value in edns0 cookie option")
|
|
end
|
|
|
|
-- The second one should be a subnet option
|
|
if options[2].option ~= EDNS0SUBNET then
|
|
return nil, Error.new("unexpected subnet option value in subnet option")
|
|
end
|
|
if options[2].family ~= 1 then
|
|
return nil, Error.new("unexpected value in edns0 subnet option")
|
|
end
|
|
|
|
-- Reply with an extended error message
|
|
local answer = Message.new()
|
|
answer:set_reply(msg)
|
|
answer.rcode = RcodeNXDOMAIN
|
|
answer:set_edns0(4096, false)
|
|
edns0 = answer:is_edns0()
|
|
edns0.option = {
|
|
EDNS0_EDE.new(15, "totally blocked"),
|
|
}
|
|
|
|
return answer, nil
|
|
end`,
|
|
}
|
|
|
|
var ci ClientInfo
|
|
resolver := new(TestResolver)
|
|
|
|
r, err := NewLua("test-lua", opt, resolver)
|
|
require.NoError(t, err)
|
|
|
|
q := new(dns.Msg)
|
|
q.SetQuestion("example.com.", dns.TypeMX)
|
|
q.SetEdns0(4096, false)
|
|
edns0 := q.IsEdns0()
|
|
|
|
edns0.Option = append(edns0.Option,
|
|
&dns.EDNS0_COOKIE{
|
|
Code: dns.EDNS0COOKIE,
|
|
Cookie: "testing",
|
|
},
|
|
&dns.EDNS0_SUBNET{
|
|
Code: dns.EDNS0SUBNET,
|
|
Family: 1,
|
|
SourceNetmask: 32,
|
|
SourceScope: 0,
|
|
Address: net.ParseIP("127.0.0.1"),
|
|
},
|
|
)
|
|
|
|
a, err := r.Resolve(q, ci)
|
|
require.NoError(t, err)
|
|
|
|
edns0 = a.IsEdns0()
|
|
require.NotNil(t, edns0)
|
|
require.Len(t, edns0.Option, 1)
|
|
require.Equal(t, uint16(dns.EDNS0EDE), edns0.Option[0].Option())
|
|
ede := edns0.Option[0].(*dns.EDNS0_EDE)
|
|
require.Equal(t, uint16(15), ede.InfoCode)
|
|
require.Equal(t, "totally blocked", ede.ExtraText)
|
|
}
|