func unpackRR(data []byte, index int) (rr RR, offset int, err error) { offset = index var hdr RRHeader hdr.Name, offset, err = UnpackDomainName(data, offset, MAX_COMPRESSION_DEPTH) if err != nil { return } if offset+10 > len(data) { err = errors.New("out of range") return } hdr.Type, offset = UnPackUint16(data, offset) hdr.Class, offset = UnPackUint16(data, offset) hdr.Ttl, offset = UnPackUint32(data, offset) hdr.RDLength, offset = UnPackUint16(data, offset) if hdr.Class != CLASS_INET { err = errors.New("unimplement") return } rr, err = RRNew(hdr.Type) if err != nil { return } rr.SetHeader(&hdr) offset, err = rr.UnpackRData(data, offset) return }
func PackDomainName(name string, buf []byte, index int, compression map[string]int) (offset int, err error) { bufLen := len(buf) offset = index if len(name) > MAX_DOMAIN_NAME_LEN { err = errors.New(fmt.Sprintf("Domain name length must <= %d: %s", MAX_DOMAIN_NAME_LEN, name)) return } tempName := name for { if len(tempName) == 0 { break } if compression != nil { //need compress if ptr, ok := compression[tempName]; ok { offset = PackUint16(uint16(ptr), buf, offset) buf[offset-2] |= 0xC0 return } } dotIndex := strings.Index(tempName, ".") var label string if dotIndex == -1 { label = tempName tempName = "" } else { label = tempName[:dotIndex] tempName = tempName[dotIndex+1:] } labelLen := len(label) if labelLen > MAX_DOMAIN_LABEL_LEN { err = errors.New(fmt.Sprintf("Domain label length must <= %d: %s", MAX_DOMAIN_LABEL_LEN, label)) return } if labelLen+1 > bufLen-offset { err = errors.New("buffer too small to store " + name) return } if compression != nil { compression[label+"."+tempName] = offset } buf[offset] = uint8(labelLen) offset++ copy(buf[offset:], label) offset += labelLen } buf[offset] = 0 offset++ return }
func (m *RRMx) SetRData(data string) (err error) { fields := strings.Split(data, " ") if len(fields) != 2 { err = errors.New("invalid mx rdata: " + data) return } m.domain = fields[0] priority, err := strconv.Atoi(fields[1]) if err != nil { err = errors.New("invalid mx priority: " + fields[1]) return } m.priority = uint16(priority) return }
func (msg *Message) UnpackAll(data []byte) (err error) { var offset int offset, err = msg.UnpackHeaderAndQuestion(data) if err != nil { return } msg.Answer = make([]RR, msg.Hdr.ANCount) msg.Authority = make([]RR, msg.Hdr.NSCount) msg.Additional = make([]RR, msg.Hdr.ARCount) for i := uint16(0); i < msg.Hdr.ANCount; i++ { msg.Answer[i], offset, err = unpackRR(data, offset) if err != nil { return } } for i := uint16(0); i < msg.Hdr.NSCount; i++ { msg.Authority[i], offset, err = unpackRR(data, offset) if err != nil { return } } for i := uint16(0); i < msg.Hdr.ARCount; i++ { msg.Additional[i], offset, err = unpackRR(data, offset) if err != nil { return } } if offset != len(data) { err = errors.New("message data too long") return } return nil }
// Unpack a binary message to a Msg structure. func (msg *Message) UnpackHeaderAndQuestion(data []byte) (offset int, err error) { if len(data) < HEADER_LENGTH { err = errors.New("message data too short") return } //unpack message hearder offset = 0 msg.Hdr.Id, offset = UnPackUint16(data, offset) msg.Hdr.QueryResponse = (uint8(data[offset]) & _QUREY_RESPONSE) >> 7 msg.Hdr.Opcode = (uint8(data[offset]) & _OPCODE) >> 3 msg.Hdr.AuthAnswer = uint8(data[offset])&_AUTH_ANSWER != 0 msg.Hdr.Truncated = uint8(data[offset])&_TRUNCATED != 0 msg.Hdr.RecursionDesired = uint8(data[offset])&_RECURSION_DESIRED != 0 offset += 1 msg.Hdr.RecursionAvailable = uint8(data[offset])&_RECURSION_AVAILABLE != 0 msg.Hdr.Zero = (uint8(data[offset]) & _ZERO) >> 4 msg.Hdr.Rcode = uint8(data[offset]) & _RCODE offset += 1 msg.Hdr.QDCount, offset = UnPackUint16(data, offset) msg.Hdr.ANCount, offset = UnPackUint16(data, offset) msg.Hdr.NSCount, offset = UnPackUint16(data, offset) msg.Hdr.ARCount, offset = UnPackUint16(data, offset) // Arrays. msg.Question = make([]Question, msg.Hdr.QDCount) for i := uint16(0); i < msg.Hdr.QDCount; i++ { offset, err = unpackQuestion(data, offset, &msg.Question[i]) if err != nil { return } } return }
func (a *RRAaaa) SetRData(data string) (err error) { a.IPv6 = net.ParseIP(data) if a.IPv6 == nil { err = errors.New("malformed ipv6 address: " + data) } return }
func (a *RRAaaa) UnpackRData(buf []byte, index int) (offset int, err error) { offset = index if len(buf)-offset < net.IPv6len { err = errors.New("data too short") return } copy(a.IPv6, buf[offset:offset+16]) offset += net.IPv6len return }
func (a *RRA) UnpackRData(buf []byte, index int) (offset int, err error) { offset = index if len(buf)-offset < net.IPv4len { err = errors.New("data too short") return } a.IPv4 = net.IPv4(buf[offset], buf[offset+1], buf[offset+2], buf[offset+3]) offset += net.IPv4len return }
func UnpackDomainName(data []byte, index int, maxDepth int) (name string, offset int, err error) { dataLen := len(data) offset = index for { if offset+1 > dataLen { err = errors.New("out of range") return } labelLen := int(data[offset]) offset++ switch uint8(labelLen) & 0xC0 { case 0x00: // end of name if labelLen == 0x00 { if len(name) == 0 { name = "." return } else { //remove the last dot name = name[:len(name)-1] return } } if offset+labelLen > dataLen { err = errors.New("out of range") return } name += string(data[offset : offset+labelLen]) name += "." offset += labelLen case 0xC0: if offset+1 > dataLen { err = errors.New("out of range") return } lablePtr := uint16(data[offset-1])<<10>>2 | uint16(data[offset]) offset++ // pointer to somewhere else in message. if int(lablePtr) > dataLen { err = errors.New("ptr out of range") return } if maxDepth == 0 { err = errors.New("too many ptr") return } tempName, _, tempErr := UnpackDomainName(data, int(lablePtr), maxDepth-1) if tempErr != nil { return } name += tempName return default: // 0x80 and 0x40 are reserved err = errors.New("fomart error") return } } return }
func packQuestion(buf []byte, index int, question *Question, compression map[string]int) (offset int, err error) { offset, err = PackDomainName(question.Name, buf, index, compression) if err != nil { return } if 4 > len(buf)-offset { err = errors.New("buffer too small to store question type and class") return } offset = PackUint16(question.Type, buf, offset) offset = PackUint16(question.Class, buf, offset) return }
func (m *RRMx) PackRData(buf []byte, index int) (offset int, err error) { offset = index if len(buf) < offset+2 { err = errors.New("buf too short") return } offset = PackUint16(m.priority, buf, offset) offset, err = PackDomainName(m.domain, buf, offset, nil) if err != nil { return } return }
func unpackQuestion(data []byte, index int, question *Question) (offset int, err error) { offset = index question.Name, offset, err = UnpackDomainName(data, offset, MAX_COMPRESSION_DEPTH) if err != nil { return } if offset+4 > len(data) { err = errors.New("out of range") return } question.Type, offset = UnPackUint16(data, offset) question.Class, offset = UnPackUint16(data, offset) return }
func RRNew(rrtype uint16) (rr RR, err error) { switch rrtype { case TYPE_A: rr = new(RRA) return case TYPE_MX: rr = new(RRMx) return case TYPE_AAAA: rr = new(RRAaaa) return default: err = errors.New("unimplement") return } }
func packRR(rr RR, buf []byte, index int, compression map[string]int) (offset int, err error) { offset = index header := rr.Header() offset, err = PackDomainName(header.Name, buf, offset, compression) if err != nil { return } //10 = sizeof(Type)+sizeof(Class)+sizeof(Ttl)+sizeof(RDLength) if 10 > len(buf)-offset { err = errors.New("buffer too small to store RR") return } offset = PackUint16(header.Type, buf, offset) offset = PackUint16(header.Class, buf, offset) offset = PackUint32(header.Ttl, buf, offset) start := offset + 2 offset, err = rr.PackRData(buf, start) if err != nil { return } header.RDLength = uint16(offset - start) PackUint16(header.RDLength, buf, start-2) return }
func (message *Message) Pack(data []byte, needCompress bool) (length int, err error) { var compression map[string]int if needCompress { compression = make(map[string]int) // Compression pointer mappings } else { compression = nil } length = 0 dataLen := len(data) if dataLen < HEADER_LENGTH { err = errors.New("too short") return } length = PackUint16(message.Hdr.Id, data, length) if message.Hdr.QueryResponse == QR_RESPONSE { data[length] |= _QUREY_RESPONSE } data[length] |= message.Hdr.Opcode << 4 >> 1 if message.Hdr.AuthAnswer { data[length] |= _AUTH_ANSWER } if message.Hdr.Truncated { data[length] |= _TRUNCATED } if message.Hdr.RecursionDesired { data[length] |= _RECURSION_DESIRED } length++ if message.Hdr.RecursionAvailable { data[length] |= _RECURSION_AVAILABLE } data[length] |= message.Hdr.Zero << 5 >> 1 data[length] |= message.Hdr.Rcode & _RCODE length++ message.Hdr.QDCount = uint16(len(message.Question)) message.Hdr.ANCount = uint16(len(message.Answer)) message.Hdr.NSCount = uint16(len(message.Authority)) message.Hdr.ARCount = uint16(len(message.Additional)) length = PackUint16(message.Hdr.QDCount, data, length) length = PackUint16(message.Hdr.ANCount, data, length) length = PackUint16(message.Hdr.NSCount, data, length) length = PackUint16(message.Hdr.ARCount, data, length) for _, question := range message.Question { length, err = packQuestion(data, length, &question, compression) if err != nil { return } } length, err = packRRs(message.Answer, data, length, compression) if err != nil { return } //length, err = packRRs(message.Authority, data, length) //if err != nil { //return //} //length, err = packRRs(message.Additional, data, length) //if err != nil { //return //} return }
func (m *RRMx) UnpackRData(buf []byte, index int) (offset int, err error) { err = errors.New("unsupport unpack mx record ") return }