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