2017-09-14 09:36:06 +01:00
|
|
|
package plugin
|
2016-03-20 21:36:55 +00:00
|
|
|
|
2016-03-21 21:21:29 +00:00
|
|
|
import (
|
2019-03-26 14:37:30 +00:00
|
|
|
"context"
|
2016-04-13 08:03:56 +01:00
|
|
|
"fmt"
|
2016-03-21 21:21:29 +00:00
|
|
|
"math"
|
|
|
|
"net"
|
|
|
|
|
2017-09-14 09:36:06 +01:00
|
|
|
"github.com/coredns/coredns/plugin/etcd/msg"
|
|
|
|
"github.com/coredns/coredns/plugin/pkg/dnsutil"
|
2017-02-21 22:51:47 -08:00
|
|
|
"github.com/coredns/coredns/request"
|
2016-03-22 08:30:30 +00:00
|
|
|
|
2016-03-21 21:21:29 +00:00
|
|
|
"github.com/miekg/dns"
|
|
|
|
)
|
|
|
|
|
2016-10-30 15:54:16 +00:00
|
|
|
// A returns A records from Backend or an error.
|
2022-02-22 09:38:57 -05:00
|
|
|
func A(ctx context.Context, b ServiceBackend, zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, truncated bool, err error) {
|
2019-03-26 14:37:30 +00:00
|
|
|
services, err := checkForApex(ctx, b, zone, state, opt)
|
2016-03-25 15:30:44 +00:00
|
|
|
if err != nil {
|
2022-02-22 09:38:57 -05:00
|
|
|
return nil, false, err
|
2016-03-25 15:30:44 +00:00
|
|
|
}
|
2016-03-22 10:29:48 +00:00
|
|
|
|
2018-12-08 13:19:22 +00:00
|
|
|
dup := make(map[string]struct{})
|
2018-06-12 03:23:25 +01:00
|
|
|
|
2016-03-22 10:29:48 +00:00
|
|
|
for _, serv := range services {
|
2017-04-22 07:58:30 +01:00
|
|
|
what, ip := serv.HostType()
|
|
|
|
|
|
|
|
switch what {
|
2017-05-30 08:20:39 -04:00
|
|
|
case dns.TypeCNAME:
|
2016-10-30 15:54:16 +00:00
|
|
|
if Name(state.Name()).Matches(dns.Fqdn(serv.Host)) {
|
2016-03-22 10:29:48 +00:00
|
|
|
// x CNAME x is a direct loop, don't add those
|
2022-04-04 15:00:17 -04:00
|
|
|
// in etcd/skydns w.x CNAME x is also direct loop due to the "recursive" nature of search results
|
2016-03-22 10:29:48 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-03-22 11:04:56 +00:00
|
|
|
newRecord := serv.NewCNAME(state.QName(), serv.Host)
|
2016-03-22 10:29:48 +00:00
|
|
|
if len(previousRecords) > 7 {
|
|
|
|
// don't add it, and just continue
|
|
|
|
continue
|
|
|
|
}
|
2016-09-07 11:10:16 +01:00
|
|
|
if dnsutil.DuplicateCNAME(newRecord, previousRecords) {
|
2016-03-22 10:29:48 +00:00
|
|
|
continue
|
|
|
|
}
|
2018-08-25 20:53:41 +08:00
|
|
|
if dns.IsSubDomain(zone, dns.Fqdn(serv.Host)) {
|
|
|
|
state1 := state.NewWithQuestion(serv.Host, state.QType())
|
|
|
|
state1.Zone = zone
|
2022-02-22 09:38:57 -05:00
|
|
|
nextRecords, tc, err := A(ctx, b, zone, state1, append(previousRecords, newRecord), opt)
|
2018-08-25 20:53:41 +08:00
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
// Not only have we found something we should add the CNAME and the IP addresses.
|
|
|
|
if len(nextRecords) > 0 {
|
|
|
|
records = append(records, newRecord)
|
|
|
|
records = append(records, nextRecords...)
|
|
|
|
}
|
2016-03-22 10:29:48 +00:00
|
|
|
}
|
2022-02-22 09:38:57 -05:00
|
|
|
if tc {
|
|
|
|
truncated = true
|
|
|
|
}
|
2016-03-22 10:29:48 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
// This means we can not complete the CNAME, try to look else where.
|
|
|
|
target := newRecord.Target
|
2016-10-30 15:54:16 +00:00
|
|
|
// Lookup
|
2019-03-26 14:37:30 +00:00
|
|
|
m1, e1 := b.Lookup(ctx, state, target, state.QType())
|
2016-03-22 10:29:48 +00:00
|
|
|
if e1 != nil {
|
2016-08-08 21:42:39 -07:00
|
|
|
continue
|
2016-03-22 10:29:48 +00:00
|
|
|
}
|
2022-02-22 09:38:57 -05:00
|
|
|
if m1.Truncated {
|
|
|
|
truncated = true
|
|
|
|
}
|
2016-03-22 10:29:48 +00:00
|
|
|
// Len(m1.Answer) > 0 here is well?
|
|
|
|
records = append(records, newRecord)
|
|
|
|
records = append(records, m1.Answer...)
|
|
|
|
continue
|
2017-04-22 07:58:30 +01:00
|
|
|
|
|
|
|
case dns.TypeA:
|
2018-06-12 03:23:25 +01:00
|
|
|
if _, ok := dup[serv.Host]; !ok {
|
2018-12-08 13:19:22 +00:00
|
|
|
dup[serv.Host] = struct{}{}
|
2018-06-12 03:23:25 +01:00
|
|
|
records = append(records, serv.NewA(state.QName(), ip))
|
|
|
|
}
|
2017-04-22 07:58:30 +01:00
|
|
|
|
|
|
|
case dns.TypeAAAA:
|
2018-06-12 03:23:25 +01:00
|
|
|
// nada
|
2016-03-22 10:29:48 +00:00
|
|
|
}
|
|
|
|
}
|
2022-02-22 09:38:57 -05:00
|
|
|
return records, truncated, nil
|
2016-03-22 10:29:48 +00:00
|
|
|
}
|
2016-03-21 21:21:29 +00:00
|
|
|
|
2016-10-30 15:54:16 +00:00
|
|
|
// AAAA returns AAAA records from Backend or an error.
|
2022-02-22 09:38:57 -05:00
|
|
|
func AAAA(ctx context.Context, b ServiceBackend, zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, truncated bool, err error) {
|
2019-03-26 14:37:30 +00:00
|
|
|
services, err := checkForApex(ctx, b, zone, state, opt)
|
2016-03-20 21:36:55 +00:00
|
|
|
if err != nil {
|
2022-02-22 09:38:57 -05:00
|
|
|
return nil, false, err
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 13:19:22 +00:00
|
|
|
dup := make(map[string]struct{})
|
2018-06-12 03:23:25 +01:00
|
|
|
|
2016-03-20 21:36:55 +00:00
|
|
|
for _, serv := range services {
|
2017-04-22 07:58:30 +01:00
|
|
|
what, ip := serv.HostType()
|
|
|
|
|
|
|
|
switch what {
|
2017-05-30 08:20:39 -04:00
|
|
|
case dns.TypeCNAME:
|
2016-03-20 21:36:55 +00:00
|
|
|
// Try to resolve as CNAME if it's not an IP, but only if we don't create loops.
|
2016-10-30 15:54:16 +00:00
|
|
|
if Name(state.Name()).Matches(dns.Fqdn(serv.Host)) {
|
2016-03-20 21:36:55 +00:00
|
|
|
// x CNAME x is a direct loop, don't add those
|
2022-04-04 15:00:17 -04:00
|
|
|
// in etcd/skydns w.x CNAME x is also direct loop due to the "recursive" nature of search results
|
2016-03-20 21:36:55 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-03-22 11:04:56 +00:00
|
|
|
newRecord := serv.NewCNAME(state.QName(), serv.Host)
|
2016-03-20 21:36:55 +00:00
|
|
|
if len(previousRecords) > 7 {
|
|
|
|
// don't add it, and just continue
|
|
|
|
continue
|
|
|
|
}
|
2016-09-07 11:10:16 +01:00
|
|
|
if dnsutil.DuplicateCNAME(newRecord, previousRecords) {
|
2016-03-20 21:36:55 +00:00
|
|
|
continue
|
|
|
|
}
|
2018-08-25 20:53:41 +08:00
|
|
|
if dns.IsSubDomain(zone, dns.Fqdn(serv.Host)) {
|
|
|
|
state1 := state.NewWithQuestion(serv.Host, state.QType())
|
|
|
|
state1.Zone = zone
|
2022-02-22 09:38:57 -05:00
|
|
|
nextRecords, tc, err := AAAA(ctx, b, zone, state1, append(previousRecords, newRecord), opt)
|
2018-08-25 20:53:41 +08:00
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
// Not only have we found something we should add the CNAME and the IP addresses.
|
|
|
|
if len(nextRecords) > 0 {
|
|
|
|
records = append(records, newRecord)
|
|
|
|
records = append(records, nextRecords...)
|
|
|
|
}
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
2022-02-22 09:38:57 -05:00
|
|
|
if tc {
|
|
|
|
truncated = true
|
|
|
|
}
|
2016-03-20 21:36:55 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
// This means we can not complete the CNAME, try to look else where.
|
|
|
|
target := newRecord.Target
|
2019-03-26 14:37:30 +00:00
|
|
|
m1, e1 := b.Lookup(ctx, state, target, state.QType())
|
2016-03-20 21:36:55 +00:00
|
|
|
if e1 != nil {
|
|
|
|
continue
|
|
|
|
}
|
2022-02-22 09:38:57 -05:00
|
|
|
if m1.Truncated {
|
|
|
|
truncated = true
|
|
|
|
}
|
2016-03-20 21:36:55 +00:00
|
|
|
// Len(m1.Answer) > 0 here is well?
|
|
|
|
records = append(records, newRecord)
|
|
|
|
records = append(records, m1.Answer...)
|
|
|
|
continue
|
2016-03-22 08:30:30 +00:00
|
|
|
// both here again
|
2017-04-22 07:58:30 +01:00
|
|
|
|
|
|
|
case dns.TypeA:
|
2018-06-12 03:23:25 +01:00
|
|
|
// nada
|
2017-04-22 07:58:30 +01:00
|
|
|
|
|
|
|
case dns.TypeAAAA:
|
2018-06-12 03:23:25 +01:00
|
|
|
if _, ok := dup[serv.Host]; !ok {
|
2018-12-08 13:19:22 +00:00
|
|
|
dup[serv.Host] = struct{}{}
|
2018-06-12 03:23:25 +01:00
|
|
|
records = append(records, serv.NewAAAA(state.QName(), ip))
|
|
|
|
}
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
}
|
2022-02-22 09:38:57 -05:00
|
|
|
return records, truncated, nil
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
|
2016-10-30 15:54:16 +00:00
|
|
|
// SRV returns SRV records from the Backend.
|
2016-03-21 21:21:29 +00:00
|
|
|
// If the Target is not a name but an IP address, a name is created on the fly.
|
2019-03-26 14:37:30 +00:00
|
|
|
func SRV(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) (records, extra []dns.RR, err error) {
|
|
|
|
services, err := b.Services(ctx, state, false, opt)
|
2016-03-20 21:36:55 +00:00
|
|
|
if err != nil {
|
2017-09-12 10:52:43 +01:00
|
|
|
return nil, nil, err
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 13:19:22 +00:00
|
|
|
dup := make(map[item]struct{})
|
|
|
|
lookup := make(map[string]struct{})
|
2018-06-12 03:23:25 +01:00
|
|
|
|
|
|
|
// Looping twice to get the right weight vs priority. This might break because we may drop duplicate SRV records latter on.
|
2016-03-20 21:36:55 +00:00
|
|
|
w := make(map[int]int)
|
|
|
|
for _, serv := range services {
|
|
|
|
weight := 100
|
|
|
|
if serv.Weight != 0 {
|
|
|
|
weight = serv.Weight
|
|
|
|
}
|
|
|
|
if _, ok := w[serv.Priority]; !ok {
|
|
|
|
w[serv.Priority] = weight
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
w[serv.Priority] += weight
|
|
|
|
}
|
|
|
|
for _, serv := range services {
|
2018-08-27 10:10:51 -04:00
|
|
|
// Don't add the entry if the port is -1 (invalid). The kubernetes plugin uses port -1 when a service/endpoint
|
|
|
|
// does not have any declared ports.
|
|
|
|
if serv.Port == -1 {
|
|
|
|
continue
|
|
|
|
}
|
2016-03-20 21:36:55 +00:00
|
|
|
w1 := 100.0 / float64(w[serv.Priority])
|
|
|
|
if serv.Weight == 0 {
|
|
|
|
w1 *= 100
|
|
|
|
} else {
|
|
|
|
w1 *= float64(serv.Weight)
|
|
|
|
}
|
|
|
|
weight := uint16(math.Floor(w1))
|
2020-06-12 14:01:28 +08:00
|
|
|
// weight should be at least 1
|
|
|
|
if weight == 0 {
|
|
|
|
weight = 1
|
|
|
|
}
|
2017-04-22 07:58:30 +01:00
|
|
|
|
|
|
|
what, ip := serv.HostType()
|
|
|
|
|
|
|
|
switch what {
|
2017-05-30 08:20:39 -04:00
|
|
|
case dns.TypeCNAME:
|
2016-03-21 21:21:29 +00:00
|
|
|
srv := serv.NewSRV(state.QName(), weight)
|
2016-03-20 21:36:55 +00:00
|
|
|
records = append(records, srv)
|
|
|
|
|
|
|
|
if _, ok := lookup[srv.Target]; ok {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2018-12-08 13:19:22 +00:00
|
|
|
lookup[srv.Target] = struct{}{}
|
2016-03-20 21:36:55 +00:00
|
|
|
|
2016-03-21 21:21:29 +00:00
|
|
|
if !dns.IsSubDomain(zone, srv.Target) {
|
2019-03-26 14:37:30 +00:00
|
|
|
m1, e1 := b.Lookup(ctx, state, srv.Target, dns.TypeA)
|
2016-03-20 21:36:55 +00:00
|
|
|
if e1 == nil {
|
|
|
|
extra = append(extra, m1.Answer...)
|
|
|
|
}
|
2016-08-08 19:54:17 -07:00
|
|
|
|
2019-03-26 14:37:30 +00:00
|
|
|
m1, e1 = b.Lookup(ctx, state, srv.Target, dns.TypeAAAA)
|
2016-03-20 21:36:55 +00:00
|
|
|
if e1 == nil {
|
|
|
|
// If we have seen CNAME's we *assume* that they are already added.
|
|
|
|
for _, a := range m1.Answer {
|
|
|
|
if _, ok := a.(*dns.CNAME); !ok {
|
|
|
|
extra = append(extra, a)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Internal name, we should have some info on them, either v4 or v6
|
2016-03-24 17:31:01 +00:00
|
|
|
// Clients expect a complete answer, because we are a recursor in their view.
|
2016-09-07 11:10:16 +01:00
|
|
|
state1 := state.NewWithQuestion(srv.Target, dns.TypeA)
|
2022-02-22 09:38:57 -05:00
|
|
|
addr, _, e1 := A(ctx, b, zone, state1, nil, opt)
|
2016-03-20 21:36:55 +00:00
|
|
|
if e1 == nil {
|
|
|
|
extra = append(extra, addr...)
|
|
|
|
}
|
2018-06-18 11:16:56 +01:00
|
|
|
// TODO(miek): AAAA as well here.
|
2016-03-20 21:36:55 +00:00
|
|
|
|
2017-04-22 07:58:30 +01:00
|
|
|
case dns.TypeA, dns.TypeAAAA:
|
2018-06-12 03:23:25 +01:00
|
|
|
addr := serv.Host
|
2016-05-23 09:16:57 +01:00
|
|
|
serv.Host = msg.Domain(serv.Key)
|
2016-03-22 08:30:30 +00:00
|
|
|
srv := serv.NewSRV(state.QName(), weight)
|
2016-03-20 21:36:55 +00:00
|
|
|
|
2018-06-18 11:16:56 +01:00
|
|
|
if ok := isDuplicate(dup, srv.Target, "", srv.Port); !ok {
|
2018-06-12 03:23:25 +01:00
|
|
|
records = append(records, srv)
|
|
|
|
}
|
|
|
|
|
2018-06-18 11:16:56 +01:00
|
|
|
if ok := isDuplicate(dup, srv.Target, addr, 0); !ok {
|
2018-06-12 03:23:25 +01:00
|
|
|
extra = append(extra, newAddress(serv, srv.Target, ip, what))
|
|
|
|
}
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-12 10:52:43 +01:00
|
|
|
return records, extra, nil
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
|
2016-10-30 15:54:16 +00:00
|
|
|
// MX returns MX records from the Backend. If the Target is not a name but an IP address, a name is created on the fly.
|
2019-03-26 14:37:30 +00:00
|
|
|
func MX(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) (records, extra []dns.RR, err error) {
|
|
|
|
services, err := b.Services(ctx, state, false, opt)
|
2016-03-20 21:36:55 +00:00
|
|
|
if err != nil {
|
2017-09-12 10:52:43 +01:00
|
|
|
return nil, nil, err
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 13:19:22 +00:00
|
|
|
dup := make(map[item]struct{})
|
|
|
|
lookup := make(map[string]struct{})
|
2016-03-20 21:36:55 +00:00
|
|
|
for _, serv := range services {
|
|
|
|
if !serv.Mail {
|
|
|
|
continue
|
|
|
|
}
|
2017-04-22 07:58:30 +01:00
|
|
|
what, ip := serv.HostType()
|
|
|
|
switch what {
|
2017-05-30 08:20:39 -04:00
|
|
|
case dns.TypeCNAME:
|
2016-03-22 08:30:30 +00:00
|
|
|
mx := serv.NewMX(state.QName())
|
2016-03-20 21:36:55 +00:00
|
|
|
records = append(records, mx)
|
|
|
|
if _, ok := lookup[mx.Mx]; ok {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2018-12-08 13:19:22 +00:00
|
|
|
lookup[mx.Mx] = struct{}{}
|
2016-03-20 21:36:55 +00:00
|
|
|
|
2016-03-22 08:30:30 +00:00
|
|
|
if !dns.IsSubDomain(zone, mx.Mx) {
|
2019-03-26 14:37:30 +00:00
|
|
|
m1, e1 := b.Lookup(ctx, state, mx.Mx, dns.TypeA)
|
2016-03-20 21:36:55 +00:00
|
|
|
if e1 == nil {
|
|
|
|
extra = append(extra, m1.Answer...)
|
|
|
|
}
|
2017-09-12 10:52:43 +01:00
|
|
|
|
2019-03-26 14:37:30 +00:00
|
|
|
m1, e1 = b.Lookup(ctx, state, mx.Mx, dns.TypeAAAA)
|
2016-03-20 21:36:55 +00:00
|
|
|
if e1 == nil {
|
|
|
|
// If we have seen CNAME's we *assume* that they are already added.
|
|
|
|
for _, a := range m1.Answer {
|
|
|
|
if _, ok := a.(*dns.CNAME); !ok {
|
|
|
|
extra = append(extra, a)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Internal name
|
2016-09-07 11:10:16 +01:00
|
|
|
state1 := state.NewWithQuestion(mx.Mx, dns.TypeA)
|
2022-02-22 09:38:57 -05:00
|
|
|
addr, _, e1 := A(ctx, b, zone, state1, nil, opt)
|
2016-03-20 21:36:55 +00:00
|
|
|
if e1 == nil {
|
|
|
|
extra = append(extra, addr...)
|
|
|
|
}
|
2018-06-18 11:16:56 +01:00
|
|
|
// TODO(miek): AAAA as well here.
|
2017-04-22 07:58:30 +01:00
|
|
|
|
|
|
|
case dns.TypeA, dns.TypeAAAA:
|
2018-06-12 03:23:25 +01:00
|
|
|
addr := serv.Host
|
2016-05-23 09:16:57 +01:00
|
|
|
serv.Host = msg.Domain(serv.Key)
|
2018-06-12 03:23:25 +01:00
|
|
|
mx := serv.NewMX(state.QName())
|
|
|
|
|
2018-06-18 11:16:56 +01:00
|
|
|
if ok := isDuplicate(dup, mx.Mx, "", mx.Preference); !ok {
|
2018-06-12 03:23:25 +01:00
|
|
|
records = append(records, mx)
|
|
|
|
}
|
|
|
|
// Fake port to be 0 for address...
|
2018-06-18 11:16:56 +01:00
|
|
|
if ok := isDuplicate(dup, serv.Host, addr, 0); !ok {
|
2018-06-12 03:23:25 +01:00
|
|
|
extra = append(extra, newAddress(serv, serv.Host, ip, what))
|
|
|
|
}
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-12 10:52:43 +01:00
|
|
|
return records, extra, nil
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
|
2016-10-30 15:54:16 +00:00
|
|
|
// CNAME returns CNAME records from the backend or an error.
|
2019-03-26 14:37:30 +00:00
|
|
|
func CNAME(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) (records []dns.RR, err error) {
|
|
|
|
services, err := b.Services(ctx, state, true, opt)
|
2016-03-20 21:36:55 +00:00
|
|
|
if err != nil {
|
2017-09-12 10:52:43 +01:00
|
|
|
return nil, err
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(services) > 0 {
|
|
|
|
serv := services[0]
|
|
|
|
if ip := net.ParseIP(serv.Host); ip == nil {
|
2016-03-22 11:04:56 +00:00
|
|
|
records = append(records, serv.NewCNAME(state.QName(), serv.Host))
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-12 10:52:43 +01:00
|
|
|
return records, nil
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
|
2016-10-30 15:54:16 +00:00
|
|
|
// TXT returns TXT records from Backend or an error.
|
2022-02-22 09:38:57 -05:00
|
|
|
func TXT(ctx context.Context, b ServiceBackend, zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, truncated bool, err error) {
|
2022-04-04 14:59:16 -04:00
|
|
|
services, err := b.Services(ctx, state, false, opt)
|
2016-06-08 10:29:46 +01:00
|
|
|
if err != nil {
|
2022-02-22 09:38:57 -05:00
|
|
|
return nil, false, err
|
2016-06-08 10:29:46 +01:00
|
|
|
}
|
|
|
|
|
2020-01-04 00:16:04 +11:00
|
|
|
dup := make(map[string]struct{})
|
|
|
|
|
2016-06-08 10:29:46 +01:00
|
|
|
for _, serv := range services {
|
2020-01-04 00:16:04 +11:00
|
|
|
what, _ := serv.HostType()
|
|
|
|
|
|
|
|
switch what {
|
|
|
|
case dns.TypeCNAME:
|
|
|
|
if Name(state.Name()).Matches(dns.Fqdn(serv.Host)) {
|
|
|
|
// x CNAME x is a direct loop, don't add those
|
2022-04-04 15:00:17 -04:00
|
|
|
// in etcd/skydns w.x CNAME x is also direct loop due to the "recursive" nature of search results
|
2020-01-04 00:16:04 +11:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
newRecord := serv.NewCNAME(state.QName(), serv.Host)
|
|
|
|
if len(previousRecords) > 7 {
|
|
|
|
// don't add it, and just continue
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if dnsutil.DuplicateCNAME(newRecord, previousRecords) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if dns.IsSubDomain(zone, dns.Fqdn(serv.Host)) {
|
|
|
|
state1 := state.NewWithQuestion(serv.Host, state.QType())
|
|
|
|
state1.Zone = zone
|
2022-02-22 09:38:57 -05:00
|
|
|
nextRecords, tc, err := TXT(ctx, b, zone, state1, append(previousRecords, newRecord), opt)
|
|
|
|
if tc {
|
|
|
|
truncated = true
|
|
|
|
}
|
2020-01-04 00:16:04 +11:00
|
|
|
if err == nil {
|
|
|
|
// Not only have we found something we should add the CNAME and the IP addresses.
|
|
|
|
if len(nextRecords) > 0 {
|
|
|
|
records = append(records, newRecord)
|
|
|
|
records = append(records, nextRecords...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// This means we can not complete the CNAME, try to look else where.
|
|
|
|
target := newRecord.Target
|
|
|
|
// Lookup
|
|
|
|
m1, e1 := b.Lookup(ctx, state, target, state.QType())
|
|
|
|
if e1 != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Len(m1.Answer) > 0 here is well?
|
|
|
|
records = append(records, newRecord)
|
|
|
|
records = append(records, m1.Answer...)
|
|
|
|
continue
|
|
|
|
|
|
|
|
case dns.TypeTXT:
|
2022-04-04 14:59:16 -04:00
|
|
|
if _, ok := dup[serv.Text]; !ok {
|
|
|
|
dup[serv.Text] = struct{}{}
|
|
|
|
records = append(records, serv.NewTXT(state.QName()))
|
2020-01-04 00:16:04 +11:00
|
|
|
}
|
|
|
|
}
|
2016-06-08 10:29:46 +01:00
|
|
|
}
|
2020-01-04 00:16:04 +11:00
|
|
|
|
2022-02-22 09:38:57 -05:00
|
|
|
return records, truncated, nil
|
2016-06-08 10:29:46 +01:00
|
|
|
}
|
|
|
|
|
2016-10-30 15:54:16 +00:00
|
|
|
// PTR returns the PTR records from the backend, only services that have a domain name as host are included.
|
2019-03-26 14:37:30 +00:00
|
|
|
func PTR(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) (records []dns.RR, err error) {
|
|
|
|
services, err := b.Reverse(ctx, state, true, opt)
|
2016-03-20 21:36:55 +00:00
|
|
|
if err != nil {
|
2017-09-12 10:52:43 +01:00
|
|
|
return nil, err
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 13:19:22 +00:00
|
|
|
dup := make(map[string]struct{})
|
2018-06-12 03:23:25 +01:00
|
|
|
|
2016-03-20 21:36:55 +00:00
|
|
|
for _, serv := range services {
|
2016-10-30 15:54:16 +00:00
|
|
|
if ip := net.ParseIP(serv.Host); ip == nil {
|
2018-06-12 03:23:25 +01:00
|
|
|
if _, ok := dup[serv.Host]; !ok {
|
2018-12-08 13:19:22 +00:00
|
|
|
dup[serv.Host] = struct{}{}
|
2018-06-12 03:23:25 +01:00
|
|
|
records = append(records, serv.NewPTR(state.QName(), serv.Host))
|
|
|
|
}
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-12 10:52:43 +01:00
|
|
|
return records, nil
|
2016-03-20 21:36:55 +00:00
|
|
|
}
|
|
|
|
|
2016-10-30 15:54:16 +00:00
|
|
|
// NS returns NS records from the backend
|
2019-03-26 14:37:30 +00:00
|
|
|
func NS(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) (records, extra []dns.RR, err error) {
|
2016-04-13 08:03:56 +01:00
|
|
|
// NS record for this zone live in a special place, ns.dns.<zone>. Fake our lookup.
|
|
|
|
// only a tad bit fishy...
|
|
|
|
old := state.QName()
|
2016-04-26 17:57:11 +01:00
|
|
|
|
|
|
|
state.Clear()
|
2020-07-31 10:58:09 +02:00
|
|
|
state.Req.Question[0].Name = dnsutil.Join("ns.dns.", zone)
|
2019-03-26 14:37:30 +00:00
|
|
|
services, err := b.Services(ctx, state, false, opt)
|
2016-04-13 08:03:56 +01:00
|
|
|
if err != nil {
|
2017-09-12 10:52:43 +01:00
|
|
|
return nil, nil, err
|
2016-04-13 08:03:56 +01:00
|
|
|
}
|
2016-04-26 17:57:11 +01:00
|
|
|
// ... and reset
|
2016-04-13 08:03:56 +01:00
|
|
|
state.Req.Question[0].Name = old
|
|
|
|
|
2019-09-05 09:07:55 -04:00
|
|
|
seen := map[string]bool{}
|
|
|
|
|
2016-04-13 08:03:56 +01:00
|
|
|
for _, serv := range services {
|
2017-04-22 07:58:30 +01:00
|
|
|
what, ip := serv.HostType()
|
|
|
|
switch what {
|
2017-05-30 08:20:39 -04:00
|
|
|
case dns.TypeCNAME:
|
2017-09-12 10:52:43 +01:00
|
|
|
return nil, nil, fmt.Errorf("NS record must be an IP address: %s", serv.Host)
|
2017-04-22 07:58:30 +01:00
|
|
|
|
|
|
|
case dns.TypeA, dns.TypeAAAA:
|
2016-05-23 09:16:57 +01:00
|
|
|
serv.Host = msg.Domain(serv.Key)
|
2019-09-05 09:07:55 -04:00
|
|
|
ns := serv.NewNS(state.QName())
|
2021-05-04 00:48:51 +09:00
|
|
|
extra = append(extra, newAddress(serv, ns.Ns, ip, what))
|
2019-09-05 09:07:55 -04:00
|
|
|
if _, ok := seen[ns.Ns]; ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
seen[ns.Ns] = true
|
|
|
|
records = append(records, ns)
|
2016-04-13 08:03:56 +01:00
|
|
|
}
|
|
|
|
}
|
2017-09-12 10:52:43 +01:00
|
|
|
return records, extra, nil
|
2016-04-13 08:03:56 +01:00
|
|
|
}
|
|
|
|
|
2016-10-30 15:54:16 +00:00
|
|
|
// SOA returns a SOA record from the backend.
|
2019-03-26 14:37:30 +00:00
|
|
|
func SOA(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) ([]dns.RR, error) {
|
2018-11-16 14:42:49 +01:00
|
|
|
minTTL := b.MinTTL(state)
|
|
|
|
ttl := uint32(300)
|
|
|
|
if minTTL < ttl {
|
|
|
|
ttl = minTTL
|
|
|
|
}
|
|
|
|
|
|
|
|
header := dns.RR_Header{Name: zone, Rrtype: dns.TypeSOA, Ttl: ttl, Class: dns.ClassINET}
|
Allow debug queries to etcd middleware (#150)
With this you can retreive the raw data that the etcd middleware
used to create the reply. The debug data is put in TXT records
that are stuffed in the CH classs. This is only enabled if you
specify `debug` in the etcd stanza.
You can retrieve it by prefixing your query with 'o-o.debug.'
For instance:
; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost -p 1053 SRV o-o.debug.production.*.skydns.local
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47798
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 3
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;o-o.debug.production.*.skydns.local. IN SRV
;; ANSWER SECTION:
production.*.skydns.local. 154 IN SRV 10 50 8080 service1.example.com.
production.*.skydns.local. 154 IN SRV 10 50 8080 service2.example.com.
;; ADDITIONAL SECTION:
skydns.local.skydns.east.production.rails.1. 154 CH TXT "service1.example.com:8080(10,0,,false)[0,]"
skydns.local.skydns.west.production.rails.2. 154 CH TXT "service2.example.com:8080(10,0,,false)[0,]"
2016-05-22 21:16:26 +01:00
|
|
|
|
2020-07-31 10:58:09 +02:00
|
|
|
Mbox := dnsutil.Join(hostmaster, zone)
|
|
|
|
Ns := dnsutil.Join("ns.dns", zone)
|
2017-07-31 10:48:50 -07:00
|
|
|
|
Allow debug queries to etcd middleware (#150)
With this you can retreive the raw data that the etcd middleware
used to create the reply. The debug data is put in TXT records
that are stuffed in the CH classs. This is only enabled if you
specify `debug` in the etcd stanza.
You can retrieve it by prefixing your query with 'o-o.debug.'
For instance:
; <<>> DiG 9.10.3-P4-Ubuntu <<>> @localhost -p 1053 SRV o-o.debug.production.*.skydns.local
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 47798
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 3
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;o-o.debug.production.*.skydns.local. IN SRV
;; ANSWER SECTION:
production.*.skydns.local. 154 IN SRV 10 50 8080 service1.example.com.
production.*.skydns.local. 154 IN SRV 10 50 8080 service2.example.com.
;; ADDITIONAL SECTION:
skydns.local.skydns.east.production.rails.1. 154 CH TXT "service1.example.com:8080(10,0,,false)[0,]"
skydns.local.skydns.west.production.rails.2. 154 CH TXT "service2.example.com:8080(10,0,,false)[0,]"
2016-05-22 21:16:26 +01:00
|
|
|
soa := &dns.SOA{Hdr: header,
|
2017-07-31 10:48:50 -07:00
|
|
|
Mbox: Mbox,
|
|
|
|
Ns: Ns,
|
2017-11-01 10:11:34 +00:00
|
|
|
Serial: b.Serial(state),
|
2016-04-19 11:13:24 +01:00
|
|
|
Refresh: 7200,
|
|
|
|
Retry: 1800,
|
|
|
|
Expire: 86400,
|
2018-11-16 14:42:49 +01:00
|
|
|
Minttl: minTTL,
|
2016-04-12 23:26:46 +01:00
|
|
|
}
|
2017-09-12 10:52:43 +01:00
|
|
|
return []dns.RR{soa}, nil
|
2016-03-22 10:29:48 +00:00
|
|
|
}
|
2016-10-30 15:54:16 +00:00
|
|
|
|
|
|
|
// BackendError writes an error response to the client.
|
2019-03-26 14:37:30 +00:00
|
|
|
func BackendError(ctx context.Context, b ServiceBackend, zone string, rcode int, state request.Request, err error, opt Options) (int, error) {
|
2016-10-30 15:54:16 +00:00
|
|
|
m := new(dns.Msg)
|
|
|
|
m.SetRcode(state.Req, rcode)
|
2018-12-30 17:05:08 +01:00
|
|
|
m.Authoritative = true
|
2019-03-26 14:37:30 +00:00
|
|
|
m.Ns, _ = SOA(ctx, b, zone, state, opt)
|
2017-09-12 10:52:43 +01:00
|
|
|
|
2016-10-30 15:54:16 +00:00
|
|
|
state.W.WriteMsg(m)
|
|
|
|
// Return success as the rcode to signal we have written to the client.
|
2016-12-20 18:58:05 +00:00
|
|
|
return dns.RcodeSuccess, err
|
2016-10-30 15:54:16 +00:00
|
|
|
}
|
|
|
|
|
2017-04-22 07:58:30 +01:00
|
|
|
func newAddress(s msg.Service, name string, ip net.IP, what uint16) dns.RR {
|
|
|
|
hdr := dns.RR_Header{Name: name, Rrtype: what, Class: dns.ClassINET, Ttl: s.TTL}
|
|
|
|
|
|
|
|
if what == dns.TypeA {
|
|
|
|
return &dns.A{Hdr: hdr, A: ip}
|
|
|
|
}
|
|
|
|
// Should always be dns.TypeAAAA
|
|
|
|
return &dns.AAAA{Hdr: hdr, AAAA: ip}
|
|
|
|
}
|
|
|
|
|
2019-02-17 15:31:12 +07:00
|
|
|
// checkForApex checks the special apex.dns directory for records that will be returned as A or AAAA.
|
2019-03-26 14:37:30 +00:00
|
|
|
func checkForApex(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) ([]msg.Service, error) {
|
2018-04-28 10:03:35 +01:00
|
|
|
if state.Name() != zone {
|
2019-03-26 14:37:30 +00:00
|
|
|
return b.Services(ctx, state, false, opt)
|
2018-04-28 10:03:35 +01:00
|
|
|
}
|
2018-04-27 20:36:58 +02:00
|
|
|
|
2018-04-28 10:03:35 +01:00
|
|
|
// If the zone name itself is queried we fake the query to search for a special entry
|
|
|
|
// this is equivalent to the NS search code.
|
|
|
|
old := state.QName()
|
|
|
|
state.Clear()
|
2018-09-22 15:12:02 +01:00
|
|
|
state.Req.Question[0].Name = dnsutil.Join("apex.dns", zone)
|
2018-04-28 10:03:35 +01:00
|
|
|
|
2019-03-26 14:37:30 +00:00
|
|
|
services, err := b.Services(ctx, state, false, opt)
|
2018-04-28 10:03:35 +01:00
|
|
|
if err == nil {
|
2018-04-27 20:36:58 +02:00
|
|
|
state.Req.Question[0].Name = old
|
2018-04-28 10:03:35 +01:00
|
|
|
return services, err
|
2018-04-27 20:36:58 +02:00
|
|
|
}
|
|
|
|
|
2018-04-28 10:03:35 +01:00
|
|
|
state.Req.Question[0].Name = old
|
2019-03-26 14:37:30 +00:00
|
|
|
return b.Services(ctx, state, false, opt)
|
2018-04-27 20:36:58 +02:00
|
|
|
}
|
|
|
|
|
2018-06-18 11:16:56 +01:00
|
|
|
// item holds records.
|
|
|
|
type item struct {
|
|
|
|
name string // name of the record (either owner or something else unique).
|
|
|
|
port uint16 // port of the record (used for address records, A and AAAA).
|
|
|
|
addr string // address of the record (A and AAAA).
|
|
|
|
}
|
|
|
|
|
|
|
|
// isDuplicate uses m to see if the combo (name, addr, port) already exists. If it does
|
|
|
|
// not exist already IsDuplicate will also add the record to the map.
|
2018-12-08 13:19:22 +00:00
|
|
|
func isDuplicate(m map[item]struct{}, name, addr string, port uint16) bool {
|
2018-06-18 11:16:56 +01:00
|
|
|
if addr != "" {
|
|
|
|
_, ok := m[item{name, 0, addr}]
|
|
|
|
if !ok {
|
2018-12-08 13:19:22 +00:00
|
|
|
m[item{name, 0, addr}] = struct{}{}
|
2018-06-18 11:16:56 +01:00
|
|
|
}
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
_, ok := m[item{name, port, ""}]
|
|
|
|
if !ok {
|
2018-12-08 13:19:22 +00:00
|
|
|
m[item{name, port, ""}] = struct{}{}
|
2018-06-18 11:16:56 +01:00
|
|
|
}
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
2017-11-01 10:11:34 +00:00
|
|
|
const hostmaster = "hostmaster"
|