forked from miekg/skydns2
-
Notifications
You must be signed in to change notification settings - Fork 304
/
Copy pathstub.go
124 lines (109 loc) · 3.37 KB
/
stub.go
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright (c) 2014 The SkyDNS Authors. All rights reserved.
// Use of this source code is governed by The MIT License (MIT) that can be
// found in the LICENSE file.
package server
import (
"net"
"strconv"
"strings"
"github.com/miekg/dns"
"github.com/skynetservices/skydns/msg"
)
const ednsStubCode = dns.EDNS0LOCALSTART + 10
// ednsStub is the EDNS0 record we add to stub queries. Queries which have this record are
// not forwarded again.
var ednsStub = func() *dns.OPT {
o := new(dns.OPT)
o.Hdr.Name = "."
o.Hdr.Rrtype = dns.TypeOPT
e := new(dns.EDNS0_LOCAL)
e.Code = ednsStubCode
e.Data = []byte{1}
o.Option = append(o.Option, e)
return o
}()
// Look in .../dns/stub/<domain>/xx for msg.Services. Loop through them
// extract <domain> and add them as forwarders (ip:port-combos) for
// the stub zones. Only numeric (i.e. IP address) hosts are used.
func (s *server) UpdateStubZones() {
stubmap := make(map[string][]string)
services, err := s.backend.Records("stub.dns."+s.config.Domain, false)
if err != nil {
logf("stub zone update failed: %s", err)
return
}
for _, serv := range services {
if serv.Port == 0 {
serv.Port = 53
}
ip := net.ParseIP(serv.Host)
if ip == nil {
logf("stub zone non-address %s seen for: %s", serv.Key, serv.Host)
continue
}
domain := msg.Domain(serv.Key)
// Chop of left most label, because that is used as the nameserver place holder
// and drop the right most labels that belong to localDomain.
labels := dns.SplitDomainName(domain)
domain = dns.Fqdn(strings.Join(labels[1:len(labels)-dns.CountLabel(s.config.localDomain)], "."))
// If the remaining name equals s.config.LocalDomain we ignore it.
if domain == s.config.localDomain {
logf("not adding stub zone for my own domain")
continue
}
stubmap[domain] = append(stubmap[domain], net.JoinHostPort(serv.Host, strconv.Itoa(serv.Port)))
}
s.config.stub = &stubmap
}
// ServeDNSStubForward forwards a request to a nameservers and returns the response.
func (s *server) ServeDNSStubForward(w dns.ResponseWriter, req *dns.Msg, ns []string) *dns.Msg {
// Check EDNS0 Stub option, if set drop the packet.
option := req.IsEdns0()
if option != nil {
for _, o := range option.Option {
if o.Option() == ednsStubCode && len(o.(*dns.EDNS0_LOCAL).Data) == 1 &&
o.(*dns.EDNS0_LOCAL).Data[0] == 1 {
// Maybe log source IP here?
logf("not fowarding stub request to another stub")
return nil
}
}
}
// Add a custom EDNS0 option to the packet, so we can detect loops
// when 2 stubs are forwarding to each other.
if option != nil {
option.Option = append(option.Option, &dns.EDNS0_LOCAL{ednsStubCode, []byte{1}})
} else {
req.Extra = append(req.Extra, ednsStub)
}
var (
r *dns.Msg
err error
)
// Use request Id for "random" nameserver selection.
nsid := int(req.Id) % len(ns)
try := 0
Redo:
if isTCP(w) {
r, err = exchangeWithRetry(s.dnsTCPclient, req, ns[nsid])
} else {
r, err = exchangeWithRetry(s.dnsUDPclient, req, ns[nsid])
}
if err == nil || err == dns.ErrTruncated {
r.Compress = true
r.Id = req.Id
w.WriteMsg(r)
return r
}
// Seen an error, this can only mean, "server not reached", try again
// but only if we have not exausted our nameservers.
if try < len(ns) {
try++
nsid = (nsid + 1) % len(ns)
goto Redo
}
logf("failure to forward stub request %q", err)
m := s.ServerFailure(req)
w.WriteMsg(m)
return m
}