mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
563 lines
13 KiB
563 lines
13 KiB
// 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) |
|
}) |
|
} |
|
}
|
|
|