Add Dedup function

Add function that dedups a list of RRs. Work on strings, which
adds garbage, but seems to be the least intrusive and takes the
last amount of memory.

Some fmt changes snook in as well.
This commit is contained in:
Miek Gieben 2015-08-24 22:02:32 +01:00
parent 5b9c36bf67
commit 79547a0341
5 changed files with 167 additions and 8 deletions

2
msg.go
View File

@ -36,7 +36,7 @@ var (
// ErrFqdn indicates that a domain name does not have a closing dot.
ErrFqdn error = &Error{err: "domain must be fully qualified"}
// ErrId indicates there is a mismatch with the message's ID.
ErrId error = &Error{err: "id mismatch"}
ErrId error = &Error{err: "id mismatch"}
// ErrKeyAlg indicates that the algorithm in the key is not valid.
ErrKeyAlg error = &Error{err: "bad key algorithm"}
ErrKey error = &Error{err: "bad key"}

View File

@ -907,11 +907,11 @@ func TestILNP(t *testing.T) {
func TestGposEidNimloc(t *testing.T) {
dt := map[string]string{
"444433332222111199990123000000ff. NSAP-PTR foo.bar.com.": "444433332222111199990123000000ff.\t3600\tIN\tNSAP-PTR\tfoo.bar.com.",
"lillee. IN GPOS -32.6882 116.8652 10.0": "lillee.\t3600\tIN\tGPOS\t-32.6882 116.8652 10.0",
"hinault. IN GPOS -22.6882 116.8652 250.0": "hinault.\t3600\tIN\tGPOS\t-22.6882 116.8652 250.0",
"VENERA. IN NIMLOC 75234159EAC457800920": "VENERA.\t3600\tIN\tNIMLOC\t75234159EAC457800920",
"VAXA. IN EID 3141592653589793": "VAXA.\t3600\tIN\tEID\t3141592653589793",
"444433332222111199990123000000ff. NSAP-PTR foo.bar.com.": "444433332222111199990123000000ff.\t3600\tIN\tNSAP-PTR\tfoo.bar.com.",
"lillee. IN GPOS -32.6882 116.8652 10.0": "lillee.\t3600\tIN\tGPOS\t-32.6882 116.8652 10.0",
"hinault. IN GPOS -22.6882 116.8652 250.0": "hinault.\t3600\tIN\tGPOS\t-22.6882 116.8652 250.0",
"VENERA. IN NIMLOC 75234159EAC457800920": "VENERA.\t3600\tIN\tNIMLOC\t75234159EAC457800920",
"VAXA. IN EID 3141592653589793": "VAXA.\t3600\tIN\tEID\t3141592653589793",
}
for i, o := range dt {
rr, err := NewRR(i)
@ -1507,7 +1507,7 @@ func TestPackCAA(t *testing.T) {
func TestParseURI(t *testing.T) {
lt := map[string]string{
"_http._tcp. IN URI 10 1 \"http://www.example.com/path\"": "_http._tcp.\t3600\tIN\tURI\t10 1 \"http://www.example.com/path\"",
"_http._tcp. IN URI 10 1 \"\"": "_http._tcp.\t3600\tIN\tURI\t10 1 \"\"",
"_http._tcp. IN URI 10 1 \"\"": "_http._tcp.\t3600\tIN\tURI\t10 1 \"\"",
}
for i, o := range lt {
rr, err := NewRR(i)

View File

@ -49,7 +49,7 @@ func mkPrivateRR(rrtype uint16) *PrivateRR {
// Header return the RR header of r.
func (r *PrivateRR) Header() *RR_Header { return &r.Hdr }
func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() }
func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() }
// Private len and copy parts to satisfy RR interface.
func (r *PrivateRR) len() int { return r.Hdr.len() + r.Data.Len() }

76
sanitize.go Normal file
View File

@ -0,0 +1,76 @@
package dns
// Dedup removes identical RRs from rrs. It preserves the original ordering.
// TODO(miek): CNAME, DNAME 'n stuff.
func Dedup(rrs []RR) []RR {
m := make(map[string]RR)
keys := []string{}
for _, r := range rrs {
key := normalizedString(r)
keys = append(keys, key)
if _, ok := m[key]; ok {
// Shortest TTL wins.
if m[key].Header().Ttl > r.Header().Ttl {
m[key].Header().Ttl = r.Header().Ttl
}
continue
}
m[key] = r
}
// If the length of the result map equals the amount of RRs we got,
// it means they were all different. We can then just return the original
// rrset.
if len(m) == len(rrs) {
return rrs
}
var i = 0
for i, _ = range rrs {
if len(m) == 0 {
break
}
// We saved the key for each RR.
delete(m, keys[i])
}
return rrs[:i]
}
// normalizedString returns a normalized string from r. The TTL
// is removed and the domain name is lowercased.
func normalizedString(r RR) string {
// A string Go DNS makes has: domainname<TAB>TTL<TAB>...
b := []byte(r.String())
// find the first non-escaped tab, then another, so we capture
// where the TTL lives.
esc := false
ttlStart, ttlEnd := 0, 0
for i, c := range b {
if c == '\\' {
esc = true
continue
}
if esc {
esc = false
continue
}
if c == '\t' {
if ttlStart == 0 {
ttlStart = i
continue
}
if ttlEnd == 0 {
ttlEnd = i
break
}
}
if c >= 'A' && c <= 'Z' {
b[i] = c + 32
}
}
// remove TTL.
copy(b[ttlStart:], b[ttlEnd:])
cut := ttlEnd - ttlStart
return string(b[:len(b)-cut])
}

83
sanitize_test.go Normal file
View File

@ -0,0 +1,83 @@
package dns
import "testing"
func TestDedup(t *testing.T) {
in := []RR{
newRR(t, "miek.nl. IN A 127.0.0.1"),
newRR(t, "miek.nl. IN A 127.0.0.1"),
}
out := Dedup(in)
if len(out) != 1 && out[0].String() != "miek.nl. IN A 127.0.0.1" {
dump(out, t)
t.Errorf("dedup failed, expected %d, got %d", 1, len(out))
}
in = []RR{}
out = Dedup(in)
if len(out) != 0 {
dump(out, t)
t.Errorf("dedup failed, expected %d, got %d", 0, len(out))
}
in = []RR{
newRR(t, "miEk.nl. 2000 IN A 127.0.0.1"),
newRR(t, "mieK.Nl. 1000 IN A 127.0.0.1"),
}
out = Dedup(in)
if len(out) != 1 {
dump(out, t)
t.Errorf("dedup failed, expected %d, got %d", 2, len(out))
}
in = []RR{
newRR(t, "miek.nl. IN A 127.0.0.1"),
newRR(t, "miek.nl. CH A 127.0.0.1"),
}
out = Dedup(in)
if len(out) != 2 {
dump(out, t)
t.Errorf("dedup failed, expected %d, got %d", 2, len(out))
}
in = []RR{
newRR(t, "miek.nl. CH A 127.0.0.1"),
newRR(t, "miek.nl. IN A 127.0.0.1"),
newRR(t, "mIek.Nl. IN A 127.0.0.1"),
}
out = Dedup(in)
if len(out) != 2 {
// TODO(miek): check ordering.
dump(out, t)
t.Errorf("dedup failed, expected %d, got %d", 2, len(out))
}
}
func dump(rrs []RR, t *testing.T) {
t.Logf("********\n")
for _, r := range rrs {
t.Logf("%v\n", r)
}
}
func TestNormalizedString(t *testing.T) {
tests := map[RR]string{
newRR(t, "mIEk.Nl. 3600 IN A 127.0.0.1"): "miek.nl.\tIN\tA\t127.0.0.1",
newRR(t, "m\\ iek.nL. 3600 IN A 127.0.0.1"): "m\\ iek.nl.\tIN\tA\t127.0.0.1",
newRR(t, "m\\\tIeK.nl. 3600 in A 127.0.0.1"): "m\\tiek.nl.\tIN\tA\t127.0.0.1",
}
for tc, expected := range tests {
a1 := normalizedString(tc)
if a1 != expected {
t.Logf("expected %s, got %s", expected, a1)
t.Fail()
}
}
}
func newRR(t *testing.T, s string) RR {
r, e := NewRR(s)
if e != nil {
t.Logf("newRR: %s", e)
}
return r
}