This commit is contained in:
unknown
2026-02-04 20:27:13 +08:00
commit 3b042d1dad
9410 changed files with 1488147 additions and 0 deletions

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

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

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

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

View 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),
}
}

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

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

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

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

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

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

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

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

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

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