mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-31 10:13:14 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			557 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			557 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package dns64
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"net"
 | |
| 	"reflect"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/coredns/coredns/plugin/pkg/dnstest"
 | |
| 	"github.com/coredns/coredns/plugin/test"
 | |
| 	"github.com/coredns/coredns/request"
 | |
| 
 | |
| 	"github.com/miekg/dns"
 | |
| )
 | |
| 
 | |
| func To6(prefix, address string) (net.IP, error) {
 | |
| 	_, pref, _ := net.ParseCIDR(prefix)
 | |
| 	addr := net.ParseIP(address)
 | |
| 
 | |
| 	return to6(pref, addr)
 | |
| }
 | |
| 
 | |
| func TestRequestShouldIntercept(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name      string
 | |
| 		allowIpv4 bool
 | |
| 		remoteIP  string
 | |
| 		msg       *dns.Msg
 | |
| 		want      bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name:      "should intercept request from IPv6 network - AAAA - IN",
 | |
| 			allowIpv4: true,
 | |
| 			remoteIP:  "::1",
 | |
| 			msg:       new(dns.Msg).SetQuestion("example.com", dns.TypeAAAA),
 | |
| 			want:      true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "should intercept request from IPv4 network - AAAA - IN",
 | |
| 			allowIpv4: true,
 | |
| 			remoteIP:  "127.0.0.1",
 | |
| 			msg:       new(dns.Msg).SetQuestion("example.com", dns.TypeAAAA),
 | |
| 			want:      true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "should not intercept request from IPv4 network - AAAA - IN",
 | |
| 			allowIpv4: false,
 | |
| 			remoteIP:  "127.0.0.1",
 | |
| 			msg:       new(dns.Msg).SetQuestion("example.com", dns.TypeAAAA),
 | |
| 			want:      false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "should not intercept request from IPv6 network - A - IN",
 | |
| 			allowIpv4: false,
 | |
| 			remoteIP:  "::1",
 | |
| 			msg:       new(dns.Msg).SetQuestion("example.com", dns.TypeA),
 | |
| 			want:      false,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tc := range tests {
 | |
| 		t.Run(tc.name, func(t *testing.T) {
 | |
| 			h := DNS64{AllowIPv4: tc.allowIpv4}
 | |
| 			rec := dnstest.NewRecorder(&test.ResponseWriter{RemoteIP: tc.remoteIP})
 | |
| 			r := request.Request{W: rec, Req: tc.msg}
 | |
| 
 | |
| 			actual := h.requestShouldIntercept(&r)
 | |
| 
 | |
| 			if actual != tc.want {
 | |
| 				t.Fatalf("Expected %v, but got %v", tc.want, actual)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestTo6(t *testing.T) {
 | |
| 	v6, err := To6("64:ff9b::/96", "64.64.64.64")
 | |
| 	if err != nil {
 | |
| 		t.Error(err)
 | |
| 	}
 | |
| 	if v6.String() != "64:ff9b::4040:4040" {
 | |
| 		t.Errorf("%d", v6)
 | |
| 	}
 | |
| 
 | |
| 	v6, err = To6("64:ff9b::/64", "64.64.64.64")
 | |
| 	if err != nil {
 | |
| 		t.Error(err)
 | |
| 	}
 | |
| 	if v6.String() != "64:ff9b::40:4040:4000:0" {
 | |
| 		t.Errorf("%d", v6)
 | |
| 	}
 | |
| 
 | |
| 	v6, err = To6("64:ff9b::/56", "64.64.64.64")
 | |
| 	if err != nil {
 | |
| 		t.Error(err)
 | |
| 	}
 | |
| 	if v6.String() != "64:ff9b:0:40:40:4040::" {
 | |
| 		t.Errorf("%d", v6)
 | |
| 	}
 | |
| 
 | |
| 	v6, err = To6("64::/32", "64.64.64.64")
 | |
| 	if err != nil {
 | |
| 		t.Error(err)
 | |
| 	}
 | |
| 	if v6.String() != "64:0:4040:4040::" {
 | |
| 		t.Errorf("%d", v6)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResponseShould(t *testing.T) {
 | |
| 	var tests = []struct {
 | |
| 		resp         dns.Msg
 | |
| 		translateAll bool
 | |
| 		expected     bool
 | |
| 	}{
 | |
| 		// If there's an AAAA record, then no
 | |
| 		{
 | |
| 			resp: dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Rcode: dns.RcodeSuccess,
 | |
| 				},
 | |
| 				Answer: []dns.RR{
 | |
| 					test.AAAA("example.com. IN AAAA ::1"),
 | |
| 				},
 | |
| 			},
 | |
| 			expected: false,
 | |
| 		},
 | |
| 		// If there's no AAAA, then true
 | |
| 		{
 | |
| 			resp: dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Rcode: dns.RcodeSuccess,
 | |
| 				},
 | |
| 				Ns: []dns.RR{
 | |
| 					test.SOA("example.com. IN SOA foo bar 1 1 1 1 1"),
 | |
| 				},
 | |
| 			},
 | |
| 			expected: true,
 | |
| 		},
 | |
| 		// Failure, except NameError, should be true
 | |
| 		{
 | |
| 			resp: dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Rcode: dns.RcodeNotImplemented,
 | |
| 				},
 | |
| 				Ns: []dns.RR{
 | |
| 					test.SOA("example.com. IN SOA foo bar 1 1 1 1 1"),
 | |
| 				},
 | |
| 			},
 | |
| 			expected: true,
 | |
| 		},
 | |
| 		// NameError should be false
 | |
| 		{
 | |
| 			resp: dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Rcode: dns.RcodeNameError,
 | |
| 				},
 | |
| 				Ns: []dns.RR{
 | |
| 					test.SOA("example.com. IN SOA foo bar 1 1 1 1 1"),
 | |
| 				},
 | |
| 			},
 | |
| 			expected: false,
 | |
| 		},
 | |
| 		// If there's an AAAA record, but translate_all is configured, then yes
 | |
| 		{
 | |
| 			resp: dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Rcode: dns.RcodeSuccess,
 | |
| 				},
 | |
| 				Answer: []dns.RR{
 | |
| 					test.AAAA("example.com. IN AAAA ::1"),
 | |
| 				},
 | |
| 			},
 | |
| 			translateAll: true,
 | |
| 			expected:     true,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	d := DNS64{}
 | |
| 
 | |
| 	for idx, tc := range tests {
 | |
| 		t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) {
 | |
| 			d.TranslateAll = tc.translateAll
 | |
| 			actual := d.responseShouldDNS64(&tc.resp)
 | |
| 			if actual != tc.expected {
 | |
| 				t.Fatalf("Expected %v got %v", tc.expected, actual)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDNS64(t *testing.T) {
 | |
| 	var cases = []struct {
 | |
| 		// a brief summary of the test case
 | |
| 		name string
 | |
| 
 | |
| 		// the request
 | |
| 		req *dns.Msg
 | |
| 
 | |
| 		// the initial response from the "downstream" server
 | |
| 		initResp *dns.Msg
 | |
| 
 | |
| 		// A response to provide
 | |
| 		aResp *dns.Msg
 | |
| 
 | |
| 		// the expected ultimate result
 | |
| 		resp *dns.Msg
 | |
| 	}{
 | |
| 		{
 | |
| 			// no AAAA record, yes A record. Do DNS64
 | |
| 			name: "standard flow",
 | |
| 			req: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					RecursionDesired: true,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 			},
 | |
| 			initResp: &dns.Msg{ //success, no answers
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Rcode:            dns.RcodeSuccess,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 				Ns:       []dns.RR{test.SOA("example.com. 70 IN SOA foo bar 1 1 1 1 1")},
 | |
| 			},
 | |
| 			aResp: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               43,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Rcode:            dns.RcodeSuccess,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET}},
 | |
| 				Answer: []dns.RR{
 | |
| 					test.A("example.com. 60 IN A 192.0.2.42"),
 | |
| 					test.A("example.com. 5000 IN A 192.0.2.43"),
 | |
| 				},
 | |
| 			},
 | |
| 
 | |
| 			resp: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Rcode:            dns.RcodeSuccess,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 				Answer: []dns.RR{
 | |
| 					test.AAAA("example.com. 60 IN AAAA 64:ff9b::192.0.2.42"),
 | |
| 					// override RR ttl to SOA ttl, since it's lower
 | |
| 					test.AAAA("example.com. 70 IN AAAA 64:ff9b::192.0.2.43"),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			// name exists, but has neither A nor AAAA record
 | |
| 			name: "a empty",
 | |
| 			req: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					RecursionDesired: true,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 			},
 | |
| 			initResp: &dns.Msg{ //success, no answers
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Rcode:            dns.RcodeSuccess,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 				Ns:       []dns.RR{test.SOA("example.com. 3600 IN SOA foo bar 1 7200 900 1209600 86400")},
 | |
| 			},
 | |
| 			aResp: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               43,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Rcode:            dns.RcodeSuccess,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET}},
 | |
| 				Ns:       []dns.RR{test.SOA("example.com. 3600 IN SOA foo bar 1 7200 900 1209600 86400")},
 | |
| 			},
 | |
| 
 | |
| 			resp: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Rcode:            dns.RcodeSuccess,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 				Ns:       []dns.RR{test.SOA("example.com. 3600 IN SOA foo bar 1 7200 900 1209600 86400")},
 | |
| 				Answer:   []dns.RR{}, // just to make comparison happy
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			// Query error other than NameError
 | |
| 			name: "non-nxdomain error",
 | |
| 			req: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					RecursionDesired: true,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 			},
 | |
| 			initResp: &dns.Msg{ // failure
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Rcode:            dns.RcodeRefused,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 			},
 | |
| 			aResp: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               43,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Rcode:            dns.RcodeSuccess,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET}},
 | |
| 				Answer: []dns.RR{
 | |
| 					test.A("example.com. 60 IN A 192.0.2.42"),
 | |
| 					test.A("example.com. 5000 IN A 192.0.2.43"),
 | |
| 				},
 | |
| 			},
 | |
| 
 | |
| 			resp: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Rcode:            dns.RcodeSuccess,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 				Answer: []dns.RR{
 | |
| 					test.AAAA("example.com. 60 IN AAAA 64:ff9b::192.0.2.42"),
 | |
| 					test.AAAA("example.com. 600 IN AAAA 64:ff9b::192.0.2.43"),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			// nxdomain (NameError): don't even try an A request.
 | |
| 			name: "nxdomain",
 | |
| 			req: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					RecursionDesired: true,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 			},
 | |
| 			initResp: &dns.Msg{ // failure
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Rcode:            dns.RcodeNameError,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 				Ns:       []dns.RR{test.SOA("example.com. 3600 IN SOA foo bar 1 7200 900 1209600 86400")},
 | |
| 			},
 | |
| 			resp: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Rcode:            dns.RcodeNameError,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 				Ns:       []dns.RR{test.SOA("example.com. 3600 IN SOA foo bar 1 7200 900 1209600 86400")},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			// AAAA record exists
 | |
| 			name: "AAAA record",
 | |
| 			req: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					RecursionDesired: true,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 			},
 | |
| 
 | |
| 			initResp: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Rcode:            dns.RcodeSuccess,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 				Answer: []dns.RR{
 | |
| 					test.AAAA("example.com. 60 IN AAAA ::1"),
 | |
| 					test.AAAA("example.com. 5000 IN AAAA ::2"),
 | |
| 				},
 | |
| 			},
 | |
| 
 | |
| 			resp: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Rcode:            dns.RcodeSuccess,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 				Answer: []dns.RR{
 | |
| 					test.AAAA("example.com. 60 IN AAAA ::1"),
 | |
| 					test.AAAA("example.com. 5000 IN AAAA ::2"),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			// no AAAA records, A record response truncated.
 | |
| 			name: "truncated A response",
 | |
| 			req: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					RecursionDesired: true,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 			},
 | |
| 			initResp: &dns.Msg{ //success, no answers
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Rcode:            dns.RcodeSuccess,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 				Ns:       []dns.RR{test.SOA("example.com. 70 IN SOA foo bar 1 1 1 1 1")},
 | |
| 			},
 | |
| 			aResp: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               43,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Truncated:        true,
 | |
| 					Rcode:            dns.RcodeSuccess,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET}},
 | |
| 				Answer: []dns.RR{
 | |
| 					test.A("example.com. 60 IN A 192.0.2.42"),
 | |
| 					test.A("example.com. 5000 IN A 192.0.2.43"),
 | |
| 				},
 | |
| 			},
 | |
| 
 | |
| 			resp: &dns.Msg{
 | |
| 				MsgHdr: dns.MsgHdr{
 | |
| 					Id:               42,
 | |
| 					Opcode:           dns.OpcodeQuery,
 | |
| 					RecursionDesired: true,
 | |
| 					Truncated:        true,
 | |
| 					Rcode:            dns.RcodeSuccess,
 | |
| 					Response:         true,
 | |
| 				},
 | |
| 				Question: []dns.Question{{Name: "example.com.", Qtype: dns.TypeAAAA, Qclass: dns.ClassINET}},
 | |
| 				Answer: []dns.RR{
 | |
| 					test.AAAA("example.com. 60 IN AAAA 64:ff9b::192.0.2.42"),
 | |
| 					// override RR ttl to SOA ttl, since it's lower
 | |
| 					test.AAAA("example.com. 70 IN AAAA 64:ff9b::192.0.2.43"),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	_, pfx, _ := net.ParseCIDR("64:ff9b::/96")
 | |
| 
 | |
| 	for idx, tc := range cases {
 | |
| 		t.Run(fmt.Sprintf("%d_%s", idx, tc.name), func(t *testing.T) {
 | |
| 			d := DNS64{
 | |
| 				Next:     &fakeHandler{t, tc.initResp},
 | |
| 				Prefix:   pfx,
 | |
| 				Upstream: &fakeUpstream{t, tc.req.Question[0].Name, tc.aResp},
 | |
| 			}
 | |
| 
 | |
| 			rec := dnstest.NewRecorder(&test.ResponseWriter{RemoteIP: "::1"})
 | |
| 			rc, err := d.ServeDNS(context.Background(), rec, tc.req)
 | |
| 			if err != nil {
 | |
| 				t.Fatal(err)
 | |
| 			}
 | |
| 			actual := rec.Msg
 | |
| 			if actual.Rcode != rc {
 | |
| 				t.Fatalf("ServeDNS should return real result code %q != %q", actual.Rcode, rc)
 | |
| 			}
 | |
| 
 | |
| 			if !reflect.DeepEqual(actual, tc.resp) {
 | |
| 				t.Fatalf("Final answer should match expected %q != %q", actual, tc.resp)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type fakeHandler struct {
 | |
| 	t     *testing.T
 | |
| 	reply *dns.Msg
 | |
| }
 | |
| 
 | |
| func (fh *fakeHandler) ServeDNS(_ context.Context, w dns.ResponseWriter, _ *dns.Msg) (int, error) {
 | |
| 	if fh.reply == nil {
 | |
| 		panic("fakeHandler ServeDNS with nil reply")
 | |
| 	}
 | |
| 	w.WriteMsg(fh.reply)
 | |
| 
 | |
| 	return fh.reply.Rcode, nil
 | |
| }
 | |
| func (fh *fakeHandler) Name() string {
 | |
| 	return "fake"
 | |
| }
 | |
| 
 | |
| type fakeUpstream struct {
 | |
| 	t     *testing.T
 | |
| 	qname string
 | |
| 	resp  *dns.Msg
 | |
| }
 | |
| 
 | |
| func (fu *fakeUpstream) Lookup(_ context.Context, _ request.Request, name string, typ uint16) (*dns.Msg, error) {
 | |
| 	if fu.qname == "" {
 | |
| 		fu.t.Fatalf("Unexpected A lookup for %s", name)
 | |
| 	}
 | |
| 	if name != fu.qname {
 | |
| 		fu.t.Fatalf("Wrong A lookup for %s, expected %s", name, fu.qname)
 | |
| 	}
 | |
| 
 | |
| 	if typ != dns.TypeA {
 | |
| 		fu.t.Fatalf("Wrong lookup type %d, expected %d", typ, dns.TypeA)
 | |
| 	}
 | |
| 
 | |
| 	return fu.resp, nil
 | |
| }
 |