ns-controller: do not use cname aliases
diff --git a/core/ns-controller/controllers/store.go b/core/ns-controller/controllers/store.go
index 2e1e955..533e767 100644
--- a/core/ns-controller/controllers/store.go
+++ b/core/ns-controller/controllers/store.go
@@ -29,7 +29,7 @@
 type ZoneConfig struct {
 	Zone        string     `json:"zone,omitempty"`
 	PublicIPs   []string   `json:"publicIPs,omitempty"`
-	PrivateIP   string     `json:"privateIP",omitempty`
+	PrivateIP   string     `json:"privateIP,omitempty"`
 	Nameservers []string   `json:"nameservers,omitempty"`
 	DNSSec      *DNSSecKey `json:"dnsSec,omitempty"`
 }
@@ -210,10 +210,10 @@
 ns{{ add1 $i }}.{{ $zone }}. 10800 IN A {{ $ns }}
 {{ end }}
 {{ range .publicIngressIPs }}
-@ 10800 IN A {{ . }}
+{{ $zone }}. 10800 IN A {{ . }}
+*.{{ $zone }}. 10800 IN A {{ . }}
+*.*.{{ $zone }}. 10800 IN A {{ . }}
 {{ end }}
-*.{{ $zone }}. 10800 IN CNAME {{ $zone }}.
-p.{{ $zone }}. 10800 IN CNAME {{ $zone }}.
 *.p.{{ $zone }}. 10800 IN A {{ .privateIngressIP }}
 `)
 	records, err := fs.Create("zone.db")
@@ -252,7 +252,11 @@
 	if err != nil {
 		return err
 	}
-	z.CreateOrReplaceTxtRecord(fmt.Sprintf("%s.%s.", entry, s.zone.Zone), txt)
+	var fqdn = fmt.Sprintf("%s.%s.", entry, s.zone.Zone)
+	z.CreateOrReplaceTxtRecord(fqdn, txt)
+	for _, ip := range s.zone.PublicIPs {
+		z.CreateARecord(fqdn, ip)
+	}
 	w, err := s.fs.Create("zone.db")
 	if err != nil {
 		return err
@@ -261,9 +265,6 @@
 	if err := z.Write(w); err != nil {
 		return err
 	}
-	// if _, err := r.Write([]byte(fmt.Sprintf("%s 300 IN TXT \"%s\"", entry, txt))); err != nil {
-	// 	return err
-	// }
 	return nil
 }
 
@@ -277,7 +278,9 @@
 	if err != nil {
 		return err
 	}
-	z.DeleteTxtRecord(fmt.Sprintf("%s.%s.", entry, s.zone.Zone), txt)
+	fqdn := fmt.Sprintf("%s.%s.", entry, s.zone.Zone)
+	z.DeleteTxtRecord(fqdn, txt)
+	z.DeleteRecordsFor(fqdn)
 	w, err := s.fs.Create("zone.db")
 	if err != nil {
 		return err
diff --git a/core/ns-controller/controllers/zone.go b/core/ns-controller/controllers/zone.go
index 41fb98f..4969771 100644
--- a/core/ns-controller/controllers/zone.go
+++ b/core/ns-controller/controllers/zone.go
@@ -3,14 +3,17 @@
 import (
 	"fmt"
 	"io"
+	"net"
 	"strings"
+	"sync"
 	"time"
 
 	"github.com/miekg/dns"
 )
 
 type ZoneFile struct {
-	rrs []dns.RR
+	lock sync.Locker
+	rrs  []dns.RR
 }
 
 func NewZoneFile(r io.Reader) (*ZoneFile, error) {
@@ -27,10 +30,12 @@
 			break
 		}
 	}
-	return &ZoneFile{rrs}, nil
+	return &ZoneFile{&sync.Mutex{}, rrs}, nil
 }
 
 func (z *ZoneFile) DeleteTxtRecord(name, value string) {
+	z.lock.Lock()
+	defer z.lock.Unlock()
 	for i, rr := range z.rrs {
 		if txt, ok := rr.(*dns.TXT); ok {
 			if txt.Hdr.Name == name && strings.Join(txt.Txt, "") == value {
@@ -40,7 +45,21 @@
 	}
 }
 
+func (z *ZoneFile) DeleteRecordsFor(name string) {
+	z.lock.Lock()
+	defer z.lock.Unlock()
+	rrs := make([]dns.RR, 0)
+	for _, rr := range z.rrs {
+		if rr.Header().Name != name {
+			rrs = append(rrs, rr)
+		}
+	}
+	z.rrs = rrs
+}
+
 func (z *ZoneFile) CreateOrReplaceTxtRecord(name, value string) {
+	z.lock.Lock()
+	defer z.lock.Unlock()
 	for i, rr := range z.rrs {
 		if txt, ok := rr.(*dns.TXT); ok {
 			if txt.Hdr.Name == name && strings.Join(txt.Txt, "") == value {
@@ -62,7 +81,23 @@
 	})
 }
 
+func (z *ZoneFile) CreateARecord(name, value string) {
+	z.lock.Lock()
+	defer z.lock.Unlock()
+	z.rrs = append(z.rrs, &dns.A{
+		Hdr: dns.RR_Header{
+			Name:   name,
+			Rrtype: dns.TypeA,
+			Class:  dns.ClassINET,
+			Ttl:    300,
+		},
+		A: net.ParseIP(value),
+	})
+}
+
 func (z *ZoneFile) Write(w io.Writer) error {
+	z.lock.Lock()
+	defer z.lock.Unlock()
 	for _, rr := range z.rrs {
 		if soa, ok := rr.(*dns.SOA); ok {
 			soa.Serial = NowUnix()