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
11 //go:generate go run msg_generate.go
25 maxCompressionOffset = 2 << 13 // We have 14 bits for the compression pointer
26 maxDomainNameWireOctets = 255 // See RFC 1035 section 2.3.4
28 // This is the maximum number of compression pointers that should occur in a
29 // semantically valid message. Each label in a domain name must be at least one
30 // octet and is separated by a period. The root label won't be represented by a
31 // compression pointer to a compression pointer, hence the -2 to exclude the
32 // smallest valid root label.
34 // It is possible to construct a valid message that has more compression pointers
35 // than this, and still doesn't loop, by pointing to a previous pointer. This is
36 // not something a well written implementation should ever do, so we leave them
37 // to trip the maximum compression pointer check.
38 maxCompressionPointers = (maxDomainNameWireOctets+1)/2 - 2
40 // This is the maximum length of a domain name in presentation format. The
41 // maximum wire length of a domain name is 255 octets (see above), with the
42 // maximum label length being 63. The wire format requires one extra byte over
43 // the presentation format, reducing the number of octets by 1. Each label in
44 // the name will be separated by a single period, with each octet in the label
45 // expanding to at most 4 bytes (\DDD). If all other labels are of the maximum
46 // length, then the final label can only be 61 octets long to not exceed the
47 // maximum allowed wire length.
48 maxDomainNamePresentationLength = 61*4 + 1 + 63*4 + 1 + 63*4 + 1 + 63*4 + 1
51 // Errors defined in this package.
53 ErrAlg error = &Error{err: "bad algorithm"} // ErrAlg indicates an error with the (DNSSEC) algorithm.
54 ErrAuth error = &Error{err: "bad authentication"} // ErrAuth indicates an error in the TSIG authentication.
55 ErrBuf error = &Error{err: "buffer size too small"} // ErrBuf indicates that the buffer used is too small for the message.
56 ErrConnEmpty error = &Error{err: "conn has no connection"} // ErrConnEmpty indicates a connection is being used before it is initialized.
57 ErrExtendedRcode error = &Error{err: "bad extended rcode"} // ErrExtendedRcode ...
58 ErrFqdn error = &Error{err: "domain must be fully qualified"} // ErrFqdn indicates that a domain name does not have a closing dot.
59 ErrId error = &Error{err: "id mismatch"} // ErrId indicates there is a mismatch with the message's ID.
60 ErrKeyAlg error = &Error{err: "bad key algorithm"} // ErrKeyAlg indicates that the algorithm in the key is not valid.
61 ErrKey error = &Error{err: "bad key"}
62 ErrKeySize error = &Error{err: "bad key size"}
63 ErrLongDomain error = &Error{err: fmt.Sprintf("domain name exceeded %d wire-format octets", maxDomainNameWireOctets)}
64 ErrNoSig error = &Error{err: "no signature found"}
65 ErrPrivKey error = &Error{err: "bad private key"}
66 ErrRcode error = &Error{err: "bad rcode"}
67 ErrRdata error = &Error{err: "bad rdata"}
68 ErrRRset error = &Error{err: "bad rrset"}
69 ErrSecret error = &Error{err: "no secrets defined"}
70 ErrShortRead error = &Error{err: "short read"}
71 ErrSig error = &Error{err: "bad signature"} // ErrSig indicates that a signature can not be cryptographically validated.
72 ErrSoa error = &Error{err: "no SOA"} // ErrSOA indicates that no SOA RR was seen when doing zone transfers.
73 ErrTime error = &Error{err: "bad time"} // ErrTime indicates a timing error in TSIG authentication.
76 // Id by default, returns a 16 bits random number to be used as a
77 // message id. The random provided should be good enough. This being a
78 // variable the function can be reassigned to a custom function.
79 // For instance, to make it return a static value:
81 // dns.Id = func() uint16 { return 3 }
89 // id returns a 16 bits random number to be used as a
90 // message id. The random provided should be good enough.
95 // This (partially) works around
96 // https://github.com/golang/go/issues/11833 by only
97 // seeding idRand upon the first call to id.
102 if _, err := crand.Read(buf[:]); err == nil {
103 seed = int64(binary.LittleEndian.Uint64(buf[:]))
108 idRand = rand.New(rand.NewSource(seed))
111 // The call to idRand.Uint32 must be within the
112 // mutex lock because *rand.Rand is not safe for
115 // There is no added performance overhead to calling
116 // idRand.Uint32 inside a mutex lock over just
117 // calling rand.Uint32 as the global math/rand rng
118 // is internally protected by a sync.Mutex.
119 id := uint16(idRand.Uint32())
125 // MsgHdr is a a manually-unpacked version of (id, bits).
132 RecursionDesired bool
133 RecursionAvailable bool
135 AuthenticatedData bool
136 CheckingDisabled bool
140 // Msg contains the layout of a DNS message.
143 Compress bool `json:"-"` // If true, the message will be compressed when converted to wire format.
144 Question []Question // Holds the RR(s) of the question section.
145 Answer []RR // Holds the RR(s) of the answer section.
146 Ns []RR // Holds the RR(s) of the authority section.
147 Extra []RR // Holds the RR(s) of the additional section.
150 // ClassToString is a maps Classes to strings for each CLASS wire type.
151 var ClassToString = map[uint16]string{
160 // OpcodeToString maps Opcodes to strings.
161 var OpcodeToString = map[int]string{
162 OpcodeQuery: "QUERY",
163 OpcodeIQuery: "IQUERY",
164 OpcodeStatus: "STATUS",
165 OpcodeNotify: "NOTIFY",
166 OpcodeUpdate: "UPDATE",
169 // RcodeToString maps Rcodes to strings.
170 var RcodeToString = map[int]string{
171 RcodeSuccess: "NOERROR",
172 RcodeFormatError: "FORMERR",
173 RcodeServerFailure: "SERVFAIL",
174 RcodeNameError: "NXDOMAIN",
175 RcodeNotImplemented: "NOTIMP",
176 RcodeRefused: "REFUSED",
177 RcodeYXDomain: "YXDOMAIN", // See RFC 2136
178 RcodeYXRrset: "YXRRSET",
179 RcodeNXRrset: "NXRRSET",
180 RcodeNotAuth: "NOTAUTH",
181 RcodeNotZone: "NOTZONE",
182 RcodeBadSig: "BADSIG", // Also known as RcodeBadVers, see RFC 6891
183 // RcodeBadVers: "BADVERS",
184 RcodeBadKey: "BADKEY",
185 RcodeBadTime: "BADTIME",
186 RcodeBadMode: "BADMODE",
187 RcodeBadName: "BADNAME",
188 RcodeBadAlg: "BADALG",
189 RcodeBadTrunc: "BADTRUNC",
190 RcodeBadCookie: "BADCOOKIE",
193 // compressionMap is used to allow a more efficient compression map
194 // to be used for internal packDomainName calls without changing the
195 // signature or functionality of public API.
197 // In particular, map[string]uint16 uses 25% less per-entry memory
198 // than does map[string]int.
199 type compressionMap struct {
200 ext map[string]int // external callers
201 int map[string]uint16 // internal callers
204 func (m compressionMap) valid() bool {
205 return m.int != nil || m.ext != nil
208 func (m compressionMap) insert(s string, pos int) {
212 m.int[s] = uint16(pos)
216 func (m compressionMap) find(s string) (int, bool) {
226 // Domain names are a sequence of counted strings
227 // split at the dots. They end with a zero-length string.
229 // PackDomainName packs a domain name s into msg[off:].
230 // If compression is wanted compress must be true and the compression
231 // map needs to hold a mapping between domain names and offsets
232 // pointing into msg.
233 func PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {
234 return packDomainName(s, msg, off, compressionMap{ext: compression}, compress)
237 func packDomainName(s string, msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
238 // XXX: A logical copy of this function exists in IsDomainName and
239 // should be kept in sync with this function.
242 if ls == 0 { // Ok, for instance when dealing with update RR without any rdata.
246 // If not fully qualified, error out.
248 return len(msg), ErrFqdn
251 // Each dot ends a segment of the name.
252 // We trade each dot byte for a length byte.
253 // Except for escaped dots (\.), which are normal dots.
254 // There is also a trailing zero.
259 // Emit sequence of counted strings, chopping at dots.
268 for i := 0; i < ls; i++ {
278 if off+1 > len(msg) {
279 return len(msg), ErrBuf
287 if i+3 < ls && isDigit(bs[i+1]) && isDigit(bs[i+2]) && isDigit(bs[i+3]) {
288 bs[i] = dddToByte(bs[i+1:])
289 copy(bs[i+1:ls-3], bs[i+4:])
293 copy(bs[i:ls-1], bs[i+1:])
301 // two dots back to back is not legal
302 return len(msg), ErrRdata
306 labelLen := i - begin
307 if labelLen >= 1<<6 { // top two bits of length must be clear
308 return len(msg), ErrRdata
311 // off can already (we're in a loop) be bigger than len(msg)
312 // this happens when a name isn't fully qualified
313 if off+1+labelLen > len(msg) {
314 return len(msg), ErrBuf
317 // Don't try to compress '.'
318 // We should only compress when compress is true, but we should also still pick
319 // up names that can be used for *future* compression(s).
320 if compression.valid() && !isRootLabel(s, bs, begin, ls) {
321 if p, ok := compression.find(s[compBegin:]); ok {
322 // The first hit is the longest matching dname
323 // keep the pointer offset we get back and store
324 // the offset of the current name, because that's
325 // where we need to insert the pointer later
327 // If compress is true, we're allowed to compress this dname
329 pointer = p // Where to point to
332 } else if off < maxCompressionOffset {
333 // Only offsets smaller than maxCompressionOffset can be used.
334 compression.insert(s[compBegin:], off)
338 // The following is covered by the length check above.
339 msg[off] = byte(labelLen)
342 copy(msg[off+1:], s[begin:i])
344 copy(msg[off+1:], bs[begin:i])
349 compBegin = begin + compOff
355 // Root label is special
356 if isRootLabel(s, bs, 0, ls) {
360 // If we did compression and we find something add the pointer here
362 // We have two bytes (14 bits) to put the pointer in
363 binary.BigEndian.PutUint16(msg[off:], uint16(pointer^0xC000))
374 // isRootLabel returns whether s or bs, from off to end, is the root
377 // If bs is nil, s will be checked, otherwise bs will be checked.
378 func isRootLabel(s string, bs []byte, off, end int) bool {
380 return s[off:end] == "."
383 return end-off == 1 && bs[off] == '.'
386 // Unpack a domain name.
387 // In addition to the simple sequences of counted strings above,
388 // domain names are allowed to refer to strings elsewhere in the
389 // packet, to avoid repeating common suffixes when returning
390 // many entries in a single domain. The pointers are marked
391 // by a length byte with the top two bits set. Ignoring those
392 // two bits, that byte and the next give a 14 bit offset from msg[0]
393 // where we should pick up the trail.
394 // Note that if we jump elsewhere in the packet,
395 // we return off1 == the offset after the first pointer we found,
396 // which is where the next record will start.
397 // In theory, the pointers are only allowed to jump backward.
398 // We let them jump anywhere and stop jumping after a while.
400 // UnpackDomainName unpacks a domain name into a string. It returns
401 // the name, the new offset into msg and any error that occurred.
403 // When an error is encountered, the unpacked name will be discarded
404 // and len(msg) will be returned as the offset.
405 func UnpackDomainName(msg []byte, off int) (string, int, error) {
406 s := make([]byte, 0, maxDomainNamePresentationLength)
409 budget := maxDomainNameWireOctets
410 ptr := 0 // number of pointers followed
414 return "", lenmsg, ErrBuf
426 return "", lenmsg, ErrBuf
428 budget -= c + 1 // +1 for the label separator
430 return "", lenmsg, ErrLongDomain
432 for j := off; j < off+c; j++ {
433 switch b := msg[j]; b {
434 case '.', '(', ')', ';', ' ', '@':
437 s = append(s, '\\', b)
439 if b < ' ' || b > '~' { // unprintable, use \DDD
440 s = append(s, escapeByte(b)...)
449 // pointer to somewhere else in msg.
450 // remember location after first ptr,
451 // since that's how many bytes we consumed.
452 // also, don't follow too many pointers --
453 // maybe there's a loop.
455 return "", lenmsg, ErrBuf
462 if ptr++; ptr > maxCompressionPointers {
463 return "", lenmsg, &Error{err: "too many compression pointers"}
465 // pointer should guarantee that it advances and points forwards at least
466 // but the condition on previous three lines guarantees that it's
467 // at least loop-free
468 off = (c^0xC0)<<8 | int(c1)
470 // 0x80 and 0x40 are reserved
471 return "", lenmsg, ErrRdata
478 return ".", off1, nil
480 return string(s), off1, nil
483 func packTxt(txt []string, msg []byte, offset int, tmp []byte) (int, error) {
485 if offset >= len(msg) {
486 return offset, ErrBuf
493 if len(txt[i]) > len(tmp) {
494 return offset, ErrBuf
496 offset, err = packTxtString(txt[i], msg, offset, tmp)
504 func packTxtString(s string, msg []byte, offset int, tmp []byte) (int, error) {
505 lenByteOffset := offset
506 if offset >= len(msg) || len(s) > len(tmp) {
507 return offset, ErrBuf
512 for i := 0; i < len(bs); i++ {
513 if len(msg) <= offset {
514 return offset, ErrBuf
522 if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) {
523 msg[offset] = dddToByte(bs[i:])
533 l := offset - lenByteOffset - 1
535 return offset, &Error{err: "string exceeded 255 bytes in txt"}
537 msg[lenByteOffset] = byte(l)
541 func packOctetString(s string, msg []byte, offset int, tmp []byte) (int, error) {
542 if offset >= len(msg) || len(s) > len(tmp) {
543 return offset, ErrBuf
547 for i := 0; i < len(bs); i++ {
548 if len(msg) <= offset {
549 return offset, ErrBuf
557 if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) {
558 msg[offset] = dddToByte(bs[i:])
571 func unpackTxt(msg []byte, off0 int) (ss []string, off int, err error) {
574 for off < len(msg) && err == nil {
575 s, off, err = unpackString(msg, off)
583 // Helpers for dealing with escaped bytes
584 func isDigit(b byte) bool { return b >= '0' && b <= '9' }
586 func dddToByte(s []byte) byte {
587 _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808
588 return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0'))
591 func dddStringToByte(s string) byte {
592 _ = s[2] // bounds check hint to compiler; see golang.org/issue/14808
593 return byte((s[0]-'0')*100 + (s[1]-'0')*10 + (s[2] - '0'))
596 // Helper function for packing and unpacking
597 func intToBytes(i *big.Int, length int) []byte {
599 if len(buf) < length {
600 b := make([]byte, length)
601 copy(b[length-len(buf):], buf)
607 // PackRR packs a resource record rr into msg[off:].
608 // See PackDomainName for documentation about the compression.
609 func PackRR(rr RR, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {
610 headerEnd, off1, err := packRR(rr, msg, off, compressionMap{ext: compression}, compress)
612 // packRR no longer sets the Rdlength field on the rr, but
613 // callers might be expecting it so we set it here.
614 rr.Header().Rdlength = uint16(off1 - headerEnd)
619 func packRR(rr RR, msg []byte, off int, compression compressionMap, compress bool) (headerEnd int, off1 int, err error) {
621 return len(msg), len(msg), &Error{err: "nil rr"}
624 headerEnd, err = rr.Header().packHeader(msg, off, compression, compress)
626 return headerEnd, len(msg), err
629 off1, err = rr.pack(msg, headerEnd, compression, compress)
631 return headerEnd, len(msg), err
634 rdlength := off1 - headerEnd
635 if int(uint16(rdlength)) != rdlength { // overflow
636 return headerEnd, len(msg), ErrRdata
639 // The RDLENGTH field is the last field in the header and we set it here.
640 binary.BigEndian.PutUint16(msg[headerEnd-2:], uint16(rdlength))
641 return headerEnd, off1, nil
644 // UnpackRR unpacks msg[off:] into an RR.
645 func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) {
646 h, off, msg, err := unpackHeader(msg, off)
648 return nil, len(msg), err
651 return UnpackRRWithHeader(h, msg, off)
654 // UnpackRRWithHeader unpacks the record type specific payload given an existing
656 func UnpackRRWithHeader(h RR_Header, msg []byte, off int) (rr RR, off1 int, err error) {
657 if newFn, ok := TypeToRR[h.Rrtype]; ok {
661 rr = &RFC3597{Hdr: h}
668 end := off + int(h.Rdlength)
670 off, err = rr.unpack(msg, off)
675 return &h, end, &Error{err: "bad rdlength"}
681 // unpackRRslice unpacks msg[off:] into an []RR.
682 // If we cannot unpack the whole array, then it will return nil
683 func unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error) {
685 // Don't pre-allocate, l may be under attacker control
687 for i := 0; i < l; i++ {
689 r, off, err = UnpackRR(msg, off)
694 // If offset does not increase anymore, l is a lie
701 if err != nil && off == len(msg) {
707 // Convert a MsgHdr to a string, with dig-like headers:
709 //;; opcode: QUERY, status: NOERROR, id: 48404
711 //;; flags: qr aa rd ra;
712 func (h *MsgHdr) String() string {
714 return "<nil> MsgHdr"
717 s := ";; opcode: " + OpcodeToString[h.Opcode]
718 s += ", status: " + RcodeToString[h.Rcode]
719 s += ", id: " + strconv.Itoa(int(h.Id)) + "\n"
731 if h.RecursionDesired {
734 if h.RecursionAvailable {
740 if h.AuthenticatedData {
743 if h.CheckingDisabled {
751 // Pack packs a Msg: it is converted to to wire format.
752 // If the dns.Compress is true the message will be in compressed wire format.
753 func (dns *Msg) Pack() (msg []byte, err error) {
754 return dns.PackBuffer(nil)
757 // PackBuffer packs a Msg, using the given buffer buf. If buf is too small a new buffer is allocated.
758 func (dns *Msg) PackBuffer(buf []byte) (msg []byte, err error) {
759 // If this message can't be compressed, avoid filling the
760 // compression map and creating garbage.
761 if dns.Compress && dns.isCompressible() {
762 compression := make(map[string]uint16) // Compression pointer mappings.
763 return dns.packBufferWithCompressionMap(buf, compressionMap{int: compression}, true)
766 return dns.packBufferWithCompressionMap(buf, compressionMap{}, false)
769 // packBufferWithCompressionMap packs a Msg, using the given buffer buf.
770 func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression compressionMap, compress bool) (msg []byte, err error) {
771 if dns.Rcode < 0 || dns.Rcode > 0xFFF {
775 // Set extended rcode unconditionally if we have an opt, this will allow
776 // reseting the extended rcode bits if they need to.
777 if opt := dns.IsEdns0(); opt != nil {
778 opt.SetExtendedRcode(uint16(dns.Rcode))
779 } else if dns.Rcode > 0xF {
780 // If Rcode is an extended one and opt is nil, error out.
781 return nil, ErrExtendedRcode
784 // Convert convenient Msg into wire-like Header.
787 dh.Bits = uint16(dns.Opcode)<<11 | uint16(dns.Rcode&0xF)
791 if dns.Authoritative {
797 if dns.RecursionDesired {
800 if dns.RecursionAvailable {
806 if dns.AuthenticatedData {
809 if dns.CheckingDisabled {
813 dh.Qdcount = uint16(len(dns.Question))
814 dh.Ancount = uint16(len(dns.Answer))
815 dh.Nscount = uint16(len(dns.Ns))
816 dh.Arcount = uint16(len(dns.Extra))
818 // We need the uncompressed length here, because we first pack it and then compress it.
820 uncompressedLen := msgLenWithCompressionMap(dns, nil)
821 if packLen := uncompressedLen + 1; len(msg) < packLen {
822 msg = make([]byte, packLen)
825 // Pack it in: header and then the pieces.
827 off, err = dh.pack(msg, off, compression, compress)
831 for _, r := range dns.Question {
832 off, err = r.pack(msg, off, compression, compress)
837 for _, r := range dns.Answer {
838 _, off, err = packRR(r, msg, off, compression, compress)
843 for _, r := range dns.Ns {
844 _, off, err = packRR(r, msg, off, compression, compress)
849 for _, r := range dns.Extra {
850 _, off, err = packRR(r, msg, off, compression, compress)
855 return msg[:off], nil
858 func (dns *Msg) unpack(dh Header, msg []byte, off int) (err error) {
859 // If we are at the end of the message we should return *just* the
860 // header. This can still be useful to the caller. 9.9.9.9 sends these
861 // when responding with REFUSED for instance.
863 // reset sections before returning
864 dns.Question, dns.Answer, dns.Ns, dns.Extra = nil, nil, nil, nil
868 // Qdcount, Ancount, Nscount, Arcount can't be trusted, as they are
869 // attacker controlled. This means we can't use them to pre-allocate
872 for i := 0; i < int(dh.Qdcount); i++ {
875 q, off, err = unpackQuestion(msg, off)
879 if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie!
880 dh.Qdcount = uint16(i)
883 dns.Question = append(dns.Question, q)
886 dns.Answer, off, err = unpackRRslice(int(dh.Ancount), msg, off)
887 // The header counts might have been wrong so we need to update it
888 dh.Ancount = uint16(len(dns.Answer))
890 dns.Ns, off, err = unpackRRslice(int(dh.Nscount), msg, off)
892 // The header counts might have been wrong so we need to update it
893 dh.Nscount = uint16(len(dns.Ns))
895 dns.Extra, off, err = unpackRRslice(int(dh.Arcount), msg, off)
897 // The header counts might have been wrong so we need to update it
898 dh.Arcount = uint16(len(dns.Extra))
900 // Set extended Rcode
901 if opt := dns.IsEdns0(); opt != nil {
902 dns.Rcode |= opt.ExtendedRcode()
906 // TODO(miek) make this an error?
907 // use PackOpt to let people tell how detailed the error reporting should be?
908 // println("dns: extra bytes in dns packet", off, "<", len(msg))
914 // Unpack unpacks a binary message to a Msg structure.
915 func (dns *Msg) Unpack(msg []byte) (err error) {
916 dh, off, err := unpackMsgHdr(msg, 0)
922 return dns.unpack(dh, msg, off)
925 // Convert a complete message to a string with dig-like output.
926 func (dns *Msg) String() string {
928 return "<nil> MsgHdr"
930 s := dns.MsgHdr.String() + " "
931 s += "QUERY: " + strconv.Itoa(len(dns.Question)) + ", "
932 s += "ANSWER: " + strconv.Itoa(len(dns.Answer)) + ", "
933 s += "AUTHORITY: " + strconv.Itoa(len(dns.Ns)) + ", "
934 s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n"
935 if len(dns.Question) > 0 {
936 s += "\n;; QUESTION SECTION:\n"
937 for i := 0; i < len(dns.Question); i++ {
938 s += dns.Question[i].String() + "\n"
941 if len(dns.Answer) > 0 {
942 s += "\n;; ANSWER SECTION:\n"
943 for i := 0; i < len(dns.Answer); i++ {
944 if dns.Answer[i] != nil {
945 s += dns.Answer[i].String() + "\n"
950 s += "\n;; AUTHORITY SECTION:\n"
951 for i := 0; i < len(dns.Ns); i++ {
952 if dns.Ns[i] != nil {
953 s += dns.Ns[i].String() + "\n"
957 if len(dns.Extra) > 0 {
958 s += "\n;; ADDITIONAL SECTION:\n"
959 for i := 0; i < len(dns.Extra); i++ {
960 if dns.Extra[i] != nil {
961 s += dns.Extra[i].String() + "\n"
968 // isCompressible returns whether the msg may be compressible.
969 func (dns *Msg) isCompressible() bool {
970 // If we only have one question, there is nothing we can ever compress.
971 return len(dns.Question) > 1 || len(dns.Answer) > 0 ||
972 len(dns.Ns) > 0 || len(dns.Extra) > 0
975 // Len returns the message length when in (un)compressed wire format.
976 // If dns.Compress is true compression it is taken into account. Len()
977 // is provided to be a faster way to get the size of the resulting packet,
978 // than packing it, measuring the size and discarding the buffer.
979 func (dns *Msg) Len() int {
980 // If this message can't be compressed, avoid filling the
981 // compression map and creating garbage.
982 if dns.Compress && dns.isCompressible() {
983 compression := make(map[string]struct{})
984 return msgLenWithCompressionMap(dns, compression)
987 return msgLenWithCompressionMap(dns, nil)
990 func msgLenWithCompressionMap(dns *Msg, compression map[string]struct{}) int {
993 for _, r := range dns.Question {
994 l += r.len(l, compression)
996 for _, r := range dns.Answer {
998 l += r.len(l, compression)
1001 for _, r := range dns.Ns {
1003 l += r.len(l, compression)
1006 for _, r := range dns.Extra {
1008 l += r.len(l, compression)
1015 func domainNameLen(s string, off int, compression map[string]struct{}, compress bool) int {
1016 if s == "" || s == "." {
1020 escaped := strings.Contains(s, "\\")
1022 if compression != nil && (compress || off < maxCompressionOffset) {
1023 // compressionLenSearch will insert the entry into the compression
1024 // map if it doesn't contain it.
1025 if l, ok := compressionLenSearch(compression, s, off); ok && compress {
1027 return escapedNameLen(s[:l]) + 2
1035 return escapedNameLen(s) + 1
1041 func escapedNameLen(s string) int {
1043 for i := 0; i < len(s); i++ {
1048 if i+3 < len(s) && isDigit(s[i+1]) && isDigit(s[i+2]) && isDigit(s[i+3]) {
1060 func compressionLenSearch(c map[string]struct{}, s string, msgOff int) (int, bool) {
1061 for off, end := 0, false; !end; off, end = NextLabel(s, off) {
1062 if _, ok := c[s[off:]]; ok {
1066 if msgOff+off < maxCompressionOffset {
1067 c[s[off:]] = struct{}{}
1074 // Copy returns a new RR which is a deep-copy of r.
1075 func Copy(r RR) RR { return r.copy() }
1077 // Len returns the length (in octets) of the uncompressed RR in wire format.
1078 func Len(r RR) int { return r.len(0, nil) }
1080 // Copy returns a new *Msg which is a deep-copy of dns.
1081 func (dns *Msg) Copy() *Msg { return dns.CopyTo(new(Msg)) }
1083 // CopyTo copies the contents to the provided message using a deep-copy and returns the copy.
1084 func (dns *Msg) CopyTo(r1 *Msg) *Msg {
1085 r1.MsgHdr = dns.MsgHdr
1086 r1.Compress = dns.Compress
1088 if len(dns.Question) > 0 {
1089 r1.Question = make([]Question, len(dns.Question))
1090 copy(r1.Question, dns.Question) // TODO(miek): Question is an immutable value, ok to do a shallow-copy
1093 rrArr := make([]RR, len(dns.Answer)+len(dns.Ns)+len(dns.Extra))
1096 if len(dns.Answer) > 0 {
1098 for i := 0; i < len(dns.Answer); i++ {
1099 rrArr[rri] = dns.Answer[i].copy()
1102 r1.Answer = rrArr[rrbegin:rri:rri]
1105 if len(dns.Ns) > 0 {
1107 for i := 0; i < len(dns.Ns); i++ {
1108 rrArr[rri] = dns.Ns[i].copy()
1111 r1.Ns = rrArr[rrbegin:rri:rri]
1114 if len(dns.Extra) > 0 {
1116 for i := 0; i < len(dns.Extra); i++ {
1117 rrArr[rri] = dns.Extra[i].copy()
1120 r1.Extra = rrArr[rrbegin:rri:rri]
1126 func (q *Question) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) {
1127 off, err := packDomainName(q.Name, msg, off, compression, compress)
1131 off, err = packUint16(q.Qtype, msg, off)
1135 off, err = packUint16(q.Qclass, msg, off)
1142 func unpackQuestion(msg []byte, off int) (Question, int, error) {
1147 q.Name, off, err = UnpackDomainName(msg, off)
1151 if off == len(msg) {
1154 q.Qtype, off, err = unpackUint16(msg, off)
1158 if off == len(msg) {
1161 q.Qclass, off, err = unpackUint16(msg, off)
1162 if off == len(msg) {
1168 func (dh *Header) pack(msg []byte, off int, compression compressionMap, compress bool) (int, error) {
1169 off, err := packUint16(dh.Id, msg, off)
1173 off, err = packUint16(dh.Bits, msg, off)
1177 off, err = packUint16(dh.Qdcount, msg, off)
1181 off, err = packUint16(dh.Ancount, msg, off)
1185 off, err = packUint16(dh.Nscount, msg, off)
1189 off, err = packUint16(dh.Arcount, msg, off)
1196 func unpackMsgHdr(msg []byte, off int) (Header, int, error) {
1201 dh.Id, off, err = unpackUint16(msg, off)
1205 dh.Bits, off, err = unpackUint16(msg, off)
1209 dh.Qdcount, off, err = unpackUint16(msg, off)
1213 dh.Ancount, off, err = unpackUint16(msg, off)
1217 dh.Nscount, off, err = unpackUint16(msg, off)
1221 dh.Arcount, off, err = unpackUint16(msg, off)
1228 // setHdr set the header in the dns using the binary data in dh.
1229 func (dns *Msg) setHdr(dh Header) {
1231 dns.Response = dh.Bits&_QR != 0
1232 dns.Opcode = int(dh.Bits>>11) & 0xF
1233 dns.Authoritative = dh.Bits&_AA != 0
1234 dns.Truncated = dh.Bits&_TC != 0
1235 dns.RecursionDesired = dh.Bits&_RD != 0
1236 dns.RecursionAvailable = dh.Bits&_RA != 0
1237 dns.Zero = dh.Bits&_Z != 0 // _Z covers the zero bit, which should be zero; not sure why we set it to the opposite.
1238 dns.AuthenticatedData = dh.Bits&_AD != 0
1239 dns.CheckingDisabled = dh.Bits&_CD != 0
1240 dns.Rcode = int(dh.Bits & 0xF)