mirror of https://github.com/hashicorp/consul
564 lines
13 KiB
Go
564 lines
13 KiB
Go
|
// Copyright (c) HashiCorp, Inc.
|
||
|
// SPDX-License-Identifier: BUSL-1.1
|
||
|
|
||
|
package dns
|
||
|
|
||
|
import (
|
||
|
"net"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/miekg/dns"
|
||
|
"github.com/stretchr/testify/mock"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
|
||
|
"github.com/hashicorp/consul/agent/discovery"
|
||
|
)
|
||
|
|
||
|
func Test_HandleRequest_PTR(t *testing.T) {
|
||
|
testCases := []HandleTestCase{
|
||
|
{
|
||
|
name: "PTR lookup for node, query type is ANY",
|
||
|
request: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
},
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "4.3.2.1.in-addr.arpa",
|
||
|
Qtype: dns.TypeANY,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
configureDataFetcher: func(fetcher discovery.CatalogDataFetcher) {
|
||
|
results := []*discovery.Result{
|
||
|
{
|
||
|
Node: &discovery.Location{Name: "foo", Address: "1.2.3.4"},
|
||
|
Service: &discovery.Location{Name: "bar", Address: "foo"},
|
||
|
Type: discovery.ResultTypeNode,
|
||
|
Tenancy: discovery.ResultTenancy{
|
||
|
Datacenter: "dc2",
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
fetcher.(*discovery.MockCatalogDataFetcher).
|
||
|
On("FetchRecordsByIp", mock.Anything, mock.Anything).
|
||
|
Return(results, nil).
|
||
|
Run(func(args mock.Arguments) {
|
||
|
req := args.Get(1).(net.IP)
|
||
|
|
||
|
require.NotNil(t, req)
|
||
|
require.Equal(t, "1.2.3.4", req.String())
|
||
|
})
|
||
|
},
|
||
|
response: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
Response: true,
|
||
|
Authoritative: true,
|
||
|
},
|
||
|
Compress: true,
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "4.3.2.1.in-addr.arpa.",
|
||
|
Qtype: dns.TypeANY,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
Answer: []dns.RR{
|
||
|
&dns.PTR{
|
||
|
Hdr: dns.RR_Header{
|
||
|
Name: "4.3.2.1.in-addr.arpa.",
|
||
|
Rrtype: dns.TypePTR,
|
||
|
Class: dns.ClassINET,
|
||
|
},
|
||
|
Ptr: "foo.node.dc2.consul.",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "PTR lookup for IPV6 node",
|
||
|
request: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
},
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa",
|
||
|
Qtype: dns.TypePTR,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
configureDataFetcher: func(fetcher discovery.CatalogDataFetcher) {
|
||
|
results := []*discovery.Result{
|
||
|
{
|
||
|
Node: &discovery.Location{Name: "foo", Address: "2001:db8::567:89ab"},
|
||
|
Service: &discovery.Location{Name: "web", Address: "foo"},
|
||
|
Type: discovery.ResultTypeNode,
|
||
|
Tenancy: discovery.ResultTenancy{
|
||
|
Datacenter: "dc2",
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
fetcher.(*discovery.MockCatalogDataFetcher).
|
||
|
On("FetchRecordsByIp", mock.Anything, mock.Anything).
|
||
|
Return(results, nil).
|
||
|
Run(func(args mock.Arguments) {
|
||
|
req := args.Get(1).(net.IP)
|
||
|
|
||
|
require.NotNil(t, req)
|
||
|
require.Equal(t, "2001:db8::567:89ab", req.String())
|
||
|
})
|
||
|
},
|
||
|
response: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
Response: true,
|
||
|
Authoritative: true,
|
||
|
},
|
||
|
Compress: true,
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
|
||
|
Qtype: dns.TypePTR,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
Answer: []dns.RR{
|
||
|
&dns.PTR{
|
||
|
Hdr: dns.RR_Header{
|
||
|
Name: "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
|
||
|
Rrtype: dns.TypePTR,
|
||
|
Class: dns.ClassINET,
|
||
|
},
|
||
|
Ptr: "foo.node.dc2.consul.",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "PTR lookup for invalid IP address",
|
||
|
request: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
},
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "257.3.2.1.in-addr.arpa",
|
||
|
Qtype: dns.TypeANY,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
response: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
Response: true,
|
||
|
Authoritative: true,
|
||
|
Rcode: dns.RcodeNameError,
|
||
|
},
|
||
|
Compress: true,
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "257.3.2.1.in-addr.arpa.",
|
||
|
Qtype: dns.TypeANY,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
Ns: []dns.RR{
|
||
|
&dns.SOA{
|
||
|
Hdr: dns.RR_Header{
|
||
|
Name: "consul.",
|
||
|
Rrtype: dns.TypeSOA,
|
||
|
Class: dns.ClassINET,
|
||
|
Ttl: 4,
|
||
|
},
|
||
|
Ns: "ns.consul.",
|
||
|
Serial: uint32(time.Now().Unix()),
|
||
|
Mbox: "hostmaster.consul.",
|
||
|
Refresh: 1,
|
||
|
Expire: 3,
|
||
|
Retry: 2,
|
||
|
Minttl: 4,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "PTR lookup for invalid subdomain",
|
||
|
request: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
},
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "4.3.2.1.blah.arpa",
|
||
|
Qtype: dns.TypeANY,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
response: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
Response: true,
|
||
|
Authoritative: true,
|
||
|
Rcode: dns.RcodeNameError,
|
||
|
},
|
||
|
Compress: true,
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "4.3.2.1.blah.arpa.",
|
||
|
Qtype: dns.TypeANY,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
Ns: []dns.RR{
|
||
|
&dns.SOA{
|
||
|
Hdr: dns.RR_Header{
|
||
|
Name: "consul.",
|
||
|
Rrtype: dns.TypeSOA,
|
||
|
Class: dns.ClassINET,
|
||
|
Ttl: 4,
|
||
|
},
|
||
|
Ns: "ns.consul.",
|
||
|
Serial: uint32(time.Now().Unix()),
|
||
|
Mbox: "hostmaster.consul.",
|
||
|
Refresh: 1,
|
||
|
Expire: 3,
|
||
|
Retry: 2,
|
||
|
Minttl: 4,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "[ENT] PTR Lookup for node w/ peer name in default partition, query type is ANY",
|
||
|
request: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
},
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "4.3.2.1.in-addr.arpa",
|
||
|
Qtype: dns.TypeANY,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
configureDataFetcher: func(fetcher discovery.CatalogDataFetcher) {
|
||
|
results := []*discovery.Result{
|
||
|
{
|
||
|
Node: &discovery.Location{Name: "foo", Address: "1.2.3.4"},
|
||
|
Type: discovery.ResultTypeNode,
|
||
|
Service: &discovery.Location{Name: "foo-web", Address: "foo"},
|
||
|
Tenancy: discovery.ResultTenancy{
|
||
|
Datacenter: "dc2",
|
||
|
PeerName: "peer1",
|
||
|
Partition: "default",
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
fetcher.(*discovery.MockCatalogDataFetcher).
|
||
|
On("FetchRecordsByIp", mock.Anything, mock.Anything).
|
||
|
Return(results, nil).
|
||
|
Run(func(args mock.Arguments) {
|
||
|
req := args.Get(1).(net.IP)
|
||
|
|
||
|
require.NotNil(t, req)
|
||
|
require.Equal(t, "1.2.3.4", req.String())
|
||
|
})
|
||
|
},
|
||
|
response: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
Response: true,
|
||
|
Authoritative: true,
|
||
|
},
|
||
|
Compress: true,
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "4.3.2.1.in-addr.arpa.",
|
||
|
Qtype: dns.TypeANY,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
Answer: []dns.RR{
|
||
|
&dns.PTR{
|
||
|
Hdr: dns.RR_Header{
|
||
|
Name: "4.3.2.1.in-addr.arpa.",
|
||
|
Rrtype: dns.TypePTR,
|
||
|
Class: dns.ClassINET,
|
||
|
},
|
||
|
Ptr: "foo.node.peer1.peer.default.ap.consul.",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "[ENT] PTR Lookup for service in default namespace, query type is PTR",
|
||
|
request: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
},
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "4.3.2.1.in-addr.arpa",
|
||
|
Qtype: dns.TypePTR,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
configureDataFetcher: func(fetcher discovery.CatalogDataFetcher) {
|
||
|
results := []*discovery.Result{
|
||
|
{
|
||
|
Node: &discovery.Location{Name: "foo", Address: "1.2.3.4"},
|
||
|
Type: discovery.ResultTypeService,
|
||
|
Service: &discovery.Location{Name: "foo", Address: "foo"},
|
||
|
Tenancy: discovery.ResultTenancy{
|
||
|
Datacenter: "dc2",
|
||
|
Namespace: "default",
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
fetcher.(*discovery.MockCatalogDataFetcher).
|
||
|
On("FetchRecordsByIp", mock.Anything, mock.Anything).
|
||
|
Return(results, nil).
|
||
|
Run(func(args mock.Arguments) {
|
||
|
req := args.Get(1).(net.IP)
|
||
|
|
||
|
require.NotNil(t, req)
|
||
|
require.Equal(t, "1.2.3.4", req.String())
|
||
|
})
|
||
|
},
|
||
|
response: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
Response: true,
|
||
|
Authoritative: true,
|
||
|
},
|
||
|
Compress: true,
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "4.3.2.1.in-addr.arpa.",
|
||
|
Qtype: dns.TypePTR,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
Answer: []dns.RR{
|
||
|
&dns.PTR{
|
||
|
Hdr: dns.RR_Header{
|
||
|
Name: "4.3.2.1.in-addr.arpa.",
|
||
|
Rrtype: dns.TypePTR,
|
||
|
Class: dns.ClassINET,
|
||
|
},
|
||
|
Ptr: "foo.service.default.dc2.consul.",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "[ENT] PTR Lookup for service in a non-default namespace, query type is PTR",
|
||
|
request: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
},
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "4.3.2.1.in-addr.arpa",
|
||
|
Qtype: dns.TypePTR,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
configureDataFetcher: func(fetcher discovery.CatalogDataFetcher) {
|
||
|
results := []*discovery.Result{
|
||
|
{
|
||
|
Node: &discovery.Location{Name: "foo-node", Address: "1.2.3.4"},
|
||
|
Type: discovery.ResultTypeService,
|
||
|
Service: &discovery.Location{Name: "foo", Address: "foo"},
|
||
|
Tenancy: discovery.ResultTenancy{
|
||
|
Datacenter: "dc2",
|
||
|
Namespace: "bar",
|
||
|
Partition: "baz",
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
fetcher.(*discovery.MockCatalogDataFetcher).
|
||
|
On("FetchRecordsByIp", mock.Anything, mock.Anything).
|
||
|
Return(results, nil).
|
||
|
Run(func(args mock.Arguments) {
|
||
|
req := args.Get(1).(net.IP)
|
||
|
|
||
|
require.NotNil(t, req)
|
||
|
require.Equal(t, "1.2.3.4", req.String())
|
||
|
})
|
||
|
},
|
||
|
response: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
Response: true,
|
||
|
Authoritative: true,
|
||
|
},
|
||
|
Compress: true,
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "4.3.2.1.in-addr.arpa.",
|
||
|
Qtype: dns.TypePTR,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
Answer: []dns.RR{
|
||
|
&dns.PTR{
|
||
|
Hdr: dns.RR_Header{
|
||
|
Name: "4.3.2.1.in-addr.arpa.",
|
||
|
Rrtype: dns.TypePTR,
|
||
|
Class: dns.ClassINET,
|
||
|
},
|
||
|
Ptr: "foo.service.bar.dc2.consul.",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "[CE] PTR Lookup for node w/ peer name, query type is ANY",
|
||
|
request: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
},
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "4.3.2.1.in-addr.arpa",
|
||
|
Qtype: dns.TypeANY,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
configureDataFetcher: func(fetcher discovery.CatalogDataFetcher) {
|
||
|
results := []*discovery.Result{
|
||
|
{
|
||
|
Node: &discovery.Location{Name: "foo", Address: "1.2.3.4"},
|
||
|
Type: discovery.ResultTypeNode,
|
||
|
Service: &discovery.Location{Name: "foo", Address: "foo"},
|
||
|
Tenancy: discovery.ResultTenancy{
|
||
|
Datacenter: "dc2",
|
||
|
PeerName: "peer1",
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
fetcher.(*discovery.MockCatalogDataFetcher).
|
||
|
On("FetchRecordsByIp", mock.Anything, mock.Anything).
|
||
|
Return(results, nil).
|
||
|
Run(func(args mock.Arguments) {
|
||
|
req := args.Get(1).(net.IP)
|
||
|
|
||
|
require.NotNil(t, req)
|
||
|
require.Equal(t, "1.2.3.4", req.String())
|
||
|
})
|
||
|
},
|
||
|
response: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
Response: true,
|
||
|
Authoritative: true,
|
||
|
},
|
||
|
Compress: true,
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "4.3.2.1.in-addr.arpa.",
|
||
|
Qtype: dns.TypeANY,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
Answer: []dns.RR{
|
||
|
&dns.PTR{
|
||
|
Hdr: dns.RR_Header{
|
||
|
Name: "4.3.2.1.in-addr.arpa.",
|
||
|
Rrtype: dns.TypePTR,
|
||
|
Class: dns.ClassINET,
|
||
|
},
|
||
|
Ptr: "foo.node.peer1.peer.consul.",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "[CE] PTR Lookup for service, query type is PTR",
|
||
|
request: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
},
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "4.3.2.1.in-addr.arpa",
|
||
|
Qtype: dns.TypePTR,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
configureDataFetcher: func(fetcher discovery.CatalogDataFetcher) {
|
||
|
results := []*discovery.Result{
|
||
|
{
|
||
|
Node: &discovery.Location{Name: "foo", Address: "1.2.3.4"},
|
||
|
Service: &discovery.Location{Name: "foo", Address: "foo"},
|
||
|
Type: discovery.ResultTypeService,
|
||
|
Tenancy: discovery.ResultTenancy{
|
||
|
Datacenter: "dc2",
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
fetcher.(*discovery.MockCatalogDataFetcher).
|
||
|
On("FetchRecordsByIp", mock.Anything, mock.Anything).
|
||
|
Return(results, nil).
|
||
|
Run(func(args mock.Arguments) {
|
||
|
req := args.Get(1).(net.IP)
|
||
|
|
||
|
require.NotNil(t, req)
|
||
|
require.Equal(t, "1.2.3.4", req.String())
|
||
|
})
|
||
|
},
|
||
|
response: &dns.Msg{
|
||
|
MsgHdr: dns.MsgHdr{
|
||
|
Opcode: dns.OpcodeQuery,
|
||
|
Response: true,
|
||
|
Authoritative: true,
|
||
|
},
|
||
|
Compress: true,
|
||
|
Question: []dns.Question{
|
||
|
{
|
||
|
Name: "4.3.2.1.in-addr.arpa.",
|
||
|
Qtype: dns.TypePTR,
|
||
|
Qclass: dns.ClassINET,
|
||
|
},
|
||
|
},
|
||
|
Answer: []dns.RR{
|
||
|
&dns.PTR{
|
||
|
Hdr: dns.RR_Header{
|
||
|
Name: "4.3.2.1.in-addr.arpa.",
|
||
|
Rrtype: dns.TypePTR,
|
||
|
Class: dns.ClassINET,
|
||
|
},
|
||
|
Ptr: "foo.service.dc2.consul.",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tc := range testCases {
|
||
|
t.Run(tc.name, func(t *testing.T) {
|
||
|
runHandleTestCases(t, tc)
|
||
|
})
|
||
|
}
|
||
|
}
|