coredns/plugin/rewrite/cname_target_test.go
Amila Senadheera ff40400065
rewrite: fix multi request concurrency issue in cname rewrite (#6407)
* fix concurrent issue with cname rewrite plugin

Signed-off-by: amila <amila.15@cse.mrt.ac.lk>

* add nil check before deref, add AAAA type test case

Signed-off-by: amila <amila.15@cse.mrt.ac.lk>

---------

Signed-off-by: amila <amila.15@cse.mrt.ac.lk>
2023-12-09 07:53:52 -05:00

180 lines
5.7 KiB
Go

package rewrite
import (
"context"
"reflect"
"testing"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/pkg/dnstest"
"github.com/coredns/coredns/plugin/test"
"github.com/coredns/coredns/request"
"github.com/miekg/dns"
)
type MockedUpstream struct{}
func (u *MockedUpstream) Lookup(ctx context.Context, state request.Request, name string, typ uint16) (*dns.Msg, error) {
m := new(dns.Msg)
m.SetReply(state.Req)
m.Authoritative = true
switch state.Req.Question[0].Name {
case "xyz.example.com.":
switch state.Req.Question[0].Qtype {
case dns.TypeA:
m.Answer = []dns.RR{
test.A("xyz.example.com. 3600 IN A 3.4.5.6"),
}
case dns.TypeAAAA:
m.Answer = []dns.RR{
test.AAAA("xyz.example.com. 3600 IN AAAA 3a01:7e00::f03c:91ff:fe79:234c"),
}
}
return m, nil
case "bard.google.com.cdn.cloudflare.net.":
m.Answer = []dns.RR{
test.A("bard.google.com.cdn.cloudflare.net. 1800 IN A 9.7.2.1"),
}
return m, nil
case "www.hosting.xyz.":
m.Answer = []dns.RR{
test.A("www.hosting.xyz. 500 IN A 20.30.40.50"),
}
return m, nil
case "abcd.zzzz.www.pqrst.":
m.Answer = []dns.RR{
test.A("abcd.zzzz.www.pqrst. 120 IN A 101.20.5.1"),
test.A("abcd.zzzz.www.pqrst. 120 IN A 101.20.5.2"),
}
return m, nil
case "orders.webapp.eu.org.":
m.Answer = []dns.RR{
test.A("orders.webapp.eu.org. 120 IN A 20.0.0.9"),
}
return m, nil
}
return &dns.Msg{}, nil
}
func TestCNameTargetRewrite(t *testing.T) {
rules := []Rule{}
ruleset := []struct {
args []string
expectedType reflect.Type
}{
{[]string{"continue", "cname", "exact", "def.example.com.", "xyz.example.com."}, reflect.TypeOf(&cnameTargetRule{})},
{[]string{"continue", "cname", "prefix", "chat.openai.com", "bard.google.com"}, reflect.TypeOf(&cnameTargetRule{})},
{[]string{"continue", "cname", "suffix", "uvw.", "xyz."}, reflect.TypeOf(&cnameTargetRule{})},
{[]string{"continue", "cname", "substring", "efgh", "zzzz.www"}, reflect.TypeOf(&cnameTargetRule{})},
{[]string{"continue", "cname", "regex", `(.*)\.web\.(.*)\.site\.`, `{1}.webapp.{2}.org.`}, reflect.TypeOf(&cnameTargetRule{})},
}
for i, r := range ruleset {
rule, err := newRule(r.args...)
if err != nil {
t.Fatalf("Rule %d: FAIL, %s: %s", i, r.args, err)
}
if reflect.TypeOf(rule) != r.expectedType {
t.Fatalf("Rule %d: FAIL, %s: rule type mismatch, expected %q, but got %q", i, r.args, r.expectedType, rule)
}
cnameTargetRule := rule.(*cnameTargetRule)
cnameTargetRule.Upstream = &MockedUpstream{}
rules = append(rules, rule)
}
doTestCNameTargetTests(rules, t)
}
func doTestCNameTargetTests(rules []Rule, t *testing.T) {
tests := []struct {
from string
fromType uint16
answer []dns.RR
expectedAnswer []dns.RR
}{
{"abc.example.com", dns.TypeA,
[]dns.RR{
test.CNAME("abc.example.com. 5 IN CNAME def.example.com."),
test.A("def.example.com. 5 IN A 1.2.3.4"),
},
[]dns.RR{
test.CNAME("abc.example.com. 5 IN CNAME xyz.example.com."),
test.A("xyz.example.com. 3600 IN A 3.4.5.6"),
},
},
{"abc.example.com", dns.TypeAAAA,
[]dns.RR{
test.CNAME("abc.example.com. 5 IN CNAME def.example.com."),
test.AAAA("def.example.com. 5 IN AAAA 2a01:7e00::f03c:91ff:fe79:234c"),
},
[]dns.RR{
test.CNAME("abc.example.com. 5 IN CNAME xyz.example.com."),
test.AAAA("xyz.example.com. 3600 IN AAAA 3a01:7e00::f03c:91ff:fe79:234c"),
},
},
{"chat.openai.com", dns.TypeA,
[]dns.RR{
test.CNAME("chat.openai.com. 20 IN CNAME chat.openai.com.cdn.cloudflare.net."),
test.A("chat.openai.com.cdn.cloudflare.net. 30 IN A 23.2.1.2"),
test.A("chat.openai.com.cdn.cloudflare.net. 30 IN A 24.6.0.8"),
},
[]dns.RR{
test.CNAME("chat.openai.com. 20 IN CNAME bard.google.com.cdn.cloudflare.net."),
test.A("bard.google.com.cdn.cloudflare.net. 1800 IN A 9.7.2.1"),
},
},
{"coredns.io", dns.TypeA,
[]dns.RR{
test.CNAME("coredns.io. 100 IN CNAME www.hosting.uvw."),
test.A("www.hosting.uvw. 200 IN A 7.2.3.4"),
},
[]dns.RR{
test.CNAME("coredns.io. 100 IN CNAME www.hosting.xyz."),
test.A("www.hosting.xyz. 500 IN A 20.30.40.50"),
},
},
{"core.dns.rocks", dns.TypeA,
[]dns.RR{
test.CNAME("core.dns.rocks. 200 IN CNAME abcd.efgh.pqrst."),
test.A("abcd.efgh.pqrst. 100 IN A 200.30.45.67"),
},
[]dns.RR{
test.CNAME("core.dns.rocks. 200 IN CNAME abcd.zzzz.www.pqrst."),
test.A("abcd.zzzz.www.pqrst. 120 IN A 101.20.5.1"),
test.A("abcd.zzzz.www.pqrst. 120 IN A 101.20.5.2"),
},
},
{"order.service.eu", dns.TypeA,
[]dns.RR{
test.CNAME("order.service.eu. 200 IN CNAME orders.web.eu.site."),
test.A("orders.web.eu.site. 50 IN A 10.10.15.1"),
},
[]dns.RR{
test.CNAME("order.service.eu. 200 IN CNAME orders.webapp.eu.org."),
test.A("orders.webapp.eu.org. 120 IN A 20.0.0.9"),
},
},
}
ctx := context.TODO()
for i, tc := range tests {
m := new(dns.Msg)
m.SetQuestion(tc.from, tc.fromType)
m.Question[0].Qclass = dns.ClassINET
m.Answer = tc.answer
rw := Rewrite{
Next: plugin.HandlerFunc(msgPrinter),
Rules: rules,
}
rec := dnstest.NewRecorder(&test.ResponseWriter{})
rw.ServeDNS(ctx, rec, m)
resp := rec.Msg
if len(resp.Answer) == 0 {
t.Errorf("Test %d: FAIL %s (%d) Expected valid response but received %q", i, tc.from, tc.fromType, resp)
continue
}
if !reflect.DeepEqual(resp.Answer, tc.expectedAnswer) {
t.Errorf("Test %d: FAIL %s (%d) Actual are expected answer does not match, actual: %v, expected: %v",
i, tc.from, tc.fromType, resp.Answer, tc.expectedAnswer)
continue
}
}
}