You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

238 lines
4.3 KiB

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) 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
}