more etcd stuff

This commit is contained in:
Miek Gieben
2016-03-20 21:36:55 +00:00
parent 0c94de4f71
commit b6341e8b63
9 changed files with 609 additions and 206 deletions

View File

@@ -8,24 +8,24 @@ import (
"net/http"
"time"
etcdc "github.com/coreos/etcd/client"
"github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware/file"
"github.com/miekg/coredns/middleware/etcd"
etcdc "github.com/coreos/etcd/client"
)
const defaultAddress = "http://127.0.0.1:2379"
const defaultEndpoint = "http://127.0.0.1:2379"
// Etcd sets up the etcd middleware.
func Etcd(c *Controller) (middleware.Middleware, error) {
keysapi, err := etcdParse(c)
client, err := etcdParse(c)
if err != nil {
return nil, err
}
return func(next middleware.Handler) middleware.Handler {
return file.File{Next: next, Zones: zones}
return etcd.NewEtcd(client, next, c.ServerBlockHosts)
}, nil
}
func etcdParse(c *Controller) (etcdc.KeysAPI, error) {
@@ -33,43 +33,30 @@ func etcdParse(c *Controller) (etcdc.KeysAPI, error) {
if c.Val() == "etcd" {
// etcd [address...]
if !c.NextArg() {
return file.Zones{}, c.ArgErr()
// TODO(certs) and friends, this is client side
client, err := newEtcdClient([]string{defaultEndpoint}, "", "", "")
return client, err
}
args1 := c.RemainingArgs()
fileName := c.Val()
origin := c.ServerBlockHosts[c.ServerBlockHostIndex]
if c.NextArg() {
c.Next()
origin = c.Val()
}
// normalize this origin
origin = middleware.Host(origin).StandardHost()
zone, err := parseZone(origin, fileName)
if err == nil {
z[origin] = zone
}
names = append(names, origin)
client, err := newEtcdClient(c.RemainingArgs(), "", "", "")
return client, err
}
}
return file.Zones{Z: z, Names: names}, nil
return nil, nil
}
func newEtcdClient(machines []string, tlsCert, tlsKey, tlsCACert string) (etcd.KeysAPI, error) {
etcdCfg := etcd.Config{
Endpoints: machines,
func newEtcdClient(endpoints []string, tlsCert, tlsKey, tlsCACert string) (etcdc.KeysAPI, error) {
etcdCfg := etcdc.Config{
Endpoints: endpoints,
Transport: newHTTPSTransport(tlsCert, tlsKey, tlsCACert),
}
cli, err := etcd.New(etcdCfg)
cli, err := etcdc.New(etcdCfg)
if err != nil {
return nil, err
}
return etcd.NewKeysAPI(cli), nil
return etcdc.NewKeysAPI(cli), nil
}
func newHTTPSTransport(tlsCertFile, tlsKeyFile, tlsCACertFile string) etcd.CancelableTransport {
func newHTTPSTransport(tlsCertFile, tlsKeyFile, tlsCACertFile string) etcdc.CancelableTransport {
var cc *tls.Config = nil
if tlsCertFile != "" && tlsKeyFile != "" {

View File

@@ -6,6 +6,7 @@ import (
"github.com/miekg/coredns/middleware"
"github.com/miekg/coredns/middleware/file"
"github.com/miekg/dns"
)

View File

@@ -1,164 +0,0 @@
// Package etcd provides the etcd server Backend implementation,
package etcd
import (
"encoding/json"
"fmt"
"strings"
"github.com/miekg/coredns/middleware/etcd/msg"
etcdc "github.com/coreos/etcd/client"
)
const (
priority = 10 // default priority when nothing is set
ttl = 3600 // default ttl when nothing is set
)
func (g *Backend) Records(name string, exact bool) ([]msg.Service, error) {
path, star := msg.PathWithWildcard(name)
r, err := g.get(path, true)
if err != nil {
return nil, err
}
segments := strings.Split(msg.Path(name), "/")
switch {
case exact && r.Node.Dir:
return nil, nil
case r.Node.Dir:
return g.loopNodes(r.Node.Nodes, segments, star, nil)
default:
return g.loopNodes([]*etcdc.Node{r.Node}, segments, false, nil)
}
}
func (g *Backend) ReverseRecord(name string) (*msg.Service, error) {
path, star := msg.PathWithWildcard(name)
if star {
return nil, fmt.Errorf("reverse can not contain wildcards")
}
r, err := g.get(path, true)
if err != nil {
return nil, err
}
if r.Node.Dir {
return nil, fmt.Errorf("reverse must not be a directory")
}
segments := strings.Split(msg.Path(name), "/")
records, err := g.loopNodes([]*etcdc.Node{r.Node}, segments, false, nil)
if err != nil {
return nil, err
}
if len(records) != 1 {
return nil, fmt.Errorf("must be only one service record")
}
return &records[0], nil
}
// get is a wrapper for client.Get that uses SingleInflight to suppress multiple
// outstanding queries.
func (g *Backend) get(path string, recursive bool) (*etcdc.Response, error) {
resp, err := g.inflight.Do(path, func() (interface{}, error) {
r, e := g.client.Get(g.ctx, path, &etcdc.GetOptions{Sort: false, Recursive: recursive})
if e != nil {
return nil, e
}
return r, e
})
if err != nil {
return nil, err
}
return resp.(*etcdc.Response), err
}
type bareService struct {
Host string
Port int
Priority int
Weight int
Text string
}
// skydns/local/skydns/east/staging/web
// skydns/local/skydns/west/production/web
//
// skydns/local/skydns/*/*/web
// skydns/local/skydns/*/web
// loopNodes recursively loops through the nodes and returns all the values. The nodes' keyname
// will be match against any wildcards when star is true.
func (g *Backend) loopNodes(ns []*etcdc.Node, nameParts []string, star bool, bx map[bareService]bool) (sx []msg.Service, err error) {
if bx == nil {
bx = make(map[bareService]bool)
}
Nodes:
for _, n := range ns {
if n.Dir {
nodes, err := g.loopNodes(n.Nodes, nameParts, star, bx)
if err != nil {
return nil, err
}
sx = append(sx, nodes...)
continue
}
if star {
keyParts := strings.Split(n.Key, "/")
for i, n := range nameParts {
if i > len(keyParts)-1 {
// name is longer than key
continue Nodes
}
if n == "*" || n == "any" {
continue
}
if keyParts[i] != n {
continue Nodes
}
}
}
serv := new(msg.Service)
if err := json.Unmarshal([]byte(n.Value), serv); err != nil {
return nil, err
}
b := bareService{serv.Host, serv.Port, serv.Priority, serv.Weight, serv.Text}
if _, ok := bx[b]; ok {
continue
}
bx[b] = true
serv.Key = n.Key
serv.Ttl = g.calculateTtl(n, serv)
if serv.Priority == 0 {
serv.Priority = priority
}
sx = append(sx, *serv)
}
return sx, nil
}
// calculateTtl returns the smaller of the etcd TTL and the service's
// TTL. If neither of these are set (have a zero value), the server
// default is used.
func (g *Backend) calculateTtl(node *etcdc.Node, serv *msg.Service) uint32 {
etcdTtl := uint32(node.TTL)
if etcdTtl == 0 && serv.Ttl == 0 {
return ttl
}
if etcdTtl == 0 {
return serv.Ttl
}
if serv.Ttl == 0 {
return etcdTtl
}
if etcdTtl < serv.Ttl {
return etcdTtl
}
return serv.Ttl
}
// Client exposes the underlying Etcd client (used in tests).
func (g *Backend) Client() etcdc.KeysAPI {
return g.client
}

View File

@@ -2,9 +2,12 @@
package etcd
import (
"encoding/json"
"strings"
"github.com/miekg/coredns/middleware"
"github.com/miekg/dns"
"github.com/skynetservices/skydns/singleflight"
"github.com/miekg/coredns/middleware/etcd/msg"
"github.com/miekg/coredns/middleware/etcd/singleflight"
etcdc "github.com/coreos/etcd/client"
"golang.org/x/net/context"
@@ -12,23 +15,136 @@ import (
type (
Etcd struct {
Next middleware.Handler
client etcd.KeysAPI
Next middleware.Handler
Zones []string
client etcdc.KeysAPI
ctx context.Context
inflight *singleflight.Group
}
)
func NewEtcd(client etcdc.KeysAPI, next middleware.Handler) Etcd {
func NewEtcd(client etcdc.KeysAPI, next middleware.Handler, zones []string) Etcd {
return Etcd{
Next: next,
Zones: zones,
client: client,
ctx: context.Background(),
inflight: &singleflight.Group{},
}
}
func (e Etcd) ServerDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
return 0, nil
func (g Etcd) Records(name string, exact bool) ([]msg.Service, error) {
path, star := msg.PathWithWildcard(name)
r, err := g.Get(path, true)
if err != nil {
return nil, err
}
segments := strings.Split(msg.Path(name), "/")
switch {
case exact && r.Node.Dir:
return nil, nil
case r.Node.Dir:
return g.loopNodes(r.Node.Nodes, segments, star, nil)
default:
return g.loopNodes([]*etcdc.Node{r.Node}, segments, false, nil)
}
}
// Get is a wrapper for client.Get that uses SingleInflight to suppress multiple outstanding queries.
func (g Etcd) Get(path string, recursive bool) (*etcdc.Response, error) {
resp, err := g.inflight.Do(path, func() (interface{}, error) {
r, e := g.client.Get(g.ctx, path, &etcdc.GetOptions{Sort: false, Recursive: recursive})
if e != nil {
return nil, e
}
return r, e
})
if err != nil {
return nil, err
}
return resp.(*etcdc.Response), err
}
// skydns/local/skydns/east/staging/web
// skydns/local/skydns/west/production/web
//
// skydns/local/skydns/*/*/web
// skydns/local/skydns/*/web
// loopNodes recursively loops through the nodes and returns all the values. The nodes' keyname
// will be match against any wildcards when star is true.
func (g Etcd) loopNodes(ns []*etcdc.Node, nameParts []string, star bool, bx map[msg.Service]bool) (sx []msg.Service, err error) {
if bx == nil {
bx = make(map[msg.Service]bool)
}
Nodes:
for _, n := range ns {
if n.Dir {
nodes, err := g.loopNodes(n.Nodes, nameParts, star, bx)
if err != nil {
return nil, err
}
sx = append(sx, nodes...)
continue
}
if star {
keyParts := strings.Split(n.Key, "/")
for i, n := range nameParts {
if i > len(keyParts)-1 {
// name is longer than key
continue Nodes
}
if n == "*" || n == "any" {
continue
}
if keyParts[i] != n {
continue Nodes
}
}
}
serv := new(msg.Service)
if err := json.Unmarshal([]byte(n.Value), serv); err != nil {
return nil, err
}
b := msg.Service{Host: serv.Host, Port: serv.Port, Priority: serv.Priority, Weight: serv.Weight, Text: serv.Text}
if _, ok := bx[b]; ok {
continue
}
bx[b] = true
serv.Key = n.Key
serv.Ttl = g.Ttl(n, serv)
if serv.Priority == 0 {
serv.Priority = priority
}
sx = append(sx, *serv)
}
return sx, nil
}
// Ttl returns the smaller of the etcd TTL and the service's
// TTL. If neither of these are set (have a zero value), a default is used.
func (g Etcd) Ttl(node *etcdc.Node, serv *msg.Service) uint32 {
etcdTtl := uint32(node.TTL)
if etcdTtl == 0 && serv.Ttl == 0 {
return ttl
}
if etcdTtl == 0 {
return serv.Ttl
}
if serv.Ttl == 0 {
return etcdTtl
}
if etcdTtl < serv.Ttl {
return etcdTtl
}
return serv.Ttl
}
const (
priority = 10 // default priority when nothing is set
ttl = 300 // default ttl when nothing is set
minTtl = 60
hostmaster = "hostmaster"
)

View File

@@ -2,7 +2,10 @@
`etcd` enabled reading zone data from an etcd instance. The data in etcd has to be encoded as
a [message](https://github.com/skynetservices/skydns/blob/2fcff74cdc9f9a7dd64189a447ef27ac354b725f/msg/service.go#L26)
like SkyDNS.
like [SkyDNS](https//github.com/skynetservices/skydns).
If you need replies to SOA and NS queries you should add a little zone after etcd directive that has
these resource records.
## Syntax
@@ -14,16 +17,16 @@ etcd [endpoint...]
The will default to `/skydns` as the path and the local etcd proxy (http://127.0.0.1:2379).
If you want to `round robin` A and AAAA responses look at the `round_robin` middleware.
~~~
etcd {
round_robin
path /skydns
endpoint address...
stubzones
}
~~~
* `round_robin`
* `path` /skydns
* `endpoint` address...
* `stubzones`

View File

@@ -0,0 +1,105 @@
package etcd
import (
"github.com/miekg/coredns/middleware"
"github.com/miekg/dns"
"golang.org/x/net/context"
)
func (e Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
println("ETCD MIDDLEWARE HIT")
state := middleware.State{W: w, Req: r}
m := state.AnswerMessage()
m.Authoritative = true
m.RecursionAvailable = true
m.Compress = true
return 0, nil
}
// only needs state and current zone name we are auth for.
/*
func (s *server) ServeDNS(w dns.ResponseWriter, req *dns.Msg) {
q := req.Question[0]
name := strings.ToLower(q.Name)
switch q.Qtype {
case dns.TypeNS:
records, extra, err := s.NSRecords(q, s.config.dnsDomain)
if isEtcdNameError(err, s) {
m = s.NameError(req)
return
}
m.Answer = append(m.Answer, records...)
m.Extra = append(m.Extra, extra...)
case dns.TypeA, dns.TypeAAAA:
records, err := s.AddressRecords(q, name, nil, bufsize, dnssec, false)
if isEtcdNameError(err, s) {
m = s.NameError(req)
return
}
m.Answer = append(m.Answer, records...)
case dns.TypeTXT:
records, err := s.TXTRecords(q, name)
if isEtcdNameError(err, s) {
m = s.NameError(req)
return
}
m.Answer = append(m.Answer, records...)
case dns.TypeCNAME:
records, err := s.CNAMERecords(q, name)
if isEtcdNameError(err, s) {
m = s.NameError(req)
return
}
m.Answer = append(m.Answer, records...)
case dns.TypeMX:
records, extra, err := s.MXRecords(q, name, bufsize, dnssec)
if isEtcdNameError(err, s) {
m = s.NameError(req)
return
}
m.Answer = append(m.Answer, records...)
m.Extra = append(m.Extra, extra...)
default:
fallthrough // also catch other types, so that they return NODATA
case dns.TypeSRV:
records, extra, err := s.SRVRecords(q, name, bufsize, dnssec)
if err != nil {
if isEtcdNameError(err, s) {
m = s.NameError(req)
return
}
logf("got error from backend: %s", err)
if q.Qtype == dns.TypeSRV { // Otherwise NODATA
m = s.ServerFailure(req)
return
}
}
// if we are here again, check the types, because an answer may only
// be given for SRV. All other types should return NODATA, the
// NXDOMAIN part is handled in the above code. TODO(miek): yes this
// can be done in a more elegant manor.
if q.Qtype == dns.TypeSRV {
m.Answer = append(m.Answer, records...)
m.Extra = append(m.Extra, extra...)
}
}
if len(m.Answer) == 0 { // NODATA response
m.Ns = []dns.RR{s.NewSOA()}
m.Ns[0].Header().Ttl = s.config.MinTtl
}
}
// etcNameError checks if the error is ErrorCodeKeyNotFound from etcd.
func isEtcdNameError(err error, s *server) bool {
if e, ok := err.(etcd.Error); ok && e.Code == etcd.ErrorCodeKeyNotFound {
return true
}
return false
}
*/

327
middleware/etcd/lookup.go Normal file
View File

@@ -0,0 +1,327 @@
package etcd
/*
func (s *server) AddressRecords(q dns.Question, name string, previousRecords []dns.RR, state middleware.State) (records []dns.RR, err error) {
services, err := s.backend.Records(name, false)
if err != nil {
return nil, err
}
services = msg.Group(services)
for _, serv := range services {
ip := net.ParseIP(serv.Host)
switch {
case ip == nil:
// Try to resolve as CNAME if it's not an IP, but only if we don't create loops.
if q.Name == dns.Fqdn(serv.Host) {
// x CNAME x is a direct loop, don't add those
continue
}
newRecord := serv.NewCNAME(q.Name, dns.Fqdn(serv.Host))
if len(previousRecords) > 7 {
logf("CNAME lookup limit of 8 exceeded for %s", newRecord)
// don't add it, and just continue
continue
}
if s.isDuplicateCNAME(newRecord, previousRecords) {
logf("CNAME loop detected for record %s", newRecord)
continue
}
nextRecords, err := s.AddressRecords(dns.Question{Name: dns.Fqdn(serv.Host), Qtype: q.Qtype, Qclass: q.Qclass},
strings.ToLower(dns.Fqdn(serv.Host)), append(previousRecords, newRecord), state)
if err == nil {
// 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
if dns.IsSubDomain(s.config.Domain, target) {
// We should already have found it
continue
}
m1, e1 := s.Lookup(target, q.Qtype, bufsize, dnssec)
if e1 != nil {
logf("incomplete CNAME chain: %s", e1)
continue
}
// Len(m1.Answer) > 0 here is well?
records = append(records, newRecord)
records = append(records, m1.Answer...)
continue
case ip.To4() != nil && (q.Qtype == dns.TypeA || both):
records = append(records, serv.NewA(q.Name, ip.To4()))
case ip.To4() == nil && (q.Qtype == dns.TypeAAAA || both):
records = append(records, serv.NewAAAA(q.Name, ip.To16()))
}
}
return records, nil
}
// NSRecords returns NS records from etcd.
func (s *server) NSRecords(q dns.Question, state middleware.State) (records []dns.RR, extra []dns.RR, err error) {
services, err := s.backend.Records(name, false)
if err != nil {
return nil, nil, err
}
services = msg.Group(services)
for _, serv := range services {
ip := net.ParseIP(serv.Host)
switch {
case ip == nil:
return nil, nil, fmt.Errorf("NS record must be an IP address")
case ip.To4() != nil:
serv.Host = msg.Domain(serv.Key)
records = append(records, serv.NewNS(q.Name, serv.Host))
extra = append(extra, serv.NewA(serv.Host, ip.To4()))
case ip.To4() == nil:
serv.Host = msg.Domain(serv.Key)
records = append(records, serv.NewNS(q.Name, serv.Host))
extra = append(extra, serv.NewAAAA(serv.Host, ip.To16()))
}
}
return records, extra, nil
}
// SRVRecords returns SRV records from etcd.
// If the Target is not a name but an IP address, a name is created.
func (s *server) SRVRecords(s middleware.State) (records []dns.RR, extra []dns.RR, err error) {
services, err := s.backend.Records(name, false)
if err != nil {
return nil, nil, err
}
services = msg.Group(services)
// Looping twice to get the right weight vs priority
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
}
lookup := make(map[string]bool)
for _, serv := range services {
w1 := 100.0 / float64(w[serv.Priority])
if serv.Weight == 0 {
w1 *= 100
} else {
w1 *= float64(serv.Weight)
}
weight := uint16(math.Floor(w1))
ip := net.ParseIP(serv.Host)
switch {
case ip == nil:
srv := serv.NewSRV(q.Name, weight)
records = append(records, srv)
if _, ok := lookup[srv.Target]; ok {
break
}
lookup[srv.Target] = true
if !dns.IsSubDomain(s.config.Domain, srv.Target) {
m1, e1 := s.Lookup(srv.Target, dns.TypeA, bufsize, dnssec)
if e1 == nil {
extra = append(extra, m1.Answer...)
}
m1, e1 = s.Lookup(srv.Target, dns.TypeAAAA, bufsize, dnssec)
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
// Clients expect a complete answer, because we are a recursor in their
// view.
addr, e1 := s.AddressRecords(dns.Question{srv.Target, dns.ClassINET, dns.TypeA},
srv.Target, nil, bufsize, dnssec, true)
if e1 == nil {
extra = append(extra, addr...)
}
case ip.To4() != nil:
serv.Host = msg.Domain(serv.Key)
srv := serv.NewSRV(q.Name, weight)
records = append(records, srv)
extra = append(extra, serv.NewA(srv.Target, ip.To4()))
case ip.To4() == nil:
serv.Host = msg.Domain(serv.Key)
srv := serv.NewSRV(q.Name, weight)
records = append(records, srv)
extra = append(extra, serv.NewAAAA(srv.Target, ip.To16()))
}
}
return records, extra, nil
}
// MXRecords returns MX records from etcd.
// If the Target is not a name but an IP address, a name is created.
func (s *server) MXRecords(q dns.Question, name string, s middleware.State) (records []dns.RR, extra []dns.RR, err error) {
services, err := s.backend.Records(name, false)
if err != nil {
return nil, nil, err
}
lookup := make(map[string]bool)
for _, serv := range services {
if !serv.Mail {
continue
}
ip := net.ParseIP(serv.Host)
switch {
case ip == nil:
mx := serv.NewMX(q.Name)
records = append(records, mx)
if _, ok := lookup[mx.Mx]; ok {
break
}
lookup[mx.Mx] = true
if !dns.IsSubDomain(s.config.Domain, mx.Mx) {
m1, e1 := s.Lookup(mx.Mx, dns.TypeA, bufsize, dnssec)
if e1 == nil {
extra = append(extra, m1.Answer...)
}
m1, e1 = s.Lookup(mx.Mx, dns.TypeAAAA, bufsize, dnssec)
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
addr, e1 := s.AddressRecords(dns.Question{mx.Mx, dns.ClassINET, dns.TypeA},
mx.Mx, nil, bufsize, dnssec, true)
if e1 == nil {
extra = append(extra, addr...)
}
case ip.To4() != nil:
serv.Host = msg.Domain(serv.Key)
records = append(records, serv.NewMX(q.Name))
extra = append(extra, serv.NewA(serv.Host, ip.To4()))
case ip.To4() == nil:
serv.Host = msg.Domain(serv.Key)
records = append(records, serv.NewMX(q.Name))
extra = append(extra, serv.NewAAAA(serv.Host, ip.To16()))
}
}
return records, extra, nil
}
func (s *server) CNAMERecords(q dns.Question, state middleware.State) (records []dns.RR, err error) {
services, err := s.backend.Records(name, true)
if err != nil {
return nil, err
}
services = msg.Group(services)
if len(services) > 0 {
serv := services[0]
if ip := net.ParseIP(serv.Host); ip == nil {
records = append(records, serv.NewCNAME(q.Name, dns.Fqdn(serv.Host)))
}
}
return records, nil
}
func (s *server) TXTRecords(q dns.Question, state middleware.State) (records []dns.RR, err error) {
services, err := s.backend.Records(name, false)
if err != nil {
return nil, err
}
services = msg.Group(services)
for _, serv := range services {
if serv.Text == "" {
continue
}
records = append(records, serv.NewTXT(q.Name))
}
return records, nil
}
func isDuplicateCNAME(r *dns.CNAME, records []dns.RR) bool {
for _, rec := range records {
if v, ok := rec.(*dns.CNAME); ok {
if v.Target == r.Target {
return true
}
}
}
return false
}
// Move to state.go somehow?
func (s *server) NameError(req *dns.Msg) *dns.Msg {
m := new(dns.Msg)
m.SetRcode(req, dns.RcodeNameError)
m.Ns = []dns.RR{s.NewSOA()}
m.Ns[0].Header().Ttl = s.config.MinTtl
return m
}
// overflowOrTruncated writes back an error to the client if the message does not fit.
// It updates prometheus metrics. If something has been written to the client, true
// will be returned.
func (s *server) overflowOrTruncated(w dns.ResponseWriter, m *dns.Msg, bufsize int, sy metrics.System) bool {
switch isTCP(w) {
case true:
if _, overflow := Fit(m, dns.MaxMsgSize, true); overflow {
metrics.ReportErrorCount(m, sy)
msgFail := s.ServerFailure(m)
w.WriteMsg(msgFail)
return true
}
case false:
// Overflow with udp always results in TC.
Fit(m, bufsize, false)
metrics.ReportErrorCount(m, sy)
if m.Truncated {
w.WriteMsg(m)
return true
}
}
return false
}
// etcNameError return a NameError to the client if the error
// returned from etcd has ErrorCode == 100.
func isEtcdNameError(err error, s *server) bool {
if e, ok := err.(etcd.Error); ok && e.Code == etcd.ErrorCodeKeyNotFound {
return true
}
if err != nil {
logf("error from backend: %s", err)
}
return false
}
*/

View File

@@ -8,3 +8,7 @@ func Exchange(c *dns.Client, m *dns.Msg, server string) (*dns.Msg, error) {
r, _, err := c.Exchange(m, server)
return r, err
}
// Lookup functions, ala
// LookupHost
// LookupCNAME

View File

@@ -83,6 +83,30 @@ func (s State) Family() int {
return 2
}
// Do returns if the request has the DO (DNSSEC OK) bit set.
func (s State) Do() bool {
if o := s.Req.IsEdns0(); o != nil {
return o.Do()
}
return false
}
// UDPSize returns if UDP buffer size advertised in the requests OPT record.
// Or when the request was over TCP, we return the maximum allowed size of 64K.
func (s State) Size() int {
if s.Proto() == "tcp" {
return dns.MaxMsgSize
}
if o := s.Req.IsEdns0(); o != nil {
s := o.UDPSize()
if s < dns.MinMsgSize {
s = dns.MinMsgSize
}
return int(s)
}
return dns.MinMsgSize
}
// Type returns the type of the question as a string.
func (s State) Type() string {
return dns.Type(s.Req.Question[0].Qtype).String()