From 302b9c00d04fb3d5f9d60c9146d7eaf1efd34022 Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Tue, 10 Apr 2018 02:56:43 +0200
Subject: [PATCH 01/12] Fixed len computation when size just goes beyond 14
bits
---
length_test.go | 19 +++++++++++++++++++
msg.go | 2 +-
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/length_test.go b/length_test.go
index 5324c22c9..31c279619 100644
--- a/length_test.go
+++ b/length_test.go
@@ -172,3 +172,22 @@ func TestMsgCompressLengthLargeRecords(t *testing.T) {
t.Fatalf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d", msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
}
}
+
+func TestMsgCompressLengthLargeRecordsAllValues(t *testing.T) {
+ msg := new(Msg)
+ msg.Compress = true
+ msg.SetQuestion("redis.service.consul.", TypeSRV)
+ for i := 0; i < 900; i++ {
+ target := fmt.Sprintf("host-redis-%d-%d.test.acme.com.node.dc1.consul.", i/256, i%256)
+ msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
+ msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: 1, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.%d.%d.", i/256, i%256)})
+ predicted := msg.Len()
+ buf, err := msg.Pack()
+ if err != nil {
+ t.Error(err)
+ }
+ if predicted != len(buf) {
+ t.Fatalf("predicted compressed length is wrong for %d records: predicted %s (len=%d) %d, actual %d", i, msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
+ }
+ }
+}
diff --git a/msg.go b/msg.go
index 276e6b027..b58b326bc 100644
--- a/msg.go
+++ b/msg.go
@@ -971,7 +971,6 @@ func compressionLenSlice(len int, c map[string]int, rs []RR) int {
// track this length, and the global length in len, while taking compression into account for both.
x := r.len()
l += x
- len += x
k, ok := compressionLenSearch(c, r.Header().Name)
if ok {
@@ -992,6 +991,7 @@ func compressionLenSlice(len int, c map[string]int, rs []RR) int {
if len < maxCompressionOffset {
compressionLenHelperType(c, r)
}
+ len += x
}
return l
}
From 6c75d8245382ddce282554459de38fee1c425330 Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Tue, 10 Apr 2018 19:06:46 +0200
Subject: [PATCH 02/12] Added bouds checks around 14bits
---
length_test.go | 24 ++++++++++++++++++
msg.go | 30 ++++++++++++++--------
zcompress.go | 68 +++++++++++++++++++++++++-------------------------
3 files changed, 77 insertions(+), 45 deletions(-)
diff --git a/length_test.go b/length_test.go
index 31c279619..bd5fd97cf 100644
--- a/length_test.go
+++ b/length_test.go
@@ -4,6 +4,7 @@ import (
"encoding/hex"
"fmt"
"net"
+ "strings"
"testing"
)
@@ -173,6 +174,29 @@ func TestMsgCompressLengthLargeRecords(t *testing.T) {
}
}
+func TestMsgCompressLengthLargeRecordsWithPaddingPermutation(t *testing.T) {
+ msg := new(Msg)
+ msg.Compress = true
+ msg.SetQuestion("my.service.acme.", TypeSRV)
+
+ for i := 0; i < 250; i++ {
+ target := fmt.Sprintf("host-redis-x-%d.test.acme.com.node.dc1.consul.", i)
+ msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
+ msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: 1, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.x.%d.", i)})
+ }
+ for labelSize := 1; labelSize < 63; labelSize++ {
+ msg.SetQuestion(fmt.Sprintf("my.%s.service.acme.", strings.Repeat("x", labelSize)), TypeSRV)
+ predicted := msg.Len()
+ buf, err := msg.Pack()
+ if err != nil {
+ t.Error(err)
+ }
+ if predicted != len(buf) {
+ t.Fatalf("padding= %d ; predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d", labelSize, msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
+ }
+ }
+}
+
func TestMsgCompressLengthLargeRecordsAllValues(t *testing.T) {
msg := new(Msg)
msg.Compress = true
diff --git a/msg.go b/msg.go
index b58b326bc..d8a69fc59 100644
--- a/msg.go
+++ b/msg.go
@@ -931,7 +931,7 @@ func compressedLen(dns *Msg, compress bool) int {
compression := map[string]int{}
for _, r := range dns.Question {
l += r.len()
- compressionLenHelper(compression, r.Name)
+ compressionLenHelper(compression, r.Name, l)
}
l += compressionLenSlice(l, compression, dns.Answer)
l += compressionLenSlice(l, compression, dns.Ns)
@@ -962,7 +962,7 @@ func compressedLen(dns *Msg, compress bool) int {
return l
}
-func compressionLenSlice(len int, c map[string]int, rs []RR) int {
+func compressionLenSlice(lenp int, c map[string]int, rs []RR) int {
var l int
for _, r := range rs {
if r == nil {
@@ -970,40 +970,48 @@ func compressionLenSlice(len int, c map[string]int, rs []RR) int {
}
// track this length, and the global length in len, while taking compression into account for both.
x := r.len()
+ initLen := lenp
l += x
+ compressedSize := 0
k, ok := compressionLenSearch(c, r.Header().Name)
if ok {
l += 1 - k
- len += 1 - k
+ lenp += 1 - k
+ compressedSize = len(r.Header().Name) - k + 1
}
- if len < maxCompressionOffset {
- compressionLenHelper(c, r.Header().Name)
+ if initLen+compressedSize < maxCompressionOffset {
+ compressionLenHelper(c, r.Header().Name, initLen+compressedSize)
}
k, ok = compressionLenSearchType(c, r)
if ok {
l += 1 - k
- len += 1 - k
+ lenp += 1 - k
+ compressedSize += len(r.Header().Name) - k + 1
}
- if len < maxCompressionOffset {
- compressionLenHelperType(c, r)
+ if initLen+compressedSize < maxCompressionOffset {
+ compressionLenHelperType(c, r, initLen+compressedSize)
}
- len += x
+ lenp += x
}
return l
}
// Put the parts of the name in the compression map.
-func compressionLenHelper(c map[string]int, s string) {
+func compressionLenHelper(c map[string]int, s string, currentLen int) {
pref := ""
lbs := Split(s)
for j := len(lbs) - 1; j >= 0; j-- {
pref = s[lbs[j]:]
if _, ok := c[pref]; !ok {
- c[pref] = len(pref)
+ lenAdded := len(pref)
+ offsetOfLabel := currentLen + len(s) - len(pref) + 6
+ if offsetOfLabel < maxCompressionOffset {
+ c[pref] = lenAdded
+ }
}
}
}
diff --git a/zcompress.go b/zcompress.go
index c2503204d..a75bb38b7 100644
--- a/zcompress.go
+++ b/zcompress.go
@@ -2,71 +2,71 @@
package dns
-func compressionLenHelperType(c map[string]int, r RR) {
+func compressionLenHelperType(c map[string]int, r RR, currentLen int) {
switch x := r.(type) {
case *AFSDB:
- compressionLenHelper(c, x.Hostname)
+ compressionLenHelper(c, x.Hostname, currentLen)
case *CNAME:
- compressionLenHelper(c, x.Target)
+ compressionLenHelper(c, x.Target, currentLen)
case *DNAME:
- compressionLenHelper(c, x.Target)
+ compressionLenHelper(c, x.Target, currentLen)
case *HIP:
for i := range x.RendezvousServers {
- compressionLenHelper(c, x.RendezvousServers[i])
+ compressionLenHelper(c, x.RendezvousServers[i], currentLen)
}
case *KX:
- compressionLenHelper(c, x.Exchanger)
+ compressionLenHelper(c, x.Exchanger, currentLen)
case *LP:
- compressionLenHelper(c, x.Fqdn)
+ compressionLenHelper(c, x.Fqdn, currentLen)
case *MB:
- compressionLenHelper(c, x.Mb)
+ compressionLenHelper(c, x.Mb, currentLen)
case *MD:
- compressionLenHelper(c, x.Md)
+ compressionLenHelper(c, x.Md, currentLen)
case *MF:
- compressionLenHelper(c, x.Mf)
+ compressionLenHelper(c, x.Mf, currentLen)
case *MG:
- compressionLenHelper(c, x.Mg)
+ compressionLenHelper(c, x.Mg, currentLen)
case *MINFO:
- compressionLenHelper(c, x.Rmail)
- compressionLenHelper(c, x.Email)
+ compressionLenHelper(c, x.Rmail, currentLen)
+ compressionLenHelper(c, x.Email, currentLen)
case *MR:
- compressionLenHelper(c, x.Mr)
+ compressionLenHelper(c, x.Mr, currentLen)
case *MX:
- compressionLenHelper(c, x.Mx)
+ compressionLenHelper(c, x.Mx, currentLen)
case *NAPTR:
- compressionLenHelper(c, x.Replacement)
+ compressionLenHelper(c, x.Replacement, currentLen)
case *NS:
- compressionLenHelper(c, x.Ns)
+ compressionLenHelper(c, x.Ns, currentLen)
case *NSAPPTR:
- compressionLenHelper(c, x.Ptr)
+ compressionLenHelper(c, x.Ptr, currentLen)
case *NSEC:
- compressionLenHelper(c, x.NextDomain)
+ compressionLenHelper(c, x.NextDomain, currentLen)
case *PTR:
- compressionLenHelper(c, x.Ptr)
+ compressionLenHelper(c, x.Ptr, currentLen)
case *PX:
- compressionLenHelper(c, x.Map822)
- compressionLenHelper(c, x.Mapx400)
+ compressionLenHelper(c, x.Map822, currentLen)
+ compressionLenHelper(c, x.Mapx400, currentLen)
case *RP:
- compressionLenHelper(c, x.Mbox)
- compressionLenHelper(c, x.Txt)
+ compressionLenHelper(c, x.Mbox, currentLen)
+ compressionLenHelper(c, x.Txt, currentLen)
case *RRSIG:
- compressionLenHelper(c, x.SignerName)
+ compressionLenHelper(c, x.SignerName, currentLen)
case *RT:
- compressionLenHelper(c, x.Host)
+ compressionLenHelper(c, x.Host, currentLen)
case *SIG:
- compressionLenHelper(c, x.SignerName)
+ compressionLenHelper(c, x.SignerName, currentLen)
case *SOA:
- compressionLenHelper(c, x.Ns)
- compressionLenHelper(c, x.Mbox)
+ compressionLenHelper(c, x.Ns, currentLen)
+ compressionLenHelper(c, x.Mbox, currentLen)
case *SRV:
- compressionLenHelper(c, x.Target)
+ compressionLenHelper(c, x.Target, currentLen)
case *TALINK:
- compressionLenHelper(c, x.PreviousName)
- compressionLenHelper(c, x.NextName)
+ compressionLenHelper(c, x.PreviousName, currentLen)
+ compressionLenHelper(c, x.NextName, currentLen)
case *TKEY:
- compressionLenHelper(c, x.Algorithm)
+ compressionLenHelper(c, x.Algorithm, currentLen)
case *TSIG:
- compressionLenHelper(c, x.Algorithm)
+ compressionLenHelper(c, x.Algorithm, currentLen)
}
}
From 9452f40faefd2464218c8d364057cc1fc6ba29db Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Mon, 16 Apr 2018 10:28:43 +0200
Subject: [PATCH 03/12] Len() always right including when around 14bits
boudaries
---
compress_generate.go | 23 +++++---
length_test.go | 64 +++++++++++++++++++++
msg.go | 58 ++++++++++---------
zcompress.go | 130 ++++++++++++++++++++++---------------------
4 files changed, 172 insertions(+), 103 deletions(-)
diff --git a/compress_generate.go b/compress_generate.go
index 87fb36f68..9bd346427 100644
--- a/compress_generate.go
+++ b/compress_generate.go
@@ -101,7 +101,8 @@ Names:
// compressionLenHelperType - all types that have domain-name/cdomain-name can be used for compressing names
- fmt.Fprint(b, "func compressionLenHelperType(c map[string]int, r RR) {\n")
+ fmt.Fprint(b, "func compressionLenHelperType(c map[string]int, r RR, initLen int) int {\n")
+ fmt.Fprint(b, "currentLen := initLen\n")
fmt.Fprint(b, "switch x := r.(type) {\n")
for _, name := range domainTypes {
o := scope.Lookup(name)
@@ -109,7 +110,9 @@ Names:
fmt.Fprintf(b, "case *%s:\n", name)
for i := 1; i < st.NumFields(); i++ {
- out := func(s string) { fmt.Fprintf(b, "compressionLenHelper(c, x.%s)\n", st.Field(i).Name()) }
+ out := func(s string) {
+ fmt.Fprintf(b, "currentLen += compressionLenHelper(c, x.%s, currentLen)\n", st.Field(i).Name())
+ }
if _, ok := st.Field(i).Type().(*types.Slice); ok {
switch st.Tag(i) {
@@ -118,8 +121,8 @@ Names:
case `dns:"cdomain-name"`:
// For HIP we need to slice over the elements in this slice.
fmt.Fprintf(b, `for i := range x.%s {
- compressionLenHelper(c, x.%s[i])
- }
+ currentLen += compressionLenHelper(c, x.%s[i], currentLen)
+}
`, st.Field(i).Name(), st.Field(i).Name())
}
continue
@@ -133,11 +136,11 @@ Names:
}
}
}
- fmt.Fprintln(b, "}\n}\n\n")
+ fmt.Fprintln(b, "}\nreturn currentLen - initLen\n}\n\n")
// compressionLenSearchType - search cdomain-tags types for compressible names.
- fmt.Fprint(b, "func compressionLenSearchType(c map[string]int, r RR) (int, bool) {\n")
+ fmt.Fprint(b, "func compressionLenSearchType(c map[string]int, r RR) (int, bool, int) {\n")
fmt.Fprint(b, "switch x := r.(type) {\n")
for _, name := range cdomainTypes {
o := scope.Lookup(name)
@@ -147,7 +150,7 @@ Names:
j := 1
for i := 1; i < st.NumFields(); i++ {
out := func(s string, j int) {
- fmt.Fprintf(b, "k%d, ok%d := compressionLenSearch(c, x.%s)\n", j, j, st.Field(i).Name())
+ fmt.Fprintf(b, "k%d, ok%d, sz%d := compressionLenSearch(c, x.%s)\n", j, j, j, st.Field(i).Name())
}
// There are no slice types with names that can be compressed.
@@ -160,13 +163,15 @@ Names:
}
k := "k1"
ok := "ok1"
+ sz := "sz1"
for i := 2; i < j; i++ {
k += fmt.Sprintf(" + k%d", i)
ok += fmt.Sprintf(" && ok%d", i)
+ sz += fmt.Sprintf(" + sz%d", i)
}
- fmt.Fprintf(b, "return %s, %s\n", k, ok)
+ fmt.Fprintf(b, "return %s, %s, %s\n", k, ok, sz)
}
- fmt.Fprintln(b, "}\nreturn 0, false\n}\n\n")
+ fmt.Fprintln(b, "}\nreturn 0, false, 0\n}\n\n")
// gofmt
res, err := format.Source(b.Bytes())
diff --git a/length_test.go b/length_test.go
index bd5fd97cf..27a13804b 100644
--- a/length_test.go
+++ b/length_test.go
@@ -80,6 +80,70 @@ func TestMsgLength(t *testing.T) {
}
}
+func TestCompressionLenHelper(t *testing.T) {
+ c := make(map[string]int)
+ compressionLenHelper(c, "example.com", 12)
+ if c["example.com"] != 11 {
+ t.Errorf("bad %d", c["example.com"])
+ }
+ if c["com"] != 3 {
+ t.Errorf("bad %d", c["com"])
+ }
+
+ // Test boundaries
+ c = make(map[string]int)
+ // foo label starts at 16379
+ // com label starts at 16384
+ compressionLenHelper(c, "foo.com", 16379)
+ if c["foo.com"] != 7 {
+ t.Errorf("bad %d", c["foo.com"])
+ }
+ // com label is accessible
+ if c["com"] != 3 {
+ t.Errorf("bad %d", c["com"])
+ }
+
+ c = make(map[string]int)
+ // foo label starts at 16379
+ // com label starts at 16385 => outside range
+ compressionLenHelper(c, "foo.com", 16380)
+ if c["foo.com"] != 7 {
+ t.Errorf("bad %d", c["foo.com"])
+ }
+ // com label is NOT accessible
+ if c["com"] != 0 {
+ t.Errorf("bad %d", c["com"])
+ }
+
+ c = make(map[string]int)
+ compressionLenHelper(c, "example.com", 16375)
+ if c["example.com"] != 11 {
+ t.Errorf("bad %d", c["example.com"])
+ }
+ // com starts AFTER 16384
+ if c["com"] != 3 {
+ t.Errorf("bad %d", c["com"])
+ }
+
+ c = make(map[string]int)
+ compressionLenHelper(c, "example.com", 16376)
+ if c["example.com"] != 11 {
+ t.Errorf("bad %d", c["example.com"])
+ }
+ // com starts AFTER 16384
+ if c["com"] != 0 {
+ t.Errorf("bad %d", c["com"])
+ }
+}
+
+func TestCompressionLenSearch(t *testing.T) {
+ c := make(map[string]int)
+ compressed, ok, fullSize := compressionLenSearch(c, "a.b.org.")
+ if compressed != 0 || ok || fullSize != 14 {
+ panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize))
+ }
+}
+
func TestMsgLength2(t *testing.T) {
// Serialized replies
var testMessages = []string{
diff --git a/msg.go b/msg.go
index d8a69fc59..75013c23a 100644
--- a/msg.go
+++ b/msg.go
@@ -963,77 +963,75 @@ func compressedLen(dns *Msg, compress bool) int {
}
func compressionLenSlice(lenp int, c map[string]int, rs []RR) int {
- var l int
+ initLen := lenp
for _, r := range rs {
if r == nil {
continue
}
- // track this length, and the global length in len, while taking compression into account for both.
+ // TmpLen is to track len of record at 14bits boudaries, 6 bytes is the raw overhead
+ tmpLen := lenp + 6
x := r.len()
- initLen := lenp
- l += x
- compressedSize := 0
-
- k, ok := compressionLenSearch(c, r.Header().Name)
+ // track this length, and the global length in len, while taking compression into account for both.
+ k, ok, sz := compressionLenSearch(c, r.Header().Name)
if ok {
- l += 1 - k
- lenp += 1 - k
- compressedSize = len(r.Header().Name) - k + 1
- }
-
- if initLen+compressedSize < maxCompressionOffset {
- compressionLenHelper(c, r.Header().Name, initLen+compressedSize)
+ x += 1 - k
}
+ tmpLen += sz
- k, ok = compressionLenSearchType(c, r)
+ tmpLen += compressionLenHelper(c, r.Header().Name, tmpLen)
+ k, ok, added := compressionLenSearchType(c, r)
if ok {
- l += 1 - k
- lenp += 1 - k
- compressedSize += len(r.Header().Name) - k + 1
- }
-
- if initLen+compressedSize < maxCompressionOffset {
- compressionLenHelperType(c, r, initLen+compressedSize)
+ x += 1 - k
}
+ tmpLen += added
+ tmpLen += compressionLenHelperType(c, r, tmpLen)
lenp += x
}
- return l
+ return lenp - initLen
}
-// Put the parts of the name in the compression map.
-func compressionLenHelper(c map[string]int, s string, currentLen int) {
+// Put the parts of the name in the compression map, return the size added in payload
+func compressionLenHelper(c map[string]int, s string, currentLen int) int {
+ addedSize := 0
pref := ""
lbs := Split(s)
for j := len(lbs) - 1; j >= 0; j-- {
pref = s[lbs[j]:]
if _, ok := c[pref]; !ok {
lenAdded := len(pref)
- offsetOfLabel := currentLen + len(s) - len(pref) + 6
+ numLabelsBefore := len(lbs) - j - 1
+ offsetOfLabel := currentLen + len(s) - len(pref) + numLabelsBefore*2
if offsetOfLabel < maxCompressionOffset {
c[pref] = lenAdded
+ addedSize += 2 + lenAdded
}
}
}
+ return addedSize
}
// Look for each part in the compression map and returns its length,
// keep on searching so we get the longest match.
-func compressionLenSearch(c map[string]int, s string) (int, bool) {
+// Will return the size of compression found, whether a match has been
+// found and the size of record if added in payload
+func compressionLenSearch(c map[string]int, s string) (int, bool, int) {
off := 0
end := false
if s == "" { // don't bork on bogus data
- return 0, false
+ return 0, false, 0
}
+ fullSize := 0
for {
if _, ok := c[s[off:]]; ok {
- return len(s[off:]), true
+ return len(s[off:]), true, fullSize + off
}
if end {
break
}
+ fullSize += 2
off, end = NextLabel(s, off)
}
- return 0, false
+ return 0, false, fullSize + len(s)
}
// Copy returns a new RR which is a deep-copy of r.
diff --git a/zcompress.go b/zcompress.go
index a75bb38b7..f01901677 100644
--- a/zcompress.go
+++ b/zcompress.go
@@ -2,117 +2,119 @@
package dns
-func compressionLenHelperType(c map[string]int, r RR, currentLen int) {
+func compressionLenHelperType(c map[string]int, r RR, initLen int) int {
+ currentLen := initLen
switch x := r.(type) {
case *AFSDB:
- compressionLenHelper(c, x.Hostname, currentLen)
+ currentLen += compressionLenHelper(c, x.Hostname, currentLen)
case *CNAME:
- compressionLenHelper(c, x.Target, currentLen)
+ currentLen += compressionLenHelper(c, x.Target, currentLen)
case *DNAME:
- compressionLenHelper(c, x.Target, currentLen)
+ currentLen += compressionLenHelper(c, x.Target, currentLen)
case *HIP:
for i := range x.RendezvousServers {
- compressionLenHelper(c, x.RendezvousServers[i], currentLen)
+ currentLen += compressionLenHelper(c, x.RendezvousServers[i], currentLen)
}
case *KX:
- compressionLenHelper(c, x.Exchanger, currentLen)
+ currentLen += compressionLenHelper(c, x.Exchanger, currentLen)
case *LP:
- compressionLenHelper(c, x.Fqdn, currentLen)
+ currentLen += compressionLenHelper(c, x.Fqdn, currentLen)
case *MB:
- compressionLenHelper(c, x.Mb, currentLen)
+ currentLen += compressionLenHelper(c, x.Mb, currentLen)
case *MD:
- compressionLenHelper(c, x.Md, currentLen)
+ currentLen += compressionLenHelper(c, x.Md, currentLen)
case *MF:
- compressionLenHelper(c, x.Mf, currentLen)
+ currentLen += compressionLenHelper(c, x.Mf, currentLen)
case *MG:
- compressionLenHelper(c, x.Mg, currentLen)
+ currentLen += compressionLenHelper(c, x.Mg, currentLen)
case *MINFO:
- compressionLenHelper(c, x.Rmail, currentLen)
- compressionLenHelper(c, x.Email, currentLen)
+ currentLen += compressionLenHelper(c, x.Rmail, currentLen)
+ currentLen += compressionLenHelper(c, x.Email, currentLen)
case *MR:
- compressionLenHelper(c, x.Mr, currentLen)
+ currentLen += compressionLenHelper(c, x.Mr, currentLen)
case *MX:
- compressionLenHelper(c, x.Mx, currentLen)
+ currentLen += compressionLenHelper(c, x.Mx, currentLen)
case *NAPTR:
- compressionLenHelper(c, x.Replacement, currentLen)
+ currentLen += compressionLenHelper(c, x.Replacement, currentLen)
case *NS:
- compressionLenHelper(c, x.Ns, currentLen)
+ currentLen += compressionLenHelper(c, x.Ns, currentLen)
case *NSAPPTR:
- compressionLenHelper(c, x.Ptr, currentLen)
+ currentLen += compressionLenHelper(c, x.Ptr, currentLen)
case *NSEC:
- compressionLenHelper(c, x.NextDomain, currentLen)
+ currentLen += compressionLenHelper(c, x.NextDomain, currentLen)
case *PTR:
- compressionLenHelper(c, x.Ptr, currentLen)
+ currentLen += compressionLenHelper(c, x.Ptr, currentLen)
case *PX:
- compressionLenHelper(c, x.Map822, currentLen)
- compressionLenHelper(c, x.Mapx400, currentLen)
+ currentLen += compressionLenHelper(c, x.Map822, currentLen)
+ currentLen += compressionLenHelper(c, x.Mapx400, currentLen)
case *RP:
- compressionLenHelper(c, x.Mbox, currentLen)
- compressionLenHelper(c, x.Txt, currentLen)
+ currentLen += compressionLenHelper(c, x.Mbox, currentLen)
+ currentLen += compressionLenHelper(c, x.Txt, currentLen)
case *RRSIG:
- compressionLenHelper(c, x.SignerName, currentLen)
+ currentLen += compressionLenHelper(c, x.SignerName, currentLen)
case *RT:
- compressionLenHelper(c, x.Host, currentLen)
+ currentLen += compressionLenHelper(c, x.Host, currentLen)
case *SIG:
- compressionLenHelper(c, x.SignerName, currentLen)
+ currentLen += compressionLenHelper(c, x.SignerName, currentLen)
case *SOA:
- compressionLenHelper(c, x.Ns, currentLen)
- compressionLenHelper(c, x.Mbox, currentLen)
+ currentLen += compressionLenHelper(c, x.Ns, currentLen)
+ currentLen += compressionLenHelper(c, x.Mbox, currentLen)
case *SRV:
- compressionLenHelper(c, x.Target, currentLen)
+ currentLen += compressionLenHelper(c, x.Target, currentLen)
case *TALINK:
- compressionLenHelper(c, x.PreviousName, currentLen)
- compressionLenHelper(c, x.NextName, currentLen)
+ currentLen += compressionLenHelper(c, x.PreviousName, currentLen)
+ currentLen += compressionLenHelper(c, x.NextName, currentLen)
case *TKEY:
- compressionLenHelper(c, x.Algorithm, currentLen)
+ currentLen += compressionLenHelper(c, x.Algorithm, currentLen)
case *TSIG:
- compressionLenHelper(c, x.Algorithm, currentLen)
+ currentLen += compressionLenHelper(c, x.Algorithm, currentLen)
}
+ return currentLen - initLen
}
-func compressionLenSearchType(c map[string]int, r RR) (int, bool) {
+func compressionLenSearchType(c map[string]int, r RR) (int, bool, int) {
switch x := r.(type) {
case *AFSDB:
- k1, ok1 := compressionLenSearch(c, x.Hostname)
- return k1, ok1
+ k1, ok1, sz1 := compressionLenSearch(c, x.Hostname)
+ return k1, ok1, sz1
case *CNAME:
- k1, ok1 := compressionLenSearch(c, x.Target)
- return k1, ok1
+ k1, ok1, sz1 := compressionLenSearch(c, x.Target)
+ return k1, ok1, sz1
case *MB:
- k1, ok1 := compressionLenSearch(c, x.Mb)
- return k1, ok1
+ k1, ok1, sz1 := compressionLenSearch(c, x.Mb)
+ return k1, ok1, sz1
case *MD:
- k1, ok1 := compressionLenSearch(c, x.Md)
- return k1, ok1
+ k1, ok1, sz1 := compressionLenSearch(c, x.Md)
+ return k1, ok1, sz1
case *MF:
- k1, ok1 := compressionLenSearch(c, x.Mf)
- return k1, ok1
+ k1, ok1, sz1 := compressionLenSearch(c, x.Mf)
+ return k1, ok1, sz1
case *MG:
- k1, ok1 := compressionLenSearch(c, x.Mg)
- return k1, ok1
+ k1, ok1, sz1 := compressionLenSearch(c, x.Mg)
+ return k1, ok1, sz1
case *MINFO:
- k1, ok1 := compressionLenSearch(c, x.Rmail)
- k2, ok2 := compressionLenSearch(c, x.Email)
- return k1 + k2, ok1 && ok2
+ k1, ok1, sz1 := compressionLenSearch(c, x.Rmail)
+ k2, ok2, sz2 := compressionLenSearch(c, x.Email)
+ return k1 + k2, ok1 && ok2, sz1 + sz2
case *MR:
- k1, ok1 := compressionLenSearch(c, x.Mr)
- return k1, ok1
+ k1, ok1, sz1 := compressionLenSearch(c, x.Mr)
+ return k1, ok1, sz1
case *MX:
- k1, ok1 := compressionLenSearch(c, x.Mx)
- return k1, ok1
+ k1, ok1, sz1 := compressionLenSearch(c, x.Mx)
+ return k1, ok1, sz1
case *NS:
- k1, ok1 := compressionLenSearch(c, x.Ns)
- return k1, ok1
+ k1, ok1, sz1 := compressionLenSearch(c, x.Ns)
+ return k1, ok1, sz1
case *PTR:
- k1, ok1 := compressionLenSearch(c, x.Ptr)
- return k1, ok1
+ k1, ok1, sz1 := compressionLenSearch(c, x.Ptr)
+ return k1, ok1, sz1
case *RT:
- k1, ok1 := compressionLenSearch(c, x.Host)
- return k1, ok1
+ k1, ok1, sz1 := compressionLenSearch(c, x.Host)
+ return k1, ok1, sz1
case *SOA:
- k1, ok1 := compressionLenSearch(c, x.Ns)
- k2, ok2 := compressionLenSearch(c, x.Mbox)
- return k1 + k2, ok1 && ok2
+ k1, ok1, sz1 := compressionLenSearch(c, x.Ns)
+ k2, ok2, sz2 := compressionLenSearch(c, x.Mbox)
+ return k1 + k2, ok1 && ok2, sz1 + sz2
}
- return 0, false
+ return 0, false, 0
}
From 80580bd48181973cdcbf014c5f61941afa9b434d Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Mon, 16 Apr 2018 15:41:05 +0200
Subject: [PATCH 04/12] Avoid splitting into labels when not applicable
---
msg.go | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/msg.go b/msg.go
index 75013c23a..4f145ab5d 100644
--- a/msg.go
+++ b/msg.go
@@ -990,9 +990,15 @@ func compressionLenSlice(lenp int, c map[string]int, rs []RR) int {
return lenp - initLen
}
-// Put the parts of the name in the compression map, return the size added in payload
+// Put the parts of the name in the compression map, return the size in bytes added in payload
func compressionLenHelper(c map[string]int, s string, currentLen int) int {
- addedSize := 0
+ if currentLen >= maxCompressionOffset {
+ return 0
+ }
+ if _, ok := c[s]; ok {
+ return 0
+ }
+ addedSizeBytes := 0
pref := ""
lbs := Split(s)
for j := len(lbs) - 1; j >= 0; j-- {
@@ -1000,14 +1006,14 @@ func compressionLenHelper(c map[string]int, s string, currentLen int) int {
if _, ok := c[pref]; !ok {
lenAdded := len(pref)
numLabelsBefore := len(lbs) - j - 1
- offsetOfLabel := currentLen + len(s) - len(pref) + numLabelsBefore*2
+ offsetOfLabel := currentLen + len(s) - lenAdded + numLabelsBefore*2
if offsetOfLabel < maxCompressionOffset {
c[pref] = lenAdded
- addedSize += 2 + lenAdded
+ addedSizeBytes += 2 + lenAdded
}
}
}
- return addedSize
+ return addedSizeBytes
}
// Look for each part in the compression map and returns its length,
From a4e1a80fb7bbeab0e409717db55124a800caf6f4 Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Mon, 16 Apr 2018 19:17:57 +0200
Subject: [PATCH 05/12] Fixed comments
---
msg.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/msg.go b/msg.go
index 4f145ab5d..1bb846f31 100644
--- a/msg.go
+++ b/msg.go
@@ -992,7 +992,7 @@ func compressionLenSlice(lenp int, c map[string]int, rs []RR) int {
// Put the parts of the name in the compression map, return the size in bytes added in payload
func compressionLenHelper(c map[string]int, s string, currentLen int) int {
- if currentLen >= maxCompressionOffset {
+ if currentLen+3 >= maxCompressionOffset {
return 0
}
if _, ok := c[s]; ok {
From 3a2f2f6114e1815afa9fa4500ffaeecdc4334691 Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Thu, 19 Apr 2018 00:15:18 +0200
Subject: [PATCH 06/12] Added comments in code
---
msg.go | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/msg.go b/msg.go
index 1bb846f31..85ba7186c 100644
--- a/msg.go
+++ b/msg.go
@@ -974,6 +974,8 @@ func compressionLenSlice(lenp int, c map[string]int, rs []RR) int {
// track this length, and the global length in len, while taking compression into account for both.
k, ok, sz := compressionLenSearch(c, r.Header().Name)
if ok {
+ // Size of x is reduced by k, but we add 1 since k includes the '.' and label descriptor take 2 bytes
+ // so, basically x:= x - k - 1 + 2
x += 1 - k
}
tmpLen += sz
@@ -992,7 +994,8 @@ func compressionLenSlice(lenp int, c map[string]int, rs []RR) int {
// Put the parts of the name in the compression map, return the size in bytes added in payload
func compressionLenHelper(c map[string]int, s string, currentLen int) int {
- if currentLen+3 >= maxCompressionOffset {
+ if currentLen > maxCompressionOffset {
+ // We won't be able to add any label that could be re-used later anyway
return 0
}
if _, ok := c[s]; ok {
@@ -1006,7 +1009,9 @@ func compressionLenHelper(c map[string]int, s string, currentLen int) int {
if _, ok := c[pref]; !ok {
lenAdded := len(pref)
numLabelsBefore := len(lbs) - j - 1
+ // 2 bytes per label before this label to take into account
offsetOfLabel := currentLen + len(s) - lenAdded + numLabelsBefore*2
+ // If first byte label is within the first 14bits, it might be re-used later
if offsetOfLabel < maxCompressionOffset {
c[pref] = lenAdded
addedSizeBytes += 2 + lenAdded
@@ -1034,6 +1039,7 @@ func compressionLenSearch(c map[string]int, s string) (int, bool, int) {
if end {
break
}
+ // Each label descriptor takes 2 bytes, add it
fullSize += 2
off, end = NextLabel(s, off)
}
From 7edff1b0982142965f9ab97232e0040491b6d59c Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Fri, 20 Apr 2018 10:25:17 +0200
Subject: [PATCH 07/12] Added new test cases
---
length_test.go | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/length_test.go b/length_test.go
index 27a13804b..93d52869e 100644
--- a/length_test.go
+++ b/length_test.go
@@ -142,6 +142,28 @@ func TestCompressionLenSearch(t *testing.T) {
if compressed != 0 || ok || fullSize != 14 {
panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize))
}
+ c["org."] = 3
+ compressed, ok, fullSize = compressionLenSearch(c, "a.b.org.")
+ if compressed != 4 || !ok || fullSize != 8 {
+ panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize))
+ }
+ c["b.org."] = 5
+ compressed, ok, fullSize = compressionLenSearch(c, "a.b.org.")
+ if compressed != 6 || !ok || fullSize != 4 {
+ panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize))
+ }
+ // Not found long compression
+ c["x.b.org."] = 5
+ compressed, ok, fullSize = compressionLenSearch(c, "a.b.org.")
+ if compressed != 6 || !ok || fullSize != 4 {
+ panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize))
+ }
+ // Found long compression
+ c["a.b.org."] = 5
+ compressed, ok, fullSize = compressionLenSearch(c, "a.b.org.")
+ if compressed != 8 || !ok || fullSize != 0 {
+ panic(fmt.Errorf("Failed: compressed:=%d, ok:=%v, fullSize:=%d", compressed, ok, fullSize))
+ }
}
func TestMsgLength2(t *testing.T) {
From d3b59f85d035e9975b90bcd3418a4056bc611a49 Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Mon, 30 Apr 2018 12:39:45 +0200
Subject: [PATCH 08/12] Fixed computation of Len() for SRV and all kind of
records
---
compress_generate.go | 5 +++
length_test.go | 90 ++++++++++++++++++++++++++++++++++++++------
msg.go | 80 +++++++++++++++++++++++----------------
zcompress.go | 35 +++++++++++++++++
4 files changed, 166 insertions(+), 44 deletions(-)
diff --git a/compress_generate.go b/compress_generate.go
index 9bd346427..9a136c414 100644
--- a/compress_generate.go
+++ b/compress_generate.go
@@ -111,6 +111,7 @@ Names:
fmt.Fprintf(b, "case *%s:\n", name)
for i := 1; i < st.NumFields(); i++ {
out := func(s string) {
+ fmt.Fprintf(b, "currentLen -= len(x.%s) + 1\n", st.Field(i).Name())
fmt.Fprintf(b, "currentLen += compressionLenHelper(c, x.%s, currentLen)\n", st.Field(i).Name())
}
@@ -121,6 +122,10 @@ Names:
case `dns:"cdomain-name"`:
// For HIP we need to slice over the elements in this slice.
fmt.Fprintf(b, `for i := range x.%s {
+ currentLen -= len(x.%s[i]) + 1
+}
+`, st.Field(i).Name(), st.Field(i).Name())
+ fmt.Fprintf(b, `for i := range x.%s {
currentLen += compressionLenHelper(c, x.%s[i], currentLen)
}
`, st.Field(i).Name(), st.Field(i).Name())
diff --git a/length_test.go b/length_test.go
index 93d52869e..66521a5f2 100644
--- a/length_test.go
+++ b/length_test.go
@@ -4,6 +4,7 @@ import (
"encoding/hex"
"fmt"
"net"
+ "reflect"
"strings"
"testing"
)
@@ -53,6 +54,7 @@ func TestMsgCompressLength(t *testing.T) {
func TestMsgLength(t *testing.T) {
makeMsg := func(question string, ans, ns, e []RR) *Msg {
msg := new(Msg)
+ msg.Compress = true
msg.SetQuestion(Fqdn(question), TypeANY)
msg.Answer = append(msg.Answer, ans...)
msg.Ns = append(msg.Ns, ns...)
@@ -83,10 +85,10 @@ func TestMsgLength(t *testing.T) {
func TestCompressionLenHelper(t *testing.T) {
c := make(map[string]int)
compressionLenHelper(c, "example.com", 12)
- if c["example.com"] != 11 {
+ if c["example.com"] != 12 {
t.Errorf("bad %d", c["example.com"])
}
- if c["com"] != 3 {
+ if c["com"] != 20 {
t.Errorf("bad %d", c["com"])
}
@@ -95,11 +97,11 @@ func TestCompressionLenHelper(t *testing.T) {
// foo label starts at 16379
// com label starts at 16384
compressionLenHelper(c, "foo.com", 16379)
- if c["foo.com"] != 7 {
+ if c["foo.com"] != 16379 {
t.Errorf("bad %d", c["foo.com"])
}
// com label is accessible
- if c["com"] != 3 {
+ if c["com"] != 16383 {
t.Errorf("bad %d", c["com"])
}
@@ -107,7 +109,7 @@ func TestCompressionLenHelper(t *testing.T) {
// foo label starts at 16379
// com label starts at 16385 => outside range
compressionLenHelper(c, "foo.com", 16380)
- if c["foo.com"] != 7 {
+ if c["foo.com"] != 16380 {
t.Errorf("bad %d", c["foo.com"])
}
// com label is NOT accessible
@@ -117,17 +119,17 @@ func TestCompressionLenHelper(t *testing.T) {
c = make(map[string]int)
compressionLenHelper(c, "example.com", 16375)
- if c["example.com"] != 11 {
+ if c["example.com"] != 16375 {
t.Errorf("bad %d", c["example.com"])
}
// com starts AFTER 16384
- if c["com"] != 3 {
+ if c["com"] != 16383 {
t.Errorf("bad %d", c["com"])
}
c = make(map[string]int)
compressionLenHelper(c, "example.com", 16376)
- if c["example.com"] != 11 {
+ if c["example.com"] != 16376 {
t.Errorf("bad %d", c["example.com"])
}
// com starts AFTER 16384
@@ -246,7 +248,7 @@ func TestMsgCompressLengthLargeRecords(t *testing.T) {
msg.SetQuestion("my.service.acme.", TypeSRV)
j := 1
for i := 0; i < 250; i++ {
- target := fmt.Sprintf("host-redis-%d-%d.test.acme.com.node.dc1.consul.", j, i)
+ target := fmt.Sprintf("host-redis-1-%d.test.acme.com.node.dc1.consul.", i)
msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: 1, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.%d.%d.", j, i)})
}
@@ -260,6 +262,72 @@ func TestMsgCompressLengthLargeRecords(t *testing.T) {
}
}
+func TestCompareCompressionMapsForANY(t *testing.T) {
+ msg := new(Msg)
+ msg.Compress = true
+ msg.SetQuestion("a.service.acme.", TypeANY)
+ // Be sure to have more than 14bits
+ for i := 0; i < 2000; i++ {
+ target := fmt.Sprintf("host.app-%d.x%d.test.acme.", i%250, i)
+ msg.Answer = append(msg.Answer, &AAAA{Hdr: RR_Header{Name: target, Rrtype: TypeAAAA, Class: ClassINET, Ttl: 0x3c}, AAAA: net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, byte(i / 255), byte(i % 255)}})
+ msg.Answer = append(msg.Answer, &A{Hdr: RR_Header{Name: target, Rrtype: TypeA, Class: ClassINET, Ttl: 0x3c}, A: net.IP{127, 0, byte(i / 255), byte(i % 255)}})
+ if msg.Len() > 16384 {
+ break
+ }
+ }
+ for labelSize := 0; labelSize < 63; labelSize++ {
+ msg.SetQuestion(fmt.Sprintf("a%s.service.acme.", strings.Repeat("x", labelSize)), TypeANY)
+
+ compressionFake := make(map[string]int)
+ lenFake := compressedLenWithCompressionMap(msg, compressionFake)
+
+ compressionReal := make(map[string]int)
+ buf, err := msg.packBufferWithCompressionMap(nil, compressionReal)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if lenFake != len(buf) {
+ t.Fatalf("padding= %d ; Predicted len := %d != real:= %d", labelSize, lenFake, len(buf))
+ }
+ if !reflect.DeepEqual(compressionFake, compressionReal) {
+ t.Fatalf("padding= %d ; Fake Compression Map != Real Compression Map\n*** Real:= %v\n\n***Fake:= %v", labelSize, compressionReal, compressionFake)
+ }
+ }
+}
+
+func TestCompareCompressionMapsForSRV(t *testing.T) {
+ msg := new(Msg)
+ msg.Compress = true
+ msg.SetQuestion("a.service.acme.", TypeSRV)
+ // Be sure to have more than 14bits
+ for i := 0; i < 2000; i++ {
+ target := fmt.Sprintf("host.app-%d.x%d.test.acme.", i%250, i)
+ msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: ClassINET, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
+ msg.Extra = append(msg.Extra, &A{Hdr: RR_Header{Name: target, Rrtype: TypeA, Class: ClassINET, Ttl: 0x3c}, A: net.IP{127, 0, byte(i / 255), byte(i % 255)}})
+ if msg.Len() > 16384 {
+ break
+ }
+ }
+ for labelSize := 0; labelSize < 63; labelSize++ {
+ msg.SetQuestion(fmt.Sprintf("a%s.service.acme.", strings.Repeat("x", labelSize)), TypeAAAA)
+
+ compressionFake := make(map[string]int)
+ lenFake := compressedLenWithCompressionMap(msg, compressionFake)
+
+ compressionReal := make(map[string]int)
+ buf, err := msg.packBufferWithCompressionMap(nil, compressionReal)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if lenFake != len(buf) {
+ t.Fatalf("padding= %d ; Predicted len := %d != real:= %d", labelSize, lenFake, len(buf))
+ }
+ if !reflect.DeepEqual(compressionFake, compressionReal) {
+ t.Fatalf("padding= %d ; Fake Compression Map != Real Compression Map\n*** Real:= %v\n\n***Fake:= %v", labelSize, compressionReal, compressionFake)
+ }
+ }
+}
+
func TestMsgCompressLengthLargeRecordsWithPaddingPermutation(t *testing.T) {
msg := new(Msg)
msg.Compress = true
@@ -268,7 +336,7 @@ func TestMsgCompressLengthLargeRecordsWithPaddingPermutation(t *testing.T) {
for i := 0; i < 250; i++ {
target := fmt.Sprintf("host-redis-x-%d.test.acme.com.node.dc1.consul.", i)
msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
- msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: 1, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.x.%d.", i)})
+ msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: ClassINET, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.x.%d.", i)})
}
for labelSize := 1; labelSize < 63; labelSize++ {
msg.SetQuestion(fmt.Sprintf("my.%s.service.acme.", strings.Repeat("x", labelSize)), TypeSRV)
@@ -290,7 +358,7 @@ func TestMsgCompressLengthLargeRecordsAllValues(t *testing.T) {
for i := 0; i < 900; i++ {
target := fmt.Sprintf("host-redis-%d-%d.test.acme.com.node.dc1.consul.", i/256, i%256)
msg.Answer = append(msg.Answer, &SRV{Hdr: RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
- msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: 1, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.%d.%d.", i/256, i%256)})
+ msg.Extra = append(msg.Extra, &CNAME{Hdr: RR_Header{Name: target, Class: ClassINET, Rrtype: TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.%d.%d.", i/256, i%256)})
predicted := msg.Len()
buf, err := msg.Pack()
if err != nil {
diff --git a/msg.go b/msg.go
index 85ba7186c..6de5b2432 100644
--- a/msg.go
+++ b/msg.go
@@ -694,15 +694,18 @@ func (dns *Msg) Pack() (msg []byte, err error) {
// PackBuffer packs a Msg, using the given buffer buf. If buf is too small
// a new buffer is allocated.
func (dns *Msg) PackBuffer(buf []byte) (msg []byte, err error) {
- // We use a similar function in tsig.go's stripTsig.
- var (
- dh Header
- compression map[string]int
- )
-
+ var compression map[string]int
if dns.Compress {
compression = make(map[string]int) // Compression pointer mappings
}
+ return dns.packBufferWithCompressionMap(msg, compression)
+}
+
+func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression map[string]int) (msg []byte, err error) {
+ // We use a similar function in tsig.go's stripTsig.
+ var (
+ dh Header
+ )
if dns.Rcode < 0 || dns.Rcode > 0xFFF {
return nil, ErrRcode
@@ -922,23 +925,27 @@ func (dns *Msg) String() string {
// than packing it, measuring the size and discarding the buffer.
func (dns *Msg) Len() int { return compressedLen(dns, dns.Compress) }
+func compressedLenWithCompressionMap(dns *Msg, compression map[string]int) int {
+ l := 12 // Message header is always 12 bytes
+ for _, r := range dns.Question {
+ compressionLenHelper(compression, r.Name, l)
+ l += r.len()
+ }
+ l += compressionLenSlice(l, compression, dns.Answer, 0)
+ l += compressionLenSlice(l, compression, dns.Ns, 0)
+ l += compressionLenSlice(l, compression, dns.Extra, 0)
+ return l
+}
+
// compressedLen returns the message length when in compressed wire format
// when compress is true, otherwise the uncompressed length is returned.
func compressedLen(dns *Msg, compress bool) int {
// We always return one more than needed.
- l := 12 // Message header is always 12 bytes
if compress {
compression := map[string]int{}
- for _, r := range dns.Question {
- l += r.len()
- compressionLenHelper(compression, r.Name, l)
- }
- l += compressionLenSlice(l, compression, dns.Answer)
- l += compressionLenSlice(l, compression, dns.Ns)
- l += compressionLenSlice(l, compression, dns.Extra)
-
- return l
+ return compressedLenWithCompressionMap(dns, compression)
}
+ l := 12 // Message header is always 12 bytes
for _, r := range dns.Question {
l += r.len()
@@ -962,32 +969,34 @@ func compressedLen(dns *Msg, compress bool) int {
return l
}
-func compressionLenSlice(lenp int, c map[string]int, rs []RR) int {
+func compressionLenSlice(lenp int, c map[string]int, rs []RR, padding int) int {
initLen := lenp
for _, r := range rs {
if r == nil {
continue
}
// TmpLen is to track len of record at 14bits boudaries, 6 bytes is the raw overhead
- tmpLen := lenp + 6
+ // 10 bytes for Signalization + 2 bytes for pointer
+ tmpLen := lenp + padding
+
x := r.len()
// track this length, and the global length in len, while taking compression into account for both.
- k, ok, sz := compressionLenSearch(c, r.Header().Name)
+ k, ok, _ := compressionLenSearch(c, r.Header().Name)
if ok {
// Size of x is reduced by k, but we add 1 since k includes the '.' and label descriptor take 2 bytes
// so, basically x:= x - k - 1 + 2
x += 1 - k
}
- tmpLen += sz
tmpLen += compressionLenHelper(c, r.Header().Name, tmpLen)
- k, ok, added := compressionLenSearchType(c, r)
+ k, ok, _ = compressionLenSearchType(c, r)
if ok {
x += 1 - k
}
- tmpLen += added
- tmpLen += compressionLenHelperType(c, r, tmpLen)
lenp += x
+ tmpLen = lenp
+ tmpLen += compressionLenHelperType(c, r, tmpLen)
+
}
return lenp - initLen
}
@@ -1001,24 +1010,29 @@ func compressionLenHelper(c map[string]int, s string, currentLen int) int {
if _, ok := c[s]; ok {
return 0
}
- addedSizeBytes := 0
+ initLen := currentLen
pref := ""
+ prev := s
lbs := Split(s)
- for j := len(lbs) - 1; j >= 0; j-- {
+ for j := 0; j < len(lbs); j++ {
pref = s[lbs[j]:]
+ currentLen += len(prev) - len(pref)
+ prev = pref
if _, ok := c[pref]; !ok {
- lenAdded := len(pref)
- numLabelsBefore := len(lbs) - j - 1
- // 2 bytes per label before this label to take into account
- offsetOfLabel := currentLen + len(s) - lenAdded + numLabelsBefore*2
// If first byte label is within the first 14bits, it might be re-used later
- if offsetOfLabel < maxCompressionOffset {
- c[pref] = lenAdded
- addedSizeBytes += 2 + lenAdded
+ if currentLen < maxCompressionOffset {
+ c[pref] = currentLen
+ }
+ } else {
+ added := currentLen - initLen
+ if j > 0 {
+ // We added a new PTR
+ added += 2
}
+ return added
}
}
- return addedSizeBytes
+ return currentLen - initLen
}
// Look for each part in the compression map and returns its length,
diff --git a/zcompress.go b/zcompress.go
index f01901677..a2c09dd48 100644
--- a/zcompress.go
+++ b/zcompress.go
@@ -6,67 +6,102 @@ func compressionLenHelperType(c map[string]int, r RR, initLen int) int {
currentLen := initLen
switch x := r.(type) {
case *AFSDB:
+ currentLen -= len(x.Hostname) + 1
currentLen += compressionLenHelper(c, x.Hostname, currentLen)
case *CNAME:
+ currentLen -= len(x.Target) + 1
currentLen += compressionLenHelper(c, x.Target, currentLen)
case *DNAME:
+ currentLen -= len(x.Target) + 1
currentLen += compressionLenHelper(c, x.Target, currentLen)
case *HIP:
+ for i := range x.RendezvousServers {
+ currentLen -= len(x.RendezvousServers[i]) + 1
+ }
for i := range x.RendezvousServers {
currentLen += compressionLenHelper(c, x.RendezvousServers[i], currentLen)
}
case *KX:
+ currentLen -= len(x.Exchanger) + 1
currentLen += compressionLenHelper(c, x.Exchanger, currentLen)
case *LP:
+ currentLen -= len(x.Fqdn) + 1
currentLen += compressionLenHelper(c, x.Fqdn, currentLen)
case *MB:
+ currentLen -= len(x.Mb) + 1
currentLen += compressionLenHelper(c, x.Mb, currentLen)
case *MD:
+ currentLen -= len(x.Md) + 1
currentLen += compressionLenHelper(c, x.Md, currentLen)
case *MF:
+ currentLen -= len(x.Mf) + 1
currentLen += compressionLenHelper(c, x.Mf, currentLen)
case *MG:
+ currentLen -= len(x.Mg) + 1
currentLen += compressionLenHelper(c, x.Mg, currentLen)
case *MINFO:
+ currentLen -= len(x.Rmail) + 1
currentLen += compressionLenHelper(c, x.Rmail, currentLen)
+ currentLen -= len(x.Email) + 1
currentLen += compressionLenHelper(c, x.Email, currentLen)
case *MR:
+ currentLen -= len(x.Mr) + 1
currentLen += compressionLenHelper(c, x.Mr, currentLen)
case *MX:
+ currentLen -= len(x.Mx) + 1
currentLen += compressionLenHelper(c, x.Mx, currentLen)
case *NAPTR:
+ currentLen -= len(x.Replacement) + 1
currentLen += compressionLenHelper(c, x.Replacement, currentLen)
case *NS:
+ currentLen -= len(x.Ns) + 1
currentLen += compressionLenHelper(c, x.Ns, currentLen)
case *NSAPPTR:
+ currentLen -= len(x.Ptr) + 1
currentLen += compressionLenHelper(c, x.Ptr, currentLen)
case *NSEC:
+ currentLen -= len(x.NextDomain) + 1
currentLen += compressionLenHelper(c, x.NextDomain, currentLen)
case *PTR:
+ currentLen -= len(x.Ptr) + 1
currentLen += compressionLenHelper(c, x.Ptr, currentLen)
case *PX:
+ currentLen -= len(x.Map822) + 1
currentLen += compressionLenHelper(c, x.Map822, currentLen)
+ currentLen -= len(x.Mapx400) + 1
currentLen += compressionLenHelper(c, x.Mapx400, currentLen)
case *RP:
+ currentLen -= len(x.Mbox) + 1
currentLen += compressionLenHelper(c, x.Mbox, currentLen)
+ currentLen -= len(x.Txt) + 1
currentLen += compressionLenHelper(c, x.Txt, currentLen)
case *RRSIG:
+ currentLen -= len(x.SignerName) + 1
currentLen += compressionLenHelper(c, x.SignerName, currentLen)
case *RT:
+ currentLen -= len(x.Host) + 1
currentLen += compressionLenHelper(c, x.Host, currentLen)
case *SIG:
+ currentLen -= len(x.SignerName) + 1
currentLen += compressionLenHelper(c, x.SignerName, currentLen)
case *SOA:
+ currentLen -= len(x.Ns) + 1
currentLen += compressionLenHelper(c, x.Ns, currentLen)
+ currentLen -= len(x.Mbox) + 1
currentLen += compressionLenHelper(c, x.Mbox, currentLen)
case *SRV:
+ currentLen -= len(x.Target) + 1
currentLen += compressionLenHelper(c, x.Target, currentLen)
case *TALINK:
+ currentLen -= len(x.PreviousName) + 1
currentLen += compressionLenHelper(c, x.PreviousName, currentLen)
+ currentLen -= len(x.NextName) + 1
currentLen += compressionLenHelper(c, x.NextName, currentLen)
case *TKEY:
+ currentLen -= len(x.Algorithm) + 1
currentLen += compressionLenHelper(c, x.Algorithm, currentLen)
case *TSIG:
+ currentLen -= len(x.Algorithm) + 1
currentLen += compressionLenHelper(c, x.Algorithm, currentLen)
}
return currentLen - initLen
From 51c5108a43d4f89c1bb838d899741c3ad033ff11 Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Mon, 30 Apr 2018 12:52:54 +0200
Subject: [PATCH 09/12] Fixed Sign that was relying on non-copy for Unit tests
---
sig0.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sig0.go b/sig0.go
index f31e9e684..b4feae596 100644
--- a/sig0.go
+++ b/sig0.go
@@ -30,7 +30,7 @@ func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) {
rr.Labels = 0
buf := make([]byte, m.Len()+rr.len())
- mbuf, err := m.PackBuffer(buf)
+ mbuf, err := m.packBufferWithCompressionMap(buf, nil)
if err != nil {
return nil, err
}
From 0d606ada1643acf985c7b32ac90c723b888bbc8a Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Mon, 30 Apr 2018 13:06:52 +0200
Subject: [PATCH 10/12] Removed unused padding
---
msg.go | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/msg.go b/msg.go
index 6de5b2432..9e0291be6 100644
--- a/msg.go
+++ b/msg.go
@@ -931,9 +931,9 @@ func compressedLenWithCompressionMap(dns *Msg, compression map[string]int) int {
compressionLenHelper(compression, r.Name, l)
l += r.len()
}
- l += compressionLenSlice(l, compression, dns.Answer, 0)
- l += compressionLenSlice(l, compression, dns.Ns, 0)
- l += compressionLenSlice(l, compression, dns.Extra, 0)
+ l += compressionLenSlice(l, compression, dns.Answer)
+ l += compressionLenSlice(l, compression, dns.Ns)
+ l += compressionLenSlice(l, compression, dns.Extra)
return l
}
@@ -969,15 +969,14 @@ func compressedLen(dns *Msg, compress bool) int {
return l
}
-func compressionLenSlice(lenp int, c map[string]int, rs []RR, padding int) int {
+func compressionLenSlice(lenp int, c map[string]int, rs []RR) int {
initLen := lenp
for _, r := range rs {
if r == nil {
continue
}
- // TmpLen is to track len of record at 14bits boudaries, 6 bytes is the raw overhead
- // 10 bytes for Signalization + 2 bytes for pointer
- tmpLen := lenp + padding
+ // TmpLen is to track len of record at 14bits boudaries
+ tmpLen := lenp
x := r.len()
// track this length, and the global length in len, while taking compression into account for both.
From e471e7f6f9b8a3f183ec114ba21cfab059444dd0 Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Fri, 11 May 2018 11:11:35 +0200
Subject: [PATCH 11/12] Fixed typo in PackBuffer() function
---
msg.go | 2 +-
sig0.go | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/msg.go b/msg.go
index 9e0291be6..4993087de 100644
--- a/msg.go
+++ b/msg.go
@@ -698,7 +698,7 @@ func (dns *Msg) PackBuffer(buf []byte) (msg []byte, err error) {
if dns.Compress {
compression = make(map[string]int) // Compression pointer mappings
}
- return dns.packBufferWithCompressionMap(msg, compression)
+ return dns.packBufferWithCompressionMap(buf, compression)
}
func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression map[string]int) (msg []byte, err error) {
diff --git a/sig0.go b/sig0.go
index b4feae596..f31e9e684 100644
--- a/sig0.go
+++ b/sig0.go
@@ -30,7 +30,7 @@ func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) {
rr.Labels = 0
buf := make([]byte, m.Len()+rr.len())
- mbuf, err := m.packBufferWithCompressionMap(buf, nil)
+ mbuf, err := m.PackBuffer(buf)
if err != nil {
return nil, err
}
From 33f7d9fba067d311b0df2c3083252b26d940f461 Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Fri, 11 May 2018 11:19:49 +0200
Subject: [PATCH 12/12] Added comment about packBufferWithCompressionMap() for
testing purposes
---
msg.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/msg.go b/msg.go
index 4993087de..27a54b61d 100644
--- a/msg.go
+++ b/msg.go
@@ -701,6 +701,7 @@ func (dns *Msg) PackBuffer(buf []byte) (msg []byte, err error) {
return dns.packBufferWithCompressionMap(buf, compression)
}
+// packBufferWithCompressionMap is ONLY for testing purposes
func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression map[string]int) (msg []byte, err error) {
// We use a similar function in tsig.go's stripTsig.
var (