diff --git a/msg.go b/msg.go index d98ac87f..a10b51c0 100644 --- a/msg.go +++ b/msg.go @@ -22,7 +22,7 @@ import ( "time" "strconv" "encoding/base64" - "encoding/base32" + "encoding/base32" "encoding/hex" ) @@ -76,14 +76,14 @@ var Rr_str = map[uint16]string{ TypeLOC: "LOC", TypeOPT: "OPT", TypeDS: "DS", - TypeIPSECKEY: "IPSECKEY", + TypeIPSECKEY: "IPSECKEY", TypeSSHFP: "SSHFP", TypeRRSIG: "RRSIG", TypeNSEC: "NSEC", TypeDNSKEY: "DNSKEY", TypeNSEC3: "NSEC3", TypeNSEC3PARAM: "NSEC3PARAM", - TypeTALINK: "TALINK", + TypeTALINK: "TALINK", TypeSPF: "SPF", TypeTKEY: "TKEY", // 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. // We trade each dot byte for a length byte. + // Except for escaped dots (\.), which are normal dots. // There is also a trailing zero. // Check that we have all the space we need. 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. begin := 0 - for i := 0; i < len(s); i++ { - if s[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] = s[j] - off++ - } - begin = i + 1 + bs := []byte(s) + ls := len(bs) + for i := 0; i < ls; i++ { + if bs[i] == '\\' { + for j := i; j < len(s)-1; j++ { + bs[j] = bs[j+1] + } + ls-- + continue + } + + 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 - if s == "." { + if string(bs) == "." { return off, true } msg[off] = 0 @@ -367,39 +378,39 @@ func packStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, o default: //fmt.Fprintf(os.Stderr, "dns: unknown packing string tag %v", f.Tag) return len(msg), false - case "base32": - b32, err := packBase32([]byte(s)) + case "base32": + b32, err := packBase32([]byte(s)) if err != nil { //fmt.Fprintf(os.Stderr, "dns: overflow packing base32") return len(msg), false } - copy(msg[off:off+len(b32)], b32) - off += len(b32) + copy(msg[off:off+len(b32)], b32) + off += len(b32) case "base64": - b64, err := packBase64([]byte(s)) + b64, err := packBase64([]byte(s)) if err != nil { //fmt.Fprintf(os.Stderr, "dns: overflow packing base64") return len(msg), false } - copy(msg[off:off+len(b64)], b64) - off += len(b64) - /* - b64len := base64.StdEncoding.DecodedLen(len(s)) - _, err := base64.StdEncoding.Decode(msg[off:off+b64len], []byte(s)) - if err != nil { - //fmt.Fprintf(os.Stderr, "dns: overflow packing base64") - return len(msg), false - } - off += b64len - */ + copy(msg[off:off+len(b64)], b64) + off += len(b64) + /* + b64len := base64.StdEncoding.DecodedLen(len(s)) + _, err := base64.StdEncoding.Decode(msg[off:off+b64len], []byte(s)) + if err != nil { + //fmt.Fprintf(os.Stderr, "dns: overflow packing base64") + return len(msg), false + } + off += b64len + */ case "domain-name": off, ok = packDomainName(s, msg, off) if !ok { //fmt.Fprintf(os.Stderr, "dns: overflow packing domain-name") return len(msg), false } - case "size-hex": - fallthrough; + case "size-hex": + fallthrough case "hex": // There is no length encoded here 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) case "RR_NSEC3PARAM": consumed = 5 // Hash(1) + Flags(1) + Iterations(2) + SaltLength(1) - case "RR_RFC3597": - fallthrough; // Rest is the unknown data + case "RR_RFC3597": + fallthrough // Rest is the unknown data default: 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") return len(msg), false } - case "size-base32": + case "size-base32": var size int switch val.Type().Name() { case "RR_NSEC3": @@ -645,15 +656,15 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, name := val.FieldByName("HashLength") size = int(name.(*reflect.UintValue).Get()) } - } + } if off+size > len(msg) { //fmt.Fprintf(os.Stderr, "dns: failure unpacking size-base32 string") return len(msg), false } s = unpackBase32(msg[off : off+size]) off += size - case "size-hex": - // a "size" string, but a it must be encoded in hex in the string + case "size-hex": + // a "size" string, but a it must be encoded in hex in the string var size int switch val.Type().Name() { case "RR_NSEC3": @@ -665,7 +676,7 @@ func unpackStructValue(val *reflect.StructValue, msg []byte, off int) (off1 int, name := val.FieldByName("HashLength") size = int(name.(*reflect.UintValue).Get()) } - } + } if off+size > len(msg) { //fmt.Fprintf(os.Stderr, "dns: failure unpacking hex-size string") 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. // again inefficient but doesn't need to be fast. mk, known := rr_mk[int(h.Rrtype)] - if !known { - rr = new(RR_RFC3597) + if !known { + rr = new(RR_RFC3597) } else { - rr = mk() - } + rr = mk() + } off, ok = unpackStruct(rr, msg, off0) if off != end { return &h, end, true diff --git a/parse_test.go b/parse_test.go index 3299e5f6..ef9adc08 100644 --- a/parse_test.go +++ b/parse_test.go @@ -1,8 +1,8 @@ package dns import ( - "fmt" - "net" + "fmt" + "net" "testing" "crypto/rsa" ) @@ -75,7 +75,7 @@ Activate: 20110109154937` k.Hdr.Rrtype = TypeDNSKEY k.Hdr.Class = ClassINET k.Hdr.Name = "miek.nl." - k.Hdr.Ttl = 3600 + k.Hdr.Ttl = 3600 k.Protocol = 3 k.Flags = 256 p, _ := k.PrivateKeySetString(a) @@ -92,47 +92,66 @@ Activate: 20110109154937` t.Fail() } - soa := new(RR_SOA) - soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0} - soa.Ns = "open.nlnetlabs.nl." - soa.Mbox = "miekg.atoom.net." - soa.Serial = 1293945905 - soa.Refresh = 14400 - soa.Retry = 3600 - soa.Expire = 604800 - soa.Minttl = 86400 + soa := new(RR_SOA) + soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0} + soa.Ns = "open.nlnetlabs.nl." + soa.Mbox = "miekg.atoom.net." + soa.Serial = 1293945905 + soa.Refresh = 14400 + soa.Retry = 3600 + soa.Expire = 604800 + soa.Minttl = 86400 - sig := new(RR_RRSIG) - sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} - 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.KeyTag = k.KeyTag() - sig.SignerName = k.Hdr.Name - sig.Algorithm = k.Algorithm + sig := new(RR_RRSIG) + sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0} + 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.KeyTag = k.KeyTag() + sig.SignerName = k.Hdr.Name + sig.Algorithm = k.Algorithm - sig.Sign(p, []RR{soa}) - fmt.Printf("%v\n%v\n%v\n", k, soa, sig) + sig.Sign(p, []RR{soa}) + fmt.Printf("%v\n%v\n%v\n", k, soa, sig) } func TestA(t *testing.T) { - a := new(RR_A) - a.Hdr = RR_Header{"miek.nl.", TypeA, ClassINET, 14400, 0} - a.A = net.ParseIP("192.168.1.1") - str := a.String() - if str != "miek.nl.\t14400\tIN\tA\t192.168.1.1" { - t.Log(str) - t.Fail() - } + a := new(RR_A) + a.Hdr = RR_Header{"miek.nl.", TypeA, ClassINET, 14400, 0} + a.A = net.ParseIP("192.168.1.1") + str := a.String() + if str != "miek.nl.\t14400\tIN\tA\t192.168.1.1" { + t.Log(str) + t.Fail() + } } func TestQuadA(t *testing.T) { - a := new(RR_AAAA) - a.Hdr = RR_Header{"miek.nl.", TypeAAAA, ClassINET, 14400, 0} - a.AAAA = net.ParseIP("::1") - str := a.String() - if str != "miek.nl.\t14400\tIN\tAAAA\t::1" { - t.Log(str) + a := new(RR_AAAA) + a.Hdr = RR_Header{"miek.nl.", TypeAAAA, ClassINET, 14400, 0} + a.AAAA = net.ParseIP("::1") + str := a.String() + if str != "miek.nl.\t14400\tIN\tAAAA\t::1" { + 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() } + 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) }