// DNS packet assembly, see RFC 1035. Converting from - Unpack() -
// and to - Pack() - wire format.
// All the packers and unpackers take a (msg []byte, off int)
// and return (off1 int, ok bool).  If they return ok==false, they
// also return off1==len(msg), so that the next unpacker will
// also fail.  This lets us avoid checks of ok until the end of a
// packing sequence.

package dns

//go:generate go run msg_generate.go

import (
	
	
	
	
	
	
)

const (
	maxCompressionOffset    = 2 << 13 // We have 14 bits for the compression pointer
	maxDomainNameWireOctets = 255     // See RFC 1035 section 2.3.4

	// This is the maximum number of compression pointers that should occur in a
	// semantically valid message. Each label in a domain name must be at least one
	// octet and is separated by a period. The root label won't be represented by a
	// compression pointer to a compression pointer, hence the -2 to exclude the
	// smallest valid root label.
	//
	// It is possible to construct a valid message that has more compression pointers
	// than this, and still doesn't loop, by pointing to a previous pointer. This is
	// not something a well written implementation should ever do, so we leave them
	// to trip the maximum compression pointer check.
	maxCompressionPointers = (maxDomainNameWireOctets+1)/2 - 2

	// This is the maximum length of a domain name in presentation format. The
	// maximum wire length of a domain name is 255 octets (see above), with the
	// maximum label length being 63. The wire format requires one extra byte over
	// the presentation format, reducing the number of octets by 1. Each label in
	// the name will be separated by a single period, with each octet in the label
	// expanding to at most 4 bytes (\DDD). If all other labels are of the maximum
	// length, then the final label can only be 61 octets long to not exceed the
	// maximum allowed wire length.
	maxDomainNamePresentationLength = 61*4 + 1 + 63*4 + 1 + 63*4 + 1 + 63*4 + 1
)

// Errors defined in this package.
var (
	ErrAlg           error = &Error{err: "bad algorithm"}                  // ErrAlg indicates an error with the (DNSSEC) algorithm.
	ErrAuth          error = &Error{err: "bad authentication"}             // ErrAuth indicates an error in the TSIG authentication.
	ErrBuf           error = &Error{err: "buffer size too small"}          // ErrBuf indicates that the buffer used is too small for the message.
	ErrConnEmpty     error = &Error{err: "conn has no connection"}         // ErrConnEmpty indicates a connection is being used before it is initialized.
	ErrExtendedRcode error = &Error{err: "bad extended rcode"}             // ErrExtendedRcode ...
	ErrFqdn          error = &Error{err: "domain must be fully qualified"} // ErrFqdn indicates that a domain name does not have a closing dot.
	ErrId            error = &Error{err: "id mismatch"}                    // ErrId indicates there is a mismatch with the message's ID.
	ErrKeyAlg        error = &Error{err: "bad key algorithm"}              // ErrKeyAlg indicates that the algorithm in the key is not valid.
	ErrKey           error = &Error{err: "bad key"}
	ErrKeySize       error = &Error{err: "bad key size"}
	ErrLongDomain    error = &Error{err: fmt.Sprintf("domain name exceeded %d wire-format octets", maxDomainNameWireOctets)}
	ErrNoSig         error = &Error{err: "no signature found"}
	ErrPrivKey       error = &Error{err: "bad private key"}
	ErrRcode         error = &Error{err: "bad rcode"}
	ErrRdata         error = &Error{err: "bad rdata"}
	ErrRRset         error = &Error{err: "bad rrset"}
	ErrSecret        error = &Error{err: "no secrets defined"}
	ErrShortRead     error = &Error{err: "short read"}
	ErrSig           error = &Error{err: "bad signature"} // ErrSig indicates that a signature can not be cryptographically validated.
	ErrSoa           error = &Error{err: "no SOA"}        // ErrSOA indicates that no SOA RR was seen when doing zone transfers.
	ErrTime          error = &Error{err: "bad time"}      // ErrTime indicates a timing error in TSIG authentication.
)

// Id by default returns a 16-bit random number to be used as a message id. The
// number is drawn from a cryptographically secure random number generator.
// This being a variable the function can be reassigned to a custom function.
// For instance, to make it return a static value for testing:
//
//	dns.Id = func() uint16 { return 3 }
var Id = id

// id returns a 16 bits random number to be used as a
// message id. The random provided should be good enough.
func id() uint16 {
	var  uint16
	 := binary.Read(rand.Reader, binary.BigEndian, &)
	if  != nil {
		panic("dns: reading random id failed: " + .Error())
	}
	return 
}

// MsgHdr is a a manually-unpacked version of (id, bits).
type MsgHdr struct {
	Id                 uint16
	Response           bool
	Opcode             int
	Authoritative      bool
	Truncated          bool
	RecursionDesired   bool
	RecursionAvailable bool
	Zero               bool
	AuthenticatedData  bool
	CheckingDisabled   bool
	Rcode              int
}

// Msg contains the layout of a DNS message.
type Msg struct {
	MsgHdr
	Compress bool       `json:"-"` // If true, the message will be compressed when converted to wire format.
	Question []Question // Holds the RR(s) of the question section.
	Answer   []RR       // Holds the RR(s) of the answer section.
	Ns       []RR       // Holds the RR(s) of the authority section.
	Extra    []RR       // Holds the RR(s) of the additional section.
}

// ClassToString is a maps Classes to strings for each CLASS wire type.
var ClassToString = map[uint16]string{
	ClassINET:   "IN",
	ClassCSNET:  "CS",
	ClassCHAOS:  "CH",
	ClassHESIOD: "HS",
	ClassNONE:   "NONE",
	ClassANY:    "ANY",
}

// OpcodeToString maps Opcodes to strings.
var OpcodeToString = map[int]string{
	OpcodeQuery:  "QUERY",
	OpcodeIQuery: "IQUERY",
	OpcodeStatus: "STATUS",
	OpcodeNotify: "NOTIFY",
	OpcodeUpdate: "UPDATE",
}

// RcodeToString maps Rcodes to strings.
var RcodeToString = map[int]string{
	RcodeSuccess:                    "NOERROR",
	RcodeFormatError:                "FORMERR",
	RcodeServerFailure:              "SERVFAIL",
	RcodeNameError:                  "NXDOMAIN",
	RcodeNotImplemented:             "NOTIMP",
	RcodeRefused:                    "REFUSED",
	RcodeYXDomain:                   "YXDOMAIN", // See RFC 2136
	RcodeYXRrset:                    "YXRRSET",
	RcodeNXRrset:                    "NXRRSET",
	RcodeNotAuth:                    "NOTAUTH",
	RcodeNotZone:                    "NOTZONE",
	RcodeStatefulTypeNotImplemented: "DSOTYPENI",
	RcodeBadSig:                     "BADSIG", // Also known as RcodeBadVers, see RFC 6891
	//	RcodeBadVers:        "BADVERS",
	RcodeBadKey:    "BADKEY",
	RcodeBadTime:   "BADTIME",
	RcodeBadMode:   "BADMODE",
	RcodeBadName:   "BADNAME",
	RcodeBadAlg:    "BADALG",
	RcodeBadTrunc:  "BADTRUNC",
	RcodeBadCookie: "BADCOOKIE",
}

// compressionMap is used to allow a more efficient compression map
// to be used for internal packDomainName calls without changing the
// signature or functionality of public API.
//
// In particular, map[string]uint16 uses 25% less per-entry memory
// than does map[string]int.
type compressionMap struct {
	ext map[string]int    // external callers
	int map[string]uint16 // internal callers
}

func ( compressionMap) () bool {
	return .int != nil || .ext != nil
}

func ( compressionMap) ( string,  int) {
	if .ext != nil {
		.ext[] = 
	} else {
		.int[] = uint16()
	}
}

func ( compressionMap) ( string) (int, bool) {
	if .ext != nil {
		,  := .ext[]
		return , 
	}

	,  := .int[]
	return int(), 
}

// Domain names are a sequence of counted strings
// split at the dots. They end with a zero-length string.

// PackDomainName packs a domain name s into msg[off:].
// If compression is wanted compress must be true and the compression
// map needs to hold a mapping between domain names and offsets
// pointing into msg.
func ( string,  []byte,  int,  map[string]int,  bool) ( int,  error) {
	return packDomainName(, , , compressionMap{ext: }, )
}

func packDomainName( string,  []byte,  int,  compressionMap,  bool) ( int,  error) {
	// XXX: A logical copy of this function exists in IsDomainName and
	// should be kept in sync with this function.

	 := len()
	if  == 0 { // Ok, for instance when dealing with update RR without any rdata.
		return , nil
	}

	// If not fully qualified, error out.
	if !IsFqdn() {
		return len(), ErrFqdn
	}

	// Each dot ends a segment of the name.
	// We trade each dot byte for a length byte.
	// Except for escaped dots (\.), which are normal dots.
	// There is also a trailing zero.

	// Compression
	 := -1

	// Emit sequence of counted strings, chopping at dots.
	var (
		     int
		 int
		   int
		        []byte
		    bool
	)
:
	for  := 0;  < ; ++ {
		var  byte
		if  == nil {
			 = []
		} else {
			 = []
		}

		switch  {
		case '\\':
			if +1 > len() {
				return len(), ErrBuf
			}

			if  == nil {
				 = []byte()
			}

			// check for \DDD
			if isDDD([+1:]) {
				[] = dddToByte([+1:])
				copy([+1:-3], [+4:])
				 -= 3
				 += 3
			} else {
				copy([:-1], [+1:])
				--
				++
			}

			 = false
		case '.':
			if  == 0 && len() > 1 {
				// leading dots are not legal except for the root zone
				return len(), ErrRdata
			}

			if  {
				// two dots back to back is not legal
				return len(), ErrRdata
			}
			 = true

			 :=  - 
			if  >= 1<<6 { // top two bits of length must be clear
				return len(), ErrRdata
			}

			// off can already (we're in a loop) be bigger than len(msg)
			// this happens when a name isn't fully qualified
			if +1+ > len() {
				return len(), ErrBuf
			}

			// Don't try to compress '.'
			// We should only compress when compress is true, but we should also still pick
			// up names that can be used for *future* compression(s).
			if .valid() && !isRootLabel(, , , ) {
				if ,  := .find([:]);  {
					// The first hit is the longest matching dname
					// keep the pointer offset we get back and store
					// the offset of the current name, because that's
					// where we need to insert the pointer later

					// If compress is true, we're allowed to compress this dname
					if  {
						 =  // Where to point to
						break 
					}
				} else if  < maxCompressionOffset {
					// Only offsets smaller than maxCompressionOffset can be used.
					.insert([:], )
				}
			}

			// The following is covered by the length check above.
			[] = byte()

			if  == nil {
				copy([+1:], [:])
			} else {
				copy([+1:], [:])
			}
			 += 1 + 

			 =  + 1
			 =  + 
		default:
			 = false
		}
	}

	// Root label is special
	if isRootLabel(, , 0, ) {
		return , nil
	}

	// If we did compression and we find something add the pointer here
	if  != -1 {
		// We have two bytes (14 bits) to put the pointer in
		binary.BigEndian.PutUint16([:], uint16(^0xC000))
		return  + 2, nil
	}

	if  < len() {
		[] = 0
	}

	return  + 1, nil
}

// isRootLabel returns whether s or bs, from off to end, is the root
// label ".".
//
// If bs is nil, s will be checked, otherwise bs will be checked.
func isRootLabel( string,  []byte, ,  int) bool {
	if  == nil {
		return [:] == "."
	}

	return - == 1 && [] == '.'
}

// Unpack a domain name.
// In addition to the simple sequences of counted strings above,
// domain names are allowed to refer to strings elsewhere in the
// packet, to avoid repeating common suffixes when returning
// many entries in a single domain.  The pointers are marked
// by a length byte with the top two bits set.  Ignoring those
// two bits, that byte and the next give a 14 bit offset from msg[0]
// where we should pick up the trail.
// Note that if we jump elsewhere in the packet,
// we return off1 == the offset after the first pointer we found,
// which is where the next record will start.
// In theory, the pointers are only allowed to jump backward.
// We let them jump anywhere and stop jumping after a while.

// UnpackDomainName unpacks a domain name into a string. It returns
// the name, the new offset into msg and any error that occurred.
//
// When an error is encountered, the unpacked name will be discarded
// and len(msg) will be returned as the offset.
func ( []byte,  int) (string, int, error) {
	 := make([]byte, 0, maxDomainNamePresentationLength)
	 := 0
	 := len()
	 := maxDomainNameWireOctets
	 := 0 // number of pointers followed
:
	for {
		if  >=  {
			return "", , ErrBuf
		}
		 := int([])
		++
		switch  & 0xC0 {
		case 0x00:
			if  == 0x00 {
				// end of name
				break 
			}
			// literal string
			if + >  {
				return "", , ErrBuf
			}
			 -=  + 1 // +1 for the label separator
			if  <= 0 {
				return "", , ErrLongDomain
			}
			for ,  := range [ : +] {
				if isDomainNameLabelSpecial() {
					 = append(, '\\', )
				} else if  < ' ' ||  > '~' {
					 = append(, escapeByte()...)
				} else {
					 = append(, )
				}
			}
			 = append(, '.')
			 += 
		case 0xC0:
			// pointer to somewhere else in msg.
			// remember location after first ptr,
			// since that's how many bytes we consumed.
			// also, don't follow too many pointers --
			// maybe there's a loop.
			if  >=  {
				return "", , ErrBuf
			}
			 := []
			++
			if  == 0 {
				 = 
			}
			if ++;  > maxCompressionPointers {
				return "", , &Error{err: "too many compression pointers"}
			}
			// pointer should guarantee that it advances and points forwards at least
			// but the condition on previous three lines guarantees that it's
			// at least loop-free
			 = (^0xC0)<<8 | int()
		default:
			// 0x80 and 0x40 are reserved
			return "", , ErrRdata
		}
	}
	if  == 0 {
		 = 
	}
	if len() == 0 {
		return ".", , nil
	}
	return string(), , nil
}

func packTxt( []string,  []byte,  int) (int, error) {
	if len() == 0 {
		if  >= len() {
			return , ErrBuf
		}
		[] = 0
		return , nil
	}
	var  error
	for ,  := range  {
		,  = packTxtString(, , )
		if  != nil {
			return , 
		}
	}
	return , nil
}

func packTxtString( string,  []byte,  int) (int, error) {
	 := 
	if  >= len() || len() > 256*4+1 /* If all \DDD */ {
		return , ErrBuf
	}
	++
	for  := 0;  < len(); ++ {
		if len() <=  {
			return , ErrBuf
		}
		if [] == '\\' {
			++
			if  == len() {
				break
			}
			// check for \DDD
			if isDDD([:]) {
				[] = dddToByte([:])
				 += 2
			} else {
				[] = []
			}
		} else {
			[] = []
		}
		++
	}
	 :=  -  - 1
	if  > 255 {
		return , &Error{err: "string exceeded 255 bytes in txt"}
	}
	[] = byte()
	return , nil
}

func packOctetString( string,  []byte,  int) (int, error) {
	if  >= len() || len() > 256*4+1 {
		return , ErrBuf
	}
	for  := 0;  < len(); ++ {
		if len() <=  {
			return , ErrBuf
		}
		if [] == '\\' {
			++
			if  == len() {
				break
			}
			// check for \DDD
			if isDDD([:]) {
				[] = dddToByte([:])
				 += 2
			} else {
				[] = []
			}
		} else {
			[] = []
		}
		++
	}
	return , nil
}

func unpackTxt( []byte,  int) ( []string,  int,  error) {
	 = 
	var  string
	for  < len() &&  == nil {
		, ,  = unpackString(, )
		if  == nil {
			 = append(, )
		}
	}
	return
}

// Helpers for dealing with escaped bytes
func isDigit( byte) bool { return  >= '0' &&  <= '9' }

func isDDD[ ~[]byte | ~string]( ) bool {
	return len() >= 3 && isDigit([0]) && isDigit([1]) && isDigit([2])
}

func dddToByte[ ~[]byte | ~string]( ) byte {
	_ = [2] // bounds check hint to compiler; see golang.org/issue/14808
	return byte(([0]-'0')*100 + ([1]-'0')*10 + ([2] - '0'))
}

// Helper function for packing and unpacking
func intToBytes( *big.Int,  int) []byte {
	 := .Bytes()
	if len() <  {
		 := make([]byte, )
		copy([-len():], )
		return 
	}
	return 
}

// PackRR packs a resource record rr into msg[off:].
// See PackDomainName for documentation about the compression.
func ( RR,  []byte,  int,  map[string]int,  bool) ( int,  error) {
	, ,  := packRR(, , , compressionMap{ext: }, )
	if  == nil {
		// packRR no longer sets the Rdlength field on the rr, but
		// callers might be expecting it so we set it here.
		.Header().Rdlength = uint16( - )
	}
	return , 
}

func packRR( RR,  []byte,  int,  compressionMap,  bool) ( int,  int,  error) {
	if  == nil {
		return len(), len(), &Error{err: "nil rr"}
	}

	,  = .Header().packHeader(, , , )
	if  != nil {
		return , len(), 
	}

	,  = .pack(, , , )
	if  != nil {
		return , len(), 
	}

	 :=  - 
	if int(uint16()) !=  { // overflow
		return , len(), ErrRdata
	}

	// The RDLENGTH field is the last field in the header and we set it here.
	binary.BigEndian.PutUint16([-2:], uint16())
	return , , nil
}

// UnpackRR unpacks msg[off:] into an RR.
func ( []byte,  int) ( RR,  int,  error) {
	, , ,  := unpackHeader(, )
	if  != nil {
		return nil, len(), 
	}

	return UnpackRRWithHeader(, , )
}

// UnpackRRWithHeader unpacks the record type specific payload given an existing
// RR_Header.
func ( RR_Header,  []byte,  int) ( RR,  int,  error) {
	if ,  := TypeToRR[.Rrtype];  {
		 = ()
		*.Header() = 
	} else {
		 = &RFC3597{Hdr: }
	}

	if  < 0 ||  > len() {
		return &, , &Error{err: "bad off"}
	}

	 :=  + int(.Rdlength)
	if  <  ||  > len() {
		return &, , &Error{err: "bad rdlength"}
	}

	if noRdata() {
		return , , nil
	}

	,  = .unpack(, )
	if  != nil {
		return nil, , 
	}
	if  !=  {
		return &, , &Error{err: "bad rdlength"}
	}

	return , , nil
}

// unpackRRslice unpacks msg[off:] into an []RR.
// If we cannot unpack the whole array, then it will return nil
func unpackRRslice( int,  []byte,  int) ( []RR,  int,  error) {
	var  RR
	// Don't pre-allocate, l may be under attacker control
	var  []RR
	for  := 0;  < ; ++ {
		 := 
		, ,  = UnpackRR(, )
		if  != nil {
			 = len()
			break
		}
		// If offset does not increase anymore, l is a lie
		if  ==  {
			break
		}
		 = append(, )
	}
	if  != nil &&  == len() {
		 = nil
	}
	return , , 
}

// Convert a MsgHdr to a string, with dig-like headers:
//
// ;; opcode: QUERY, status: NOERROR, id: 48404
//
// ;; flags: qr aa rd ra;
func ( *MsgHdr) () string {
	if  == nil {
		return "<nil> MsgHdr"
	}

	 := ";; opcode: " + OpcodeToString[.Opcode]
	 += ", status: " + RcodeToString[.Rcode]
	 += ", id: " + strconv.Itoa(int(.Id)) + "\n"

	 += ";; flags:"
	if .Response {
		 += " qr"
	}
	if .Authoritative {
		 += " aa"
	}
	if .Truncated {
		 += " tc"
	}
	if .RecursionDesired {
		 += " rd"
	}
	if .RecursionAvailable {
		 += " ra"
	}
	if .Zero { // Hmm
		 += " z"
	}
	if .AuthenticatedData {
		 += " ad"
	}
	if .CheckingDisabled {
		 += " cd"
	}

	 += ";"
	return 
}

// Pack packs a Msg: it is converted to wire format.
// If the dns.Compress is true the message will be in compressed wire format.
func ( *Msg) () ( []byte,  error) {
	return .PackBuffer(nil)
}

// PackBuffer packs a Msg, using the given buffer buf. If buf is too small a new buffer is allocated.
func ( *Msg) ( []byte) ( []byte,  error) {
	// If this message can't be compressed, avoid filling the
	// compression map and creating garbage.
	if .Compress && .isCompressible() {
		 := make(map[string]uint16) // Compression pointer mappings.
		return .packBufferWithCompressionMap(, compressionMap{int: }, true)
	}

	return .packBufferWithCompressionMap(, compressionMap{}, false)
}

// packBufferWithCompressionMap packs a Msg, using the given buffer buf.
func ( *Msg) ( []byte,  compressionMap,  bool) ( []byte,  error) {
	if .Rcode < 0 || .Rcode > 0xFFF {
		return nil, ErrRcode
	}

	// Set extended rcode unconditionally if we have an opt, this will allow
	// resetting the extended rcode bits if they need to.
	if  := .IsEdns0();  != nil {
		.SetExtendedRcode(uint16(.Rcode))
	} else if .Rcode > 0xF {
		// If Rcode is an extended one and opt is nil, error out.
		return nil, ErrExtendedRcode
	}

	// Convert convenient Msg into wire-like Header.
	var  Header
	.Id = .Id
	.Bits = uint16(.Opcode)<<11 | uint16(.Rcode&0xF)
	if .Response {
		.Bits |= _QR
	}
	if .Authoritative {
		.Bits |= _AA
	}
	if .Truncated {
		.Bits |= _TC
	}
	if .RecursionDesired {
		.Bits |= _RD
	}
	if .RecursionAvailable {
		.Bits |= _RA
	}
	if .Zero {
		.Bits |= _Z
	}
	if .AuthenticatedData {
		.Bits |= _AD
	}
	if .CheckingDisabled {
		.Bits |= _CD
	}

	.Qdcount = uint16(len(.Question))
	.Ancount = uint16(len(.Answer))
	.Nscount = uint16(len(.Ns))
	.Arcount = uint16(len(.Extra))

	// We need the uncompressed length here, because we first pack it and then compress it.
	 = 
	 := msgLenWithCompressionMap(, nil)
	if  :=  + 1; len() <  {
		 = make([]byte, )
	}

	// Pack it in: header and then the pieces.
	 := 0
	,  = .pack(, , , )
	if  != nil {
		return nil, 
	}
	for ,  := range .Question {
		,  = .pack(, , , )
		if  != nil {
			return nil, 
		}
	}
	for ,  := range .Answer {
		_, ,  = packRR(, , , , )
		if  != nil {
			return nil, 
		}
	}
	for ,  := range .Ns {
		_, ,  = packRR(, , , , )
		if  != nil {
			return nil, 
		}
	}
	for ,  := range .Extra {
		_, ,  = packRR(, , , , )
		if  != nil {
			return nil, 
		}
	}
	return [:], nil
}

func ( *Msg) ( Header,  []byte,  int) ( error) {
	// If we are at the end of the message we should return *just* the
	// header. This can still be useful to the caller. 9.9.9.9 sends these
	// when responding with REFUSED for instance.
	if  == len() {
		// reset sections before returning
		.Question, .Answer, .Ns, .Extra = nil, nil, nil, nil
		return nil
	}

	// Qdcount, Ancount, Nscount, Arcount can't be trusted, as they are
	// attacker controlled. This means we can't use them to pre-allocate
	// slices.
	.Question = nil
	for  := 0;  < int(.Qdcount); ++ {
		 := 
		var  Question
		, ,  = unpackQuestion(, )
		if  != nil {
			return 
		}
		if  ==  { // Offset does not increase anymore, dh.Qdcount is a lie!
			.Qdcount = uint16()
			break
		}
		.Question = append(.Question, )
	}

	.Answer, ,  = unpackRRslice(int(.Ancount), , )
	// The header counts might have been wrong so we need to update it
	.Ancount = uint16(len(.Answer))
	if  == nil {
		.Ns, ,  = unpackRRslice(int(.Nscount), , )
	}
	// The header counts might have been wrong so we need to update it
	.Nscount = uint16(len(.Ns))
	if  == nil {
		.Extra, _,  = unpackRRslice(int(.Arcount), , )
	}
	// The header counts might have been wrong so we need to update it
	.Arcount = uint16(len(.Extra))

	// Set extended Rcode
	if  := .IsEdns0();  != nil {
		.Rcode |= .ExtendedRcode()
	}

	// TODO(miek) make this an error?
	// use PackOpt to let people tell how detailed the error reporting should be?
	// if off != len(msg) {
	// 	// println("dns: extra bytes in dns packet", off, "<", len(msg))
	// }
	return 
}

// Unpack unpacks a binary message to a Msg structure.
func ( *Msg) ( []byte) ( error) {
	, ,  := unpackMsgHdr(, 0)
	if  != nil {
		return 
	}

	.setHdr()
	return .unpack(, , )
}

// Convert a complete message to a string with dig-like output.
func ( *Msg) () string {
	if  == nil {
		return "<nil> MsgHdr"
	}
	 := .MsgHdr.String() + " "
	if .MsgHdr.Opcode == OpcodeUpdate {
		 += "ZONE: " + strconv.Itoa(len(.Question)) + ", "
		 += "PREREQ: " + strconv.Itoa(len(.Answer)) + ", "
		 += "UPDATE: " + strconv.Itoa(len(.Ns)) + ", "
		 += "ADDITIONAL: " + strconv.Itoa(len(.Extra)) + "\n"
	} else {
		 += "QUERY: " + strconv.Itoa(len(.Question)) + ", "
		 += "ANSWER: " + strconv.Itoa(len(.Answer)) + ", "
		 += "AUTHORITY: " + strconv.Itoa(len(.Ns)) + ", "
		 += "ADDITIONAL: " + strconv.Itoa(len(.Extra)) + "\n"
	}
	 := .IsEdns0()
	if  != nil {
		// OPT PSEUDOSECTION
		 += .String() + "\n"
	}
	if len(.Question) > 0 {
		if .MsgHdr.Opcode == OpcodeUpdate {
			 += "\n;; ZONE SECTION:\n"
		} else {
			 += "\n;; QUESTION SECTION:\n"
		}
		for ,  := range .Question {
			 += .String() + "\n"
		}
	}
	if len(.Answer) > 0 {
		if .MsgHdr.Opcode == OpcodeUpdate {
			 += "\n;; PREREQUISITE SECTION:\n"
		} else {
			 += "\n;; ANSWER SECTION:\n"
		}
		for ,  := range .Answer {
			if  != nil {
				 += .String() + "\n"
			}
		}
	}
	if len(.Ns) > 0 {
		if .MsgHdr.Opcode == OpcodeUpdate {
			 += "\n;; UPDATE SECTION:\n"
		} else {
			 += "\n;; AUTHORITY SECTION:\n"
		}
		for ,  := range .Ns {
			if  != nil {
				 += .String() + "\n"
			}
		}
	}
	if len(.Extra) > 0 && ( == nil || len(.Extra) > 1) {
		 += "\n;; ADDITIONAL SECTION:\n"
		for ,  := range .Extra {
			if  != nil && .Header().Rrtype != TypeOPT {
				 += .String() + "\n"
			}
		}
	}
	return 
}

// isCompressible returns whether the msg may be compressible.
func ( *Msg) () bool {
	// If we only have one question, there is nothing we can ever compress.
	return len(.Question) > 1 || len(.Answer) > 0 ||
		len(.Ns) > 0 || len(.Extra) > 0
}

// Len returns the message length when in (un)compressed wire format.
// If dns.Compress is true compression it is taken into account. Len()
// is provided to be a faster way to get the size of the resulting packet,
// than packing it, measuring the size and discarding the buffer.
func ( *Msg) () int {
	// If this message can't be compressed, avoid filling the
	// compression map and creating garbage.
	if .Compress && .isCompressible() {
		 := make(map[string]struct{})
		return msgLenWithCompressionMap(, )
	}

	return msgLenWithCompressionMap(, nil)
}

func msgLenWithCompressionMap( *Msg,  map[string]struct{}) int {
	 := headerSize

	for ,  := range .Question {
		 += .len(, )
	}
	for ,  := range .Answer {
		if  != nil {
			 += .len(, )
		}
	}
	for ,  := range .Ns {
		if  != nil {
			 += .len(, )
		}
	}
	for ,  := range .Extra {
		if  != nil {
			 += .len(, )
		}
	}

	return 
}

func domainNameLen( string,  int,  map[string]struct{},  bool) int {
	if  == "" ||  == "." {
		return 1
	}

	 := strings.Contains(, "\\")

	if  != nil && ( ||  < maxCompressionOffset) {
		// compressionLenSearch will insert the entry into the compression
		// map if it doesn't contain it.
		if ,  := compressionLenSearch(, , );  &&  {
			if  {
				return escapedNameLen([:]) + 2
			}

			return  + 2
		}
	}

	if  {
		return escapedNameLen() + 1
	}

	return len() + 1
}

func escapedNameLen( string) int {
	 := len()
	for  := 0;  < len(); ++ {
		if [] != '\\' {
			continue
		}

		if isDDD([+1:]) {
			 -= 3
			 += 3
		} else {
			--
			++
		}
	}

	return 
}

func compressionLenSearch( map[string]struct{},  string,  int) (int, bool) {
	for ,  := 0, false; !; ,  = NextLabel(, ) {
		if ,  := [[:]];  {
			return , true
		}

		if + < maxCompressionOffset {
			[[:]] = struct{}{}
		}
	}

	return 0, false
}

// Copy returns a new RR which is a deep-copy of r.
func ( RR) RR { return .copy() }

// Len returns the length (in octets) of the uncompressed RR in wire format.
func ( RR) int { return .len(0, nil) }

// Copy returns a new *Msg which is a deep-copy of dns.
func ( *Msg) () *Msg { return .CopyTo(new(Msg)) }

// CopyTo copies the contents to the provided message using a deep-copy and returns the copy.
func ( *Msg) ( *Msg) *Msg {
	.MsgHdr = .MsgHdr
	.Compress = .Compress

	if len(.Question) > 0 {
		// TODO(miek): Question is an immutable value, ok to do a shallow-copy
		.Question = cloneSlice(.Question)
	}

	 := make([]RR, len(.Answer)+len(.Ns)+len(.Extra))
	.Answer,  = [:0:len(.Answer)], [len(.Answer):]
	.Ns,  = [:0:len(.Ns)], [len(.Ns):]
	.Extra = [:0:len(.Extra)]

	for ,  := range .Answer {
		.Answer = append(.Answer, .copy())
	}

	for ,  := range .Ns {
		.Ns = append(.Ns, .copy())
	}

	for ,  := range .Extra {
		.Extra = append(.Extra, .copy())
	}

	return 
}

func ( *Question) ( []byte,  int,  compressionMap,  bool) (int, error) {
	,  := packDomainName(.Name, , , , )
	if  != nil {
		return , 
	}
	,  = packUint16(.Qtype, , )
	if  != nil {
		return , 
	}
	,  = packUint16(.Qclass, , )
	if  != nil {
		return , 
	}
	return , nil
}

func unpackQuestion( []byte,  int) (Question, int, error) {
	var (
		   Question
		 error
	)
	.Name, ,  = UnpackDomainName(, )
	if  != nil {
		return , , 
	}
	if  == len() {
		return , , nil
	}
	.Qtype, ,  = unpackUint16(, )
	if  != nil {
		return , , 
	}
	if  == len() {
		return , , nil
	}
	.Qclass, ,  = unpackUint16(, )
	if  == len() {
		return , , nil
	}
	return , , 
}

func ( *Header) ( []byte,  int,  compressionMap,  bool) (int, error) {
	,  := packUint16(.Id, , )
	if  != nil {
		return , 
	}
	,  = packUint16(.Bits, , )
	if  != nil {
		return , 
	}
	,  = packUint16(.Qdcount, , )
	if  != nil {
		return , 
	}
	,  = packUint16(.Ancount, , )
	if  != nil {
		return , 
	}
	,  = packUint16(.Nscount, , )
	if  != nil {
		return , 
	}
	,  = packUint16(.Arcount, , )
	if  != nil {
		return , 
	}
	return , nil
}

func unpackMsgHdr( []byte,  int) (Header, int, error) {
	var (
		  Header
		 error
	)
	.Id, ,  = unpackUint16(, )
	if  != nil {
		return , , 
	}
	.Bits, ,  = unpackUint16(, )
	if  != nil {
		return , , 
	}
	.Qdcount, ,  = unpackUint16(, )
	if  != nil {
		return , , 
	}
	.Ancount, ,  = unpackUint16(, )
	if  != nil {
		return , , 
	}
	.Nscount, ,  = unpackUint16(, )
	if  != nil {
		return , , 
	}
	.Arcount, ,  = unpackUint16(, )
	if  != nil {
		return , , 
	}
	return , , nil
}

// setHdr set the header in the dns using the binary data in dh.
func ( *Msg) ( Header) {
	.Id = .Id
	.Response = .Bits&_QR != 0
	.Opcode = int(.Bits>>11) & 0xF
	.Authoritative = .Bits&_AA != 0
	.Truncated = .Bits&_TC != 0
	.RecursionDesired = .Bits&_RD != 0
	.RecursionAvailable = .Bits&_RA != 0
	.Zero = .Bits&_Z != 0 // _Z covers the zero bit, which should be zero; not sure why we set it to the opposite.
	.AuthenticatedData = .Bits&_AD != 0
	.CheckingDisabled = .Bits&_CD != 0
	.Rcode = int(.Bits & 0xF)
}