package dnsmessage
import (
"errors"
)
type Type uint16
const (
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
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" ,
}
func (t Type ) String () string {
if n , ok := typeNames [t ]; ok {
return n
}
return printUint16 (uint16 (t ))
}
func (t Type ) GoString () string {
if n , ok := typeNames [t ]; ok {
return "dnsmessage." + n
}
return printUint16 (uint16 (t ))
}
type Class uint16
const (
ClassINET Class = 1
ClassCSNET Class = 2
ClassCHAOS Class = 3
ClassHESIOD Class = 4
ClassANY Class = 255
)
var classNames = map [Class ]string {
ClassINET : "ClassINET" ,
ClassCSNET : "ClassCSNET" ,
ClassCHAOS : "ClassCHAOS" ,
ClassHESIOD : "ClassHESIOD" ,
ClassANY : "ClassANY" ,
}
func (c Class ) String () string {
if n , ok := classNames [c ]; ok {
return n
}
return printUint16 (uint16 (c ))
}
func (c Class ) GoString () string {
if n , ok := classNames [c ]; ok {
return "dnsmessage." + n
}
return printUint16 (uint16 (c ))
}
type OpCode uint16
func (o OpCode ) GoString () string {
return printUint16 (uint16 (o ))
}
type RCode uint16
const (
RCodeSuccess RCode = 0
RCodeFormatError RCode = 1
RCodeServerFailure RCode = 2
RCodeNameError RCode = 3
RCodeNotImplemented RCode = 4
RCodeRefused RCode = 5
)
var rCodeNames = map [RCode ]string {
RCodeSuccess : "RCodeSuccess" ,
RCodeFormatError : "RCodeFormatError" ,
RCodeServerFailure : "RCodeServerFailure" ,
RCodeNameError : "RCodeNameError" ,
RCodeNotImplemented : "RCodeNotImplemented" ,
RCodeRefused : "RCodeRefused" ,
}
func (r RCode ) String () string {
if n , ok := rCodeNames [r ]; ok {
return n
}
return printUint16 (uint16 (r ))
}
func (r RCode ) GoString () string {
if n , ok := rCodeNames [r ]; ok {
return "dnsmessage." + n
}
return printUint16 (uint16 (r ))
}
func printPaddedUint8(i uint8 ) string {
b := byte (i )
return string ([]byte {
b /100 + '0' ,
b /10 %10 + '0' ,
b %10 + '0' ,
})
}
func printUint8Bytes(buf []byte , i uint8 ) []byte {
b := byte (i )
if i >= 100 {
buf = append (buf , b /100 +'0' )
}
if i >= 10 {
buf = append (buf , b /10 %10 +'0' )
}
return append (buf , b %10 +'0' )
}
func printByteSlice(b []byte ) string {
if len (b ) == 0 {
return ""
}
buf := make ([]byte , 0 , 5 *len (b ))
buf = printUint8Bytes (buf , uint8 (b [0 ]))
for _ , n := range b [1 :] {
buf = append (buf , ',' , ' ' )
buf = printUint8Bytes (buf , uint8 (n ))
}
return string (buf )
}
const hexDigits = "0123456789abcdef"
func printString(str []byte ) string {
buf := make ([]byte , 0 , len (str ))
for i := 0 ; i < len (str ); i ++ {
c := str [i ]
if c == '.' || c == '-' || c == ' ' ||
'A' <= c && c <= 'Z' ||
'a' <= c && c <= 'z' ||
'0' <= c && c <= '9' {
buf = append (buf , c )
continue
}
upper := c >> 4
lower := (c << 4 ) >> 4
buf = append (
buf ,
'\\' ,
'x' ,
hexDigits [upper ],
hexDigits [lower ],
)
}
return string (buf )
}
func printUint16(i uint16 ) string {
return printUint32 (uint32 (i ))
}
func printUint32(i uint32 ) string {
buf := make ([]byte , 10 )
for b , d := buf , uint32 (1000000000 ); d > 0 ; d /= 10 {
b [0 ] = byte (i /d %10 + '0' )
if b [0 ] == '0' && len (b ) == len (buf ) && len (buf ) > 1 {
buf = buf [1 :]
}
b = b [1 :]
i %= d
}
return string (buf )
}
func printBool(b bool ) string {
if b {
return "true"
}
return "false"
}
var (
ErrNotStarted = errors .New ("parsing/packing of this type isn't available yet" )
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)" )
)
const (
packStartingCap = 512
uint16Len = 2
uint32Len = 4
headerLen = 6 * uint16Len
)
type nestedError struct {
s string
err error
}
func (e *nestedError ) Error () string {
return e .s + ": " + e .err .Error()
}
type Header struct {
ID uint16
Response bool
OpCode OpCode
Authoritative bool
Truncated bool
RecursionDesired bool
RecursionAvailable bool
AuthenticData bool
CheckingDisabled bool
RCode RCode
}
func (m *Header ) pack () (id uint16 , bits uint16 ) {
id = m .ID
bits = uint16 (m .OpCode )<<11 | uint16 (m .RCode )
if m .RecursionAvailable {
bits |= headerBitRA
}
if m .RecursionDesired {
bits |= headerBitRD
}
if m .Truncated {
bits |= headerBitTC
}
if m .Authoritative {
bits |= headerBitAA
}
if m .Response {
bits |= headerBitQR
}
if m .AuthenticData {
bits |= headerBitAD
}
if m .CheckingDisabled {
bits |= headerBitCD
}
return
}
func (m *Header ) GoString () string {
return "dnsmessage.Header{" +
"ID: " + printUint16 (m .ID ) + ", " +
"Response: " + printBool (m .Response ) + ", " +
"OpCode: " + m .OpCode .GoString () + ", " +
"Authoritative: " + printBool (m .Authoritative ) + ", " +
"Truncated: " + printBool (m .Truncated ) + ", " +
"RecursionDesired: " + printBool (m .RecursionDesired ) + ", " +
"RecursionAvailable: " + printBool (m .RecursionAvailable ) + ", " +
"AuthenticData: " + printBool (m .AuthenticData ) + ", " +
"CheckingDisabled: " + printBool (m .CheckingDisabled ) + ", " +
"RCode: " + m .RCode .GoString () + "}"
}
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
headerBitAA = 1 << 10
headerBitTC = 1 << 9
headerBitRD = 1 << 8
headerBitRA = 1 << 7
headerBitAD = 1 << 5
headerBitCD = 1 << 4
)
var sectionNames = map [section ]string {
sectionHeader : "header" ,
sectionQuestions : "Question" ,
sectionAnswers : "Answer" ,
sectionAuthorities : "Authority" ,
sectionAdditionals : "Additional" ,
}
type header struct {
id uint16
bits uint16
questions uint16
answers uint16
authorities uint16
additionals uint16
}
func (h *header ) count (sec section ) uint16 {
switch sec {
case sectionQuestions :
return h .questions
case sectionAnswers :
return h .answers
case sectionAuthorities :
return h .authorities
case sectionAdditionals :
return h .additionals
}
return 0
}
func (h *header ) pack (msg []byte ) []byte {
msg = packUint16 (msg , h .id )
msg = packUint16 (msg , h .bits )
msg = packUint16 (msg , h .questions )
msg = packUint16 (msg , h .answers )
msg = packUint16 (msg , h .authorities )
return packUint16 (msg , h .additionals )
}
func (h *header ) unpack (msg []byte , off int ) (int , error ) {
newOff := off
var err error
if h .id , newOff , err = unpackUint16 (msg , newOff ); err != nil {
return off , &nestedError {"id" , err }
}
if h .bits , newOff , err = unpackUint16 (msg , newOff ); err != nil {
return off , &nestedError {"bits" , err }
}
if h .questions , newOff , err = unpackUint16 (msg , newOff ); err != nil {
return off , &nestedError {"questions" , err }
}
if h .answers , newOff , err = unpackUint16 (msg , newOff ); err != nil {
return off , &nestedError {"answers" , err }
}
if h .authorities , newOff , err = unpackUint16 (msg , newOff ); err != nil {
return off , &nestedError {"authorities" , err }
}
if h .additionals , newOff , err = unpackUint16 (msg , newOff ); err != nil {
return off , &nestedError {"additionals" , err }
}
return newOff , nil
}
func (h *header ) header () Header {
return Header {
ID : h .id ,
Response : (h .bits & headerBitQR ) != 0 ,
OpCode : OpCode (h .bits >>11 ) & 0xF ,
Authoritative : (h .bits & headerBitAA ) != 0 ,
Truncated : (h .bits & headerBitTC ) != 0 ,
RecursionDesired : (h .bits & headerBitRD ) != 0 ,
RecursionAvailable : (h .bits & headerBitRA ) != 0 ,
AuthenticData : (h .bits & headerBitAD ) != 0 ,
CheckingDisabled : (h .bits & headerBitCD ) != 0 ,
RCode : RCode (h .bits & 0xF ),
}
}
type Resource struct {
Header ResourceHeader
Body ResourceBody
}
func (r *Resource ) GoString () string {
return "dnsmessage.Resource{" +
"Header: " + r .Header .GoString () +
", Body: &" + r .Body .GoString () +
"}"
}
type ResourceBody interface {
pack(msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error )
realType() Type
GoString () string
}
func (r *Resource ) pack (msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error ) {
if r .Body == nil {
return msg , errNilResouceBody
}
oldMsg := msg
r .Header .Type = r .Body .realType ()
msg , lenOff , err := r .Header .pack (msg , compression , compressionOff )
if err != nil {
return msg , &nestedError {"ResourceHeader" , err }
}
preLen := len (msg )
msg , err = r .Body .pack (msg , compression , compressionOff )
if err != nil {
return msg , &nestedError {"content" , err }
}
if err := r .Header .fixLen (msg , lenOff , preLen ); err != nil {
return oldMsg , err
}
return msg , nil
}
type Parser struct {
msg []byte
header header
section section
off int
index int
resHeaderValid bool
resHeaderOffset int
resHeaderType Type
resHeaderLength uint16
}
func (p *Parser ) Start (msg []byte ) (Header , error ) {
if p .msg != nil {
*p = Parser {}
}
p .msg = msg
var err error
if p .off , err = p .header .unpack (msg , 0 ); err != nil {
return Header {}, &nestedError {"unpacking header" , err }
}
p .section = sectionQuestions
return p .header .header (), nil
}
func (p *Parser ) checkAdvance (sec section ) error {
if p .section < sec {
return ErrNotStarted
}
if p .section > sec {
return ErrSectionDone
}
p .resHeaderValid = false
if p .index == int (p .header .count (sec )) {
p .index = 0
p .section ++
return ErrSectionDone
}
return nil
}
func (p *Parser ) resource (sec section ) (Resource , error ) {
var r Resource
var err error
r .Header , err = p .resourceHeader (sec )
if err != nil {
return r , err
}
p .resHeaderValid = false
r .Body , p .off , err = unpackResourceBody (p .msg , p .off , r .Header )
if err != nil {
return Resource {}, &nestedError {"unpacking " + sectionNames [sec ], err }
}
p .index ++
return r , nil
}
func (p *Parser ) resourceHeader (sec section ) (ResourceHeader , error ) {
if p .resHeaderValid {
p .off = p .resHeaderOffset
}
if err := p .checkAdvance (sec ); err != nil {
return ResourceHeader {}, err
}
var hdr ResourceHeader
off , err := hdr .unpack (p .msg , p .off )
if err != nil {
return ResourceHeader {}, err
}
p .resHeaderValid = true
p .resHeaderOffset = p .off
p .resHeaderType = hdr .Type
p .resHeaderLength = hdr .Length
p .off = off
return hdr , nil
}
func (p *Parser ) skipResource (sec section ) error {
if p .resHeaderValid && p .section == sec {
newOff := p .off + int (p .resHeaderLength )
if newOff > len (p .msg ) {
return errResourceLen
}
p .off = newOff
p .resHeaderValid = false
p .index ++
return nil
}
if err := p .checkAdvance (sec ); err != nil {
return err
}
var err error
p .off , err = skipResource (p .msg , p .off )
if err != nil {
return &nestedError {"skipping: " + sectionNames [sec ], err }
}
p .index ++
return nil
}
func (p *Parser ) Question () (Question , error ) {
if err := p .checkAdvance (sectionQuestions ); err != nil {
return Question {}, err
}
var name Name
off , err := name .unpack (p .msg , p .off )
if err != nil {
return Question {}, &nestedError {"unpacking Question.Name" , err }
}
typ , off , err := unpackType (p .msg , off )
if err != nil {
return Question {}, &nestedError {"unpacking Question.Type" , err }
}
class , off , err := unpackClass (p .msg , off )
if err != nil {
return Question {}, &nestedError {"unpacking Question.Class" , err }
}
p .off = off
p .index ++
return Question {name , typ , class }, nil
}
func (p *Parser ) AllQuestions () ([]Question , error ) {
qs := []Question {}
for {
q , err := p .Question ()
if err == ErrSectionDone {
return qs , nil
}
if err != nil {
return nil , err
}
qs = append (qs , q )
}
}
func (p *Parser ) SkipQuestion () error {
if err := p .checkAdvance (sectionQuestions ); err != nil {
return err
}
off , err := skipName (p .msg , p .off )
if err != nil {
return &nestedError {"skipping Question Name" , err }
}
if off , err = skipType (p .msg , off ); err != nil {
return &nestedError {"skipping Question Type" , err }
}
if off , err = skipClass (p .msg , off ); err != nil {
return &nestedError {"skipping Question Class" , err }
}
p .off = off
p .index ++
return nil
}
func (p *Parser ) SkipAllQuestions () error {
for {
if err := p .SkipQuestion (); err == ErrSectionDone {
return nil
} else if err != nil {
return err
}
}
}
func (p *Parser ) AnswerHeader () (ResourceHeader , error ) {
return p .resourceHeader (sectionAnswers )
}
func (p *Parser ) Answer () (Resource , error ) {
return p .resource (sectionAnswers )
}
func (p *Parser ) AllAnswers () ([]Resource , error ) {
n := int (p .header .answers )
if n > 20 {
n = 20
}
as := make ([]Resource , 0 , n )
for {
a , err := p .Answer ()
if err == ErrSectionDone {
return as , nil
}
if err != nil {
return nil , err
}
as = append (as , a )
}
}
func (p *Parser ) SkipAnswer () error {
return p .skipResource (sectionAnswers )
}
func (p *Parser ) SkipAllAnswers () error {
for {
if err := p .SkipAnswer (); err == ErrSectionDone {
return nil
} else if err != nil {
return err
}
}
}
func (p *Parser ) AuthorityHeader () (ResourceHeader , error ) {
return p .resourceHeader (sectionAuthorities )
}
func (p *Parser ) Authority () (Resource , error ) {
return p .resource (sectionAuthorities )
}
func (p *Parser ) AllAuthorities () ([]Resource , error ) {
n := int (p .header .authorities )
if n > 10 {
n = 10
}
as := make ([]Resource , 0 , n )
for {
a , err := p .Authority ()
if err == ErrSectionDone {
return as , nil
}
if err != nil {
return nil , err
}
as = append (as , a )
}
}
func (p *Parser ) SkipAuthority () error {
return p .skipResource (sectionAuthorities )
}
func (p *Parser ) SkipAllAuthorities () error {
for {
if err := p .SkipAuthority (); err == ErrSectionDone {
return nil
} else if err != nil {
return err
}
}
}
func (p *Parser ) AdditionalHeader () (ResourceHeader , error ) {
return p .resourceHeader (sectionAdditionals )
}
func (p *Parser ) Additional () (Resource , error ) {
return p .resource (sectionAdditionals )
}
func (p *Parser ) AllAdditionals () ([]Resource , error ) {
n := int (p .header .additionals )
if n > 10 {
n = 10
}
as := make ([]Resource , 0 , n )
for {
a , err := p .Additional ()
if err == ErrSectionDone {
return as , nil
}
if err != nil {
return nil , err
}
as = append (as , a )
}
}
func (p *Parser ) SkipAdditional () error {
return p .skipResource (sectionAdditionals )
}
func (p *Parser ) SkipAllAdditionals () error {
for {
if err := p .SkipAdditional (); err == ErrSectionDone {
return nil
} else if err != nil {
return err
}
}
}
func (p *Parser ) CNAMEResource () (CNAMEResource , error ) {
if !p .resHeaderValid || p .resHeaderType != TypeCNAME {
return CNAMEResource {}, ErrNotStarted
}
r , err := unpackCNAMEResource (p .msg , p .off )
if err != nil {
return CNAMEResource {}, err
}
p .off += int (p .resHeaderLength )
p .resHeaderValid = false
p .index ++
return r , nil
}
func (p *Parser ) MXResource () (MXResource , error ) {
if !p .resHeaderValid || p .resHeaderType != TypeMX {
return MXResource {}, ErrNotStarted
}
r , err := unpackMXResource (p .msg , p .off )
if err != nil {
return MXResource {}, err
}
p .off += int (p .resHeaderLength )
p .resHeaderValid = false
p .index ++
return r , nil
}
func (p *Parser ) NSResource () (NSResource , error ) {
if !p .resHeaderValid || p .resHeaderType != TypeNS {
return NSResource {}, ErrNotStarted
}
r , err := unpackNSResource (p .msg , p .off )
if err != nil {
return NSResource {}, err
}
p .off += int (p .resHeaderLength )
p .resHeaderValid = false
p .index ++
return r , nil
}
func (p *Parser ) PTRResource () (PTRResource , error ) {
if !p .resHeaderValid || p .resHeaderType != TypePTR {
return PTRResource {}, ErrNotStarted
}
r , err := unpackPTRResource (p .msg , p .off )
if err != nil {
return PTRResource {}, err
}
p .off += int (p .resHeaderLength )
p .resHeaderValid = false
p .index ++
return r , nil
}
func (p *Parser ) SOAResource () (SOAResource , error ) {
if !p .resHeaderValid || p .resHeaderType != TypeSOA {
return SOAResource {}, ErrNotStarted
}
r , err := unpackSOAResource (p .msg , p .off )
if err != nil {
return SOAResource {}, err
}
p .off += int (p .resHeaderLength )
p .resHeaderValid = false
p .index ++
return r , nil
}
func (p *Parser ) TXTResource () (TXTResource , error ) {
if !p .resHeaderValid || p .resHeaderType != TypeTXT {
return TXTResource {}, ErrNotStarted
}
r , err := unpackTXTResource (p .msg , p .off , p .resHeaderLength )
if err != nil {
return TXTResource {}, err
}
p .off += int (p .resHeaderLength )
p .resHeaderValid = false
p .index ++
return r , nil
}
func (p *Parser ) SRVResource () (SRVResource , error ) {
if !p .resHeaderValid || p .resHeaderType != TypeSRV {
return SRVResource {}, ErrNotStarted
}
r , err := unpackSRVResource (p .msg , p .off )
if err != nil {
return SRVResource {}, err
}
p .off += int (p .resHeaderLength )
p .resHeaderValid = false
p .index ++
return r , nil
}
func (p *Parser ) AResource () (AResource , error ) {
if !p .resHeaderValid || p .resHeaderType != TypeA {
return AResource {}, ErrNotStarted
}
r , err := unpackAResource (p .msg , p .off )
if err != nil {
return AResource {}, err
}
p .off += int (p .resHeaderLength )
p .resHeaderValid = false
p .index ++
return r , nil
}
func (p *Parser ) AAAAResource () (AAAAResource , error ) {
if !p .resHeaderValid || p .resHeaderType != TypeAAAA {
return AAAAResource {}, ErrNotStarted
}
r , err := unpackAAAAResource (p .msg , p .off )
if err != nil {
return AAAAResource {}, err
}
p .off += int (p .resHeaderLength )
p .resHeaderValid = false
p .index ++
return r , nil
}
func (p *Parser ) OPTResource () (OPTResource , error ) {
if !p .resHeaderValid || p .resHeaderType != TypeOPT {
return OPTResource {}, ErrNotStarted
}
r , err := unpackOPTResource (p .msg , p .off , p .resHeaderLength )
if err != nil {
return OPTResource {}, err
}
p .off += int (p .resHeaderLength )
p .resHeaderValid = false
p .index ++
return r , nil
}
func (p *Parser ) UnknownResource () (UnknownResource , error ) {
if !p .resHeaderValid {
return UnknownResource {}, ErrNotStarted
}
r , err := unpackUnknownResource (p .resHeaderType , p .msg , p .off , p .resHeaderLength )
if err != nil {
return UnknownResource {}, err
}
p .off += int (p .resHeaderLength )
p .resHeaderValid = false
p .index ++
return r , nil
}
func (m *Message ) Unpack (msg []byte ) error {
var p Parser
var err error
if m .Header , err = p .Start (msg ); err != nil {
return err
}
if m .Questions , err = p .AllQuestions (); err != nil {
return err
}
if m .Answers , err = p .AllAnswers (); err != nil {
return err
}
if m .Authorities , err = p .AllAuthorities (); err != nil {
return err
}
if m .Additionals , err = p .AllAdditionals (); err != nil {
return err
}
return nil
}
func (m *Message ) Pack () ([]byte , error ) {
return m .AppendPack (make ([]byte , 0 , packStartingCap ))
}
func (m *Message ) AppendPack (b []byte ) ([]byte , error ) {
if len (m .Questions ) > int (^uint16 (0 )) {
return nil , errTooManyQuestions
}
if len (m .Answers ) > int (^uint16 (0 )) {
return nil , errTooManyAnswers
}
if len (m .Authorities ) > int (^uint16 (0 )) {
return nil , errTooManyAuthorities
}
if len (m .Additionals ) > int (^uint16 (0 )) {
return nil , errTooManyAdditionals
}
var h header
h .id , h .bits = m .Header .pack ()
h .questions = uint16 (len (m .Questions ))
h .answers = uint16 (len (m .Answers ))
h .authorities = uint16 (len (m .Authorities ))
h .additionals = uint16 (len (m .Additionals ))
compressionOff := len (b )
msg := h .pack (b )
compression := map [string ]uint16 {}
for i := range m .Questions {
var err error
if msg , err = m .Questions [i ].pack (msg , compression , compressionOff ); err != nil {
return nil , &nestedError {"packing Question" , err }
}
}
for i := range m .Answers {
var err error
if msg , err = m .Answers [i ].pack (msg , compression , compressionOff ); err != nil {
return nil , &nestedError {"packing Answer" , err }
}
}
for i := range m .Authorities {
var err error
if msg , err = m .Authorities [i ].pack (msg , compression , compressionOff ); err != nil {
return nil , &nestedError {"packing Authority" , err }
}
}
for i := range m .Additionals {
var err error
if msg , err = m .Additionals [i ].pack (msg , compression , compressionOff ); err != nil {
return nil , &nestedError {"packing Additional" , err }
}
}
return msg , nil
}
func (m *Message ) GoString () string {
s := "dnsmessage.Message{Header: " + m .Header .GoString () + ", " +
"Questions: []dnsmessage.Question{"
if len (m .Questions ) > 0 {
s += m .Questions [0 ].GoString ()
for _ , q := range m .Questions [1 :] {
s += ", " + q .GoString ()
}
}
s += "}, Answers: []dnsmessage.Resource{"
if len (m .Answers ) > 0 {
s += m .Answers [0 ].GoString ()
for _ , a := range m .Answers [1 :] {
s += ", " + a .GoString ()
}
}
s += "}, Authorities: []dnsmessage.Resource{"
if len (m .Authorities ) > 0 {
s += m .Authorities [0 ].GoString ()
for _ , a := range m .Authorities [1 :] {
s += ", " + a .GoString ()
}
}
s += "}, Additionals: []dnsmessage.Resource{"
if len (m .Additionals ) > 0 {
s += m .Additionals [0 ].GoString ()
for _ , a := range m .Additionals [1 :] {
s += ", " + a .GoString ()
}
}
return s + "}}"
}
type Builder struct {
msg []byte
section section
header header
start int
compression map [string ]uint16
}
func NewBuilder (buf []byte , h Header ) Builder {
if buf == nil {
buf = make ([]byte , 0 , packStartingCap )
}
b := Builder {msg : buf , start : len (buf )}
b .header .id , b .header .bits = h .pack ()
var hb [headerLen ]byte
b .msg = append (b .msg , hb [:]...)
b .section = sectionHeader
return b
}
func (b *Builder ) EnableCompression () {
b .compression = map [string ]uint16 {}
}
func (b *Builder ) startCheck (s section ) error {
if b .section <= sectionNotStarted {
return ErrNotStarted
}
if b .section > s {
return ErrSectionDone
}
return nil
}
func (b *Builder ) StartQuestions () error {
if err := b .startCheck (sectionQuestions ); err != nil {
return err
}
b .section = sectionQuestions
return nil
}
func (b *Builder ) StartAnswers () error {
if err := b .startCheck (sectionAnswers ); err != nil {
return err
}
b .section = sectionAnswers
return nil
}
func (b *Builder ) StartAuthorities () error {
if err := b .startCheck (sectionAuthorities ); err != nil {
return err
}
b .section = sectionAuthorities
return nil
}
func (b *Builder ) StartAdditionals () error {
if err := b .startCheck (sectionAdditionals ); err != nil {
return err
}
b .section = sectionAdditionals
return nil
}
func (b *Builder ) incrementSectionCount () error {
var count *uint16
var err error
switch b .section {
case sectionQuestions :
count = &b .header .questions
err = errTooManyQuestions
case sectionAnswers :
count = &b .header .answers
err = errTooManyAnswers
case sectionAuthorities :
count = &b .header .authorities
err = errTooManyAuthorities
case sectionAdditionals :
count = &b .header .additionals
err = errTooManyAdditionals
}
if *count == ^uint16 (0 ) {
return err
}
*count ++
return nil
}
func (b *Builder ) Question (q Question ) error {
if b .section < sectionQuestions {
return ErrNotStarted
}
if b .section > sectionQuestions {
return ErrSectionDone
}
msg , err := q .pack (b .msg , b .compression , b .start )
if err != nil {
return err
}
if err := b .incrementSectionCount (); err != nil {
return err
}
b .msg = msg
return nil
}
func (b *Builder ) checkResourceSection () error {
if b .section < sectionAnswers {
return ErrNotStarted
}
if b .section > sectionAdditionals {
return ErrSectionDone
}
return nil
}
func (b *Builder ) CNAMEResource (h ResourceHeader , r CNAMEResource ) error {
if err := b .checkResourceSection (); err != nil {
return err
}
h .Type = r .realType ()
msg , lenOff , err := h .pack (b .msg , b .compression , b .start )
if err != nil {
return &nestedError {"ResourceHeader" , err }
}
preLen := len (msg )
if msg , err = r .pack (msg , b .compression , b .start ); err != nil {
return &nestedError {"CNAMEResource body" , err }
}
if err := h .fixLen (msg , lenOff , preLen ); err != nil {
return err
}
if err := b .incrementSectionCount (); err != nil {
return err
}
b .msg = msg
return nil
}
func (b *Builder ) MXResource (h ResourceHeader , r MXResource ) error {
if err := b .checkResourceSection (); err != nil {
return err
}
h .Type = r .realType ()
msg , lenOff , err := h .pack (b .msg , b .compression , b .start )
if err != nil {
return &nestedError {"ResourceHeader" , err }
}
preLen := len (msg )
if msg , err = r .pack (msg , b .compression , b .start ); err != nil {
return &nestedError {"MXResource body" , err }
}
if err := h .fixLen (msg , lenOff , preLen ); err != nil {
return err
}
if err := b .incrementSectionCount (); err != nil {
return err
}
b .msg = msg
return nil
}
func (b *Builder ) NSResource (h ResourceHeader , r NSResource ) error {
if err := b .checkResourceSection (); err != nil {
return err
}
h .Type = r .realType ()
msg , lenOff , err := h .pack (b .msg , b .compression , b .start )
if err != nil {
return &nestedError {"ResourceHeader" , err }
}
preLen := len (msg )
if msg , err = r .pack (msg , b .compression , b .start ); err != nil {
return &nestedError {"NSResource body" , err }
}
if err := h .fixLen (msg , lenOff , preLen ); err != nil {
return err
}
if err := b .incrementSectionCount (); err != nil {
return err
}
b .msg = msg
return nil
}
func (b *Builder ) PTRResource (h ResourceHeader , r PTRResource ) error {
if err := b .checkResourceSection (); err != nil {
return err
}
h .Type = r .realType ()
msg , lenOff , err := h .pack (b .msg , b .compression , b .start )
if err != nil {
return &nestedError {"ResourceHeader" , err }
}
preLen := len (msg )
if msg , err = r .pack (msg , b .compression , b .start ); err != nil {
return &nestedError {"PTRResource body" , err }
}
if err := h .fixLen (msg , lenOff , preLen ); err != nil {
return err
}
if err := b .incrementSectionCount (); err != nil {
return err
}
b .msg = msg
return nil
}
func (b *Builder ) SOAResource (h ResourceHeader , r SOAResource ) error {
if err := b .checkResourceSection (); err != nil {
return err
}
h .Type = r .realType ()
msg , lenOff , err := h .pack (b .msg , b .compression , b .start )
if err != nil {
return &nestedError {"ResourceHeader" , err }
}
preLen := len (msg )
if msg , err = r .pack (msg , b .compression , b .start ); err != nil {
return &nestedError {"SOAResource body" , err }
}
if err := h .fixLen (msg , lenOff , preLen ); err != nil {
return err
}
if err := b .incrementSectionCount (); err != nil {
return err
}
b .msg = msg
return nil
}
func (b *Builder ) TXTResource (h ResourceHeader , r TXTResource ) error {
if err := b .checkResourceSection (); err != nil {
return err
}
h .Type = r .realType ()
msg , lenOff , err := h .pack (b .msg , b .compression , b .start )
if err != nil {
return &nestedError {"ResourceHeader" , err }
}
preLen := len (msg )
if msg , err = r .pack (msg , b .compression , b .start ); err != nil {
return &nestedError {"TXTResource body" , err }
}
if err := h .fixLen (msg , lenOff , preLen ); err != nil {
return err
}
if err := b .incrementSectionCount (); err != nil {
return err
}
b .msg = msg
return nil
}
func (b *Builder ) SRVResource (h ResourceHeader , r SRVResource ) error {
if err := b .checkResourceSection (); err != nil {
return err
}
h .Type = r .realType ()
msg , lenOff , err := h .pack (b .msg , b .compression , b .start )
if err != nil {
return &nestedError {"ResourceHeader" , err }
}
preLen := len (msg )
if msg , err = r .pack (msg , b .compression , b .start ); err != nil {
return &nestedError {"SRVResource body" , err }
}
if err := h .fixLen (msg , lenOff , preLen ); err != nil {
return err
}
if err := b .incrementSectionCount (); err != nil {
return err
}
b .msg = msg
return nil
}
func (b *Builder ) AResource (h ResourceHeader , r AResource ) error {
if err := b .checkResourceSection (); err != nil {
return err
}
h .Type = r .realType ()
msg , lenOff , err := h .pack (b .msg , b .compression , b .start )
if err != nil {
return &nestedError {"ResourceHeader" , err }
}
preLen := len (msg )
if msg , err = r .pack (msg , b .compression , b .start ); err != nil {
return &nestedError {"AResource body" , err }
}
if err := h .fixLen (msg , lenOff , preLen ); err != nil {
return err
}
if err := b .incrementSectionCount (); err != nil {
return err
}
b .msg = msg
return nil
}
func (b *Builder ) AAAAResource (h ResourceHeader , r AAAAResource ) error {
if err := b .checkResourceSection (); err != nil {
return err
}
h .Type = r .realType ()
msg , lenOff , err := h .pack (b .msg , b .compression , b .start )
if err != nil {
return &nestedError {"ResourceHeader" , err }
}
preLen := len (msg )
if msg , err = r .pack (msg , b .compression , b .start ); err != nil {
return &nestedError {"AAAAResource body" , err }
}
if err := h .fixLen (msg , lenOff , preLen ); err != nil {
return err
}
if err := b .incrementSectionCount (); err != nil {
return err
}
b .msg = msg
return nil
}
func (b *Builder ) OPTResource (h ResourceHeader , r OPTResource ) error {
if err := b .checkResourceSection (); err != nil {
return err
}
h .Type = r .realType ()
msg , lenOff , err := h .pack (b .msg , b .compression , b .start )
if err != nil {
return &nestedError {"ResourceHeader" , err }
}
preLen := len (msg )
if msg , err = r .pack (msg , b .compression , b .start ); err != nil {
return &nestedError {"OPTResource body" , err }
}
if err := h .fixLen (msg , lenOff , preLen ); err != nil {
return err
}
if err := b .incrementSectionCount (); err != nil {
return err
}
b .msg = msg
return nil
}
func (b *Builder ) UnknownResource (h ResourceHeader , r UnknownResource ) error {
if err := b .checkResourceSection (); err != nil {
return err
}
h .Type = r .realType ()
msg , lenOff , err := h .pack (b .msg , b .compression , b .start )
if err != nil {
return &nestedError {"ResourceHeader" , err }
}
preLen := len (msg )
if msg , err = r .pack (msg , b .compression , b .start ); err != nil {
return &nestedError {"UnknownResource body" , err }
}
if err := h .fixLen (msg , lenOff , preLen ); err != nil {
return err
}
if err := b .incrementSectionCount (); err != nil {
return err
}
b .msg = msg
return nil
}
func (b *Builder ) Finish () ([]byte , error ) {
if b .section < sectionHeader {
return nil , ErrNotStarted
}
b .section = sectionDone
b .header .pack (b .msg [b .start :b .start ])
return b .msg , nil
}
type ResourceHeader struct {
Name Name
Type Type
Class Class
TTL uint32
Length uint16
}
func (h *ResourceHeader ) GoString () string {
return "dnsmessage.ResourceHeader{" +
"Name: " + h .Name .GoString () + ", " +
"Type: " + h .Type .GoString () + ", " +
"Class: " + h .Class .GoString () + ", " +
"TTL: " + printUint32 (h .TTL ) + ", " +
"Length: " + printUint16 (h .Length ) + "}"
}
func (h *ResourceHeader ) pack (oldMsg []byte , compression map [string ]uint16 , compressionOff int ) (msg []byte , lenOff int , err error ) {
msg = oldMsg
if msg , err = h .Name .pack (msg , compression , compressionOff ); err != nil {
return oldMsg , 0 , &nestedError {"Name" , err }
}
msg = packType (msg , h .Type )
msg = packClass (msg , h .Class )
msg = packUint32 (msg , h .TTL )
lenOff = len (msg )
msg = packUint16 (msg , h .Length )
return msg , lenOff , nil
}
func (h *ResourceHeader ) unpack (msg []byte , off int ) (int , error ) {
newOff := off
var err error
if newOff , err = h .Name .unpack (msg , newOff ); err != nil {
return off , &nestedError {"Name" , err }
}
if h .Type , newOff , err = unpackType (msg , newOff ); err != nil {
return off , &nestedError {"Type" , err }
}
if h .Class , newOff , err = unpackClass (msg , newOff ); err != nil {
return off , &nestedError {"Class" , err }
}
if h .TTL , newOff , err = unpackUint32 (msg , newOff ); err != nil {
return off , &nestedError {"TTL" , err }
}
if h .Length , newOff , err = unpackUint16 (msg , newOff ); err != nil {
return off , &nestedError {"Length" , err }
}
return newOff , nil
}
func (h *ResourceHeader ) fixLen (msg []byte , lenOff int , preLen int ) error {
conLen := len (msg ) - preLen
if conLen > int (^uint16 (0 )) {
return errResTooLong
}
packUint16 (msg [lenOff :lenOff ], uint16 (conLen ))
h .Length = uint16 (conLen )
return nil
}
const (
edns0Version = 0
edns0DNSSECOK = 0x00008000
ednsVersionMask = 0x00ff0000
edns0DNSSECOKMask = 0x00ff8000
)
func (h *ResourceHeader ) SetEDNS0 (udpPayloadLen int , extRCode RCode , dnssecOK bool ) error {
h .Name = Name {Data : [255 ]byte {'.' }, Length : 1 }
h .Type = TypeOPT
h .Class = Class (udpPayloadLen )
h .TTL = uint32 (extRCode ) >> 4 << 24
if dnssecOK {
h .TTL |= edns0DNSSECOK
}
return nil
}
func (h *ResourceHeader ) DNSSECAllowed () bool {
return h .TTL &edns0DNSSECOKMask == edns0DNSSECOK
}
func (h *ResourceHeader ) ExtendedRCode (rcode RCode ) RCode {
if h .TTL &ednsVersionMask == edns0Version {
return RCode (h .TTL >>24 <<4 ) | rcode
}
return rcode
}
func skipResource(msg []byte , off int ) (int , error ) {
newOff , err := skipName (msg , off )
if err != nil {
return off , &nestedError {"Name" , err }
}
if newOff , err = skipType (msg , newOff ); err != nil {
return off , &nestedError {"Type" , err }
}
if newOff , err = skipClass (msg , newOff ); err != nil {
return off , &nestedError {"Class" , err }
}
if newOff , err = skipUint32 (msg , newOff ); err != nil {
return off , &nestedError {"TTL" , err }
}
length , newOff , err := unpackUint16 (msg , newOff )
if err != nil {
return off , &nestedError {"Length" , err }
}
if newOff += int (length ); newOff > len (msg ) {
return off , errResourceLen
}
return newOff , nil
}
func packUint16(msg []byte , field uint16 ) []byte {
return append (msg , byte (field >>8 ), byte (field ))
}
func unpackUint16(msg []byte , off int ) (uint16 , int , error ) {
if off +uint16Len > len (msg ) {
return 0 , off , errBaseLen
}
return uint16 (msg [off ])<<8 | uint16 (msg [off +1 ]), off + uint16Len , nil
}
func skipUint16(msg []byte , off int ) (int , error ) {
if off +uint16Len > len (msg ) {
return off , errBaseLen
}
return off + uint16Len , nil
}
func packType(msg []byte , field Type ) []byte {
return packUint16 (msg , uint16 (field ))
}
func unpackType(msg []byte , off int ) (Type , int , error ) {
t , o , err := unpackUint16 (msg , off )
return Type (t ), o , err
}
func skipType(msg []byte , off int ) (int , error ) {
return skipUint16 (msg , off )
}
func packClass(msg []byte , field Class ) []byte {
return packUint16 (msg , uint16 (field ))
}
func unpackClass(msg []byte , off int ) (Class , int , error ) {
c , o , err := unpackUint16 (msg , off )
return Class (c ), o , err
}
func skipClass(msg []byte , off int ) (int , error ) {
return skipUint16 (msg , off )
}
func packUint32(msg []byte , field uint32 ) []byte {
return append (
msg ,
byte (field >>24 ),
byte (field >>16 ),
byte (field >>8 ),
byte (field ),
)
}
func unpackUint32(msg []byte , off int ) (uint32 , int , error ) {
if off +uint32Len > len (msg ) {
return 0 , off , errBaseLen
}
v := uint32 (msg [off ])<<24 | uint32 (msg [off +1 ])<<16 | uint32 (msg [off +2 ])<<8 | uint32 (msg [off +3 ])
return v , off + uint32Len , nil
}
func skipUint32(msg []byte , off int ) (int , error ) {
if off +uint32Len > len (msg ) {
return off , errBaseLen
}
return off + uint32Len , nil
}
func packText(msg []byte , field string ) ([]byte , error ) {
l := len (field )
if l > 255 {
return nil , errStringTooLong
}
msg = append (msg , byte (l ))
msg = append (msg , field ...)
return msg , nil
}
func unpackText(msg []byte , off int ) (string , int , error ) {
if off >= len (msg ) {
return "" , off , errBaseLen
}
beginOff := off + 1
endOff := beginOff + int (msg [off ])
if endOff > len (msg ) {
return "" , off , errCalcLen
}
return string (msg [beginOff :endOff ]), endOff , nil
}
func packBytes(msg []byte , field []byte ) []byte {
return append (msg , field ...)
}
func unpackBytes(msg []byte , off int , field []byte ) (int , error ) {
newOff := off + len (field )
if newOff > len (msg ) {
return off , errBaseLen
}
copy (field , msg [off :newOff ])
return newOff , nil
}
const nonEncodedNameMax = 254
type Name struct {
Data [255 ]byte
Length uint8
}
func NewName (name string ) (Name , error ) {
n := Name {Length : uint8 (len (name ))}
if len (name ) > len (n .Data ) {
return Name {}, errCalcLen
}
copy (n .Data [:], name )
return n , nil
}
func MustNewName (name string ) Name {
n , err := NewName (name )
if err != nil {
panic ("creating name: " + err .Error())
}
return n
}
func (n Name ) String () string {
return string (n .Data [:n .Length ])
}
func (n *Name ) GoString () string {
return `dnsmessage.MustNewName("` + printString (n .Data [:n .Length ]) + `")`
}
func (n *Name ) pack (msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error ) {
oldMsg := msg
if n .Length > nonEncodedNameMax {
return nil , errNameTooLong
}
if n .Length == 0 || n .Data [n .Length -1 ] != '.' {
return oldMsg , errNonCanonicalName
}
if n .Data [0 ] == '.' && n .Length == 1 {
return append (msg , 0 ), nil
}
var nameAsStr string
for i , begin := 0 , 0 ; i < int (n .Length ); i ++ {
if n .Data [i ] == '.' {
if i -begin >= 1 <<6 {
return oldMsg , errSegTooLong
}
if i -begin == 0 {
return oldMsg , errZeroSegLen
}
msg = append (msg , byte (i -begin ))
for j := begin ; j < i ; j ++ {
msg = append (msg , n .Data [j ])
}
begin = i + 1
continue
}
if (i == 0 || n .Data [i -1 ] == '.' ) && compression != nil {
if ptr , ok := compression [string (n .Data [i :n .Length ])]; ok {
return append (msg , byte (ptr >>8 |0xC0 ), byte (ptr )), nil
}
newPtr := len (msg ) - compressionOff
if newPtr <= int (^uint16 (0 )>>2 ) {
if nameAsStr == "" {
nameAsStr = string (n .Data [:n .Length ])
}
compression [nameAsStr [i :]] = uint16 (newPtr )
}
}
}
return append (msg , 0 ), nil
}
func (n *Name ) unpack (msg []byte , off int ) (int , error ) {
currOff := off
newOff := off
var ptr int
name := n .Data [:0 ]
Loop :
for {
if currOff >= len (msg ) {
return off , errBaseLen
}
c := int (msg [currOff ])
currOff ++
switch c & 0xC0 {
case 0x00 :
if c == 0x00 {
break Loop
}
endOff := currOff + c
if endOff > len (msg ) {
return off , errCalcLen
}
for _ , v := range msg [currOff :endOff ] {
if v == '.' {
return off , errInvalidName
}
}
name = append (name , msg [currOff :endOff ]...)
name = append (name , '.' )
currOff = endOff
case 0xC0 :
if currOff >= len (msg ) {
return off , errInvalidPtr
}
c1 := msg [currOff ]
currOff ++
if ptr == 0 {
newOff = currOff
}
if ptr ++; ptr > 10 {
return off , errTooManyPtr
}
currOff = (c ^0xC0 )<<8 | int (c1 )
default :
return off , errReserved
}
}
if len (name ) == 0 {
name = append (name , '.' )
}
if len (name ) > nonEncodedNameMax {
return off , errNameTooLong
}
n .Length = uint8 (len (name ))
if ptr == 0 {
newOff = currOff
}
return newOff , nil
}
func skipName(msg []byte , off int ) (int , error ) {
newOff := off
Loop :
for {
if newOff >= len (msg ) {
return off , errBaseLen
}
c := int (msg [newOff ])
newOff ++
switch c & 0xC0 {
case 0x00 :
if c == 0x00 {
break Loop
}
newOff += c
if newOff > len (msg ) {
return off , errCalcLen
}
case 0xC0 :
newOff ++
break Loop
default :
return off , errReserved
}
}
return newOff , nil
}
type Question struct {
Name Name
Type Type
Class Class
}
func (q *Question ) pack (msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error ) {
msg , err := q .Name .pack (msg , compression , compressionOff )
if err != nil {
return msg , &nestedError {"Name" , err }
}
msg = packType (msg , q .Type )
return packClass (msg , q .Class ), nil
}
func (q *Question ) GoString () string {
return "dnsmessage.Question{" +
"Name: " + q .Name .GoString () + ", " +
"Type: " + q .Type .GoString () + ", " +
"Class: " + q .Class .GoString () + "}"
}
func unpackResourceBody(msg []byte , off int , hdr ResourceHeader ) (ResourceBody , int , error ) {
var (
r ResourceBody
err error
name string
)
switch hdr .Type {
case TypeA :
var rb AResource
rb , err = unpackAResource (msg , off )
r = &rb
name = "A"
case TypeNS :
var rb NSResource
rb , err = unpackNSResource (msg , off )
r = &rb
name = "NS"
case TypeCNAME :
var rb CNAMEResource
rb , err = unpackCNAMEResource (msg , off )
r = &rb
name = "CNAME"
case TypeSOA :
var rb SOAResource
rb , err = unpackSOAResource (msg , off )
r = &rb
name = "SOA"
case TypePTR :
var rb PTRResource
rb , err = unpackPTRResource (msg , off )
r = &rb
name = "PTR"
case TypeMX :
var rb MXResource
rb , err = unpackMXResource (msg , off )
r = &rb
name = "MX"
case TypeTXT :
var rb TXTResource
rb , err = unpackTXTResource (msg , off , hdr .Length )
r = &rb
name = "TXT"
case TypeAAAA :
var rb AAAAResource
rb , err = unpackAAAAResource (msg , off )
r = &rb
name = "AAAA"
case TypeSRV :
var rb SRVResource
rb , err = unpackSRVResource (msg , off )
r = &rb
name = "SRV"
case TypeOPT :
var rb OPTResource
rb , err = unpackOPTResource (msg , off , hdr .Length )
r = &rb
name = "OPT"
default :
var rb UnknownResource
rb , err = unpackUnknownResource (hdr .Type , msg , off , hdr .Length )
r = &rb
name = "Unknown"
}
if err != nil {
return nil , off , &nestedError {name + " record" , err }
}
return r , off + int (hdr .Length ), nil
}
type CNAMEResource struct {
CNAME Name
}
func (r *CNAMEResource ) realType () Type {
return TypeCNAME
}
func (r *CNAMEResource ) pack (msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error ) {
return r .CNAME .pack (msg , compression , compressionOff )
}
func (r *CNAMEResource ) GoString () string {
return "dnsmessage.CNAMEResource{CNAME: " + r .CNAME .GoString () + "}"
}
func unpackCNAMEResource(msg []byte , off int ) (CNAMEResource , error ) {
var cname Name
if _ , err := cname .unpack (msg , off ); err != nil {
return CNAMEResource {}, err
}
return CNAMEResource {cname }, nil
}
type MXResource struct {
Pref uint16
MX Name
}
func (r *MXResource ) realType () Type {
return TypeMX
}
func (r *MXResource ) pack (msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error ) {
oldMsg := msg
msg = packUint16 (msg , r .Pref )
msg , err := r .MX .pack (msg , compression , compressionOff )
if err != nil {
return oldMsg , &nestedError {"MXResource.MX" , err }
}
return msg , nil
}
func (r *MXResource ) GoString () string {
return "dnsmessage.MXResource{" +
"Pref: " + printUint16 (r .Pref ) + ", " +
"MX: " + r .MX .GoString () + "}"
}
func unpackMXResource(msg []byte , off int ) (MXResource , error ) {
pref , off , err := unpackUint16 (msg , off )
if err != nil {
return MXResource {}, &nestedError {"Pref" , err }
}
var mx Name
if _ , err := mx .unpack (msg , off ); err != nil {
return MXResource {}, &nestedError {"MX" , err }
}
return MXResource {pref , mx }, nil
}
type NSResource struct {
NS Name
}
func (r *NSResource ) realType () Type {
return TypeNS
}
func (r *NSResource ) pack (msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error ) {
return r .NS .pack (msg , compression , compressionOff )
}
func (r *NSResource ) GoString () string {
return "dnsmessage.NSResource{NS: " + r .NS .GoString () + "}"
}
func unpackNSResource(msg []byte , off int ) (NSResource , error ) {
var ns Name
if _ , err := ns .unpack (msg , off ); err != nil {
return NSResource {}, err
}
return NSResource {ns }, nil
}
type PTRResource struct {
PTR Name
}
func (r *PTRResource ) realType () Type {
return TypePTR
}
func (r *PTRResource ) pack (msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error ) {
return r .PTR .pack (msg , compression , compressionOff )
}
func (r *PTRResource ) GoString () string {
return "dnsmessage.PTRResource{PTR: " + r .PTR .GoString () + "}"
}
func unpackPTRResource(msg []byte , off int ) (PTRResource , error ) {
var ptr Name
if _ , err := ptr .unpack (msg , off ); err != nil {
return PTRResource {}, err
}
return PTRResource {ptr }, nil
}
type SOAResource struct {
NS Name
MBox Name
Serial uint32
Refresh uint32
Retry uint32
Expire uint32
MinTTL uint32
}
func (r *SOAResource ) realType () Type {
return TypeSOA
}
func (r *SOAResource ) pack (msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error ) {
oldMsg := msg
msg , err := r .NS .pack (msg , compression , compressionOff )
if err != nil {
return oldMsg , &nestedError {"SOAResource.NS" , err }
}
msg , err = r .MBox .pack (msg , compression , compressionOff )
if err != nil {
return oldMsg , &nestedError {"SOAResource.MBox" , err }
}
msg = packUint32 (msg , r .Serial )
msg = packUint32 (msg , r .Refresh )
msg = packUint32 (msg , r .Retry )
msg = packUint32 (msg , r .Expire )
return packUint32 (msg , r .MinTTL ), nil
}
func (r *SOAResource ) GoString () string {
return "dnsmessage.SOAResource{" +
"NS: " + r .NS .GoString () + ", " +
"MBox: " + r .MBox .GoString () + ", " +
"Serial: " + printUint32 (r .Serial ) + ", " +
"Refresh: " + printUint32 (r .Refresh ) + ", " +
"Retry: " + printUint32 (r .Retry ) + ", " +
"Expire: " + printUint32 (r .Expire ) + ", " +
"MinTTL: " + printUint32 (r .MinTTL ) + "}"
}
func unpackSOAResource(msg []byte , off int ) (SOAResource , error ) {
var ns Name
off , err := ns .unpack (msg , off )
if err != nil {
return SOAResource {}, &nestedError {"NS" , err }
}
var mbox Name
if off , err = mbox .unpack (msg , off ); err != nil {
return SOAResource {}, &nestedError {"MBox" , err }
}
serial , off , err := unpackUint32 (msg , off )
if err != nil {
return SOAResource {}, &nestedError {"Serial" , err }
}
refresh , off , err := unpackUint32 (msg , off )
if err != nil {
return SOAResource {}, &nestedError {"Refresh" , err }
}
retry , off , err := unpackUint32 (msg , off )
if err != nil {
return SOAResource {}, &nestedError {"Retry" , err }
}
expire , off , err := unpackUint32 (msg , off )
if err != nil {
return SOAResource {}, &nestedError {"Expire" , err }
}
minTTL , _ , err := unpackUint32 (msg , off )
if err != nil {
return SOAResource {}, &nestedError {"MinTTL" , err }
}
return SOAResource {ns , mbox , serial , refresh , retry , expire , minTTL }, nil
}
type TXTResource struct {
TXT []string
}
func (r *TXTResource ) realType () Type {
return TypeTXT
}
func (r *TXTResource ) pack (msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error ) {
oldMsg := msg
for _ , s := range r .TXT {
var err error
msg , err = packText (msg , s )
if err != nil {
return oldMsg , err
}
}
return msg , nil
}
func (r *TXTResource ) GoString () string {
s := "dnsmessage.TXTResource{TXT: []string{"
if len (r .TXT ) == 0 {
return s + "}}"
}
s += `"` + printString ([]byte (r .TXT [0 ]))
for _ , t := range r .TXT [1 :] {
s += `", "` + printString ([]byte (t ))
}
return s + `"}}`
}
func unpackTXTResource(msg []byte , off int , length uint16 ) (TXTResource , error ) {
txts := make ([]string , 0 , 1 )
for n := uint16 (0 ); n < length ; {
var t string
var err error
if t , off , err = unpackText (msg , off ); err != nil {
return TXTResource {}, &nestedError {"text" , err }
}
if length -n < uint16 (len (t ))+1 {
return TXTResource {}, errCalcLen
}
n += uint16 (len (t )) + 1
txts = append (txts , t )
}
return TXTResource {txts }, nil
}
type SRVResource struct {
Priority uint16
Weight uint16
Port uint16
Target Name
}
func (r *SRVResource ) realType () Type {
return TypeSRV
}
func (r *SRVResource ) pack (msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error ) {
oldMsg := msg
msg = packUint16 (msg , r .Priority )
msg = packUint16 (msg , r .Weight )
msg = packUint16 (msg , r .Port )
msg , err := r .Target .pack (msg , nil , compressionOff )
if err != nil {
return oldMsg , &nestedError {"SRVResource.Target" , err }
}
return msg , nil
}
func (r *SRVResource ) GoString () string {
return "dnsmessage.SRVResource{" +
"Priority: " + printUint16 (r .Priority ) + ", " +
"Weight: " + printUint16 (r .Weight ) + ", " +
"Port: " + printUint16 (r .Port ) + ", " +
"Target: " + r .Target .GoString () + "}"
}
func unpackSRVResource(msg []byte , off int ) (SRVResource , error ) {
priority , off , err := unpackUint16 (msg , off )
if err != nil {
return SRVResource {}, &nestedError {"Priority" , err }
}
weight , off , err := unpackUint16 (msg , off )
if err != nil {
return SRVResource {}, &nestedError {"Weight" , err }
}
port , off , err := unpackUint16 (msg , off )
if err != nil {
return SRVResource {}, &nestedError {"Port" , err }
}
var target Name
if _ , err := target .unpack (msg , off ); err != nil {
return SRVResource {}, &nestedError {"Target" , err }
}
return SRVResource {priority , weight , port , target }, nil
}
type AResource struct {
A [4 ]byte
}
func (r *AResource ) realType () Type {
return TypeA
}
func (r *AResource ) pack (msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error ) {
return packBytes (msg , r .A [:]), nil
}
func (r *AResource ) GoString () string {
return "dnsmessage.AResource{" +
"A: [4]byte{" + printByteSlice (r .A [:]) + "}}"
}
func unpackAResource(msg []byte , off int ) (AResource , error ) {
var a [4 ]byte
if _ , err := unpackBytes (msg , off , a [:]); err != nil {
return AResource {}, err
}
return AResource {a }, nil
}
type AAAAResource struct {
AAAA [16 ]byte
}
func (r *AAAAResource ) realType () Type {
return TypeAAAA
}
func (r *AAAAResource ) GoString () string {
return "dnsmessage.AAAAResource{" +
"AAAA: [16]byte{" + printByteSlice (r .AAAA [:]) + "}}"
}
func (r *AAAAResource ) pack (msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error ) {
return packBytes (msg , r .AAAA [:]), nil
}
func unpackAAAAResource(msg []byte , off int ) (AAAAResource , error ) {
var aaaa [16 ]byte
if _ , err := unpackBytes (msg , off , aaaa [:]); err != nil {
return AAAAResource {}, err
}
return AAAAResource {aaaa }, nil
}
type OPTResource struct {
Options []Option
}
type Option struct {
Code uint16
Data []byte
}
func (o *Option ) GoString () string {
return "dnsmessage.Option{" +
"Code: " + printUint16 (o .Code ) + ", " +
"Data: []byte{" + printByteSlice (o .Data ) + "}}"
}
func (r *OPTResource ) realType () Type {
return TypeOPT
}
func (r *OPTResource ) pack (msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error ) {
for _ , opt := range r .Options {
msg = packUint16 (msg , opt .Code )
l := uint16 (len (opt .Data ))
msg = packUint16 (msg , l )
msg = packBytes (msg , opt .Data )
}
return msg , nil
}
func (r *OPTResource ) GoString () string {
s := "dnsmessage.OPTResource{Options: []dnsmessage.Option{"
if len (r .Options ) == 0 {
return s + "}}"
}
s += r .Options [0 ].GoString ()
for _ , o := range r .Options [1 :] {
s += ", " + o .GoString ()
}
return s + "}}"
}
func unpackOPTResource(msg []byte , off int , length uint16 ) (OPTResource , error ) {
var opts []Option
for oldOff := off ; off < oldOff +int (length ); {
var err error
var o Option
o .Code , off , err = unpackUint16 (msg , off )
if err != nil {
return OPTResource {}, &nestedError {"Code" , err }
}
var l uint16
l , off , err = unpackUint16 (msg , off )
if err != nil {
return OPTResource {}, &nestedError {"Data" , err }
}
o .Data = make ([]byte , l )
if copy (o .Data , msg [off :]) != int (l ) {
return OPTResource {}, &nestedError {"Data" , errCalcLen }
}
off += int (l )
opts = append (opts , o )
}
return OPTResource {opts }, nil
}
type UnknownResource struct {
Type Type
Data []byte
}
func (r *UnknownResource ) realType () Type {
return r .Type
}
func (r *UnknownResource ) pack (msg []byte , compression map [string ]uint16 , compressionOff int ) ([]byte , error ) {
return packBytes (msg , r .Data [:]), nil
}
func (r *UnknownResource ) GoString () string {
return "dnsmessage.UnknownResource{" +
"Type: " + r .Type .GoString () + ", " +
"Data: []byte{" + printByteSlice (r .Data ) + "}}"
}
func unpackUnknownResource(recordType Type , msg []byte , off int , length uint16 ) (UnknownResource , error ) {
parsed := UnknownResource {
Type : recordType ,
Data : make ([]byte , length ),
}
if _ , err := unpackBytes (msg , off , parsed .Data ); err != nil {
return UnknownResource {}, err
}
return parsed , nil
}
The pages are generated with Golds v0.7.0-preview . (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 .