/
plugin.go
156 lines (132 loc) · 3.32 KB
/
plugin.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
155
156
// package zerozone is a coredns plugin that serves Zero Zones.
package zerozone
import (
"context"
"net"
"strings"
"github.com/bitnami-labs/zerozone/pkg/store"
"github.com/coredns/coredns/core/dnsserver"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/pkg/log"
"github.com/coredns/coredns/request"
"github.com/caddyserver/caddy"
"github.com/miekg/dns"
)
// ZeroZoneHandler implemens that coredns plugin handler.
type ZeroZoneHandler struct {
Domain string
Fetcher store.Fetcher
Next plugin.Handler
}
func init() {
caddy.RegisterPlugin("zerozone", caddy.Plugin{
ServerType: "dns",
Action: setup,
})
}
func setup(c *caddy.Controller) error {
c.Next()
var ipfsNodeAddr string
if !c.Args(&ipfsNodeAddr) {
return plugin.Error("zerozone", c.ArgErr())
}
var fetcher store.Fetcher
if strings.HasPrefix(ipfsNodeAddr, "http") {
fetcher = store.NewIPNSGatewayFetcher(ipfsNodeAddr)
} else {
fetcher = store.NewIPNSFetcher(ipfsNodeAddr)
}
cfg := dnsserver.GetConfig(c)
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
return &ZeroZoneHandler{
Domain: cfg.Zone,
Fetcher: store.NewSingleFlightFetcher(store.NewCachingFetcher(fetcher)),
Next: next,
}
})
return nil
}
func (h *ZeroZoneHandler) Name() string { return "zerozone" }
func parseQuery(qname, domain string) (hostname, zoneID string, ok bool) {
comp := dns.SplitDomainName(strings.TrimSuffix(qname, domain))
if len(comp) == 0 {
return "", "", false
}
return strings.Join(comp[:len(comp)-1], "."), comp[len(comp)-1], true
}
func (h *ZeroZoneHandler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
state := request.Request{W: w, Req: r}
qname := state.Name()
domain := plugin.Zones([]string{h.Domain}).Matches(qname)
if domain == "" {
return plugin.NextOrFailure(h.Name(), h.Next, ctx, w, r)
}
state.Zone = domain
hostname, zoneID, ok := parseQuery(qname, domain)
// allow falling through
if !ok {
return plugin.NextOrFailure(h.Name(), h.Next, ctx, w, r)
}
zone, err := h.Fetcher.FetchZone(zoneID)
if err != nil {
return dns.RcodeServerFailure, plugin.Error("zerozone", err)
}
m := new(dns.Msg)
m.SetReply(r)
for _, rr := range zone.Records {
hdr := dns.RR_Header{Name: qname, Rrtype: state.QType(), Class: dns.ClassINET, Ttl: rr.TTL}
if hostname == rr.Name && (state.Type() == rr.Type || (state.QType() == dns.TypeA && rr.Type == "CNAME")) {
for _, d := range rr.RRDatas {
var ans dns.RR
switch t := rr.Type; t {
case "A":
ans = &dns.A{
Hdr: hdr,
A: net.ParseIP(d),
}
case "AAAA":
ans = &dns.AAAA{
Hdr: hdr,
AAAA: net.ParseIP(d),
}
case "CNAME":
hdr.Rrtype = dns.TypeCNAME
ans = &dns.CNAME{
Hdr: hdr,
Target: d,
}
case "TXT":
ans = &dns.TXT{
Hdr: hdr,
Txt: split255(d),
}
default:
log.Debugf("unhandled type %q", t)
}
m.Answer = append(m.Answer, ans)
}
break
}
}
state.SizeAndDo(m)
m = state.Scrub(m)
w.WriteMsg(m)
return dns.RcodeSuccess, nil
}
func split255(s string) []string {
if len(s) < 255 {
return []string{s}
}
sx := []string{}
p, i := 0, 255
for {
if i <= len(s) {
sx = append(sx, s[p:i])
} else {
sx = append(sx, s[p:])
break
}
p, i = p+255, i+255
}
return sx
}