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:
parent
9c72d3aa52
commit
9c1ee5d5ca
37
defaults.go
37
defaults.go
|
@ -160,40 +160,9 @@ func (dns *Msg) IsEdns0() *OPT {
|
||||||
// Note that non fully qualified domain name is considered valid, in this case the
|
// Note that non fully qualified domain name is considered valid, in this case the
|
||||||
// last label is counted in the number of labels.
|
// last label is counted in the number of labels.
|
||||||
// When false is returned the number of labels is not defined.
|
// When false is returned the number of labels is not defined.
|
||||||
func IsDomainName(s string) (int, bool) {
|
func IsDomainName(s string) (labels int, ok bool) {
|
||||||
// use PackDomainName
|
_, labels, err := packDomainName(s, nil, 0, nil, false)
|
||||||
if buf == nil {
|
return labels, err == 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSubDomain checks if child is indeed a child of the parent. Both child and
|
// IsSubDomain checks if child is indeed a child of the parent. Both child and
|
||||||
|
|
|
@ -222,8 +222,7 @@ func (rr *RRSIG) Sign(k PrivateKey, rrset []RR) error {
|
||||||
rr.OrigTtl = rrset[0].Header().Ttl
|
rr.OrigTtl = rrset[0].Header().Ttl
|
||||||
rr.TypeCovered = rrset[0].Header().Rrtype
|
rr.TypeCovered = rrset[0].Header().Rrtype
|
||||||
rr.TypeCovered = rrset[0].Header().Rrtype
|
rr.TypeCovered = rrset[0].Header().Rrtype
|
||||||
x := CountLabel(rrset[0].Header().Name)
|
rr.Labels = uint8(CountLabel(rrset[0].Header().Name))
|
||||||
rr.Labels = uint8(x)
|
|
||||||
|
|
||||||
if strings.HasPrefix(rrset[0].Header().Name, "*") {
|
if strings.HasPrefix(rrset[0].Header().Name, "*") {
|
||||||
rr.Labels-- // wildcard, remove from label count
|
rr.Labels-- // wildcard, remove from label count
|
||||||
|
|
|
@ -173,7 +173,7 @@ func TestSignVerify(t *testing.T) {
|
||||||
sig := new(RRSIG)
|
sig := new(RRSIG)
|
||||||
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
|
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
|
||||||
sig.TypeCovered = soa.Hdr.Rrtype
|
sig.TypeCovered = soa.Hdr.Rrtype
|
||||||
sig.Labels, _, _ = IsDomainName(soa.Hdr.Name)
|
sig.Labels = uint8(CountLabel(soa.Hdr.Name))
|
||||||
sig.OrigTtl = soa.Hdr.Ttl
|
sig.OrigTtl = soa.Hdr.Ttl
|
||||||
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"
|
||||||
|
|
|
@ -95,20 +95,21 @@ domainLoop:
|
||||||
func TestIsDomainName(t *testing.T) {
|
func TestIsDomainName(t *testing.T) {
|
||||||
type ret struct {
|
type ret struct {
|
||||||
ok bool
|
ok bool
|
||||||
lab uint8
|
lab int
|
||||||
l uint8
|
|
||||||
}
|
}
|
||||||
names := map[string]*ret{
|
names := map[string]*ret{
|
||||||
"www.example.com": &ret{true, 3, 15},
|
"..": &ret{false, 1},
|
||||||
"www.example.com.": &ret{true, 3, 16},
|
"@.": &ret{true, 1},
|
||||||
"mi\\k.nl.": &ret{true, 2, 8},
|
"www.example.com": &ret{true, 3},
|
||||||
"mi\\k.nl": &ret{true, 2, 7},
|
"www.example.com.": &ret{true, 3},
|
||||||
|
"mi\\k.nl.": &ret{true, 2},
|
||||||
|
"mi\\k.nl": &ret{true, 2},
|
||||||
}
|
}
|
||||||
for d, ok := range names {
|
for d, ok := range names {
|
||||||
l1, l2, k := IsDomainName(d)
|
l, k := IsDomainName(d)
|
||||||
if ok.ok != k || ok.lab != l1 || ok.l != l2 {
|
if ok.ok != k || ok.lab != l {
|
||||||
t.Logf("Got %v %d %d for %s ", k, l1, l2, d)
|
t.Logf(" got %v %d for %s ", k, l, d)
|
||||||
t.Logf(" %v %d %d for %s ", ok.ok, ok.lab, ok.l, d)
|
t.Logf("have %v %d for %s ", ok.ok, ok.lab, d)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
57
msg.go
57
msg.go
|
@ -212,14 +212,31 @@ var RcodeToString = map[int]string{
|
||||||
// map needs to hold a mapping between domain names and offsets
|
// map needs to hold a mapping between domain names and offsets
|
||||||
// pointing into msg[].
|
// pointing into msg[].
|
||||||
func PackDomainName(s string, msg []byte, off int, compression map[string]int, compress bool) (off1 int, err error) {
|
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)
|
ls := len(s)
|
||||||
if ls == 0 { // Ok, for instance when dealing with update RR without any rdata.
|
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 not fully qualified, error out, but only if msg == nil #ugly
|
||||||
if s[ls-1] != '.' {
|
switch {
|
||||||
return lenmsg, ErrFqdn
|
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.
|
// 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.
|
||||||
|
@ -239,7 +256,7 @@ func PackDomainName(s string, msg []byte, off int, compression map[string]int, c
|
||||||
}
|
}
|
||||||
ls--
|
ls--
|
||||||
if off+1 > lenmsg {
|
if off+1 > lenmsg {
|
||||||
return lenmsg, ErrBuf
|
return lenmsg, labels, ErrBuf
|
||||||
}
|
}
|
||||||
// check for \DDD
|
// check for \DDD
|
||||||
if i+2 < ls && bs[i] >= '0' && bs[i] <= '9' &&
|
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 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
|
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)
|
// off can already (we're in a loop) be bigger than len(msg)
|
||||||
// this happens when a name isn't fully qualified
|
// this happens when a name isn't fully qualified
|
||||||
if off+1 > lenmsg {
|
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
|
offset := off
|
||||||
off++
|
off++
|
||||||
for j := begin; j < i; j++ {
|
for j := begin; j < i; j++ {
|
||||||
if off+1 > lenmsg {
|
if off+1 > lenmsg {
|
||||||
return lenmsg, ErrBuf
|
return lenmsg, labels, ErrBuf
|
||||||
|
}
|
||||||
|
if msg != nil {
|
||||||
|
msg[off] = bs[j]
|
||||||
}
|
}
|
||||||
msg[off] = bs[j]
|
|
||||||
off++
|
off++
|
||||||
}
|
}
|
||||||
// Dont try to compress '.'
|
// 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
|
begin = i + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Root label is special
|
// Root label is special
|
||||||
if len(bs) == 1 && bs[0] == '.' {
|
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 we did compression and we find something add the pointer here
|
||||||
if pointer != -1 {
|
if pointer != -1 {
|
||||||
// We have two bytes (14 bits) to put the pointer in
|
// 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))
|
msg[nameoffset], msg[nameoffset+1] = packUint16(uint16(pointer ^ 0xC000))
|
||||||
off = nameoffset + 1
|
off = nameoffset + 1
|
||||||
goto End
|
goto End
|
||||||
}
|
}
|
||||||
msg[off] = 0
|
if msg != nil {
|
||||||
|
msg[off] = 0
|
||||||
|
}
|
||||||
End:
|
End:
|
||||||
off++
|
off++
|
||||||
return off, nil
|
return off, labels, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unpack a domain name.
|
// Unpack a domain name.
|
||||||
|
|
|
@ -357,7 +357,8 @@ func TestParseFailure(t *testing.T) {
|
||||||
"miek.nl. IN AAAA ::x",
|
"miek.nl. IN AAAA ::x",
|
||||||
"miek.nl. IN MX a0 miek.nl.",
|
"miek.nl. IN MX a0 miek.nl.",
|
||||||
"miek.nl aap IN MX mx.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. PA MX 10 miek.nl.",
|
||||||
"miek.nl. ) IN MX 10 miek.nl.",
|
"miek.nl. ) IN MX 10 miek.nl.",
|
||||||
}
|
}
|
||||||
|
@ -365,7 +366,7 @@ func TestParseFailure(t *testing.T) {
|
||||||
for _, s := range tests {
|
for _, s := range tests {
|
||||||
_, err := NewRR(s)
|
_, err := NewRR(s)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Log("Should have triggered an error")
|
t.Logf("Should have triggered an error: \"%s\"", s)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue