1package dns
2
3import (
4	"fmt"
5	"strings"
6)
7
8// PrivateRdata is an interface used for implementing "Private Use" RR types, see
9// RFC 6895. This allows one to experiment with new RR types, without requesting an
10// official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove.
11type PrivateRdata interface {
12	// String returns the text presentaton of the Rdata of the Private RR.
13	String() string
14	// Parse parses the Rdata of the private RR.
15	Parse([]string) error
16	// Pack is used when packing a private RR into a buffer.
17	Pack([]byte) (int, error)
18	// Unpack is used when unpacking a private RR from a buffer.
19	// TODO(miek): diff. signature than Pack, see edns0.go for instance.
20	Unpack([]byte) (int, error)
21	// Copy copies the Rdata.
22	Copy(PrivateRdata) error
23	// Len returns the length in octets of the Rdata.
24	Len() int
25}
26
27// PrivateRR represents an RR that uses a PrivateRdata user-defined type.
28// It mocks normal RRs and implements dns.RR interface.
29type PrivateRR struct {
30	Hdr  RR_Header
31	Data PrivateRdata
32}
33
34func mkPrivateRR(rrtype uint16) *PrivateRR {
35	// Panics if RR is not an instance of PrivateRR.
36	rrfunc, ok := TypeToRR[rrtype]
37	if !ok {
38		panic(fmt.Sprintf("dns: invalid operation with Private RR type %d", rrtype))
39	}
40
41	anyrr := rrfunc()
42	switch rr := anyrr.(type) {
43	case *PrivateRR:
44		return rr
45	}
46	panic(fmt.Sprintf("dns: RR is not a PrivateRR, TypeToRR[%d] generator returned %T", rrtype, anyrr))
47}
48
49// Header return the RR header of r.
50func (r *PrivateRR) Header() *RR_Header { return &r.Hdr }
51
52func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() }
53
54// Private len and copy parts to satisfy RR interface.
55func (r *PrivateRR) len() int { return r.Hdr.len() + r.Data.Len() }
56func (r *PrivateRR) copy() RR {
57	// make new RR like this:
58	rr := mkPrivateRR(r.Hdr.Rrtype)
59	rr.Hdr = r.Hdr
60
61	err := r.Data.Copy(rr.Data)
62	if err != nil {
63		panic("dns: got value that could not be used to copy Private rdata")
64	}
65	return rr
66}
67func (r *PrivateRR) pack(msg []byte, off int, compression map[string]int, compress bool) (int, error) {
68	off, err := r.Hdr.pack(msg, off, compression, compress)
69	if err != nil {
70		return off, err
71	}
72	headerEnd := off
73	n, err := r.Data.Pack(msg[off:])
74	if err != nil {
75		return len(msg), err
76	}
77	off += n
78	r.Header().Rdlength = uint16(off - headerEnd)
79	return off, nil
80}
81
82// PrivateHandle registers a private resource record type. It requires
83// string and numeric representation of private RR type and generator function as argument.
84func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) {
85	rtypestr = strings.ToUpper(rtypestr)
86
87	TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} }
88	TypeToString[rtype] = rtypestr
89	StringToType[rtypestr] = rtype
90
91	typeToUnpack[rtype] = func(h RR_Header, msg []byte, off int) (RR, int, error) {
92		if noRdata(h) {
93			return &h, off, nil
94		}
95		var err error
96
97		rr := mkPrivateRR(h.Rrtype)
98		rr.Hdr = h
99
100		off1, err := rr.Data.Unpack(msg[off:])
101		off += off1
102		if err != nil {
103			return rr, off, err
104		}
105		return rr, off, err
106	}
107
108	setPrivateRR := func(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
109		rr := mkPrivateRR(h.Rrtype)
110		rr.Hdr = h
111
112		var l lex
113		text := make([]string, 0, 2) // could be 0..N elements, median is probably 1
114	Fetch:
115		for {
116			// TODO(miek): we could also be returning _QUOTE, this might or might not
117			// be an issue (basically parsing TXT becomes hard)
118			switch l = <-c; l.value {
119			case zNewline, zEOF:
120				break Fetch
121			case zString:
122				text = append(text, l.token)
123			}
124		}
125
126		err := rr.Data.Parse(text)
127		if err != nil {
128			return nil, &ParseError{f, err.Error(), l}, ""
129		}
130
131		return rr, nil, ""
132	}
133
134	typeToparserFunc[rtype] = parserFunc{setPrivateRR, true}
135}
136
137// PrivateHandleRemove removes defenitions required to support private RR type.
138func PrivateHandleRemove(rtype uint16) {
139	rtypestr, ok := TypeToString[rtype]
140	if ok {
141		delete(TypeToRR, rtype)
142		delete(TypeToString, rtype)
143		delete(typeToparserFunc, rtype)
144		delete(StringToType, rtypestr)
145		delete(typeToUnpack, rtype)
146	}
147	return
148}
149