Merge branch 'v2'
This commit is contained in:
commit
4ebfc6b220
|
@ -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
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
19
msg.go
|
@ -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:]) != ".'" {
|
||||
|
|
2
nsecx.go
2
nsecx.go
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
21
server.go
21
server.go
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
4
tsig.go
4
tsig.go
|
@ -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")
|
||||
}
|
||||
|
|
18
types.go
18
types.go
|
@ -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
10
xfr.go
|
@ -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
356
zone.go
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue