APL record support (#1058)

* APL record: add structure and code point

* APL record: add wire format support

* APL record: add presentation format support

* APL record: add isDuplicate implementation

* APL record: add copy implementation

* APL record: add len implementation

* APL record: run go generate

* APL record: fix condition checking for equality

* APL record: use switches to map family to address length

* APL record: check bounds of individual fields rather than whole header

* APL record: stylistic changes

* APL record: remove APLPrefix methods from public interface

* APL record: update README

* APL record: additional cleanup for code review

* APL record: change return type from pointer to struct

* APL record: refactor of pack and unpack to eliminate extra variables
This commit is contained in:
Jan Včelák 2020-01-03 13:41:45 +01:00 committed by Miek Gieben
parent e636c10380
commit c9b62b4215
12 changed files with 802 additions and 1 deletions

View File

@ -127,6 +127,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
* 2915 - NAPTR record
* 2929 - DNS IANA Considerations
* 3110 - RSASHA1 DNS keys
* 3123 - APL record
* 3225 - DO bit (DNSSEC OK)
* 340{1,2,3} - NAPTR record
* 3445 - Limiting the scope of (DNS)KEY

View File

@ -111,6 +111,16 @@ func main() {
continue
}
if st.Tag(i) == `dns:"apl"` {
o3(`for i := 0; i < len(r1.%s); i++ {
if !r1.%s[i].equals(&r2.%s[i]) {
return false
}
}`)
continue
}
o3(`for i := 0; i < len(r1.%s); i++ {
if r1.%s[i] != r2.%s[i] {
return false

View File

@ -112,6 +112,8 @@ return off, err
o("off, err = packDataNsec(rr.%s, msg, off)\n")
case `dns:"domain-name"`:
o("off, err = packDataDomainNames(rr.%s, msg, off, compression, false)\n")
case `dns:"apl"`:
o("off, err = packDataApl(rr.%s, msg, off)\n")
default:
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
}
@ -236,6 +238,8 @@ return off, err
o("rr.%s, off, err = unpackDataNsec(msg, off)\n")
case `dns:"domain-name"`:
o("rr.%s, off, err = unpackDataDomainNames(msg, off, rdStart + int(rr.Hdr.Rdlength))\n")
case `dns:"apl"`:
o("rr.%s, off, err = unpackDataApl(msg, off)\n")
default:
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
}

View File

@ -688,3 +688,123 @@ func packDataDomainNames(names []string, msg []byte, off int, compression compre
}
return off, nil
}
func packDataApl(data []APLPrefix, msg []byte, off int) (int, error) {
var err error
for i := range data {
off, err = packDataAplPrefix(&data[i], msg, off)
if err != nil {
return len(msg), err
}
}
return off, nil
}
func packDataAplPrefix(p *APLPrefix, msg []byte, off int) (int, error) {
if len(p.Network.IP) != len(p.Network.Mask) {
return len(msg), &Error{err: "address and mask lengths don't match"}
}
var err error
prefix, _ := p.Network.Mask.Size()
addr := p.Network.IP.Mask(p.Network.Mask)[:(prefix+7)/8]
switch len(p.Network.IP) {
case net.IPv4len:
off, err = packUint16(1, msg, off)
case net.IPv6len:
off, err = packUint16(2, msg, off)
default:
err = &Error{err: "unrecognized address family"}
}
if err != nil {
return len(msg), err
}
off, err = packUint8(uint8(prefix), msg, off)
if err != nil {
return len(msg), err
}
var n uint8
if p.Negation {
n = 0x80
}
adflen := uint8(len(addr)) & 0x7f
off, err = packUint8(n|adflen, msg, off)
if err != nil {
return len(msg), err
}
if off+len(addr) > len(msg) {
return len(msg), &Error{err: "overflow packing APL prefix"}
}
off += copy(msg[off:], addr)
return off, nil
}
func unpackDataApl(msg []byte, off int) ([]APLPrefix, int, error) {
var result []APLPrefix
for off < len(msg) {
prefix, end, err := unpackDataAplPrefix(msg, off)
if err != nil {
return nil, len(msg), err
}
off = end
result = append(result, prefix)
}
return result, off, nil
}
func unpackDataAplPrefix(msg []byte, off int) (APLPrefix, int, error) {
family, off, err := unpackUint16(msg, off)
if err != nil {
return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL prefix"}
}
prefix, off, err := unpackUint8(msg, off)
if err != nil {
return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL prefix"}
}
nlen, off, err := unpackUint8(msg, off)
if err != nil {
return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL prefix"}
}
var ip []byte
switch family {
case 1:
ip = make([]byte, net.IPv4len)
case 2:
ip = make([]byte, net.IPv6len)
default:
return APLPrefix{}, len(msg), &Error{err: "unrecognized APL address family"}
}
if int(prefix) > 8*len(ip) {
return APLPrefix{}, len(msg), &Error{err: "APL prefix too long"}
}
afdlen := int(nlen & 0x7f)
if (int(prefix)+7)/8 != afdlen {
return APLPrefix{}, len(msg), &Error{err: "invalid APL address length"}
}
if off+afdlen > len(msg) {
return APLPrefix{}, len(msg), &Error{err: "overflow unpacking APL address"}
}
off += copy(ip, msg[off:off+afdlen])
if prefix%8 > 0 {
last := ip[afdlen-1]
zero := uint8(0xff) >> (prefix % 8)
if last&zero > 0 {
return APLPrefix{}, len(msg), &Error{err: "extra APL address bits"}
}
}
return APLPrefix{
Negation: (nlen & 0x80) != 0,
Network: net.IPNet{
IP: ip,
Mask: net.CIDRMask(int(prefix), 8*len(ip)),
},
}, off, nil
}

View File

@ -1,6 +1,10 @@
package dns
import "testing"
import (
"bytes"
"net"
"testing"
)
// TestPacketDataNsec tests generated using fuzz.go and with a message pack
// containing the following bytes: 0000\x00\x00000000\x00\x002000000\x0060000\x00\x130000000000000000000"
@ -153,3 +157,309 @@ func BenchmarkUnpackString(b *testing.B) {
}
})
}
func TestPackDataAplPrefix(t *testing.T) {
tests := []struct {
name string
negation bool
ip net.IP
mask net.IPMask
expect []byte
}{
{
"1:192.0.2.0/24",
false,
net.ParseIP("192.0.2.0").To4(),
net.CIDRMask(24, 32),
[]byte{0x00, 0x01, 0x18, 0x03, 192, 0, 2},
},
{
"2:2001:db8:cafe::0/48",
false,
net.ParseIP("2001:db8:cafe::"),
net.CIDRMask(48, 128),
[]byte{0x00, 0x02, 0x30, 0x06, 0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe},
},
{
"!2:2001:db8::/32",
true,
net.ParseIP("2001:db8::"),
net.CIDRMask(32, 128),
[]byte{0x00, 0x02, 0x20, 0x84, 0x20, 0x01, 0x0d, 0xb8},
},
{
"normalize 1:198.51.103.255/22",
false,
net.ParseIP("198.51.103.255").To4(),
net.CIDRMask(22, 32),
[]byte{0x00, 0x01, 0x16, 0x03, 198, 51, 100}, // 1:198.51.100.0/22
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ap := &APLPrefix{
Negation: tt.negation,
Network: net.IPNet{IP: tt.ip, Mask: tt.mask},
}
out := make([]byte, 16)
off, err := packDataAplPrefix(ap, out, 0)
if err != nil {
t.Fatalf("expected no error, got %q", err)
}
if !bytes.Equal(tt.expect, out[:off]) {
t.Fatalf("expected output %02x, got %02x", tt.expect, out[:off])
}
})
}
}
func TestPackDataAplPrefix_Failures(t *testing.T) {
tests := []struct {
name string
ip net.IP
mask net.IPMask
}{
{
"family mismatch",
net.ParseIP("2001:db8::"),
net.CIDRMask(24, 32),
},
{
"unrecognized family",
[]byte{0x42},
[]byte{0xff},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ap := &APLPrefix{Network: net.IPNet{IP: tt.ip, Mask: tt.mask}}
msg := make([]byte, 16)
off, err := packDataAplPrefix(ap, msg, 0)
if err == nil {
t.Fatal("expected error, got none")
}
if off != len(msg) {
t.Fatalf("expected %d, got %d", len(msg), off)
}
})
}
}
func TestPackDataAplPrefix_BufferBounds(t *testing.T) {
ap := &APLPrefix{
Negation: false,
Network: net.IPNet{
IP: net.ParseIP("2001:db8::"),
Mask: net.CIDRMask(32, 128),
},
}
wire := []byte{0x00, 0x02, 0x20, 0x04, 0x20, 0x01, 0x0d, 0xb8}
t.Run("small", func(t *testing.T) {
msg := make([]byte, len(wire))
_, err := packDataAplPrefix(ap, msg, 1) // offset
if err == nil {
t.Fatal("expected error, got none")
}
})
t.Run("exact fit", func(t *testing.T) {
msg := make([]byte, len(wire))
off, err := packDataAplPrefix(ap, msg, 0)
if err != nil {
t.Fatalf("expected no error, got %q", err)
}
if !bytes.Equal(wire, msg[:off]) {
t.Fatalf("expected %02x, got %02x", wire, msg[:off])
}
})
}
func TestPackDataApl(t *testing.T) {
in := []APLPrefix{
APLPrefix{
Negation: true,
Network: net.IPNet{
IP: net.ParseIP("198.51.0.0").To4(),
Mask: net.CIDRMask(16, 32),
},
},
APLPrefix{
Negation: false,
Network: net.IPNet{
IP: net.ParseIP("2001:db8:beef::"),
Mask: net.CIDRMask(48, 128),
},
},
}
expect := []byte{
// 1:192.51.0.0/16
0x00, 0x01, 0x10, 0x82, 0xc6, 0x33,
// 2:2001:db8:beef::0/48
0x00, 0x02, 0x30, 0x06, 0x20, 0x01, 0x0d, 0xb8, 0xbe, 0xef,
}
msg := make([]byte, 32)
off, err := packDataApl(in, msg, 0)
if err != nil {
t.Fatalf("expected no error, got %q", err)
}
if !bytes.Equal(expect, msg[:off]) {
t.Fatalf("expected %02x, got %02x", expect, msg[:off])
}
}
func TestUnpackDataAplPrefix(t *testing.T) {
tests := []struct {
name string
wire []byte
negation bool
ip net.IP
mask net.IPMask
}{
{
"1:192.0.2.0/24",
[]byte{0x00, 0x01, 0x18, 0x03, 192, 0, 2},
false,
net.ParseIP("192.0.2.0").To4(),
net.CIDRMask(24, 32),
},
{
"2:2001:db8::/32",
[]byte{0x00, 0x02, 0x20, 0x04, 0x20, 0x01, 0x0d, 0xb8},
false,
net.ParseIP("2001:db8::"),
net.CIDRMask(32, 128),
},
{
"!2:2001:db8:8000::/33",
[]byte{0x00, 0x02, 0x21, 0x85, 0x20, 0x01, 0x0d, 0xb8, 0x80},
true,
net.ParseIP("2001:db8:8000::"),
net.CIDRMask(33, 128),
},
{
"1:0.0.0.0/0",
[]byte{0x00, 0x01, 0x00, 0x00},
false,
net.ParseIP("0.0.0.0").To4(),
net.CIDRMask(0, 32),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, off, err := unpackDataAplPrefix(tt.wire, 0)
if err != nil {
t.Fatalf("expected no error, got %q", err)
}
if off != len(tt.wire) {
t.Fatalf("expected offset %d, got %d", len(tt.wire), off)
}
if got.Negation != tt.negation {
t.Errorf("expected negation %v, got %v", tt.negation, got.Negation)
}
if !bytes.Equal(got.Network.IP, tt.ip) {
t.Errorf("expected IP %02x, got %02x", tt.ip, got.Network.IP)
}
if !bytes.Equal(got.Network.Mask, tt.mask) {
t.Errorf("expected mask %02x, got %02x", tt.mask, got.Network.Mask)
}
})
}
}
func TestUnpackDataAplPrefix_Errors(t *testing.T) {
tests := []struct {
name string
wire []byte
}{
{
"incomplete header",
[]byte{0x00, 0x01, 0x18},
},
{
"unrecognized family",
[]byte{0x00, 0x03, 0x00, 0x00},
},
{
"prefix length exceeded",
[]byte{0x00, 0x01, 0x21, 0x04, 192, 0, 2, 0},
},
{
"address with extra byte",
[]byte{0x00, 0x01, 0x10, 0x03, 192, 0, 2},
},
{
"incomplete buffer",
[]byte{0x00, 0x01, 0x10, 0x02, 192},
},
{
"extra bits set",
[]byte{0x00, 0x01, 22, 0x03, 192, 0, 2},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, _, err := unpackDataAplPrefix(tt.wire, 0)
if err == nil {
t.Fatal("expected error, got none")
}
})
}
}
func TestUnpackDataApl(t *testing.T) {
wire := []byte{
// 2:2001:db8:cafe:4200:0/56
0x00, 0x02, 0x38, 0x07, 0x20, 0x01, 0x0d, 0xb8, 0xca, 0xfe, 0x42,
// 1:192.0.2.0/24
0x00, 0x01, 0x18, 0x03, 192, 0, 2,
// !1:192.0.2.128/25
0x00, 0x01, 0x19, 0x84, 192, 0, 2, 128,
}
expect := []APLPrefix{
{
Negation: false,
Network: net.IPNet{
IP: net.ParseIP("2001:db8:cafe:4200::"),
Mask: net.CIDRMask(56, 128),
},
},
{
Negation: false,
Network: net.IPNet{
IP: net.ParseIP("192.0.2.0").To4(),
Mask: net.CIDRMask(24, 32),
},
},
{
Negation: true,
Network: net.IPNet{
IP: net.ParseIP("192.0.2.128").To4(),
Mask: net.CIDRMask(25, 32),
},
},
}
got, off, err := unpackDataApl(wire, 0)
if err != nil {
t.Fatalf("expected no error, got %q", err)
}
if off != len(wire) {
t.Fatalf("expected offset %d, got %d", len(wire), off)
}
if len(got) != len(expect) {
t.Fatalf("expected %d prefixes, got %d", len(expect), len(got))
}
for i, exp := range expect {
if got[i].Negation != exp.Negation {
t.Errorf("[%d] expected negation %v, got %v", i, exp.Negation, got[i].Negation)
}
if !bytes.Equal(got[i].Network.IP, exp.Network.IP) {
t.Errorf("[%d] expected IP %02x, got %02x", i, exp.Network.IP, got[i].Network.IP)
}
if !bytes.Equal(got[i].Network.Mask, exp.Network.Mask) {
t.Errorf("[%d] expected mask %02x, got %02x", i, exp.Network.Mask, got[i].Network.Mask)
}
}
}

View File

@ -1584,3 +1584,137 @@ func TestNULLRecord(t *testing.T) {
t.Fatalf("Expected packet to contain NULL record")
}
}
func TestParseAPL(t *testing.T) {
tests := []struct {
name string
in string
expect string
}{
{
"v4",
". APL 1:192.0.2.0/24",
".\t3600\tIN\tAPL\t1:192.0.2.0/24",
},
{
"v6",
". APL 2:2001:db8::/32",
".\t3600\tIN\tAPL\t2:2001:db8::/32",
},
{
"null v6",
". APL 2:::/0",
".\t3600\tIN\tAPL\t2:::/0",
},
{
"null v4",
". APL 1:0.0.0.0/0",
".\t3600\tIN\tAPL\t1:0.0.0.0/0",
},
{
"full v6",
". APL 2:::/0",
".\t3600\tIN\tAPL\t2:::/0",
},
{
"full v4",
". APL 1:192.0.2.1/32",
".\t3600\tIN\tAPL\t1:192.0.2.1/32",
},
{
"full v6",
". APL 2:2001:0db8:d2b4:b6ba:50db:49cc:a8d1:5bb1/128",
".\t3600\tIN\tAPL\t2:2001:db8:d2b4:b6ba:50db:49cc:a8d1:5bb1/128",
},
{
"v4in6",
". APL 2:::ffff:192.0.2.0/120",
".\t3600\tIN\tAPL\t2:::ffff:192.0.2.0/120",
},
{
"v4in6 v6 syntax",
". APL 2:::ffff:c000:0200/120",
".\t3600\tIN\tAPL\t2:::ffff:192.0.2.0/120",
},
{
"negate",
". APL !1:192.0.2.0/24",
".\t3600\tIN\tAPL\t!1:192.0.2.0/24",
},
{
"multiple",
". APL 1:192.0.2.0/24 !1:192.0.2.1/32 2:2001:db8::/32 !2:2001:db8:1::0/48",
".\t3600\tIN\tAPL\t1:192.0.2.0/24 !1:192.0.2.1/32 2:2001:db8::/32 !2:2001:db8:1::/48",
},
{
"no address",
". APL",
".\t3600\tIN\tAPL\t",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
rr, err := NewRR(tc.in)
if err != nil {
t.Fatalf("failed to parse RR: %s", err)
}
got := rr.String()
if got != tc.expect {
t.Errorf("expected %q, got %q", tc.expect, got)
}
})
}
}
func TestParseAPLErrors(t *testing.T) {
tests := []struct {
name string
in string
}{
{
"unexpected",
`. APL ""`,
},
{
"unrecognized family",
". APL 3:0.0.0.0/0",
},
{
"malformed family",
". APL foo:0.0.0.0/0",
},
{
"malformed address",
". APL 1:192.0.2/16",
},
{
"extra bits",
". APL 2:2001:db8::/0",
},
{
"address mismatch v2",
". APL 1:2001:db8::/64",
},
{
"address mismatch v6",
". APL 2:192.0.2.1/32",
},
{
"no prefix",
". APL 1:192.0.2.1",
},
{
"no family",
". APL 0.0.0.0/0",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
_, err := NewRR(tc.in)
if err == nil {
t.Fatal("expected error, got none")
}
})
}
}

View File

@ -1696,3 +1696,69 @@ func (rr *TKEY) parse(c *zlexer, o string) *ParseError {
return nil
}
func (rr *APL) parse(c *zlexer, o string) *ParseError {
var prefixes []APLPrefix
for {
l, _ := c.Next()
if l.value == zNewline || l.value == zEOF {
break
}
if l.value == zBlank && prefixes != nil {
continue
}
if l.value != zString {
return &ParseError{"", "unexpected APL field", l}
}
// Expected format: [!]afi:address/prefix
colon := strings.IndexByte(l.token, ':')
if colon == -1 {
return &ParseError{"", "missing colon in APL field", l}
}
family, cidr := l.token[:colon], l.token[colon+1:]
var negation bool
if family != "" && family[0] == '!' {
negation = true
family = family[1:]
}
afi, err := strconv.ParseUint(family, 10, 16)
if err != nil {
return &ParseError{"", "failed to parse APL family: " + err.Error(), l}
}
var addrLen int
switch afi {
case 1:
addrLen = net.IPv4len
case 2:
addrLen = net.IPv6len
default:
return &ParseError{"", "unrecognized APL family", l}
}
ip, subnet, err := net.ParseCIDR(cidr)
if err != nil {
return &ParseError{"", "failed to parse APL address: " + err.Error(), l}
}
if !ip.Equal(subnet.IP) {
return &ParseError{"", "extra bits in APL address", l}
}
if len(subnet.IP) != addrLen {
return &ParseError{"", "address mismatch with the APL family", l}
}
prefixes = append(prefixes, APLPrefix{
Negation: negation,
Network: *subnet,
})
}
rr.Prefixes = prefixes
return nil
}

View File

@ -1,6 +1,7 @@
package dns
import (
"bytes"
"fmt"
"net"
"strconv"
@ -61,6 +62,7 @@ const (
TypeCERT uint16 = 37
TypeDNAME uint16 = 39
TypeOPT uint16 = 41 // EDNS
TypeAPL uint16 = 42
TypeDS uint16 = 43
TypeSSHFP uint16 = 44
TypeRRSIG uint16 = 46
@ -1353,6 +1355,88 @@ func (rr *CSYNC) len(off int, compression map[string]struct{}) int {
return l
}
// APL RR. See RFC 3123.
type APL struct {
Hdr RR_Header
Prefixes []APLPrefix `dns:"apl"`
}
// APLPrefix is an address prefix hold by an APL record.
type APLPrefix struct {
Negation bool
Network net.IPNet
}
// String returns presentation form of the APL record.
func (rr *APL) String() string {
var sb strings.Builder
sb.WriteString(rr.Hdr.String())
for i, p := range rr.Prefixes {
if i > 0 {
sb.WriteByte(' ')
}
sb.WriteString(p.str())
}
return sb.String()
}
// str returns presentation form of the APL prefix.
func (p *APLPrefix) str() string {
var sb strings.Builder
if p.Negation {
sb.WriteByte('!')
}
switch len(p.Network.IP) {
case net.IPv4len:
sb.WriteByte('1')
case net.IPv6len:
sb.WriteByte('2')
}
sb.WriteByte(':')
switch len(p.Network.IP) {
case net.IPv4len:
sb.WriteString(p.Network.IP.String())
case net.IPv6len:
// add prefix for IPv4-mapped IPv6
if v4 := p.Network.IP.To4(); v4 != nil {
sb.WriteString("::ffff:")
}
sb.WriteString(p.Network.IP.String())
}
sb.WriteByte('/')
prefix, _ := p.Network.Mask.Size()
sb.WriteString(strconv.Itoa(prefix))
return sb.String()
}
// equals reports whether two APL prefixes are identical.
func (a *APLPrefix) equals(b *APLPrefix) bool {
return a.Negation == b.Negation &&
bytes.Equal(a.Network.IP, b.Network.IP) &&
bytes.Equal(a.Network.Mask, b.Network.Mask)
}
// copy returns a copy of the APL prefix.
func (p *APLPrefix) copy() APLPrefix {
return APLPrefix{
Negation: p.Negation,
Network: copyNet(p.Network),
}
}
// len returns size of the prefix in wire format.
func (p *APLPrefix) len() int {
// 4-byte header and the network address prefix (see Section 4 of RFC 3123)
prefix, _ := p.Network.Mask.Size()
return 4 + (prefix+7)/8
}
// TimeToString translates the RRSIG's incep. and expir. times to the
// string representation used when printing the record.
// It takes serial arithmetic (RFC 1982) into account.
@ -1409,6 +1493,17 @@ func copyIP(ip net.IP) net.IP {
return p
}
// copyNet returns a copy of a subnet.
func copyNet(n net.IPNet) net.IPNet {
m := make(net.IPMask, len(n.Mask))
copy(m, n.Mask)
return net.IPNet{
IP: copyIP(n.IP),
Mask: m,
}
}
// SplitN splits a string into N sized string chunks.
// This might become an exported function once.
func splitN(s string, n int) []string {

View File

@ -179,6 +179,8 @@ func main() {
o("for _, x := range rr.%s { l += domainNameLen(x, off+l, compression, false) }\n")
case `dns:"txt"`:
o("for _, x := range rr.%s { l += len(x) + 1 }\n")
case `dns:"apl"`:
o("for _, x := range rr.%s { l += x.len() }\n")
default:
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
}
@ -260,6 +262,12 @@ func main() {
fields = append(fields, f)
continue
}
if t == "APLPrefix" {
fmt.Fprintf(b, "%s := make([]%s, len(rr.%s));\nfor i := range rr.%s {\n %s[i] = rr.%s[i].copy()\n}\n",
f, t, f, f, f, f)
fields = append(fields, f)
continue
}
fmt.Fprintf(b, "%s := make([]%s, len(rr.%s)); copy(%s, rr.%s)\n",
f, t, f, f, f)
fields = append(fields, f)

View File

@ -52,6 +52,23 @@ func (r1 *ANY) isDuplicate(_r2 RR) bool {
return true
}
func (r1 *APL) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*APL)
if !ok {
return false
}
_ = r2
if len(r1.Prefixes) != len(r2.Prefixes) {
return false
}
for i := 0; i < len(r1.Prefixes); i++ {
if !r1.Prefixes[i].equals(&r2.Prefixes[i]) {
return false
}
}
return true
}
func (r1 *AVC) isDuplicate(_r2 RR) bool {
r2, ok := _r2.(*AVC)
if !ok {

19
zmsg.go
View File

@ -36,6 +36,14 @@ func (rr *ANY) pack(msg []byte, off int, compression compressionMap, compress bo
return off, nil
}
func (rr *APL) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
off, err = packDataApl(rr.Prefixes, msg, off)
if err != nil {
return off, err
}
return off, nil
}
func (rr *AVC) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
off, err = packStringTxt(rr.Txt, msg, off)
if err != nil {
@ -1127,6 +1135,17 @@ func (rr *ANY) unpack(msg []byte, off int) (off1 int, err error) {
return off, nil
}
func (rr *APL) unpack(msg []byte, off int) (off1 int, err error) {
rdStart := off
_ = rdStart
rr.Prefixes, off, err = unpackDataApl(msg, off)
if err != nil {
return off, err
}
return off, nil
}
func (rr *AVC) unpack(msg []byte, off int) (off1 int, err error) {
rdStart := off
_ = rdStart

View File

@ -13,6 +13,7 @@ var TypeToRR = map[uint16]func() RR{
TypeAAAA: func() RR { return new(AAAA) },
TypeAFSDB: func() RR { return new(AFSDB) },
TypeANY: func() RR { return new(ANY) },
TypeAPL: func() RR { return new(APL) },
TypeAVC: func() RR { return new(AVC) },
TypeCAA: func() RR { return new(CAA) },
TypeCDNSKEY: func() RR { return new(CDNSKEY) },
@ -87,6 +88,7 @@ var TypeToString = map[uint16]string{
TypeAAAA: "AAAA",
TypeAFSDB: "AFSDB",
TypeANY: "ANY",
TypeAPL: "APL",
TypeATMA: "ATMA",
TypeAVC: "AVC",
TypeAXFR: "AXFR",
@ -169,6 +171,7 @@ func (rr *A) Header() *RR_Header { return &rr.Hdr }
func (rr *AAAA) Header() *RR_Header { return &rr.Hdr }
func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr }
func (rr *ANY) Header() *RR_Header { return &rr.Hdr }
func (rr *APL) Header() *RR_Header { return &rr.Hdr }
func (rr *AVC) Header() *RR_Header { return &rr.Hdr }
func (rr *CAA) Header() *RR_Header { return &rr.Hdr }
func (rr *CDNSKEY) Header() *RR_Header { return &rr.Hdr }
@ -262,6 +265,13 @@ func (rr *ANY) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression)
return l
}
func (rr *APL) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression)
for _, x := range rr.Prefixes {
l += x.len()
}
return l
}
func (rr *AVC) len(off int, compression map[string]struct{}) int {
l := rr.Hdr.len(off, compression)
for _, x := range rr.Txt {
@ -673,6 +683,13 @@ func (rr *AFSDB) copy() RR {
func (rr *ANY) copy() RR {
return &ANY{rr.Hdr}
}
func (rr *APL) copy() RR {
Prefixes := make([]APLPrefix, len(rr.Prefixes))
for i := range rr.Prefixes {
Prefixes[i] = rr.Prefixes[i].copy()
}
return &APL{rr.Hdr, Prefixes}
}
func (rr *AVC) copy() RR {
Txt := make([]string, len(rr.Txt))
copy(Txt, rr.Txt)