summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Hanley <felix@userspace.com.au>2017-11-22 04:12:15 +0000
committerFelix Hanley <felix@userspace.com.au>2017-11-22 04:17:48 +0000
commitf80bcc60d27fd5fb6a5b1c638984f84cb03f91ea (patch)
tree02da35d7ab94e3b56676ab845f9adff9b8efb1ae
parentf18488df5291dafe59b53585715f025d5c3c29b2 (diff)
downloaddyndnsd-f80bcc60d27fd5fb6a5b1c638984f84cb03f91ea.tar.gz
dyndnsd-f80bcc60d27fd5fb6a5b1c638984f84cb03f91ea.tar.bz2
Add basic NSD format with tests
-rw-r--r--nsd.go47
-rw-r--r--nsd_test.go88
2 files changed, 135 insertions, 0 deletions
diff --git a/nsd.go b/nsd.go
new file mode 100644
index 0000000..88aeee1
--- /dev/null
+++ b/nsd.go
@@ -0,0 +1,47 @@
+package dyndns
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "regexp"
+ "strconv"
+ "text/template"
+)
+
+type nsdFormat struct {
+}
+
+func (f nsdFormat) Write(hosts []Host) ([]byte, error) {
+ var out bytes.Buffer
+ t := template.Must(template.New("zone").Parse(nsdZone))
+ err := t.Execute(&out, hosts)
+ return out.Bytes(), err
+}
+
+func (f nsdFormat) Read(b []byte) (out []Host, err error) {
+ re := regexp.MustCompile(nsdRE)
+
+ scanner := bufio.NewScanner(bytes.NewReader(b))
+ for scanner.Scan() {
+ r := re.FindStringSubmatch(scanner.Text())
+ if len(r) != 5 {
+ return out, ErrInvalidZone
+ }
+ ttl, err := strconv.ParseInt(r[2], 10, 0)
+ if err != nil {
+ fmt.Printf("failed to parse zone entry %q\n", scanner.Text())
+ return out, ErrInvalidZone
+ }
+ out = append(out, Host{Name: r[1], TTL: int(ttl), Type: r[3], Address: r[4]})
+ }
+ return out, err
+}
+
+const (
+ // Regexp to read zone entries
+ nsdRE = `^\s*(\S+)\s+(\d+)\s+(A|AAAA)\s+(\S+)\s*$`
+
+ // The zone file template
+ nsdZone = "{{range .}}{{.Name}} {{.TTL}} {{.Type}} {{.Address}}\n{{end}}"
+)
diff --git a/nsd_test.go b/nsd_test.go
new file mode 100644
index 0000000..63bd658
--- /dev/null
+++ b/nsd_test.go
@@ -0,0 +1,88 @@
+package dyndns
+
+import (
+ "github.com/google/go-cmp/cmp"
+ "testing"
+)
+
+func TestNSDWrite(t *testing.T) {
+ nsd := nsdFormat{}
+
+ var tests = []struct {
+ in interface{}
+ out interface{}
+ }{
+ {
+ in: []Host{{Name: "test.com", TTL: 60, Type: "A", Address: "10.1.1.1"}},
+ out: []byte("test.com\t60\tA\t10.1.1.1\n"),
+ },
+ {
+ in: []Host{
+ {Name: "test.com", TTL: 60, Type: "A", Address: "10.1.1.1"},
+ {Name: "test2.com", TTL: 61, Type: "AAAA", Address: "10.1.1.2"},
+ },
+ out: []byte("test.com\t60\tA\t10.1.1.1\ntest2.com\t61\tAAAA\t10.1.1.2\n"),
+ },
+ {
+ in: []Host{{Name: "test.com", TTL: 6, Type: "A", Address: "10.1.1.1"}},
+ out: []byte("test.com\t6\tA\t10.1.1.1\n"),
+ },
+ }
+
+ for _, tt := range tests {
+ r, err := nsd.Write(tt.in.([]Host))
+ if err != nil {
+ t.Errorf("Write(%q) failed, got %s\n", tt.in, err)
+ }
+ if !cmp.Equal(r, tt.out) {
+ t.Errorf("Write(%q) => %q, expected %q\n", tt.in, r, tt.out)
+ }
+ }
+}
+
+func TestNSDRead(t *testing.T) {
+ nsd := nsdFormat{}
+
+ var tests = []struct {
+ in interface{}
+ out interface{}
+ }{
+ {
+ in: []byte("test.com\t60\tA\t10.1.1.1\n"),
+ out: []Host{{Name: "test.com", TTL: 60, Type: "A", Address: "10.1.1.1"}},
+ },
+ {
+ in: []byte("test.com \t 60\t \tA\t\t\t10.1.1.1\n"),
+ out: []Host{{Name: "test.com", TTL: 60, Type: "A", Address: "10.1.1.1"}},
+ },
+ {
+ in: []byte("test.com\t60\tA\t10.1.1.1\ntest2.com\t61\tAAAA\t10.1.1.2\n"),
+ out: []Host{
+ {Name: "test.com", TTL: 60, Type: "A", Address: "10.1.1.1"},
+ {Name: "test2.com", TTL: 61, Type: "AAAA", Address: "10.1.1.2"},
+ },
+ },
+ {
+ in: []byte("test.com\t6\tA\t10.1.1.1\n"),
+ out: []Host{{Name: "test.com", TTL: 6, Type: "A", Address: "10.1.1.1"}},
+ },
+ {
+ in: []byte("test.com\tA\t10.1.1.1\n"),
+ },
+ {
+ in: []byte("test.com\t6 5\tA\t10.1.1.1\n"),
+ },
+ }
+
+ for _, tt := range tests {
+ r, err := nsd.Read(tt.in.([]byte))
+ if err == nil {
+ if !cmp.Equal(r, tt.out) {
+ t.Errorf("Read(%q) => %q %q, expected %q\n", tt.in, r, err, tt.out)
+
+ }
+ } else if tt.out != nil {
+ t.Errorf("Write(%q) failed, got %s\n", tt.in, err)
+ }
+ }
+}