a go dns packet parser
at codereview 14 kB view raw
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}