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 k := d.Kind(); k {
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.IPv6len {
35				return net.IPv4(byte(d.Index(0).Uint()),
36					byte(d.Index(1).Uint()),
37					byte(d.Index(2).Uint()),
38					byte(d.Index(3).Uint())).String()
39			}
40			return net.IPv4(byte(d.Index(12).Uint()),
41				byte(d.Index(13).Uint()),
42				byte(d.Index(14).Uint()),
43				byte(d.Index(15).Uint())).String()
44		case `dns:"aaaa"`:
45			return net.IP{
46				byte(d.Index(0).Uint()),
47				byte(d.Index(1).Uint()),
48				byte(d.Index(2).Uint()),
49				byte(d.Index(3).Uint()),
50				byte(d.Index(4).Uint()),
51				byte(d.Index(5).Uint()),
52				byte(d.Index(6).Uint()),
53				byte(d.Index(7).Uint()),
54				byte(d.Index(8).Uint()),
55				byte(d.Index(9).Uint()),
56				byte(d.Index(10).Uint()),
57				byte(d.Index(11).Uint()),
58				byte(d.Index(12).Uint()),
59				byte(d.Index(13).Uint()),
60				byte(d.Index(14).Uint()),
61				byte(d.Index(15).Uint()),
62			}.String()
63		case `dns:"nsec"`:
64			if d.Len() == 0 {
65				return ""
66			}
67			s := Type(d.Index(0).Uint()).String()
68			for i := 1; i < d.Len(); i++ {
69				s += " " + Type(d.Index(i).Uint()).String()
70			}
71			return s
72		default:
73			// if it does not have a tag its a string slice
74			fallthrough
75		case `dns:"txt"`:
76			if d.Len() == 0 {
77				return ""
78			}
79			s := d.Index(0).String()
80			for i := 1; i < d.Len(); i++ {
81				s += " " + d.Index(i).String()
82			}
83			return s
84		}
85	}
86	return ""
87}
88