1package dns
2
3import (
4	"net"
5	"reflect"
6	"strconv"
7)
8
9// NumField returns the number of rdata fields r has.
10func NumField(r RR) int {
11	return reflect.ValueOf(r).Elem().NumField() - 1 // Remove RR_Header
12}
13
14// Field returns the rdata field i as a string. Fields are indexed starting from 1.
15// RR types that holds slice data, for instance the NSEC type bitmap will return a single
16// string where the types are concatenated using a space.
17// Accessing non existing fields will cause a panic.
18func Field(r RR, i int) string {
19	if i == 0 {
20		return ""
21	}
22	d := reflect.ValueOf(r).Elem().Field(i)
23	switch d.Kind() {
24	case reflect.String:
25		return d.String()
26	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
27		return strconv.FormatInt(d.Int(), 10)
28	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
29		return strconv.FormatUint(d.Uint(), 10)
30	case reflect.Slice:
31		switch reflect.ValueOf(r).Elem().Type().Field(i).Tag {
32		case `dns:"a"`:
33			// TODO(miek): Hmm store this as 16 bytes
34			if d.Len() < net.IPv4len {
35				return ""
36			}
37			if d.Len() < net.IPv6len {
38				return net.IPv4(byte(d.Index(0).Uint()),
39					byte(d.Index(1).Uint()),
40					byte(d.Index(2).Uint()),
41					byte(d.Index(3).Uint())).String()
42			}
43			return net.IPv4(byte(d.Index(12).Uint()),
44				byte(d.Index(13).Uint()),
45				byte(d.Index(14).Uint()),
46				byte(d.Index(15).Uint())).String()
47		case `dns:"aaaa"`:
48			if d.Len() < net.IPv6len {
49				return ""
50			}
51			return net.IP{
52				byte(d.Index(0).Uint()),
53				byte(d.Index(1).Uint()),
54				byte(d.Index(2).Uint()),
55				byte(d.Index(3).Uint()),
56				byte(d.Index(4).Uint()),
57				byte(d.Index(5).Uint()),
58				byte(d.Index(6).Uint()),
59				byte(d.Index(7).Uint()),
60				byte(d.Index(8).Uint()),
61				byte(d.Index(9).Uint()),
62				byte(d.Index(10).Uint()),
63				byte(d.Index(11).Uint()),
64				byte(d.Index(12).Uint()),
65				byte(d.Index(13).Uint()),
66				byte(d.Index(14).Uint()),
67				byte(d.Index(15).Uint()),
68			}.String()
69		case `dns:"nsec"`:
70			if d.Len() == 0 {
71				return ""
72			}
73			s := Type(d.Index(0).Uint()).String()
74			for i := 1; i < d.Len(); i++ {
75				s += " " + Type(d.Index(i).Uint()).String()
76			}
77			return s
78		default:
79			// if it does not have a tag its a string slice
80			fallthrough
81		case `dns:"txt"`:
82			if d.Len() == 0 {
83				return ""
84			}
85			s := d.Index(0).String()
86			for i := 1; i < d.Len(); i++ {
87				s += " " + d.Index(i).String()
88			}
89			return s
90		}
91	}
92	return ""
93}
94