Update IsDomainName

This new functions just compiles the domain to wire format, if that
works, the name is deemed OK. It is also much less strict than the
older code. Almost everything is allowed in the name, except two
dots back to back (there is an explicit test for that).
This commit is contained in:
Miek Gieben 2013-09-10 18:09:22 +00:00
parent 9c72d3aa52
commit 9c1ee5d5ca
6 changed files with 62 additions and 63 deletions

View File

@ -160,40 +160,9 @@ func (dns *Msg) IsEdns0() *OPT {
// Note that non fully qualified domain name is considered valid, in this case the
// last label is counted in the number of labels.
// When false is returned the number of labels is not defined.
func IsDomainName(s string) (int, bool) {
// use PackDomainName
if buf == nil {
buf = make([]byte, 256)
}
lenmsg, err := PackDomainName(s, buf, 0, nil, false)
if err != nil {
return 0, false
}
// There are no compression pointers, because the map was nil, so
// walk the binary name and count the length bits - this is the number
// of labels.
off := 0
labels := 0
Loop:
for {
if off >= lenmsg {
return labels, false
}
c := int(buf[off])
switch c & 0xC0 {
case 0x00:
if c == 0x00 {
// end of name
break
}
if off+c > lenmsg {
return labels, false
}
labels++
off += c
}
}
return labels, true
func IsDomainName(s string) (labels int, ok bool) {
_, labels, err := packDomainName(s, nil, 0, nil, false)
return labels, err == nil
}
// IsSubDomain checks if child is indeed a child of the parent. Both child and

View File

@ -222,8 +222,7 @@ func (rr *RRSIG) Sign(k PrivateKey, rrset []RR) error {
rr.OrigTtl = rrset[0].Header().Ttl
rr.TypeCovered = rrset[0].Header().Rrtype
rr.TypeCovered = rrset[0].Header().Rrtype
x := CountLabel(rrset[0].Header().Name)
rr.Labels = uint8(x)
rr.Labels = uint8(CountLabel(rrset[0].Header().Name))
if strings.HasPrefix(rrset[0].Header().Name, "*") {
rr.Labels-- // wildcard, remove from label count

View File

@ -173,7 +173,7 @@ func TestSignVerify(t *testing.T) {
sig := new(RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.TypeCovered = soa.Hdr.Rrtype
sig.Labels, _, _ = IsDomainName(soa.Hdr.Name)
sig.Labels = uint8(CountLabel(soa.Hdr.Name))
sig.OrigTtl = soa.Hdr.Ttl
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"

View File

@ -95,20 +95,21 @@ domainLoop:
func TestIsDomainName(t *testing.T) {
type ret struct {
ok bool
lab uint8
l uint8
lab int
}
names := map[string]*ret{
"www.example.com": &ret{true, 3, 15},
"www.example.com.": &ret{true, 3, 16},
"mi\\k.nl.": &ret{true, 2, 8},
"mi\\k.nl": &ret{true, 2, 7},
"..": &ret{false, 1},
"@.": &ret{true, 1},
"www.example.com": &ret{true, 3},
"www.example.com.": &ret{true, 3},
"mi\\k.nl.": &ret{true, 2},
"mi\\k.nl": &ret{true, 2},
}
for d, ok := range names {
l1, l2, k := IsDomainName(d)
if ok.ok != k || ok.lab != l1 || ok.l != l2 {
t.Logf("Got %v %d %d for %s ", k, l1, l2, d)
t.Logf(" %v %d %d for %s ", ok.ok, ok.lab, ok.l, d)
l, k := IsDomainName(d)
if ok.ok != k || ok.lab != l {
t.Logf(" got %v %d for %s ", k, l, d)
t.Logf("have %v %d for %s ", ok.ok, ok.lab, d)
t.Fail()
}
}

57
msg.go
View File

@ -212,14 +212,31 @@ var RcodeToString = map[int]string{
// map needs to hold a mapping between domain names and offsets
// pointing into msg[].
func PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {
lenmsg := len(msg)
off1, _, err = packDomainName(s, msg, off, compression, compress)
return
}
func packDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, labels int, err error) {
// special case if msg == nil
lenmsg := 256
if msg != nil {
lenmsg = len(msg)
}
ls := len(s)
if ls == 0 { // Ok, for instance when dealing with update RR without any rdata.
return off, nil
return off, 0, nil
}
// If not fully qualified, error out
if s[ls-1] != '.' {
return lenmsg, ErrFqdn
// If not fully qualified, error out, but only if msg == nil #ugly
switch {
case msg == nil:
if s[ls-1] != '.' {
s += "."
ls++
}
case msg != nil:
if s[ls-1] != '.' {
return lenmsg, 0, ErrFqdn
}
}
// Each dot ends a segment of the name.
// We trade each dot byte for a length byte.
@ -239,7 +256,7 @@ func PackDomainName(s string, msg []byte, off int, compression map[string]int, c
}
ls--
if off+1 > lenmsg {
return lenmsg, ErrBuf
return lenmsg, labels, ErrBuf
}
// check for \DDD
if i+2 < ls && bs[i] >= '0' && bs[i] <= '9' &&
@ -255,22 +272,30 @@ func PackDomainName(s string, msg []byte, off int, compression map[string]int, c
}
if bs[i] == '.' {
if i > 0 && bs[i-1] == '.' {
// two dots back to back is not legal
return lenmsg, labels, ErrRdata
}
if i-begin >= 1<<6 { // top two bits of length must be clear
return lenmsg, ErrRdata
return lenmsg, labels, ErrRdata
}
// off can already (we're in a loop) be bigger than len(msg)
// this happens when a name isn't fully qualified
if off+1 > lenmsg {
return lenmsg, ErrBuf
return lenmsg, labels, ErrBuf
}
if msg != nil {
msg[off] = byte(i - begin)
}
msg[off] = byte(i - begin)
offset := off
off++
for j := begin; j < i; j++ {
if off+1 > lenmsg {
return lenmsg, ErrBuf
return lenmsg, labels, ErrBuf
}
if msg != nil {
msg[off] = bs[j]
}
msg[off] = bs[j]
off++
}
// Dont try to compress '.'
@ -294,24 +319,28 @@ func PackDomainName(s string, msg []byte, off int, compression map[string]int, c
}
}
}
labels++
begin = i + 1
}
}
// Root label is special
if len(bs) == 1 && bs[0] == '.' {
return off, nil
return off, labels, nil
}
// If we did compression and we find something add the pointer here
if pointer != -1 {
// We have two bytes (14 bits) to put the pointer in
// if msg == nil, we will never do compression
msg[nameoffset], msg[nameoffset+1] = packUint16(uint16(pointer ^ 0xC000))
off = nameoffset + 1
goto End
}
msg[off] = 0
if msg != nil {
msg[off] = 0
}
End:
off++
return off, nil
return off, labels, nil
}
// Unpack a domain name.

View File

@ -357,7 +357,8 @@ func TestParseFailure(t *testing.T) {
"miek.nl. IN AAAA ::x",
"miek.nl. IN MX a0 miek.nl.",
"miek.nl aap IN MX mx.miek.nl.",
"miek.nl. IN CNAME ",
// "miek.nl. IN CNAME ", // actually valid nowadays, zero size rdata
"miek.nl. IN CNAME ..",
"miek.nl. PA MX 10 miek.nl.",
"miek.nl. ) IN MX 10 miek.nl.",
}
@ -365,7 +366,7 @@ func TestParseFailure(t *testing.T) {
for _, s := range tests {
_, err := NewRR(s)
if err == nil {
t.Log("Should have triggered an error")
t.Logf("Should have triggered an error: \"%s\"", s)
t.Fail()
}
}