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,]"
This commit is contained in:
Miek Gieben 2016-05-22 21:16:26 +01:00
parent d35394a8df
commit c30671f4c0
9 changed files with 323 additions and 62 deletions

View file

@ -68,6 +68,8 @@ func etcdParse(c *Controller) (etcd.Etcd, bool, error) {
switch c.Val() {
case "stubzones":
stubzones = true
case "debug":
etc.Debug = true
case "path":
if !c.NextArg() {
return etcd.Etcd{}, false, c.ArgErr()
@ -103,6 +105,8 @@ func etcdParse(c *Controller) (etcd.Etcd, bool, error) {
switch c.Val() {
case "stubzones":
stubzones = true
case "debug":
etc.Debug = true
case "path":
if !c.NextArg() {
return etcd.Etcd{}, false, c.ArgErr()

View file

@ -27,6 +27,7 @@ etcd [zones...] {
endpoint endpoint...
upstream address...
tls cert key cacert
debug
}
~~~
@ -38,6 +39,13 @@ etcd [zones...] {
pointing to external names. If you want CoreDNS to act as a proxy for clients you'll need to add
the proxy middleware.
* `tls` followed the cert, key and the CA's cert filenames.
* `debug` allow debug queries. Prefix the name with `o-o.debug.` to reveive extra information in the
additional section of the reply in the form of text records:
skydns.test.skydns.dom.a. 300 CH TXT "127.0.0.1:0(10,0,,false)[0,]"
This shows the complete key as the owername, the rdata of the TXT record has:
`host:port(priority,weight,txt content,mail)[targetstrip,group]`.
## Examples

38
middleware/etcd/debug.go Normal file
View file

@ -0,0 +1,38 @@
package etcd
import (
"strings"
"github.com/miekg/coredns/middleware/etcd/msg"
"github.com/miekg/dns"
)
const debugName = "o-o.debug."
// isDebug checks if name is a debugging name, i.e. starts with o-o.debug.
// it return the empty string if it is not a debug message, otherwise it will return the
// name with o-o.debug. stripped off.
func isDebug(name string) string {
if len(name) == len(debugName) {
return ""
}
debug := strings.HasPrefix(name, debugName)
if !debug {
return ""
}
return name[len(debugName):]
}
// servicesToTxt puts debug in TXT RRs.
func servicesToTxt(debug []msg.Service) []dns.RR {
if debug == nil {
return nil
}
rr := make([]dns.RR, len(debug))
for i, d := range debug {
rr[i] = d.RR()
}
return rr
}

View file

@ -0,0 +1,147 @@
package etcd
import (
"sort"
"testing"
"github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware/etcd/msg"
"github.com/miekg/coredns/middleware/test"
"github.com/miekg/dns"
)
func TestisDebug(t *testing.T) {
if ok := isDebug("o-o.debug.miek.nl."); ok != "miek.nl." {
t.Errorf("expected o-o.debug.miek.nl. to be debug")
}
if ok := isDebug("o-o.Debug.miek.nl."); ok != "miek.nl." {
t.Errorf("expected o-o.Debug.miek.nl. to be debug")
}
if ok := isDebug("i-o.Debug.miek.nl."); ok != "" {
t.Errorf("expected i-o.Debug.miek.nl. to be non-debug")
}
if ok := isDebug("i-o.Debug."); ok != "" {
t.Errorf("expected o-o.Debug. to be non-debug")
}
}
func TestDebugLookup(t *testing.T) {
for _, serv := range servicesDebug {
set(t, etc, serv.Key, 0, serv)
defer delete(t, etc, serv.Key)
}
etc.Debug = true
defer func() { etc.Debug = false }()
for _, tc := range dnsTestCasesDebug {
m := tc.Msg()
rec := middleware.NewResponseRecorder(&test.ResponseWriter{})
_, err := etc.ServeDNS(ctx, rec, m)
if err != nil {
t.Errorf("expected no error, got %v\n", err)
continue
}
resp := rec.Msg()
sort.Sort(test.RRSet(resp.Answer))
sort.Sort(test.RRSet(resp.Ns))
sort.Sort(test.RRSet(resp.Extra))
if !test.Header(t, tc, resp) {
t.Logf("%v\n", resp)
continue
}
if !test.Section(t, tc, test.Answer, resp.Answer) {
t.Logf("%v\n", resp)
}
if !test.Section(t, tc, test.Ns, resp.Ns) {
t.Logf("%v\n", resp)
}
if !test.Section(t, tc, test.Extra, resp.Extra) {
t.Logf("%v\n", resp)
}
}
}
func TestDebugLookupFalse(t *testing.T) {
for _, serv := range servicesDebug {
set(t, etc, serv.Key, 0, serv)
defer delete(t, etc, serv.Key)
}
for _, tc := range dnsTestCasesDebugFalse {
m := tc.Msg()
rec := middleware.NewResponseRecorder(&test.ResponseWriter{})
_, err := etc.ServeDNS(ctx, rec, m)
if err != nil {
t.Errorf("expected no error, got %v\n", err)
continue
}
resp := rec.Msg()
sort.Sort(test.RRSet(resp.Answer))
sort.Sort(test.RRSet(resp.Ns))
sort.Sort(test.RRSet(resp.Extra))
if !test.Header(t, tc, resp) {
t.Logf("%v\n", resp)
continue
}
if !test.Section(t, tc, test.Answer, resp.Answer) {
t.Logf("%v\n", resp)
}
if !test.Section(t, tc, test.Ns, resp.Ns) {
t.Logf("%v\n", resp)
}
if !test.Section(t, tc, test.Extra, resp.Extra) {
t.Logf("%v\n", resp)
}
}
}
var servicesDebug = []*msg.Service{
{Host: "127.0.0.1", Key: "a.dom.skydns.test."},
{Host: "127.0.0.2", Key: "b.sub.dom.skydns.test."},
}
var dnsTestCasesDebug = []test.Case{
{
Qname: "o-o.debug.dom.skydns.test.", Qtype: dns.TypeA,
Answer: []dns.RR{
test.A("dom.skydns.test. 300 IN A 127.0.0.1"),
test.A("dom.skydns.test. 300 IN A 127.0.0.2"),
},
Extra: []dns.RR{
test.TXT(`skydns.test.skydns.dom.a. 300 CH TXT "127.0.0.1:0(10,0,,false)[0,]"`),
test.TXT(`skydns.test.skydns.dom.sub.b. 300 CH TXT "127.0.0.2:0(10,0,,false)[0,]"`),
},
},
{
Qname: "o-o.debug.dom.skydns.test.", Qtype: dns.TypeTXT,
Ns: []dns.RR{
test.SOA("skydns.test. 300 IN SOA ns.dns.skydns.test. hostmaster.skydns.test. 1463943291 7200 1800 86400 60"),
},
Extra: []dns.RR{
test.TXT(`skydns.test.skydns.dom.a. 300 CH TXT "127.0.0.1:0(10,0,,false)[0,]"`),
test.TXT(`skydns.test.skydns.dom.sub.b. 300 CH TXT "127.0.0.2:0(10,0,,false)[0,]"`),
},
},
}
var dnsTestCasesDebugFalse = []test.Case{
{
Qname: "o-o.debug.dom.skydns.test.", Qtype: dns.TypeA,
Rcode: dns.RcodeNameError,
Ns: []dns.RR{
test.SOA("skydns.test. 300 IN SOA ns.dns.skydns.test. hostmaster.skydns.test. 1463943291 7200 1800 86400 60"),
},
},
{
Qname: "o-o.debug.dom.skydns.test.", Qtype: dns.TypeTXT,
Rcode: dns.RcodeNameError,
Ns: []dns.RR{
test.SOA("skydns.test. 300 IN SOA ns.dns.skydns.test. hostmaster.skydns.test. 1463943291 7200 1800 86400 60"),
},
},
}

View file

@ -24,6 +24,8 @@ type Etcd struct {
Ctx context.Context
Inflight *singleflight.Group
Stubmap *map[string]proxy.Proxy // List of proxies for stub resolving.
Debug bool // Do we allow debug queries.
debug string // Should we return debugging information, if so, contains original qname.
}
// Records looks up records in etcd. If exact is true, it will lookup just

View file

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware/etcd/msg"
"github.com/miekg/dns"
"golang.org/x/net/context"
@ -14,11 +15,18 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
if state.QClass() != dns.ClassINET {
return dns.RcodeServerFailure, fmt.Errorf("can only deal with ClassINET")
}
name := state.Name()
if e.Debug {
if debug := isDebug(name); debug != "" {
e.debug = r.Question[0].Name
state.Clear()
state.Req.Question[0].Name = debug
}
}
// We need to check stubzones first, because we may get a request for a zone we
// are not auth. for *but* do have a stubzone forward for. If we do the stubzone
// handler will handle the request.
name := state.Name()
if e.Stubmap != nil && len(*e.Stubmap) > 0 {
for zone, _ := range *e.Stubmap {
if middleware.Name(zone).Matches(name) {
@ -36,52 +44,62 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
return e.Next.ServeDNS(ctx, w, r)
}
m := new(dns.Msg)
m.SetReply(r)
m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
var (
records, extra []dns.RR
debug []msg.Service
err error
)
switch state.Type() {
case "A":
records, err = e.A(zone, state, nil)
records, debug, err = e.A(zone, state, nil)
case "AAAA":
records, err = e.AAAA(zone, state, nil)
records, debug, err = e.AAAA(zone, state, nil)
case "TXT":
records, err = e.TXT(zone, state)
records, debug, err = e.TXT(zone, state)
case "CNAME":
records, err = e.CNAME(zone, state)
records, debug, err = e.CNAME(zone, state)
case "MX":
records, extra, err = e.MX(zone, state)
records, extra, debug, err = e.MX(zone, state)
case "SRV":
records, extra, err = e.SRV(zone, state)
records, extra, debug, err = e.SRV(zone, state)
case "SOA":
records = []dns.RR{e.SOA(zone, state)}
records, debug, err = e.SOA(zone, state)
case "NS":
if state.Name() == zone {
records, extra, err = e.NS(zone, state)
records, extra, debug, err = e.NS(zone, state)
break
}
fallthrough
default:
// Do a fake A lookup, so we can distinguish betwen NODATA and NXDOMAIN
_, err = e.A(zone, state, nil)
// Do a fake A lookup, so we can distinguish between NODATA and NXDOMAIN
_, debug, err = e.A(zone, state, nil)
}
if e.debug != "" {
// substitute this name with the original when we return the request.
state.Clear()
state.Req.Question[0].Name = e.debug
}
if isEtcdNameError(err) {
return e.Err(zone, dns.RcodeNameError, state)
return e.Err(zone, dns.RcodeNameError, state, debug)
}
if err != nil {
return dns.RcodeServerFailure, err
}
if len(records) == 0 {
return e.Err(zone, dns.RcodeSuccess, state)
return e.Err(zone, dns.RcodeSuccess, state, debug)
}
m := new(dns.Msg)
m.SetReply(r)
m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true
m.Answer = append(m.Answer, records...)
m.Extra = append(m.Extra, extra...)
if e.debug != "" {
m.Extra = append(m.Extra, servicesToTxt(debug)...)
}
m = dedup(m)
state.SizeAndDo(m)
@ -90,11 +108,12 @@ func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i
return dns.RcodeSuccess, nil
}
// NoData write a nodata response to the client.
func (e Etcd) Err(zone string, rcode int, state middleware.State) (int, error) {
// Err write an error response to the client.
func (e Etcd) Err(zone string, rcode int, state middleware.State, debug []msg.Service) (int, error) {
m := new(dns.Msg)
m.SetRcode(state.Req, rcode)
m.Ns = []dns.RR{e.SOA(zone, state)}
m.Ns, _, _ = e.SOA(zone, state)
m.Extra = servicesToTxt(debug)
state.SizeAndDo(m)
state.W.WriteMsg(m)
return rcode, nil

View file

@ -12,19 +12,22 @@ import (
"github.com/miekg/dns"
)
func (e Etcd) records(state middleware.State, exact bool) ([]msg.Service, error) {
services, err := e.Records(state.Name(), exact)
func (e Etcd) records(state middleware.State, exact bool) (services, debug []msg.Service, err error) {
services, err = e.Records(state.Name(), exact)
if err != nil {
return nil, err
return
}
if e.debug != "" {
debug = services
}
services = msg.Group(services)
return services, nil
return
}
func (e Etcd) A(zone string, state middleware.State, previousRecords []dns.RR) (records []dns.RR, err error) {
services, err := e.records(state, false)
func (e Etcd) A(zone string, state middleware.State, previousRecords []dns.RR) (records []dns.RR, debug []msg.Service, err error) {
services, debug, err := e.records(state, false)
if err != nil {
return nil, err
return nil, debug, err
}
for _, serv := range services {
@ -47,13 +50,14 @@ func (e Etcd) A(zone string, state middleware.State, previousRecords []dns.RR) (
}
state1 := copyState(state, serv.Host, state.QType())
nextRecords, err := e.A(zone, state1, append(previousRecords, newRecord))
nextRecords, nextDebug, err := e.A(zone, state1, append(previousRecords, newRecord))
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...)
debug = append(debug, nextDebug...)
}
continue
}
@ -77,13 +81,13 @@ func (e Etcd) A(zone string, state middleware.State, previousRecords []dns.RR) (
// nodata?
}
}
return records, nil
return records, debug, nil
}
func (e Etcd) AAAA(zone string, state middleware.State, previousRecords []dns.RR) (records []dns.RR, err error) {
services, err := e.records(state, false)
func (e Etcd) AAAA(zone string, state middleware.State, previousRecords []dns.RR) (records []dns.RR, debug []msg.Service, err error) {
services, debug, err := e.records(state, false)
if err != nil {
return nil, err
return nil, debug, err
}
for _, serv := range services {
@ -106,13 +110,14 @@ func (e Etcd) AAAA(zone string, state middleware.State, previousRecords []dns.RR
}
state1 := copyState(state, serv.Host, state.QType())
nextRecords, err := e.AAAA(zone, state1, append(previousRecords, newRecord))
nextRecords, nextDebug, err := e.AAAA(zone, state1, append(previousRecords, newRecord))
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...)
debug = append(debug, nextDebug...)
}
continue
}
@ -137,15 +142,15 @@ func (e Etcd) AAAA(zone string, state middleware.State, previousRecords []dns.RR
records = append(records, serv.NewAAAA(state.QName(), ip.To16()))
}
}
return records, nil
return records, debug, nil
}
// SRV returns SRV records from etcd.
// If the Target is not a name but an IP address, a name is created on the fly.
func (e Etcd) SRV(zone string, state middleware.State) (records []dns.RR, extra []dns.RR, err error) {
services, err := e.records(state, false)
func (e Etcd) SRV(zone string, state middleware.State) (records, extra []dns.RR, debug []msg.Service, err error) {
services, debug, err := e.records(state, false)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
// Looping twice to get the right weight vs priority
@ -201,9 +206,10 @@ func (e Etcd) SRV(zone string, state middleware.State) (records []dns.RR, extra
// Internal name, we should have some info on them, either v4 or v6
// Clients expect a complete answer, because we are a recursor in their view.
state1 := copyState(state, srv.Target, dns.TypeA)
addr, e1 := e.A(zone, state1, nil)
addr, debugAddr, e1 := e.A(zone, state1, nil)
if e1 == nil {
extra = append(extra, addr...)
debug = append(debug, debugAddr...)
}
// e.AAA(zone, state1, nil) as well...?
case ip.To4() != nil:
@ -220,15 +226,15 @@ func (e Etcd) SRV(zone string, state middleware.State) (records []dns.RR, extra
extra = append(extra, serv.NewAAAA(srv.Target, ip.To16()))
}
}
return records, extra, nil
return records, extra, debug, nil
}
// MX returns MX records from etcd.
// If the Target is not a name but an IP address, a name is created on the fly.
func (e Etcd) MX(zone string, state middleware.State) (records []dns.RR, extra []dns.RR, err error) {
services, err := e.records(state, false)
func (e Etcd) MX(zone string, state middleware.State) (records, extra []dns.RR, debug []msg.Service, err error) {
services, debug, err := e.records(state, false)
if err != nil {
return nil, nil, err
return nil, nil, debug, err
}
lookup := make(map[string]bool)
@ -265,9 +271,10 @@ func (e Etcd) MX(zone string, state middleware.State) (records []dns.RR, extra [
}
// Internal name
state1 := copyState(state, mx.Mx, dns.TypeA)
addr, e1 := e.A(zone, state1, nil)
addr, debugAddr, e1 := e.A(zone, state1, nil)
if e1 == nil {
extra = append(extra, addr...)
debug = append(debug, debugAddr...)
}
// e.AAAA as well
case ip.To4() != nil:
@ -280,13 +287,13 @@ func (e Etcd) MX(zone string, state middleware.State) (records []dns.RR, extra [
extra = append(extra, serv.NewAAAA(serv.Host, ip.To16()))
}
}
return records, extra, nil
return records, extra, debug, nil
}
func (e Etcd) CNAME(zone string, state middleware.State) (records []dns.RR, err error) {
services, err := e.records(state, true)
func (e Etcd) CNAME(zone string, state middleware.State) (records []dns.RR, debug []msg.Service, err error) {
services, debug, err := e.records(state, true)
if err != nil {
return nil, err
return nil, debug, err
}
if len(services) > 0 {
@ -295,13 +302,13 @@ func (e Etcd) CNAME(zone string, state middleware.State) (records []dns.RR, err
records = append(records, serv.NewCNAME(state.QName(), serv.Host))
}
}
return records, nil
return records, debug, nil
}
func (e Etcd) TXT(zone string, state middleware.State) (records []dns.RR, err error) {
services, err := e.records(state, false)
func (e Etcd) TXT(zone string, state middleware.State) (records []dns.RR, debug []msg.Service, err error) {
services, debug, err := e.records(state, false)
if err != nil {
return nil, err
return nil, debug, err
}
for _, serv := range services {
@ -310,19 +317,19 @@ func (e Etcd) TXT(zone string, state middleware.State) (records []dns.RR, err er
}
records = append(records, serv.NewTXT(state.QName()))
}
return records, nil
return records, debug, nil
}
func (e Etcd) NS(zone string, state middleware.State) (records, extra []dns.RR, err error) {
func (e Etcd) NS(zone string, state middleware.State) (records, extra []dns.RR, debug []msg.Service, err error) {
// NS record for this zone live in a special place, ns.dns.<zone>. Fake our lookup.
// only a tad bit fishy...
old := state.QName()
state.Clear()
state.Req.Question[0].Name = "ns.dns." + zone
services, err := e.records(state, false)
services, debug, err := e.records(state, false)
if err != nil {
return nil, nil, err
return nil, nil, debug, err
}
// ... and reset
state.Req.Question[0].Name = old
@ -331,7 +338,7 @@ func (e Etcd) NS(zone string, state middleware.State) (records, extra []dns.RR,
ip := net.ParseIP(serv.Host)
switch {
case ip == nil:
return nil, nil, fmt.Errorf("NS record must be an IP address: %s", serv.Host)
return nil, nil, debug, fmt.Errorf("NS record must be an IP address: %s", serv.Host)
case ip.To4() != nil:
serv.Host = e.Domain(serv.Key)
records = append(records, serv.NewNS(state.QName()))
@ -342,13 +349,14 @@ func (e Etcd) NS(zone string, state middleware.State) (records, extra []dns.RR,
extra = append(extra, serv.NewAAAA(serv.Host, ip.To16()))
}
}
return records, extra, nil
return records, extra, debug, nil
}
// SOA Record returns a SOA record.
func (e Etcd) SOA(zone string, state middleware.State) *dns.SOA {
func (e Etcd) SOA(zone string, state middleware.State) ([]dns.RR, []msg.Service, error) {
header := dns.RR_Header{Name: zone, Rrtype: dns.TypeSOA, Ttl: 300, Class: dns.ClassINET}
return &dns.SOA{Hdr: header,
soa := &dns.SOA{Hdr: header,
Mbox: "hostmaster." + zone,
Ns: "ns.dns." + zone,
Serial: uint32(time.Now().Unix()),
@ -357,10 +365,10 @@ func (e Etcd) SOA(zone string, state middleware.State) *dns.SOA {
Expire: 86400,
Minttl: 60,
}
// TODO(miek): fake some msg.Service here when returning.
return []dns.RR{soa}, nil, nil
}
// TODO(miek): DNSKEY and friends... intercepted by the DNSSEC middleware?
func isDuplicateCNAME(r *dns.CNAME, records []dns.RR) bool {
for _, rec := range records {
if v, ok := rec.(*dns.CNAME); ok {
@ -372,6 +380,7 @@ func isDuplicateCNAME(r *dns.CNAME, records []dns.RR) bool {
return false
}
// TODO(miek): Move to middleware?
func copyState(state middleware.State, target string, typ uint16) middleware.State {
state1 := middleware.State{W: state.W, Req: state.Req.Copy()}
state1.Req.Question[0] = dns.Question{dns.Fqdn(target), dns.ClassINET, typ}

View file

@ -1,6 +1,7 @@
package msg
import (
"fmt"
"net"
"strings"
@ -35,6 +36,37 @@ type Service struct {
Key string `json:"-"`
}
// RR returns an RR representation of s. It is in a condensed form to minimize space
// when this is returned in a DNS message.
// The RR will look like:
// skydns.local.skydns.east.production.rails.1. 300 CH TXT "service1.example.com:8080(10,0,,false)[0,]"
// etcd Key Ttl Host:Port < see below >
// between parens: (Priority, Weight, Text (only first 200 bytes!), Mail)
// between blockquotes: [TargetStrip,Group]
// If the record is synthesised by CoreDNS (i.e. no lookup in etcd happened):
//
// skydns.local.skydns.east.production.rails.1. 300 CH TXT "service1.example.com:8080(10,0,,false)[0,]"
//
func (s *Service) RR() *dns.TXT {
l := len(s.Text)
if l > 200 {
l = 200
}
t := new(dns.TXT)
t.Hdr.Class = dns.ClassCHAOS
t.Hdr.Ttl = s.Ttl
t.Hdr.Rrtype = dns.TypeTXT
// TODO(miek): key guaranteerd to be > 1?
t.Hdr.Name = strings.Replace(s.Key[1:], "/", ".", -1) + "." // TODO(miek): slightly more like etcd.Domain()
t.Txt = make([]string, 1)
t.Txt[0] = fmt.Sprintf("%s:%d(%d,%d,%s,%t)[%d,%s]",
s.Host, s.Port,
s.Priority, s.Weight, s.Text[:l], s.Mail,
s.TargetStrip, s.Group)
return t
}
// NewSRV returns a new SRV record based on the Service.
func (s *Service) NewSRV(name string, weight uint16) *dns.SRV {
host := targetStrip(dns.Fqdn(s.Host), s.TargetStrip)

View file

@ -190,7 +190,8 @@ func (s *State) Type() string { return dns.Type(s.Req.Question[0].Qtype).String(
func (s *State) QType() uint16 { return s.Req.Question[0].Qtype }
// Name returns the name of the question in the request. Note
// this name will always have a closing dot and will be lower cased.
// this name will always have a closing dot and will be lower cased. After a call Name
// the value will be cached. To clear this caching call Clear.
func (s *State) Name() string {
if s.name != "" {
return s.name
@ -222,6 +223,7 @@ func (s *State) Clear() {
}
const (
// TODO(miek): make this less awkward.
doTrue = 1
doFalse = 2
)