Skip to content

Commit fa6a23b

Browse files
authored
Ver1.12.0 (#18)
* RFC4034 section 3 * add sign.go
1 parent 1b5bdfa commit fa6a23b

22 files changed

+1281
-18
lines changed

interface.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ type NameNodeInterface interface {
5454
// sort oreder is implementation dependent.
5555
IterateNameNode(func(NameNodeInterface) error) error
5656

57+
// IterateNameNode can iterate function by NameNodeInterface
58+
// sort oreder is implementation dependent.
59+
IterateNameNodeWithValue(f func(NameNodeInterface, any) (any, error), v any) error
60+
5761
// AddChildNode adds child node into children.
5862
AddChildNameNode(NameNodeInterface) error
5963

name_node.go

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -164,19 +164,28 @@ func (n *NameNode) IterateNameRRSet(f func(RRSetInterface) error) error {
164164
}
165165

166166
// IterateNameNode is implement of NameNodeInterface.IterateNameNode
167-
// sort order using sort.StringSlice Sort.
167+
// sort order using SortName (rfc4034#section6-1).
168168
func (n *NameNode) IterateNameNode(f func(NameNodeInterface) error) error {
169-
if err := f(n); err != nil {
169+
return n.IterateNameNodeWithValue(func(nni NameNodeInterface, _ any) (any, error) {
170+
return nil, f(nni)
171+
}, nil)
172+
}
173+
174+
// IterateNameNodeWithValue is implement of NameNodeInterface.IterateNameNodeWithValue
175+
// sort order using SortName (rfc4034#section6-1).
176+
func (n *NameNode) IterateNameNodeWithValue(f func(NameNodeInterface, any) (any, error), v any) error {
177+
res, err := f(n, v)
178+
if err != nil {
170179
return err
171180
}
172-
rrsetMap := n.children()
173-
keys := sort.StringSlice{}
174-
for key := range rrsetMap {
181+
children := n.children()
182+
keys := []string{}
183+
for key := range children {
175184
keys = append(keys, key)
176185
}
177-
keys.Sort()
186+
keys, _ = SortNames(keys)
178187
for _, name := range keys {
179-
if err := rrsetMap[name].IterateNameNode(f); err != nil {
188+
if err := children[name].IterateNameNodeWithValue(f, res); err != nil {
180189
return err
181190
}
182191
}
@@ -201,7 +210,7 @@ func (n *NameNode) AddChildNameNode(nn NameNodeInterface) error {
201210
return nil
202211
}
203212

204-
// RemoveChildNameNode is implement of NameNodeInterface.AddChildNameNode
213+
// RemoveChildNameNode is implement of NameNodeInterface.RemoveChildNameNode
205214
func (n *NameNode) RemoveChildNameNode(name string) error {
206215
name = dns.CanonicalName(name)
207216
if !dns.IsSubDomain(n.GetName(), name) {
@@ -237,14 +246,18 @@ func (n *NameNode) SetRRSet(set RRSetInterface) error {
237246
rrsetMap := n.rrsetMap()
238247
rrsetMap[set.GetRRtype()] = set
239248

240-
if !IsEmptyRRSet(rrsetMap[dns.TypeCNAME]) {
241-
if n.RRSetLen() > 1 {
242-
return ErrConflictCNAME
249+
switch set.GetRRtype() {
250+
case dns.TypeNSEC, dns.TypeRRSIG:
251+
default:
252+
if !IsEmptyRRSet(rrsetMap[dns.TypeCNAME]) {
253+
if n.RRSetLen() > 1 {
254+
return ErrConflictCNAME
255+
}
243256
}
244-
}
245-
if !IsEmptyRRSet(rrsetMap[dns.TypeDNAME]) {
246-
if n.RRSetLen() > 1 {
247-
return ErrConflictDNAME
257+
if !IsEmptyRRSet(rrsetMap[dns.TypeDNAME]) {
258+
if n.RRSetLen() > 1 {
259+
return ErrConflictDNAME
260+
}
248261
}
249262
}
250263
n.rrsetValue.Store(rrsetMap)

sign.go

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
package dnsutils
2+
3+
import (
4+
"crypto"
5+
"fmt"
6+
"io"
7+
"sort"
8+
"time"
9+
10+
"github.com/miekg/dns"
11+
)
12+
13+
type DenialOfExistenceMethod string
14+
15+
const (
16+
DenialOfExistenceMethodNSEC = "NSEC"
17+
DenialOfExistenceMethodNSEC3 = "NSEC3"
18+
)
19+
20+
var (
21+
DefaultBeforeSign time.Duration = time.Hour
22+
DefaultExpiry time.Duration = time.Hour * 24 * 14
23+
)
24+
25+
type SignOption struct {
26+
DoEMethod DenialOfExistenceMethod
27+
BeforeSign *time.Duration
28+
Expiry *time.Duration
29+
Inception *uint32
30+
Expiration *uint32
31+
// TODO: AddCDS bool
32+
// TODO: AddCDNSKEY bool
33+
}
34+
35+
func (o *SignOption) GetBeforSign() time.Duration {
36+
if o.BeforeSign == nil {
37+
return DefaultBeforeSign
38+
}
39+
return *o.BeforeSign
40+
}
41+
42+
func (o *SignOption) GetExpiry() time.Duration {
43+
if o.Expiry == nil {
44+
return DefaultExpiry
45+
}
46+
return *o.Expiry
47+
}
48+
49+
func (o *SignOption) GetInception() uint32 {
50+
if o.Inception == nil {
51+
return uint32(time.Now().UTC().Add(-o.GetBeforSign()).Unix())
52+
}
53+
return *o.Inception
54+
}
55+
56+
func (o *SignOption) GetExpiration() uint32 {
57+
if o.Expiration == nil {
58+
return uint32(time.Now().UTC().Add(o.GetExpiry()).Unix())
59+
}
60+
return *o.Expiration
61+
}
62+
63+
type DNSKEY struct {
64+
rr *dns.DNSKEY
65+
signer crypto.Signer
66+
}
67+
68+
func ReadDNSKEY(priv, pub io.Reader) (*DNSKEY, error) {
69+
var dnskey *dns.DNSKEY
70+
zp := dns.NewZoneParser(pub, "", "")
71+
for rr, ok := zp.Next(); ok; rr, ok = zp.Next() {
72+
if rr.Header().Rrtype == dns.TypeDNSKEY {
73+
dnskey, _ = rr.(*dns.DNSKEY)
74+
}
75+
}
76+
if dnskey == nil {
77+
return nil, fmt.Errorf("DNSKEY not found")
78+
}
79+
privateKey, err := dnskey.ReadPrivateKey(priv, "")
80+
if err != nil {
81+
return nil, fmt.Errorf("DNSKEY not found")
82+
}
83+
signer, _ := privateKey.(crypto.Signer)
84+
return &DNSKEY{
85+
rr: dnskey,
86+
signer: signer,
87+
}, nil
88+
}
89+
90+
func (d *DNSKEY) GetSigner() crypto.Signer {
91+
return d.signer
92+
}
93+
94+
func (d *DNSKEY) GetRR() *dns.DNSKEY {
95+
return d.rr
96+
}
97+
98+
func (d *DNSKEY) IsKSK() bool {
99+
return d.rr.Flags == 257
100+
}
101+
102+
func (d *DNSKEY) IsZSK() bool {
103+
return d.rr.Flags == 256
104+
}
105+
106+
func AddDNSKEY(z ZoneInterface, dnskeys []*DNSKEY, ttl uint32, generator Generator) error {
107+
if len(dnskeys) == 0 {
108+
return fmt.Errorf("empty DNSKEYs")
109+
}
110+
if ttl == 0 {
111+
ttl = 3600
112+
}
113+
rrset, err := GetRRSetOrCreate(z.GetRootNode(), dns.TypeDNSKEY, ttl, generator)
114+
if err != nil {
115+
return fmt.Errorf("failed to create DNSKEY rrset: %w", err)
116+
}
117+
for _, dnskey := range dnskeys {
118+
rr := dnskey.GetRR()
119+
rr.Hdr.Ttl = rrset.GetTTL()
120+
if err := rrset.AddRR(rr); err != nil {
121+
return fmt.Errorf("failed to add DNSKEY RR: %w", err)
122+
}
123+
}
124+
if err := z.GetRootNode().SetRRSet(rrset); err != nil {
125+
return fmt.Errorf("failed to set DNSKEY rrset: %w", err)
126+
}
127+
return nil
128+
}
129+
130+
func SignZone(z ZoneInterface, opt SignOption, dnskeys []*DNSKEY, generator Generator) error {
131+
if generator == nil {
132+
generator = &DefaultGenerator{}
133+
}
134+
// Sign
135+
return z.GetRootNode().IterateNameNodeWithValue(func(nni NameNodeInterface, a any) (any, error) {
136+
auth := a.(bool)
137+
if z.GetName() != nni.GetName() {
138+
if nsRRset := nni.GetRRSet(dns.TypeNS); nsRRset != nil {
139+
return false, signNode(nni, opt, dnskeys, generator, nni == z.GetRootNode(), true)
140+
}
141+
}
142+
return auth, signNode(nni, opt, dnskeys, generator, nni == z.GetRootNode(), auth)
143+
}, true)
144+
}
145+
146+
func signNode(nni NameNodeInterface, opt SignOption, dnskeys []*DNSKEY, generator Generator, apex, auth bool) error {
147+
if !auth {
148+
return nil
149+
}
150+
rrsig, err := GetRRSetOrCreate(nni, dns.TypeRRSIG, 0, generator)
151+
if err != nil {
152+
return err
153+
}
154+
err = nni.IterateNameRRSet(func(ri RRSetInterface) error {
155+
if ri.GetRRtype() == dns.TypeNS && !apex {
156+
return nil
157+
}
158+
rrsigRRs, err := SignRRSet(ri, opt, dnskeys)
159+
if err != nil {
160+
return err
161+
}
162+
for _, rr := range rrsigRRs {
163+
rrsig.AddRR(rr)
164+
}
165+
return nil
166+
})
167+
if err != nil {
168+
return fmt.Errorf("failed to sign rrset: %w", err)
169+
}
170+
if len(rrsig.GetRRs()) == 0 {
171+
return nil
172+
}
173+
return nni.SetRRSet(rrsig)
174+
}
175+
176+
func SignRRSet(ri RRSetInterface, opt SignOption, dnskeys []*DNSKEY) ([]*dns.RRSIG, error) {
177+
var rrs []*dns.RRSIG
178+
for _, dnskey := range dnskeys {
179+
if (ri.GetRRtype() == dns.TypeDNSKEY && dnskey.IsKSK()) ||
180+
(ri.GetRRtype() != dns.TypeDNSKEY && dnskey.IsZSK()) {
181+
rrsig := &dns.RRSIG{
182+
Hdr: dns.RR_Header{
183+
Ttl: ri.GetTTL(),
184+
},
185+
KeyTag: dnskey.GetRR().KeyTag(),
186+
SignerName: dnskey.GetRR().Header().Name,
187+
Algorithm: dnskey.GetRR().Algorithm,
188+
Inception: opt.GetInception(),
189+
Expiration: opt.GetExpiration(),
190+
}
191+
192+
if err := rrsig.Sign(dnskey.GetSigner(), ri.GetRRs()); err != nil {
193+
return nil, fmt.Errorf("failed to sign: %w", err)
194+
}
195+
rrs = append(rrs, rrsig)
196+
}
197+
}
198+
return rrs, nil
199+
}
200+
201+
func CreateDoE(z ZoneInterface, opt SignOption, generator RRSetGenerator) error {
202+
if generator == nil {
203+
generator = &DefaultGenerator{}
204+
}
205+
switch opt.DoEMethod {
206+
case DenialOfExistenceMethodNSEC, "":
207+
return createNSEC(z, generator)
208+
}
209+
return fmt.Errorf("not support: %s", opt.DoEMethod)
210+
}
211+
212+
func createNSEC(z ZoneInterface, generator RRSetGenerator) error {
213+
var nodes = map[string]NameNodeInterface{}
214+
var names []string
215+
soa, err := GetSOA(z)
216+
if err != nil {
217+
return ErrBadZone
218+
}
219+
220+
zoneCuts, _, err := GetZoneCuts(z.GetRootNode())
221+
if err != nil {
222+
return ErrBadZone
223+
}
224+
225+
// get next domain names
226+
z.GetRootNode().IterateNameNode(func(nni NameNodeInterface) error {
227+
// Blocks with no types present MUST NOT be included
228+
if nni.RRSetLen() == 0 {
229+
return nil
230+
}
231+
// A zone MUST NOT include an NSEC RR for any domain name that only holds glue records
232+
parent, strict := zoneCuts.GetNameNode(nni.GetName())
233+
if parent.GetName() != z.GetName() {
234+
if !strict && parent.GetRRSet(dns.TypeNS) != nil {
235+
return nil
236+
}
237+
}
238+
nodes[nni.GetName()] = nni
239+
names = append(names, nni.GetName())
240+
return nil
241+
})
242+
243+
sortedNames, _ := SortNames(names)
244+
for i, name := range sortedNames {
245+
nsec := &dns.NSEC{
246+
Hdr: dns.RR_Header{
247+
Name: name,
248+
Rrtype: dns.TypeNSEC,
249+
Class: dns.ClassINET,
250+
// The NSEC RR SHOULD have the same TTL value as the SOA minimum TTL field.
251+
// This is in the spirit of negative caching ([RFC2308]).
252+
Ttl: soa.Minttl,
253+
},
254+
TypeBitMap: []uint16{dns.TypeRRSIG, dns.TypeNSEC},
255+
}
256+
if i+1 < len(sortedNames) {
257+
nsec.NextDomain = sortedNames[i+1]
258+
} else {
259+
nsec.NextDomain = sortedNames[0]
260+
}
261+
rresetMap := nodes[name].CopyRRSetMap()
262+
for rtype := range rresetMap {
263+
switch rtype {
264+
case dns.TypeRRSIG:
265+
case dns.TypeNSEC:
266+
default:
267+
nsec.TypeBitMap = append(nsec.TypeBitMap, rtype)
268+
}
269+
}
270+
sort.SliceStable(nsec.TypeBitMap, func(i, j int) bool { return nsec.TypeBitMap[i] < nsec.TypeBitMap[j] })
271+
272+
set, err := generator.NewRRSet(name, soa.Minttl, dns.ClassINET, dns.TypeNSEC)
273+
if err != nil {
274+
return err
275+
}
276+
if err := set.AddRR(nsec); err != nil {
277+
return err
278+
}
279+
if err := nodes[name].SetRRSet(set); err != nil {
280+
return err
281+
}
282+
}
283+
return nil
284+
}

0 commit comments

Comments
 (0)