Files
routedns/lua_test.go
folbrich f9cc8ac892 dev
2025-06-01 17:13:33 +02:00

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)
}