2014-10-20 15:36:50 +00:00
|
|
|
package main
|
|
|
|
import "encoding/base32"
|
|
|
|
import "fmt"
|
|
|
|
import "github.com/miekg/dns"
|
|
|
|
import "github.com/hlandau/degoutils/log"
|
|
|
|
import "time"
|
2014-10-21 20:30:19 +00:00
|
|
|
import "github.com/hlandau/ncdns/util"
|
2014-10-20 15:36:50 +00:00
|
|
|
|
2014-10-21 19:42:44 +00:00
|
|
|
// 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
|
2014-10-20 15:36:50 +00:00
|
|
|
}
|
|
|
|
|
2014-10-21 19:42:44 +00:00
|
|
|
// 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 {
|
2014-10-20 15:36:50 +00:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2014-10-21 19:42:44 +00:00
|
|
|
b, err := base32.HexEncoding.DecodeString(hashB32Hex)
|
|
|
|
log.Panice(err, hashB32Hex)
|
2014-10-20 15:36:50 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2014-10-21 19:42:44 +00:00
|
|
|
// Returns true iff a type should be covered by a RRSIG.
|
2014-10-20 15:36:50 +00:00
|
|
|
func shouldSignType(t uint16, isAuthoritySection bool) bool {
|
|
|
|
switch t {
|
|
|
|
case dns.TypeOPT:
|
|
|
|
return false
|
|
|
|
case dns.TypeNS:
|
|
|
|
return !isAuthoritySection
|
|
|
|
default:
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-21 19:42:44 +00:00
|
|
|
// Returns true iff a client requested DNSSEC.
|
2014-10-20 15:36:50 +00:00
|
|
|
func (tx *Tx) useDNSSEC() bool {
|
|
|
|
opt := tx.req.IsEdns0()
|
|
|
|
if opt == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return opt.Do()
|
|
|
|
}
|
|
|
|
|
2014-10-21 19:42:44 +00:00
|
|
|
// 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.
|
2014-10-20 15:36:50 +00:00
|
|
|
func (tx *Tx) setRcode(x int) {
|
|
|
|
if tx.rcode == 0 {
|
|
|
|
tx.rcode = x
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-10-21 19:42:44 +00:00
|
|
|
// Determines the maximum TTL for a slice of resource records.
|
|
|
|
// Returns 0 if the slice is empty.
|
2014-10-20 15:44:06 +00:00
|
|
|
func rraMaxTTL(rra []dns.RR) uint32 {
|
|
|
|
x := uint32(0)
|
|
|
|
for _, rr := range rra {
|
|
|
|
ttl := rr.Header().Ttl
|
|
|
|
if ttl > x {
|
|
|
|
x = ttl
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return x
|
|
|
|
}
|
|
|
|
|
2014-10-21 19:42:44 +00:00
|
|
|
// Used by signResponseSection.
|
2014-10-20 15:36:50 +00:00
|
|
|
func (tx *Tx) signRRs(rra []dns.RR, useKSK bool) (dns.RR, error) {
|
|
|
|
if len(rra) == 0 {
|
|
|
|
return nil, fmt.Errorf("no RRs to such")
|
|
|
|
}
|
|
|
|
|
2014-10-20 15:44:06 +00:00
|
|
|
maxttl := rraMaxTTL(rra)
|
|
|
|
exp := time.Duration(maxttl)*time.Second + time.Duration(10)*time.Minute
|
|
|
|
|
2014-10-20 15:50:05 +00:00
|
|
|
log.Info("maxttl: ", maxttl, " expiration: ", exp)
|
|
|
|
|
2014-10-20 15:36:50 +00:00
|
|
|
now := time.Now()
|
|
|
|
rrsig := &dns.RRSIG {
|
2014-10-20 15:44:06 +00:00
|
|
|
Hdr: dns.RR_Header { Ttl: maxttl, },
|
2014-10-20 15:36:50 +00:00
|
|
|
Algorithm: dns.RSASHA256,
|
2014-10-20 15:44:06 +00:00
|
|
|
Expiration: uint32(now.Add(exp).Unix()),
|
2014-10-20 16:16:17 +00:00
|
|
|
Inception: uint32(now.Add(time.Duration(-10)*time.Minute).Unix()),
|
2014-10-21 20:30:19 +00:00
|
|
|
SignerName: util.Absname(tx.soa.Hdr.Name),
|
2014-10-20 15:36:50 +00:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-10-21 19:42:44 +00:00
|
|
|
// Used by signResponse.
|
2014-10-20 15:36:50 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-10-21 19:42:44 +00:00
|
|
|
// 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.
|
2014-10-20 15:36:50 +00:00
|
|
|
func (tx *Tx) signResponse() error {
|
|
|
|
if !tx.useDNSSEC() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-10-20 21:58:28 +00:00
|
|
|
for _, r := range []*[]dns.RR { &tx.res.Answer, &tx.res.Ns, /*&tx.res.Extra*/ } {
|
2014-10-20 15:36:50 +00:00
|
|
|
err := tx.signResponseSection(r)
|
|
|
|
if err != nil {
|
|
|
|
log.Infoe(err, "fail signResponse")
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Info("done signResponse")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-10-21 19:42:44 +00:00
|
|
|
// Used for sorting RRTYPE lists for encoding into type bit maps.
|
2014-10-20 15:36:50 +00:00
|
|
|
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] }
|