// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package dnsmessage provides a mostly RFC 1035 compliant implementation of // DNS message packing and unpacking. // // The package also supports messages with Extension Mechanisms for DNS // (EDNS(0)) as defined in RFC 6891. // // This implementation is designed to minimize heap allocations and avoid // unnecessary packing and unpacking as much as possible.
package dnsmessage import ( ) // Message formats // A Type is a type of DNS request and response. type Type uint16 const ( // ResourceHeader.Type and Question.Type TypeA Type = 1 TypeNS Type = 2 TypeCNAME Type = 5 TypeSOA Type = 6 TypePTR Type = 12 TypeMX Type = 15 TypeTXT Type = 16 TypeAAAA Type = 28 TypeSRV Type = 33 TypeOPT Type = 41 // Question.Type TypeWKS Type = 11 TypeHINFO Type = 13 TypeMINFO Type = 14 TypeAXFR Type = 252 TypeALL Type = 255 ) var typeNames = map[Type]string{ TypeA: "TypeA", TypeNS: "TypeNS", TypeCNAME: "TypeCNAME", TypeSOA: "TypeSOA", TypePTR: "TypePTR", TypeMX: "TypeMX", TypeTXT: "TypeTXT", TypeAAAA: "TypeAAAA", TypeSRV: "TypeSRV", TypeOPT: "TypeOPT", TypeWKS: "TypeWKS", TypeHINFO: "TypeHINFO", TypeMINFO: "TypeMINFO", TypeAXFR: "TypeAXFR", TypeALL: "TypeALL", } // String implements fmt.Stringer.String. func ( Type) () string { if , := typeNames[]; { return } return printUint16(uint16()) } // GoString implements fmt.GoStringer.GoString. func ( Type) () string { if , := typeNames[]; { return "dnsmessage." + } return printUint16(uint16()) } // A Class is a type of network. type Class uint16 const ( // ResourceHeader.Class and Question.Class ClassINET Class = 1 ClassCSNET Class = 2 ClassCHAOS Class = 3 ClassHESIOD Class = 4 // Question.Class ClassANY Class = 255 ) var classNames = map[Class]string{ ClassINET: "ClassINET", ClassCSNET: "ClassCSNET", ClassCHAOS: "ClassCHAOS", ClassHESIOD: "ClassHESIOD", ClassANY: "ClassANY", } // String implements fmt.Stringer.String. func ( Class) () string { if , := classNames[]; { return } return printUint16(uint16()) } // GoString implements fmt.GoStringer.GoString. func ( Class) () string { if , := classNames[]; { return "dnsmessage." + } return printUint16(uint16()) } // An OpCode is a DNS operation code. type OpCode uint16 // GoString implements fmt.GoStringer.GoString. func ( OpCode) () string { return printUint16(uint16()) } // An RCode is a DNS response status code. type RCode uint16 // Header.RCode values. const ( RCodeSuccess RCode = 0 // NoError RCodeFormatError RCode = 1 // FormErr RCodeServerFailure RCode = 2 // ServFail RCodeNameError RCode = 3 // NXDomain RCodeNotImplemented RCode = 4 // NotImp RCodeRefused RCode = 5 // Refused ) var rCodeNames = map[RCode]string{ RCodeSuccess: "RCodeSuccess", RCodeFormatError: "RCodeFormatError", RCodeServerFailure: "RCodeServerFailure", RCodeNameError: "RCodeNameError", RCodeNotImplemented: "RCodeNotImplemented", RCodeRefused: "RCodeRefused", } // String implements fmt.Stringer.String. func ( RCode) () string { if , := rCodeNames[]; { return } return printUint16(uint16()) } // GoString implements fmt.GoStringer.GoString. func ( RCode) () string { if , := rCodeNames[]; { return "dnsmessage." + } return printUint16(uint16()) } func printPaddedUint8( uint8) string { := byte() return string([]byte{ /100 + '0', /10%10 + '0', %10 + '0', }) } func printUint8Bytes( []byte, uint8) []byte { := byte() if >= 100 { = append(, /100+'0') } if >= 10 { = append(, /10%10+'0') } return append(, %10+'0') } func printByteSlice( []byte) string { if len() == 0 { return "" } := make([]byte, 0, 5*len()) = printUint8Bytes(, uint8([0])) for , := range [1:] { = append(, ',', ' ') = printUint8Bytes(, uint8()) } return string() } const hexDigits = "0123456789abcdef" func printString( []byte) string { := make([]byte, 0, len()) for := 0; < len(); ++ { := [] if == '.' || == '-' || == ' ' || 'A' <= && <= 'Z' || 'a' <= && <= 'z' || '0' <= && <= '9' { = append(, ) continue } := >> 4 := ( << 4) >> 4 = append( , '\\', 'x', hexDigits[], hexDigits[], ) } return string() } func printUint16( uint16) string { return printUint32(uint32()) } func printUint32( uint32) string { // Max value is 4294967295. := make([]byte, 10) for , := , uint32(1000000000); > 0; /= 10 { [0] = byte(/%10 + '0') if [0] == '0' && len() == len() && len() > 1 { = [1:] } = [1:] %= } return string() } func printBool( bool) string { if { return "true" } return "false" } var ( // ErrNotStarted indicates that the prerequisite information isn't // available yet because the previous records haven't been appropriately // parsed, skipped or finished. ErrNotStarted = errors.New("parsing/packing of this type isn't available yet") // ErrSectionDone indicated that all records in the section have been // parsed or finished. ErrSectionDone = errors.New("parsing/packing of this section has completed") errBaseLen = errors.New("insufficient data for base length type") errCalcLen = errors.New("insufficient data for calculated length type") errReserved = errors.New("segment prefix is reserved") errTooManyPtr = errors.New("too many pointers (>10)") errInvalidPtr = errors.New("invalid pointer") errInvalidName = errors.New("invalid dns name") errNilResouceBody = errors.New("nil resource body") errResourceLen = errors.New("insufficient data for resource body length") errSegTooLong = errors.New("segment length too long") errNameTooLong = errors.New("name too long") errZeroSegLen = errors.New("zero length segment") errResTooLong = errors.New("resource length too long") errTooManyQuestions = errors.New("too many Questions to pack (>65535)") errTooManyAnswers = errors.New("too many Answers to pack (>65535)") errTooManyAuthorities = errors.New("too many Authorities to pack (>65535)") errTooManyAdditionals = errors.New("too many Additionals to pack (>65535)") errNonCanonicalName = errors.New("name is not in canonical format (it must end with a .)") errStringTooLong = errors.New("character string exceeds maximum length (255)") ) // Internal constants. const ( // packStartingCap is the default initial buffer size allocated during // packing. // // The starting capacity doesn't matter too much, but most DNS responses // Will be <= 512 bytes as it is the limit for DNS over UDP. packStartingCap = 512 // uint16Len is the length (in bytes) of a uint16. uint16Len = 2 // uint32Len is the length (in bytes) of a uint32. uint32Len = 4 // headerLen is the length (in bytes) of a DNS header. // // A header is comprised of 6 uint16s and no padding. headerLen = 6 * uint16Len ) type nestedError struct { // s is the current level's error message. s string // err is the nested error. err error } // nestedError implements error.Error. func ( *nestedError) () string { return .s + ": " + .err.Error() } // Header is a representation of a DNS message header. type Header struct { ID uint16 Response bool OpCode OpCode Authoritative bool Truncated bool RecursionDesired bool RecursionAvailable bool AuthenticData bool CheckingDisabled bool RCode RCode } func ( *Header) () ( uint16, uint16) { = .ID = uint16(.OpCode)<<11 | uint16(.RCode) if .RecursionAvailable { |= headerBitRA } if .RecursionDesired { |= headerBitRD } if .Truncated { |= headerBitTC } if .Authoritative { |= headerBitAA } if .Response { |= headerBitQR } if .AuthenticData { |= headerBitAD } if .CheckingDisabled { |= headerBitCD } return } // GoString implements fmt.GoStringer.GoString. func ( *Header) () string { return "dnsmessage.Header{" + "ID: " + printUint16(.ID) + ", " + "Response: " + printBool(.Response) + ", " + "OpCode: " + .OpCode.GoString() + ", " + "Authoritative: " + printBool(.Authoritative) + ", " + "Truncated: " + printBool(.Truncated) + ", " + "RecursionDesired: " + printBool(.RecursionDesired) + ", " + "RecursionAvailable: " + printBool(.RecursionAvailable) + ", " + "AuthenticData: " + printBool(.AuthenticData) + ", " + "CheckingDisabled: " + printBool(.CheckingDisabled) + ", " + "RCode: " + .RCode.GoString() + "}" } // Message is a representation of a DNS message. type Message struct { Header Questions []Question Answers []Resource Authorities []Resource Additionals []Resource } type section uint8 const ( sectionNotStarted section = iota sectionHeader sectionQuestions sectionAnswers sectionAuthorities sectionAdditionals sectionDone headerBitQR = 1 << 15 // query/response (response=1) headerBitAA = 1 << 10 // authoritative headerBitTC = 1 << 9 // truncated headerBitRD = 1 << 8 // recursion desired headerBitRA = 1 << 7 // recursion available headerBitAD = 1 << 5 // authentic data headerBitCD = 1 << 4 // checking disabled ) var sectionNames = map[section]string{ sectionHeader: "header", sectionQuestions: "Question", sectionAnswers: "Answer", sectionAuthorities: "Authority", sectionAdditionals: "Additional", } // header is the wire format for a DNS message header. type header struct { id uint16 bits uint16 questions uint16 answers uint16 authorities uint16 additionals uint16 } func ( *header) ( section) uint16 { switch { case sectionQuestions: return .questions case sectionAnswers: return .answers case sectionAuthorities: return .authorities case sectionAdditionals: return .additionals } return 0 } // pack appends the wire format of the header to msg. func ( *header) ( []byte) []byte { = packUint16(, .id) = packUint16(, .bits) = packUint16(, .questions) = packUint16(, .answers) = packUint16(, .authorities) return packUint16(, .additionals) } func ( *header) ( []byte, int) (int, error) { := var error if .id, , = unpackUint16(, ); != nil { return , &nestedError{"id", } } if .bits, , = unpackUint16(, ); != nil { return , &nestedError{"bits", } } if .questions, , = unpackUint16(, ); != nil { return , &nestedError{"questions", } } if .answers, , = unpackUint16(, ); != nil { return , &nestedError{"answers", } } if .authorities, , = unpackUint16(, ); != nil { return , &nestedError{"authorities", } } if .additionals, , = unpackUint16(, ); != nil { return , &nestedError{"additionals", } } return , nil } func ( *header) () Header { return Header{ ID: .id, Response: (.bits & headerBitQR) != 0, OpCode: OpCode(.bits>>11) & 0xF, Authoritative: (.bits & headerBitAA) != 0, Truncated: (.bits & headerBitTC) != 0, RecursionDesired: (.bits & headerBitRD) != 0, RecursionAvailable: (.bits & headerBitRA) != 0, AuthenticData: (.bits & headerBitAD) != 0, CheckingDisabled: (.bits & headerBitCD) != 0, RCode: RCode(.bits & 0xF), } } // A Resource is a DNS resource record. type Resource struct { Header ResourceHeader Body ResourceBody } func ( *Resource) () string { return "dnsmessage.Resource{" + "Header: " + .Header.GoString() + ", Body: &" + .Body.GoString() + "}" } // A ResourceBody is a DNS resource record minus the header. type ResourceBody interface { // pack packs a Resource except for its header. pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) // realType returns the actual type of the Resource. This is used to // fill in the header Type field. realType() Type // GoString implements fmt.GoStringer.GoString. GoString() string } // pack appends the wire format of the Resource to msg. func ( *Resource) ( []byte, map[string]uint16, int) ([]byte, error) { if .Body == nil { return , errNilResouceBody } := .Header.Type = .Body.realType() , , := .Header.pack(, , ) if != nil { return , &nestedError{"ResourceHeader", } } := len() , = .Body.pack(, , ) if != nil { return , &nestedError{"content", } } if := .Header.fixLen(, , ); != nil { return , } return , nil } // A Parser allows incrementally parsing a DNS message. // // When parsing is started, the Header is parsed. Next, each Question can be // either parsed or skipped. Alternatively, all Questions can be skipped at // once. When all Questions have been parsed, attempting to parse Questions // will return the [ErrSectionDone] error. // After all Questions have been either parsed or skipped, all // Answers, Authorities and Additionals can be either parsed or skipped in the // same way, and each type of Resource must be fully parsed or skipped before // proceeding to the next type of Resource. // // Parser is safe to copy to preserve the parsing state. // // Note that there is no requirement to fully skip or parse the message. type Parser struct { msg []byte header header section section off int index int resHeaderValid bool resHeaderOffset int resHeaderType Type resHeaderLength uint16 } // Start parses the header and enables the parsing of Questions. func ( *Parser) ( []byte) (Header, error) { if .msg != nil { * = Parser{} } .msg = var error if .off, = .header.unpack(, 0); != nil { return Header{}, &nestedError{"unpacking header", } } .section = sectionQuestions return .header.header(), nil } func ( *Parser) ( section) error { if .section < { return ErrNotStarted } if .section > { return ErrSectionDone } .resHeaderValid = false if .index == int(.header.count()) { .index = 0 .section++ return ErrSectionDone } return nil } func ( *Parser) ( section) (Resource, error) { var Resource var error .Header, = .resourceHeader() if != nil { return , } .resHeaderValid = false .Body, .off, = unpackResourceBody(.msg, .off, .Header) if != nil { return Resource{}, &nestedError{"unpacking " + sectionNames[], } } .index++ return , nil } func ( *Parser) ( section) (ResourceHeader, error) { if .resHeaderValid { .off = .resHeaderOffset } if := .checkAdvance(); != nil { return ResourceHeader{}, } var ResourceHeader , := .unpack(.msg, .off) if != nil { return ResourceHeader{}, } .resHeaderValid = true .resHeaderOffset = .off .resHeaderType = .Type .resHeaderLength = .Length .off = return , nil } func ( *Parser) ( section) error { if .resHeaderValid && .section == { := .off + int(.resHeaderLength) if > len(.msg) { return errResourceLen } .off = .resHeaderValid = false .index++ return nil } if := .checkAdvance(); != nil { return } var error .off, = skipResource(.msg, .off) if != nil { return &nestedError{"skipping: " + sectionNames[], } } .index++ return nil } // Question parses a single Question. func ( *Parser) () (Question, error) { if := .checkAdvance(sectionQuestions); != nil { return Question{}, } var Name , := .unpack(.msg, .off) if != nil { return Question{}, &nestedError{"unpacking Question.Name", } } , , := unpackType(.msg, ) if != nil { return Question{}, &nestedError{"unpacking Question.Type", } } , , := unpackClass(.msg, ) if != nil { return Question{}, &nestedError{"unpacking Question.Class", } } .off = .index++ return Question{, , }, nil } // AllQuestions parses all Questions. func ( *Parser) () ([]Question, error) { // Multiple questions are valid according to the spec, // but servers don't actually support them. There will // be at most one question here. // // Do not pre-allocate based on info in p.header, since // the data is untrusted. := []Question{} for { , := .Question() if == ErrSectionDone { return , nil } if != nil { return nil, } = append(, ) } } // SkipQuestion skips a single Question. func ( *Parser) () error { if := .checkAdvance(sectionQuestions); != nil { return } , := skipName(.msg, .off) if != nil { return &nestedError{"skipping Question Name", } } if , = skipType(.msg, ); != nil { return &nestedError{"skipping Question Type", } } if , = skipClass(.msg, ); != nil { return &nestedError{"skipping Question Class", } } .off = .index++ return nil } // SkipAllQuestions skips all Questions. func ( *Parser) () error { for { if := .SkipQuestion(); == ErrSectionDone { return nil } else if != nil { return } } } // AnswerHeader parses a single Answer ResourceHeader. func ( *Parser) () (ResourceHeader, error) { return .resourceHeader(sectionAnswers) } // Answer parses a single Answer Resource. func ( *Parser) () (Resource, error) { return .resource(sectionAnswers) } // AllAnswers parses all Answer Resources. func ( *Parser) () ([]Resource, error) { // The most common query is for A/AAAA, which usually returns // a handful of IPs. // // Pre-allocate up to a certain limit, since p.header is // untrusted data. := int(.header.answers) if > 20 { = 20 } := make([]Resource, 0, ) for { , := .Answer() if == ErrSectionDone { return , nil } if != nil { return nil, } = append(, ) } } // SkipAnswer skips a single Answer Resource. // // It does not perform a complete validation of the resource header, which means // it may return a nil error when the [AnswerHeader] would actually return an error. func ( *Parser) () error { return .skipResource(sectionAnswers) } // SkipAllAnswers skips all Answer Resources. func ( *Parser) () error { for { if := .SkipAnswer(); == ErrSectionDone { return nil } else if != nil { return } } } // AuthorityHeader parses a single Authority ResourceHeader. func ( *Parser) () (ResourceHeader, error) { return .resourceHeader(sectionAuthorities) } // Authority parses a single Authority Resource. func ( *Parser) () (Resource, error) { return .resource(sectionAuthorities) } // AllAuthorities parses all Authority Resources. func ( *Parser) () ([]Resource, error) { // Authorities contains SOA in case of NXDOMAIN and friends, // otherwise it is empty. // // Pre-allocate up to a certain limit, since p.header is // untrusted data. := int(.header.authorities) if > 10 { = 10 } := make([]Resource, 0, ) for { , := .Authority() if == ErrSectionDone { return , nil } if != nil { return nil, } = append(, ) } } // SkipAuthority skips a single Authority Resource. // // It does not perform a complete validation of the resource header, which means // it may return a nil error when the [AuthorityHeader] would actually return an error. func ( *Parser) () error { return .skipResource(sectionAuthorities) } // SkipAllAuthorities skips all Authority Resources. func ( *Parser) () error { for { if := .SkipAuthority(); == ErrSectionDone { return nil } else if != nil { return } } } // AdditionalHeader parses a single Additional ResourceHeader. func ( *Parser) () (ResourceHeader, error) { return .resourceHeader(sectionAdditionals) } // Additional parses a single Additional Resource. func ( *Parser) () (Resource, error) { return .resource(sectionAdditionals) } // AllAdditionals parses all Additional Resources. func ( *Parser) () ([]Resource, error) { // Additionals usually contain OPT, and sometimes A/AAAA // glue records. // // Pre-allocate up to a certain limit, since p.header is // untrusted data. := int(.header.additionals) if > 10 { = 10 } := make([]Resource, 0, ) for { , := .Additional() if == ErrSectionDone { return , nil } if != nil { return nil, } = append(, ) } } // SkipAdditional skips a single Additional Resource. // // It does not perform a complete validation of the resource header, which means // it may return a nil error when the [AdditionalHeader] would actually return an error. func ( *Parser) () error { return .skipResource(sectionAdditionals) } // SkipAllAdditionals skips all Additional Resources. func ( *Parser) () error { for { if := .SkipAdditional(); == ErrSectionDone { return nil } else if != nil { return } } } // CNAMEResource parses a single CNAMEResource. // // One of the XXXHeader methods must have been called before calling this // method. func ( *Parser) () (CNAMEResource, error) { if !.resHeaderValid || .resHeaderType != TypeCNAME { return CNAMEResource{}, ErrNotStarted } , := unpackCNAMEResource(.msg, .off) if != nil { return CNAMEResource{}, } .off += int(.resHeaderLength) .resHeaderValid = false .index++ return , nil } // MXResource parses a single MXResource. // // One of the XXXHeader methods must have been called before calling this // method. func ( *Parser) () (MXResource, error) { if !.resHeaderValid || .resHeaderType != TypeMX { return MXResource{}, ErrNotStarted } , := unpackMXResource(.msg, .off) if != nil { return MXResource{}, } .off += int(.resHeaderLength) .resHeaderValid = false .index++ return , nil } // NSResource parses a single NSResource. // // One of the XXXHeader methods must have been called before calling this // method. func ( *Parser) () (NSResource, error) { if !.resHeaderValid || .resHeaderType != TypeNS { return NSResource{}, ErrNotStarted } , := unpackNSResource(.msg, .off) if != nil { return NSResource{}, } .off += int(.resHeaderLength) .resHeaderValid = false .index++ return , nil } // PTRResource parses a single PTRResource. // // One of the XXXHeader methods must have been called before calling this // method. func ( *Parser) () (PTRResource, error) { if !.resHeaderValid || .resHeaderType != TypePTR { return PTRResource{}, ErrNotStarted } , := unpackPTRResource(.msg, .off) if != nil { return PTRResource{}, } .off += int(.resHeaderLength) .resHeaderValid = false .index++ return , nil } // SOAResource parses a single SOAResource. // // One of the XXXHeader methods must have been called before calling this // method. func ( *Parser) () (SOAResource, error) { if !.resHeaderValid || .resHeaderType != TypeSOA { return SOAResource{}, ErrNotStarted } , := unpackSOAResource(.msg, .off) if != nil { return SOAResource{}, } .off += int(.resHeaderLength) .resHeaderValid = false .index++ return , nil } // TXTResource parses a single TXTResource. // // One of the XXXHeader methods must have been called before calling this // method. func ( *Parser) () (TXTResource, error) { if !.resHeaderValid || .resHeaderType != TypeTXT { return TXTResource{}, ErrNotStarted } , := unpackTXTResource(.msg, .off, .resHeaderLength) if != nil { return TXTResource{}, } .off += int(.resHeaderLength) .resHeaderValid = false .index++ return , nil } // SRVResource parses a single SRVResource. // // One of the XXXHeader methods must have been called before calling this // method. func ( *Parser) () (SRVResource, error) { if !.resHeaderValid || .resHeaderType != TypeSRV { return SRVResource{}, ErrNotStarted } , := unpackSRVResource(.msg, .off) if != nil { return SRVResource{}, } .off += int(.resHeaderLength) .resHeaderValid = false .index++ return , nil } // AResource parses a single AResource. // // One of the XXXHeader methods must have been called before calling this // method. func ( *Parser) () (AResource, error) { if !.resHeaderValid || .resHeaderType != TypeA { return AResource{}, ErrNotStarted } , := unpackAResource(.msg, .off) if != nil { return AResource{}, } .off += int(.resHeaderLength) .resHeaderValid = false .index++ return , nil } // AAAAResource parses a single AAAAResource. // // One of the XXXHeader methods must have been called before calling this // method. func ( *Parser) () (AAAAResource, error) { if !.resHeaderValid || .resHeaderType != TypeAAAA { return AAAAResource{}, ErrNotStarted } , := unpackAAAAResource(.msg, .off) if != nil { return AAAAResource{}, } .off += int(.resHeaderLength) .resHeaderValid = false .index++ return , nil } // OPTResource parses a single OPTResource. // // One of the XXXHeader methods must have been called before calling this // method. func ( *Parser) () (OPTResource, error) { if !.resHeaderValid || .resHeaderType != TypeOPT { return OPTResource{}, ErrNotStarted } , := unpackOPTResource(.msg, .off, .resHeaderLength) if != nil { return OPTResource{}, } .off += int(.resHeaderLength) .resHeaderValid = false .index++ return , nil } // UnknownResource parses a single UnknownResource. // // One of the XXXHeader methods must have been called before calling this // method. func ( *Parser) () (UnknownResource, error) { if !.resHeaderValid { return UnknownResource{}, ErrNotStarted } , := unpackUnknownResource(.resHeaderType, .msg, .off, .resHeaderLength) if != nil { return UnknownResource{}, } .off += int(.resHeaderLength) .resHeaderValid = false .index++ return , nil } // Unpack parses a full Message. func ( *Message) ( []byte) error { var Parser var error if .Header, = .Start(); != nil { return } if .Questions, = .AllQuestions(); != nil { return } if .Answers, = .AllAnswers(); != nil { return } if .Authorities, = .AllAuthorities(); != nil { return } if .Additionals, = .AllAdditionals(); != nil { return } return nil } // Pack packs a full Message. func ( *Message) () ([]byte, error) { return .AppendPack(make([]byte, 0, packStartingCap)) } // AppendPack is like Pack but appends the full Message to b and returns the // extended buffer. func ( *Message) ( []byte) ([]byte, error) { // Validate the lengths. It is very unlikely that anyone will try to // pack more than 65535 of any particular type, but it is possible and // we should fail gracefully. if len(.Questions) > int(^uint16(0)) { return nil, errTooManyQuestions } if len(.Answers) > int(^uint16(0)) { return nil, errTooManyAnswers } if len(.Authorities) > int(^uint16(0)) { return nil, errTooManyAuthorities } if len(.Additionals) > int(^uint16(0)) { return nil, errTooManyAdditionals } var header .id, .bits = .Header.pack() .questions = uint16(len(.Questions)) .answers = uint16(len(.Answers)) .authorities = uint16(len(.Authorities)) .additionals = uint16(len(.Additionals)) := len() := .pack() // RFC 1035 allows (but does not require) compression for packing. RFC // 1035 requires unpacking implementations to support compression, so // unconditionally enabling it is fine. // // DNS lookups are typically done over UDP, and RFC 1035 states that UDP // DNS messages can be a maximum of 512 bytes long. Without compression, // many DNS response messages are over this limit, so enabling // compression will help ensure compliance. := map[string]uint16{} for := range .Questions { var error if , = .Questions[].pack(, , ); != nil { return nil, &nestedError{"packing Question", } } } for := range .Answers { var error if , = .Answers[].pack(, , ); != nil { return nil, &nestedError{"packing Answer", } } } for := range .Authorities { var error if , = .Authorities[].pack(, , ); != nil { return nil, &nestedError{"packing Authority", } } } for := range .Additionals { var error if , = .Additionals[].pack(, , ); != nil { return nil, &nestedError{"packing Additional", } } } return , nil } // GoString implements fmt.GoStringer.GoString. func ( *Message) () string { := "dnsmessage.Message{Header: " + .Header.GoString() + ", " + "Questions: []dnsmessage.Question{" if len(.Questions) > 0 { += .Questions[0].GoString() for , := range .Questions[1:] { += ", " + .GoString() } } += "}, Answers: []dnsmessage.Resource{" if len(.Answers) > 0 { += .Answers[0].GoString() for , := range .Answers[1:] { += ", " + .GoString() } } += "}, Authorities: []dnsmessage.Resource{" if len(.Authorities) > 0 { += .Authorities[0].GoString() for , := range .Authorities[1:] { += ", " + .GoString() } } += "}, Additionals: []dnsmessage.Resource{" if len(.Additionals) > 0 { += .Additionals[0].GoString() for , := range .Additionals[1:] { += ", " + .GoString() } } return + "}}" } // A Builder allows incrementally packing a DNS message. // // Example usage: // // buf := make([]byte, 2, 514) // b := NewBuilder(buf, Header{...}) // b.EnableCompression() // // Optionally start a section and add things to that section. // // Repeat adding sections as necessary. // buf, err := b.Finish() // // If err is nil, buf[2:] will contain the built bytes. type Builder struct { // msg is the storage for the message being built. msg []byte // section keeps track of the current section being built. section section // header keeps track of what should go in the header when Finish is // called. header header // start is the starting index of the bytes allocated in msg for header. start int // compression is a mapping from name suffixes to their starting index // in msg. compression map[string]uint16 } // NewBuilder creates a new builder with compression disabled. // // Note: Most users will want to immediately enable compression with the // EnableCompression method. See that method's comment for why you may or may // not want to enable compression. // // The DNS message is appended to the provided initial buffer buf (which may be // nil) as it is built. The final message is returned by the (*Builder).Finish // method, which includes buf[:len(buf)] and may return the same underlying // array if there was sufficient capacity in the slice. func ( []byte, Header) Builder { if == nil { = make([]byte, 0, packStartingCap) } := Builder{msg: , start: len()} .header.id, .header.bits = .pack() var [headerLen]byte .msg = append(.msg, [:]...) .section = sectionHeader return } // EnableCompression enables compression in the Builder. // // Leaving compression disabled avoids compression related allocations, but can // result in larger message sizes. Be careful with this mode as it can cause // messages to exceed the UDP size limit. // // According to RFC 1035, section 4.1.4, the use of compression is optional, but // all implementations must accept both compressed and uncompressed DNS // messages. // // Compression should be enabled before any sections are added for best results. func ( *Builder) () { .compression = map[string]uint16{} } func ( *Builder) ( section) error { if .section <= sectionNotStarted { return ErrNotStarted } if .section > { return ErrSectionDone } return nil } // StartQuestions prepares the builder for packing Questions. func ( *Builder) () error { if := .startCheck(sectionQuestions); != nil { return } .section = sectionQuestions return nil } // StartAnswers prepares the builder for packing Answers. func ( *Builder) () error { if := .startCheck(sectionAnswers); != nil { return } .section = sectionAnswers return nil } // StartAuthorities prepares the builder for packing Authorities. func ( *Builder) () error { if := .startCheck(sectionAuthorities); != nil { return } .section = sectionAuthorities return nil } // StartAdditionals prepares the builder for packing Additionals. func ( *Builder) () error { if := .startCheck(sectionAdditionals); != nil { return } .section = sectionAdditionals return nil } func ( *Builder) () error { var *uint16 var error switch .section { case sectionQuestions: = &.header.questions = errTooManyQuestions case sectionAnswers: = &.header.answers = errTooManyAnswers case sectionAuthorities: = &.header.authorities = errTooManyAuthorities case sectionAdditionals: = &.header.additionals = errTooManyAdditionals } if * == ^uint16(0) { return } *++ return nil } // Question adds a single Question. func ( *Builder) ( Question) error { if .section < sectionQuestions { return ErrNotStarted } if .section > sectionQuestions { return ErrSectionDone } , := .pack(.msg, .compression, .start) if != nil { return } if := .incrementSectionCount(); != nil { return } .msg = return nil } func ( *Builder) () error { if .section < sectionAnswers { return ErrNotStarted } if .section > sectionAdditionals { return ErrSectionDone } return nil } // CNAMEResource adds a single CNAMEResource. func ( *Builder) ( ResourceHeader, CNAMEResource) error { if := .checkResourceSection(); != nil { return } .Type = .realType() , , := .pack(.msg, .compression, .start) if != nil { return &nestedError{"ResourceHeader", } } := len() if , = .pack(, .compression, .start); != nil { return &nestedError{"CNAMEResource body", } } if := .fixLen(, , ); != nil { return } if := .incrementSectionCount(); != nil { return } .msg = return nil } // MXResource adds a single MXResource. func ( *Builder) ( ResourceHeader, MXResource) error { if := .checkResourceSection(); != nil { return } .Type = .realType() , , := .pack(.msg, .compression, .start) if != nil { return &nestedError{"ResourceHeader", } } := len() if , = .pack(, .compression, .start); != nil { return &nestedError{"MXResource body", } } if := .fixLen(, , ); != nil { return } if := .incrementSectionCount(); != nil { return } .msg = return nil } // NSResource adds a single NSResource. func ( *Builder) ( ResourceHeader, NSResource) error { if := .checkResourceSection(); != nil { return } .Type = .realType() , , := .pack(.msg, .compression, .start) if != nil { return &nestedError{"ResourceHeader", } } := len() if , = .pack(, .compression, .start); != nil { return &nestedError{"NSResource body", } } if := .fixLen(, , ); != nil { return } if := .incrementSectionCount(); != nil { return } .msg = return nil } // PTRResource adds a single PTRResource. func ( *Builder) ( ResourceHeader, PTRResource) error { if := .checkResourceSection(); != nil { return } .Type = .realType() , , := .pack(.msg, .compression, .start) if != nil { return &nestedError{"ResourceHeader", } } := len() if , = .pack(, .compression, .start); != nil { return &nestedError{"PTRResource body", } } if := .fixLen(, , ); != nil { return } if := .incrementSectionCount(); != nil { return } .msg = return nil } // SOAResource adds a single SOAResource. func ( *Builder) ( ResourceHeader, SOAResource) error { if := .checkResourceSection(); != nil { return } .Type = .realType() , , := .pack(.msg, .compression, .start) if != nil { return &nestedError{"ResourceHeader", } } := len() if , = .pack(, .compression, .start); != nil { return &nestedError{"SOAResource body", } } if := .fixLen(, , ); != nil { return } if := .incrementSectionCount(); != nil { return } .msg = return nil } // TXTResource adds a single TXTResource. func ( *Builder) ( ResourceHeader, TXTResource) error { if := .checkResourceSection(); != nil { return } .Type = .realType() , , := .pack(.msg, .compression, .start) if != nil { return &nestedError{"ResourceHeader", } } := len() if , = .pack(, .compression, .start); != nil { return &nestedError{"TXTResource body", } } if := .fixLen(, , ); != nil { return } if := .incrementSectionCount(); != nil { return } .msg = return nil } // SRVResource adds a single SRVResource. func ( *Builder) ( ResourceHeader, SRVResource) error { if := .checkResourceSection(); != nil { return } .Type = .realType() , , := .pack(.msg, .compression, .start) if != nil { return &nestedError{"ResourceHeader", } } := len() if , = .pack(, .compression, .start); != nil { return &nestedError{"SRVResource body", } } if := .fixLen(, , ); != nil { return } if := .incrementSectionCount(); != nil { return } .msg = return nil } // AResource adds a single AResource. func ( *Builder) ( ResourceHeader, AResource) error { if := .checkResourceSection(); != nil { return } .Type = .realType() , , := .pack(.msg, .compression, .start) if != nil { return &nestedError{"ResourceHeader", } } := len() if , = .pack(, .compression, .start); != nil { return &nestedError{"AResource body", } } if := .fixLen(, , ); != nil { return } if := .incrementSectionCount(); != nil { return } .msg = return nil } // AAAAResource adds a single AAAAResource. func ( *Builder) ( ResourceHeader, AAAAResource) error { if := .checkResourceSection(); != nil { return } .Type = .realType() , , := .pack(.msg, .compression, .start) if != nil { return &nestedError{"ResourceHeader", } } := len() if , = .pack(, .compression, .start); != nil { return &nestedError{"AAAAResource body", } } if := .fixLen(, , ); != nil { return } if := .incrementSectionCount(); != nil { return } .msg = return nil } // OPTResource adds a single OPTResource. func ( *Builder) ( ResourceHeader, OPTResource) error { if := .checkResourceSection(); != nil { return } .Type = .realType() , , := .pack(.msg, .compression, .start) if != nil { return &nestedError{"ResourceHeader", } } := len() if , = .pack(, .compression, .start); != nil { return &nestedError{"OPTResource body", } } if := .fixLen(, , ); != nil { return } if := .incrementSectionCount(); != nil { return } .msg = return nil } // UnknownResource adds a single UnknownResource. func ( *Builder) ( ResourceHeader, UnknownResource) error { if := .checkResourceSection(); != nil { return } .Type = .realType() , , := .pack(.msg, .compression, .start) if != nil { return &nestedError{"ResourceHeader", } } := len() if , = .pack(, .compression, .start); != nil { return &nestedError{"UnknownResource body", } } if := .fixLen(, , ); != nil { return } if := .incrementSectionCount(); != nil { return } .msg = return nil } // Finish ends message building and generates a binary message. func ( *Builder) () ([]byte, error) { if .section < sectionHeader { return nil, ErrNotStarted } .section = sectionDone // Space for the header was allocated in NewBuilder. .header.pack(.msg[.start:.start]) return .msg, nil } // A ResourceHeader is the header of a DNS resource record. There are // many types of DNS resource records, but they all share the same header. type ResourceHeader struct { // Name is the domain name for which this resource record pertains. Name Name // Type is the type of DNS resource record. // // This field will be set automatically during packing. Type Type // Class is the class of network to which this DNS resource record // pertains. Class Class // TTL is the length of time (measured in seconds) which this resource // record is valid for (time to live). All Resources in a set should // have the same TTL (RFC 2181 Section 5.2). TTL uint32 // Length is the length of data in the resource record after the header. // // This field will be set automatically during packing. Length uint16 } // GoString implements fmt.GoStringer.GoString. func ( *ResourceHeader) () string { return "dnsmessage.ResourceHeader{" + "Name: " + .Name.GoString() + ", " + "Type: " + .Type.GoString() + ", " + "Class: " + .Class.GoString() + ", " + "TTL: " + printUint32(.TTL) + ", " + "Length: " + printUint16(.Length) + "}" } // pack appends the wire format of the ResourceHeader to oldMsg. // // lenOff is the offset in msg where the Length field was packed. func ( *ResourceHeader) ( []byte, map[string]uint16, int) ( []byte, int, error) { = if , = .Name.pack(, , ); != nil { return , 0, &nestedError{"Name", } } = packType(, .Type) = packClass(, .Class) = packUint32(, .TTL) = len() = packUint16(, .Length) return , , nil } func ( *ResourceHeader) ( []byte, int) (int, error) { := var error if , = .Name.unpack(, ); != nil { return , &nestedError{"Name", } } if .Type, , = unpackType(, ); != nil { return , &nestedError{"Type", } } if .Class, , = unpackClass(, ); != nil { return , &nestedError{"Class", } } if .TTL, , = unpackUint32(, ); != nil { return , &nestedError{"TTL", } } if .Length, , = unpackUint16(, ); != nil { return , &nestedError{"Length", } } return , nil } // fixLen updates a packed ResourceHeader to include the length of the // ResourceBody. // // lenOff is the offset of the ResourceHeader.Length field in msg. // // preLen is the length that msg was before the ResourceBody was packed. func ( *ResourceHeader) ( []byte, int, int) error { := len() - if > int(^uint16(0)) { return errResTooLong } // Fill in the length now that we know how long the content is. packUint16([:], uint16()) .Length = uint16() return nil } // EDNS(0) wire constants. const ( edns0Version = 0 edns0DNSSECOK = 0x00008000 ednsVersionMask = 0x00ff0000 edns0DNSSECOKMask = 0x00ff8000 ) // SetEDNS0 configures h for EDNS(0). // // The provided extRCode must be an extended RCode. func ( *ResourceHeader) ( int, RCode, bool) error { .Name = Name{Data: [255]byte{'.'}, Length: 1} // RFC 6891 section 6.1.2 .Type = TypeOPT .Class = Class() .TTL = uint32() >> 4 << 24 if { .TTL |= edns0DNSSECOK } return nil } // DNSSECAllowed reports whether the DNSSEC OK bit is set. func ( *ResourceHeader) () bool { return .TTL&edns0DNSSECOKMask == edns0DNSSECOK // RFC 6891 section 6.1.3 } // ExtendedRCode returns an extended RCode. // // The provided rcode must be the RCode in DNS message header. func ( *ResourceHeader) ( RCode) RCode { if .TTL&ednsVersionMask == edns0Version { // RFC 6891 section 6.1.3 return RCode(.TTL>>24<<4) | } return } func skipResource( []byte, int) (int, error) { , := skipName(, ) if != nil { return , &nestedError{"Name", } } if , = skipType(, ); != nil { return , &nestedError{"Type", } } if , = skipClass(, ); != nil { return , &nestedError{"Class", } } if , = skipUint32(, ); != nil { return , &nestedError{"TTL", } } , , := unpackUint16(, ) if != nil { return , &nestedError{"Length", } } if += int(); > len() { return , errResourceLen } return , nil } // packUint16 appends the wire format of field to msg. func packUint16( []byte, uint16) []byte { return append(, byte(>>8), byte()) } func unpackUint16( []byte, int) (uint16, int, error) { if +uint16Len > len() { return 0, , errBaseLen } return uint16([])<<8 | uint16([+1]), + uint16Len, nil } func skipUint16( []byte, int) (int, error) { if +uint16Len > len() { return , errBaseLen } return + uint16Len, nil } // packType appends the wire format of field to msg. func packType( []byte, Type) []byte { return packUint16(, uint16()) } func unpackType( []byte, int) (Type, int, error) { , , := unpackUint16(, ) return Type(), , } func skipType( []byte, int) (int, error) { return skipUint16(, ) } // packClass appends the wire format of field to msg. func packClass( []byte, Class) []byte { return packUint16(, uint16()) } func unpackClass( []byte, int) (Class, int, error) { , , := unpackUint16(, ) return Class(), , } func skipClass( []byte, int) (int, error) { return skipUint16(, ) } // packUint32 appends the wire format of field to msg. func packUint32( []byte, uint32) []byte { return append( , byte(>>24), byte(>>16), byte(>>8), byte(), ) } func unpackUint32( []byte, int) (uint32, int, error) { if +uint32Len > len() { return 0, , errBaseLen } := uint32([])<<24 | uint32([+1])<<16 | uint32([+2])<<8 | uint32([+3]) return , + uint32Len, nil } func skipUint32( []byte, int) (int, error) { if +uint32Len > len() { return , errBaseLen } return + uint32Len, nil } // packText appends the wire format of field to msg. func packText( []byte, string) ([]byte, error) { := len() if > 255 { return nil, errStringTooLong } = append(, byte()) = append(, ...) return , nil } func unpackText( []byte, int) (string, int, error) { if >= len() { return "", , errBaseLen } := + 1 := + int([]) if > len() { return "", , errCalcLen } return string([:]), , nil } // packBytes appends the wire format of field to msg. func packBytes( []byte, []byte) []byte { return append(, ...) } func unpackBytes( []byte, int, []byte) (int, error) { := + len() if > len() { return , errBaseLen } copy(, [:]) return , nil } const nonEncodedNameMax = 254 // A Name is a non-encoded and non-escaped domain name. It is used instead of strings to avoid // allocations. type Name struct { Data [255]byte Length uint8 } // NewName creates a new Name from a string. func ( string) (Name, error) { := Name{Length: uint8(len())} if len() > len(.Data) { return Name{}, errCalcLen } copy(.Data[:], ) return , nil } // MustNewName creates a new Name from a string and panics on error. func ( string) Name { , := NewName() if != nil { panic("creating name: " + .Error()) } return } // String implements fmt.Stringer.String. // // Note: characters inside the labels are not escaped in any way. func ( Name) () string { return string(.Data[:.Length]) } // GoString implements fmt.GoStringer.GoString. func ( *Name) () string { return `dnsmessage.MustNewName("` + printString(.Data[:.Length]) + `")` } // pack appends the wire format of the Name to msg. // // Domain names are a sequence of counted strings split at the dots. They end // with a zero-length string. Compression can be used to reuse domain suffixes. // // The compression map will be updated with new domain suffixes. If compression // is nil, compression will not be used. func ( *Name) ( []byte, map[string]uint16, int) ([]byte, error) { := if .Length > nonEncodedNameMax { return nil, errNameTooLong } // Add a trailing dot to canonicalize name. if .Length == 0 || .Data[.Length-1] != '.' { return , errNonCanonicalName } // Allow root domain. if .Data[0] == '.' && .Length == 1 { return append(, 0), nil } var string // Emit sequence of counted strings, chopping at dots. for , := 0, 0; < int(.Length); ++ { // Check for the end of the segment. if .Data[] == '.' { // The two most significant bits have special meaning. // It isn't allowed for segments to be long enough to // need them. if - >= 1<<6 { return , errSegTooLong } // Segments must have a non-zero length. if - == 0 { return , errZeroSegLen } = append(, byte(-)) for := ; < ; ++ { = append(, .Data[]) } = + 1 continue } // We can only compress domain suffixes starting with a new // segment. A pointer is two bytes with the two most significant // bits set to 1 to indicate that it is a pointer. if ( == 0 || .Data[-1] == '.') && != nil { if , := [string(.Data[:.Length])]; { // Hit. Emit a pointer instead of the rest of // the domain. return append(, byte(>>8|0xC0), byte()), nil } // Miss. Add the suffix to the compression table if the // offset can be stored in the available 14 bits. := len() - if <= int(^uint16(0)>>2) { if == "" { // allocate n.Data on the heap once, to avoid allocating it // multiple times (for next labels). = string(.Data[:.Length]) } [[:]] = uint16() } } } return append(, 0), nil } // unpack unpacks a domain name. func ( *Name) ( []byte, int) (int, error) { // currOff is the current working offset. := // newOff is the offset where the next record will start. Pointers lead // to data that belongs to other names and thus doesn't count towards to // the usage of this name. := // ptr is the number of pointers followed. var int // Name is a slice representation of the name data. := .Data[:0] : for { if >= len() { return , errBaseLen } := int([]) ++ switch & 0xC0 { case 0x00: // String segment if == 0x00 { // A zero length signals the end of the name. break } := + if > len() { return , errCalcLen } // Reject names containing dots. // See issue golang/go#56246 for , := range [:] { if == '.' { return , errInvalidName } } = append(, [:]...) = append(, '.') = case 0xC0: // Pointer if >= len() { return , errInvalidPtr } := [] ++ if == 0 { = } // Don't follow too many pointers, maybe there's a loop. if ++; > 10 { return , errTooManyPtr } = (^0xC0)<<8 | int() default: // Prefixes 0x80 and 0x40 are reserved. return , errReserved } } if len() == 0 { = append(, '.') } if len() > nonEncodedNameMax { return , errNameTooLong } .Length = uint8(len()) if == 0 { = } return , nil } func skipName( []byte, int) (int, error) { // newOff is the offset where the next record will start. Pointers lead // to data that belongs to other names and thus doesn't count towards to // the usage of this name. := : for { if >= len() { return , errBaseLen } := int([]) ++ switch & 0xC0 { case 0x00: if == 0x00 { // A zero length signals the end of the name. break } // literal string += if > len() { return , errCalcLen } case 0xC0: // Pointer to somewhere else in msg. // Pointers are two bytes. ++ // Don't follow the pointer as the data here has ended. break default: // Prefixes 0x80 and 0x40 are reserved. return , errReserved } } return , nil } // A Question is a DNS query. type Question struct { Name Name Type Type Class Class } // pack appends the wire format of the Question to msg. func ( *Question) ( []byte, map[string]uint16, int) ([]byte, error) { , := .Name.pack(, , ) if != nil { return , &nestedError{"Name", } } = packType(, .Type) return packClass(, .Class), nil } // GoString implements fmt.GoStringer.GoString. func ( *Question) () string { return "dnsmessage.Question{" + "Name: " + .Name.GoString() + ", " + "Type: " + .Type.GoString() + ", " + "Class: " + .Class.GoString() + "}" } func unpackResourceBody( []byte, int, ResourceHeader) (ResourceBody, int, error) { var ( ResourceBody error string ) switch .Type { case TypeA: var AResource , = unpackAResource(, ) = & = "A" case TypeNS: var NSResource , = unpackNSResource(, ) = & = "NS" case TypeCNAME: var CNAMEResource , = unpackCNAMEResource(, ) = & = "CNAME" case TypeSOA: var SOAResource , = unpackSOAResource(, ) = & = "SOA" case TypePTR: var PTRResource , = unpackPTRResource(, ) = & = "PTR" case TypeMX: var MXResource , = unpackMXResource(, ) = & = "MX" case TypeTXT: var TXTResource , = unpackTXTResource(, , .Length) = & = "TXT" case TypeAAAA: var AAAAResource , = unpackAAAAResource(, ) = & = "AAAA" case TypeSRV: var SRVResource , = unpackSRVResource(, ) = & = "SRV" case TypeOPT: var OPTResource , = unpackOPTResource(, , .Length) = & = "OPT" default: var UnknownResource , = unpackUnknownResource(.Type, , , .Length) = & = "Unknown" } if != nil { return nil, , &nestedError{ + " record", } } return , + int(.Length), nil } // A CNAMEResource is a CNAME Resource record. type CNAMEResource struct { CNAME Name } func ( *CNAMEResource) () Type { return TypeCNAME } // pack appends the wire format of the CNAMEResource to msg. func ( *CNAMEResource) ( []byte, map[string]uint16, int) ([]byte, error) { return .CNAME.pack(, , ) } // GoString implements fmt.GoStringer.GoString. func ( *CNAMEResource) () string { return "dnsmessage.CNAMEResource{CNAME: " + .CNAME.GoString() + "}" } func unpackCNAMEResource( []byte, int) (CNAMEResource, error) { var Name if , := .unpack(, ); != nil { return CNAMEResource{}, } return CNAMEResource{}, nil } // An MXResource is an MX Resource record. type MXResource struct { Pref uint16 MX Name } func ( *MXResource) () Type { return TypeMX } // pack appends the wire format of the MXResource to msg. func ( *MXResource) ( []byte, map[string]uint16, int) ([]byte, error) { := = packUint16(, .Pref) , := .MX.pack(, , ) if != nil { return , &nestedError{"MXResource.MX", } } return , nil } // GoString implements fmt.GoStringer.GoString. func ( *MXResource) () string { return "dnsmessage.MXResource{" + "Pref: " + printUint16(.Pref) + ", " + "MX: " + .MX.GoString() + "}" } func unpackMXResource( []byte, int) (MXResource, error) { , , := unpackUint16(, ) if != nil { return MXResource{}, &nestedError{"Pref", } } var Name if , := .unpack(, ); != nil { return MXResource{}, &nestedError{"MX", } } return MXResource{, }, nil } // An NSResource is an NS Resource record. type NSResource struct { NS Name } func ( *NSResource) () Type { return TypeNS } // pack appends the wire format of the NSResource to msg. func ( *NSResource) ( []byte, map[string]uint16, int) ([]byte, error) { return .NS.pack(, , ) } // GoString implements fmt.GoStringer.GoString. func ( *NSResource) () string { return "dnsmessage.NSResource{NS: " + .NS.GoString() + "}" } func unpackNSResource( []byte, int) (NSResource, error) { var Name if , := .unpack(, ); != nil { return NSResource{}, } return NSResource{}, nil } // A PTRResource is a PTR Resource record. type PTRResource struct { PTR Name } func ( *PTRResource) () Type { return TypePTR } // pack appends the wire format of the PTRResource to msg. func ( *PTRResource) ( []byte, map[string]uint16, int) ([]byte, error) { return .PTR.pack(, , ) } // GoString implements fmt.GoStringer.GoString. func ( *PTRResource) () string { return "dnsmessage.PTRResource{PTR: " + .PTR.GoString() + "}" } func unpackPTRResource( []byte, int) (PTRResource, error) { var Name if , := .unpack(, ); != nil { return PTRResource{}, } return PTRResource{}, nil } // An SOAResource is an SOA Resource record. type SOAResource struct { NS Name MBox Name Serial uint32 Refresh uint32 Retry uint32 Expire uint32 // MinTTL the is the default TTL of Resources records which did not // contain a TTL value and the TTL of negative responses. (RFC 2308 // Section 4) MinTTL uint32 } func ( *SOAResource) () Type { return TypeSOA } // pack appends the wire format of the SOAResource to msg. func ( *SOAResource) ( []byte, map[string]uint16, int) ([]byte, error) { := , := .NS.pack(, , ) if != nil { return , &nestedError{"SOAResource.NS", } } , = .MBox.pack(, , ) if != nil { return , &nestedError{"SOAResource.MBox", } } = packUint32(, .Serial) = packUint32(, .Refresh) = packUint32(, .Retry) = packUint32(, .Expire) return packUint32(, .MinTTL), nil } // GoString implements fmt.GoStringer.GoString. func ( *SOAResource) () string { return "dnsmessage.SOAResource{" + "NS: " + .NS.GoString() + ", " + "MBox: " + .MBox.GoString() + ", " + "Serial: " + printUint32(.Serial) + ", " + "Refresh: " + printUint32(.Refresh) + ", " + "Retry: " + printUint32(.Retry) + ", " + "Expire: " + printUint32(.Expire) + ", " + "MinTTL: " + printUint32(.MinTTL) + "}" } func unpackSOAResource( []byte, int) (SOAResource, error) { var Name , := .unpack(, ) if != nil { return SOAResource{}, &nestedError{"NS", } } var Name if , = .unpack(, ); != nil { return SOAResource{}, &nestedError{"MBox", } } , , := unpackUint32(, ) if != nil { return SOAResource{}, &nestedError{"Serial", } } , , := unpackUint32(, ) if != nil { return SOAResource{}, &nestedError{"Refresh", } } , , := unpackUint32(, ) if != nil { return SOAResource{}, &nestedError{"Retry", } } , , := unpackUint32(, ) if != nil { return SOAResource{}, &nestedError{"Expire", } } , , := unpackUint32(, ) if != nil { return SOAResource{}, &nestedError{"MinTTL", } } return SOAResource{, , , , , , }, nil } // A TXTResource is a TXT Resource record. type TXTResource struct { TXT []string } func ( *TXTResource) () Type { return TypeTXT } // pack appends the wire format of the TXTResource to msg. func ( *TXTResource) ( []byte, map[string]uint16, int) ([]byte, error) { := for , := range .TXT { var error , = packText(, ) if != nil { return , } } return , nil } // GoString implements fmt.GoStringer.GoString. func ( *TXTResource) () string { := "dnsmessage.TXTResource{TXT: []string{" if len(.TXT) == 0 { return + "}}" } += `"` + printString([]byte(.TXT[0])) for , := range .TXT[1:] { += `", "` + printString([]byte()) } return + `"}}` } func unpackTXTResource( []byte, int, uint16) (TXTResource, error) { := make([]string, 0, 1) for := uint16(0); < ; { var string var error if , , = unpackText(, ); != nil { return TXTResource{}, &nestedError{"text", } } // Check if we got too many bytes. if - < uint16(len())+1 { return TXTResource{}, errCalcLen } += uint16(len()) + 1 = append(, ) } return TXTResource{}, nil } // An SRVResource is an SRV Resource record. type SRVResource struct { Priority uint16 Weight uint16 Port uint16 Target Name // Not compressed as per RFC 2782. } func ( *SRVResource) () Type { return TypeSRV } // pack appends the wire format of the SRVResource to msg. func ( *SRVResource) ( []byte, map[string]uint16, int) ([]byte, error) { := = packUint16(, .Priority) = packUint16(, .Weight) = packUint16(, .Port) , := .Target.pack(, nil, ) if != nil { return , &nestedError{"SRVResource.Target", } } return , nil } // GoString implements fmt.GoStringer.GoString. func ( *SRVResource) () string { return "dnsmessage.SRVResource{" + "Priority: " + printUint16(.Priority) + ", " + "Weight: " + printUint16(.Weight) + ", " + "Port: " + printUint16(.Port) + ", " + "Target: " + .Target.GoString() + "}" } func unpackSRVResource( []byte, int) (SRVResource, error) { , , := unpackUint16(, ) if != nil { return SRVResource{}, &nestedError{"Priority", } } , , := unpackUint16(, ) if != nil { return SRVResource{}, &nestedError{"Weight", } } , , := unpackUint16(, ) if != nil { return SRVResource{}, &nestedError{"Port", } } var Name if , := .unpack(, ); != nil { return SRVResource{}, &nestedError{"Target", } } return SRVResource{, , , }, nil } // An AResource is an A Resource record. type AResource struct { A [4]byte } func ( *AResource) () Type { return TypeA } // pack appends the wire format of the AResource to msg. func ( *AResource) ( []byte, map[string]uint16, int) ([]byte, error) { return packBytes(, .A[:]), nil } // GoString implements fmt.GoStringer.GoString. func ( *AResource) () string { return "dnsmessage.AResource{" + "A: [4]byte{" + printByteSlice(.A[:]) + "}}" } func unpackAResource( []byte, int) (AResource, error) { var [4]byte if , := unpackBytes(, , [:]); != nil { return AResource{}, } return AResource{}, nil } // An AAAAResource is an AAAA Resource record. type AAAAResource struct { AAAA [16]byte } func ( *AAAAResource) () Type { return TypeAAAA } // GoString implements fmt.GoStringer.GoString. func ( *AAAAResource) () string { return "dnsmessage.AAAAResource{" + "AAAA: [16]byte{" + printByteSlice(.AAAA[:]) + "}}" } // pack appends the wire format of the AAAAResource to msg. func ( *AAAAResource) ( []byte, map[string]uint16, int) ([]byte, error) { return packBytes(, .AAAA[:]), nil } func unpackAAAAResource( []byte, int) (AAAAResource, error) { var [16]byte if , := unpackBytes(, , [:]); != nil { return AAAAResource{}, } return AAAAResource{}, nil } // An OPTResource is an OPT pseudo Resource record. // // The pseudo resource record is part of the extension mechanisms for DNS // as defined in RFC 6891. type OPTResource struct { Options []Option } // An Option represents a DNS message option within OPTResource. // // The message option is part of the extension mechanisms for DNS as // defined in RFC 6891. type Option struct { Code uint16 // option code Data []byte } // GoString implements fmt.GoStringer.GoString. func ( *Option) () string { return "dnsmessage.Option{" + "Code: " + printUint16(.Code) + ", " + "Data: []byte{" + printByteSlice(.Data) + "}}" } func ( *OPTResource) () Type { return TypeOPT } func ( *OPTResource) ( []byte, map[string]uint16, int) ([]byte, error) { for , := range .Options { = packUint16(, .Code) := uint16(len(.Data)) = packUint16(, ) = packBytes(, .Data) } return , nil } // GoString implements fmt.GoStringer.GoString. func ( *OPTResource) () string { := "dnsmessage.OPTResource{Options: []dnsmessage.Option{" if len(.Options) == 0 { return + "}}" } += .Options[0].GoString() for , := range .Options[1:] { += ", " + .GoString() } return + "}}" } func unpackOPTResource( []byte, int, uint16) (OPTResource, error) { var []Option for := ; < +int(); { var error var Option .Code, , = unpackUint16(, ) if != nil { return OPTResource{}, &nestedError{"Code", } } var uint16 , , = unpackUint16(, ) if != nil { return OPTResource{}, &nestedError{"Data", } } .Data = make([]byte, ) if copy(.Data, [:]) != int() { return OPTResource{}, &nestedError{"Data", errCalcLen} } += int() = append(, ) } return OPTResource{}, nil } // An UnknownResource is a catch-all container for unknown record types. type UnknownResource struct { Type Type Data []byte } func ( *UnknownResource) () Type { return .Type } // pack appends the wire format of the UnknownResource to msg. func ( *UnknownResource) ( []byte, map[string]uint16, int) ([]byte, error) { return packBytes(, .Data[:]), nil } // GoString implements fmt.GoStringer.GoString. func ( *UnknownResource) () string { return "dnsmessage.UnknownResource{" + "Type: " + .Type.GoString() + ", " + "Data: []byte{" + printByteSlice(.Data) + "}}" } func unpackUnknownResource( Type, []byte, int, uint16) (UnknownResource, error) { := UnknownResource{ Type: , Data: make([]byte, ), } if , := unpackBytes(, , .Data); != nil { return UnknownResource{}, } return , nil }