Allow for escaping of dots in domainnames

This commit is contained in:
Miek Gieben 2011-02-21 20:33:36 +01:00
parent 865ba16420
commit c7c4d8061e
2 changed files with 110 additions and 80 deletions

101
msg.go
View File

@ -22,7 +22,7 @@ import (
"time" "time"
"strconv" "strconv"
"encoding/base64" "encoding/base64"
"encoding/base32" "encoding/base32"
"encoding/hex" "encoding/hex"
) )
@ -76,14 +76,14 @@ var Rr_str = map[uint16]string{
TypeLOC: "LOC", TypeLOC: "LOC",
TypeOPT: "OPT", TypeOPT: "OPT",
TypeDS: "DS", TypeDS: "DS",
TypeIPSECKEY: "IPSECKEY", TypeIPSECKEY: "IPSECKEY",
TypeSSHFP: "SSHFP", TypeSSHFP: "SSHFP",
TypeRRSIG: "RRSIG", TypeRRSIG: "RRSIG",
TypeNSEC: "NSEC", TypeNSEC: "NSEC",
TypeDNSKEY: "DNSKEY", TypeDNSKEY: "DNSKEY",
TypeNSEC3: "NSEC3", TypeNSEC3: "NSEC3",
TypeNSEC3PARAM: "NSEC3PARAM", TypeNSEC3PARAM: "NSEC3PARAM",
TypeTALINK: "TALINK", TypeTALINK: "TALINK",
TypeSPF: "SPF", TypeSPF: "SPF",
TypeTKEY: "TKEY", // Meta RR TypeTKEY: "TKEY", // Meta RR
TypeTSIG: "TSIG", // Meta RR TypeTSIG: "TSIG", // Meta RR
@ -147,6 +147,7 @@ func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) {
// Each dot ends a segment of the name. // Each dot ends a segment of the name.
// We trade each dot byte for a length byte. // We trade each dot byte for a length byte.
// Except for escaped dots (\.), which are normal dots.
// There is also a trailing zero. // There is also a trailing zero.
// Check that we have all the space we need. // Check that we have all the space we need.
tot := len(s) + 1 tot := len(s) + 1
@ -156,22 +157,32 @@ func packDomainName(s string, msg []byte, off int) (off1 int, ok bool) {
// Emit sequence of counted strings, chopping at dots. // Emit sequence of counted strings, chopping at dots.
begin := 0 begin := 0
for i := 0; i < len(s); i++ { bs := []byte(s)
if s[i] == '.' { ls := len(bs)
if i-begin >= 1<<6 { // top two bits of length must be clear for i := 0; i < ls; i++ {
return len(msg), false if bs[i] == '\\' {
} for j := i; j < len(s)-1; j++ {
msg[off] = byte(i - begin) bs[j] = bs[j+1]
off++ }
for j := begin; j < i; j++ { ls--
msg[off] = s[j] continue
off++ }
}
begin = i + 1 if bs[i] == '.' {
if i-begin >= 1<<6 { // top two bits of length must be clear
return len(msg), false
}
msg[off] = byte(i - begin)
off++
for j := begin; j < i; j++ {
msg[off] = bs[j]
off++
}
begin = i + 1
} }
} }
// Root label is special // Root label is special
if s == "." { if string(bs) == "." {
return off, true return off, true
} }
msg[off] = 0 msg[off] = 0
@ -367,39 +378,39 @@ func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, o
default: default:
//fmt.Fprintf(os.Stderr, "dns: unknown packing string tag %v", f.Tag) //fmt.Fprintf(os.Stderr, "dns: unknown packing string tag %v", f.Tag)
return len(msg), false return len(msg), false
case "base32": case "base32":
b32, err := packBase32([]byte(s)) b32, err := packBase32([]byte(s))
if err != nil { if err != nil {
//fmt.Fprintf(os.Stderr, "dns: overflow packing base32") //fmt.Fprintf(os.Stderr, "dns: overflow packing base32")
return len(msg), false return len(msg), false
} }
copy(msg[off:off+len(b32)], b32) copy(msg[off:off+len(b32)], b32)
off += len(b32) off += len(b32)
case "base64": case "base64":
b64, err := packBase64([]byte(s)) b64, err := packBase64([]byte(s))
if err != nil { if err != nil {
//fmt.Fprintf(os.Stderr, "dns: overflow packing base64") //fmt.Fprintf(os.Stderr, "dns: overflow packing base64")
return len(msg), false return len(msg), false
} }
copy(msg[off:off+len(b64)], b64) copy(msg[off:off+len(b64)], b64)
off += len(b64) off += len(b64)
/* /*
b64len := base64.StdEncoding.DecodedLen(len(s)) b64len := base64.StdEncoding.DecodedLen(len(s))
_, err := base64.StdEncoding.Decode(msg[off:off+b64len], []byte(s)) _, err := base64.StdEncoding.Decode(msg[off:off+b64len], []byte(s))
if err != nil { if err != nil {
//fmt.Fprintf(os.Stderr, "dns: overflow packing base64") //fmt.Fprintf(os.Stderr, "dns: overflow packing base64")
return len(msg), false return len(msg), false
} }
off += b64len off += b64len
*/ */
case "domain-name": case "domain-name":
off, ok = packDomainName(s, msg, off) off, ok = packDomainName(s, msg, off)
if !ok { if !ok {
//fmt.Fprintf(os.Stderr, "dns: overflow packing domain-name") //fmt.Fprintf(os.Stderr, "dns: overflow packing domain-name")
return len(msg), false return len(msg), false
} }
case "size-hex": case "size-hex":
fallthrough; fallthrough
case "hex": case "hex":
// There is no length encoded here // There is no length encoded here
h, e := hex.DecodeString(s) h, e := hex.DecodeString(s)
@ -604,8 +615,8 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int,
consumed = 2 // Algorithm(1) + Type(1) consumed = 2 // Algorithm(1) + Type(1)
case "RR_NSEC3PARAM": case "RR_NSEC3PARAM":
consumed = 5 // Hash(1) + Flags(1) + Iterations(2) + SaltLength(1) consumed = 5 // Hash(1) + Flags(1) + Iterations(2) + SaltLength(1)
case "RR_RFC3597": case "RR_RFC3597":
fallthrough; // Rest is the unknown data fallthrough // Rest is the unknown data
default: default:
consumed = 0 // return len(msg), false? consumed = 0 // return len(msg), false?
} }
@ -636,7 +647,7 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int,
//fmt.Fprintf(os.Stderr, "dns: failure unpacking domain-name") //fmt.Fprintf(os.Stderr, "dns: failure unpacking domain-name")
return len(msg), false return len(msg), false
} }
case "size-base32": case "size-base32":
var size int var size int
switch val.Type().Name() { switch val.Type().Name() {
case "RR_NSEC3": case "RR_NSEC3":
@ -645,15 +656,15 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int,
name := val.FieldByName("HashLength") name := val.FieldByName("HashLength")
size = int(name.(*reflect.UintValue).Get()) size = int(name.(*reflect.UintValue).Get())
} }
} }
if off+size > len(msg) { if off+size > len(msg) {
//fmt.Fprintf(os.Stderr, "dns: failure unpacking size-base32 string") //fmt.Fprintf(os.Stderr, "dns: failure unpacking size-base32 string")
return len(msg), false return len(msg), false
} }
s = unpackBase32(msg[off : off+size]) s = unpackBase32(msg[off : off+size])
off += size off += size
case "size-hex": case "size-hex":
// a "size" string, but a it must be encoded in hex in the string // a "size" string, but a it must be encoded in hex in the string
var size int var size int
switch val.Type().Name() { switch val.Type().Name() {
case "RR_NSEC3": case "RR_NSEC3":
@ -665,7 +676,7 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int,
name := val.FieldByName("HashLength") name := val.FieldByName("HashLength")
size = int(name.(*reflect.UintValue).Get()) size = int(name.(*reflect.UintValue).Get())
} }
} }
if off+size > len(msg) { if off+size > len(msg) {
//fmt.Fprintf(os.Stderr, "dns: failure unpacking hex-size string") //fmt.Fprintf(os.Stderr, "dns: failure unpacking hex-size string")
return len(msg), false return len(msg), false
@ -800,11 +811,11 @@ func unpackRR(msg []byte, off int) (rr RR, off1 int, ok bool) {
// make an rr of that type and re-unpack. // make an rr of that type and re-unpack.
// again inefficient but doesn't need to be fast. // again inefficient but doesn't need to be fast.
mk, known := rr_mk[int(h.Rrtype)] mk, known := rr_mk[int(h.Rrtype)]
if !known { if !known {
rr = new(RR_RFC3597) rr = new(RR_RFC3597)
} else { } else {
rr = mk() rr = mk()
} }
off, ok = unpackStruct(rr, msg, off0) off, ok = unpackStruct(rr, msg, off0)
if off != end { if off != end {
return &h, end, true return &h, end, true

View File

@ -1,8 +1,8 @@
package dns package dns
import ( import (
"fmt" "fmt"
"net" "net"
"testing" "testing"
"crypto/rsa" "crypto/rsa"
) )
@ -75,7 +75,7 @@ Activate: 20110109154937`
k.Hdr.Rrtype = TypeDNSKEY k.Hdr.Rrtype = TypeDNSKEY
k.Hdr.Class = ClassINET k.Hdr.Class = ClassINET
k.Hdr.Name = "miek.nl." k.Hdr.Name = "miek.nl."
k.Hdr.Ttl = 3600 k.Hdr.Ttl = 3600
k.Protocol = 3 k.Protocol = 3
k.Flags = 256 k.Flags = 256
p, _ := k.PrivateKeySetString(a) p, _ := k.PrivateKeySetString(a)
@ -92,47 +92,66 @@ Activate: 20110109154937`
t.Fail() t.Fail()
} }
soa := new(RR_SOA) soa := new(RR_SOA)
soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0} soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
soa.Ns = "open.nlnetlabs.nl." soa.Ns = "open.nlnetlabs.nl."
soa.Mbox = "miekg.atoom.net." soa.Mbox = "miekg.atoom.net."
soa.Serial = 1293945905 soa.Serial = 1293945905
soa.Refresh = 14400 soa.Refresh = 14400
soa.Retry = 3600 soa.Retry = 3600
soa.Expire = 604800 soa.Expire = 604800
soa.Minttl = 86400 soa.Minttl = 86400
sig := new(RR_RRSIG) sig := new(RR_RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05" sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05" sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
sig.KeyTag = k.KeyTag() sig.KeyTag = k.KeyTag()
sig.SignerName = k.Hdr.Name sig.SignerName = k.Hdr.Name
sig.Algorithm = k.Algorithm sig.Algorithm = k.Algorithm
sig.Sign(p, []RR{soa}) sig.Sign(p, []RR{soa})
fmt.Printf("%v\n%v\n%v\n", k, soa, sig) fmt.Printf("%v\n%v\n%v\n", k, soa, sig)
} }
func TestA(t *testing.T) { func TestA(t *testing.T) {
a := new(RR_A) a := new(RR_A)
a.Hdr = RR_Header{"miek.nl.", TypeA, ClassINET, 14400, 0} a.Hdr = RR_Header{"miek.nl.", TypeA, ClassINET, 14400, 0}
a.A = net.ParseIP("192.168.1.1") a.A = net.ParseIP("192.168.1.1")
str := a.String() str := a.String()
if str != "miek.nl.\t14400\tIN\tA\t192.168.1.1" { if str != "miek.nl.\t14400\tIN\tA\t192.168.1.1" {
t.Log(str) t.Log(str)
t.Fail() t.Fail()
} }
} }
func TestQuadA(t *testing.T) { func TestQuadA(t *testing.T) {
a := new(RR_AAAA) a := new(RR_AAAA)
a.Hdr = RR_Header{"miek.nl.", TypeAAAA, ClassINET, 14400, 0} a.Hdr = RR_Header{"miek.nl.", TypeAAAA, ClassINET, 14400, 0}
a.AAAA = net.ParseIP("::1") a.AAAA = net.ParseIP("::1")
str := a.String() str := a.String()
if str != "miek.nl.\t14400\tIN\tAAAA\t::1" { if str != "miek.nl.\t14400\tIN\tAAAA\t::1" {
t.Log(str) t.Log(str)
t.Fail()
}
}
func TestDotInName(t *testing.T) {
buf := make([]byte, 20)
packDomainName("aa\\.bb.nl", buf, 0)
// index 3 must be a real dot
if buf[3] != '.' {
t.Log("Dot should be a real dot")
t.Fail() t.Fail()
} }
if buf[6] != 2 {
t.Log("This must have the value 2")
t.Fail()
}
dom, _, ok := unpackDomainName(buf, 0)
// printing it should yield the backspace again
println(dom)
println(ok)
} }