Rename ErrXfrSoa to ErrSOA

This commit is contained in:
Miek Gieben 2012-09-12 21:13:57 +02:00
parent 7d6a9cd8a4
commit 449c2c013d
3 changed files with 111 additions and 61 deletions

3
msg.go
View File

@ -45,8 +45,7 @@ var (
ErrSecret error = &Error{Err: "dns: no secrets defined"} ErrSecret error = &Error{Err: "dns: no secrets defined"}
ErrSigGen error = &Error{Err: "dns: bad signature generation"} ErrSigGen error = &Error{Err: "dns: bad signature generation"}
ErrAuth error = &Error{Err: "dns: bad authentication"} ErrAuth error = &Error{Err: "dns: bad authentication"}
ErrXfrSoa error = &Error{Err: "dns: no SOA seen"} ErrSoa error = &Error{Err: "dns: no SOA"}
ErrXfrType error = &Error{Err: "dns: no ixfr, nor axfr"}
ErrHandle error = &Error{Err: "dns: handle is nil"} ErrHandle error = &Error{Err: "dns: handle is nil"}
ErrChan error = &Error{Err: "dns: channel is nil"} ErrChan error = &Error{Err: "dns: channel is nil"}
ErrName error = &Error{Err: "dns: type not found for name"} ErrName error = &Error{Err: "dns: type not found for name"}

8
xfr.go
View File

@ -40,7 +40,7 @@ func (c *Client) XfrReceive(q *Msg, a string) (chan *XfrToken, error) {
go w.ixfrReceive(q, e) go w.ixfrReceive(q, e)
return e, nil return e, nil
default: default:
return nil, ErrXfrType return nil, nil
} }
panic("dns: not reached") panic("dns: not reached")
} }
@ -61,7 +61,7 @@ func (w *reply) axfrReceive(q *Msg, c chan *XfrToken) {
} }
if first { if first {
if !checkXfrSOA(in, true) { if !checkXfrSOA(in, true) {
c <- &XfrToken{in.Answer, ErrXfrSoa} c <- &XfrToken{in.Answer, ErrSoa}
return return
} }
first = !first first = !first
@ -103,7 +103,7 @@ func (w *reply) ixfrReceive(q *Msg, c chan *XfrToken) {
// Check if the returned answer is ok // Check if the returned answer is ok
if !checkXfrSOA(in, true) { if !checkXfrSOA(in, true) {
c <- &XfrToken{in.Answer, ErrXfrSoa} c <- &XfrToken{in.Answer, ErrSoa}
return return
} }
// This serial is important // This serial is important
@ -169,7 +169,7 @@ func XfrSend(w ResponseWriter, q *Msg, c chan *XfrToken, e *error) error {
go axfrSend(w, q, c, e) go axfrSend(w, q, c, e)
return nil return nil
default: default:
return ErrXfrType return nil
} }
panic("not reached") panic("not reached")
} }

161
zone.go
View File

@ -5,6 +5,7 @@ package dns
import ( import (
"github.com/miekg/radix" "github.com/miekg/radix"
"math/rand" "math/rand"
"runtime"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@ -22,6 +23,14 @@ type Zone struct {
// Do we need a timemodified? // 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 // SignatureConfig holds the parameters for zone (re)signing. This
// is copied from OpenDNSSEC. See: // is copied from OpenDNSSEC. See:
// https://wiki.opendnssec.org/display/DOCS/kasp.xml // https://wiki.opendnssec.org/display/DOCS/kasp.xml
@ -40,16 +49,20 @@ type SignatureConfig struct {
// calibrated clocks on the internet can still validate a signature. // calibrated clocks on the internet can still validate a signature.
// Typical value is 300 seconds. // Typical value is 300 seconds.
InceptionOffset time.Duration 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 -- no override // SOA MINTTL value used as the TTL on NSEC/NSEC3 -- no override
minttl uint32 minttl uint32
} }
func newSignatureConfig() *SignatureConfig { 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, 0} 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, // DefaultSignaturePolicy has the following values. Validity is 4 weeks,
// Refresh is set to 3 days, Jitter to 12 hours and InceptionOffset to 300 seconds. // 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() var DefaultSignatureConfig = newSignatureConfig()
// NewZone creates an initialized zone with Origin set to origin. // NewZone creates an initialized zone with Origin set to origin.
@ -146,6 +159,12 @@ Types:
return s return s
} }
// Lock locks z for writing.
func (z *Zone) Lock() { z.mutex.Lock() }
// Unlock unlocks 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 // Insert inserts an RR into the zone. There is no check for duplicate data, although
// Remove will remove all duplicates. // Remove will remove all duplicates.
func (z *Zone) Insert(r RR) error { func (z *Zone) Insert(r RR) error {
@ -155,11 +174,11 @@ func (z *Zone) Insert(r RR) error {
// TODO(mg): quick check for doubles? // TODO(mg): quick check for doubles?
key := toRadixName(r.Header().Name) key := toRadixName(r.Header().Name)
z.mutex.Lock() z.Lock()
zd, exact := z.Radix.Find(key) zd, exact := z.Radix.Find(key)
if !exact { if !exact {
// Not an exact match, so insert new value // Not an exact match, so insert new value
defer z.mutex.Unlock() defer z.Unlock()
// Check if it's a wildcard name // Check if it's a wildcard name
if len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' { if len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
z.Wildcard++ z.Wildcard++
@ -181,7 +200,7 @@ func (z *Zone) Insert(r RR) error {
z.Radix.Insert(key, zd) z.Radix.Insert(key, zd)
return nil return nil
} }
z.mutex.Unlock() z.Unlock()
zd.Value.(*ZoneData).mutex.Lock() zd.Value.(*ZoneData).mutex.Lock()
defer zd.Value.(*ZoneData).mutex.Unlock() defer zd.Value.(*ZoneData).mutex.Unlock()
// Name already there // Name already there
@ -204,13 +223,13 @@ func (z *Zone) Insert(r RR) error {
// this is a no-op. // this is a no-op.
func (z *Zone) Remove(r RR) error { func (z *Zone) Remove(r RR) error {
key := toRadixName(r.Header().Name) key := toRadixName(r.Header().Name)
z.mutex.Lock() z.Lock()
zd, exact := z.Radix.Find(key) zd, exact := z.Radix.Find(key)
if !exact { if !exact {
defer z.mutex.Unlock() defer z.Unlock()
return nil return nil
} }
z.mutex.Unlock() z.Unlock()
zd.Value.(*ZoneData).mutex.Lock() zd.Value.(*ZoneData).mutex.Lock()
defer zd.Value.(*ZoneData).mutex.Unlock() defer zd.Value.(*ZoneData).mutex.Unlock()
remove := false remove := false
@ -257,21 +276,6 @@ func (z *Zone) Find(s string) (node *ZoneData, exact bool) {
return return
} }
// FindAndNext looks up the ownername s and its successor. It works
// just like Find.
func (z *Zone) FindAndNext(s string) (node, next *ZoneData, exact bool) {
z.mutex.RLock()
defer z.mutex.RUnlock()
n, e := z.Radix.Find(toRadixName(s))
if n == nil {
return nil, nil, false
}
node = n.Value.(*ZoneData)
next = n.Next().Value.(*ZoneData) // There is always a next
exact = e
return
}
// FindFunc works like Find, but the function f is executed on // FindFunc works like Find, but the function f is executed on
// each node which has a non-nil Value during the tree traversal. // each node which has a non-nil Value during the tree traversal.
// If f returns true, that node is returned. // If f returns true, that node is returned.
@ -287,10 +291,17 @@ func (z *Zone) FindFunc(s string, f func(interface{}) bool) (*ZoneData, bool, bo
// Sign (re)signes the zone z with the given keys, it knows about ZSKs and KSKs. // 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 // NSECs and RRSIGs are added as needed. The public keys themselves are not added
// to the zone. // to the zone. If config is nil DefaultSignatureConfig is used.
// 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
// }
func (z *Zone) Sign(keys map[*RR_DNSKEY]PrivateKey, config *SignatureConfig) error { func (z *Zone) Sign(keys map[*RR_DNSKEY]PrivateKey, config *SignatureConfig) error {
// TODO(mg): Write lock z.Lock()
defer z.Unlock()
if config == nil { if config == nil {
config = DefaultSignatureConfig config = DefaultSignatureConfig
} }
@ -299,37 +310,73 @@ func (z *Zone) Sign(keys map[*RR_DNSKEY]PrivateKey, config *SignatureConfig) err
for k, _ := range keys { for k, _ := range keys {
keytags[k] = k.KeyTag() keytags[k] = k.KeyTag()
} }
// FindAndNext returns the value I want the raw Radix nodes
// TODO(mg): LOCKING errChan := make(chan error)
apex, e := z.Radix.Find(toRadixName(z.Origin)) signChan := make(chan *signData)
e = e // TODO(mg)
config.minttl = apex.Value.(*ZoneData).RR[TypeSOA][0].(*RR_SOA).Minttl // Start the signer goroutines
next := apex.Next() for i := 0; i < config.SignerRoutines; i++ {
signZoneData(apex.Value.(*ZoneData), next.Value.(*ZoneData), keys, keytags, config) println("Signer", i, "started")
for next.Value.(*ZoneData).Name != z.Origin { go signerRoutine(keys, keytags, config, signChan, errChan)
nextnext := next.Next()
signZoneData(next.Value.(*ZoneData), nextnext.Value.(*ZoneData), keys, keytags, config)
next = nextnext
} }
apex, e := z.Radix.Find(toRadixName(z.Origin))
if !e {
// apex not found...?
return nil
}
config.minttl = apex.Value.(*ZoneData).RR[TypeSOA][0].(*RR_SOA).Minttl
next := apex.Next()
signChan <- &signData{apex.Value.(*ZoneData), next.Value.(*ZoneData)}
for next.Value.(*ZoneData).Name != z.Origin {
nextnext := next.Next()
signChan <- &signData{next.Value.(*ZoneData), nextnext.Value.(*ZoneData)}
next = nextnext
}
println("READY")
close(signChan)
close(errChan)
return nil return nil
} }
// Sign each ZoneData in place. // signerRoutine is a small helper routines to make the concurrent signing work.
// TODO(mg): assume not signed func signerRoutine(keys map[*RR_DNSKEY]PrivateKey, keytags map[*RR_DNSKEY]uint16, config *SignatureConfig, in chan *signData, err chan error) {
func signZoneData(node, next *ZoneData, keys map[*RR_DNSKEY]PrivateKey, keytags map[*RR_DNSKEY]uint16, config *SignatureConfig) { for {
select {
case data, ok := <-in:
if !ok {
return
}
e := data.node.Sign(data.next, keys, keytags, config)
if e != nil {
err <- e
return
}
}
}
}
// 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 is locked for writing.
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 := new(RR_NSEC)
nsec.Hdr.Rrtype = TypeNSEC nsec.Hdr.Rrtype = TypeNSEC
nsec.Hdr.Ttl = 3600 // Must be SOA Min TTL nsec.Hdr.Ttl = config.minttl // SOA's minimum value
nsec.Hdr.Name = node.Name nsec.Hdr.Name = node.Name
nsec.NextDomain = next.Name // Only thing I need from next, actually nsec.NextDomain = next.Name // Only thing I need from next, actually
nsec.Hdr.Class = ClassINET nsec.Hdr.Class = ClassINET
if node.NonAuth == true { if node.NonAuth == true {
// NSEC needed. Don't know. TODO(mg) // Check for DS records, FIXME(mg)
for t, _ := range node.RR { for t, _ := range node.RR {
nsec.TypeBitMap = append(nsec.TypeBitMap, t) 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)) sort.Sort(uint16Slice(nsec.TypeBitMap))
node.RR[TypeNSEC] = []RR{nsec} node.RR[TypeNSEC] = []RR{nsec}
for k, p := range keys { for k, p := range keys {
@ -338,12 +385,15 @@ func signZoneData(node, next *ZoneData, keys map[*RR_DNSKEY]PrivateKey, keytags
s.Hdr.Ttl = k.Hdr.Ttl s.Hdr.Ttl = k.Hdr.Ttl
s.Algorithm = k.Algorithm s.Algorithm = k.Algorithm
s.KeyTag = keytags[k] s.KeyTag = keytags[k]
s.Inception = 0 // TODO(mg) s.Inception = TimeToUint32(time.Now().UTC().Add(-config.InceptionOffset))
s.Expiration = 0 s.Expiration = TimeToUint32(time.Now().UTC().Add(jitterDuration(config.Jitter)).Add(config.Validity))
s.Sign(p, []RR{nsec}) // discard error, TODO(mg) e := s.Sign(p, []RR{nsec})
if e != nil {
return e
}
node.Signatures[TypeNSEC] = append(node.Signatures[TypeNSEC], s) node.Signatures[TypeNSEC] = append(node.Signatures[TypeNSEC], s)
} }
return return nil
} }
for k, p := range keys { for k, p := range keys {
for t, rrset := range node.RR { for t, rrset := range node.RR {
@ -355,12 +405,15 @@ func signZoneData(node, next *ZoneData, keys map[*RR_DNSKEY]PrivateKey, keytags
s.KeyTag = keytags[k] s.KeyTag = keytags[k]
s.Inception = TimeToUint32(time.Now().UTC().Add(-config.InceptionOffset)) s.Inception = TimeToUint32(time.Now().UTC().Add(-config.InceptionOffset))
s.Expiration = TimeToUint32(time.Now().UTC().Add(jitterDuration(config.Jitter)).Add(config.Validity)) s.Expiration = TimeToUint32(time.Now().UTC().Add(jitterDuration(config.Jitter)).Add(config.Validity))
s.Sign(p, rrset) // discard error, TODO(mg) e := s.Sign(p, rrset)
if e != nil {
return e
}
node.Signatures[t] = append(node.Signatures[t], s) node.Signatures[t] = append(node.Signatures[t], s)
nsec.TypeBitMap = append(nsec.TypeBitMap, t) nsec.TypeBitMap = append(nsec.TypeBitMap, t)
} }
nsec.TypeBitMap = append(nsec.TypeBitMap, TypeRRSIG) // Add sig too nsec.TypeBitMap = append(nsec.TypeBitMap, TypeRRSIG) // Add sig too
nsec.TypeBitMap = append(nsec.TypeBitMap, TypeNSEC) // Add me too! nsec.TypeBitMap = append(nsec.TypeBitMap, TypeNSEC) // Add me too!
sort.Sort(uint16Slice(nsec.TypeBitMap)) sort.Sort(uint16Slice(nsec.TypeBitMap))
node.RR[TypeNSEC] = []RR{nsec} node.RR[TypeNSEC] = []RR{nsec}
// NSEC // NSEC
@ -371,17 +424,15 @@ func signZoneData(node, next *ZoneData, keys map[*RR_DNSKEY]PrivateKey, keytags
s.KeyTag = keytags[k] s.KeyTag = keytags[k]
s.Inception = TimeToUint32(time.Now().UTC().Add(-config.InceptionOffset)) s.Inception = TimeToUint32(time.Now().UTC().Add(-config.InceptionOffset))
s.Expiration = TimeToUint32(time.Now().UTC().Add(jitterDuration(config.Jitter)).Add(config.Validity)) s.Expiration = TimeToUint32(time.Now().UTC().Add(jitterDuration(config.Jitter)).Add(config.Validity))
s.Sign(p, []RR{nsec}) // discard error, TODO(mg) e := s.Sign(p, []RR{nsec})
if e != nil {
return e
}
node.Signatures[TypeNSEC] = append(node.Signatures[TypeNSEC], s) node.Signatures[TypeNSEC] = append(node.Signatures[TypeNSEC], s)
} }
return nil
} }
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] }
// TimeToUint32 translates a time.Time to a 32 bit value which // TimeToUint32 translates a time.Time to a 32 bit value which
// can be used as the RRSIG's inception or expiration times. // can be used as the RRSIG's inception or expiration times.
func TimeToUint32(t time.Time) uint32 { func TimeToUint32(t time.Time) uint32 {