3 // msg_generate.go is meant to run with go generate. It will use
4 // go/{importer,types} to track down all the RR struct types. Then for each type
5 // it will generate pack/unpack methods based on the struct tags. The generated source is
6 // written to zmsg.go, and is meant to be checked into git.
21 // Code generated by "go run msg_generate.go"; DO NOT EDIT.
27 // getTypeStruct will take a type and the package scope, and return the
28 // (innermost) struct if the type is considered a RR type (currently defined as
29 // those structs beginning with a RR_Header, could be redefined as implementing
30 // the RR interface). The bool return value indicates if embedded structs were
32 func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
33 st, ok := t.Underlying().(*types.Struct)
37 if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
40 if st.Field(0).Anonymous() {
41 st, _ := getTypeStruct(st.Field(0).Type(), scope)
48 // Import and type-check the package
49 pkg, err := importer.Default().Import("github.com/miekg/dns")
53 // Collect actual types (*X)
54 var namedTypes []string
55 for _, name := range scope.Names() {
56 o := scope.Lookup(name)
57 if o == nil || !o.Exported() {
60 if st, _ := getTypeStruct(o.Type(), scope); st == nil {
63 if name == "PrivateRR" {
67 // Check if corresponding TypeX exists
68 if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" {
69 log.Fatalf("Constant Type%s does not exist.", o.Name())
72 namedTypes = append(namedTypes, o.Name())
76 b.WriteString(packageHdr)
78 fmt.Fprint(b, "// pack*() functions\n\n")
79 for _, name := range namedTypes {
80 o := scope.Lookup(name)
81 st, _ := getTypeStruct(o.Type(), scope)
83 fmt.Fprintf(b, "func (rr *%s) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {\n", name)
84 for i := 1; i < st.NumFields(); i++ {
86 fmt.Fprintf(b, s, st.Field(i).Name())
87 fmt.Fprint(b, `if err != nil {
93 if _, ok := st.Field(i).Type().(*types.Slice); ok {
95 case `dns:"-"`: // ignored
97 o("off, err = packStringTxt(rr.%s, msg, off)\n")
99 o("off, err = packDataOpt(rr.%s, msg, off)\n")
101 o("off, err = packDataNsec(rr.%s, msg, off)\n")
102 case `dns:"domain-name"`:
103 o("off, err = packDataDomainNames(rr.%s, msg, off, compression, false)\n")
105 log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
111 case st.Tag(i) == `dns:"-"`: // ignored
112 case st.Tag(i) == `dns:"cdomain-name"`:
113 o("off, err = packDomainName(rr.%s, msg, off, compression, compress)\n")
114 case st.Tag(i) == `dns:"domain-name"`:
115 o("off, err = packDomainName(rr.%s, msg, off, compression, false)\n")
116 case st.Tag(i) == `dns:"a"`:
117 o("off, err = packDataA(rr.%s, msg, off)\n")
118 case st.Tag(i) == `dns:"aaaa"`:
119 o("off, err = packDataAAAA(rr.%s, msg, off)\n")
120 case st.Tag(i) == `dns:"uint48"`:
121 o("off, err = packUint48(rr.%s, msg, off)\n")
122 case st.Tag(i) == `dns:"txt"`:
123 o("off, err = packString(rr.%s, msg, off)\n")
125 case strings.HasPrefix(st.Tag(i), `dns:"size-base32`): // size-base32 can be packed just like base32
127 case st.Tag(i) == `dns:"base32"`:
128 o("off, err = packStringBase32(rr.%s, msg, off)\n")
130 case strings.HasPrefix(st.Tag(i), `dns:"size-base64`): // size-base64 can be packed just like base64
132 case st.Tag(i) == `dns:"base64"`:
133 o("off, err = packStringBase64(rr.%s, msg, off)\n")
135 case strings.HasPrefix(st.Tag(i), `dns:"size-hex:SaltLength`):
136 // directly write instead of using o() so we get the error check in the correct place
137 field := st.Field(i).Name()
138 fmt.Fprintf(b, `// Only pack salt if value is not "-", i.e. empty
140 off, err = packStringHex(rr.%s, msg, off)
147 case strings.HasPrefix(st.Tag(i), `dns:"size-hex`): // size-hex can be packed just like hex
149 case st.Tag(i) == `dns:"hex"`:
150 o("off, err = packStringHex(rr.%s, msg, off)\n")
151 case st.Tag(i) == `dns:"any"`:
152 o("off, err = packStringAny(rr.%s, msg, off)\n")
153 case st.Tag(i) == `dns:"octet"`:
154 o("off, err = packStringOctet(rr.%s, msg, off)\n")
155 case st.Tag(i) == "":
156 switch st.Field(i).Type().(*types.Basic).Kind() {
158 o("off, err = packUint8(rr.%s, msg, off)\n")
160 o("off, err = packUint16(rr.%s, msg, off)\n")
162 o("off, err = packUint32(rr.%s, msg, off)\n")
164 o("off, err = packUint64(rr.%s, msg, off)\n")
166 o("off, err = packString(rr.%s, msg, off)\n")
168 log.Fatalln(name, st.Field(i).Name())
171 log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
174 fmt.Fprintln(b, "return off, nil }\n")
177 fmt.Fprint(b, "// unpack*() functions\n\n")
178 for _, name := range namedTypes {
179 o := scope.Lookup(name)
180 st, _ := getTypeStruct(o.Type(), scope)
182 fmt.Fprintf(b, "func (rr *%s) unpack(msg []byte, off int) (off1 int, err error) {\n", name)
183 fmt.Fprint(b, `rdStart := off
187 for i := 1; i < st.NumFields(); i++ {
188 o := func(s string) {
189 fmt.Fprintf(b, s, st.Field(i).Name())
190 fmt.Fprint(b, `if err != nil {
196 // size-* are special, because they reference a struct member we should use for the length.
197 if strings.HasPrefix(st.Tag(i), `dns:"size-`) {
198 structMember := structMember(st.Tag(i))
199 structTag := structTag(st.Tag(i))
202 fmt.Fprintf(b, "rr.%s, off, err = unpackStringHex(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
204 fmt.Fprintf(b, "rr.%s, off, err = unpackStringBase32(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
206 fmt.Fprintf(b, "rr.%s, off, err = unpackStringBase64(msg, off, off + int(rr.%s))\n", st.Field(i).Name(), structMember)
208 log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
210 fmt.Fprint(b, `if err != nil {
217 if _, ok := st.Field(i).Type().(*types.Slice); ok {
219 case `dns:"-"`: // ignored
221 o("rr.%s, off, err = unpackStringTxt(msg, off)\n")
223 o("rr.%s, off, err = unpackDataOpt(msg, off)\n")
225 o("rr.%s, off, err = unpackDataNsec(msg, off)\n")
226 case `dns:"domain-name"`:
227 o("rr.%s, off, err = unpackDataDomainNames(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
229 log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
235 case `dns:"-"`: // ignored
236 case `dns:"cdomain-name"`:
238 case `dns:"domain-name"`:
239 o("rr.%s, off, err = UnpackDomainName(msg, off)\n")
241 o("rr.%s, off, err = unpackDataA(msg, off)\n")
243 o("rr.%s, off, err = unpackDataAAAA(msg, off)\n")
245 o("rr.%s, off, err = unpackUint48(msg, off)\n")
247 o("rr.%s, off, err = unpackString(msg, off)\n")
249 o("rr.%s, off, err = unpackStringBase32(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
251 o("rr.%s, off, err = unpackStringBase64(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
253 o("rr.%s, off, err = unpackStringHex(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
255 o("rr.%s, off, err = unpackStringAny(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
257 o("rr.%s, off, err = unpackStringOctet(msg, off)\n")
259 switch st.Field(i).Type().(*types.Basic).Kind() {
261 o("rr.%s, off, err = unpackUint8(msg, off)\n")
263 o("rr.%s, off, err = unpackUint16(msg, off)\n")
265 o("rr.%s, off, err = unpackUint32(msg, off)\n")
267 o("rr.%s, off, err = unpackUint64(msg, off)\n")
269 o("rr.%s, off, err = unpackString(msg, off)\n")
271 log.Fatalln(name, st.Field(i).Name())
274 log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
276 // If we've hit len(msg) we return without error.
277 if i < st.NumFields()-1 {
278 fmt.Fprintf(b, `if off == len(msg) {
284 fmt.Fprintf(b, "return off, nil }\n\n")
288 res, err := format.Source(b.Bytes())
295 f, err := os.Create("zmsg.go")
301 // structMember will take a tag like dns:"size-base32:SaltLength" and return the last part of this string.
302 func structMember(s string) string {
303 fields := strings.Split(s, ":")
304 if len(fields) == 0 {
307 f := fields[len(fields)-1]
308 // f should have a closing "
315 // structTag will take a tag like dns:"size-base32:SaltLength" and return base32.
316 func structTag(s string) string {
317 fields := strings.Split(s, ":")
321 return fields[1][len("\"size-"):]
324 func fatalIfErr(err error) {