You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ncdns/txutil.go

192 lines
4.4 KiB
Go

package main
import "encoding/base32"
import "fmt"
import "github.com/miekg/dns"
import "github.com/hlandau/degoutils/log"
import "time"
// Determines if a transaction should be considered to have the given query type.
// Returns true iff the query type was qtype or ANY.
func (tx *Tx) istype(qtype uint16) bool {
return tx.qtype == qtype || tx.qtype == dns.TypeANY
}
// This is used in NSEC3 hash generation. A hash like ...decafbad has one added
// to it so that it becomes ...decafbae. This is needed because NSEC3's hashes
// are inclusive-exclusive (i.e. "[,)"), and we want a hash that covers only the
// name specified.
//
// Takes a hash in base32hex form.
func stepName(hashB32Hex string) string {
if len(hashB32Hex) == 0 {
return ""
}
b, err := base32.HexEncoding.DecodeString(hashB32Hex)
log.Panice(err, hashB32Hex)
for i := len(b)-1; i>=0; i-- {
b[i] += 1
if b[i] != 0 { // didn't rollover, don't need to continue
break
}
}
return base32.HexEncoding.EncodeToString(b)
}
// Returns true iff a type should be covered by a RRSIG.
func shouldSignType(t uint16, isAuthoritySection bool) bool {
switch t {
case dns.TypeOPT:
return false
case dns.TypeNS:
return !isAuthoritySection
default:
return true
}
}
// Returns true iff a client requested DNSSEC.
func (tx *Tx) useDNSSEC() bool {
opt := tx.req.IsEdns0()
if opt == nil {
return false
}
return opt.Do()
}
// Sets an rcode for the response if there is no error rcode currently set for
// the response. The idea is to return the rcode corresponding to the first
// error which occurs.
func (tx *Tx) setRcode(x int) {
if tx.rcode == 0 {
tx.rcode = x
}
}
// Determines the maximum TTL for a slice of resource records.
// Returns 0 if the slice is empty.
func rraMaxTTL(rra []dns.RR) uint32 {
x := uint32(0)
for _, rr := range rra {
ttl := rr.Header().Ttl
if ttl > x {
x = ttl
}
}
return x
}
// Used by signResponseSection.
func (tx *Tx) signRRs(rra []dns.RR, useKSK bool) (dns.RR, error) {
if len(rra) == 0 {
return nil, fmt.Errorf("no RRs to such")
}
maxttl := rraMaxTTL(rra)
exp := time.Duration(maxttl)*time.Second + time.Duration(10)*time.Minute
log.Info("maxttl: ", maxttl, " expiration: ", exp)
now := time.Now()
rrsig := &dns.RRSIG {
Hdr: dns.RR_Header { Ttl: maxttl, },
Algorithm: dns.RSASHA256,
Expiration: uint32(now.Add(exp).Unix()),
Inception: uint32(now.Add(time.Duration(-10)*time.Minute).Unix()),
SignerName: dns.Fqdn(tx.soa.Hdr.Name),
}
pk := tx.s.zskPrivate
if useKSK {
pk = tx.s.kskPrivate
rrsig.KeyTag = tx.s.ksk.KeyTag()
} else {
rrsig.KeyTag = tx.s.zsk.KeyTag()
}
err := rrsig.Sign(pk, rra)
if err != nil {
return nil, err
}
return rrsig, nil
}
// Used by signResponse.
func (tx *Tx) signResponseSection(rra *[]dns.RR) error {
if len(*rra) == 0 {
return nil
}
//log.Info("sign section: ", *rra)
i := 0
a := []dns.RR{}
pt := (*rra)[0].Header().Rrtype
t := uint16(0)
origrra := *rra
for i < len(origrra) {
for i < len(origrra) {
t = (*rra)[i].Header().Rrtype
if t != pt {
break
}
a = append(a, origrra[i])
i++
}
if shouldSignType(pt, (rra == &tx.res.Ns) ) {
useKSK := (pt == dns.TypeDNSKEY)
if useKSK {
srr, err := tx.signRRs(a, true)
if err != nil {
return err
}
*rra = append(*rra, srr)
}
srr, err := tx.signRRs(a, false)
if err != nil {
return err
}
*rra = append(*rra, srr)
}
pt = t
a = []dns.RR{}
}
return nil
}
// This is called to append RRSIGs to the response based on the current records in the Answer and
// Authority sections of the response. Records in the Additional section are not signed.
func (tx *Tx) signResponse() error {
if !tx.useDNSSEC() {
return nil
}
for _, r := range []*[]dns.RR { &tx.res.Answer, &tx.res.Ns, /*&tx.res.Extra*/ } {
err := tx.signResponseSection(r)
if err != nil {
log.Infoe(err, "fail signResponse")
return err
}
}
log.Info("done signResponse")
return nil
}
// Used for sorting RRTYPE lists for encoding into type bit maps.
type uint16Slice []uint16
func (p uint16Slice) Len() int { return len(p) }
func (p uint16Slice) Less(i, j int) bool { return p[i] < p[j] }
func (p uint16Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }