dns/dnssec.go

758 lines
18 KiB
Go
Raw Normal View History

package dns
import (
2011-09-07 20:38:34 +00:00
"bytes"
2011-02-24 15:22:14 +00:00
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
_ "crypto/sha1"
_ "crypto/sha256"
_ "crypto/sha512"
"encoding/asn1"
"encoding/binary"
"encoding/hex"
"math/big"
"sort"
"strings"
"time"
)
// DNSSEC encryption algorithm codes.
const (
_ uint8 = iota
RSAMD5
DH
DSA
_ // Skip 4, RFC 6725, section 2.1
RSASHA1
DSANSEC3SHA1
RSASHA1NSEC3SHA1
RSASHA256
_ // Skip 9, RFC 6725, section 2.1
RSASHA512
_ // Skip 11, RFC 6725, section 2.1
ECCGOST
ECDSAP256SHA256
ECDSAP384SHA384
ED25519
ED448
INDIRECT uint8 = 252
PRIVATEDNS uint8 = 253 // Private (experimental keys)
PRIVATEOID uint8 = 254
)
// AlgorithmToString is a map of algorithm IDs to algorithm names.
var AlgorithmToString = map[uint8]string{
RSAMD5: "RSAMD5",
DH: "DH",
DSA: "DSA",
RSASHA1: "RSASHA1",
DSANSEC3SHA1: "DSA-NSEC3-SHA1",
RSASHA1NSEC3SHA1: "RSASHA1-NSEC3-SHA1",
RSASHA256: "RSASHA256",
RSASHA512: "RSASHA512",
ECCGOST: "ECC-GOST",
ECDSAP256SHA256: "ECDSAP256SHA256",
ECDSAP384SHA384: "ECDSAP384SHA384",
ED25519: "ED25519",
ED448: "ED448",
INDIRECT: "INDIRECT",
PRIVATEDNS: "PRIVATEDNS",
PRIVATEOID: "PRIVATEOID",
}
2017-02-15 20:40:16 +00:00
// AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's.
var AlgorithmToHash = map[uint8]crypto.Hash{
RSAMD5: crypto.MD5, // Deprecated in RFC 6725
DSA: crypto.SHA1,
RSASHA1: crypto.SHA1,
RSASHA1NSEC3SHA1: crypto.SHA1,
RSASHA256: crypto.SHA256,
ECDSAP256SHA256: crypto.SHA256,
ECDSAP384SHA384: crypto.SHA384,
RSASHA512: crypto.SHA512,
ED25519: crypto.Hash(0),
}
// DNSSEC hashing algorithm codes.
const (
_ uint8 = iota
SHA1 // RFC 4034
SHA256 // RFC 4509
GOST94 // RFC 5933
SHA384 // Experimental
SHA512 // Experimental
)
2017-02-15 20:40:16 +00:00
// HashToString is a map of hash IDs to names.
var HashToString = map[uint8]string{
SHA1: "SHA1",
SHA256: "SHA256",
GOST94: "GOST94",
SHA384: "SHA384",
SHA512: "SHA512",
}
// DNSKEY flag values.
2011-03-03 10:45:33 +00:00
const (
2012-01-08 15:49:54 +00:00
SEP = 1
REVOKE = 1 << 7
ZONE = 1 << 8
2011-03-03 10:45:33 +00:00
)
// The RRSIG needs to be converted to wireformat with some of the rdata (the signature) missing.
type rrsigWireFmt struct {
TypeCovered uint16
Algorithm uint8
Labels uint8
OrigTtl uint32
Expiration uint32
Inception uint32
KeyTag uint16
SignerName string `dns:"domain-name"`
/* No Signature */
}
// Used for converting DNSKEY's rdata to wirefmt.
type dnskeyWireFmt struct {
Flags uint16
Protocol uint8
Algorithm uint8
PublicKey string `dns:"base64"`
2011-01-18 10:34:48 +00:00
/* Nothing is left out */
}
func divRoundUp(a, b int) int {
return (a + b - 1) / b
}
2012-01-15 15:14:22 +00:00
// KeyTag calculates the keytag (or key-id) of the DNSKEY.
func (k *DNSKEY) KeyTag() uint16 {
if k == nil {
return 0
}
var keytag int
switch k.Algorithm {
2011-07-08 15:27:44 +00:00
case RSAMD5:
2012-04-17 08:55:02 +00:00
// Look at the bottom two bytes of the modules, which the last
// item in the pubkey.
// This algorithm has been deprecated, but keep this key-tag calculation.
modulus, _ := fromBase64([]byte(k.PublicKey))
2012-05-05 15:37:48 +00:00
if len(modulus) > 1 {
x := binary.BigEndian.Uint16(modulus[len(modulus)-2:])
2012-04-17 08:55:02 +00:00
keytag = int(x)
}
default:
keywire := new(dnskeyWireFmt)
keywire.Flags = k.Flags
keywire.Protocol = k.Protocol
keywire.Algorithm = k.Algorithm
keywire.PublicKey = k.PublicKey
2011-01-27 19:43:55 +00:00
wire := make([]byte, DefaultMsgSize)
n, err := packKeyWire(keywire, wire)
if err != nil {
return 0
}
wire = wire[:n]
for i, v := range wire {
if i&1 != 0 {
keytag += int(v) // must be larger than uint32
} else {
keytag += int(v) << 8
}
}
keytag += keytag >> 16 & 0xFFFF
keytag &= 0xFFFF
}
return uint16(keytag)
}
2011-09-08 17:25:45 +00:00
// ToDS converts a DNSKEY record to a DS record.
func (k *DNSKEY) ToDS(h uint8) *DS {
if k == nil {
return nil
}
ds := new(DS)
ds.Hdr.Name = k.Hdr.Name
ds.Hdr.Class = k.Hdr.Class
2011-03-24 08:24:24 +00:00
ds.Hdr.Rrtype = TypeDS
ds.Hdr.Ttl = k.Hdr.Ttl
ds.Algorithm = k.Algorithm
ds.DigestType = h
ds.KeyTag = k.KeyTag()
keywire := new(dnskeyWireFmt)
keywire.Flags = k.Flags
keywire.Protocol = k.Protocol
keywire.Algorithm = k.Algorithm
keywire.PublicKey = k.PublicKey
2011-01-27 19:43:55 +00:00
wire := make([]byte, DefaultMsgSize)
n, err := packKeyWire(keywire, wire)
if err != nil {
2012-03-19 19:00:40 +00:00
return nil
}
wire = wire[:n]
owner := make([]byte, 255)
off, err1 := PackDomainName(CanonicalName(k.Hdr.Name), owner, 0, nil, false)
2012-10-10 20:17:50 +00:00
if err1 != nil {
2012-03-19 19:00:40 +00:00
return nil
}
owner = owner[:off]
2011-03-15 18:41:28 +00:00
// RFC4034:
// digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
// "|" denotes concatenation
// DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.
var hash crypto.Hash
switch h {
2011-07-08 15:27:44 +00:00
case SHA1:
hash = crypto.SHA1
2011-07-08 15:27:44 +00:00
case SHA256:
hash = crypto.SHA256
2011-07-23 21:43:43 +00:00
case SHA384:
hash = crypto.SHA384
case SHA512:
hash = crypto.SHA512
default:
return nil
}
s := hash.New()
s.Write(owner)
s.Write(wire)
ds.Digest = hex.EncodeToString(s.Sum(nil))
return ds
}
// ToCDNSKEY converts a DNSKEY record to a CDNSKEY record.
func (k *DNSKEY) ToCDNSKEY() *CDNSKEY {
c := &CDNSKEY{DNSKEY: *k}
c.Hdr = k.Hdr
c.Hdr.Rrtype = TypeCDNSKEY
return c
}
// ToCDS converts a DS record to a CDS record.
func (d *DS) ToCDS() *CDS {
c := &CDS{DS: *d}
c.Hdr = d.Hdr
c.Hdr.Rrtype = TypeCDS
return c
}
// Sign signs an RRSet. The signature needs to be filled in with the values:
// Inception, Expiration, KeyTag, SignerName and Algorithm. The rest is copied
// from the RRset. Sign returns a non-nill error when the signing went OK.
// There is no check if RRSet is a proper (RFC 2181) RRSet. If OrigTTL is non
// zero, it is used as-is, otherwise the TTL of the RRset is used as the
// OrigTTL.
func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
if k == nil {
return ErrPrivKey
}
// s.Inception and s.Expiration may be 0 (rollover etc.), the rest must be set
2012-09-03 06:35:21 +00:00
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
return ErrKey
2011-02-24 15:22:14 +00:00
}
h0 := rrset[0].Header()
2012-09-03 06:35:21 +00:00
rr.Hdr.Rrtype = TypeRRSIG
rr.Hdr.Name = h0.Name
rr.Hdr.Class = h0.Class
2014-01-26 20:22:19 +00:00
if rr.OrigTtl == 0 { // If set don't override
rr.OrigTtl = h0.Ttl
}
rr.TypeCovered = h0.Rrtype
rr.Labels = uint8(CountLabel(h0.Name))
2012-04-06 18:12:00 +00:00
if strings.HasPrefix(h0.Name, "*") {
2012-09-03 06:35:21 +00:00
rr.Labels-- // wildcard, remove from label count
2011-02-24 15:22:14 +00:00
}
sigwire := new(rrsigWireFmt)
2012-09-03 06:35:21 +00:00
sigwire.TypeCovered = rr.TypeCovered
sigwire.Algorithm = rr.Algorithm
sigwire.Labels = rr.Labels
sigwire.OrigTtl = rr.OrigTtl
sigwire.Expiration = rr.Expiration
sigwire.Inception = rr.Inception
sigwire.KeyTag = rr.KeyTag
2012-02-28 18:25:09 +00:00
// For signing, lowercase this name
sigwire.SignerName = CanonicalName(rr.SignerName)
// Create the desired binary blob
2011-01-27 08:38:52 +00:00
signdata := make([]byte, DefaultMsgSize)
n, err := packSigWire(sigwire, signdata)
if err != nil {
return err
}
signdata = signdata[:n]
wire, err := rawSignatureData(rrset, rr)
if err != nil {
return err
2011-02-24 15:22:14 +00:00
}
hash, ok := AlgorithmToHash[rr.Algorithm]
if !ok {
return ErrAlg
2011-07-23 21:43:43 +00:00
}
switch rr.Algorithm {
case ED25519:
// ed25519 signs the raw message and performs hashing internally.
// All other supported signature schemes operate over the pre-hashed
// message, and thus ed25519 must be handled separately here.
//
// The raw message is passed directly into sign and crypto.Hash(0) is
// used to signal to the crypto.Signer that the data has not been hashed.
signature, err := sign(k, append(signdata, wire...), crypto.Hash(0), rr.Algorithm)
if err != nil {
return err
}
rr.Signature = toBase64(signature)
return nil
case RSAMD5, DSA, DSANSEC3SHA1:
// See RFC 6944.
return ErrAlg
default:
h := hash.New()
h.Write(signdata)
h.Write(wire)
signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm)
if err != nil {
return err
}
rr.Signature = toBase64(signature)
return nil
}
2011-01-13 16:14:14 +00:00
}
func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, error) {
signature, err := k.Sign(rand.Reader, hashed, hash)
if err != nil {
return nil, err
}
switch alg {
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
return signature, nil
case ECDSAP256SHA256, ECDSAP384SHA384:
ecdsaSignature := &struct {
R, S *big.Int
}{}
if _, err := asn1.Unmarshal(signature, ecdsaSignature); err != nil {
return nil, err
}
var intlen int
switch alg {
case ECDSAP256SHA256:
intlen = 32
case ECDSAP384SHA384:
intlen = 48
}
signature := intToBytes(ecdsaSignature.R, intlen)
signature = append(signature, intToBytes(ecdsaSignature.S, intlen)...)
return signature, nil
case ED25519:
return signature, nil
default:
return nil, ErrAlg
}
}
2011-09-08 17:25:45 +00:00
// Verify validates an RRSet with the signature and key. This is only the
2012-01-28 23:34:59 +00:00
// cryptographic test, the signature validity period must be checked separately.
2013-05-11 19:02:17 +00:00
// This function copies the rdata of some RRs (to lowercase domain names) for the validation to work.
func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
2012-02-10 08:19:23 +00:00
// First the easy checks
if !IsRRset(rrset) {
2012-08-27 06:41:14 +00:00
return ErrRRset
2012-02-28 18:25:09 +00:00
}
2012-09-03 06:37:16 +00:00
if rr.KeyTag != k.KeyTag() {
return ErrKey
}
2012-09-03 06:37:16 +00:00
if rr.Hdr.Class != k.Hdr.Class {
return ErrKey
}
2012-09-03 06:37:16 +00:00
if rr.Algorithm != k.Algorithm {
return ErrKey
}
if !strings.EqualFold(rr.SignerName, k.Hdr.Name) {
return ErrKey
}
2012-02-23 09:47:50 +00:00
if k.Protocol != 3 {
return ErrKey
}
// IsRRset checked that we have at least one RR and that the RRs in
// the set have consistent type, class, and name. Also check that type and
// class matches the RRSIG record.
if h0 := rrset[0].Header(); h0.Class != rr.Hdr.Class || h0.Rrtype != rr.TypeCovered {
return ErrRRset
}
// RFC 4035 5.3.2. Reconstructing the Signed Data
// Copy the sig, except the rrsig data
sigwire := new(rrsigWireFmt)
2012-09-03 06:37:16 +00:00
sigwire.TypeCovered = rr.TypeCovered
sigwire.Algorithm = rr.Algorithm
sigwire.Labels = rr.Labels
sigwire.OrigTtl = rr.OrigTtl
sigwire.Expiration = rr.Expiration
sigwire.Inception = rr.Inception
sigwire.KeyTag = rr.KeyTag
sigwire.SignerName = CanonicalName(rr.SignerName)
// Create the desired binary blob
2011-01-27 08:38:52 +00:00
signeddata := make([]byte, DefaultMsgSize)
n, err := packSigWire(sigwire, signeddata)
if err != nil {
return err
}
signeddata = signeddata[:n]
wire, err := rawSignatureData(rrset, rr)
if err != nil {
return err
2011-02-24 15:22:14 +00:00
}
2012-09-03 06:37:16 +00:00
sigbuf := rr.sigBuf() // Get the binary signature data
if rr.Algorithm == PRIVATEDNS { // PRIVATEOID
// TODO(miek)
// remove the domain name and assume its ours?
2011-12-09 20:45:57 +00:00
}
hash, ok := AlgorithmToHash[rr.Algorithm]
if !ok {
return ErrAlg
}
2012-09-03 06:37:16 +00:00
switch rr.Algorithm {
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
2012-04-15 19:37:00 +00:00
// TODO(mg): this can be done quicker, ie. cache the pubkey data somewhere??
2012-04-11 14:09:40 +00:00
pubkey := k.publicKeyRSA() // Get the key
2012-02-29 20:11:45 +00:00
if pubkey == nil {
return ErrKey
}
h := hash.New()
h.Write(signeddata)
h.Write(wire)
return rsa.VerifyPKCS1v15(pubkey, hash, h.Sum(nil), sigbuf)
2012-04-11 14:20:29 +00:00
case ECDSAP256SHA256, ECDSAP384SHA384:
pubkey := k.publicKeyECDSA()
2012-04-11 14:20:29 +00:00
if pubkey == nil {
return ErrKey
}
2012-04-15 19:37:00 +00:00
// Split sigbuf into the r and s coordinates
r := new(big.Int).SetBytes(sigbuf[:len(sigbuf)/2])
s := new(big.Int).SetBytes(sigbuf[len(sigbuf)/2:])
h := hash.New()
h.Write(signeddata)
h.Write(wire)
if ecdsa.Verify(pubkey, h.Sum(nil), r, s) {
return nil
2012-04-15 19:37:00 +00:00
}
return ErrSig
case ED25519:
pubkey := k.publicKeyED25519()
if pubkey == nil {
return ErrKey
}
if ed25519.Verify(pubkey, append(signeddata, wire...), sigbuf) {
return nil
}
return ErrSig
default:
return ErrAlg
}
}
2013-05-11 19:02:17 +00:00
// ValidityPeriod uses RFC1982 serial arithmetic to calculate
// if a signature period is valid. If t is the zero time, the
// current time is taken other t is. Returns true if the signature
// is valid at the given time, otherwise returns false.
2014-01-27 14:45:34 +00:00
func (rr *RRSIG) ValidityPeriod(t time.Time) bool {
var utc int64
2014-01-27 14:45:34 +00:00
if t.IsZero() {
utc = time.Now().UTC().Unix()
} else {
2014-01-27 14:45:34 +00:00
utc = t.UTC().Unix()
}
2012-09-03 06:37:16 +00:00
modi := (int64(rr.Inception) - utc) / year68
mode := (int64(rr.Expiration) - utc) / year68
ti := int64(rr.Inception) + modi*year68
te := int64(rr.Expiration) + mode*year68
return ti <= utc && utc <= te
}
// Return the signatures base64 encoding sigdata as a byte slice.
func (rr *RRSIG) sigBuf() []byte {
sigbuf, err := fromBase64([]byte(rr.Signature))
if err != nil {
return nil
}
return sigbuf
}
2012-04-11 14:09:40 +00:00
// publicKeyRSA returns the RSA public key from a DNSKEY record.
func (k *DNSKEY) publicKeyRSA() *rsa.PublicKey {
keybuf, err := fromBase64([]byte(k.PublicKey))
if err != nil {
return nil
}
if len(keybuf) < 1+1+64 {
// Exponent must be at least 1 byte and modulus at least 64
return nil
}
// RFC 2537/3110, section 2. RSA Public KEY Resource Records
// Length is in the 0th byte, unless its zero, then it
// it in bytes 1 and 2 and its a 16 bit number
explen := uint16(keybuf[0])
keyoff := 1
if explen == 0 {
explen = uint16(keybuf[1])<<8 | uint16(keybuf[2])
keyoff = 3
}
if explen > 4 || explen == 0 || keybuf[keyoff] == 0 {
// Exponent larger than supported by the crypto package,
// empty, or contains prohibited leading zero.
return nil
}
modoff := keyoff + int(explen)
modlen := len(keybuf) - modoff
if modlen < 64 || modlen > 512 || keybuf[modoff] == 0 {
// Modulus is too small, large, or contains prohibited leading zero.
return nil
}
pubkey := new(rsa.PublicKey)
2012-02-29 20:11:45 +00:00
var expo uint64
// The exponent of length explen is between keyoff and modoff.
for _, v := range keybuf[keyoff:modoff] {
expo <<= 8
expo |= uint64(v)
}
if expo > 1<<31-1 {
// Larger exponent than supported by the crypto package.
2012-02-29 20:11:45 +00:00
return nil
}
pubkey.E = int(expo)
pubkey.N = new(big.Int).SetBytes(keybuf[modoff:])
return pubkey
}
// publicKeyECDSA returns the Curve public key from the DNSKEY record.
func (k *DNSKEY) publicKeyECDSA() *ecdsa.PublicKey {
keybuf, err := fromBase64([]byte(k.PublicKey))
2011-07-23 21:43:43 +00:00
if err != nil {
return nil
}
2012-04-11 12:32:44 +00:00
pubkey := new(ecdsa.PublicKey)
2011-07-23 21:43:43 +00:00
switch k.Algorithm {
2012-04-11 12:37:51 +00:00
case ECDSAP256SHA256:
2012-04-11 12:32:44 +00:00
pubkey.Curve = elliptic.P256()
2012-04-12 13:39:54 +00:00
if len(keybuf) != 64 {
// wrongly encoded key
return nil
}
2012-04-11 12:37:51 +00:00
case ECDSAP384SHA384:
2012-04-11 12:32:44 +00:00
pubkey.Curve = elliptic.P384()
2012-04-12 13:39:54 +00:00
if len(keybuf) != 96 {
// Wrongly encoded key
return nil
}
2011-07-23 21:43:43 +00:00
}
pubkey.X = new(big.Int).SetBytes(keybuf[:len(keybuf)/2])
pubkey.Y = new(big.Int).SetBytes(keybuf[len(keybuf)/2:])
2011-07-23 21:43:43 +00:00
return pubkey
2011-07-08 09:08:31 +00:00
}
func (k *DNSKEY) publicKeyED25519() ed25519.PublicKey {
keybuf, err := fromBase64([]byte(k.PublicKey))
if err != nil {
return nil
}
if len(keybuf) != ed25519.PublicKeySize {
return nil
}
return keybuf
}
2011-09-07 20:38:34 +00:00
type wireSlice [][]byte
func (p wireSlice) Len() int { return len(p) }
func (p wireSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
2011-09-07 20:38:34 +00:00
func (p wireSlice) Less(i, j int) bool {
2011-10-06 18:16:23 +00:00
_, ioff, _ := UnpackDomainName(p[i], 0)
_, joff, _ := UnpackDomainName(p[j], 0)
2011-09-07 20:38:34 +00:00
return bytes.Compare(p[i][ioff+10:], p[j][joff+10:]) < 0
}
// Return the raw signature data.
func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
2011-09-07 20:38:34 +00:00
wires := make(wireSlice, len(rrset))
for i, r := range rrset {
r1 := r.copy()
h := r1.Header()
h.Ttl = s.OrigTtl
labels := SplitDomainName(h.Name)
// 6.2. Canonical RR Form. (4) - wildcards
if len(labels) > int(s.Labels) {
// Wildcard
h.Name = "*." + strings.Join(labels[len(labels)-int(s.Labels):], ".") + "."
}
// RFC 4034: 6.2. Canonical RR Form. (2) - domain name to lowercase
h.Name = CanonicalName(h.Name)
2012-02-23 09:47:50 +00:00
// 6.2. Canonical RR Form. (3) - domain rdata to lowercase.
// NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
// HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
// SRV, DNAME, A6
//
// RFC 6840 - Clarifications and Implementation Notes for DNS Security (DNSSEC):
// Section 6.2 of [RFC4034] also erroneously lists HINFO as a record
// that needs conversion to lowercase, and twice at that. Since HINFO
// records contain no domain names, they are not subject to case
// conversion.
switch x := r1.(type) {
case *NS:
x.Ns = CanonicalName(x.Ns)
case *MD:
x.Md = CanonicalName(x.Md)
case *MF:
x.Mf = CanonicalName(x.Mf)
case *CNAME:
x.Target = CanonicalName(x.Target)
case *SOA:
x.Ns = CanonicalName(x.Ns)
x.Mbox = CanonicalName(x.Mbox)
case *MB:
x.Mb = CanonicalName(x.Mb)
case *MG:
x.Mg = CanonicalName(x.Mg)
case *MR:
x.Mr = CanonicalName(x.Mr)
case *PTR:
x.Ptr = CanonicalName(x.Ptr)
case *MINFO:
x.Rmail = CanonicalName(x.Rmail)
x.Email = CanonicalName(x.Email)
case *MX:
x.Mx = CanonicalName(x.Mx)
case *RP:
x.Mbox = CanonicalName(x.Mbox)
x.Txt = CanonicalName(x.Txt)
case *AFSDB:
x.Hostname = CanonicalName(x.Hostname)
case *RT:
x.Host = CanonicalName(x.Host)
case *SIG:
x.SignerName = CanonicalName(x.SignerName)
case *PX:
x.Map822 = CanonicalName(x.Map822)
x.Mapx400 = CanonicalName(x.Mapx400)
case *NAPTR:
x.Replacement = CanonicalName(x.Replacement)
case *KX:
x.Exchanger = CanonicalName(x.Exchanger)
case *SRV:
x.Target = CanonicalName(x.Target)
case *DNAME:
x.Target = CanonicalName(x.Target)
2012-02-23 09:47:50 +00:00
}
// 6.2. Canonical RR Form. (5) - origTTL
Properly calculate compressed message lengths (#833) * Remove fullSize return from compressionLenSearch This wasn't used anywhere but TestCompressionLenSearch, and was very wrong. * Add generated compressedLen functions and use them This replaces the confusing and complicated compressionLenSlice function. * Use compressedLenWithCompressionMap even for uncompressed This leaves the len() functions unused and they'll soon be removed. This also fixes the off-by-one error of compressedLen when a (Q)NAME is ".". * Use Len helper instead of RR.len private method * Merge len and compressedLen functions * Merge compressedLen helper into Msg.Len * Remove compress bool from compressedLenWithCompressionMap * Merge map insertion into compressionLenSearch This eliminates the need to loop over the domain name twice when we're compressing the name. * Use compressedNameLen for NSEC.NextDomain This was a mistake. * Remove compress from RR.len * Add test case for multiple questions length * Add test case for MINFO and SOA compression These are the only RRs with multiple compressible names within the same RR, and they were previously broken. * Rename compressedNameLen to domainNameLen It also handles the length of uncompressed domain names. * Use off directly instead of len(s[:off]) * Move initial maxCompressionOffset check out of compressionLenMapInsert This should allow us to avoid the call overhead of compressionLenMapInsert in certain limited cases and may result in a slight performance increase. compressionLenMapInsert still has a maxCompressionOffset check inside the for loop. * Rename compressedLenWithCompressionMap to msgLenWithCompressionMap This better reflects that it also calculates the uncompressed length. * Merge TestMsgCompressMINFO with TestMsgCompressSOA They're both testing the same thing. * Remove compressionLenMapInsert compressionLenSearch does everything compressionLenMapInsert did anyway. * Only call compressionLenSearch in one place in domainNameLen * Split if statement in domainNameLen The last two commits worsened the performance of domainNameLen noticably, this change restores it's original performance. name old time/op new time/op delta MsgLength-12 550ns ±13% 510ns ±21% ~ (p=0.050 n=10+10) MsgLengthNoCompression-12 26.9ns ± 2% 27.0ns ± 1% ~ (p=0.198 n=9+10) MsgLengthPack-12 2.30µs ±12% 2.26µs ±16% ~ (p=0.739 n=10+10) MsgLengthMassive-12 32.9µs ± 7% 32.0µs ±10% ~ (p=0.243 n=9+10) MsgLengthOnlyQuestion-12 9.60ns ± 1% 9.20ns ± 1% -4.16% (p=0.000 n=9+9) * Remove stray newline from TestMsgCompressionMultipleQuestions * Remove stray newline in length_test.go This was introduced when resolving merge conflicts.
2018-11-29 23:33:41 +00:00
wire := make([]byte, Len(r1)+1) // +1 to be safe(r)
off, err1 := PackRR(r1, wire, 0, nil, false)
if err1 != nil {
return nil, err1
}
2012-01-21 10:58:26 +00:00
wire = wire[:off]
2011-09-07 20:38:34 +00:00
wires[i] = wire
}
sort.Sort(wires)
for i, wire := range wires {
if i > 0 && bytes.Equal(wire, wires[i-1]) {
continue
}
2011-02-24 15:22:14 +00:00
buf = append(buf, wire...)
}
return buf, nil
}
func packSigWire(sw *rrsigWireFmt, msg []byte) (int, error) {
// copied from zmsg.go RRSIG packing
off, err := packUint16(sw.TypeCovered, msg, 0)
if err != nil {
return off, err
}
off, err = packUint8(sw.Algorithm, msg, off)
if err != nil {
return off, err
}
off, err = packUint8(sw.Labels, msg, off)
if err != nil {
return off, err
}
off, err = packUint32(sw.OrigTtl, msg, off)
if err != nil {
return off, err
}
off, err = packUint32(sw.Expiration, msg, off)
if err != nil {
return off, err
}
off, err = packUint32(sw.Inception, msg, off)
if err != nil {
return off, err
}
off, err = packUint16(sw.KeyTag, msg, off)
if err != nil {
return off, err
}
off, err = PackDomainName(sw.SignerName, msg, off, nil, false)
if err != nil {
return off, err
}
return off, nil
}
func packKeyWire(dw *dnskeyWireFmt, msg []byte) (int, error) {
// copied from zmsg.go DNSKEY packing
off, err := packUint16(dw.Flags, msg, 0)
if err != nil {
return off, err
}
off, err = packUint8(dw.Protocol, msg, off)
if err != nil {
return off, err
}
off, err = packUint8(dw.Algorithm, msg, off)
if err != nil {
return off, err
}
off, err = packStringBase64(dw.PublicKey, msg, off)
if err != nil {
return off, err
}
return off, nil
}