forked from serverwentdown/dns64
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdns64.go
154 lines (133 loc) · 3.78 KB
/
dns64.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Package dns64 implements a plugin that performs DNS64.
package dns64
import (
"errors"
"net"
"time"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/pkg/response"
"github.com/coredns/coredns/plugin/proxy"
"github.com/coredns/coredns/request"
"github.com/miekg/dns"
"golang.org/x/net/context"
)
// DNS64 performs DNS64.
type DNS64 struct {
Next plugin.Handler
Proxy proxy.Proxy
Prefix *net.IPNet
}
// ServeDNS implements the plugin.Handler interface.
func (d DNS64) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
drr := &ResponseWriter{d, w}
return d.Next.ServeDNS(ctx, drr, r)
}
// Name implements the Handler interface.
func (d DNS64) Name() string { return "dns64" }
// ResponseWriter is a response writer that implements DNS64, when an AAAA query returns
// NODATA, it will try and fetch any A records and synthesize the AAAA records on the fly.
type ResponseWriter struct {
DNS64
dns.ResponseWriter
}
// WriteMsg implements the dns.ResponseWriter interface.
func (r *ResponseWriter) WriteMsg(res *dns.Msg) error {
state := request.Request{W: r, Req: res}
// only respond with this when the request came in over IPv6.
if state.Family() == 1 { // if it came in over v4, don't do anything.
return r.ResponseWriter.WriteMsg(res)
}
// do not modify if query is not AAAA or not of class IN.
if state.QType() != dns.TypeAAAA || state.QClass() != dns.ClassINET {
return r.ResponseWriter.WriteMsg(res)
}
// do not modify if there are AAAA records or NameError. continue if NoData or any other error.
ty, _ := response.Typify(res, time.Now().UTC())
if ty == response.NoError || ty == response.NameError {
if hasAAAA(res) {
return r.ResponseWriter.WriteMsg(res)
}
}
// perform request to upstream.
res2, err := r.Proxy.Lookup(state, state.Name(), dns.TypeA)
if err != nil {
log.Warningf("[WARNING] Unable to query upstream DNS: %v", err)
res.MsgHdr.Rcode = dns.RcodeServerFailure
return r.ResponseWriter.WriteMsg(res)
}
// modify response.
res.MsgHdr.Rcode = dns.RcodeSuccess
nsTtl := uint32(600)
for i := 0; i < len(res.Ns); i++ {
if res.Ns[i].Header().Rrtype == dns.TypeSOA {
nsTtl = res.Ns[i].Header().Ttl
}
}
res.Answer = res2.Answer
for i := 0; i < len(res.Answer); i++ {
ans := res.Answer[i]
hdr := ans.Header()
if hdr.Rrtype == dns.TypeA {
aaaa, _ := To6(r.Prefix, ans.(*dns.A).A)
ttl := nsTtl
if ans.Header().Ttl < ttl {
ttl = ans.Header().Ttl
}
res.Answer[i] = &dns.AAAA{
Hdr: dns.RR_Header{
Name: hdr.Name,
Rrtype: dns.TypeAAAA,
Class: hdr.Class,
Ttl: ttl,
},
AAAA: aaaa,
}
}
}
res.Ns = []dns.RR{}
return r.ResponseWriter.WriteMsg(res)
}
// Write implements the dns.ResponseWriter interface.
func (r *ResponseWriter) Write(buf []byte) (int, error) {
log.Warning("[WARNING] DNS64 called with Write: not performing DNS64")
n, err := r.ResponseWriter.Write(buf)
return n, err
}
// Hijack implements the dns.ResponseWriter interface.
func (r *ResponseWriter) Hijack() {
r.ResponseWriter.Hijack()
return
}
// To6 takes a prefix and IPv4 address and returns an IPv6 address according to RFC 6052.
func To6(prefix *net.IPNet, addr net.IP) (net.IP, error) {
addr = addr.To4()
if addr == nil {
return nil, errors.New("Not a valid IPv4 address")
}
n, _ := prefix.Mask.Size()
// assumes prefix has been validated during setup
v6 := make([]byte, 16)
i, j := 0, 0
for ; i < n/8; i++ {
v6[i] = prefix.IP[i]
}
for ; i < 8; i, j = i+1, j+1 {
v6[i] = addr[j]
}
if i == 8 {
i++
}
for ; j < 4; i, j = i+1, j+1 {
v6[i] = addr[j]
}
return v6, nil
}
// hasAAAA checks if AAAA records exists in dns.Msg
func hasAAAA(res *dns.Msg) bool {
for _, a := range res.Answer {
if a.Header().Rrtype == dns.TypeAAAA {
return true
}
}
return false
}