1.4.5.2
This commit is contained in:
9
EdgeDNS/internal/models/agent_ip.go
Normal file
9
EdgeDNS/internal/models/agent_ip.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package models
|
||||
|
||||
type AgentIP struct {
|
||||
Id int64
|
||||
IP string
|
||||
AgentCode string
|
||||
}
|
||||
15
EdgeDNS/internal/models/ns_domain.go
Normal file
15
EdgeDNS/internal/models/ns_domain.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build plus
|
||||
|
||||
package models
|
||||
|
||||
import "github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
|
||||
|
||||
type NSDomain struct {
|
||||
Id int64
|
||||
ClusterId int64
|
||||
UserId int64
|
||||
Name string
|
||||
TSIG *dnsconfigs.NSTSIGConfig
|
||||
Version int64
|
||||
}
|
||||
13
EdgeDNS/internal/models/ns_key.go
Normal file
13
EdgeDNS/internal/models/ns_key.go
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package models
|
||||
|
||||
type NSKey struct {
|
||||
Id int64
|
||||
DomainId int64
|
||||
ZoneId int64
|
||||
Algo string
|
||||
Secret string
|
||||
SecretType string
|
||||
Version int64
|
||||
}
|
||||
27
EdgeDNS/internal/models/ns_keys.go
Normal file
27
EdgeDNS/internal/models/ns_keys.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package models
|
||||
|
||||
type NSKeys struct {
|
||||
m map[int64]*NSKey // keyId => *NSKey
|
||||
}
|
||||
|
||||
func NewNSKeys() *NSKeys {
|
||||
return &NSKeys{m: map[int64]*NSKey{}}
|
||||
}
|
||||
|
||||
func (this *NSKeys) Add(key *NSKey) {
|
||||
this.m[key.Id] = key
|
||||
}
|
||||
|
||||
func (this *NSKeys) Remove(keyId int64) {
|
||||
delete(this.m, keyId)
|
||||
}
|
||||
|
||||
func (this *NSKeys) All() []*NSKey {
|
||||
var result = []*NSKey{}
|
||||
for _, k := range this.m {
|
||||
result = append(result, k)
|
||||
}
|
||||
return result
|
||||
}
|
||||
166
EdgeDNS/internal/models/ns_record.go
Normal file
166
EdgeDNS/internal/models/ns_record.go
Normal file
@@ -0,0 +1,166 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"github.com/miekg/dns"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type NSRecord struct {
|
||||
Id int64
|
||||
Name string
|
||||
Type dnsconfigs.RecordType
|
||||
Value string
|
||||
|
||||
MXPriority int32
|
||||
|
||||
SRVPriority int32
|
||||
SRVWeight int32
|
||||
SRVPort int32
|
||||
|
||||
CAAFlag int32
|
||||
CAATag string
|
||||
|
||||
Ttl int32
|
||||
Weight int32
|
||||
Version int64
|
||||
RouteIds []string
|
||||
DomainId int64
|
||||
}
|
||||
|
||||
func (this *NSRecord) ToRRAnswer(questionName string, rrClass uint16) dns.RR {
|
||||
if this.Ttl <= 0 {
|
||||
this.Ttl = 60
|
||||
}
|
||||
|
||||
switch this.Type {
|
||||
case dnsconfigs.RecordTypeA:
|
||||
return &dns.A{
|
||||
Hdr: this.ToRRHeader(questionName, dns.TypeA, rrClass),
|
||||
A: net.ParseIP(this.Value),
|
||||
}
|
||||
case dnsconfigs.RecordTypeCNAME:
|
||||
var value = this.Value
|
||||
if !strings.HasSuffix(value, ".") {
|
||||
value += "."
|
||||
}
|
||||
return &dns.CNAME{
|
||||
Hdr: this.ToRRHeader(questionName, dns.TypeCNAME, rrClass),
|
||||
Target: value,
|
||||
}
|
||||
case dnsconfigs.RecordTypeAAAA:
|
||||
return &dns.AAAA{
|
||||
Hdr: this.ToRRHeader(questionName, dns.TypeAAAA, rrClass),
|
||||
AAAA: net.ParseIP(this.Value),
|
||||
}
|
||||
case dnsconfigs.RecordTypeNS:
|
||||
var value = this.Value
|
||||
if !strings.HasSuffix(value, ".") {
|
||||
value += "."
|
||||
}
|
||||
return &dns.NS{
|
||||
Hdr: this.ToRRHeader(questionName, dns.TypeNS, rrClass),
|
||||
Ns: value,
|
||||
}
|
||||
case dnsconfigs.RecordTypeMX:
|
||||
var value = this.Value
|
||||
if !strings.HasSuffix(value, ".") {
|
||||
value += "."
|
||||
}
|
||||
|
||||
var preference uint16 = 0
|
||||
var priority = this.MXPriority
|
||||
if priority >= 0 {
|
||||
if priority > 65535 {
|
||||
priority = 65535
|
||||
}
|
||||
preference = types.Uint16(priority)
|
||||
}
|
||||
|
||||
return &dns.MX{
|
||||
Hdr: this.ToRRHeader(questionName, dns.TypeMX, rrClass),
|
||||
Preference: preference,
|
||||
Mx: value,
|
||||
}
|
||||
case dnsconfigs.RecordTypeSRV:
|
||||
var priority uint16 = 10
|
||||
if this.SRVPriority > 0 {
|
||||
priority = uint16(this.SRVPriority)
|
||||
}
|
||||
|
||||
var weight uint16 = 10
|
||||
if this.SRVWeight > 0 {
|
||||
weight = uint16(this.SRVWeight)
|
||||
}
|
||||
|
||||
var port uint16 = 0
|
||||
if this.SRVPort > 0 {
|
||||
port = uint16(this.SRVPort)
|
||||
}
|
||||
|
||||
var value = this.Value
|
||||
if !strings.HasSuffix(value, ".") {
|
||||
value += "."
|
||||
}
|
||||
|
||||
return &dns.SRV{
|
||||
Hdr: this.ToRRHeader(questionName, dns.TypeSRV, rrClass),
|
||||
Priority: priority,
|
||||
Weight: weight,
|
||||
Port: port,
|
||||
Target: value,
|
||||
}
|
||||
case dnsconfigs.RecordTypeTXT:
|
||||
var values []string
|
||||
var runes = []rune(this.Value)
|
||||
const maxChars = 255
|
||||
for {
|
||||
if len(runes) <= maxChars {
|
||||
values = append(values, string(runes))
|
||||
break
|
||||
}
|
||||
values = append(values, string(runes[:maxChars]))
|
||||
runes = runes[maxChars:]
|
||||
if len(runes) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &dns.TXT{
|
||||
Hdr: this.ToRRHeader(questionName, dns.TypeTXT, rrClass),
|
||||
Txt: values, // TODO 可以添加多个
|
||||
}
|
||||
case dnsconfigs.RecordTypeCAA:
|
||||
var flag uint8 = 0
|
||||
if this.CAAFlag >= 0 && this.CAAFlag <= 128 {
|
||||
flag = uint8(this.CAAFlag)
|
||||
}
|
||||
|
||||
var tag = this.CAATag
|
||||
if tag != "issue" && tag != "issuewild" && tag != "iodef" {
|
||||
tag = "issue"
|
||||
}
|
||||
|
||||
return &dns.CAA{
|
||||
Hdr: this.ToRRHeader(questionName, dns.TypeCAA, rrClass),
|
||||
Flag: flag, // 0-128
|
||||
Tag: tag, // issue|issuewild|iodef
|
||||
Value: this.Value,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *NSRecord) ToRRHeader(questionName string, rrType uint16, rrClass uint16) dns.RR_Header {
|
||||
return dns.RR_Header{
|
||||
Name: questionName,
|
||||
Rrtype: rrType,
|
||||
Class: rrClass,
|
||||
Ttl: uint32(this.Ttl),
|
||||
}
|
||||
}
|
||||
45
EdgeDNS/internal/models/ns_route.go
Normal file
45
EdgeDNS/internal/models/ns_route.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build plus
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
|
||||
"net"
|
||||
)
|
||||
|
||||
type NSRoute struct {
|
||||
Id int64
|
||||
Ranges []dnsconfigs.NSRouteRangeInterface
|
||||
Priority int32
|
||||
Order int32
|
||||
UserId int64
|
||||
Version int64
|
||||
}
|
||||
|
||||
func (this *NSRoute) Contains(ip net.IP) bool {
|
||||
if len(ip) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 先执行IsReverse
|
||||
for _, r := range this.Ranges {
|
||||
if r.IsExcluding() && r.Contains(ip) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 再执行正常的
|
||||
for _, r := range this.Ranges {
|
||||
if !r.IsExcluding() && r.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RealCode 代号
|
||||
// TODO 支持自定义代号
|
||||
func (this *NSRoute) RealCode() string {
|
||||
return RouteIdString(this.Id)
|
||||
}
|
||||
58
EdgeDNS/internal/models/ranges.go
Normal file
58
EdgeDNS/internal/models/ranges.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build plus
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
// InitRangesFromJSON 从JSON中初始化线路范围
|
||||
func InitRangesFromJSON(rangesJSON []byte) (ranges []dnsconfigs.NSRouteRangeInterface, err error) {
|
||||
if len(rangesJSON) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var rangeMaps = []maps.Map{}
|
||||
err = json.Unmarshal(rangesJSON, &rangeMaps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, rangeMap := range rangeMaps {
|
||||
var rangeType = rangeMap.GetString("type")
|
||||
paramsJSON, err := json.Marshal(rangeMap.Get("params"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var r dnsconfigs.NSRouteRangeInterface
|
||||
|
||||
switch rangeType {
|
||||
case dnsconfigs.NSRouteRangeTypeIP:
|
||||
r = &dnsconfigs.NSRouteRangeIPRange{}
|
||||
case dnsconfigs.NSRouteRangeTypeCIDR:
|
||||
r = &dnsconfigs.NSRouteRangeCIDR{}
|
||||
case dnsconfigs.NSRouteRangeTypeRegion:
|
||||
r = &dnsconfigs.NSRouteRangeRegion{
|
||||
Connector: rangeMap.GetString("connector"),
|
||||
}
|
||||
r.SetRegionResolver(DefaultRegionResolver)
|
||||
default:
|
||||
return nil, errors.New("invalid route line type '" + rangeType + "'")
|
||||
}
|
||||
|
||||
err = json.Unmarshal(paramsJSON, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = r.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ranges = append(ranges, r)
|
||||
}
|
||||
return
|
||||
}
|
||||
172
EdgeDNS/internal/models/record_ids.go
Normal file
172
EdgeDNS/internal/models/record_ids.go
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build plus
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
type recordIdInfo struct {
|
||||
Id int64
|
||||
Weight int32
|
||||
}
|
||||
|
||||
type RecordIds struct {
|
||||
IdList []*recordIdInfo
|
||||
IdBucket []int64
|
||||
|
||||
RoundIndex int
|
||||
|
||||
totalWeight int64
|
||||
}
|
||||
|
||||
func NewRecordIds() *RecordIds {
|
||||
return &RecordIds{}
|
||||
}
|
||||
|
||||
func (this *RecordIds) IsEmpty() bool {
|
||||
return len(this.IdList) == 0
|
||||
}
|
||||
|
||||
func (this *RecordIds) Add(newId int64, weight int32) {
|
||||
if weight <= 0 {
|
||||
weight = 10
|
||||
}
|
||||
|
||||
const maxWeight = 999999
|
||||
if weight > maxWeight {
|
||||
weight = maxWeight
|
||||
}
|
||||
|
||||
// 检查是否存在
|
||||
for _, idInfo := range this.IdList {
|
||||
if idInfo.Id == newId {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 添加
|
||||
this.IdList = append(this.IdList, &recordIdInfo{
|
||||
Id: newId,
|
||||
Weight: weight,
|
||||
})
|
||||
|
||||
// 重置数据
|
||||
this.resetData()
|
||||
}
|
||||
|
||||
func (this *RecordIds) Remove(oldId int64) {
|
||||
defer this.resetData()
|
||||
|
||||
var newIdList = []*recordIdInfo{}
|
||||
for _, idInfo := range this.IdList {
|
||||
if idInfo.Id == oldId {
|
||||
continue
|
||||
}
|
||||
newIdList = append(newIdList, idInfo)
|
||||
}
|
||||
this.IdList = newIdList
|
||||
}
|
||||
|
||||
// NextId for round-robin
|
||||
func (this *RecordIds) NextId() int64 {
|
||||
var l = len(this.IdList)
|
||||
|
||||
if l == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
if l == 1 {
|
||||
return this.IdList[0].Id
|
||||
}
|
||||
|
||||
if this.RoundIndex > l-1 {
|
||||
this.RoundIndex = 0
|
||||
}
|
||||
|
||||
var id = this.IdList[this.RoundIndex].Id
|
||||
|
||||
this.RoundIndex++
|
||||
return id
|
||||
}
|
||||
|
||||
func (this *RecordIds) RandomIds(count int) []int64 {
|
||||
if count <= 0 {
|
||||
count = dnsconfigs.NSAnswerDefaultSize
|
||||
}
|
||||
|
||||
var totalRecords = len(this.IdList)
|
||||
|
||||
if totalRecords == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if totalRecords == 1 {
|
||||
return []int64{this.IdList[0].Id} // duplicate
|
||||
}
|
||||
|
||||
if totalRecords < count {
|
||||
count = totalRecords
|
||||
}
|
||||
|
||||
var totalIds = len(this.IdBucket)
|
||||
var startIndex = rands.Int(0, totalIds-1)
|
||||
var endIndex = startIndex + count - 1
|
||||
if endIndex <= totalIds-1 {
|
||||
return this.IdBucket[startIndex : endIndex+1]
|
||||
}
|
||||
return append(this.IdBucket[startIndex:totalIds], this.IdBucket[0:endIndex-totalIds+1]...)
|
||||
}
|
||||
|
||||
func (this *RecordIds) resetData() {
|
||||
this.resetWeight()
|
||||
}
|
||||
|
||||
func (this *RecordIds) resetWeight() {
|
||||
var totalWeight int64
|
||||
|
||||
var weightMap = map[int32]bool{} // weight => bool
|
||||
var hasUniqueWeights = false
|
||||
var ids []int64
|
||||
|
||||
for _, idInfo := range this.IdList {
|
||||
totalWeight += int64(idInfo.Weight)
|
||||
|
||||
// 检查是否有不同的权重
|
||||
if len(weightMap) > 0 && !weightMap[idInfo.Weight] {
|
||||
hasUniqueWeights = true
|
||||
}
|
||||
weightMap[idInfo.Weight] = true
|
||||
|
||||
ids = append(ids, idInfo.Id)
|
||||
}
|
||||
|
||||
// 根据权重,重新组织IDs
|
||||
if hasUniqueWeights {
|
||||
var newIds = []int64{}
|
||||
for _, idInfo := range this.IdList {
|
||||
for i := int32(0); i < idInfo.Weight; i++ {
|
||||
newIds = append(newIds, idInfo.Id)
|
||||
}
|
||||
}
|
||||
ids = newIds
|
||||
}
|
||||
|
||||
var countIds = len(ids)
|
||||
if countIds > 0 {
|
||||
rand.Shuffle(countIds, func(i, j int) {
|
||||
ids[i], ids[j] = ids[j], ids[i]
|
||||
})
|
||||
}
|
||||
|
||||
this.totalWeight = totalWeight
|
||||
this.IdBucket = ids
|
||||
}
|
||||
131
EdgeDNS/internal/models/record_ids_test.go
Normal file
131
EdgeDNS/internal/models/record_ids_test.go
Normal file
@@ -0,0 +1,131 @@
|
||||
// Copyright 2023 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
//go:build plus
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRecordIds_RandomIds_Once(t *testing.T) {
|
||||
var recordIds = NewRecordIds()
|
||||
for id := 1; id <= 10; id++ {
|
||||
recordIds.Add(int64(id), 1)
|
||||
}
|
||||
t.Log("totalWeight:", recordIds.totalWeight)
|
||||
t.Log(recordIds.RandomIds(5))
|
||||
}
|
||||
|
||||
func TestRecordIds_RandomIds_Once2(t *testing.T) {
|
||||
var recordIds = NewRecordIds()
|
||||
for id := 1; id <= 10; id++ {
|
||||
var weight int32 = 1
|
||||
if id%3 == 0 {
|
||||
weight = 3
|
||||
}
|
||||
recordIds.Add(int64(id), weight)
|
||||
}
|
||||
t.Log("totalWeight:", recordIds.totalWeight)
|
||||
t.Log(recordIds.RandomIds(5))
|
||||
}
|
||||
|
||||
func TestRecordIds_RandomIds(t *testing.T) {
|
||||
var recordIds = NewRecordIds()
|
||||
for id := 1; id <= 10; id++ {
|
||||
recordIds.Add(int64(id), 1)
|
||||
}
|
||||
t.Log("totalWeight:", recordIds.totalWeight)
|
||||
|
||||
var statMap = map[int64]int{}
|
||||
for i := 0; i < 2_000_000; i++ {
|
||||
var resultIds = recordIds.RandomIds(5)
|
||||
for _, resultId := range resultIds {
|
||||
statMap[resultId]++
|
||||
}
|
||||
}
|
||||
logs.PrintAsJSON(statMap, t)
|
||||
}
|
||||
|
||||
func TestRecordIds_RandomIds_Weight1(t *testing.T) {
|
||||
var recordIds = NewRecordIds()
|
||||
for id := 1; id <= 10; id++ {
|
||||
var weight int32 = 10
|
||||
if id%3 == 0 {
|
||||
weight = 20
|
||||
}
|
||||
recordIds.Add(int64(id), weight)
|
||||
}
|
||||
t.Log("totalWeight:", recordIds.totalWeight)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
t.Log(recordIds.RandomIds(5))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordIds_RandomIds_Weight2(t *testing.T) {
|
||||
var recordIds = NewRecordIds()
|
||||
for id := 1; id <= 10; id++ {
|
||||
var weight int32 = 10
|
||||
if id%3 == 0 {
|
||||
weight = 20
|
||||
}
|
||||
recordIds.Add(int64(id), weight)
|
||||
}
|
||||
t.Log("totalWeight:", recordIds.totalWeight)
|
||||
|
||||
var statMap = map[int64]int{}
|
||||
for i := 0; i < 2_000_000; i++ {
|
||||
var resultIds = recordIds.RandomIds(5)
|
||||
for _, resultId := range resultIds {
|
||||
statMap[resultId]++
|
||||
break
|
||||
}
|
||||
}
|
||||
logs.PrintAsJSON(statMap, t)
|
||||
}
|
||||
|
||||
func TestRecordIds_RandomIds_Weight3(t *testing.T) {
|
||||
var recordIds = NewRecordIds()
|
||||
for id := 1; id <= 5; id++ {
|
||||
var weight int32 = 10
|
||||
if id%3 == 0 {
|
||||
weight = 20
|
||||
}
|
||||
recordIds.Add(int64(id), weight)
|
||||
}
|
||||
t.Log("totalWeight:", recordIds.totalWeight)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
t.Log(recordIds.RandomIds(5))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRecordIds_RandomIds_SAME_Weight(b *testing.B) {
|
||||
var recordIds = NewRecordIds()
|
||||
for id := 1; id <= 100; id++ {
|
||||
var weight int32 = 10
|
||||
recordIds.Add(int64(id), weight)
|
||||
}
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
recordIds.RandomIds(5)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRecordIds_RandomIds(b *testing.B) {
|
||||
var recordIds = NewRecordIds()
|
||||
for id := 1; id <= 100; id++ {
|
||||
var weight int32 = 10
|
||||
if id%3 == 0 {
|
||||
weight = 20
|
||||
}
|
||||
recordIds.Add(int64(id), weight)
|
||||
}
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
recordIds.RandomIds(5)
|
||||
}
|
||||
}
|
||||
12
EdgeDNS/internal/models/record_key.go
Normal file
12
EdgeDNS/internal/models/record_key.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package models
|
||||
|
||||
import "strings"
|
||||
|
||||
type RecordKey string
|
||||
|
||||
func NewRecordKey(recordName string, recordType string) RecordKey {
|
||||
// 记录名全部使用小写
|
||||
return RecordKey(strings.ToLower(recordName) + "|" + recordType)
|
||||
}
|
||||
80
EdgeDNS/internal/models/records_domain.go
Normal file
80
EdgeDNS/internal/models/records_domain.go
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build plus
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DomainRecords struct {
|
||||
RecordsMap map[RecordKey]*RouteRecords // key => records
|
||||
Keys map[int64]RecordKey // recordId => key
|
||||
}
|
||||
|
||||
func NewDomainRecords() *DomainRecords {
|
||||
return &DomainRecords{
|
||||
RecordsMap: map[RecordKey]*RouteRecords{},
|
||||
Keys: map[int64]RecordKey{},
|
||||
}
|
||||
}
|
||||
|
||||
func (this *DomainRecords) Add(record *NSRecord) {
|
||||
var key = NewRecordKey(record.Name, record.Type)
|
||||
records, ok := this.RecordsMap[key]
|
||||
if !ok {
|
||||
records = NewRouteRecords()
|
||||
this.RecordsMap[key] = records
|
||||
}
|
||||
records.Add(record)
|
||||
|
||||
this.Keys[record.Id] = key
|
||||
}
|
||||
|
||||
func (this *DomainRecords) Find(routeCodes []string, recordName string, recordType string, config *dnsconfigs.NSAnswerConfig, strictMode bool) (record []*NSRecord, routeCode string) {
|
||||
// NAME.example.com
|
||||
var key = NewRecordKey(recordName, recordType)
|
||||
records, ok := this.RecordsMap[key]
|
||||
if ok {
|
||||
return records.Find(routeCodes, config, strictMode)
|
||||
}
|
||||
|
||||
// @.example.com
|
||||
if len(recordName) == 0 {
|
||||
records, ok = this.RecordsMap[NewRecordKey("@", recordType)]
|
||||
if ok {
|
||||
return records.Find(routeCodes, config, strictMode)
|
||||
}
|
||||
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
// *.NAME.example.com
|
||||
var dotIndex = strings.Index(recordName, ".")
|
||||
var wildcardNames = []string{}
|
||||
if dotIndex > 0 {
|
||||
wildcardNames = append(wildcardNames, "*."+recordName[dotIndex+1:])
|
||||
}
|
||||
wildcardNames = append(wildcardNames, "*")
|
||||
for _, wildcardName := range wildcardNames {
|
||||
records, ok = this.RecordsMap[NewRecordKey(wildcardName, recordType)]
|
||||
if ok {
|
||||
return records.Find(routeCodes, config, strictMode)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
func (this *DomainRecords) Remove(recordId int64) {
|
||||
key, ok := this.Keys[recordId]
|
||||
if ok {
|
||||
var recordsMap = this.RecordsMap[key]
|
||||
recordsMap.Remove(recordId)
|
||||
if recordsMap.IsEmpty() {
|
||||
delete(this.RecordsMap, key)
|
||||
}
|
||||
delete(this.Keys, recordId)
|
||||
}
|
||||
}
|
||||
57
EdgeDNS/internal/models/records_domains_test.go
Normal file
57
EdgeDNS/internal/models/records_domains_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build plus
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDomainRecords_Find(t *testing.T) {
|
||||
var records = NewDomainRecords()
|
||||
records.Add(&NSRecord{Id: 1, Name: "", Value: "1", Type: dnsconfigs.RecordTypeA})
|
||||
records.Add(&NSRecord{Id: 2, Name: "@", Value: "@", Type: dnsconfigs.RecordTypeA})
|
||||
records.Add(&NSRecord{Id: 3, Name: "*", Value: "*", Type: dnsconfigs.RecordTypeA})
|
||||
records.Add(&NSRecord{Id: 4, Name: "hello", Value: "HELLO", Type: dnsconfigs.RecordTypeA})
|
||||
records.Add(&NSRecord{Id: 5, Name: "*.world", Value: "*.world", Type: dnsconfigs.RecordTypeA})
|
||||
for _, name := range []string{"", "hello", "world", "hello.world", "hello.world2"} {
|
||||
record, routeCode := records.Find([]string{}, name, dnsconfigs.RecordTypeA, nil, false)
|
||||
t.Log(name, record, routeCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDomainRecords_Find_RouteIds(t *testing.T) {
|
||||
var records = NewDomainRecords()
|
||||
records.Add(&NSRecord{Id: 1, Name: "", Value: "1", Type: dnsconfigs.RecordTypeA, RouteIds: []string{RouteIdString(11), RouteIdString(22)}})
|
||||
records.Add(&NSRecord{Id: 2, Name: "@", Value: "@", Type: dnsconfigs.RecordTypeA})
|
||||
records.Add(&NSRecord{Id: 3, Name: "*", Value: "*", Type: dnsconfigs.RecordTypeA})
|
||||
records.Add(&NSRecord{Id: 4, Name: "hello", Value: "HELLO", Type: dnsconfigs.RecordTypeA})
|
||||
records.Add(&NSRecord{Id: 41, Name: "hello", Value: "HELLO1", Type: dnsconfigs.RecordTypeA})
|
||||
records.Add(&NSRecord{Id: 42, Name: "hello", Value: "HELLO2", Type: dnsconfigs.RecordTypeA})
|
||||
records.Add(&NSRecord{Id: 43, Name: "hello", Value: "HELLO3", Type: dnsconfigs.RecordTypeA})
|
||||
records.Add(&NSRecord{Id: 5, Name: "*.world", Value: "*.world", Type: dnsconfigs.RecordTypeA})
|
||||
for _, name := range []string{"", "hello", "world", "hello.world", "hello.world2"} {
|
||||
record, _ := records.Find([]string{RouteIdString(11), RouteIdString(22)}, name, dnsconfigs.RecordTypeA, nil, false)
|
||||
if record == nil {
|
||||
t.Fatal("'" + name + "' record should not be nil")
|
||||
}
|
||||
t.Log(name, record)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDomainRecords_Remove(t *testing.T) {
|
||||
var records = NewDomainRecords()
|
||||
records.Add(&NSRecord{Id: 1, Name: "", Value: "1", Type: dnsconfigs.RecordTypeA})
|
||||
records.Add(&NSRecord{Id: 2, Name: "@", Value: "@", Type: dnsconfigs.RecordTypeA})
|
||||
records.Add(&NSRecord{Id: 3, Name: "*", Value: "*", Type: dnsconfigs.RecordTypeA})
|
||||
records.Add(&NSRecord{Id: 4, Name: "hello", Value: "HELLO", Type: dnsconfigs.RecordTypeA})
|
||||
records.Add(&NSRecord{Id: 5, Name: "*.world", Value: "*.world", Type: dnsconfigs.RecordTypeA})
|
||||
records.Remove(1)
|
||||
records.Remove(2)
|
||||
records.Remove(3)
|
||||
records.Remove(4)
|
||||
//records.Remove(5)
|
||||
logs.PrintAsJSON(records, t)
|
||||
}
|
||||
134
EdgeDNS/internal/models/records_route.go
Normal file
134
EdgeDNS/internal/models/records_route.go
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build plus
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/dnsconfigs"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
)
|
||||
|
||||
func RouteIdString(routeId int64) string {
|
||||
return "id:" + types.String(routeId)
|
||||
}
|
||||
|
||||
type RouteRecords struct {
|
||||
routeRecordsMap map[string]*RecordIds // routeCode => { recordId1, recordId2, ... }
|
||||
recordsMap map[int64]*NSRecord // recordId => *NSRecord
|
||||
}
|
||||
|
||||
func NewRouteRecords() *RouteRecords {
|
||||
return &RouteRecords{
|
||||
routeRecordsMap: map[string]*RecordIds{},
|
||||
recordsMap: map[int64]*NSRecord{},
|
||||
}
|
||||
}
|
||||
|
||||
func (this *RouteRecords) Add(record *NSRecord) {
|
||||
// 先删除
|
||||
this.remove(record.Id)
|
||||
|
||||
// 添加记录
|
||||
this.recordsMap[record.Id] = record
|
||||
|
||||
// 添加线路
|
||||
var routeIds = record.RouteIds
|
||||
if len(routeIds) == 0 || (len(routeIds) == 1 && routeIds[0] == "") {
|
||||
routeIds = []string{"default"}
|
||||
}
|
||||
|
||||
for _, routeId := range routeIds {
|
||||
recordIds, ok := this.routeRecordsMap[routeId]
|
||||
if !ok {
|
||||
recordIds = NewRecordIds()
|
||||
this.routeRecordsMap[routeId] = recordIds
|
||||
}
|
||||
recordIds.Add(record.Id, record.Weight)
|
||||
}
|
||||
}
|
||||
|
||||
// Find 查找与线路匹配的记录
|
||||
// strictMode 表示是否严格匹配线路
|
||||
func (this *RouteRecords) Find(routeCodes []string, config *dnsconfigs.NSAnswerConfig, strictMode bool) (records []*NSRecord, routeCode string) {
|
||||
if config == nil {
|
||||
config = dnsconfigs.DefaultNSAnswerConfig()
|
||||
}
|
||||
|
||||
var maxSize = int(config.MaxSize)
|
||||
if maxSize <= 0 {
|
||||
maxSize = dnsconfigs.NSAnswerDefaultSize
|
||||
}
|
||||
|
||||
// 查找匹配的线路
|
||||
for _, routeId := range routeCodes {
|
||||
recordIds, ok := this.routeRecordsMap[routeId]
|
||||
if ok && !recordIds.IsEmpty() {
|
||||
return this.recordsWithIds(recordIds, config.Mode, maxSize), routeId
|
||||
}
|
||||
}
|
||||
|
||||
// 查找默认线路
|
||||
recordIds, ok := this.routeRecordsMap["default"]
|
||||
if ok && !recordIds.IsEmpty() {
|
||||
return this.recordsWithIds(recordIds, config.Mode, maxSize), "default"
|
||||
}
|
||||
|
||||
// 随机一个
|
||||
if !strictMode {
|
||||
for _, record := range this.recordsMap {
|
||||
return []*NSRecord{record}, "default"
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
func (this *RouteRecords) Remove(recordId int64) {
|
||||
this.remove(recordId)
|
||||
}
|
||||
|
||||
func (this *RouteRecords) IsEmpty() bool {
|
||||
return len(this.recordsMap) == 0
|
||||
}
|
||||
|
||||
func (this *RouteRecords) remove(recordId int64) {
|
||||
oldRecord, ok := this.recordsMap[recordId]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
delete(this.recordsMap, recordId)
|
||||
|
||||
var oldRouteIds = oldRecord.RouteIds
|
||||
if len(oldRouteIds) == 0 || (len(oldRouteIds) == 1 && oldRouteIds[0] == "") {
|
||||
oldRouteIds = []string{"default"}
|
||||
}
|
||||
|
||||
for _, routeId := range oldRouteIds {
|
||||
recordIds, ok := this.routeRecordsMap[routeId]
|
||||
if ok {
|
||||
recordIds.Remove(recordId)
|
||||
if recordIds.IsEmpty() {
|
||||
delete(this.routeRecordsMap, routeId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *RouteRecords) recordsWithIds(recordIds *RecordIds, mode dnsconfigs.NSAnswerMode, maxSize int) (records []*NSRecord) {
|
||||
// round-robin
|
||||
if mode == dnsconfigs.NSAnswerModeRoundRobin {
|
||||
var recordId = recordIds.NextId()
|
||||
if recordId > 0 {
|
||||
return []*NSRecord{this.recordsMap[recordId]}
|
||||
}
|
||||
}
|
||||
|
||||
// random
|
||||
var randomIds = recordIds.RandomIds(maxSize)
|
||||
for _, randomId := range randomIds {
|
||||
records = append(records, this.recordsMap[randomId])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
118
EdgeDNS/internal/models/records_route_test.go
Normal file
118
EdgeDNS/internal/models/records_route_test.go
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
//go:build plus
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRouteRecords_Add(t *testing.T) {
|
||||
var records = NewRouteRecords()
|
||||
{
|
||||
records.Add(&NSRecord{Id: 1, RouteIds: []string{"CN"}})
|
||||
records.Add(&NSRecord{Id: 2})
|
||||
logs.PrintAsJSON(records.routeRecordsMap, t)
|
||||
logs.PrintAsJSON(records.recordsMap, t)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteRecords_Add2(t *testing.T) {
|
||||
var records = NewRouteRecords()
|
||||
{
|
||||
records.Add(&NSRecord{Id: 1, RouteIds: []string{RouteIdString(11), RouteIdString(22)}})
|
||||
records.Add(&NSRecord{Id: 2, RouteIds: []string{RouteIdString(11), RouteIdString(22)}})
|
||||
logs.PrintAsJSON(records.routeRecordsMap, t)
|
||||
logs.PrintAsJSON(records.recordsMap, t)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteRecords_Add3(t *testing.T) {
|
||||
var records = NewRouteRecords()
|
||||
{
|
||||
records.Add(&NSRecord{Id: 1, RouteIds: []string{RouteIdString(11), RouteIdString(22)}})
|
||||
records.Add(&NSRecord{Id: 2, RouteIds: []string{RouteIdString(11), RouteIdString(22)}})
|
||||
records.Add(&NSRecord{Id: 2, RouteIds: []string{RouteIdString(33), RouteIdString(44)}}) // duplicated
|
||||
logs.PrintAsJSON(records.routeRecordsMap, t)
|
||||
logs.PrintAsJSON(records.recordsMap, t)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteRecords_Remove(t *testing.T) {
|
||||
var records = NewRouteRecords()
|
||||
records.Add(&NSRecord{Id: 1})
|
||||
records.Add(&NSRecord{Id: 2, RouteIds: []string{RouteIdString(11), RouteIdString(22)}})
|
||||
records.Add(&NSRecord{Id: 3, RouteIds: []string{RouteIdString(11), RouteIdString(22)}})
|
||||
records.Add(&NSRecord{Id: 4, RouteIds: []string{RouteIdString(11)}})
|
||||
t.Log("===before===")
|
||||
logs.PrintAsJSON(records.routeRecordsMap, t)
|
||||
logs.PrintAsJSON(records.recordsMap, t)
|
||||
t.Log("===after===")
|
||||
//records.Remove(1)
|
||||
records.Remove(2)
|
||||
logs.PrintAsJSON(records.routeRecordsMap, t)
|
||||
logs.PrintAsJSON(records.recordsMap, t)
|
||||
}
|
||||
|
||||
func TestRouteRecords_Find(t *testing.T) {
|
||||
var records = NewRouteRecords()
|
||||
records.Add(&NSRecord{Id: 1})
|
||||
records.Add(&NSRecord{Id: 2, RouteIds: []string{RouteIdString(11), RouteIdString(22)}})
|
||||
records.Add(&NSRecord{Id: 3, RouteIds: []string{RouteIdString(11), RouteIdString(22)}})
|
||||
records.Add(&NSRecord{Id: 4, RouteIds: []string{RouteIdString(11)}})
|
||||
for _, routeIds := range [][]string{
|
||||
{},
|
||||
{RouteIdString(11)},
|
||||
{RouteIdString(22)},
|
||||
{RouteIdString(100)},
|
||||
} {
|
||||
record, routeId := records.Find(routeIds, nil, false)
|
||||
t.Logf("routeIds: %v, record: %#v, route: %s", routeIds, record, routeId)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteRecords_Find_Balance(t *testing.T) {
|
||||
var records = NewRouteRecords()
|
||||
records.Add(&NSRecord{Id: 1, RouteIds: []string{"aa"}})
|
||||
records.Add(&NSRecord{Id: 2, RouteIds: []string{"aa", "bb", "default"}})
|
||||
records.Add(&NSRecord{Id: 3})
|
||||
records.Add(&NSRecord{Id: 4})
|
||||
|
||||
for _, route := range []string{"", "default", "aa", "bb", "cc"} {
|
||||
var m = map[int64]int{} // id => count
|
||||
for i := 0; i < 1_000_000; i++ {
|
||||
var records, _ = records.Find([]string{route}, nil, false)
|
||||
for _, record := range records {
|
||||
m[record.Id]++
|
||||
}
|
||||
}
|
||||
t.Logf("%s: %+v", route, m)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRouteRecords_Remove(b *testing.B) {
|
||||
var records = NewRouteRecords()
|
||||
for i := 0; i < 1_000_000; i++ {
|
||||
records.Add(&NSRecord{Id: int64(i), RouteIds: []string{RouteIdString(int64(i % 100))}})
|
||||
}
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
records.Remove(int64(rands.Int(0, 100)))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRouteRecords_Find(b *testing.B) {
|
||||
var records = NewRouteRecords()
|
||||
for i := 0; i < 1_000_000; i++ {
|
||||
records.Add(&NSRecord{Id: int64(i), RouteIds: []string{RouteIdString(int64(i % 100))}})
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = records.Find([]string{RouteIdString(int64(i % 200))}, nil, false)
|
||||
}
|
||||
}
|
||||
24
EdgeDNS/internal/models/region_resolver.go
Normal file
24
EdgeDNS/internal/models/region_resolver.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||
"net"
|
||||
)
|
||||
|
||||
var DefaultRegionResolver = &RegionResolver{}
|
||||
|
||||
type RegionResolver struct {
|
||||
}
|
||||
|
||||
func (this *RegionResolver) Resolve(ip net.IP) (countryId int64, provinceId int64, cityId int64, providerId int64) {
|
||||
var result = iplibrary.Lookup(ip)
|
||||
if result != nil && result.IsOk() {
|
||||
countryId = result.CountryId()
|
||||
provinceId = result.ProvinceId()
|
||||
cityId = result.CityId()
|
||||
providerId = result.ProviderId()
|
||||
}
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user