// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved. //go:build plus package dnsconfigs import ( "encoding/json" "errors" "fmt" "github.com/TeaOSLab/EdgeCommon/pkg/configutils" "github.com/TeaOSLab/EdgeCommon/pkg/iputils" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared" "github.com/iwind/TeaGo/maps" "net" ) type NSRouteRangeType = string const ( NSRouteRangeTypeIP NSRouteRangeType = "ipRange" // IP范围 NSRouteRangeTypeCIDR NSRouteRangeType = "cidr" // CIDR NSRouteRangeTypeRegion NSRouteRangeType = "region" // 区域 ) func AllNSRouteRangeTypes() []*shared.Definition { return []*shared.Definition{ { Name: "IP范围", Code: NSRouteRangeTypeIP, }, { Name: "CIDR", Code: NSRouteRangeTypeCIDR, }, { Name: "区域", Code: NSRouteRangeTypeRegion, }, } } // NSRouteRegionResolver 解析IP接口 type NSRouteRegionResolver interface { Resolve(ip net.IP) (countryId int64, provinceId int64, cityId int64, providerId int64) } // NSRouteRangeInterface 线路范围接口 type NSRouteRangeInterface interface { // Init 初始化 Init() error // Contains 判断是否包含 Contains(ip net.IP) bool // SetRegionResolver 设置IP解析接口 SetRegionResolver(resolver NSRouteRegionResolver) // IsExcluding 是否为排除 IsExcluding() bool } type NSBaseRouteRange struct { IsReverse bool `json:"isReverse"` routeRegionResolver NSRouteRegionResolver } func (this *NSBaseRouteRange) SetRegionResolver(resolver NSRouteRegionResolver) { this.routeRegionResolver = resolver } func (this *NSBaseRouteRange) IsExcluding() bool { return this.IsReverse } // NSRouteRangeIPRange IP范围配置 // IPv4和IPv6不能混用 type NSRouteRangeIPRange struct { NSBaseRouteRange IPFrom string `json:"ipFrom"` IPTo string `json:"ipTo"` ipFrom iputils.IP ipTo iputils.IP ipVersion int // 4|6 } func (this *NSRouteRangeIPRange) Init() error { var ipFrom = net.ParseIP(this.IPFrom) var ipTo = net.ParseIP(this.IPTo) if ipFrom == nil { return errors.New("invalid ipFrom '" + this.IPFrom + "'") } if ipTo == nil { return errors.New("invalid ipTo '" + this.IPTo + "'") } var ipFromVersion = configutils.IPVersion(ipFrom) var ipToVersion = configutils.IPVersion(ipTo) if ipFromVersion != ipToVersion { return errors.New("ipFrom and ipTo version are not same") } this.ipVersion = ipFromVersion this.ipFrom = iputils.NewIP(ipFrom) this.ipTo = iputils.NewIP(ipTo) if this.ipFrom.Compare(this.ipTo) > 0 { this.ipFrom, this.ipTo = this.ipTo, this.ipFrom } return nil } func (this *NSRouteRangeIPRange) Contains(netIP net.IP) bool { if len(netIP) == 0 { return false } var version = configutils.IPVersion(netIP) if version != this.ipVersion { return false } return iputils.NewIP(netIP).Between(this.ipFrom, this.ipTo) } // NSRouteRangeCIDR CIDR范围配置 type NSRouteRangeCIDR struct { NSBaseRouteRange CIDR string `json:"cidr"` cidr *net.IPNet } func (this *NSRouteRangeCIDR) Init() error { _, ipNet, err := net.ParseCIDR(this.CIDR) if err != nil { return fmt.Errorf("parse cidr failed: %w", err) } this.cidr = ipNet return nil } func (this *NSRouteRangeCIDR) Contains(netIP net.IP) bool { if netIP == nil { return false } if this.cidr == nil { return false } return this.cidr.Contains(netIP) } // NSRouteRangeRegion 区域范围 // country:ID, province:ID, city:ID, isp:ID type NSRouteRangeRegion struct { NSBaseRouteRange Regions []*RouteRegion `json:"regions"` Connector string `json:"connector"` // AND | OR } func (this *NSRouteRangeRegion) Init() error { return nil } func (this *NSRouteRangeRegion) Contains(netIP net.IP) bool { if this.routeRegionResolver == nil { return false } if len(this.Regions) == 0 { return false } countryId, provinceId, cityId, providerId := this.routeRegionResolver.Resolve(netIP) if countryId <= 0 && provinceId <= 0 && cityId <= 0 && providerId <= 0 { return false } var matchAll = this.Connector == "AND" for _, region := range this.Regions { if region.Id <= 0 { continue } switch region.Type { case "country": if region.Id == countryId { if !matchAll { return true } } else if matchAll { return false } case "province": if region.Id == provinceId { if !matchAll { return true } } else if matchAll { return false } case "city": if region.Id == cityId { if !matchAll { return true } } else if matchAll { return false } case "provider": if region.Id == providerId { if !matchAll { return true } } else if matchAll { return false } } } return matchAll } type RouteRegion struct { Type string `json:"type"` // country|province|city|isp Id int64 `json:"id"` Name string `json:"name"` } // InitNSRangesFromJSON 从JSON中初始化线路范围 func InitNSRangesFromJSON(rangesJSON []byte) (ranges []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 NSRouteRangeInterface switch rangeType { case NSRouteRangeTypeIP: r = &NSRouteRangeIPRange{} case NSRouteRangeTypeCIDR: r = &NSRouteRangeCIDR{} case NSRouteRangeTypeRegion: r = &NSRouteRangeRegion{} 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 }