MagiTrickle/records.go

229 lines
4.6 KiB
Go
Raw Normal View History

2024-08-26 19:10:40 +03:00
package main
2024-08-24 19:47:10 +03:00
import (
2024-08-26 21:07:33 +03:00
"bytes"
2024-08-24 19:47:10 +03:00
"net"
"sync"
"time"
)
2024-08-26 21:07:33 +03:00
type ARecord struct {
Address net.IP
Deadline time.Time
2024-08-24 19:47:10 +03:00
}
2024-08-26 21:07:33 +03:00
func NewARecord(addr net.IP, deadline time.Time) *ARecord {
return &ARecord{
Address: addr,
Deadline: deadline,
}
}
type CNameRecord struct {
2024-09-14 16:13:59 +03:00
Alias string
2024-08-26 21:07:33 +03:00
Deadline time.Time
}
func NewCNameRecord(domainName string, deadline time.Time) *CNameRecord {
return &CNameRecord{
2024-09-14 16:13:59 +03:00
Alias: domainName,
2024-08-26 21:07:33 +03:00
Deadline: deadline,
}
}
2024-09-14 16:13:59 +03:00
type Records struct {
mutex sync.RWMutex
ARecords map[string][]*ARecord
CNameRecords map[string]*CNameRecord
2024-08-26 21:07:33 +03:00
}
2024-09-14 16:13:59 +03:00
func (r *Records) cleanupARecords(now time.Time) {
for name, aRecords := range r.ARecords {
i := 0
for _, aRecord := range aRecords {
2024-09-14 19:25:20 +03:00
if now.After(aRecord.Deadline) {
2024-09-14 16:13:59 +03:00
continue
}
aRecords[i] = aRecord
2024-08-27 01:53:30 +03:00
i++
2024-08-24 19:47:10 +03:00
}
2024-09-14 16:13:59 +03:00
aRecords = aRecords[:i]
if i == 0 {
delete(r.ARecords, name)
2024-08-26 21:07:33 +03:00
}
}
}
2024-09-14 16:13:59 +03:00
func (r *Records) cleanupCNameRecords(now time.Time) {
for name, record := range r.CNameRecords {
2024-09-14 19:25:20 +03:00
if now.After(record.Deadline) {
2024-09-14 16:13:59 +03:00
delete(r.CNameRecords, name)
}
2024-08-26 21:07:33 +03:00
}
}
2024-09-14 16:13:59 +03:00
func (r *Records) getAliasedDomain(now time.Time, domainName string) string {
processedDomains := make(map[string]struct{})
for {
if _, processed := processedDomains[domainName]; processed {
// Loop detected!
return ""
} else {
processedDomains[domainName] = struct{}{}
}
cname, ok := r.CNameRecords[domainName]
if !ok {
break
}
2024-09-14 19:25:20 +03:00
if now.After(cname.Deadline) {
2024-09-14 16:13:59 +03:00
delete(r.CNameRecords, domainName)
break
}
domainName = cname.Alias
}
return domainName
2024-08-26 21:07:33 +03:00
}
2024-09-14 16:13:59 +03:00
func (r *Records) getActualARecords(now time.Time, domainName string) []*ARecord {
aRecords, ok := r.ARecords[domainName]
2024-08-26 21:07:33 +03:00
if !ok {
return nil
}
2024-09-14 16:13:59 +03:00
i := 0
for _, aRecord := range aRecords {
2024-09-14 19:25:20 +03:00
if now.After(aRecord.Deadline) {
2024-09-14 16:13:59 +03:00
continue
}
aRecords[i] = aRecord
i++
}
aRecords = aRecords[:i]
if i == 0 {
delete(r.ARecords, domainName)
2024-08-26 21:07:33 +03:00
return nil
}
2024-09-14 16:13:59 +03:00
return aRecords
}
2024-08-27 01:44:17 +03:00
2024-09-14 16:13:59 +03:00
func (r *Records) getActualCNames(now time.Time, domainName string, fromEnd bool) []string {
processedDomains := make(map[string]struct{})
2024-08-27 01:44:17 +03:00
cNameList := make([]string, 0)
2024-09-14 16:13:59 +03:00
if fromEnd {
domainName = r.getAliasedDomain(now, domainName)
cNameList = append(cNameList, domainName)
}
r.cleanupCNameRecords(now)
for {
if _, processed := processedDomains[domainName]; processed {
// Loop detected!
return nil
} else {
processedDomains[domainName] = struct{}{}
2024-08-27 01:44:17 +03:00
}
2024-09-14 16:13:59 +03:00
found := false
for aliasFrom, aliasTo := range r.CNameRecords {
if aliasTo.Alias == domainName {
cNameList = append(cNameList, aliasFrom)
domainName = aliasFrom
found = true
break
2024-08-26 21:07:33 +03:00
}
2024-08-27 01:44:17 +03:00
}
2024-09-14 16:13:59 +03:00
if !found {
break
2024-08-24 19:47:10 +03:00
}
}
return cNameList
}
2024-09-14 16:13:59 +03:00
func (r *Records) Cleanup() {
r.mutex.Lock()
defer r.mutex.Unlock()
now := time.Now()
r.cleanupARecords(now)
r.cleanupCNameRecords(now)
2024-08-25 00:18:15 +03:00
}
2024-09-14 16:13:59 +03:00
func (r *Records) GetCNameRecords(domainName string, fromEnd bool) []string {
2024-08-25 00:18:15 +03:00
r.mutex.RLock()
defer r.mutex.RUnlock()
2024-09-14 16:13:59 +03:00
now := time.Now()
2024-08-24 19:47:10 +03:00
2024-09-14 16:13:59 +03:00
return r.getActualCNames(now, domainName, fromEnd)
2024-08-24 19:47:10 +03:00
}
2024-09-14 16:13:59 +03:00
func (r *Records) GetARecords(domainName string) []*ARecord {
2024-08-24 19:47:10 +03:00
r.mutex.Lock()
defer r.mutex.Unlock()
2024-09-14 16:13:59 +03:00
now := time.Now()
2024-08-24 19:47:10 +03:00
2024-09-14 16:13:59 +03:00
return r.getActualARecords(now, r.getAliasedDomain(now, domainName))
}
2024-08-26 21:07:33 +03:00
2024-09-14 16:13:59 +03:00
func (r *Records) AddCNameRecord(domainName string, cName string, ttl time.Duration) {
if domainName == cName {
// Can't assing to yourself
return
2024-08-24 19:47:10 +03:00
}
2024-09-14 16:13:59 +03:00
r.mutex.Lock()
defer r.mutex.Unlock()
now := time.Now()
delete(r.ARecords, domainName)
r.CNameRecords[domainName] = NewCNameRecord(cName, now.Add(ttl))
2024-08-24 19:47:10 +03:00
}
2024-09-14 16:13:59 +03:00
func (r *Records) AddARecord(domainName string, addr net.IP, ttl time.Duration) {
2024-08-24 19:47:10 +03:00
r.mutex.Lock()
defer r.mutex.Unlock()
2024-09-14 16:13:59 +03:00
now := time.Now()
2024-08-24 19:47:10 +03:00
2024-09-14 16:13:59 +03:00
delete(r.CNameRecords, domainName)
if _, ok := r.ARecords[domainName]; !ok {
r.ARecords[domainName] = make([]*ARecord, 0)
2024-08-26 19:46:44 +03:00
}
2024-09-14 16:13:59 +03:00
for _, aRecord := range r.ARecords[domainName] {
2024-08-26 21:07:33 +03:00
if bytes.Compare(aRecord.Address, addr) == 0 {
2024-09-14 16:13:59 +03:00
aRecord.Deadline = now.Add(ttl)
2024-08-26 21:07:33 +03:00
return
}
2024-08-26 19:46:44 +03:00
}
2024-09-14 16:13:59 +03:00
r.ARecords[domainName] = append(r.ARecords[domainName], NewARecord(addr, now.Add(ttl)))
2024-08-26 19:46:44 +03:00
}
2024-09-06 06:34:52 +03:00
func (r *Records) ListKnownDomains() []string {
r.mutex.Lock()
defer r.mutex.Unlock()
2024-09-14 16:13:59 +03:00
now := time.Now()
r.cleanupARecords(now)
r.cleanupCNameRecords(now)
2024-09-06 06:34:52 +03:00
2024-09-14 16:13:59 +03:00
domains := map[string]struct{}{}
for name, _ := range r.ARecords {
domains[name] = struct{}{}
}
for name, _ := range r.CNameRecords {
domains[name] = struct{}{}
2024-09-06 06:34:52 +03:00
}
2024-09-14 16:13:59 +03:00
domainsList := make([]string, len(domains))
i := 0
for name, _ := range domains {
domainsList[i] = name
i++
2024-08-25 00:28:34 +03:00
}
2024-09-14 16:13:59 +03:00
return domainsList
2024-08-25 00:28:34 +03:00
}
2024-08-24 19:47:10 +03:00
func NewRecords() *Records {
return &Records{
2024-09-14 16:13:59 +03:00
ARecords: make(map[string][]*ARecord),
CNameRecords: make(map[string]*CNameRecord),
2024-08-24 19:47:10 +03:00
}
}