1// DNS packet assembly, see RFC 1035. Converting from - Unpack() - 2// and to - Pack() - wire format. 3// All the packers and unpackers take a (msg []byte, off int) 4// and return (off1 int, ok bool). If they return ok==false, they 5// also return off1==len(msg), so that the next unpacker will 6// also fail. This lets us avoid checks of ok until the end of a 7// packing sequence. 8 9package dns 10 11//go:generate go run msg_generate.go 12 13import ( 14 "crypto/rand" 15 "encoding/binary" 16 "fmt" 17 "math/big" 18 "strconv" 19 "strings" 20) 21 22const ( 23 maxCompressionOffset = 2 << 13 // We have 14 bits for the compression pointer 24 maxDomainNameWireOctets = 255 // See RFC 1035 section 2.3.4 25 26 // This is the maximum number of compression pointers that should occur in a 27 // semantically valid message. Each label in a domain name must be at least one 28 // octet and is separated by a period. The root label won't be represented by a 29 // compression pointer to a compression pointer, hence the -2 to exclude the 30 // smallest valid root label. 31 // 32 // It is possible to construct a valid message that has more compression pointers 33 // than this, and still doesn't loop, by pointing to a previous pointer. This is 34 // not something a well written implementation should ever do, so we leave them 35 // to trip the maximum compression pointer check. 36 maxCompressionPointers = (maxDomainNameWireOctets+1)/2 - 2 37 38 // This is the maximum length of a domain name in presentation format. The 39 // maximum wire length of a domain name is 255 octets (see above), with the 40 // maximum label length being 63. The wire format requires one extra byte over 41 // the presentation format, reducing the number of octets by 1. Each label in 42 // the name will be separated by a single period, with each octet in the label 43 // expanding to at most 4 bytes (\DDD). If all other labels are of the maximum 44 // length, then the final label can only be 61 octets long to not exceed the 45 // maximum allowed wire length. 46 maxDomainNamePresentationLength = 61*4 + 1 + 63*4 + 1 + 63*4 + 1 + 63*4 + 1 47) 48 49// Errors defined in this package. 50var ( 51 ErrAlg error = &Error{err: "bad algorithm"} // ErrAlg indicates an error with the (DNSSEC) algorithm. 52 ErrAuth error = &Error{err: "bad authentication"} // ErrAuth indicates an error in the TSIG authentication. 53 ErrBuf error = &Error{err: "buffer size too small"} // ErrBuf indicates that the buffer used is too small for the message. 54 ErrConnEmpty error = &Error{err: "conn has no connection"} // ErrConnEmpty indicates a connection is being used before it is initialized. 55 ErrExtendedRcode error = &Error{err: "bad extended rcode"} // ErrExtendedRcode ... 56 ErrFqdn error = &Error{err: "domain must be fully qualified"} // ErrFqdn indicates that a domain name does not have a closing dot. 57 ErrId error = &Error{err: "id mismatch"} // ErrId indicates there is a mismatch with the message's ID. 58 ErrKeyAlg error = &Error{err: "bad key algorithm"} // ErrKeyAlg indicates that the algorithm in the key is not valid. 59 ErrKey error = &Error{err: "bad key"} 60 ErrKeySize error = &Error{err: "bad key size"} 61 ErrLongDomain error = &Error{err: fmt.Sprintf("domain name exceeded %d wire-format octets", maxDomainNameWireOctets)} 62 ErrNoSig error = &Error{err: "no signature found"} 63 ErrPrivKey error = &Error{err: "bad private key"} 64 ErrRcode error = &Error{err: "bad rcode"} 65 ErrRdata error = &Error{err: "bad rdata"} 66 ErrRRset error = &Error{err: "bad rrset"} 67 ErrSecret error = &Error{err: "no secrets defined"} 68 ErrShortRead error = &Error{err: "short read"} 69 ErrSig error = &Error{err: "bad signature"} // ErrSig indicates that a signature can not be cryptographically validated. 70 ErrSoa error = &Error{err: "no SOA"} // ErrSOA indicates that no SOA RR was seen when doing zone transfers. 71 ErrTime error = &Error{err: "bad time"} // ErrTime indicates a timing error in TSIG authentication. 72) 73 74// Id by default returns a 16-bit random number to be used as a message id. The 75// number is drawn from a cryptographically secure random number generator. 76// This being a variable the function can be reassigned to a custom function. 77// For instance, to make it return a static value for testing: 78// 79// dns.Id = func() uint16 { return 3 } 80var Id = id 81 82// id returns a 16 bits random number to be used as a 83// message id. The random provided should be good enough. 84func id() uint16 { 85 var output uint16 86 err := binary.Read(rand.Reader, binary.BigEndian, &output) 87 if err != nil { 88 panic("dns: reading random id failed: " + err.Error()) 89 } 90 return output 91} 92 93// MsgHdr is a a manually-unpacked version of (id, bits). 94type MsgHdr struct { 95 Id uint16 96 Response bool 97 Opcode int 98 Authoritative bool 99 Truncated bool 100 RecursionDesired bool 101 RecursionAvailable bool 102 Zero bool 103 AuthenticatedData bool 104 CheckingDisabled bool 105 Rcode int 106} 107 108// Msg contains the layout of a DNS message. 109type Msg struct { 110 MsgHdr 111 Compress bool `json:"-"` // If true, the message will be compressed when converted to wire format. 112 Question []Question // Holds the RR(s) of the question section. 113 Answer []RR // Holds the RR(s) of the answer section. 114 Ns []RR // Holds the RR(s) of the authority section. 115 Extra []RR // Holds the RR(s) of the additional section. 116} 117 118// ClassToString is a maps Classes to strings for each CLASS wire type. 119var ClassToString = map[uint16]string{ 120 ClassINET: "IN", 121 ClassCSNET: "CS", 122 ClassCHAOS: "CH", 123 ClassHESIOD: "HS", 124 ClassNONE: "NONE", 125 ClassANY: "ANY", 126} 127 128// OpcodeToString maps Opcodes to strings. 129var OpcodeToString = map[int]string{ 130 OpcodeQuery: "QUERY", 131 OpcodeIQuery: "IQUERY", 132 OpcodeStatus: "STATUS", 133 OpcodeNotify: "NOTIFY", 134 OpcodeUpdate: "UPDATE", 135} 136 137// RcodeToString maps Rcodes to strings. 138var RcodeToString = map[int]string{ 139 RcodeSuccess: "NOERROR", 140 RcodeFormatError: "FORMERR", 141 RcodeServerFailure: "SERVFAIL", 142 RcodeNameError: "NXDOMAIN", 143 RcodeNotImplemented: "NOTIMP", 144 RcodeRefused: "REFUSED", 145 RcodeYXDomain: "YXDOMAIN", // See RFC 2136 146 RcodeYXRrset: "YXRRSET", 147 RcodeNXRrset: "NXRRSET", 148 RcodeNotAuth: "NOTAUTH", 149 RcodeNotZone: "NOTZONE", 150 RcodeBadSig: "BADSIG", // Also known as RcodeBadVers, see RFC 6891 151 // RcodeBadVers: "BADVERS", 152 RcodeBadKey: "BADKEY", 153 RcodeBadTime: "BADTIME", 154 RcodeBadMode: "BADMODE", 155 RcodeBadName: "BADNAME", 156 RcodeBadAlg: "BADALG", 157 RcodeBadTrunc: "BADTRUNC", 158 RcodeBadCookie: "BADCOOKIE", 159} 160 161// compressionMap is used to allow a more efficient compression map 162// to be used for internal packDomainName calls without changing the 163// signature or functionality of public API. 164// 165// In particular, map[string]uint16 uses 25% less per-entry memory 166// than does map[string]int. 167type compressionMap struct { 168 ext map[string]int // external callers 169 int map[string]uint16 // internal callers 170} 171 172func (m compressionMap) valid() bool { 173 return m.int != nil || m.ext != nil 174} 175 176func (m compressionMap) insert(s string, pos int) { 177 if m.ext != nil { 178 m.ext[s] = pos 179 } else { 180 m.int[s] = uint16(pos) 181 } 182} 183 184func (m compressionMap) find(s string) (int, bool) { 185 if m.ext != nil { 186 pos, ok := m.ext[s] 187 return pos, ok 188 } 189 190 pos, ok := m.int[s] 191 return int(pos), ok 192} 193 194// Domain names are a sequence of counted strings 195// split at the dots. They end with a zero-length string. 196 197// PackDomainName packs a domain name s into msg[off:]. 198// If compression is wanted compress must be true and the compression 199// map needs to hold a mapping between domain names and offsets 200// pointing into msg. 201func PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { 202 return packDomainName(s, msg, off, compressionMap{ext: compression}, compress) 203} 204 205func packDomainName(s string, msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) { 206 // XXX: A logical copy of this function exists in IsDomainName and 207 // should be kept in sync with this function. 208 209 ls := len(s) 210 if ls == 0 { // Ok, for instance when dealing with update RR without any rdata. 211 return off, nil 212 } 213 214 // If not fully qualified, error out. 215 if !IsFqdn(s) { 216 return len(msg), ErrFqdn 217 } 218 219 // Each dot ends a segment of the name. 220 // We trade each dot byte for a length byte. 221 // Except for escaped dots (\.), which are normal dots. 222 // There is also a trailing zero. 223 224 // Compression 225 pointer := -1 226 227 // Emit sequence of counted strings, chopping at dots. 228 var ( 229 begin int 230 compBegin int 231 compOff int 232 bs []byte 233 wasDot bool 234 ) 235loop: 236 for i := 0; i < ls; i++ { 237 var c byte 238 if bs == nil { 239 c = s[i] 240 } else { 241 c = bs[i] 242 } 243 244 switch c { 245 case '\\': 246 if off+1 > len(msg) { 247 return len(msg), ErrBuf 248 } 249 250 if bs == nil { 251 bs = []byte(s) 252 } 253 254 // check for \DDD 255 if i+3 < ls && isDigit(bs[i+1]) && isDigit(bs[i+2]) && isDigit(bs[i+3]) { 256 bs[i] = dddToByte(bs[i+1:]) 257 copy(bs[i+1:ls-3], bs[i+4:]) 258 ls -= 3 259 compOff += 3 260 } else { 261 copy(bs[i:ls-1], bs[i+1:]) 262 ls-- 263 compOff++ 264 } 265 266 wasDot = false 267 case '.': 268 if wasDot { 269 // two dots back to back is not legal 270 return len(msg), ErrRdata 271 } 272 wasDot = true 273 274 labelLen := i - begin 275 if labelLen >= 1<<6 { // top two bits of length must be clear 276 return len(msg), ErrRdata 277 } 278 279 // off can already (we're in a loop) be bigger than len(msg) 280 // this happens when a name isn't fully qualified 281 if off+1+labelLen > len(msg) { 282 return len(msg), ErrBuf 283 } 284 285 // Don't try to compress '.' 286 // We should only compress when compress is true, but we should also still pick 287 // up names that can be used for *future* compression(s). 288 if compression.valid() && !isRootLabel(s, bs, begin, ls) { 289 if p, ok := compression.find(s[compBegin:]); ok { 290 // The first hit is the longest matching dname 291 // keep the pointer offset we get back and store 292 // the offset of the current name, because that's 293 // where we need to insert the pointer later 294 295 // If compress is true, we're allowed to compress this dname 296 if compress { 297 pointer = p // Where to point to 298 break loop 299 } 300 } else if off < maxCompressionOffset { 301 // Only offsets smaller than maxCompressionOffset can be used. 302 compression.insert(s[compBegin:], off) 303 } 304 } 305 306 // The following is covered by the length check above. 307 msg[off] = byte(labelLen) 308 309 if bs == nil { 310 copy(msg[off+1:], s[begin:i]) 311 } else { 312 copy(msg[off+1:], bs[begin:i]) 313 } 314 off += 1 + labelLen 315 316 begin = i + 1 317 compBegin = begin + compOff 318 default: 319 wasDot = false 320 } 321 } 322 323 // Root label is special 324 if isRootLabel(s, bs, 0, ls) { 325 return off, nil 326 } 327 328 // If we did compression and we find something add the pointer here 329 if pointer != -1 { 330 // We have two bytes (14 bits) to put the pointer in 331 binary.BigEndian.PutUint16(msg[off:], uint16(pointer^0xC000)) 332 return off + 2, nil 333 } 334 335 if off < len(msg) { 336 msg[off] = 0 337 } 338 339 return off + 1, nil 340} 341 342// isRootLabel returns whether s or bs, from off to end, is the root 343// label ".". 344// 345// If bs is nil, s will be checked, otherwise bs will be checked. 346func isRootLabel(s string, bs []byte, off, end int) bool { 347 if bs == nil { 348 return s[off:end] == "." 349 } 350 351 return end-off == 1 && bs[off] == '.' 352} 353 354// Unpack a domain name. 355// In addition to the simple sequences of counted strings above, 356// domain names are allowed to refer to strings elsewhere in the 357// packet, to avoid repeating common suffixes when returning 358// many entries in a single domain. The pointers are marked 359// by a length byte with the top two bits set. Ignoring those 360// two bits, that byte and the next give a 14 bit offset from msg[0] 361// where we should pick up the trail. 362// Note that if we jump elsewhere in the packet, 363// we return off1 == the offset after the first pointer we found, 364// which is where the next record will start. 365// In theory, the pointers are only allowed to jump backward. 366// We let them jump anywhere and stop jumping after a while. 367 368// UnpackDomainName unpacks a domain name into a string. It returns 369// the name, the new offset into msg and any error that occurred. 370// 371// When an error is encountered, the unpacked name will be discarded 372// and len(msg) will be returned as the offset. 373func UnpackDomainName(msg []byte, off int) (string, int, error) { 374 s := make([]byte, 0, maxDomainNamePresentationLength) 375 off1 := 0 376 lenmsg := len(msg) 377 budget := maxDomainNameWireOctets 378 ptr := 0 // number of pointers followed 379Loop: 380 for { 381 if off >= lenmsg { 382 return "", lenmsg, ErrBuf 383 } 384 c := int(msg[off]) 385 off++ 386 switch c & 0xC0 { 387 case 0x00: 388 if c == 0x00 { 389 // end of name 390 break Loop 391 } 392 // literal string 393 if off+c > lenmsg { 394 return "", lenmsg, ErrBuf 395 } 396 budget -= c + 1 // +1 for the label separator 397 if budget <= 0 { 398 return "", lenmsg, ErrLongDomain 399 } 400 for _, b := range msg[off : off+c] { 401 if isDomainNameLabelSpecial(b) { 402 s = append(s, '\\', b) 403 } else if b < ' ' || b > '~' { 404 s = append(s, escapeByte(b)...) 405 } else { 406 s = append(s, b) 407 } 408 } 409 s = append(s, '.') 410 off += c 411 case 0xC0: 412 // pointer to somewhere else in msg. 413 // remember location after first ptr, 414 // since that's how many bytes we consumed. 415 // also, don't follow too many pointers -- 416 // maybe there's a loop. 417 if off >= lenmsg { 418 return "", lenmsg, ErrBuf 419 } 420 c1 := msg[off] 421 off++ 422 if ptr == 0 { 423 off1 = off 424 } 425 if ptr++; ptr > maxCompressionPointers { 426 return "", lenmsg, &Error{err: "too many compression pointers"} 427 } 428 // pointer should guarantee that it advances and points forwards at least 429 // but the condition on previous three lines guarantees that it's 430 // at least loop-free 431 off = (c^0xC0)<<8 | int(c1) 432 default: 433 // 0x80 and 0x40 are reserved 434 return "", lenmsg, ErrRdata 435 } 436 } 437 if ptr == 0 { 438 off1 = off 439 } 440 if len(s) == 0 { 441 return ".", off1, nil 442 } 443 return string(s), off1, nil 444} 445 446func packTxt(txt []string, msg []byte, offset int, tmp []byte) (int, error) { 447 if len(txt) == 0 { 448 if offset >= len(msg) { 449 return offset, ErrBuf 450 } 451 msg[offset] = 0 452 return offset, nil 453 } 454 var err error 455 for _, s := range txt { 456 if len(s) > len(tmp) { 457 return offset, ErrBuf 458 } 459 offset, err = packTxtString(s, msg, offset, tmp) 460 if err != nil { 461 return offset, err 462 } 463 } 464 return offset, nil 465} 466 467func packTxtString(s string, msg []byte, offset int, tmp []byte) (int, error) { 468 lenByteOffset := offset 469 if offset >= len(msg) || len(s) > len(tmp) { 470 return offset, ErrBuf 471 } 472 offset++ 473 bs := tmp[:len(s)] 474 copy(bs, s) 475 for i := 0; i < len(bs); i++ { 476 if len(msg) <= offset { 477 return offset, ErrBuf 478 } 479 if bs[i] == '\\' { 480 i++ 481 if i == len(bs) { 482 break 483 } 484 // check for \DDD 485 if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) { 486 msg[offset] = dddToByte(bs[i:]) 487 i += 2 488 } else { 489 msg[offset] = bs[i] 490 } 491 } else { 492 msg[offset] = bs[i] 493 } 494 offset++ 495 } 496 l := offset - lenByteOffset - 1 497 if l > 255 { 498 return offset, &Error{err: "string exceeded 255 bytes in txt"} 499 } 500 msg[lenByteOffset] = byte(l) 501 return offset, nil 502} 503 504func packOctetString(s string, msg []byte, offset int, tmp []byte) (int, error) { 505 if offset >= len(msg) || len(s) > len(tmp) { 506 return offset, ErrBuf 507 } 508 bs := tmp[:len(s)] 509 copy(bs, s) 510 for i := 0; i < len(bs); i++ { 511 if len(msg) <= offset { 512 return offset, ErrBuf 513 } 514 if bs[i] == '\\' { 515 i++ 516 if i == len(bs) { 517 break 518 } 519 // check for \DDD 520 if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) { 521 msg[offset] = dddToByte(bs[i:]) 522 i += 2 523 } else { 524 msg[offset] = bs[i] 525 } 526 } else { 527 msg[offset] = bs[i] 528 } 529 offset++ 530 } 531 return offset, nil 532} 533 534func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) { 535 off = off0 536 var s string 537 for off < len(msg) && err == nil { 538 s, off, err = unpackString(msg, off) 539 if err == nil { 540 ss = append(ss, s) 541 } 542 } 543 return 544} 545 546// Helpers for dealing with escaped bytes 547func isDigit(b byte) bool { return b >= '0' && b <= '9' } 548 549func dddToByte(s []byte) byte { 550 _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808 551 return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')) 552} 553 554func dddStringToByte(s string) byte { 555 _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808 556 return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0')) 557} 558 559// Helper function for packing and unpacking 560func intToBytes(i *big.Int, length int) []byte { 561 buf := i.Bytes() 562 if len(buf) < length { 563 b := make([]byte, length) 564 copy(b[length-len(buf):], buf) 565 return b 566 } 567 return buf 568} 569 570// PackRR packs a resource record rr into msg[off:]. 571// See PackDomainName for documentation about the compression. 572func PackRR(rr RR, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) { 573 headerEnd, off1, err := packRR(rr, msg, off, compressionMap{ext: compression}, compress) 574 if err == nil { 575 // packRR no longer sets the Rdlength field on the rr, but 576 // callers might be expecting it so we set it here. 577 rr.Header().Rdlength = uint16(off1 - headerEnd) 578 } 579 return off1, err 580} 581 582func packRR(rr RR, msg []byte, off int, compression compressionMap, compress bool) (headerEnd int, off1 int, err error) { 583 if rr == nil { 584 return len(msg), len(msg), &Error{err: "nil rr"} 585 } 586 587 headerEnd, err = rr.Header().packHeader(msg, off, compression, compress) 588 if err != nil { 589 return headerEnd, len(msg), err 590 } 591 592 off1, err = rr.pack(msg, headerEnd, compression, compress) 593 if err != nil { 594 return headerEnd, len(msg), err 595 } 596 597 rdlength := off1 - headerEnd 598 if int(uint16(rdlength)) != rdlength { // overflow 599 return headerEnd, len(msg), ErrRdata 600 } 601 602 // The RDLENGTH field is the last field in the header and we set it here. 603 binary.BigEndian.PutUint16(msg[headerEnd-2:], uint16(rdlength)) 604 return headerEnd, off1, nil 605} 606 607// UnpackRR unpacks msg[off:] into an RR. 608func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) { 609 h, off, msg, err := unpackHeader(msg, off) 610 if err != nil { 611 return nil, len(msg), err 612 } 613 614 return UnpackRRWithHeader(h, msg, off) 615} 616 617// UnpackRRWithHeader unpacks the record type specific payload given an existing 618// RR_Header. 619func UnpackRRWithHeader(h RR_Header, msg []byte, off int) (rr RR, off1 int, err error) { 620 if newFn, ok := TypeToRR[h.Rrtype]; ok { 621 rr = newFn() 622 *rr.Header() = h 623 } else { 624 rr = &RFC3597{Hdr: h} 625 } 626 627 if off < 0 || off > len(msg) { 628 return &h, off, &Error{err: "bad off"} 629 } 630 631 end := off + int(h.Rdlength) 632 if end < off || end > len(msg) { 633 return &h, end, &Error{err: "bad rdlength"} 634 } 635 636 if noRdata(h) { 637 return rr, off, nil 638 } 639 640 off, err = rr.unpack(msg, off) 641 if err != nil { 642 return nil, end, err 643 } 644 if off != end { 645 return &h, end, &Error{err: "bad rdlength"} 646 } 647 648 return rr, off, nil 649} 650 651// unpackRRslice unpacks msg[off:] into an []RR. 652// If we cannot unpack the whole array, then it will return nil 653func unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error) { 654 var r RR 655 // Don't pre-allocate, l may be under attacker control 656 var dst []RR 657 for i := 0; i < l; i++ { 658 off1 := off 659 r, off, err = UnpackRR(msg, off) 660 if err != nil { 661 off = len(msg) 662 break 663 } 664 // If offset does not increase anymore, l is a lie 665 if off1 == off { 666 break 667 } 668 dst = append(dst, r) 669 } 670 if err != nil && off == len(msg) { 671 dst = nil 672 } 673 return dst, off, err 674} 675 676// Convert a MsgHdr to a string, with dig-like headers: 677// 678//;; opcode: QUERY, status: NOERROR, id: 48404 679// 680//;; flags: qr aa rd ra; 681func (h *MsgHdr) String() string { 682 if h == nil { 683 return "<nil> MsgHdr" 684 } 685 686 s := ";; opcode: " + OpcodeToString[h.Opcode] 687 s += ", status: " + RcodeToString[h.Rcode] 688 s += ", id: " + strconv.Itoa(int(h.Id)) + "\n" 689 690 s += ";; flags:" 691 if h.Response { 692 s += " qr" 693 } 694 if h.Authoritative { 695 s += " aa" 696 } 697 if h.Truncated { 698 s += " tc" 699 } 700 if h.RecursionDesired { 701 s += " rd" 702 } 703 if h.RecursionAvailable { 704 s += " ra" 705 } 706 if h.Zero { // Hmm 707 s += " z" 708 } 709 if h.AuthenticatedData { 710 s += " ad" 711 } 712 if h.CheckingDisabled { 713 s += " cd" 714 } 715 716 s += ";" 717 return s 718} 719 720// Pack packs a Msg: it is converted to to wire format. 721// If the dns.Compress is true the message will be in compressed wire format. 722func (dns *Msg) Pack() (msg []byte, err error) { 723 return dns.PackBuffer(nil) 724} 725 726// PackBuffer packs a Msg, using the given buffer buf. If buf is too small a new buffer is allocated. 727func (dns *Msg) PackBuffer(buf []byte) (msg []byte, err error) { 728 // If this message can't be compressed, avoid filling the 729 // compression map and creating garbage. 730 if dns.Compress && dns.isCompressible() { 731 compression := make(map[string]uint16) // Compression pointer mappings. 732 return dns.packBufferWithCompressionMap(buf, compressionMap{int: compression}, true) 733 } 734 735 return dns.packBufferWithCompressionMap(buf, compressionMap{}, false) 736} 737 738// packBufferWithCompressionMap packs a Msg, using the given buffer buf. 739func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression compressionMap, compress bool) (msg []byte, err error) { 740 if dns.Rcode < 0 || dns.Rcode > 0xFFF { 741 return nil, ErrRcode 742 } 743 744 // Set extended rcode unconditionally if we have an opt, this will allow 745 // resetting the extended rcode bits if they need to. 746 if opt := dns.IsEdns0(); opt != nil { 747 opt.SetExtendedRcode(uint16(dns.Rcode)) 748 } else if dns.Rcode > 0xF { 749 // If Rcode is an extended one and opt is nil, error out. 750 return nil, ErrExtendedRcode 751 } 752 753 // Convert convenient Msg into wire-like Header. 754 var dh Header 755 dh.Id = dns.Id 756 dh.Bits = uint16(dns.Opcode)<<11 | uint16(dns.Rcode&0xF) 757 if dns.Response { 758 dh.Bits |= _QR 759 } 760 if dns.Authoritative { 761 dh.Bits |= _AA 762 } 763 if dns.Truncated { 764 dh.Bits |= _TC 765 } 766 if dns.RecursionDesired { 767 dh.Bits |= _RD 768 } 769 if dns.RecursionAvailable { 770 dh.Bits |= _RA 771 } 772 if dns.Zero { 773 dh.Bits |= _Z 774 } 775 if dns.AuthenticatedData { 776 dh.Bits |= _AD 777 } 778 if dns.CheckingDisabled { 779 dh.Bits |= _CD 780 } 781 782 dh.Qdcount = uint16(len(dns.Question)) 783 dh.Ancount = uint16(len(dns.Answer)) 784 dh.Nscount = uint16(len(dns.Ns)) 785 dh.Arcount = uint16(len(dns.Extra)) 786 787 // We need the uncompressed length here, because we first pack it and then compress it. 788 msg = buf 789 uncompressedLen := msgLenWithCompressionMap(dns, nil) 790 if packLen := uncompressedLen + 1; len(msg) < packLen { 791 msg = make([]byte, packLen) 792 } 793 794 // Pack it in: header and then the pieces. 795 off := 0 796 off, err = dh.pack(msg, off, compression, compress) 797 if err != nil { 798 return nil, err 799 } 800 for _, r := range dns.Question { 801 off, err = r.pack(msg, off, compression, compress) 802 if err != nil { 803 return nil, err 804 } 805 } 806 for _, r := range dns.Answer { 807 _, off, err = packRR(r, msg, off, compression, compress) 808 if err != nil { 809 return nil, err 810 } 811 } 812 for _, r := range dns.Ns { 813 _, off, err = packRR(r, msg, off, compression, compress) 814 if err != nil { 815 return nil, err 816 } 817 } 818 for _, r := range dns.Extra { 819 _, off, err = packRR(r, msg, off, compression, compress) 820 if err != nil { 821 return nil, err 822 } 823 } 824 return msg[:off], nil 825} 826 827func (dns *Msg) unpack(dh Header, msg []byte, off int) (err error) { 828 // If we are at the end of the message we should return *just* the 829 // header. This can still be useful to the caller. 9.9.9.9 sends these 830 // when responding with REFUSED for instance. 831 if off == len(msg) { 832 // reset sections before returning 833 dns.Question, dns.Answer, dns.Ns, dns.Extra = nil, nil, nil, nil 834 return nil 835 } 836 837 // Qdcount, Ancount, Nscount, Arcount can't be trusted, as they are 838 // attacker controlled. This means we can't use them to pre-allocate 839 // slices. 840 dns.Question = nil 841 for i := 0; i < int(dh.Qdcount); i++ { 842 off1 := off 843 var q Question 844 q, off, err = unpackQuestion(msg, off) 845 if err != nil { 846 return err 847 } 848 if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie! 849 dh.Qdcount = uint16(i) 850 break 851 } 852 dns.Question = append(dns.Question, q) 853 } 854 855 dns.Answer, off, err = unpackRRslice(int(dh.Ancount), msg, off) 856 // The header counts might have been wrong so we need to update it 857 dh.Ancount = uint16(len(dns.Answer)) 858 if err == nil { 859 dns.Ns, off, err = unpackRRslice(int(dh.Nscount), msg, off) 860 } 861 // The header counts might have been wrong so we need to update it 862 dh.Nscount = uint16(len(dns.Ns)) 863 if err == nil { 864 dns.Extra, off, err = unpackRRslice(int(dh.Arcount), msg, off) 865 } 866 // The header counts might have been wrong so we need to update it 867 dh.Arcount = uint16(len(dns.Extra)) 868 869 // Set extended Rcode 870 if opt := dns.IsEdns0(); opt != nil { 871 dns.Rcode |= opt.ExtendedRcode() 872 } 873 874 if off != len(msg) { 875 // TODO(miek) make this an error? 876 // use PackOpt to let people tell how detailed the error reporting should be? 877 // println("dns: extra bytes in dns packet", off, "<", len(msg)) 878 } 879 return err 880 881} 882 883// Unpack unpacks a binary message to a Msg structure. 884func (dns *Msg) Unpack(msg []byte) (err error) { 885 dh, off, err := unpackMsgHdr(msg, 0) 886 if err != nil { 887 return err 888 } 889 890 dns.setHdr(dh) 891 return dns.unpack(dh, msg, off) 892} 893 894// Convert a complete message to a string with dig-like output. 895func (dns *Msg) String() string { 896 if dns == nil { 897 return "<nil> MsgHdr" 898 } 899 s := dns.MsgHdr.String() + " " 900 s += "QUERY: " + strconv.Itoa(len(dns.Question)) + ", " 901 s += "ANSWER: " + strconv.Itoa(len(dns.Answer)) + ", " 902 s += "AUTHORITY: " + strconv.Itoa(len(dns.Ns)) + ", " 903 s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" 904 if len(dns.Question) > 0 { 905 s += "\n;; QUESTION SECTION:\n" 906 for _, r := range dns.Question { 907 s += r.String() + "\n" 908 } 909 } 910 if len(dns.Answer) > 0 { 911 s += "\n;; ANSWER SECTION:\n" 912 for _, r := range dns.Answer { 913 if r != nil { 914 s += r.String() + "\n" 915 } 916 } 917 } 918 if len(dns.Ns) > 0 { 919 s += "\n;; AUTHORITY SECTION:\n" 920 for _, r := range dns.Ns { 921 if r != nil { 922 s += r.String() + "\n" 923 } 924 } 925 } 926 if len(dns.Extra) > 0 { 927 s += "\n;; ADDITIONAL SECTION:\n" 928 for _, r := range dns.Extra { 929 if r != nil { 930 s += r.String() + "\n" 931 } 932 } 933 } 934 return s 935} 936 937// isCompressible returns whether the msg may be compressible. 938func (dns *Msg) isCompressible() bool { 939 // If we only have one question, there is nothing we can ever compress. 940 return len(dns.Question) > 1 || len(dns.Answer) > 0 || 941 len(dns.Ns) > 0 || len(dns.Extra) > 0 942} 943 944// Len returns the message length when in (un)compressed wire format. 945// If dns.Compress is true compression it is taken into account. Len() 946// is provided to be a faster way to get the size of the resulting packet, 947// than packing it, measuring the size and discarding the buffer. 948func (dns *Msg) Len() int { 949 // If this message can't be compressed, avoid filling the 950 // compression map and creating garbage. 951 if dns.Compress && dns.isCompressible() { 952 compression := make(map[string]struct{}) 953 return msgLenWithCompressionMap(dns, compression) 954 } 955 956 return msgLenWithCompressionMap(dns, nil) 957} 958 959func msgLenWithCompressionMap(dns *Msg, compression map[string]struct{}) int { 960 l := headerSize 961 962 for _, r := range dns.Question { 963 l += r.len(l, compression) 964 } 965 for _, r := range dns.Answer { 966 if r != nil { 967 l += r.len(l, compression) 968 } 969 } 970 for _, r := range dns.Ns { 971 if r != nil { 972 l += r.len(l, compression) 973 } 974 } 975 for _, r := range dns.Extra { 976 if r != nil { 977 l += r.len(l, compression) 978 } 979 } 980 981 return l 982} 983 984func domainNameLen(s string, off int, compression map[string]struct{}, compress bool) int { 985 if s == "" || s == "." { 986 return 1 987 } 988 989 escaped := strings.Contains(s, "\\") 990 991 if compression != nil && (compress || off < maxCompressionOffset) { 992 // compressionLenSearch will insert the entry into the compression 993 // map if it doesn't contain it. 994 if l, ok := compressionLenSearch(compression, s, off); ok && compress { 995 if escaped { 996 return escapedNameLen(s[:l]) + 2 997 } 998 999 return l + 2 1000 } 1001 } 1002 1003 if escaped { 1004 return escapedNameLen(s) + 1 1005 } 1006 1007 return len(s) + 1 1008} 1009 1010func escapedNameLen(s string) int { 1011 nameLen := len(s) 1012 for i := 0; i < len(s); i++ { 1013 if s[i] != '\\' { 1014 continue 1015 } 1016 1017 if i+3 < len(s) && isDigit(s[i+1]) && isDigit(s[i+2]) && isDigit(s[i+3]) { 1018 nameLen -= 3 1019 i += 3 1020 } else { 1021 nameLen-- 1022 i++ 1023 } 1024 } 1025 1026 return nameLen 1027} 1028 1029func compressionLenSearch(c map[string]struct{}, s string, msgOff int) (int, bool) { 1030 for off, end := 0, false; !end; off, end = NextLabel(s, off) { 1031 if _, ok := c[s[off:]]; ok { 1032 return off, true 1033 } 1034 1035 if msgOff+off < maxCompressionOffset { 1036 c[s[off:]] = struct{}{} 1037 } 1038 } 1039 1040 return 0, false 1041} 1042 1043// Copy returns a new RR which is a deep-copy of r. 1044func Copy(r RR) RR { return r.copy() } 1045 1046// Len returns the length (in octets) of the uncompressed RR in wire format. 1047func Len(r RR) int { return r.len(0, nil) } 1048 1049// Copy returns a new *Msg which is a deep-copy of dns. 1050func (dns *Msg) Copy() *Msg { return dns.CopyTo(new(Msg)) } 1051 1052// CopyTo copies the contents to the provided message using a deep-copy and returns the copy. 1053func (dns *Msg) CopyTo(r1 *Msg) *Msg { 1054 r1.MsgHdr = dns.MsgHdr 1055 r1.Compress = dns.Compress 1056 1057 if len(dns.Question) > 0 { 1058 r1.Question = make([]Question, len(dns.Question)) 1059 copy(r1.Question, dns.Question) // TODO(miek): Question is an immutable value, ok to do a shallow-copy 1060 } 1061 1062 rrArr := make([]RR, len(dns.Answer)+len(dns.Ns)+len(dns.Extra)) 1063 r1.Answer, rrArr = rrArr[:0:len(dns.Answer)], rrArr[len(dns.Answer):] 1064 r1.Ns, rrArr = rrArr[:0:len(dns.Ns)], rrArr[len(dns.Ns):] 1065 r1.Extra = rrArr[:0:len(dns.Extra)] 1066 1067 for _, r := range dns.Answer { 1068 r1.Answer = append(r1.Answer, r.copy()) 1069 } 1070 1071 for _, r := range dns.Ns { 1072 r1.Ns = append(r1.Ns, r.copy()) 1073 } 1074 1075 for _, r := range dns.Extra { 1076 r1.Extra = append(r1.Extra, r.copy()) 1077 } 1078 1079 return r1 1080} 1081 1082func (q *Question) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) { 1083 off, err := packDomainName(q.Name, msg, off, compression, compress) 1084 if err != nil { 1085 return off, err 1086 } 1087 off, err = packUint16(q.Qtype, msg, off) 1088 if err != nil { 1089 return off, err 1090 } 1091 off, err = packUint16(q.Qclass, msg, off) 1092 if err != nil { 1093 return off, err 1094 } 1095 return off, nil 1096} 1097 1098func unpackQuestion(msg []byte, off int) (Question, int, error) { 1099 var ( 1100 q Question 1101 err error 1102 ) 1103 q.Name, off, err = UnpackDomainName(msg, off) 1104 if err != nil { 1105 return q, off, err 1106 } 1107 if off == len(msg) { 1108 return q, off, nil 1109 } 1110 q.Qtype, off, err = unpackUint16(msg, off) 1111 if err != nil { 1112 return q, off, err 1113 } 1114 if off == len(msg) { 1115 return q, off, nil 1116 } 1117 q.Qclass, off, err = unpackUint16(msg, off) 1118 if off == len(msg) { 1119 return q, off, nil 1120 } 1121 return q, off, err 1122} 1123 1124func (dh *Header) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) { 1125 off, err := packUint16(dh.Id, msg, off) 1126 if err != nil { 1127 return off, err 1128 } 1129 off, err = packUint16(dh.Bits, msg, off) 1130 if err != nil { 1131 return off, err 1132 } 1133 off, err = packUint16(dh.Qdcount, msg, off) 1134 if err != nil { 1135 return off, err 1136 } 1137 off, err = packUint16(dh.Ancount, msg, off) 1138 if err != nil { 1139 return off, err 1140 } 1141 off, err = packUint16(dh.Nscount, msg, off) 1142 if err != nil { 1143 return off, err 1144 } 1145 off, err = packUint16(dh.Arcount, msg, off) 1146 if err != nil { 1147 return off, err 1148 } 1149 return off, nil 1150} 1151 1152func unpackMsgHdr(msg []byte, off int) (Header, int, error) { 1153 var ( 1154 dh Header 1155 err error 1156 ) 1157 dh.Id, off, err = unpackUint16(msg, off) 1158 if err != nil { 1159 return dh, off, err 1160 } 1161 dh.Bits, off, err = unpackUint16(msg, off) 1162 if err != nil { 1163 return dh, off, err 1164 } 1165 dh.Qdcount, off, err = unpackUint16(msg, off) 1166 if err != nil { 1167 return dh, off, err 1168 } 1169 dh.Ancount, off, err = unpackUint16(msg, off) 1170 if err != nil { 1171 return dh, off, err 1172 } 1173 dh.Nscount, off, err = unpackUint16(msg, off) 1174 if err != nil { 1175 return dh, off, err 1176 } 1177 dh.Arcount, off, err = unpackUint16(msg, off) 1178 if err != nil { 1179 return dh, off, err 1180 } 1181 return dh, off, nil 1182} 1183 1184// setHdr set the header in the dns using the binary data in dh. 1185func (dns *Msg) setHdr(dh Header) { 1186 dns.Id = dh.Id 1187 dns.Response = dh.Bits&_QR != 0 1188 dns.Opcode = int(dh.Bits>>11) & 0xF 1189 dns.Authoritative = dh.Bits&_AA != 0 1190 dns.Truncated = dh.Bits&_TC != 0 1191 dns.RecursionDesired = dh.Bits&_RD != 0 1192 dns.RecursionAvailable = dh.Bits&_RA != 0 1193 dns.Zero = dh.Bits&_Z != 0 // _Z covers the zero bit, which should be zero; not sure why we set it to the opposite. 1194 dns.AuthenticatedData = dh.Bits&_AD != 0 1195 dns.CheckingDisabled = dh.Bits&_CD != 0 1196 dns.Rcode = int(dh.Bits & 0xF) 1197} 1198