aboutsummaryrefslogtreecommitdiff
path: root/middleware/kubernetes/autopath/autopath.go
blob: c1b2179ae9b6b0154562300490295a06e08b98ab (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package autopath

import (
	"strings"

	"github.com/miekg/dns"
)

// Writer implements a ResponseWriter that also does the following:
// * reverts question section of a packet to its original state.
//   This is done to avoid the 'Question section mismatch:' error in client.
// * Defers write to the client if the response code is NXDOMAIN.  This is needed
//   to enable further search path probing if a search was not successful.
// * Allow forced write to client regardless of response code.  This is needed to
//   write the packet to the client if the final search in the path fails to
//   produce results.
// * Overwrites response code with Writer.Rcode if the response code
//   is NXDOMAIN (NameError).  This is needed to support the AutoPath.OnNXDOMAIN
//   function, which returns a NOERROR to client instead of NXDOMAIN if the final
//   search in the path fails to produce results.
type Writer struct {
	dns.ResponseWriter
	original dns.Question
	Rcode    int
}

// AutoPath enables server side search path lookups for pods.
type AutoPath struct {
	NDots          int
	ResolvConfFile string
	HostSearchPath []string
	OnNXDOMAIN     int
}

// NewWriter returns a pointer to a new Writer
func NewWriter(w dns.ResponseWriter, r *dns.Msg) *Writer {
	return &Writer{
		ResponseWriter: w,
		original:       r.Question[0],
		Rcode:          dns.RcodeSuccess,
	}
}

// WriteMsg writes to client, unless response will be NXDOMAIN.
func (apw *Writer) WriteMsg(res *dns.Msg) error {
	if res.Rcode == dns.RcodeNameError {
		res.Rcode = apw.Rcode
	}

	for _, a := range res.Answer {
		if apw.original.Name == a.Header().Name {
			continue
		}
		res.Answer = append(res.Answer, nil)
		copy(res.Answer[1:], res.Answer)
		res.Answer[0] = CNAME(apw.original.Name, a.Header().Name, a.Header().Ttl)
	}
	res.Question[0] = apw.original

	return apw.ResponseWriter.WriteMsg(res)
}

// Write is a wrapper that records the size of the message that gets written.
func (apw *Writer) Write(buf []byte) (int, error) {
	n, err := apw.ResponseWriter.Write(buf)
	return n, err
}

func SplitSearch(zone, question, namespace string) (name, search string, ok bool) {
	search = strings.Join([]string{namespace, "svc", zone}, ".")
	if dns.IsSubDomain(search, question) {
		return question[:len(question)-len(search)-1], search, true
	}
	return "", "", false
}