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.
212 lines
3.9 KiB
212 lines
3.9 KiB
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) |
|
} |
|
|
|
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 |
|
}
|
|
|