aboutsummaryrefslogtreecommitdiff
path: root/lib/dns
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2023-08-05 15:21:29 +0700
committerShulhan <ms@kilabit.info>2023-08-05 15:47:53 +0700
commit34d09fcaa210ddd9d8bf8d1754151faeebf4e485 (patch)
tree7346e07a168f65f81dd5475816bb1e88119f9a0f /lib/dns
parentbd2ae82e254e203a021c9907e41b41ec0643ad49 (diff)
downloadpakakeh.go-34d09fcaa210ddd9d8bf8d1754151faeebf4e485.tar.xz
lib/dns: always initialize the Zone SOA record to default values
Previously, if we parse, create, or remove the SOA record from zone, we assume the SOA records are valid and not touch their values. In this changes, we set the SOA fields to default values if its not set, to make the SOA record consistent and valid, in perspective of client. This changes also export the default OS values for documentation and add new method NewRDataSOA to simplify creating new SOA record.
Diffstat (limited to 'lib/dns')
-rw-r--r--lib/dns/dns.go6
-rw-r--r--lib/dns/dns_test.go7
-rw-r--r--lib/dns/rdata_soa.go31
-rw-r--r--lib/dns/server_options_test.go12
-rw-r--r--lib/dns/testdata/zone/with_soa_test.txt2
-rw-r--r--lib/dns/testdata/zone_soa_test.txt11
-rw-r--r--lib/dns/zone.go22
-rw-r--r--lib/dns/zone_test.go53
8 files changed, 114 insertions, 30 deletions
diff --git a/lib/dns/dns.go b/lib/dns/dns.go
index f7a9df17..31bfec8d 100644
--- a/lib/dns/dns.go
+++ b/lib/dns/dns.go
@@ -134,3 +134,9 @@ var rcodeNames = map[ResponseCode]string{
RCodeNotImplemented: "ERR_NOT_IMPLEMENTED",
RCodeRefused: "ERR_REFUSED",
}
+
+// timeNow return the current time.
+// This variable provides to help mocking the test that require time value.
+var timeNow = func() time.Time {
+ return time.Now()
+}
diff --git a/lib/dns/dns_test.go b/lib/dns/dns_test.go
index 1d9bac6a..207b5a3c 100644
--- a/lib/dns/dns_test.go
+++ b/lib/dns/dns_test.go
@@ -15,6 +15,9 @@ const (
testServerAddress = "127.0.0.1:5300"
testDoTServerAddress = "127.0.0.1:18053"
testTLSPort = 18053
+
+ // Equal to 2023-08-05 07:53:20 +0000 UTC.
+ testNowEpoch = 1691222000
)
var (
@@ -24,6 +27,10 @@ var (
func TestMain(m *testing.M) {
log.SetFlags(0)
+ timeNow = func() time.Time {
+ return time.Unix(testNowEpoch, 0)
+ }
+
var (
serverOptions = &ServerOptions{
ListenAddress: "127.0.0.1:5300",
diff --git a/lib/dns/rdata_soa.go b/lib/dns/rdata_soa.go
index f6964c5e..86a8c02f 100644
--- a/lib/dns/rdata_soa.go
+++ b/lib/dns/rdata_soa.go
@@ -6,14 +6,14 @@ package dns
import (
"strings"
- "time"
)
+// Default SOA record value.
const (
- defaultRName string = "root"
- defaultRefresh int32 = 1 * 24 * 60 * 60 // 1 Day.
- defaultRetry int32 = 1 * 60 * 60 // 1 Hour.
- defaultMinimumTTL uint32 = 1 * 60 * 60 // 1 Hour.
+ DefaultSoaRName string = `root`
+ DefaultSoaRefresh int32 = 1 * 24 * 60 * 60 // 1 Day.
+ DefaultSoaRetry int32 = 1 * 60 * 60 // 1 Hour.
+ DefaultSoaMinimumTtl uint32 = 1 * 60 * 60 // 1 Hour.
)
// RDataSOA contains the authority of zone.
@@ -52,6 +52,17 @@ type RDataSOA struct {
Minimum uint32
}
+// NewRDataSOA create and initialize the new SOA record using default values
+// for Serial, Refresh, Retry, Expiry, and Minimum.
+func NewRDataSOA(mname, rname string) (soa *RDataSOA) {
+ soa = &RDataSOA{
+ MName: mname,
+ RName: rname,
+ }
+ soa.init()
+ return soa
+}
+
// init initialize the SOA record by setting fields to its default value if
// its empty.
func (soa *RDataSOA) init() {
@@ -61,18 +72,18 @@ func (soa *RDataSOA) init() {
if len(soa.RName) > 0 {
soa.RName = strings.ToLower(soa.RName)
} else {
- soa.RName = defaultRName
+ soa.RName = DefaultSoaRName
}
if soa.Serial == 0 {
- soa.Serial = uint32(time.Now().Unix())
+ soa.Serial = uint32(timeNow().Unix())
}
if soa.Refresh == 0 {
- soa.Refresh = defaultRefresh
+ soa.Refresh = DefaultSoaRefresh
}
if soa.Retry == 0 {
- soa.Retry = defaultRetry
+ soa.Retry = DefaultSoaRetry
}
if soa.Minimum == 0 {
- soa.Minimum = defaultMinimumTTL
+ soa.Minimum = DefaultSoaMinimumTtl
}
}
diff --git a/lib/dns/server_options_test.go b/lib/dns/server_options_test.go
index dee0ba5f..9dddcb44 100644
--- a/lib/dns/server_options_test.go
+++ b/lib/dns/server_options_test.go
@@ -22,13 +22,7 @@ func TestServerOptionsInit(t *testing.T) {
var (
ip = net.ParseIP("0.0.0.0")
- defSoa = RDataSOA{
- RName: defaultRName,
- Serial: uint32(time.Now().Unix()),
- Refresh: defaultRefresh,
- Retry: defaultRetry,
- Minimum: defaultMinimumTTL,
- }
+ defSoa = NewRDataSOA(``, ``)
cases []testCase
c testCase
@@ -41,7 +35,7 @@ func TestServerOptionsInit(t *testing.T) {
exp: &ServerOptions{
ListenAddress: "0.0.0.0:53",
HTTPIdleTimeout: defaultHTTPIdleTimeout,
- SOA: defSoa,
+ SOA: *defSoa,
PruneDelay: time.Hour,
PruneThreshold: -1 * time.Hour,
ip: ip,
@@ -74,7 +68,7 @@ func TestServerOptionsInit(t *testing.T) {
NameServers: []string{
"udp://127.0.0.1",
},
- SOA: defSoa,
+ SOA: *defSoa,
PruneDelay: time.Hour,
PruneThreshold: -1 * time.Hour,
ip: ip,
diff --git a/lib/dns/testdata/zone/with_soa_test.txt b/lib/dns/testdata/zone/with_soa_test.txt
index d63ba4ea..632d4058 100644
--- a/lib/dns/testdata/zone/with_soa_test.txt
+++ b/lib/dns/testdata/zone/with_soa_test.txt
@@ -7,7 +7,7 @@ b 604800 IN A 192.1.1.1
<<< zone_out.txt
$ORIGIN test.dev.
-@ SOA test.dev. . 0 0 0 0 0
+@ SOA test.dev. . 1691222000 86400 3600 0 3600
b 604800 IN A 192.1.1.1
<<< message_0.hex
diff --git a/lib/dns/testdata/zone_soa_test.txt b/lib/dns/testdata/zone_soa_test.txt
new file mode 100644
index 00000000..355ac40f
--- /dev/null
+++ b/lib/dns/testdata/zone_soa_test.txt
@@ -0,0 +1,11 @@
+<<< NewZone
+$ORIGIN test.soa
+@ SOA test.soa root 1691222000 86400 3600 0 3600
+
+<<< Add_SOA
+$ORIGIN test.soa
+@ SOA new.soa admin 1691222000 86400 3600 0 3600
+
+<<< Remove_SOA
+$ORIGIN test.soa
+@ SOA test.soa root 1691222000 86400 3600 0 3600
diff --git a/lib/dns/zone.go b/lib/dns/zone.go
index c28ae9e2..59b58619 100644
--- a/lib/dns/zone.go
+++ b/lib/dns/zone.go
@@ -21,19 +21,18 @@ type Zone struct {
Path string `json:"-"`
Name string
messages []*Message
- SOA RDataSOA
+ SOA *RDataSOA
}
// NewZone create and initialize new zone.
-func NewZone(file, name string) *Zone {
- return &Zone{
- Path: file,
- Name: name,
- SOA: RDataSOA{
- MName: name,
- },
+func NewZone(file, name string) (zone *Zone) {
+ zone = &Zone{
+ Path: file,
+ Name: name,
+ SOA: NewRDataSOA(name, ``),
Records: make(ZoneRecords),
}
+ return zone
}
// LoadZoneDir load DNS record from zone formatted files in
@@ -157,7 +156,9 @@ func (zone *Zone) Add(rr *ResourceRecord) (err error) {
if rr.Type == RecordTypeSOA {
soa, _ = rr.Value.(*RDataSOA)
if soa != nil {
- zone.SOA = *soa
+ var cloneSoa = *soa
+ zone.SOA = &cloneSoa
+ zone.SOA.init()
}
} else {
zone.Records.add(rr)
@@ -204,9 +205,10 @@ func (zone *Zone) Messages() []*Message {
}
// Remove a ResourceRecord from zone file.
+// If the RR is SOA it will reset the value back to default.
func (zone *Zone) Remove(rr *ResourceRecord) (err error) {
if rr.Type == RecordTypeSOA {
- zone.SOA = RDataSOA{}
+ zone.SOA = NewRDataSOA(zone.Name, ``)
} else {
if zone.Records.remove(rr) {
err = zone.Save()
diff --git a/lib/dns/zone_test.go b/lib/dns/zone_test.go
index 13a8deee..3b713f68 100644
--- a/lib/dns/zone_test.go
+++ b/lib/dns/zone_test.go
@@ -236,3 +236,56 @@ func TestZoneParseDirectiveTTL(t *testing.T) {
test.Assert(t, `ttl`, c.exp, m.ttl)
}
}
+
+// TestZone_SOA test related to SOA, when SOA record updated, removed, or
+// other records added or removed.
+func TestZone_SOA(t *testing.T) {
+ var (
+ tdata *test.Data
+ err error
+ )
+
+ tdata, err = test.LoadData(`testdata/zone_soa_test.txt`)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var (
+ zone = NewZone(``, `test.soa`)
+ buf bytes.Buffer
+ exp []byte
+ )
+
+ _, _ = zone.WriteTo(&buf)
+ exp = tdata.Output[`NewZone`]
+ test.Assert(t, `NewZone`, string(exp), buf.String())
+
+ // Add SOA.
+ var (
+ rdataSoa = NewRDataSOA(`new.soa`, `admin`)
+ rrSoa = &ResourceRecord{
+ Value: rdataSoa,
+ Name: zone.Name,
+ Type: RecordTypeSOA,
+ Class: RecordClassIN,
+ }
+ )
+
+ err = zone.Add(rrSoa)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ buf.Reset()
+ _, _ = zone.WriteTo(&buf)
+ exp = tdata.Output[`Add_SOA`]
+ test.Assert(t, `Add_SOA`, string(exp), buf.String())
+
+ // Remove SOA.
+ _ = zone.Remove(rrSoa)
+
+ buf.Reset()
+ _, _ = zone.WriteTo(&buf)
+ exp = tdata.Output[`Remove_SOA`]
+ test.Assert(t, `Remove_SOA`, string(exp), buf.String())
+}