Skip to content

Commit

Permalink
Merge pull request #1 from TochusC:edns
Browse files Browse the repository at this point in the history
Add support for DNS RDATA OPT with encoding and decoding features
  • Loading branch information
TochusC authored Dec 12, 2024
2 parents 44d2ee6 + b0f94c1 commit 3322f31
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 0 deletions.
3 changes: 3 additions & 0 deletions dns/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,9 @@ func (rr *DNSResourceRecord) Size() int {
// String 以*易读的形式*返回 DNS 资源记录的字符串表示。
// - 其返回值为 DNS 资源记录的字符串表示。
func (rr *DNSResourceRecord) String() string {
if IsPersudoRR(rr) {
return NewPersudoRR(rr).String()
}
return fmt.Sprint(
"### DNS Resource Record ###\n",
"Name:", rr.Name, "\n",
Expand Down
87 changes: 87 additions & 0 deletions dns/metarr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package dns

import (
"encoding/binary"
"fmt"
)

var PersudoRRType = map[DNSType]interface{}{
DNSRRTypeOPT: nil,
}

func IsPersudoRR(rr *DNSResourceRecord) bool {
_, ok := PersudoRRType[rr.Type]
return ok
}

// DNSRROPT is a DNS Resource Record OPT
// See RFC 6891

// SetDNSRROPTTTL sets the TTL of the OPT RR
// The TTL is encoded as follows:
// (MSB) +1 (LSB)
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | EXTENDED-RCODE | VERSION |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | DO| Z |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
func SetDNSRROPTTTL(ercode int, version int, do bool, z int) uint32 {
var ttl [4]byte
ttl[0] = uint8(ercode)
ttl[1] = uint8(version)
binary.BigEndian.PutUint16(ttl[2:], uint16(z))
if do {
ttl[2] |= 0x80
}
return binary.BigEndian.Uint32(ttl[:])
}

// NewDNSRROPT creates a new DNS Resource Record OPT
func NewDNSRROPT(udpsize int, ttl int, rdata *DNSRDATAOPT) *DNSResourceRecord {
return &DNSResourceRecord{
Name: ".",
Type: 41,
Class: DNSClass(udpsize),
TTL: uint32(ttl),
RDLen: uint16(rdata.Size()),
RData: rdata,
}
}

type PersudoRR interface {
String() string
}

func NewPersudoRR(rr *DNSResourceRecord) PersudoRR {
switch rr.Type {
case DNSRRTypeOPT:
return &DNSRROPT{rr}
default:
return nil
}
}

type DNSRROPT struct {
rr *DNSResourceRecord
}

func (opt *DNSRROPT) String() string {
rr := opt.rr
ttl := rr.TTL
bTTL := make([]byte, 4)
binary.BigEndian.PutUint32(bTTL, ttl)
ercode := int(ttl >> 24)
version := int(ttl >> 16 & 0xff)
do := (ttl>>15)&1 == 1
z := int(ttl & 0x7fff)

return fmt.Sprint(
"### Persudo Rersouce Record OPT ###\n",
"UDP Payload Size:", int(rr.Class), "\n",
"Extended RCODE:", ercode, "\n",
"Version:", version, "\n",
"DO:", do, "\n",
"Z: ", z, "\n",
"RData:\n", rr.RData.String(),
)
}
19 changes: 19 additions & 0 deletions dns/metarr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dns

import "testing"

func TestPersudoRRString(t *testing.T) {
rdata := DNSRDATAOPT{
OptionCode: 0,
OptionLength: 4,
OptionData: []byte{0x00, 0x01, 0x02, 0x03},
}

rr := NewDNSRROPT(1024,
int(SetDNSRROPTTTL(0, 41, true, 0)),
&rdata,
)

prr := NewPersudoRR(rr)
t.Logf("PersudoRR String():\n%s", prr.String())
}
88 changes: 88 additions & 0 deletions dns/rdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ func DNSRRRDATAFactory(rtype DNSType) DNSRRRDATA {
return &DNSRDATACNAME{}
case DNSRRTypeTXT:
return &DNSRDATATXT{}
case DNSRRTypeRRSIG:
return &DNSRDATARRSIG{}
case DNSRRTypeDNSKEY:
return &DNSRDATADNSKEY{}
case DNSRRTypeNSEC:
return &DNSRDATANSEC{}
case DNSRRTypeDS:
return &DNSRDATADS{}
case DNSRRTypeOPT:
return &DNSRDATAOPT{}
default:
return &DNSRDATAUnknown{
RRType: rtype,
Expand Down Expand Up @@ -796,3 +806,81 @@ func (rdata *DNSRDATADS) DecodeFromBuffer(buffer []byte, offset int, rdLen int)
copy(rdata.Digest, buffer[offset+4:rdEnd])
return rdEnd, nil
}

// DNSKEY RDATA 编码格式
// 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
// +0 (MSB) +1 (LSB)
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | OPTION-CODE |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | OPTION-LENGTH |
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
// | |
// / OPTION-DATA /
// / /
// +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
type DNSRDATAOPT struct {
OptionCode uint16
OptionLength uint16
OptionData []byte
}

func (rdata *DNSRDATAOPT) Type() DNSType {
return DNSRRTypeOPT
}

func (rdata *DNSRDATAOPT) Size() int {
return 4 + len(rdata.OptionData)
}

func (rdata *DNSRDATAOPT) String() string {
return fmt.Sprint(
"### RDATA Section ###\n",
"Option Code: ", rdata.OptionCode,
"\nOption Length: ", rdata.OptionLength,
"\nOption Data: ", rdata.OptionData,
)
}

func (rdata *DNSRDATAOPT) Equal(rr DNSRRRDATA) bool {
rropt, ok := rr.(*DNSRDATAOPT)
if !ok {
return false
}
return rdata.OptionCode == rropt.OptionCode &&
rdata.OptionLength == rropt.OptionLength &&
bytes.Equal(rdata.OptionData, rropt.OptionData)
}

func (rdata *DNSRDATAOPT) Encode() []byte {
bytesArray := make([]byte, rdata.Size())
binary.BigEndian.PutUint16(bytesArray, rdata.OptionCode)
binary.BigEndian.PutUint16(bytesArray[2:], rdata.OptionLength)
copy(bytesArray[4:], rdata.OptionData)
return bytesArray
}

func (rdata *DNSRDATAOPT) EncodeToBuffer(buffer []byte) (int, error) {
if len(buffer) < rdata.Size() {
return -1, fmt.Errorf("method DNSRDATAOPT EncodeToBuffer failed: buffer length %d is less than OPT RDATA size %d", len(buffer), rdata.Size())
}
binary.BigEndian.PutUint16(buffer, rdata.OptionCode)
binary.BigEndian.PutUint16(buffer[2:], rdata.OptionLength)
copy(buffer[4:], rdata.OptionData)
return rdata.Size(), nil
}

func (rdata *DNSRDATAOPT) DecodeFromBuffer(buffer []byte, offset int, rdLen int) (int, error) {
rdEnd := offset + rdLen
if rdLen < 4 {
return -1, fmt.Errorf("method DNSRDATAOPT DecodeFromBuffer failed: OPT RDATA size %d is less than 4", rdLen)
}
if len(buffer) < rdEnd {
return -1, fmt.Errorf("method DNSRDATAOPT DecodeFromBuffer failed: buffer length %d is less than offset %d + OPT RDATA size %d", len(buffer), offset, rdata.Size())
}
rdata.OptionCode = binary.BigEndian.Uint16(buffer[offset:])
rdata.OptionLength = binary.BigEndian.Uint16(buffer[offset+2:])
rdata.OptionData = make([]byte, rdLen-4)
copy(rdata.OptionData, buffer[offset+4:rdEnd])
return rdEnd, nil
}
37 changes: 37 additions & 0 deletions dns/rdata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -654,3 +654,40 @@ func TestDNSRDATADSDecodeFromBuffer(t *testing.T) {
t.Error("function DNSRDATADSDecodeFromBuffer() failed: expected an error but got nil")
}
}

func TestDNSRDATAOPTEncode(t *testing.T) {
opt := DNSRDATAOPT{
OptionCode: 0,
OptionLength: 4,
OptionData: []byte{0x00, 0x01, 0x02, 0x03},
}
encoded := opt.Encode()
expected := []byte{
0x00, 0x00, 0x00, 0x04,
0x00, 0x01, 0x02, 0x03,
}
if !bytes.Equal(encoded, expected) {
t.Errorf("function DNSRDATAOPT.Encode() failed:\ngot:\n%v\nexpected:\n%v",
encoded, expected)
}
}

func TestDNSRDATAOPTDecodeFromBuffer(t *testing.T) {
encoded := []byte{
0x00, 0x00, 0x00, 0x04,
0x00, 0x01, 0x02, 0x03,
}
opt := DNSRDATAOPT{}
_, err := opt.DecodeFromBuffer(encoded, 0, len(encoded))
if err != nil {
t.Errorf("function DNSRDATAOPT.DecodeFromBuffer() failed:\n%s", err)
}
if opt.OptionCode != 0 || opt.OptionLength != 4 || !bytes.Equal(opt.OptionData, []byte{0x00, 0x01, 0x02, 0x03}) {
t.Errorf("function DNSRDATAOPT.DecodeFromBuffer() failed:\ngot:\n%v\nexpected:\n%v",
opt, DNSRDATAOPT{
OptionCode: 0,
OptionLength: 4,
OptionData: []byte{0x00, 0x01, 0x02, 0x03},
})
}
}

0 comments on commit 3322f31

Please sign in to comment.