package dns import ( "bytes" "fmt" "io" "net/netip" "sort" "time" "darvaza.org/core" "github.com/libdns/libdns" ) 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 } // SortRegions sorts regions. first by length those 3-character // or shorter, and then by length. It's mostly aimed at // supporting ISO-3166 order func SortRegions(regions []string) []string { sort.Slice(regions, func(i, j int) bool { r1, r2 := regions[i], regions[j] switch { case len(r1) < 4: switch { case len(r1) < len(r2): return true case len(r1) > len(r2): return false default: return r1 < r2 } case len(r2) < 4: return false default: return r1 < r2 } }) return regions } // 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 SortRegions(regions) } 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 }