Merge branch 'v2'

This commit is contained in:
Miek Gieben 2012-09-19 21:01:39 +02:00
commit 4ebfc6b220
14 changed files with 390 additions and 103 deletions

View File

@ -4,6 +4,10 @@
* make example from chaos
* Support for on-the-fly-signing
* (Re)sign zonefiles
* TLSA support
* create record from PEM(?) files
* sign
* verify
## Nice to have

View File

@ -142,7 +142,7 @@ func TestCompressLenght(t *testing.T) {
m.SetQuestion("miek.nl", TypeMX)
ul := m.Len()
m.Compress = true
if ul != m.Len(){
if ul != m.Len() {
t.Fatalf("Should be equal")
}
}

View File

@ -202,7 +202,6 @@ func (k *RR_DNSKEY) ToDS(h int) *RR_DS {
// the values: Inception, Expiration, KeyTag, SignerName and Algorithm.
// The rest is copied from the RRset. Sign returns true when the signing went OK,
// otherwise false.
// The signature data in the RRSIG is filled by this method.
// There is no check if RRSet is a proper (RFC 2181) RRSet.
func (rr *RR_RRSIG) Sign(k PrivateKey, rrset []RR) error {
if k == nil {
@ -255,9 +254,6 @@ func (rr *RR_RRSIG) Sign(k PrivateKey, rrset []RR) error {
switch rr.Algorithm {
case DSA, DSANSEC3SHA1:
// Implicit in the ParameterSizes
case RSAMD5:
h = md5.New()
ch = crypto.MD5
case RSASHA1, RSASHA1NSEC3SHA1:
h = sha1.New()
ch = crypto.SHA1
@ -269,6 +265,8 @@ func (rr *RR_RRSIG) Sign(k PrivateKey, rrset []RR) error {
case RSASHA512:
h = sha512.New()
ch = crypto.SHA512
case RSAMD5:
fallthrough // Deprecated in RFC 6725
default:
return ErrAlg
}
@ -286,7 +284,8 @@ func (rr *RR_RRSIG) Sign(k PrivateKey, rrset []RR) error {
signature = append(signature, s1.Bytes()...)
rr.Signature = unpackBase64(signature)
case *rsa.PrivateKey:
signature, err := rsa.SignPKCS1v15(rand.Reader, p, ch, sighash)
// We can use nil as rand.Reader here (says AGL)
signature, err := rsa.SignPKCS1v15(nil, p, ch, sighash)
if err != nil {
return err
}

View File

@ -157,7 +157,7 @@ func main() {
defer pprof.StopCPUProfile()
}
dns.HandleFunc(".", handleReflect)
dns.HandleFunc("miek.nl.", handleReflect)
dns.HandleFunc("authors.bind.", dns.HandleAuthors)
dns.HandleFunc("authors.server.", dns.HandleAuthors)
dns.HandleFunc("version.bind.", dns.HandleVersion)

19
msg.go
View File

@ -45,8 +45,7 @@ var (
ErrSecret error = &Error{Err: "dns: no secrets defined"}
ErrSigGen error = &Error{Err: "dns: bad signature generation"}
ErrAuth error = &Error{Err: "dns: bad authentication"}
ErrXfrSoa error = &Error{Err: "dns: no SOA seen"}
ErrXfrType error = &Error{Err: "dns: no ixfr, nor axfr"}
ErrSoa error = &Error{Err: "dns: no SOA"}
ErrHandle error = &Error{Err: "dns: handle is nil"}
ErrChan error = &Error{Err: "dns: channel is nil"}
ErrName error = &Error{Err: "dns: type not found for name"}
@ -222,7 +221,7 @@ func PackDomainName(s string, msg []byte, off int, compression map[string]int, c
// Emit sequence of counted strings, chopping at dots.
begin := 0
bs := []byte(s)
// ls := len(bs)
// ls := len(bs)
lens := ls
for i := 0; i < ls; i++ {
if bs[i] == '\\' {
@ -239,7 +238,7 @@ func PackDomainName(s string, msg []byte, off int, compression map[string]int, c
}
// off can already (we're in a loop) be bigger than len(msg)
// this happens when a name isn't fully qualified
if off+1 > len(msg) {
if off+1 > len(msg) {
return lenmsg, false
}
msg[off] = byte(i - begin)
@ -247,18 +246,18 @@ func PackDomainName(s string, msg []byte, off int, compression map[string]int, c
off++
// TODO(mg): because of the new check above, this can go. But
// just leave it as is for the moment.
// if off > lenmsg {
// return lenmsg, false
// }
// if off > lenmsg {
// return lenmsg, false
// }
for j := begin; j < i; j++ {
if off+1 > len(msg) {
return lenmsg, false
}
msg[off] = bs[j]
off++
// if off > lenmsg {
// return lenmsg, false
// }
// if off > lenmsg {
// return lenmsg, false
// }
}
// Dont try to compress '.'
if compression != nil && string(bs[begin:]) != ".'" {

View File

@ -117,7 +117,7 @@ func (rr *RR_NSEC) MatchType(rrtype uint16) bool {
func (rr *RR_NSEC3) Cover(domain string) bool {
hashdom := strings.ToUpper(HashName(domain, rr.Hash, rr.Iterations, rr.Salt))
nextdom := strings.ToUpper(rr.NextDomain)
owner := strings.ToUpper(SplitLabels(rr.Header().Name)[0]) // The hashed part
owner := strings.ToUpper(SplitLabels(rr.Header().Name)[0]) // The hashed part
apex := strings.ToUpper(HashName(strings.Join(SplitLabels(rr.Header().Name)[1:], "."), rr.Hash, rr.Iterations, rr.Salt)) + "." // The name of the zone
// if nextdomain equals the apex, it is considered The End. So in that case hashdom is always less then nextdomain
if hashdom > owner && nextdom == apex {

View File

@ -102,8 +102,8 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
a, _ := NewRR("www.example.net. 3600 IN A 192.0.2.1")
sig := new(RR_RRSIG)
sig.Hdr = RR_Header{"example.net.", TypeRRSIG, ClassINET, 14400, 0}
sig.Expiration, _ = DateToTime("20100909102025")
sig.Inception, _ = DateToTime("20100812102025")
sig.Expiration, _ = StringToTime("20100909102025")
sig.Inception, _ = StringToTime("20100812102025")
sig.KeyTag = eckey.(*RR_DNSKEY).KeyTag()
sig.SignerName = eckey.(*RR_DNSKEY).Hdr.Name
sig.Algorithm = eckey.(*RR_DNSKEY).Algorithm
@ -521,8 +521,8 @@ func TestRfc1982(t *testing.T) {
// fall in the current 68 year span
strtests := []string{"20120525134203", "19700101000000", "20380119031408"}
for _, v := range strtests {
if x, _ := DateToTime(v); v != TimeToDate(x) {
t.Logf("1982 arithmetic string failure %s (%s:%d)", v, TimeToDate(x), x)
if x, _ := StringToTime(v); v != TimeToString(x) {
t.Logf("1982 arithmetic string failure %s (%s:%d)", v, TimeToString(x), x)
t.Fail()
}
}
@ -532,8 +532,8 @@ func TestRfc1982(t *testing.T) {
1<<32 - 1: "21060207062815",
}
for i, v := range inttests {
if TimeToDate(i) != v {
t.Logf("1982 arithmetic int failure %d:%s (%s)", i, v, TimeToDate(i))
if TimeToString(i) != v {
t.Logf("1982 arithmetic int failure %d:%s (%s)", i, v, TimeToString(i))
t.Fail()
}
}
@ -548,8 +548,8 @@ func TestRfc1982(t *testing.T) {
"29210101121212": "21040522212236",
}
for from, to := range future {
x, _ := DateToTime(from)
y := TimeToDate(x)
x, _ := StringToTime(from)
y := TimeToString(x)
if y != to {
t.Logf("1982 arithmetic future failure %s:%s (%s)", from, to, y)
t.Fail()

View File

@ -174,27 +174,21 @@ func (mux *ServeMux) match(zone string, t uint16) Handler {
if h, e := mux.m.Find(zone); e {
// If we got queried for a DS record, we must see if we
// if we also serve the parent. We then redirect the query to it.
// TODO(mg): grandparents works too?
if t != TypeDS {
return h.Value.(Handler)
}
if d := h.Up(); d != nil {
return d.Value.(Handler)
}
// No parent zone found, let the original handler take care of it
return h.Value.(Handler)
} else {
if h == nil {
return nil
}
// Not an exact match and h may be nil and h.Value may be nil
if h.Value != nil {
return h.Value.(Handler)
}
if d := h.Up(); d != nil {
return d.Value.(Handler)
}
return h.Value.(Handler)
}
// Nothing found at all
return nil
panic("dns: not reached")
}
// Handle adds a handler to the ServeMux for pattern.
@ -220,9 +214,12 @@ func (mux *ServeMux) HandleRemove(pattern string) {
}
// ServeDNS dispatches the request to the handler whose
// pattern most closely matches the request message. For DS queries
// a parent zone is sought.
// pattern most closely matches the request message. If DefaultServeMux
// is used the correct thing for DS queries is done: a possible parent
// is sought.
// If no handler is found a standard SERVFAIL message is returned
// If the request message does not have a single question in the
// question section a SERVFAIL is returned.
func (mux *ServeMux) ServeDNS(w ResponseWriter, request *Msg) {
var h Handler
if len(request.Question) != 1 {

View File

@ -70,3 +70,29 @@ func BenchmarkServing(b *testing.B) {
c.Exchange(m, "127.0.0.1:8053")
}
}
func TestDotAsCatchAllWildcard(t *testing.T) {
mux := NewServeMux()
mux.Handle(".", HandlerFunc(HelloServer))
mux.Handle("example.com.", HandlerFunc(AnotherHelloServer))
handler := mux.match("www.miek.nl.", TypeTXT)
if handler == nil {
t.Error("wildcard match failed")
}
handler = mux.match("www.example.com.", TypeTXT)
if handler == nil {
t.Error("example.com match failed")
}
handler = mux.match("a.www.example.com.", TypeTXT)
if handler == nil {
t.Error("a.www.example.com match failed")
}
handler = mux.match("boe.", TypeTXT)
if handler == nil {
t.Error("boe. match failed")
}
}

View File

@ -95,7 +95,7 @@ func (rr *RR_TSIG) String() string {
s := "\n;; TSIG PSEUDOSECTION:\n"
s += rr.Hdr.String() +
" " + rr.Algorithm +
" " + tsigTimeToDate(rr.TimeSigned) +
" " + tsigTimeToString(rr.TimeSigned) +
" " + strconv.Itoa(int(rr.Fudge)) +
" " + strconv.Itoa(int(rr.MACSize)) +
" " + strings.ToUpper(rr.MAC) +
@ -351,7 +351,7 @@ func stripTsig(msg []byte) ([]byte, *RR_TSIG, error) {
// Translate the TSIG time signed into a date. There is no
// need for RFC1982 calculations as this date is 48 bits.
func tsigTimeToDate(t uint64) string {
func tsigTimeToString(t uint64) string {
ti := time.Unix(int64(t), 0).UTC()
return ti.Format("20060102150405")
}

View File

@ -72,9 +72,9 @@ const (
TypeMAILA uint16 = 254
TypeANY uint16 = 255
TypeURI uint16 = 256
TypeTA uint16 = 32768
TypeDLV uint16 = 32769
TypeURI uint16 = 256
TypeTA uint16 = 32768
TypeDLV uint16 = 32769
// valid Question.Qclass
ClassINET = 1
@ -844,8 +844,8 @@ func (rr *RR_RRSIG) String() string {
" " + strconv.Itoa(int(rr.Algorithm)) +
" " + strconv.Itoa(int(rr.Labels)) +
" " + strconv.FormatInt(int64(rr.OrigTtl), 10) +
" " + TimeToDate(rr.Expiration) +
" " + TimeToDate(rr.Inception) +
" " + TimeToString(rr.Expiration) +
" " + TimeToString(rr.Inception) +
" " + strconv.Itoa(int(rr.KeyTag)) +
" " + rr.SignerName +
" " + rr.Signature
@ -1371,10 +1371,10 @@ func (rr *RR_WKS) Copy() RR {
return &RR_WKS{*rr.Hdr.CopyHeader(), rr.Address, rr.Protocol, rr.BitMap}
}
// TimeToDate translates the RRSIG's incep. and expir. times to the
// TimeToString translates the RRSIG's incep. and expir. times to the
// string representation used when printing the record.
// It takes serial arithmetic (RFC 1982) into account.
func TimeToDate(t uint32) string {
func TimeToString(t uint32) string {
mod := ((int64(t) - time.Now().Unix()) / year68) - 1
if mod < 0 {
mod = 0
@ -1383,10 +1383,10 @@ func TimeToDate(t uint32) string {
return ti.Format("20060102150405")
}
// DateToTime translates the RRSIG's incep. and expir. times from
// StringToTime translates the RRSIG's incep. and expir. times from
// string values like "20110403154150" to an 32 bit integer.
// It takes serial arithmetic (RFC 1982) into account.
func DateToTime(s string) (uint32, error) {
func StringToTime(s string) (uint32, error) {
t, e := time.Parse("20060102150405", s)
if e != nil {
return 0, e

10
xfr.go
View File

@ -40,7 +40,7 @@ func (c *Client) XfrReceive(q *Msg, a string) (chan *XfrToken, error) {
go w.ixfrReceive(q, e)
return e, nil
default:
return nil, ErrXfrType
return nil, nil
}
panic("dns: not reached")
}
@ -61,7 +61,7 @@ func (w *reply) axfrReceive(q *Msg, c chan *XfrToken) {
}
if first {
if !checkXfrSOA(in, true) {
c <- &XfrToken{in.Answer, ErrXfrSoa}
c <- &XfrToken{in.Answer, ErrSoa}
return
}
first = !first
@ -103,7 +103,7 @@ func (w *reply) ixfrReceive(q *Msg, c chan *XfrToken) {
// Check if the returned answer is ok
if !checkXfrSOA(in, true) {
c <- &XfrToken{in.Answer, ErrXfrSoa}
c <- &XfrToken{in.Answer, ErrSoa}
return
}
// This serial is important
@ -141,8 +141,6 @@ func checkXfrSOA(in *Msg, first bool) bool {
return false
}
// XfrSend performs an outgoing [AI]xfr depending on the request message. The
// caller is responsible for sending the correct sequence of RR sets through
// the channel c. For reasons of symmetry XfrToken is re-used.
@ -171,7 +169,7 @@ func XfrSend(w ResponseWriter, q *Msg, c chan *XfrToken, e *error) error {
go axfrSend(w, q, c, e)
return nil
default:
return ErrXfrType
return nil
}
panic("not reached")
}

356
zone.go
View File

@ -4,6 +4,9 @@ package dns
import (
"github.com/miekg/radix"
"math/rand"
"runtime"
"sort"
"strings"
"sync"
"time"
@ -16,10 +19,18 @@ type Zone struct {
Wildcard int // Whenever we see a wildcard name, this is incremented
*radix.Radix // Zone data
mutex *sync.RWMutex
// timemodified?
expired bool // Slave zone is expired
expired bool // Slave zone is expired
// Do we need a timemodified?
}
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] }
type signData struct{ node, next *ZoneData }
// SignatureConfig holds the parameters for zone (re)signing. This
// is copied from OpenDNSSEC. See:
// https://wiki.opendnssec.org/display/DOCS/kasp.xml
@ -29,22 +40,29 @@ type SignatureConfig struct {
// When the end of the validity approaches, how much time should remain
// before we start to resign. Typical value is 3 days.
Refresh time.Duration
// Jitter is an amount of time added or subtracted from the
// Jitter is an random amount of time added or subtracted from the
// expiration time to ensure not all signatures expire a the same time.
// Typical value is 12 hours.
// Typical value is 12 hours, which means the actual jitter value is
// between -12..0..+12.
Jitter time.Duration
// InceptionOffset is subtracted from the inception time to ensure badly
// calibrated clocks on the internet can still validate a signature.
// Typical value is 300 seconds.
InceptionOffset time.Duration
// SignerRoutines specifies the number of signing goroutines, if not
// set runtime.NumCPU() + 1 is used as the value.
SignerRoutines int
// SOA MINTTL value used as the TTL on NSEC/NSEC3.
minttl uint32
}
func newSignatureConfig() *SignatureConfig {
return &SignatureConfig{time.Duration(4*7*24) * time.Hour, time.Duration(3*24) * time.Hour, time.Duration(12) * time.Hour, time.Duration(300) * time.Second}
return &SignatureConfig{time.Duration(4*7*24) * time.Hour, time.Duration(3*24) * time.Hour, time.Duration(12) * time.Hour, time.Duration(300) * time.Second, runtime.NumCPU() + 1, 0}
}
// DefaultSignaturePolicy has the following values. Validity is 4 weeks,
// Refresh is set to 3 days, Jitter to 12 hours and InceptionOffset to 300 seconds.
// SignerRoutines is set to runtime.NumCPU() + 1
var DefaultSignatureConfig = newSignatureConfig()
// NewZone creates an initialized zone with Origin set to origin.
@ -71,8 +89,8 @@ type ZoneData struct {
mutex *sync.RWMutex
}
// newZoneData creates a new zone data element
func newZoneData(s string) *ZoneData {
// NewZoneData creates a new zone data element.
func NewZoneData(s string) *ZoneData {
zd := new(ZoneData)
zd.Name = s
zd.RR = make(map[uint16][]RR)
@ -83,7 +101,7 @@ func newZoneData(s string) *ZoneData {
// toRadixName reverses a domain name so that when we store it in the radix tree
// we preserve the nsec ordering of the zone (this idea was stolen from NSD).
// each label is also lowercased.
// Each label is also lowercased.
func toRadixName(d string) string {
if d == "." {
return "."
@ -96,12 +114,66 @@ func toRadixName(d string) string {
}
s = strings.ToLower(l) + "." + s
}
return "." + s
}
// String returns a string representation of a ZoneData. There is no
// String for the entire zone, because this will (most likely) take up
// a huge amount of memory. Basic use pattern for printing an entire
// zone:
//
// // z contains the zone
// z.Radix.DoNext(func(i interface{}) {
// fmt.Printf("%s", i.(*dns.ZoneData).String()) })
func (zd *ZoneData) String() string {
var (
s string
t uint16
)
// Make sure SOA is first
// There is only one SOA, but it may have multiple sigs
if soa, ok := zd.RR[TypeSOA]; ok {
s += soa[0].String() + "\n"
if _, ok := zd.Signatures[TypeSOA]; ok {
for _, sig := range zd.Signatures[TypeSOA] {
s += sig.String() + "\n"
}
}
}
Types:
for _, rrset := range zd.RR {
for _, rr := range rrset {
t = rr.Header().Rrtype
if t == TypeSOA || t == TypeNSEC { // Done above or below
continue Types
}
s += rr.String() + "\n"
}
if _, ok := zd.Signatures[t]; ok {
for _, rr := range zd.Signatures[t] {
s += rr.String() + "\n"
}
}
}
// Make sure NSEC is last
// There is only one NSEC, but it may have multiple sigs
if soa, ok := zd.RR[TypeNSEC]; ok {
s += soa[0].String() + "\n"
if _, ok := zd.Signatures[TypeNSEC]; ok {
for _, sig := range zd.Signatures[TypeNSEC] {
s += sig.String() + "\n"
}
}
}
return s
}
func (z *Zone) String() string {
return z.Radix.String()
}
// Lock locks the zone z for writing.
func (z *Zone) Lock() { z.mutex.Lock() }
// Unlock unlocks the zone z for writing.
func (z *Zone) Unlock() { z.mutex.Unlock() }
// Insert inserts an RR into the zone. There is no check for duplicate data, although
// Remove will remove all duplicates.
@ -112,16 +184,16 @@ func (z *Zone) Insert(r RR) error {
// TODO(mg): quick check for doubles?
key := toRadixName(r.Header().Name)
z.mutex.Lock()
z.Lock()
zd, exact := z.Radix.Find(key)
if !exact {
// Not an exact match, so insert new value
defer z.mutex.Unlock()
defer z.Unlock()
// Check if it's a wildcard name
if len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
z.Wildcard++
}
zd := newZoneData(r.Header().Name)
zd := NewZoneData(r.Header().Name)
switch t := r.Header().Rrtype; t {
case TypeRRSIG:
sigtype := r.(*RR_RRSIG).TypeCovered
@ -138,7 +210,7 @@ func (z *Zone) Insert(r RR) error {
z.Radix.Insert(key, zd)
return nil
}
z.mutex.Unlock()
z.Unlock()
zd.Value.(*ZoneData).mutex.Lock()
defer zd.Value.(*ZoneData).mutex.Unlock()
// Name already there
@ -161,13 +233,13 @@ func (z *Zone) Insert(r RR) error {
// this is a no-op.
func (z *Zone) Remove(r RR) error {
key := toRadixName(r.Header().Name)
z.mutex.Lock()
z.Lock()
zd, exact := z.Radix.Find(key)
if !exact {
defer z.mutex.Unlock()
defer z.Unlock()
return nil
}
z.mutex.Unlock()
z.Unlock()
zd.Value.(*ZoneData).mutex.Lock()
defer zd.Value.(*ZoneData).mutex.Unlock()
remove := false
@ -199,51 +271,243 @@ func (z *Zone) Remove(r RR) error {
}
// Find looks up the ownername s in the zone and returns the
// data when found or nil when nothing is found.
// We can do better here, and include NXDOMAIN also. Much more efficient, only
// 1 tree walk.
func (z *Zone) Find(s string) *ZoneData {
// data and true when an exact match is found. If an exact find isn't
// possible the first parent node with a non-nil Value is returned and
// the boolean is false.
func (z *Zone) Find(s string) (node *ZoneData, exact bool) {
z.mutex.RLock()
defer z.mutex.RUnlock()
zd, e := z.Radix.Find(toRadixName(s))
if !e {
return nil
n, e := z.Radix.Find(toRadixName(s))
if n == nil {
return nil, false
}
return zd.Value.(*ZoneData)
node = n.Value.(*ZoneData)
exact = e
return
}
// Predecessor searches the zone for a name shorter than s.
func (z *Zone) Predecessor(s string) *ZoneData {
// FindFunc works like Find, but the function f is executed on
// each node which has a non-nil Value during the tree traversal.
// If f returns true, that node is returned.
func (z *Zone) FindFunc(s string, f func(interface{}) bool) (*ZoneData, bool, bool) {
z.mutex.RLock()
defer z.mutex.RUnlock()
zd := z.Radix.Predecessor(toRadixName(s))
zd, e, b := z.Radix.FindFunc(toRadixName(s), f)
if zd == nil {
return nil
return nil, false, false
}
return zd.Value.(*ZoneData)
return zd.Value.(*ZoneData), e, b
}
// Sign (re)signes the zone z. It adds keys to the zone (if not already there)
// and signs the keys with the KSKs and the rest of the zone with the ZSKs.
// NSEC is used for authenticated denial
// of existence. If config is nil DefaultSignatureConfig is used.
// TODO(mg): allow interaction with hsm
func (z *Zone) Sign(keys []*RR_DNSKEY, privkeys []PrivateKey, config *SignatureConfig) error {
// Sign (re)signes the zone z with the given keys, it knows about ZSKs and KSKs.
// NSECs and RRSIGs are added as needed. The public keys themselves are not added
// to the zone. If config is nil DefaultSignatureConfig is used.
// Basic use pattern for signing a zone with the default SignatureConfig:
//
// // A signle PublicKey/PrivateKey have been read from disk.
// e := z.Sign(map[*dns.RR_DNSKEY]dns.PrivateKey{pubkey.(*dns.RR_DNSKEY): privkey}, nil)
// if e != nil {
// // signing error
// }
// // Admire your signed zone...
// TODO(mg): resigning is not implemented
func (z *Zone) Sign(keys map[*RR_DNSKEY]PrivateKey, config *SignatureConfig) error {
z.Lock()
defer z.Unlock()
if config == nil {
config = DefaultSignatureConfig
}
// TODO(mg): concurrently walk the zone and sign the rrsets
// TODO(mg): nsec, or next pointer. Need to be a single tree-op
// Pre-calc the key tag
keytags := make(map[*RR_DNSKEY]uint16)
for k, _ := range keys {
keytags[k] = k.KeyTag()
}
errChan := make(chan error)
radChan := make(chan *radix.Radix, config.SignerRoutines*10)
// Start the signer goroutines
wg := new(sync.WaitGroup)
wg.Add(config.SignerRoutines * 5)
for i := 0; i < config.SignerRoutines*5; i++ {
go signerRoutine(wg, keys, keytags, config, radChan, errChan)
}
apex, e := z.Radix.Find(toRadixName(z.Origin))
if !e {
return ErrSoa
}
config.minttl = apex.Value.(*ZoneData).RR[TypeSOA][0].(*RR_SOA).Minttl
next := apex.Next()
radChan <- apex
var err error
Sign:
for next.Value.(*ZoneData).Name != z.Origin {
select {
case err = <-errChan:
break Sign
default:
radChan <- next
next = next.Next()
}
}
close(radChan)
close(errChan)
if err != nil {
return err
}
wg.Wait()
return nil
}
// Sign each ZoneData in place.
// TODO(mg): assume not signed
func signZoneData(zd *ZoneData, privkeys []PrivateKey, signername string, config *SignatureConfig) {
if zd.NonAuth == true {
return
// signerRoutine is a small helper routine to make the concurrent signing work.
func signerRoutine(wg *sync.WaitGroup, keys map[*RR_DNSKEY]PrivateKey, keytags map[*RR_DNSKEY]uint16, config *SignatureConfig, in chan *radix.Radix, err chan error) {
defer wg.Done()
for {
select {
case data, ok := <-in:
if !ok {
return
}
e := data.Value.(*ZoneData).Sign(data.Next().Value.(*ZoneData), keys, keytags, config)
if e != nil {
err <- e
return
}
}
}
//s := new(RR_RRSIG)
// signername
}
// Sign signs a single ZoneData node. The zonedata itself is locked for writing,
// during the execution. It is important that the nodes' next record does not
// changes. The caller must take care that the zone itself is also locked for writing.
// It works just like the Sign method for zones.
func (node *ZoneData) Sign(next *ZoneData, keys map[*RR_DNSKEY]PrivateKey, keytags map[*RR_DNSKEY]uint16, config *SignatureConfig) error {
node.mutex.Lock()
defer node.mutex.Unlock()
nsec := new(RR_NSEC)
nsec.Hdr.Rrtype = TypeNSEC
nsec.Hdr.Ttl = config.minttl // SOA's minimum value
nsec.Hdr.Name = node.Name
nsec.NextDomain = next.Name // Only thing I need from next, actually
nsec.Hdr.Class = ClassINET
if node.NonAuth == true {
for t, _ := range node.RR {
nsec.TypeBitMap = append(nsec.TypeBitMap, t)
}
nsec.TypeBitMap = append(nsec.TypeBitMap, TypeRRSIG) // Add sig too
nsec.TypeBitMap = append(nsec.TypeBitMap, TypeNSEC) // Add me too!
sort.Sort(uint16Slice(nsec.TypeBitMap))
node.RR[TypeNSEC] = []RR{nsec}
for k, p := range keys {
if k.Flags&SEP == SEP {
// only sign keys with SEP keys
continue
}
s := new(RR_RRSIG)
s.SignerName = k.Hdr.Name
s.Hdr.Ttl = k.Hdr.Ttl
s.Algorithm = k.Algorithm
s.KeyTag = keytags[k]
s.Inception = timeToUint32(time.Now().UTC().Add(-config.InceptionOffset))
s.Expiration = timeToUint32(time.Now().UTC().Add(jitterDuration(config.Jitter)).Add(config.Validity))
e := s.Sign(p, []RR{nsec})
if e != nil {
return e
}
node.Signatures[TypeNSEC] = append(node.Signatures[TypeNSEC], s)
// DS
if ds, ok := node.RR[TypeDS]; ok {
s := new(RR_RRSIG)
s.SignerName = k.Hdr.Name
s.Hdr.Ttl = k.Hdr.Ttl
s.Algorithm = k.Algorithm
s.KeyTag = keytags[k]
s.Inception = timeToUint32(time.Now().UTC().Add(-config.InceptionOffset))
s.Expiration = timeToUint32(time.Now().UTC().Add(jitterDuration(config.Jitter)).Add(config.Validity))
e := s.Sign(p, ds)
if e != nil {
return e
}
node.Signatures[TypeDS] = append(node.Signatures[TypeDS], s)
}
}
return nil
}
for k, p := range keys {
for t, rrset := range node.RR {
if k.Flags&SEP == SEP {
if _, ok := rrset[0].(*RR_DNSKEY); !ok {
// only sign keys with SEP keys
continue
}
}
s := new(RR_RRSIG)
s.SignerName = k.Hdr.Name
s.Hdr.Ttl = k.Hdr.Ttl
s.Hdr.Class = ClassINET
s.Algorithm = k.Algorithm
s.KeyTag = keytags[k]
s.Inception = timeToUint32(time.Now().UTC().Add(-config.InceptionOffset))
s.Expiration = timeToUint32(time.Now().UTC().Add(jitterDuration(config.Jitter)).Add(config.Validity))
e := s.Sign(p, rrset)
if e != nil {
return e
}
node.Signatures[t] = append(node.Signatures[t], s)
nsec.TypeBitMap = append(nsec.TypeBitMap, t)
}
nsec.TypeBitMap = append(nsec.TypeBitMap, TypeRRSIG) // Add sig too
nsec.TypeBitMap = append(nsec.TypeBitMap, TypeNSEC) // Add me too!
sort.Sort(uint16Slice(nsec.TypeBitMap))
node.RR[TypeNSEC] = []RR{nsec}
// NSEC
s := new(RR_RRSIG)
s.SignerName = k.Hdr.Name
s.Hdr.Ttl = k.Hdr.Ttl
s.Algorithm = k.Algorithm
s.KeyTag = keytags[k]
s.Inception = timeToUint32(time.Now().UTC().Add(-config.InceptionOffset))
s.Expiration = timeToUint32(time.Now().UTC().Add(jitterDuration(config.Jitter)).Add(config.Validity))
e := s.Sign(p, []RR{nsec})
if e != nil {
return e
}
node.Signatures[TypeNSEC] = append(node.Signatures[TypeNSEC], s)
}
return nil
}
// timeToUint32 translates a time.Time to a 32 bit value which
// can be used as the RRSIG's inception or expiration times.
func timeToUint32(t time.Time) uint32 {
mod := (t.Unix() / year68) - 1
if mod < 0 {
mod = 0
}
return uint32(t.Unix() - (mod * year68))
}
// uint32ToTime translates a uint32 to a time.Time
func uint32ToTime(t uint32) time.Time {
// uint32 to duration and then add it to epoch(0)
mod := (time.Now().Unix() / year68) - 1
if mod < 0 {
mod = 0
}
duration := time.Duration((mod * year68) * int64(t))
return time.Unix(0,0).Add(duration)
}
// jitterTime returns a random +/- jitter
func jitterDuration(d time.Duration) time.Duration {
jitter := rand.Intn(int(d))
if rand.Intn(1) == 1 {
return time.Duration(jitter)
}
return -time.Duration(jitter)
}

View File

@ -915,14 +915,14 @@ func setRRSIG(h RR_Header, c chan lex, o, f string) (RR, *ParseError) {
}
<-c // _BLANK
l = <-c
if i, err := DateToTime(l.token); err != nil {
if i, err := StringToTime(l.token); err != nil {
return nil, &ParseError{f, "bad RRSIG Expiration", l}
} else {
rr.Expiration = i
}
<-c // _BLANK
l = <-c
if i, err := DateToTime(l.token); err != nil {
if i, err := StringToTime(l.token); err != nil {
return nil, &ParseError{f, "bad RRSIG Inception", l}
} else {
rr.Inception = i