package magna import ( "encoding/binary" "fmt" "net" "strings" ) func (a *A) Decode(buf []byte, offset int, rdlength int) (int, error) { bytes, offset, err := getSlice(buf, offset, rdlength) if err != nil { return offset, fmt.Errorf("A record: failed to read address data: %w", err) } a.Address = net.IP(bytes) if a.Address.To4() == nil { return offset, fmt.Errorf("A record: decoded data is not a valid IPv4 address: %v", bytes) } return offset, nil } func (a *A) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { ipv4 := a.Address.To4() if ipv4 == nil { return nil, fmt.Errorf("A record: cannot encode non-IPv4 address %s", a.Address.String()) } return append(bytes, a.Address.To4()...), nil } func (a A) String() string { return a.Address.String() } func (ns *NS) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error ns.NSDName, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("NS record: failed to decode NSDName: %w", err) } return offset, nil } func (ns *NS) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes, err = encodeDomain(bytes, ns.NSDName, offsets) if err != nil { return nil, fmt.Errorf("NS record: failed to encode NSDName %s: %w", ns.NSDName, err) } return bytes, nil } func (ns NS) String() string { return ns.NSDName } func (md *MD) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error md.MADName, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("MD record: failed to decode MADName %s: %w", md.MADName, err) } return offset, nil } func (md *MD) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes, err = encodeDomain(bytes, md.MADName, offsets) if err != nil { return nil, fmt.Errorf("MD record: failed to encode MADName %s: %w", md.MADName, err) } return bytes, nil } func (md MD) String() string { return md.MADName } func (mf *MF) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error mf.MADName, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("MF record: failed to decode MADName: %w", err) } return offset, nil } func (mf *MF) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes, err = encodeDomain(bytes, mf.MADName, offsets) if err != nil { return nil, fmt.Errorf("MF record: failed to encode MADName %s: %w", mf.MADName, err) } return bytes, nil } func (mf MF) String() string { return mf.MADName } func (c *CNAME) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error c.CName, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("CNAME record: failed to decode CNAME: %w", err) } return offset, nil } func (c *CNAME) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes, err = encodeDomain(bytes, c.CName, offsets) if err != nil { return nil, fmt.Errorf("CNAME record: failed to encode CNAME %s: %w", c.CName, err) } return bytes, nil } func (c CNAME) String() string { return c.CName } func (soa *SOA) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error soa.MName, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("SOA record: failed to decode MName: %w", err) } soa.RName, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("SOA record: failed to decode RName: %w", err) } soa.Serial, offset, err = getU32(buf, offset) if err != nil { return offset, fmt.Errorf("SOA record: failed to decode Serial: %w", err) } soa.Refresh, offset, err = getU32(buf, offset) if err != nil { return offset, fmt.Errorf("SOA record: failed to decode Refresh: %w", err) } soa.Retry, offset, err = getU32(buf, offset) if err != nil { return offset, fmt.Errorf("SOA record: failed to decode Retry: %w", err) } soa.Expire, offset, err = getU32(buf, offset) if err != nil { return offset, fmt.Errorf("SOA record: failed to decode Expire: %w", err) } soa.Minimum, offset, err = getU32(buf, offset) if err != nil { return offset, fmt.Errorf("SOA record: failed to decode Minimum: %w", err) } return offset, nil } func (soa *SOA) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes, err = encodeDomain(bytes, soa.MName, offsets) if err != nil { return nil, fmt.Errorf("SOA record: failed to encode MName %s: %w", soa.MName, err) } bytes, err = encodeDomain(bytes, soa.RName, offsets) if err != nil { return nil, fmt.Errorf("SOA record: failed to encode RName %s: %w", soa.RName, err) } bytes = binary.BigEndian.AppendUint32(bytes, soa.Serial) bytes = binary.BigEndian.AppendUint32(bytes, soa.Refresh) bytes = binary.BigEndian.AppendUint32(bytes, soa.Retry) bytes = binary.BigEndian.AppendUint32(bytes, soa.Expire) bytes = binary.BigEndian.AppendUint32(bytes, soa.Minimum) return bytes, nil } func (soa SOA) String() string { return fmt.Sprintf("%s %s %d %d %d %d %d", soa.MName, soa.RName, soa.Serial, soa.Refresh, soa.Retry, soa.Expire, soa.Minimum) } func (mb *MB) Decode(buf []byte, offset int, rdlength int) (int, error) { madname, offset, err := decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("MB record: failed to decode MADName: %w", err) } mb.MADName = string(madname) return offset, nil } func (mb *MB) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes, err = encodeDomain(bytes, mb.MADName, offsets) if err != nil { return nil, fmt.Errorf("MB record: failed to encode MADName %s: %w", mb.MADName, err) } return bytes, nil } func (mb MB) String() string { return mb.MADName } func (mg *MG) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error mg.MGMName, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("MG record: failed to decode MGMName: %w", err) } return offset, nil } func (mg *MG) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes, err = encodeDomain(bytes, mg.MGMName, offsets) if err != nil { return nil, fmt.Errorf("MG record: failed to encode MGMName %s: %w", mg.MGMName, err) } return bytes, nil } func (mg MG) String() string { return mg.MGMName } func (mr *MR) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error mr.NEWName, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("MR record: failed to decode NEWName: %w", err) } return offset, nil } func (mr *MR) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes, err = encodeDomain(bytes, mr.NEWName, offsets) if err != nil { return nil, fmt.Errorf("MR record: failed to encode NEWName: %w", err) } return bytes, nil } func (mr MR) String() string { return mr.NEWName } func (null *NULL) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error null.Anything, offset, err = getSlice(buf, offset, int(rdlength)) if err != nil { return offset, fmt.Errorf("NULL record: failed to read data: %w", err) } return offset, nil } func (null *NULL) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { return append(bytes, null.Anything...), nil } func (null NULL) String() string { return string(null.Anything) } func (wks *WKS) Decode(buf []byte, offset int, rdlength int) (int, error) { if rdlength < 5 { return len(buf), fmt.Errorf("WKS record: RDLENGTH %d is too short, minimum 5 required", rdlength) } addressBytes, nextOffset, err := getSlice(buf, offset, 4) if err != nil { return len(buf), fmt.Errorf("WKS record: failed to read address: %w", err) } offset = nextOffset wks.Address = net.IP(addressBytes) protocol, nextOffset, err := getU8(buf, offset) if err != nil { return len(buf), fmt.Errorf("WKS record: failed to read protocol: %w", err) } offset = nextOffset wks.Protocol = protocol bitmapLength := rdlength - 5 wks.BitMap, nextOffset, err = getSlice(buf, offset, bitmapLength) if err != nil { return len(buf), fmt.Errorf("WKS record: failed to read bitmap: %w", err) } offset = nextOffset return offset, nil } func (wks *WKS) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { bytes = append(bytes, wks.Address.To4()...) bytes = append(bytes, wks.Protocol) bytes = append(bytes, wks.BitMap...) return bytes, nil } func (wks WKS) String() string { return fmt.Sprintf("%s %d %x", wks.Address.String(), wks.Protocol, wks.BitMap) } func (ptr *PTR) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error ptr.PTRDName, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("PTR record: failed to decode PTRDName: %w", err) } return offset, nil } func (ptr *PTR) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes, err = encodeDomain(bytes, ptr.PTRDName, offsets) if err != nil { return nil, fmt.Errorf("PTR record: failed to encode PTRD %s: %w", ptr.PTRDName, err) } return bytes, nil } func (ptr PTR) String() string { return ptr.PTRDName } func (hinfo *HINFO) Decode(buf []byte, offset int, rdlength int) (int, error) { startOffset := offset endOffset := offset + rdlength if endOffset > len(buf) { return len(buf), &BufferOverflowError{Length: len(buf), Offset: endOffset} } currentOffset := offset var err error cpuLen, nextOffset, err := getU8(buf, currentOffset) if err != nil { return len(buf), fmt.Errorf("HINFO record: failed to read CPU length: %w", err) } currentOffset = nextOffset if currentOffset+int(cpuLen) > endOffset { return len(buf), &BufferOverflowError{Length: len(buf), Offset: currentOffset + int(cpuLen)} } cpuBytes, nextOffset, err := getSlice(buf, currentOffset, int(cpuLen)) if err != nil { return len(buf), fmt.Errorf("HINFO record: failed to read CPU data: %w", err) } currentOffset = nextOffset hinfo.CPU = string(cpuBytes) osLen, nextOffset, err := getU8(buf, currentOffset) if err != nil { if currentOffset == endOffset { return len(buf), fmt.Errorf("HINFO record: missing OS length byte at offset %d (expected end: %d)", currentOffset, endOffset) } return len(buf), fmt.Errorf("HINFO record: failed to read OS length: %w", err) } currentOffset = nextOffset if currentOffset+int(osLen) > endOffset { return len(buf), &BufferOverflowError{Length: len(buf), Offset: currentOffset + int(osLen)} } osBytes, nextOffset, err := getSlice(buf, currentOffset, int(osLen)) if err != nil { return len(buf), fmt.Errorf("HINFO record: failed to read OS data: %w", err) } currentOffset = nextOffset hinfo.OS = string(osBytes) if currentOffset != endOffset { return len(buf), fmt.Errorf("HINFO record: RDATA length mismatch, consumed %d bytes, expected %d", currentOffset-startOffset, rdlength) } return currentOffset, nil } func (hinfo *HINFO) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { if len(hinfo.CPU) > 255 { return nil, fmt.Errorf("HINFO record: CPU string length %d exceeds maximum 255", len(hinfo.CPU)) } if len(hinfo.OS) > 255 { return nil, fmt.Errorf("HINFO record: OS string length %d exceeds maximum 255", len(hinfo.OS)) } bytes = append(bytes, byte(len(hinfo.CPU))) bytes = append(bytes, []byte(hinfo.CPU)...) bytes = append(bytes, byte(len(hinfo.OS))) bytes = append(bytes, []byte(hinfo.OS)...) return bytes, nil } func (hinfo HINFO) String() string { return fmt.Sprintf("%q %q", hinfo.CPU, hinfo.OS) } func (minfo *MINFO) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error minfo.RMailBx, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("MINFO record: failed to decode RMailBx: %w", err) } minfo.EMailBx, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("MINFO record: failed to decode EMailBx: %w", err) } return offset, nil } func (minfo *MINFO) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes, err = encodeDomain(bytes, minfo.RMailBx, offsets) if err != nil { return nil, fmt.Errorf("MINFO record: failed to encode RMailBx %s: %w", minfo.RMailBx, err) } bytes, err = encodeDomain(bytes, minfo.EMailBx, offsets) if err != nil { return nil, fmt.Errorf("MINFO record: failed to encode EMailBx %s: %w", minfo.EMailBx, err) } return bytes, nil } func (minfo MINFO) String() string { return minfo.RMailBx + " " + minfo.EMailBx } func (mx *MX) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error mx.Preference, offset, err = getU16(buf, offset) if err != nil { return offset, fmt.Errorf("MX record: failed to decode Preference: %w", err) } mx.Exchange, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("MX record: failed to decode Exchange: %w", err) } return offset, nil } func (mx *MX) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes = binary.BigEndian.AppendUint16(bytes, mx.Preference) bytes, err = encodeDomain(bytes, mx.Exchange, offsets) if err != nil { return nil, fmt.Errorf("MX record: failed to encode Exchange %s: %w", mx.Exchange, err) } return bytes, nil } func (mx MX) String() string { return fmt.Sprintf("%d %s", mx.Preference, mx.Exchange) } func (txt *TXT) Decode(buf []byte, offset int, rdlength int) (int, error) { txt.TxtData = make([]string, 0, 1) endOffset := offset + rdlength if endOffset > len(buf) { return len(buf), &BufferOverflowError{Length: len(buf), Offset: endOffset} } currentOffset := offset for currentOffset < endOffset { strLen, nextOffsetAfterLen, err := getU8(buf, currentOffset) if err != nil { return len(buf), fmt.Errorf("TXT record: failed to read string length byte: %w", err) } nextOffsetAfterData := nextOffsetAfterLen + int(strLen) if nextOffsetAfterData > endOffset { return len(buf), fmt.Errorf("TXT record: string segment length %d exceeds RDLENGTH boundary %d", strLen, endOffset) } strBytes, actualNextOffsetAfterData, err := getSlice(buf, nextOffsetAfterLen, int(strLen)) if err != nil { return len(buf), fmt.Errorf("TXT record: failed to read string data (length %d): %w", strLen, err) } txt.TxtData = append(txt.TxtData, string(strBytes)) currentOffset = actualNextOffsetAfterData } if currentOffset != endOffset { return len(buf), fmt.Errorf("TXT record: RDATA parsing finished at offset %d, but expected end at %d", currentOffset, endOffset) } return currentOffset, nil } func (txt *TXT) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { for _, s := range txt.TxtData { if len(s) > 255 { return nil, fmt.Errorf("TXT record: string segment length %d exceeds maximum 255", len(s)) } bytes = append(bytes, byte(len(s))) bytes = append(bytes, []byte(s)...) } return bytes, nil } func (txt TXT) String() string { quoted := make([]string, len(txt.TxtData)) for i, s := range txt.TxtData { quoted[i] = fmt.Sprintf("%q", s) } return strings.Join(quoted, " ") } func (rp *RP) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error rp.MBoxDName, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("RP record: failed to decode mbox-dname: %w", err) } rp.TXTDName, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("RP record: failed to decode txt-dname: %w", err) } return offset, nil } func (rp *RP) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes, err = encodeDomain(bytes, rp.MBoxDName, offsets) if err != nil { return nil, fmt.Errorf("RP record: failed to encode mbox-dname %s: %w", rp.MBoxDName, err) } bytes, err = encodeDomain(bytes, rp.TXTDName, offsets) if err != nil { return nil, fmt.Errorf("RP record: failed to encode txt-dname %s: %w", rp.TXTDName, err) } return bytes, nil } func (rp RP) String() string { return fmt.Sprintf("%s %s", rp.MBoxDName, rp.TXTDName) } func (a *AFSDB) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error a.Subtype, offset, err = getU16(buf, offset) if err != nil || !(a.Subtype == 1 || a.Subtype == 2) { return offset, fmt.Errorf("AFSDB record: failed to decode Subtype: %w", err) } a.Hostname, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("AFSDB record: failed to decode Hostname: %w", err) } return offset, nil } func (a *AFSDB) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes = binary.BigEndian.AppendUint16(bytes, a.Subtype) bytes, err = encodeDomain(bytes, a.Hostname, offsets) if err != nil { return nil, fmt.Errorf("AFSDB record: failed to encode Domain %s: %w", a.Hostname, err) } return bytes, nil } func (a AFSDB) String() string { return fmt.Sprintf("%d %s", a.Subtype, a.Hostname) } func (x *X25) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error endOffset := offset + rdlength if endOffset > len(buf) { return len(buf), &BufferOverflowError{Length: len(buf), Offset: endOffset} } strLen, nextOffsetAfterLen, err := getU8(buf, offset) if err != nil { return len(buf), fmt.Errorf("x25 record: failed to read string length byte: %w", err) } nextOffsetAfterData := nextOffsetAfterLen + int(strLen) if nextOffsetAfterData > endOffset { return len(buf), fmt.Errorf("x25 record: string segment length %d exceeds RDLENGTH boundary %d", strLen, endOffset) } strBytes, offset, err := getSlice(buf, nextOffsetAfterLen, int(strLen)) if err != nil { return len(buf), fmt.Errorf("x25 record: failed to read string data (length %d): %w", strLen, err) } x.PSDNAddress = string(strBytes) for _, c := range x.PSDNAddress { if c < '0' || c > '9' { return offset, fmt.Errorf("X25 record: PSDN address contains non-digit character: %c", c) } } return offset, nil } func (x *X25) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { for _, r := range x.PSDNAddress { if r < '0' || r > '9' { return nil, fmt.Errorf("X25 record: PSDN address contains non-digit character: %c", r) } } bytes = append(bytes, byte(len(x.PSDNAddress))) bytes = append(bytes, []byte(x.PSDNAddress)...) return bytes, nil } func (x X25) String() string { return x.PSDNAddress } func (isdn *ISDN) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error endOffset := offset + rdlength if endOffset > len(buf) { return len(buf), &BufferOverflowError{Length: len(buf), Offset: endOffset} } strLen, nextOffsetAfterLen, err := getU8(buf, offset) if err != nil { return len(buf), fmt.Errorf("ISDN record: failed to read string length byte: %w", err) } nextOffsetAfterData := nextOffsetAfterLen + int(strLen) if nextOffsetAfterData > endOffset { return len(buf), fmt.Errorf("ISDN record: string segment length %d exceeds RDLENGTH boundary %d", strLen, endOffset) } strBytes, offset, err := getSlice(buf, nextOffsetAfterLen, int(strLen)) if err != nil { return len(buf), fmt.Errorf("ISDN record: failed to read string data (length %d): %w", strLen, err) } isdn.ISDNAddress = string(strBytes) for _, c := range isdn.ISDNAddress { if c < '0' || c > '9' { return offset, fmt.Errorf("ISDN record: ISDN address contains non-digit character: %c", c) } } // the subaddress is an optional field if offset < endOffset { subAddrLen, nextOffsetAfterSubAddrLen, err := getU8(buf, offset) if err != nil { return offset, fmt.Errorf("ISDN record: failed to read subaddress length: %w", err) } offset = nextOffsetAfterSubAddrLen if offset+int(subAddrLen) > endOffset { return offset, fmt.Errorf("ISDN record: subaddress data length %d exceeds RDLENGTH boundary (available: %d bytes from offset %d up to %d)", subAddrLen, endOffset-offset, offset, endOffset) } subAddrBytes, nextOffsetAfterSubAddrData, err := getSlice(buf, offset, int(subAddrLen)) if err != nil { return offset, fmt.Errorf("ISDN record: failed to read subaddress data (length %d): %w", subAddrLen, err) } offset = nextOffsetAfterSubAddrData isdn.Subaddress = string(subAddrBytes) } else { isdn.Subaddress = "" } return offset, nil } func (isdn *ISDN) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { bytes = append(bytes, byte(len(isdn.ISDNAddress))) bytes = append(bytes, []byte(isdn.ISDNAddress)...) if isdn.Subaddress != "" { bytes = append(bytes, byte(len(isdn.Subaddress))) bytes = append(bytes, []byte(isdn.Subaddress)...) } return bytes, nil } func (isdn ISDN) String() string { if isdn.Subaddress != "" { return fmt.Sprintf("%q %q", isdn.ISDNAddress, isdn.Subaddress) } return fmt.Sprintf("%q", isdn.ISDNAddress) } func (rt *RT) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error rt.Preference, offset, err = getU16(buf, offset) if err != nil { return offset, fmt.Errorf("RT record: failed to decode Preference: %w", err) } rt.IntermediateHost, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("RT record: failed to decode Exchange: %w", err) } return offset, nil } func (rt *RT) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes = binary.BigEndian.AppendUint16(bytes, rt.Preference) bytes, err = encodeDomain(bytes, rt.IntermediateHost, offsets) if err != nil { return nil, fmt.Errorf("RT record: failed to encode Intermediate Host %s: %w", rt.IntermediateHost, err) } return bytes, nil } func (rt RT) String() string { return fmt.Sprintf("%d %s", rt.Preference, rt.IntermediateHost) } func (opt *OPT) Decode(buf []byte, offset int, rdlength int) (int, error) { // s := offset opt.Options = make([]EDNSOption, 0) if rdlength == 0 { return offset, nil } endOffset := offset + rdlength curOffset := offset for curOffset < endOffset { // need 4 bytes for both code and length if offset+4 > endOffset { return offset, fmt.Errorf("OPT record: truncated option header at offset: %d", offset) } optCode, offset, err := getU16(buf, offset) if err != nil { return offset, fmt.Errorf("OPT Record: failed to read option code: %w", err) } optLength, offset, err := getU16(buf, offset) if err != nil { return offset, fmt.Errorf("OPT Record: failed to read option length: %w", err) } if offset+int(optLength) > endOffset { return offset, fmt.Errorf("OPT Record: failed to read option data of length: %d", offset) } optData, offset, err := getSlice(buf, offset, int(optLength)) if err != nil { return offset, fmt.Errorf("OPT Record: failed to read option data: %w", err) } opt.Options = append(opt.Options, EDNSOption{ Code: optCode, Data: optData, }) curOffset = offset } return offset, nil } func (opt *OPT) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { for _, opt := range opt.Options { bytes = binary.BigEndian.AppendUint16(bytes, opt.Code) bytes = binary.BigEndian.AppendUint16(bytes, uint16(len(opt.Data))) bytes = append(bytes, opt.Data...) } return bytes, nil } func (opt OPT) String() string { var result strings.Builder result.WriteString("OPT [") for i, o := range opt.Options { if i < len(opt.Options)-1 { result.WriteString(fmt.Sprintf("%d <%x>,", o.Code, o.Data)) } else { result.WriteString(fmt.Sprintf("%d <%x>,", o.Code, o.Data)) } } result.WriteString("]") return result.String() } func (r *Reserved) Decode(buf []byte, offset int, rdlength int) (int, error) { var err error r.Bytes, offset, err = getSlice(buf, offset, int(rdlength)) if err != nil { return offset, fmt.Errorf("reserved record: failed to read data: %w", err) } return offset, err } func (r *Reserved) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { return append(bytes, r.Bytes...), nil } func (r Reserved) String() string { return fmt.Sprintf("[Reserved Data: %x]", r.Bytes) } // Decode decodes a resource record from buf at the offset. func (r *ResourceRecord) Decode(buf []byte, offset int) (int, error) { var err error r.Name, offset, err = decodeDomain(buf, offset) if err != nil { return offset, fmt.Errorf("rr decode: failed to decode record name: %w", err) } var rtype uint16 rtype, offset, err = getU16(buf, offset) if err != nil { return offset, fmt.Errorf("rr decode: failed to decode RType for %s: %w", r.Name, err) } r.RType = DNSType(rtype) var rclass uint16 rclass, offset, err = getU16(buf, offset) if err != nil { return offset, fmt.Errorf("rr decode: failed to decode RClass for %s: %w", r.Name, err) } r.RClass = DNSClass(rclass) r.TTL, offset, err = getU32(buf, offset) if err != nil { return offset, fmt.Errorf("rr decode: failed to decode TTL for %s: %w", r.Name, err) } r.RDLength, offset, err = getU16(buf, offset) if err != nil { return offset, fmt.Errorf("rr decode: failed to decode RDLength for %s: %w", r.Name, err) } switch r.RType { case 1: r.RData = &A{} case 2: r.RData = &NS{} case 3: r.RData = &MD{} case 4: r.RData = &MF{} case 5: r.RData = &CNAME{} case 6: r.RData = &SOA{} case 7: r.RData = &MB{} case 8: r.RData = &MG{} case 9: r.RData = &MR{} case 10: r.RData = &NULL{} case 11: r.RData = &WKS{} case 12: r.RData = &PTR{} case 13: r.RData = &HINFO{} case 14: r.RData = &MINFO{} case 15: r.RData = &MX{} case 16: r.RData = &TXT{} case 17: r.RData = &RP{} case 18: r.RData = &AFSDB{} case 19: r.RData = &X25{} case 20: r.RData = &ISDN{} case 21: r.RData = &RT{} case 41: r.RData = &OPT{} default: r.RData = &Reserved{} } if r.RData != nil { offset, err = r.RData.Decode(buf, offset, int(r.RDLength)) if err != nil { return offset, fmt.Errorf("rr decode: failed to decode RData for %s (%s): %w", r.Name, r.RType.String(), err) } } return offset, nil } // Encode encdoes a resource record and returns the input bytes appened. func (r *ResourceRecord) Encode(bytes []byte, offsets *map[string]uint16) ([]byte, error) { var err error bytes, err = encodeDomain(bytes, r.Name, offsets) if err != nil { return nil, fmt.Errorf("rr encode: failed to encode record name %s: %w", r.Name, err) } bytes = binary.BigEndian.AppendUint16(bytes, uint16(r.RType)) bytes = binary.BigEndian.AppendUint16(bytes, uint16(r.RClass)) bytes = binary.BigEndian.AppendUint32(bytes, r.TTL) rdataStart := len(bytes) bytes = binary.BigEndian.AppendUint16(bytes, 0) bytes, err = r.RData.Encode(bytes, offsets) if err != nil { return nil, fmt.Errorf("rr encode: failed to encode RData for %s (%s): %w", r.Name, r.RType.String(), err) } rdataLength := uint16(len(bytes) - rdataStart - 2) binary.BigEndian.PutUint16(bytes[rdataStart:rdataStart+2], rdataLength) return bytes, nil }