1package magna
2
3import (
4 "fmt"
5 "math/rand"
6)
7
8// Decode decodes a DNS packet.
9func (m *Message) Decode(buf []byte) (err error) {
10 // gets checked when parsing additional records
11 m.HasEDNS = false
12
13 offset, err := m.Header.Decode(buf, 0)
14 if err != nil {
15 return fmt.Errorf("failed to decode message header: %w", err)
16 }
17
18 m.Question = make([]Question, 0, m.Header.QDCount)
19 for i := range m.Header.QDCount {
20 var question Question
21 offset, err = question.Decode(buf, offset)
22 if err != nil {
23 return fmt.Errorf("failed to decode question #%d: %w", i+1, err)
24 }
25
26 m.Question = append(m.Question, question)
27 }
28
29 m.Answer = make([]ResourceRecord, 0, m.Header.ANCount)
30 for i := range m.Header.ANCount {
31 var rr ResourceRecord
32 offset, err = rr.Decode(buf, offset)
33 if err != nil {
34 return fmt.Errorf("failed to decode answer record #%d: %w", i+1, err)
35 }
36
37 m.Answer = append(m.Answer, rr)
38 }
39
40 m.Authority = make([]ResourceRecord, 0, m.Header.NSCount)
41 for i := range m.Header.NSCount {
42 var rr ResourceRecord
43 offset, err = rr.Decode(buf, offset)
44 if err != nil {
45 return fmt.Errorf("failed to decode authority record #%d: %w", i+1, err)
46 }
47
48 m.Authority = append(m.Authority, rr)
49 }
50
51 m.Additional = make([]ResourceRecord, 0, m.Header.ARCount)
52 for i := range m.Header.ARCount {
53 var rr ResourceRecord
54 offset, err = rr.Decode(buf, offset)
55 if err != nil {
56 return fmt.Errorf("failed to decode additional record #%d: %w", i+1, err)
57 }
58
59 if rr.RType == OPTType {
60 opt, ok := rr.RData.(*OPT)
61 if !ok {
62 // this should never fail
63 return fmt.Errorf("unable to parse RData as OPT")
64 }
65
66 m.HasEDNS = true
67 m.ExtendedRCode = uint8(rr.TTL >> 24 & 0xFF000000)
68 m.EDNSVersion = uint8((rr.TTL & 0x00FF0000) >> 16)
69 m.EDNSFlags = uint16(rr.TTL & 0x0000FFFF)
70 m.EDNSOptions = opt.Options
71 m.UDPSize = uint16(rr.RClass)
72 }
73
74 m.Additional = append(m.Additional, rr)
75 }
76
77 if !m.HasEDNS {
78 m.ExtendedRCode = 0
79 m.EDNSVersion = 0
80 m.EDNSFlags = 0
81 m.EDNSOptions = make([]EDNSOption, 0)
82 m.UDPSize = 512 // default in rfc-1035 section 2.3.4.
83 }
84
85 return nil
86}
87
88// Encode encodes a message to a DNS packet.
89// TODO: set truncation bit if over 512 and udp is protocol
90func (m *Message) Encode() ([]byte, error) {
91 m.offsets = make(map[string]uint16)
92 bytes := make([]byte, 0, 512)
93
94 headerBytes := m.Header.Encode()
95 bytes = append(bytes, headerBytes...)
96
97 var err error
98
99 for i, question := range m.Question {
100 bytes, err = question.Encode(bytes, &m.offsets)
101 if err != nil {
102 return nil, fmt.Errorf("failed to encode question #%d (%s): %w", i+1, question.QName, err)
103 }
104 }
105
106 for i, answer := range m.Answer {
107 bytes, err = answer.Encode(bytes, &m.offsets)
108 if err != nil {
109 return nil, fmt.Errorf("failed to encode answer record #%d (%s): %w", i+1, answer.Name, err)
110 }
111 }
112
113 for i, authority := range m.Authority {
114 bytes, err = authority.Encode(bytes, &m.offsets)
115 if err != nil {
116 return nil, fmt.Errorf("failed to encode authority record #%d (%s): %w", i+1, authority.Name, err)
117 }
118 }
119
120 for i, additional := range m.Additional {
121 bytes, err = additional.Encode(bytes, &m.offsets)
122 if err != nil {
123 return nil, fmt.Errorf("failed to encode additional record #%d (%s): %w", i+1, additional.Name, err)
124 }
125 }
126
127 return bytes, nil
128}
129
130func CreateRequest(op OPCode, rd bool) *Message {
131 return &Message{
132 Header: Header{
133 ID: uint16(rand.Intn(65534) + 1),
134 QR: false,
135 OPCode: op,
136 AA: false,
137 TC: false,
138 RD: rd,
139 RA: false,
140 Z: 0,
141 RCode: NOERROR,
142 QDCount: 0,
143 ARCount: 0,
144 NSCount: 0,
145 ANCount: 0,
146 },
147 Question: make([]Question, 0),
148 Answer: make([]ResourceRecord, 0),
149 Additional: make([]ResourceRecord, 0),
150 Authority: make([]ResourceRecord, 0),
151 }
152}
153
154func (m *Message) CreateReply(req *Message) *Message {
155 m.Header.ID = req.Header.ID
156 m.Header.QR = true
157 m.Header.OPCode = req.Header.OPCode
158
159 return m
160}
161
162func (m *Message) AddQuestion(q Question) *Message {
163 m.Header.QDCount += 1
164 m.Question = append(m.Question, q)
165
166 return m
167}
168
169func (m *Message) SetRCode(rc RCode) *Message {
170 m.Header.RCode = rc
171
172 return m
173}