summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Makefile7
-rw-r--r--dns/types.go271
-rw-r--r--generate_dns_types.go192
4 files changed, 206 insertions, 268 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..75c9781
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
1./dns-service
2
3# Generated files have a zzz_ prefix
4**/zzz_*.go
diff --git a/Makefile b/Makefile
index c174da7..37c5199 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,9 @@
1GO_FILES := $(shell find . -name '*.go') 1.PHONY: dns-service
2 2dns-service:
3dns-service: main.go $(GO_FILES) 3 go generate ./...
4 CGO_ENABLED=0 go build -o $@ $< 4 CGO_ENABLED=0 go build -o $@ $<
5 5
6.PHONY: clean 6.PHONY: clean
7clean: 7clean:
8 rm -f dns-service 8 rm -f dns-service
9 find . -name 'zzz_*.go' -delete
diff --git a/dns/types.go b/dns/types.go
index 8f840be..aa5112b 100644
--- a/dns/types.go
+++ b/dns/types.go
@@ -2,12 +2,14 @@ package dns
2 2
3import ( 3import (
4 "fmt" 4 "fmt"
5 "net" 5 _ "net"
6 6
7 "code.crute.me/mcrute/go_ddns_manager/bind" 7 "code.crute.me/mcrute/go_ddns_manager/bind"
8 "github.com/miekg/dns" 8 "github.com/miekg/dns"
9) 9)
10 10
11//go:generate go run ../generate_dns_types.go
12
11func makeHeader(name string, zone *bind.Zone, t uint16, ttl int) dns.RR_Header { 13func makeHeader(name string, zone *bind.Zone, t uint16, ttl int) dns.RR_Header {
12 return dns.RR_Header{ 14 return dns.RR_Header{
13 Name: fmt.Sprintf("%s.%s", name, zone.Name), 15 Name: fmt.Sprintf("%s.%s", name, zone.Name),
@@ -27,268 +29,7 @@ func toRRSet(z *bind.Zone, rr ...RR) []dns.RR {
27 29
28type RR interface { 30type RR interface {
29 ToDNS(*bind.Zone) dns.RR 31 ToDNS(*bind.Zone) dns.RR
30} 32 FromDNS(rr dns.RR) error
31 33 MarshalJSON() ([]byte, error)
32type A struct { 34 UnmarshalJSON(data []byte) error
33 Name string
34 Ttl int
35 A net.IP
36}
37
38func (r *A) ToDNS(zone *bind.Zone) dns.RR {
39 return &dns.A{
40 Hdr: makeHeader(r.Name, zone, dns.TypeA, r.Ttl),
41 A: r.A,
42 }
43}
44
45type AAAA struct {
46 Name string
47 Ttl int
48 AAAA net.IP
49}
50
51func (r *AAAA) ToDNS(zone *bind.Zone) dns.RR {
52 return &dns.AAAA{
53 Hdr: makeHeader(r.Name, zone, dns.TypeAAAA, r.Ttl),
54 AAAA: r.AAAA,
55 }
56}
57
58type CAA struct {
59 Name string
60 Ttl int
61 Flag uint8
62 Tag string
63 Value string
64}
65
66func (r *CAA) ToDNS(zone *bind.Zone) dns.RR {
67 return &dns.CAA{
68 Hdr: makeHeader(r.Name, zone, dns.TypeCAA, r.Ttl),
69 Flag: r.Flag,
70 Tag: r.Tag,
71 Value: r.Value,
72 }
73}
74
75type CERT struct {
76 Name string
77 Ttl int
78 Type uint16
79 KeyTag uint16
80 Algorithm uint8
81 Certificate string
82}
83
84func (r *CERT) ToDNS(zone *bind.Zone) dns.RR {
85 return &dns.CERT{
86 Hdr: makeHeader(r.Name, zone, dns.TypeCERT, r.Ttl),
87 Type: r.Type,
88 KeyTag: r.KeyTag,
89 Algorithm: r.Algorithm,
90 Certificate: r.Certificate,
91 }
92}
93
94type CNAME struct {
95 Name string
96 Ttl int
97 Target string
98}
99
100func (r *CNAME) ToDNS(zone *bind.Zone) dns.RR {
101 return &dns.CNAME{
102 Hdr: makeHeader(r.Name, zone, dns.TypeCNAME, r.Ttl),
103 Target: r.Target,
104 }
105}
106
107type DNAME struct {
108 Name string
109 Ttl int
110 Target string
111}
112
113func (r *DNAME) ToDNS(zone *bind.Zone) dns.RR {
114 return &dns.DNAME{
115 Hdr: makeHeader(r.Name, zone, dns.TypeDNAME, r.Ttl),
116 Target: r.Target,
117 }
118}
119
120type LOC struct {
121 Name string
122 Ttl int
123 Version uint8
124 Size uint8
125 HorizPre uint8
126 VertPre uint8
127 Latitude uint32
128 Longitude uint32
129 Altitude uint32
130}
131
132func (r *LOC) ToDNS(zone *bind.Zone) dns.RR {
133 return &dns.LOC{
134 Hdr: makeHeader(r.Name, zone, dns.TypeLOC, r.Ttl),
135 Version: r.Version,
136 Size: r.Size,
137 HorizPre: r.HorizPre,
138 VertPre: r.VertPre,
139 Latitude: r.Latitude,
140 Longitude: r.Longitude,
141 Altitude: r.Altitude,
142 }
143}
144
145type MX struct {
146 Name string
147 Ttl int
148 Preference uint16
149 Mx string
150}
151
152func (r *MX) ToDNS(zone *bind.Zone) dns.RR {
153 return &dns.MX{
154 Hdr: makeHeader(r.Name, zone, dns.TypeMX, r.Ttl),
155 Preference: r.Preference,
156 Mx: r.Mx,
157 }
158}
159
160type NAPTR struct {
161 Name string
162 Ttl int
163 Order uint16
164 Preference uint16
165 Flags string
166 Service string
167 Regexp string
168 Replacement string
169}
170
171func (r *NAPTR) ToDNS(zone *bind.Zone) dns.RR {
172 return &dns.NAPTR{
173 Hdr: makeHeader(r.Name, zone, dns.TypeNAPTR, r.Ttl),
174 Order: r.Order,
175 Preference: r.Preference,
176 Flags: r.Flags,
177 Service: r.Service,
178 Regexp: r.Regexp,
179 Replacement: r.Replacement,
180 }
181}
182
183type NS struct {
184 Name string
185 Ttl int
186 Ns string
187}
188
189func (r *NS) ToDNS(zone *bind.Zone) dns.RR {
190 return &dns.NS{
191 Hdr: makeHeader(r.Name, zone, dns.TypeNS, r.Ttl),
192 Ns: r.Ns,
193 }
194}
195
196type OPENPGPKEY struct {
197 Name string
198 Ttl int
199 PublicKey string
200}
201
202func (r *OPENPGPKEY) ToDNS(zone *bind.Zone) dns.RR {
203 return &dns.OPENPGPKEY{
204 Hdr: makeHeader(r.Name, zone, dns.TypeOPENPGPKEY, r.Ttl),
205 PublicKey: r.PublicKey,
206 }
207}
208
209type PTR struct {
210 Name string
211 Ttl int
212 Ptr string
213}
214
215func (r *PTR) ToDNS(zone *bind.Zone) dns.RR {
216 return &dns.PTR{
217 Hdr: makeHeader(r.Name, zone, dns.TypePTR, r.Ttl),
218 Ptr: r.Ptr,
219 }
220}
221
222type SOA struct {
223 Name string
224 Ttl int
225 Ns string
226 Mbox string
227 Serial uint32
228 Refresh uint32
229 Retry uint32
230 Expire uint32
231 Minttl uint32
232}
233
234func (r *SOA) ToDNS(zone *bind.Zone) dns.RR {
235 return &dns.SOA{
236 Hdr: makeHeader(r.Name, zone, dns.TypeSOA, r.Ttl),
237 Ns: r.Ns,
238 Mbox: r.Mbox,
239 Serial: r.Serial,
240 Refresh: r.Refresh,
241 Retry: r.Retry,
242 Expire: r.Expire,
243 Minttl: r.Minttl,
244 }
245}
246
247type SRV struct {
248 Name string
249 Ttl int
250 Priority uint16
251 Weight uint16
252 Port uint16
253 Target string
254}
255
256func (r *SRV) ToDNS(zone *bind.Zone) dns.RR {
257 return &dns.SRV{
258 Hdr: makeHeader(r.Name, zone, dns.TypeSRV, r.Ttl),
259 Priority: r.Priority,
260 Weight: r.Weight,
261 Port: r.Port,
262 Target: r.Target,
263 }
264}
265
266type SSHFP struct {
267 Name string
268 Ttl int
269 Algorithm uint8
270 Type uint8
271 FingerPrint string
272}
273
274func (r *SSHFP) ToDNS(zone *bind.Zone) dns.RR {
275 return &dns.SSHFP{
276 Hdr: makeHeader(r.Name, zone, dns.TypeSSHFP, r.Ttl),
277 Algorithm: r.Algorithm,
278 Type: r.Type,
279 FingerPrint: r.FingerPrint,
280 }
281}
282
283type TXT struct {
284 Name string
285 Ttl int
286 Txt []string
287}
288
289func (r *TXT) ToDNS(zone *bind.Zone) dns.RR {
290 return &dns.TXT{
291 Hdr: makeHeader(r.Name, zone, dns.TypeTXT, r.Ttl),
292 Txt: r.Txt,
293 }
294} 35}
diff --git a/generate_dns_types.go b/generate_dns_types.go
new file mode 100644
index 0000000..282c630
--- /dev/null
+++ b/generate_dns_types.go
@@ -0,0 +1,192 @@
1//+build ignore
2
3package main
4
5import (
6 "fmt"
7 "go/types"
8 "log"
9 "os"
10 "text/template"
11
12 "golang.org/x/tools/go/packages"
13)
14
15type Field struct {
16 Name string
17 Type string
18}
19
20var tpl = template.Must(template.New("").Parse(`package dns
21
22// GENERATED FILE, DO NOT MODIFY
23// See generate_dns_types.go in the repo root.
24
25import (
26 "encoding/json"
27 "fmt"
28 "net"
29
30 "github.com/miekg/dns"
31
32 "code.crute.me/mcrute/go_ddns_manager/bind"
33)
34
35{{ range $name, $fields := . -}}
36type {{ $name }} struct {
37 Name string
38 Ttl int
39 {{ range $fields -}}
40 {{ .Name }} {{ .Type }}
41 {{ end -}}
42}
43
44func (r *{{ $name }}) ToDNS(zone *bind.Zone) dns.RR {
45 return &dns.{{ $name }}{
46 Hdr: makeHeader(r.Name, zone, dns.Type{{ $name }}, r.Ttl),
47 {{ range $fields -}}
48 {{ .Name }}: r.{{ .Name }},
49 {{ end }}
50 }
51}
52
53func (r *{{ $name }}) FromDNS(rr dns.RR) error {
54 rt, ok := rr.(*dns.{{ $name }})
55 if !ok {
56 return fmt.Errorf("Invalid type %T for '{{ $name }}'", rr)
57 }
58
59 r.Name = rr.Header().Name
60 r.Ttl = int(rr.Header().Ttl)
61 {{ range $fields -}}
62 r.{{ .Name }} = rt.{{ .Name }}
63 {{ end }}
64
65 return nil
66}
67
68func (r *{{ $name }}) MarshalJSON() ([]byte, error) {
69 type Alias {{ $name }}
70 return json.Marshal(&struct {
71 Type string
72 *Alias
73 }{"{{ $name }}", (*Alias)(r)})
74}
75
76func (r *{{ $name }}) UnmarshalJSON(data []byte) error {
77 type Alias {{ $name }}
78 if err := json.Unmarshal(data, &struct{
79 Type string
80 *Alias
81 }{Alias: (*Alias)(r)}); err != nil {
82 return err
83 }
84 return nil
85}
86
87var _ RR = (*{{ $name }})(nil)
88
89{{ end }}
90
91
92func FromDNS(rr dns.RR) interface{} {
93 switch v := rr.(type) {
94 {{ range $name, $fields := . -}}
95 case *dns.{{ $name }}:
96 rv := &{{ $name }}{}
97 rv.FromDNS(v)
98 return rv
99 {{ end }}
100 }
101 return nil
102}
103`))
104
105var disallowedTypes = map[string]bool{
106 "CDNSKEY": true,
107 "CDS": true,
108 "DLV": true,
109 "KEY": true,
110 "OPT": true,
111 "SIG": true,
112 "PrivateRR": true,
113 "RFC3597": true,
114 "ANY": true,
115}
116
117var allowedPackages = map[string]bool{
118 "net": true,
119}
120
121func main() {
122 conf := packages.Config{Mode: packages.NeedTypes | packages.NeedTypesInfo | packages.NeedDeps}
123 pkgs, err := packages.Load(&conf, "github.com/miekg/dns")
124 if err != nil {
125 panic(err)
126 }
127
128 scope := pkgs[0].Types.Scope()
129 localTypes := map[string][]Field{}
130
131 for _, name := range scope.Names() {
132 o := scope.Lookup(name)
133 if o == nil || !o.Exported() {
134 continue
135 }
136
137 // Only consider structs
138 st, ok := o.Type().Underlying().(*types.Struct)
139 if !ok {
140 continue
141 }
142
143 name := o.Name()
144
145 // Explicitly disallow some types that have complex embedded types
146 if _, skip := disallowedTypes[name]; skip {
147 continue
148 }
149
150 // There must be a type constant for this
151 if scope.Lookup(fmt.Sprintf("Type%s", name)) == nil {
152 continue
153 }
154
155 fields := []Field{}
156 for i := 0; i < st.NumFields(); i++ {
157 f := st.Field(i)
158
159 // Exclude header field
160 if f.Name() == "Hdr" {
161 continue
162 }
163
164 // Fail if there are complex types embedded
165 if tp, ok := f.Type().(*types.Named); ok {
166 if _, ok := allowedPackages[tp.Obj().Pkg().Path()]; !ok {
167 log.Fatalf("Invalid embedded complex type: %s", tp)
168 }
169 }
170
171 // Also fail if there are complex types embedded in a slice
172 if tp, ok := f.Type().(*types.Slice); ok {
173 if ut, ok := tp.Elem().(*types.Named); ok {
174 if _, ok := allowedPackages[ut.Obj().Pkg().Path()]; !ok {
175 log.Fatalf("Invalid embedded complex type: %s", tp)
176 }
177 }
178 }
179
180 fields = append(fields, Field{f.Name(), f.Type().String()})
181 }
182
183 localTypes[name] = fields
184 }
185
186 fp, err := os.Create("zzz_types.go")
187 if err != nil {
188 panic(err)
189 }
190 defer fp.Close()
191 tpl.Execute(fp, localTypes)
192}