From 020002b9e005d15d015949a3fd3e7ff909d1bd85 Mon Sep 17 00:00:00 2001 From: Roland Shoemaker Date: Wed, 17 Jun 2015 16:06:31 -0700 Subject: [PATCH] Switch setCAA, CAA.String to presentation format, add various encoding helpers --- msg.go | 45 ++++++++++++++++++++++++++++++++++- parse_test.go | 22 +++++++++++++++++ types.go | 64 ++++++++++++++++++++++++++++++++++---------------- update_test.go | 2 +- zscan_rr.go | 56 +++++++++++++++++-------------------------- 5 files changed, 132 insertions(+), 57 deletions(-) diff --git a/msg.go b/msg.go index 31b1558a..e26ce9c9 100644 --- a/msg.go +++ b/msg.go @@ -100,7 +100,7 @@ var TypeToString = map[uint16]string{ TypeANY: "ANY", // Meta RR TypeATMA: "ATMA", TypeAXFR: "AXFR", // Meta RR - TypeCAA: "TYPE257", + TypeCAA: "CAA", TypeCDNSKEY: "CDNSKEY", TypeCDS: "CDS", TypeCERT: "CERT", @@ -543,6 +543,36 @@ func packTxtString(s string, msg []byte, offset int, tmp []byte) (int, error) { return offset, nil } +func packOctetString(s string, msg []byte, offset int, tmp []byte) (int, error) { + if offset >= len(msg) { + return offset, ErrBuf + } + bs := tmp[:len(s)] + copy(bs, s) + for i := 0; i < len(bs); i++ { + if len(msg) <= offset { + return offset, ErrBuf + } + if bs[i] == '\\' { + i++ + if i == len(bs) { + break + } + // check for \DDD + if i+2 < len(bs) && isDigit(bs[i]) && isDigit(bs[i+1]) && isDigit(bs[i+2]) { + msg[offset] = dddToByte(bs[i:]) + i += 2 + } else { + msg[offset] = bs[i] + } + } else { + msg[offset] = bs[i] + } + offset++ + } + return offset, nil +} + func unpackTxt(msg []byte, offset, rdend int) ([]string, int, error) { var err error var ss []string @@ -890,6 +920,12 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str // length of string. String is RAW (not encoded in hex, nor base64) copy(msg[off:off+len(s)], s) off += len(s) + case `dns:"octet"`: + var varstrTmp []byte + off, err = packOctetString(fv.String(), msg, off, varstrTmp) + if err != nil { + return lenmsg, err + } case `dns:"txt"`: fallthrough case "": @@ -1254,6 +1290,13 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er switch val.Type().Field(i).Tag { default: return lenmsg, &Error{"bad tag unpacking string: " + val.Type().Field(i).Tag.Get("dns")} + case `dns:"octet"`: + strend := lenrd + if strend > lenmsg { + return lenmsg, &Error{err: "overflow unpacking octet"} + } + s = string(msg[off:strend]) + off = strend case `dns:"hex"`: hexend := lenrd if val.FieldByName("Hdr").FieldByName("Rrtype").Uint() == uint64(TypeHIP) { diff --git a/parse_test.go b/parse_test.go index b0d02f45..1c2ef865 100644 --- a/parse_test.go +++ b/parse_test.go @@ -1455,3 +1455,25 @@ func TestParseHINFO(t *testing.T) { } } } + +func TestParseCAA(t *testing.T) { + lt := map[string]string{ + "example.net. CAA 0 issue \"symantec.com\"": "example.net.\t3600\tIN\tCAA\t0 issue \"symantec.com\"", + "example.net. CAA 0 issuewild \"symantec.com; stuff\"": "example.net.\t3600\tIN\tCAA\t0 issuewild \"symantec.com; stuff\"", + "example.net. CAA 128 tbs \"critical\"": "example.net.\t3600\tIN\tCAA\t128 tbs \"critical\"", + "example.net. CAA 2 auth \"0>09\\006\\010+\\006\\001\\004\\001\\214y\\002\\003\\001\\006\\009`\\134H\\001e\\003\\004\\002\\001\\004 y\\209\\012\\221r\\220\\156Q\\218\\150\\150{\\166\\245:\\231\\182%\\157:\\133\\179}\\1923r\\238\\151\\255\\128q\\145\\002\\001\\000\"": "example.net.\t3600\tIN\tCAA\t2 auth \"0>09\\006\\010+\\006\\001\\004\\001\\214y\\002\\003\\001\\006\\009`\\134H\\001e\\003\\004\\002\\001\\004 y\\209\\012\\221r\\220\\156Q\\218\\150\\150{\\166\\245:\\231\\182%\\157:\\133\\179}\\1923r\\238\\151\\255\\128q\\145\\002\\001\\000\"", + "example.net. TYPE257 0 issue \"symantec.com\"": "example.net.\t3600\tIN\tCAA\t0 issue \"symantec.com\"", + } + for i, o := range lt { + rr, err := NewRR(i) + if err != nil { + t.Error("failed to parse RR: ", err) + continue + } + if rr.String() != o { + t.Errorf("`%s' should be equal to\n`%s', but is `%s'", i, o, rr.String()) + } else { + t.Logf("RR is OK: `%s'", rr.String()) + } + } +} diff --git a/types.go b/types.go index 8cc9306f..9f00ce89 100644 --- a/types.go +++ b/types.go @@ -501,6 +501,34 @@ func sprintName(s string) string { return string(dst) } +func sprintCAAValue(s string) string { + src := []byte(s) + dst := make([]byte, 0, len(src)) + dst = append(dst, '"') + for i := 0; i < len(src); { + if i+1 < len(src) && src[i] == '\\' && src[i+1] == '.' { + dst = append(dst, src[i:i+2]...) + i += 2 + } else { + b, n := nextByte(src, i) + if n == 0 { + i++ // dangling back slash + } else if b == '.' { + dst = append(dst, b) + } else { + if b < ' ' || b > '~' { + dst = appendByte(dst, b) + } else { + dst = append(dst, b) + } + } + i += n + } + } + dst = append(dst, '"') + return string(dst) +} + func sprintTxt(txt []string) string { var out []byte for i, s := range txt { @@ -543,21 +571,24 @@ func appendTXTStringByte(s []byte, b byte) []byte { return append(s, '\\', b) } if b < ' ' || b > '~' { - var buf [3]byte - bufs := strconv.AppendInt(buf[:0], int64(b), 10) - s = append(s, '\\') - for i := 0; i < 3-len(bufs); i++ { - s = append(s, '0') - } - for _, r := range bufs { - s = append(s, r) - } - return s - + return appendByte(s, b) } return append(s, b) } +func appendByte(s []byte, b byte) []byte { + var buf [3]byte + bufs := strconv.AppendInt(buf[:0], int64(b), 10) + s = append(s, '\\') + for i := 0; i < 3-len(bufs); i++ { + s = append(s, '0') + } + for _, r := range bufs { + s = append(s, r) + } + return s +} + func nextByte(b []byte, offset int) (byte, int) { if offset >= len(b) { return 0, 0 @@ -1531,20 +1562,13 @@ type CAA struct { Hdr RR_Header Flag uint8 Tag string - Value string `dns:"hex"` + Value string `dns:"octet"` } func (rr *CAA) Header() *RR_Header { return &rr.Hdr } func (rr *CAA) copy() RR { return &CAA{*rr.Hdr.copyHeader(), rr.Flag, rr.Tag, rr.Value} } func (rr *CAA) len() int { return rr.Hdr.len() + 1 + len(rr.Tag) + len(rr.Value)/2 } - -func (rr *CAA) String() string { - s := rr.Hdr.String() - - s += "\\# " + strconv.Itoa(2 + len(rr.Tag) + len(rr.Value)/2) + " " - s += fmt.Sprintf("%02X%02X%X%s", rr.Flag, len(rr.Tag), rr.Tag, strings.ToUpper(rr.Value)) - return s -} +func (rr *CAA) String() string { return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintCAAValue(rr.Value) } type UID struct { diff --git a/update_test.go b/update_test.go index 2a5a2347..0c6d1e23 100644 --- a/update_test.go +++ b/update_test.go @@ -8,7 +8,7 @@ import ( func TestDynamicUpdateParsing(t *testing.T) { prefix := "example.com. IN " for _, typ := range TypeToString { - if typ == "TYPE257" || typ == "OPT" || typ == "AXFR" || typ == "IXFR" || typ == "ANY" || typ == "TKEY" || + if typ == "CAA" || typ == "OPT" || typ == "AXFR" || typ == "IXFR" || typ == "ANY" || typ == "TKEY" || typ == "TSIG" || typ == "ISDN" || typ == "UNSPEC" || typ == "NULL" || typ == "ATMA" { continue } diff --git a/zscan_rr.go b/zscan_rr.go index 87a657ea..34efa6c0 100644 --- a/zscan_rr.go +++ b/zscan_rr.go @@ -2,7 +2,6 @@ package dns import ( "encoding/base64" - "encoding/hex" "net" "strconv" "strings" @@ -2175,47 +2174,34 @@ func setCAA(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) { rr := new(CAA) rr.Hdr = h l := <-c - if l.token != "\\#" { - return nil, &ParseError{f, "bad CAA Rdata", l}, "" + if l.length == 0 { + return rr, nil, l.comment } - <-c // zBlank - l = <-c - rdlength, e := strconv.Atoi(l.token) - if e != nil { - return nil, &ParseError{f, "bad CAA Rdata", l}, "" - } - s, e1, c1 := endingToString(c, "bad CAA Rdata", f) - if e1 != nil { - return nil, e1, c1 - } - if rdlength*2 != len(s) || len(s) < 4 { - return nil, &ParseError{f, "bad CAA Rdata", l}, "" - } - - flagbyte, e := hex.DecodeString(s[0:2]) - if e != nil { + i, err := strconv.Atoi(l.token) + if err != nil { return nil, &ParseError{f, "bad CAA Flag", l}, "" } - rr.Flag = uint8(flagbyte[0]) + rr.Flag = uint8(i) - tagbyte, e := hex.DecodeString(s[2:4]) - if e != nil { - return nil, &ParseError{f, "bad CAA Tag length", l}, "" - } - taglength := int(tagbyte[0]) - - if rdlength*2 < (4 + taglength) { - return nil, &ParseError{f, "bad CAA Tag length", l}, "" - } - - tag, e := hex.DecodeString(s[4:4+(taglength*2)]) - if e != nil { + <-c // zBlank + l = <-c // zString + if l.value != zString { return nil, &ParseError{f, "bad CAA Tag", l}, "" } - rr.Tag = string(tag) - - rr.Value = s[4+(taglength*2):] + rr.Tag = l.token + <-c // zBlank + s, e, c1 := endingToTxtSlice(c, "bad CAA Value", f) + if e != nil { + return nil, e, "" + } + if len(s) > 1 { + return nil, &ParseError{f, "bad CAA Value", l}, "" + } else if len(s) == 0 { + rr.Value = "" + } else { + rr.Value = s[0] + } return rr, nil, c1 }