1package magna
2
3import (
4 "encoding/binary"
5 "fmt"
6 "net"
7 "strings"
8)
9
10func (a *A) Decode(buf []byte, offset int, rdlength int) (int, error) {
11 bytes, offset, err := getSlice(buf, offset, rdlength)
12 if err != nil {
13 return offset, err
14 }
15
16 a.Address = net.IP(bytes)
17 return offset, err
18}
19
20func (a *A) Encode(bytes []byte, offsets *map[string]uint16) []byte {
21 return append(bytes, a.Address.To4()...)
22}
23
24func (a A) String() string {
25 return a.Address.String()
26}
27
28func (ns *NS) Decode(buf []byte, offset int, rdlength int) (int, error) {
29 var err error
30 ns.NSDName, offset, err = decode_domain(buf, offset)
31 if err != nil {
32 return offset, err
33 }
34
35 return offset, err
36}
37
38func (ns *NS) Encode(bytes []byte, offsets *map[string]uint16) []byte {
39 return encode_domain(bytes, ns.NSDName, offsets)
40}
41
42func (ns NS) String() string {
43 return ns.NSDName
44}
45
46func (md *MD) Decode(buf []byte, offset int, rdlength int) (int, error) {
47 var err error
48 md.MADName, offset, err = decode_domain(buf, offset)
49 if err != nil {
50 return offset, err
51 }
52
53 return offset, err
54}
55
56func (md *MD) Encode(bytes []byte, offsets *map[string]uint16) []byte {
57 return encode_domain(bytes, md.MADName, offsets)
58}
59
60func (md MD) String() string {
61 return md.MADName
62}
63
64func (mf *MF) Decode(buf []byte, offset int, rdlength int) (int, error) {
65 var err error
66 mf.MADName, offset, err = decode_domain(buf, offset)
67 if err != nil {
68 return offset, err
69 }
70
71 return offset, err
72}
73
74func (mf *MF) Encode(bytes []byte, offsets *map[string]uint16) []byte {
75 return encode_domain(bytes, mf.MADName, offsets)
76}
77
78func (mf MF) String() string {
79 return mf.MADName
80}
81
82func (c *CNAME) Decode(buf []byte, offset int, rdlength int) (int, error) {
83 var err error
84 c.CName, offset, err = decode_domain(buf, offset)
85 if err != nil {
86 return offset, err
87 }
88
89 return offset, err
90}
91
92func (c *CNAME) Encode(bytes []byte, offsets *map[string]uint16) []byte {
93 return encode_domain(bytes, c.CName, offsets)
94}
95
96func (c CNAME) String() string {
97 return c.CName
98}
99
100func (soa *SOA) Decode(buf []byte, offset int, rdlength int) (int, error) {
101 var err error
102 soa.MName, offset, err = decode_domain(buf, offset)
103 if err != nil {
104 return offset, err
105 }
106
107 soa.RName, offset, err = decode_domain(buf, offset)
108 if err != nil {
109 return offset, err
110 }
111
112 soa.Serial, offset, err = getU32(buf, offset)
113 if err != nil {
114 return offset, err
115 }
116
117 soa.Refresh, offset, err = getU32(buf, offset)
118 if err != nil {
119 return offset, err
120 }
121
122 soa.Retry, offset, err = getU32(buf, offset)
123 if err != nil {
124 return offset, err
125 }
126
127 soa.Expire, offset, err = getU32(buf, offset)
128 if err != nil {
129 return offset, err
130 }
131
132 soa.Minimum, offset, err = getU32(buf, offset)
133 if err != nil {
134 return offset, err
135 }
136
137 return offset, err
138}
139
140func (soa *SOA) Encode(bytes []byte, offsets *map[string]uint16) []byte {
141 bytes = encode_domain(bytes, soa.MName, offsets)
142 bytes = encode_domain(bytes, soa.RName, offsets)
143 bytes = binary.BigEndian.AppendUint32(bytes, soa.Serial)
144 bytes = binary.BigEndian.AppendUint32(bytes, soa.Refresh)
145 bytes = binary.BigEndian.AppendUint32(bytes, soa.Retry)
146 bytes = binary.BigEndian.AppendUint32(bytes, soa.Expire)
147 bytes = binary.BigEndian.AppendUint32(bytes, soa.Minimum)
148
149 return bytes
150}
151
152func (soa SOA) String() string {
153 return fmt.Sprintf("%s %s %d %d %d %d %d", soa.MName, soa.RName, soa.Serial, soa.Refresh, soa.Retry, soa.Expire, soa.Minimum)
154}
155
156func (mb *MB) Decode(buf []byte, offset int, rdlength int) (int, error) {
157 madname, offset, err := decode_domain(buf, offset)
158 if err != nil {
159 return offset, err
160 }
161
162 mb.MADName = string(madname)
163 return offset, err
164}
165
166func (mb *MB) Encode(bytes []byte, offsets *map[string]uint16) []byte {
167 return encode_domain(bytes, mb.MADName, offsets)
168}
169
170func (mb MB) String() string {
171 return mb.MADName
172}
173
174func (mg *MG) Decode(buf []byte, offset int, rdlength int) (int, error) {
175 var err error
176 mg.MGMName, offset, err = decode_domain(buf, offset)
177 if err != nil {
178 return offset, err
179 }
180
181 return offset, err
182}
183
184func (mg *MG) Encode(bytes []byte, offsets *map[string]uint16) []byte {
185 return encode_domain(bytes, mg.MGMName, offsets)
186}
187
188func (mg MG) String() string {
189 return mg.MGMName
190}
191
192func (mr *MR) Decode(buf []byte, offset int, rdlength int) (int, error) {
193 var err error
194 mr.NEWName, offset, err = decode_domain(buf, offset)
195 if err != nil {
196 return offset, err
197 }
198
199 return offset, err
200}
201
202func (mr *MR) Encode(bytes []byte, offsets *map[string]uint16) []byte {
203 return encode_domain(bytes, mr.NEWName, offsets)
204}
205
206func (mr MR) String() string {
207 return mr.NEWName
208}
209
210func (null *NULL) Decode(buf []byte, offset int, rdlength int) (int, error) {
211 var err error
212 null.Anything, offset, err = getSlice(buf, offset, int(rdlength))
213 if err != nil {
214 return offset, err
215 }
216
217 return offset, err
218}
219
220func (null *NULL) Encode(bytes []byte, offsets *map[string]uint16) []byte {
221 return append(bytes, null.Anything...)
222}
223
224func (null NULL) String() string {
225 return string(null.Anything)
226}
227
228func (wks *WKS) Decode(buf []byte, offset int, rdlength int) (int, error) {
229 if rdlength < 5 {
230 return len(buf), &MagnaError{Message: fmt.Sprintf("magna: WKS RDLENGTH too short: %d", rdlength)}
231 }
232
233 addressBytes, nextOffset, err := getSlice(buf, offset, 4)
234 if err != nil {
235 return len(buf), fmt.Errorf("magna: WKS error reading address: %w", err)
236 }
237 offset = nextOffset
238 wks.Address = net.IP(addressBytes)
239
240 protocol, nextOffset, err := getU8(buf, offset)
241 if err != nil {
242 return len(buf), fmt.Errorf("magna: WKS error reading protocol: %w", err)
243 }
244 offset = nextOffset
245 wks.Protocol = protocol
246
247 bitmapLength := rdlength - 5
248 wks.BitMap, nextOffset, err = getSlice(buf, offset, bitmapLength)
249 if err != nil {
250 return len(buf), fmt.Errorf("magna: WKS error reading bitmap: %w", err)
251 }
252 offset = nextOffset
253
254 return offset, nil
255}
256
257func (wks *WKS) Encode(bytes []byte, offsets *map[string]uint16) []byte {
258 bytes = append(bytes, wks.Address.To4()...)
259 bytes = append(bytes, wks.Protocol)
260 bytes = append(bytes, wks.BitMap...)
261
262 return bytes
263}
264
265func (wks WKS) String() string {
266 return fmt.Sprintf("%s %d %s", wks.Address.String(), wks.Protocol, wks.BitMap)
267}
268
269func (ptr *PTR) Decode(buf []byte, offset int, rdlength int) (int, error) {
270 var err error
271 ptr.PTRDName, offset, err = decode_domain(buf, offset)
272 if err != nil {
273 return offset, err
274 }
275
276 return offset, err
277}
278
279func (ptr *PTR) Encode(bytes []byte, offsets *map[string]uint16) []byte {
280 return encode_domain(bytes, ptr.PTRDName, offsets)
281}
282
283func (ptr PTR) String() string {
284 return ptr.PTRDName
285}
286
287func (hinfo *HINFO) Decode(buf []byte, offset int, rdlength int) (int, error) {
288 endOffset := offset + rdlength
289 if endOffset > len(buf) {
290 return len(buf), &BufferOverflowError{Length: len(buf), Offset: endOffset}
291 }
292
293 currentOffset := offset
294 var err error
295
296 cpuLen, nextOffset, err := getU8(buf, currentOffset)
297 if err != nil {
298 return len(buf), fmt.Errorf("magna: HINFO error reading CPU length: %w", err)
299 }
300 currentOffset = nextOffset
301 if currentOffset+int(cpuLen) > endOffset {
302 return len(buf), &BufferOverflowError{Length: len(buf), Offset: currentOffset + int(cpuLen)}
303 }
304 cpuBytes, nextOffset, err := getSlice(buf, currentOffset, int(cpuLen))
305 if err != nil {
306 return len(buf), fmt.Errorf("magna: HINFO error reading CPU data: %w", err)
307 }
308 currentOffset = nextOffset
309 hinfo.CPU = string(cpuBytes)
310
311 osLen, nextOffset, err := getU8(buf, currentOffset)
312 if err != nil {
313 if currentOffset == endOffset {
314 return len(buf), &MagnaError{Message: "magna: HINFO missing OS string"}
315 }
316 return len(buf), fmt.Errorf("magna: HINFO error reading OS length: %w", err)
317 }
318 currentOffset = nextOffset
319 if currentOffset+int(osLen) > endOffset {
320 return len(buf), &BufferOverflowError{Length: len(buf), Offset: currentOffset + int(osLen)}
321 }
322 osBytes, nextOffset, err := getSlice(buf, currentOffset, int(osLen))
323 if err != nil {
324 return len(buf), fmt.Errorf("magna: HINFO error reading OS data: %w", err)
325 }
326 currentOffset = nextOffset
327 hinfo.OS = string(osBytes)
328
329 if currentOffset != endOffset {
330 return len(buf), &MagnaError{Message: fmt.Sprintf("magna: HINFO RDATA length mismatch, expected end at %d, ended at %d", endOffset, currentOffset)}
331 }
332
333 return currentOffset, nil
334}
335
336func (hinfo *HINFO) Encode(bytes []byte, offsets *map[string]uint16) []byte {
337 // XXX: should probally return an error
338 if len(hinfo.CPU) > 255 {
339 hinfo.CPU = hinfo.CPU[:255]
340 }
341 if len(hinfo.OS) > 255 {
342 hinfo.OS = hinfo.OS[:255]
343 }
344
345 bytes = append(bytes, byte(len(hinfo.CPU)))
346 bytes = append(bytes, []byte(hinfo.CPU)...)
347 bytes = append(bytes, byte(len(hinfo.OS)))
348 bytes = append(bytes, []byte(hinfo.OS)...)
349 return bytes
350}
351
352func (hinfo HINFO) String() string {
353 return fmt.Sprintf("%q %q", hinfo.CPU, hinfo.OS)
354}
355
356func (minfo *MINFO) Decode(buf []byte, offset int, rdlength int) (int, error) {
357 var err error
358
359 minfo.RMailBx, offset, err = decode_domain(buf, offset)
360 if err != nil {
361 return offset, err
362 }
363
364 minfo.EMailBx, offset, err = decode_domain(buf, offset)
365 if err != nil {
366 return offset, err
367 }
368
369 return offset, err
370}
371
372func (minfo *MINFO) Encode(bytes []byte, offsets *map[string]uint16) []byte {
373 bytes = encode_domain(bytes, minfo.RMailBx, offsets)
374 bytes = encode_domain(bytes, minfo.EMailBx, offsets)
375
376 return bytes
377}
378
379func (minfo MINFO) String() string {
380 return minfo.RMailBx + " " + minfo.EMailBx
381}
382
383func (mx *MX) Decode(buf []byte, offset int, rdlength int) (int, error) {
384 var err error
385 mx.Preference, offset, err = getU16(buf, offset)
386 if err != nil {
387 return offset, err
388 }
389
390 mx.Exchange, offset, err = decode_domain(buf, offset)
391 if err != nil {
392 return offset, err
393 }
394
395 return offset, err
396}
397
398func (mx *MX) Encode(bytes []byte, offsets *map[string]uint16) []byte {
399 bytes = binary.BigEndian.AppendUint16(bytes, mx.Preference)
400 bytes = encode_domain(bytes, mx.Exchange, offsets)
401
402 return bytes
403}
404
405func (mx MX) String() string {
406 return fmt.Sprintf("%d %s", mx.Preference, mx.Exchange)
407}
408
409func (txt *TXT) Decode(buf []byte, offset int, rdlength int) (int, error) {
410 txt.TxtData = make([]string, 0, 1)
411 endOffset := offset + rdlength
412 if endOffset > len(buf) {
413 return len(buf), &BufferOverflowError{Length: len(buf), Offset: endOffset}
414 }
415
416 currentOffset := offset
417 for currentOffset < endOffset {
418 strLen, nextOffsetAfterLen, err := getU8(buf, currentOffset)
419 if err != nil {
420 return len(buf), fmt.Errorf("magna: error reading TXT string length byte: %w", err)
421 }
422
423 nextOffsetAfterData := nextOffsetAfterLen + int(strLen)
424 if nextOffsetAfterData > endOffset {
425 return len(buf), &MagnaError{
426 Message: fmt.Sprintf("magna: TXT string segment length %d at offset %d exceeds RDLENGTH boundary %d", strLen, nextOffsetAfterLen, endOffset),
427 }
428 }
429
430 strBytes, actualNextOffsetAfterData, err := getSlice(buf, nextOffsetAfterLen, int(strLen))
431 if err != nil {
432 return len(buf), fmt.Errorf("magna: error reading TXT string data: %w", err)
433 }
434
435 txt.TxtData = append(txt.TxtData, string(strBytes))
436 currentOffset = actualNextOffsetAfterData
437 }
438
439 if currentOffset != endOffset {
440 return len(buf), &MagnaError{
441 Message: fmt.Sprintf("magna: TXT RDATA parsing finished at offset %d, but expected end at %d based on RDLENGTH", currentOffset, endOffset),
442 }
443 }
444
445 return currentOffset, nil
446}
447
448func (txt *TXT) Encode(bytes []byte, offsets *map[string]uint16) []byte {
449 for _, s := range txt.TxtData {
450 if len(s) > 255 {
451 // XXX: should return probably an error
452 s = s[:255]
453 }
454 bytes = append(bytes, byte(len(s)))
455 bytes = append(bytes, []byte(s)...)
456 }
457 return bytes
458}
459
460func (txt TXT) String() string {
461 quoted := make([]string, len(txt.TxtData))
462 for i, s := range txt.TxtData {
463 quoted[i] = fmt.Sprintf("%q", s)
464 }
465 return strings.Join(quoted, " ")
466}
467
468func (r *Reserved) Decode(buf []byte, offset int, rdlength int) (int, error) {
469 var err error
470 r.Bytes, offset, err = getSlice(buf, offset, int(rdlength))
471 if err != nil {
472 return offset, err
473 }
474
475 return offset, err
476}
477
478func (r *Reserved) Encode(bytes []byte, offsets *map[string]uint16) []byte {
479 return append(bytes, r.Bytes...)
480}
481
482func (r Reserved) String() string {
483 return string(r.Bytes)
484}
485
486// Decode decodes a resource record from buf at the offset.
487func (r *ResourceRecord) Decode(buf []byte, offset int) (int, error) {
488 name, offset, err := decode_domain(buf, offset)
489 if err != nil {
490 return offset, err
491 }
492 r.Name = name
493
494 rtype, offset, err := getU16(buf, offset)
495 if err != nil {
496 return offset, err
497 }
498 r.RType = DNSType(rtype)
499
500 rclass, offset, err := getU16(buf, offset)
501 if err != nil {
502 return offset, err
503 }
504 r.RClass = DNSClass(rclass)
505
506 r.TTL, offset, err = getU32(buf, offset)
507 if err != nil {
508 return offset, err
509 }
510
511 r.RDLength, offset, err = getU16(buf, offset)
512 if err != nil {
513 return offset, err
514 }
515
516 switch r.RType {
517 case 1:
518 r.RData = &A{}
519 case 2:
520 r.RData = &NS{}
521 case 3:
522 r.RData = &MD{}
523 case 4:
524 r.RData = &MF{}
525 case 5:
526 r.RData = &CNAME{}
527 case 6:
528 r.RData = &SOA{}
529 case 7:
530 r.RData = &MB{}
531 case 8:
532 r.RData = &MG{}
533 case 9:
534 r.RData = &MR{}
535 case 10:
536 r.RData = &NULL{}
537 case 11:
538 r.RData = &WKS{}
539 case 12:
540 r.RData = &PTR{}
541 case 13:
542 r.RData = &HINFO{}
543 case 14:
544 r.RData = &MINFO{}
545 case 15:
546 r.RData = &MX{}
547 case 16:
548 r.RData = &TXT{}
549 default:
550 r.RData = &Reserved{}
551 }
552
553 if r.RData != nil {
554 offset, err = r.RData.Decode(buf, offset, int(r.RDLength))
555 if err != nil {
556 return offset, err
557 }
558 }
559
560 return offset, nil
561}
562
563// Encode encdoes a resource record and returns the input bytes appened.
564func (r *ResourceRecord) Encode(bytes []byte, offsets *map[string]uint16) []byte {
565 bytes = encode_domain(bytes, r.Name, offsets)
566 bytes = binary.BigEndian.AppendUint16(bytes, uint16(r.RType))
567 bytes = binary.BigEndian.AppendUint16(bytes, uint16(r.RClass))
568 bytes = binary.BigEndian.AppendUint32(bytes, r.TTL)
569
570 rdata_start := len(bytes)
571 bytes = binary.BigEndian.AppendUint16(bytes, 0)
572 bytes = r.RData.Encode(bytes, offsets)
573 rdata_length := uint16(len(bytes) - rdata_start - 2)
574 binary.BigEndian.PutUint16(bytes[rdata_start:rdata_start+2], rdata_length)
575
576 return bytes
577}