MagiTrickle/records/records.go

168 lines
2.9 KiB
Go
Raw Normal View History

2025-02-11 02:50:13 +03:00
package records
import (
"bytes"
"net"
"sync"
"time"
)
type ARecord struct {
Address net.IP
Deadline time.Time
}
type CNameRecord struct {
Alias string
Deadline time.Time
}
type Records struct {
mux sync.RWMutex
records map[string]interface{}
}
2025-02-11 23:05:24 +03:00
func (r *Records) AddCNameRecord(domainName, alias string, ttl uint32) {
2025-02-11 02:50:13 +03:00
if domainName == alias {
return
}
r.mux.Lock()
r.records[domainName] = &CNameRecord{
Alias: alias,
2025-02-11 23:05:24 +03:00
Deadline: time.Now().Add(time.Duration(ttl) * time.Second),
2025-02-11 02:50:13 +03:00
}
r.mux.Unlock()
}
2025-02-11 23:05:24 +03:00
func (r *Records) AddARecord(domainName string, addr net.IP, ttl uint32) {
2025-02-11 02:50:13 +03:00
r.mux.Lock()
defer r.mux.Unlock()
2025-02-11 23:05:24 +03:00
deadline := time.Now().Add(time.Duration(ttl) * time.Second)
2025-02-11 02:50:13 +03:00
aRecords, _ := r.records[domainName].([]*ARecord)
for _, aRecord := range aRecords {
if bytes.Compare(aRecord.Address, addr) != 0 {
continue
}
aRecord.Deadline = deadline
return
}
r.records[domainName] = append(aRecords, &ARecord{
Address: addr,
Deadline: deadline,
})
}
func (r *Records) GetAliases(domainName string) []string {
r.mux.Lock()
defer r.mux.Unlock()
r.cleanupRecords()
domains := make(map[string]struct{})
domains[domainName] = struct{}{}
for {
var addedNew bool
for name, aRecord := range r.records {
if _, ok := domains[name]; ok {
continue
}
cname, ok := aRecord.(*CNameRecord)
if !ok {
continue
}
if _, ok = domains[cname.Alias]; !ok {
continue
}
domains[name] = struct{}{}
addedNew = true
}
if !addedNew {
break
}
}
domainList := make([]string, len(domains))
idx := 0
for name, _ := range domains {
domainList[idx] = name
idx++
}
return domainList
}
func (r *Records) GetARecords(domainName string) []*ARecord {
r.mux.Lock()
defer r.mux.Unlock()
r.cleanupRecords()
loopDetect := make(map[string]struct{})
loopDetect[domainName] = struct{}{}
for {
switch v := r.records[domainName].(type) {
case *CNameRecord:
if _, ok := loopDetect[v.Alias]; ok {
return nil
}
domainName = v.Alias
loopDetect[v.Alias] = struct{}{}
case []*ARecord:
return v
default:
return nil
}
}
}
func (r *Records) ListKnownDomains() []string {
r.mux.Lock()
defer r.mux.Unlock()
r.cleanupRecords()
domainsList := make([]string, len(r.records))
i := 0
for name, _ := range r.records {
domainsList[i] = name
i++
}
return domainsList
}
func (r *Records) cleanupRecords() {
now := time.Now()
for name, records := range r.records {
switch v := records.(type) {
case []*ARecord:
idx := 0
for _, aRecord := range v {
if now.After(aRecord.Deadline) {
continue
}
v[idx] = aRecord
idx++
}
if idx == 0 {
delete(r.records, name)
break
}
r.records[name] = v[:idx]
case *CNameRecord:
if !now.After(v.Deadline) {
continue
}
delete(r.records, name)
}
}
}
func New() *Records {
return &Records{
records: make(map[string]interface{}),
}
}