aboutsummaryrefslogtreecommitdiff
path: root/middleware/proxy/dns.go
diff options
context:
space:
mode:
Diffstat (limited to 'middleware/proxy/dns.go')
-rw-r--r--middleware/proxy/dns.go105
1 files changed, 105 insertions, 0 deletions
diff --git a/middleware/proxy/dns.go b/middleware/proxy/dns.go
new file mode 100644
index 000000000..51633c268
--- /dev/null
+++ b/middleware/proxy/dns.go
@@ -0,0 +1,105 @@
+package proxy
+
+import (
+ "net"
+ "time"
+
+ "github.com/miekg/coredns/middleware/pkg/singleflight"
+ "github.com/miekg/coredns/request"
+
+ "github.com/miekg/dns"
+)
+
+type dnsEx struct {
+ Timeout time.Duration
+ Address string // address/name of this upstream
+
+ group *singleflight.Group
+}
+
+func newDNSEx(address string) *dnsEx {
+ return &dnsEx{Address: address, group: new(singleflight.Group), Timeout: defaultTimeout * time.Second}
+}
+
+func (d *dnsEx) OnStartup() error { return nil }
+func (d *dnsEx) OnShutdown() error { return nil }
+func (d *dnsEx) SetUpstream(u Upstream) error { return nil }
+func (d *dnsEx) Protocol() protocol { return dnsProto }
+
+// Exchange implements the Exchanger interface.
+func (d *dnsEx) Exchange(state request.Request) (*dns.Msg, error) {
+ co, err := net.DialTimeout(state.Proto(), d.Address, d.Timeout)
+ if err != nil {
+ return nil, err
+ }
+
+ reply, _, err := d.ExchangeConn(state.Req, co)
+
+ co.Close()
+
+ if reply != nil && reply.Truncated {
+ // Suppress proxy error for truncated responses
+ err = nil
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ reply.Compress = true
+ reply.Id = state.Req.Id
+
+ return reply, nil
+}
+
+func (d *dnsEx) ExchangeConn(m *dns.Msg, co net.Conn) (*dns.Msg, time.Duration, error) {
+ t := "nop"
+ if t1, ok := dns.TypeToString[m.Question[0].Qtype]; ok {
+ t = t1
+ }
+ cl := "nop"
+ if cl1, ok := dns.ClassToString[m.Question[0].Qclass]; ok {
+ cl = cl1
+ }
+
+ start := time.Now()
+
+ // Name needs to be normalized! Bug in go dns.
+ r, err := d.group.Do(m.Question[0].Name+t+cl, func() (interface{}, error) {
+ return exchange(m, co)
+ })
+
+ r1 := r.(dns.Msg)
+ rtt := time.Since(start)
+ return &r1, rtt, err
+}
+
+// exchange does *not* return a pointer to dns.Msg because that leads to buffer reuse when
+// group.Do is used in Exchange.
+func exchange(m *dns.Msg, co net.Conn) (dns.Msg, error) {
+ opt := m.IsEdns0()
+
+ udpsize := uint16(dns.MinMsgSize)
+ // If EDNS0 is used use that for size.
+ if opt != nil && opt.UDPSize() >= dns.MinMsgSize {
+ udpsize = opt.UDPSize()
+ }
+
+ dnsco := &dns.Conn{Conn: co, UDPSize: udpsize}
+
+ writeDeadline := time.Now().Add(defaultTimeout)
+ dnsco.SetWriteDeadline(writeDeadline)
+ dnsco.WriteMsg(m)
+
+ readDeadline := time.Now().Add(defaultTimeout)
+ co.SetReadDeadline(readDeadline)
+ r, err := dnsco.ReadMsg()
+
+ dnsco.Close()
+ if r == nil {
+ return dns.Msg{}, err
+ }
+ return *r, err
+}
+
+const dnsProto protocol = "dns"