// 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.goimport ()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 (ErrAlgerror = &Error{err: "bad algorithm"} // ErrAlg indicates an error with the (DNSSEC) algorithm.ErrAutherror = &Error{err: "bad authentication"} // ErrAuth indicates an error in the TSIG authentication.ErrBuferror = &Error{err: "buffer size too small"} // ErrBuf indicates that the buffer used is too small for the message.ErrConnEmptyerror = &Error{err: "conn has no connection"} // ErrConnEmpty indicates a connection is being used before it is initialized.ErrExtendedRcodeerror = &Error{err: "bad extended rcode"} // ErrExtendedRcode ...ErrFqdnerror = &Error{err: "domain must be fully qualified"} // ErrFqdn indicates that a domain name does not have a closing dot.ErrIderror = &Error{err: "id mismatch"} // ErrId indicates there is a mismatch with the message's ID.ErrKeyAlgerror = &Error{err: "bad key algorithm"} // ErrKeyAlg indicates that the algorithm in the key is not valid.ErrKeyerror = &Error{err: "bad key"}ErrKeySizeerror = &Error{err: "bad key size"}ErrLongDomainerror = &Error{err: fmt.Sprintf("domain name exceeded %d wire-format octets", maxDomainNameWireOctets)}ErrNoSigerror = &Error{err: "no signature found"}ErrPrivKeyerror = &Error{err: "bad private key"}ErrRcodeerror = &Error{err: "bad rcode"}ErrRdataerror = &Error{err: "bad rdata"}ErrRRseterror = &Error{err: "bad rrset"}ErrSecreterror = &Error{err: "no secrets defined"}ErrShortReaderror = &Error{err: "short read"}ErrSigerror = &Error{err: "bad signature"} // ErrSig indicates that a signature can not be cryptographically validated.ErrSoaerror = &Error{err: "no SOA"} // ErrSOA indicates that no SOA RR was seen when doing zone transfers.ErrTimeerror = &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 }varId = 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 {varuint16 := 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).typeMsgHdrstruct { 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.typeMsgstruct {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.varClassToString = map[uint16]string{ClassINET: "IN",ClassCSNET: "CS",ClassCHAOS: "CH",ClassHESIOD: "HS",ClassNONE: "NONE",ClassANY: "ANY",}// OpcodeToString maps Opcodes to strings.varOpcodeToString = map[int]string{OpcodeQuery: "QUERY",OpcodeIQuery: "IQUERY",OpcodeStatus: "STATUS",OpcodeNotify: "NOTIFY",OpcodeUpdate: "UPDATE",}// RcodeToString maps Rcodes to strings.varRcodeToString = map[int]string{RcodeSuccess: "NOERROR",RcodeFormatError: "FORMERR",RcodeServerFailure: "SERVFAIL",RcodeNameError: "NXDOMAIN",RcodeNotImplemented: "NOTIMP",RcodeRefused: "REFUSED",RcodeYXDomain: "YXDOMAIN", // See RFC 2136RcodeYXRrset: "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[]returnint(), }// 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) {returnpackDomainName(, , , 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() {returnlen(), 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 (intintint []bytebool ):for := 0; < ; ++ {varbyteif == nil { = [] } else { = [] }switch {case'\\':if +1 > len() {returnlen(), ErrBuf }if == nil { = []byte() }// check for \DDDifisDDD([+1:]) { [] = dddToByte([+1:])copy([+1:-3], [+4:]) -= 3 += 3 } else {copy([:-1], [+1:]) -- ++ } = falsecase'.':if == 0 && len() > 1 {// leading dots are not legal except for the root zonereturnlen(), ErrRdata }if {// two dots back to back is not legalreturnlen(), ErrRdata } = true := - if >= 1<<6 { // top two bits of length must be clearreturnlen(), ErrRdata }// off can already (we're in a loop) be bigger than len(msg) // this happens when a name isn't fully qualifiedif +1+ > len() {returnlen(), 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 dnameif { = // Where to point tobreak } } elseif < 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 specialifisRootLabel(, , 0, ) {return , nil }// If we did compression and we find something add the pointer hereif != -1 {// We have two bytes (14 bits) to put the pointer inbinary.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 {case0x00:if == 0x00 {// end of namebreak }// literal stringif + > {return"", , ErrBuf } -= + 1// +1 for the label separatorif <= 0 {return"", , ErrLongDomain }for , := range [ : +] {ifisDomainNameLabelSpecial() { = append(, '\\', ) } elseif < ' ' || > '~' { = append(, escapeByte()...) } else { = append(, ) } } = append(, '.') += case0xC0:// 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 reservedreturn"", , ErrRdata } }if == 0 { = }iflen() == 0 {return".", , nil }returnstring(), , nil}func packTxt( []string, []byte, int) (int, error) {iflen() == 0 {if >= len() {return , ErrBuf } [] = 0return , nil }varerrorfor , := 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(); ++ {iflen() <= {return , ErrBuf }if [] == '\\' { ++if == len() {break }// check for \DDDifisDDD([:]) { [] = dddToByte([:]) += 2 } else { [] = [] } } else { [] = [] } ++ } := - - 1if > 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(); ++ {iflen() <= {return , ErrBuf }if [] == '\\' { ++if == len() {break }// check for \DDDifisDDD([:]) { [] = dddToByte([:]) += 2 } else { [] = [] } } else { [] = [] } ++ }return , nil}func unpackTxt( []byte, int) ( []string, int, error) { = varstringfor < len() && == nil { , , = unpackString(, )if == nil { = append(, ) } }return}// Helpers for dealing with escaped bytesfunc isDigit( byte) bool { return >= '0' && <= '9' }func isDDD[ ~[]byte | ~string]( ) bool {returnlen() >= 3 && isDigit([0]) && isDigit([1]) && isDigit([2])}func dddToByte[ ~[]byte | ~string]( ) byte { _ = [2] // bounds check hint to compiler; see golang.org/issue/14808returnbyte(([0]-'0')*100 + ([1]-'0')*10 + ([2] - '0'))}// Helper function for packing and unpackingfunc intToBytes( *big.Int, int) []byte { := .Bytes()iflen() < { := 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 {returnlen(), len(), &Error{err: "nil rr"} } , = .Header().packHeader(, , , )if != nil {return , len(), } , = .pack(, , , )if != nil {return , len(), } := - ifint(uint16()) != { // overflowreturn , 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 {returnnil, len(), }returnUnpackRRWithHeader(, , )}// 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"} }ifnoRdata() {return , , nil } , = .unpack(, )if != nil {returnnil, , }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 nilfunc unpackRRslice( int, []byte, int) ( []RR, int, error) {varRR// Don't pre-allocate, l may be under attacker controlvar []RRfor := 0; < ; ++ { := , , = UnpackRR(, )if != nil { = len()break }// If offset does not increase anymore, l is a lieif == {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 {returnnil, 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)) } elseif .Rcode > 0xF {// If Rcode is an extended one and opt is nil, error out.returnnil, ErrExtendedRcode }// Convert convenient Msg into wire-like Header.varHeader .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 {returnnil, }for , := range .Question { , = .pack(, , , )if != nil {returnnil, } }for , := range .Answer { _, , = packRR(, , , , )if != nil {returnnil, } }for , := range .Ns { _, , = packRR(, , , , )if != nil {returnnil, } }for , := range .Extra { _, , = packRR(, , , , )if != nil {returnnil, } }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, nilreturnnil }// 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 = nilfor := 0; < int(.Qdcount); ++ { := varQuestion , , = 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 Rcodeif := .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" }iflen(.Question) > 0 {if .MsgHdr.Opcode == OpcodeUpdate { += "\n;; ZONE SECTION:\n" } else { += "\n;; QUESTION SECTION:\n" }for , := range .Question { += .String() + "\n" } }iflen(.Answer) > 0 {if .MsgHdr.Opcode == OpcodeUpdate { += "\n;; PREREQUISITE SECTION:\n" } else { += "\n;; ANSWER SECTION:\n" }for , := range .Answer {if != nil { += .String() + "\n" } } }iflen(.Ns) > 0 {if .MsgHdr.Opcode == OpcodeUpdate { += "\n;; UPDATE SECTION:\n" } else { += "\n;; AUTHORITY SECTION:\n" }for , := range .Ns {if != nil { += .String() + "\n" } } }iflen(.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.returnlen(.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{})returnmsgLenWithCompressionMap(, ) }returnmsgLenWithCompressionMap(, nil)}func msgLenWithCompressionMap( *Msg, map[string]struct{}) int { := headerSizefor , := 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 == "" || == "." {return1 } := strings.Contains(, "\\")if != nil && ( || < maxCompressionOffset) {// compressionLenSearch will insert the entry into the compression // map if it doesn't contain it.if , := compressionLenSearch(, , ); && {if {returnescapedNameLen([:]) + 2 }return + 2 } }if {returnescapedNameLen() + 1 }returnlen() + 1}func escapedNameLen( string) int { := len()for := 0; < len(); ++ {if [] != '\\' {continue }ifisDDD([+1:]) { -= 3 += 3 } else { -- ++ } }return}func compressionLenSearch( map[string]struct{}, string, int) (int, bool) {for , := 0, false; !; , = NextLabel(, ) {if , := [[:]]; {return , true }if + < maxCompressionOffset { [[:]] = struct{}{} } }return0, 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 = .Compressiflen(.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 (Questionerror ) .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 (Headererror ) .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)}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.