package dns import ( "bytes" "fmt" "io" "net/netip" "sort" "strings" "time" "darvaza.org/core" "github.com/libdns/libdns" "git.jpi.io/amery/jpictl/pkg/cluster" ) func (mgr *Manager) fqdn(name string) string { return fmt.Sprintf("%s.%s.", name, mgr.domain) } // SortAddrSlice sorts a slice of [netip.Addr] func SortAddrSlice(s []netip.Addr) []netip.Addr { sort.Slice(s, func(i, j int) bool { return s[i].Less(s[j]) }) return s } // SortAddrRecords sorts a slice of [AddrRecord] // by Name and Address func SortAddrRecords(s []AddrRecord) []AddrRecord { sort.Slice(s, func(i, j int) bool { return s[i].Name < s[j].Name }) for _, p := range s { SortAddrSlice(p.Addr) } return s } // SortRecords sorts a slice of [libdns.Record], by Name, Type and Value func SortRecords(s []libdns.Record) []libdns.Record { sort.Slice(s, func(i, j int) bool { return lessRecord(s[i], s[j]) }) return s } func lessRecord(a, b libdns.Record) bool { aName := strings.ToLower(a.Name) bName := strings.ToLower(b.Name) switch { case aName < bName: return true case aName > bName: return false } aType := strings.ToUpper(a.Type) bType := strings.ToUpper(b.Type) switch { case aType < bType: return true case aType > bType: return false case aType == "A", aType == "AAAA": // IP Addresses var aa, ba netip.Addr switch { case aa.UnmarshalText([]byte(a.Value)) != nil: // bad address on a return true case ba.UnmarshalText([]byte(b.Value)) != nil: // bad address on b return false default: return aa.Less(ba) } default: // text return a.Value < b.Value } } // AddrRecord represents an A or AAAA record type AddrRecord struct { Name string Addr []netip.Addr } // Sort sorts the addresses of the record func (rr *AddrRecord) Sort() { SortAddrSlice(rr.Addr) } // Export converts the record into libdns.Record func (rr *AddrRecord) Export() []libdns.Record { out := make([]libdns.Record, len(rr.Addr)) for i, addr := range rr.Addr { out[i] = libdns.Record{ Name: rr.Name, TTL: time.Second * 1, Type: core.IIf(addr.Is6(), "AAAA", "A"), Value: addr.String(), } } return out } // WriteTo writes the record in BIND notation func (rr *AddrRecord) WriteTo(w io.Writer) (int64, error) { var total int for _, addr := range rr.Addr { n, err := fmt.Fprint(w, rr.Name, "\t", 1, "\t", core.IIf(addr.Is6(), "AAAA", "A"), "\t", addr.String(), "\n") switch { case err != nil: return 0, err case n > 0: total += n } } return int64(total), nil } // String converts the record into BIND entries func (rr *AddrRecord) String() string { var buf bytes.Buffer _, _ = rr.WriteTo(&buf) return buf.String() } func (mgr *Manager) genRegionsSorted() []string { regions := make([]string, 0, len(mgr.regions)) for name := range mgr.regions { regions = append(regions, name) } return cluster.SortRegions(regions) } func (mgr *Manager) genZonesSorted() []string { zones := make([]string, 0, len(mgr.zones)) for name := range mgr.zones { zones = append(zones, name) } sort.Strings(zones) return zones } func (mgr *Manager) genAllAddrRecords() []AddrRecord { var out []AddrRecord cache := make(map[string][]netip.Addr) // zones for _, z := range mgr.zones { // hosts s := mgr.genZoneHostRecords(z) out = append(out, s...) // zone alias addrs := mgr.genZoneAddresses(z) name := z.Name out = append(out, AddrRecord{ Name: name + mgr.suffix, Addr: addrs, }) // and cache for regions cache[name] = addrs } for _, name := range mgr.genRegionsSorted() { var addrs []netip.Addr for _, z := range mgr.regions[name] { addrs = append(addrs, cache[z]...) } rec := AddrRecord{ Name: name + mgr.suffix, Addr: addrs, } rec.Sort() out = append(out, rec) } SortAddrRecords(out) return out } func (*Manager) genZoneAddresses(z *Zone) []netip.Addr { var out []netip.Addr for _, p := range z.Hosts { if p.Active { out = append(out, p.Addrs...) } } SortAddrSlice(out) return out } func (mgr *Manager) genZoneHostRecords(z *Zone) []AddrRecord { out := make([]AddrRecord, 0, len(z.Hosts)) for _, p := range z.Hosts { rec := AddrRecord{ Name: p.String() + mgr.suffix, Addr: p.Addrs, } out = append(out, rec) } SortAddrRecords(out) return out } func (mgr *Manager) genRegionAddressesCached(name string, zones map[string][]netip.Addr) []netip.Addr { // var addrs []netip.Addr for _, zoneName := range mgr.regions[name] { addrs = append(addrs, zones[zoneName]...) } SortAddrSlice(addrs) return addrs }