mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-05-08 14:13:22 +00:00
Xray-core: Refactor geodata (#5814)
https://github.com/XTLS/Xray-core/issues/4422#issuecomment-3533007890 Breaking changes https://github.com/XTLS/Xray-core/pull/5569 Reverts https://github.com/XTLS/Xray-core/pull/5505 Closes https://github.com/XTLS/Xray-core/pull/643
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
. "github.com/xtls/xray-core/app/router/command"
|
||||
"github.com/xtls/xray-core/app/stats"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/geodata"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
"github.com/xtls/xray-core/testing/mocks"
|
||||
@@ -303,12 +304,12 @@ func TestServiceTestRoute(t *testing.T) {
|
||||
TargetTag: &router.RoutingRule_Tag{Tag: "out"},
|
||||
},
|
||||
{
|
||||
Domain: []*router.Domain{{Type: router.Domain_Domain, Value: "com"}},
|
||||
Domain: []*geodata.DomainRule{{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "com"}}}},
|
||||
TargetTag: &router.RoutingRule_Tag{Tag: "out"},
|
||||
},
|
||||
{
|
||||
SourceGeoip: []*router.GeoIP{{CountryCode: "private", Cidr: []*router.CIDR{{Ip: []byte{127, 0, 0, 0}, Prefix: 8}}}},
|
||||
TargetTag: &router.RoutingRule_Tag{Tag: "out"},
|
||||
SourceIp: []*geodata.IPRule{{Value: &geodata.IPRule_Custom{Custom: &geodata.CIDR{Ip: []byte{127, 0, 0, 0}, Prefix: 8}}}},
|
||||
TargetTag: &router.RoutingRule_Tag{Tag: "out"},
|
||||
},
|
||||
{
|
||||
UserEmail: []string{"example@example.com"},
|
||||
|
||||
@@ -2,7 +2,6 @@ package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -10,8 +9,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/geodata"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/strmatcher"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
)
|
||||
|
||||
@@ -45,67 +44,18 @@ func (v *ConditionChan) Len() int {
|
||||
return len(*v)
|
||||
}
|
||||
|
||||
var matcherTypeMap = map[Domain_Type]strmatcher.Type{
|
||||
Domain_Plain: strmatcher.Substr,
|
||||
Domain_Regex: strmatcher.Regex,
|
||||
Domain_Domain: strmatcher.Domain,
|
||||
Domain_Full: strmatcher.Full,
|
||||
}
|
||||
type DomainMatcher struct{ geodata.DomainMatcher }
|
||||
|
||||
type DomainMatcher struct {
|
||||
Matchers strmatcher.IndexMatcher
|
||||
}
|
||||
|
||||
func SerializeDomainMatcher(domains []*Domain, w io.Writer) error {
|
||||
|
||||
g := strmatcher.NewMphMatcherGroup()
|
||||
for _, d := range domains {
|
||||
matcherType, f := matcherTypeMap[d.Type]
|
||||
if !f {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := g.AddPattern(d.Value, matcherType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
g.Build()
|
||||
// serialize
|
||||
return g.Serialize(w)
|
||||
}
|
||||
|
||||
func NewDomainMatcherFromBuffer(data []byte) (*strmatcher.MphMatcherGroup, error) {
|
||||
matcher, err := strmatcher.NewMphMatcherGroupFromBuffer(data)
|
||||
func NewDomainMatcher(rules []*geodata.DomainRule) (*DomainMatcher, error) {
|
||||
m, err := geodata.DomainReg.BuildDomainMatcher(rules)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return matcher, nil
|
||||
}
|
||||
|
||||
func NewMphMatcherGroup(domains []*Domain) (*DomainMatcher, error) {
|
||||
g := strmatcher.NewMphMatcherGroup()
|
||||
for i, d := range domains {
|
||||
domains[i] = nil
|
||||
matcherType, f := matcherTypeMap[d.Type]
|
||||
if !f {
|
||||
errors.LogError(context.Background(), "ignore unsupported domain type ", d.Type, " of rule ", d.Value)
|
||||
continue
|
||||
}
|
||||
_, err := g.AddPattern(d.Value, matcherType)
|
||||
if err != nil {
|
||||
errors.LogErrorInner(context.Background(), err, "ignore domain rule ", d.Type, " ", d.Value)
|
||||
continue
|
||||
}
|
||||
}
|
||||
g.Build()
|
||||
return &DomainMatcher{
|
||||
Matchers: g,
|
||||
}, nil
|
||||
return &DomainMatcher{DomainMatcher: m}, nil
|
||||
}
|
||||
|
||||
func (m *DomainMatcher) ApplyDomain(domain string) bool {
|
||||
return len(m.Matchers.Match(strings.ToLower(domain))) > 0
|
||||
return m.DomainMatcher.MatchAny(strings.ToLower(domain))
|
||||
}
|
||||
|
||||
// Apply implements Condition.
|
||||
@@ -114,7 +64,7 @@ func (m *DomainMatcher) Apply(ctx routing.Context) bool {
|
||||
if len(domain) == 0 {
|
||||
return false
|
||||
}
|
||||
return m.ApplyDomain(domain)
|
||||
return m.DomainMatcher.MatchAny(strings.ToLower(domain))
|
||||
}
|
||||
|
||||
type MatcherAsType byte
|
||||
@@ -127,16 +77,16 @@ const (
|
||||
)
|
||||
|
||||
type IPMatcher struct {
|
||||
matcher GeoIPMatcher
|
||||
matcher geodata.IPMatcher
|
||||
asType MatcherAsType
|
||||
}
|
||||
|
||||
func NewIPMatcher(geoips []*GeoIP, asType MatcherAsType) (*IPMatcher, error) {
|
||||
matcher, err := BuildOptimizedGeoIPMatcher(geoips...)
|
||||
func NewIPMatcher(rules []*geodata.IPRule, asType MatcherAsType) (*IPMatcher, error) {
|
||||
m, err := geodata.IPReg.BuildIPMatcher(rules)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &IPMatcher{matcher: matcher, asType: asType}, nil
|
||||
return &IPMatcher{matcher: m, asType: asType}, nil
|
||||
}
|
||||
|
||||
// Apply implements Condition.
|
||||
|
||||
@@ -1,962 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
|
||||
"go4.org/netipx"
|
||||
)
|
||||
|
||||
type GeoIPMatcher interface {
|
||||
// TODO: (PERF) all net.IP -> netipx.Addr
|
||||
|
||||
// Invalid IP always return false.
|
||||
Match(ip net.IP) bool
|
||||
|
||||
// Returns true if *any* IP is valid and match.
|
||||
AnyMatch(ips []net.IP) bool
|
||||
|
||||
// Returns true only if *all* IPs are valid and match. Any invalid IP, or non-matching valid IP, causes false.
|
||||
Matches(ips []net.IP) bool
|
||||
|
||||
// Filters IPs. Invalid IPs are silently dropped and not included in either result.
|
||||
FilterIPs(ips []net.IP) (matched []net.IP, unmatched []net.IP)
|
||||
|
||||
ToggleReverse()
|
||||
|
||||
SetReverse(reverse bool)
|
||||
}
|
||||
|
||||
type GeoIPSet struct {
|
||||
ipv4, ipv6 *netipx.IPSet
|
||||
max4, max6 uint8
|
||||
}
|
||||
|
||||
type HeuristicGeoIPMatcher struct {
|
||||
ipset *GeoIPSet
|
||||
reverse bool
|
||||
}
|
||||
|
||||
type ipBucket struct {
|
||||
rep netip.Addr
|
||||
ips []net.IP
|
||||
}
|
||||
|
||||
// Match implements GeoIPMatcher.
|
||||
func (m *HeuristicGeoIPMatcher) Match(ip net.IP) bool {
|
||||
ipx, ok := netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return m.matchAddr(ipx)
|
||||
}
|
||||
|
||||
func (m *HeuristicGeoIPMatcher) matchAddr(ipx netip.Addr) bool {
|
||||
if ipx.Is4() {
|
||||
return m.ipset.ipv4.Contains(ipx) != m.reverse
|
||||
}
|
||||
if ipx.Is6() {
|
||||
return m.ipset.ipv6.Contains(ipx) != m.reverse
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AnyMatch implements GeoIPMatcher.
|
||||
func (m *HeuristicGeoIPMatcher) AnyMatch(ips []net.IP) bool {
|
||||
n := len(ips)
|
||||
if n == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if n == 1 {
|
||||
return m.Match(ips[0])
|
||||
}
|
||||
|
||||
heur4 := m.ipset.max4 <= 24
|
||||
heur6 := m.ipset.max6 <= 64
|
||||
if !heur4 && !heur6 {
|
||||
for _, ip := range ips {
|
||||
if ipx, ok := netipx.FromStdIP(ip); ok {
|
||||
if m.matchAddr(ipx) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
buckets := make(map[[9]byte]struct{}, n)
|
||||
for _, ip := range ips {
|
||||
key, ok := prefixKeyFromIP(ip)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
heur := (key[0] == 4 && heur4) || (key[0] == 6 && heur6)
|
||||
if heur {
|
||||
if _, exists := buckets[key]; exists {
|
||||
continue
|
||||
}
|
||||
}
|
||||
ipx, ok := netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if m.matchAddr(ipx) {
|
||||
return true
|
||||
}
|
||||
if heur {
|
||||
buckets[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Matches implements GeoIPMatcher.
|
||||
func (m *HeuristicGeoIPMatcher) Matches(ips []net.IP) bool {
|
||||
n := len(ips)
|
||||
if n == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if n == 1 {
|
||||
return m.Match(ips[0])
|
||||
}
|
||||
|
||||
heur4 := m.ipset.max4 <= 24
|
||||
heur6 := m.ipset.max6 <= 64
|
||||
if !heur4 && !heur6 {
|
||||
for _, ip := range ips {
|
||||
ipx, ok := netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if !m.matchAddr(ipx) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
buckets := make(map[[9]byte]netip.Addr, n)
|
||||
precise := make([]netip.Addr, 0, n)
|
||||
|
||||
for _, ip := range ips {
|
||||
key, ok := prefixKeyFromIP(ip)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if (key[0] == 4 && heur4) || (key[0] == 6 && heur6) {
|
||||
if _, exists := buckets[key]; !exists {
|
||||
ipx, ok := netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
buckets[key] = ipx
|
||||
}
|
||||
} else {
|
||||
ipx, ok := netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
precise = append(precise, ipx)
|
||||
}
|
||||
}
|
||||
|
||||
for _, ipx := range buckets {
|
||||
if !m.matchAddr(ipx) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, ipx := range precise {
|
||||
if !m.matchAddr(ipx) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func prefixKeyFromIP(ip net.IP) (key [9]byte, ok bool) {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
key[0] = 4
|
||||
key[1] = ip4[0]
|
||||
key[2] = ip4[1]
|
||||
key[3] = ip4[2] // /24
|
||||
return key, true
|
||||
}
|
||||
if ip16 := ip.To16(); ip16 != nil {
|
||||
key[0] = 6
|
||||
key[1] = ip16[0]
|
||||
key[2] = ip16[1]
|
||||
key[3] = ip16[2]
|
||||
key[4] = ip16[3]
|
||||
key[5] = ip16[4]
|
||||
key[6] = ip16[5]
|
||||
key[7] = ip16[6]
|
||||
key[8] = ip16[7] // /64
|
||||
return key, true
|
||||
}
|
||||
return key, false // illegal
|
||||
}
|
||||
|
||||
// FilterIPs implements GeoIPMatcher.
|
||||
func (m *HeuristicGeoIPMatcher) FilterIPs(ips []net.IP) (matched []net.IP, unmatched []net.IP) {
|
||||
n := len(ips)
|
||||
if n == 0 {
|
||||
return []net.IP{}, []net.IP{}
|
||||
}
|
||||
|
||||
if n == 1 {
|
||||
ipx, ok := netipx.FromStdIP(ips[0])
|
||||
if !ok {
|
||||
return []net.IP{}, []net.IP{}
|
||||
}
|
||||
if m.matchAddr(ipx) {
|
||||
return ips, []net.IP{}
|
||||
}
|
||||
return []net.IP{}, ips
|
||||
}
|
||||
|
||||
heur4 := m.ipset.max4 <= 24
|
||||
heur6 := m.ipset.max6 <= 64
|
||||
if !heur4 && !heur6 {
|
||||
matched = make([]net.IP, 0, n)
|
||||
unmatched = make([]net.IP, 0, n)
|
||||
for _, ip := range ips {
|
||||
ipx, ok := netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
continue // illegal ip, ignore
|
||||
}
|
||||
if m.matchAddr(ipx) {
|
||||
matched = append(matched, ip)
|
||||
} else {
|
||||
unmatched = append(unmatched, ip)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
buckets := make(map[[9]byte]*ipBucket, n)
|
||||
precise := make([]net.IP, 0, n)
|
||||
|
||||
for _, ip := range ips {
|
||||
key, ok := prefixKeyFromIP(ip)
|
||||
if !ok {
|
||||
continue // illegal ip, ignore
|
||||
}
|
||||
|
||||
if (key[0] == 4 && !heur4) || (key[0] == 6 && !heur6) {
|
||||
precise = append(precise, ip)
|
||||
continue
|
||||
}
|
||||
|
||||
b, exists := buckets[key]
|
||||
if !exists {
|
||||
// build bucket
|
||||
ipx, ok := netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
continue // illegal ip, ignore
|
||||
}
|
||||
b = &ipBucket{
|
||||
rep: ipx,
|
||||
ips: make([]net.IP, 0, 4), // for dns answer
|
||||
}
|
||||
buckets[key] = b
|
||||
}
|
||||
b.ips = append(b.ips, ip)
|
||||
}
|
||||
|
||||
matched = make([]net.IP, 0, n)
|
||||
unmatched = make([]net.IP, 0, n)
|
||||
for _, b := range buckets {
|
||||
if m.matchAddr(b.rep) {
|
||||
matched = append(matched, b.ips...)
|
||||
} else {
|
||||
unmatched = append(unmatched, b.ips...)
|
||||
}
|
||||
}
|
||||
for _, ip := range precise {
|
||||
ipx, ok := netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
continue // illegal ip, ignore
|
||||
}
|
||||
if m.matchAddr(ipx) {
|
||||
matched = append(matched, ip)
|
||||
} else {
|
||||
unmatched = append(unmatched, ip)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ToggleReverse implements GeoIPMatcher.
|
||||
func (m *HeuristicGeoIPMatcher) ToggleReverse() {
|
||||
m.reverse = !m.reverse
|
||||
}
|
||||
|
||||
// SetReverse implements GeoIPMatcher.
|
||||
func (m *HeuristicGeoIPMatcher) SetReverse(reverse bool) {
|
||||
m.reverse = reverse
|
||||
}
|
||||
|
||||
type GeneralMultiGeoIPMatcher struct {
|
||||
matchers []GeoIPMatcher
|
||||
}
|
||||
|
||||
// Match implements GeoIPMatcher.
|
||||
func (mm *GeneralMultiGeoIPMatcher) Match(ip net.IP) bool {
|
||||
for _, m := range mm.matchers {
|
||||
if m.Match(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AnyMatch implements GeoIPMatcher.
|
||||
func (mm *GeneralMultiGeoIPMatcher) AnyMatch(ips []net.IP) bool {
|
||||
for _, m := range mm.matchers {
|
||||
if m.AnyMatch(ips) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Matches implements GeoIPMatcher.
|
||||
func (mm *GeneralMultiGeoIPMatcher) Matches(ips []net.IP) bool {
|
||||
for _, m := range mm.matchers {
|
||||
if m.Matches(ips) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// FilterIPs implements GeoIPMatcher.
|
||||
func (mm *GeneralMultiGeoIPMatcher) FilterIPs(ips []net.IP) (matched []net.IP, unmatched []net.IP) {
|
||||
matched = make([]net.IP, 0, len(ips))
|
||||
unmatched = ips
|
||||
for _, m := range mm.matchers {
|
||||
if len(unmatched) == 0 {
|
||||
break
|
||||
}
|
||||
var mtch []net.IP
|
||||
mtch, unmatched = m.FilterIPs(unmatched)
|
||||
if len(mtch) > 0 {
|
||||
matched = append(matched, mtch...)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ToggleReverse implements GeoIPMatcher.
|
||||
func (mm *GeneralMultiGeoIPMatcher) ToggleReverse() {
|
||||
for _, m := range mm.matchers {
|
||||
m.ToggleReverse()
|
||||
}
|
||||
}
|
||||
|
||||
// SetReverse implements GeoIPMatcher.
|
||||
func (mm *GeneralMultiGeoIPMatcher) SetReverse(reverse bool) {
|
||||
for _, m := range mm.matchers {
|
||||
m.SetReverse(reverse)
|
||||
}
|
||||
}
|
||||
|
||||
type HeuristicMultiGeoIPMatcher struct {
|
||||
matchers []*HeuristicGeoIPMatcher
|
||||
}
|
||||
|
||||
// Match implements GeoIPMatcher.
|
||||
func (mm *HeuristicMultiGeoIPMatcher) Match(ip net.IP) bool {
|
||||
ipx, ok := netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, m := range mm.matchers {
|
||||
if m.matchAddr(ipx) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AnyMatch implements GeoIPMatcher.
|
||||
func (mm *HeuristicMultiGeoIPMatcher) AnyMatch(ips []net.IP) bool {
|
||||
n := len(ips)
|
||||
if n == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if n == 1 {
|
||||
return mm.Match(ips[0])
|
||||
}
|
||||
|
||||
buckets := make(map[[9]byte]struct{}, n)
|
||||
for _, ip := range ips {
|
||||
var ipx netip.Addr
|
||||
state := uint8(0) // 0 = Not initialized, 1 = Initialized, 4 = IPv4 can be skipped, 6 = IPv6 can be skipped
|
||||
for _, m := range mm.matchers {
|
||||
heur4 := m.ipset.max4 <= 24
|
||||
heur6 := m.ipset.max6 <= 64
|
||||
|
||||
if state == 0 && (heur4 || heur6) {
|
||||
key, ok := prefixKeyFromIP(ip)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if _, exists := buckets[key]; exists {
|
||||
state = key[0]
|
||||
} else {
|
||||
buckets[key] = struct{}{}
|
||||
state = 1
|
||||
}
|
||||
}
|
||||
if (heur4 && state == 4) || (heur6 && state == 6) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !ipx.IsValid() {
|
||||
nipx, ok := netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
ipx = nipx
|
||||
}
|
||||
if m.matchAddr(ipx) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Matches implements GeoIPMatcher.
|
||||
func (mm *HeuristicMultiGeoIPMatcher) Matches(ips []net.IP) bool {
|
||||
n := len(ips)
|
||||
if n == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if n == 1 {
|
||||
return mm.Match(ips[0])
|
||||
}
|
||||
|
||||
var views ipViews
|
||||
for _, m := range mm.matchers {
|
||||
if !views.ensureForMatcher(m, ips) {
|
||||
return false
|
||||
}
|
||||
|
||||
matched := true
|
||||
if m.ipset.max4 <= 24 {
|
||||
for _, ipx := range views.buckets4 {
|
||||
if !m.matchAddr(ipx) {
|
||||
matched = false
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, ipx := range views.precise4 {
|
||||
if !m.matchAddr(ipx) {
|
||||
matched = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
|
||||
if m.ipset.max6 <= 64 {
|
||||
for _, ipx := range views.buckets6 {
|
||||
if !m.matchAddr(ipx) {
|
||||
matched = false
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, ipx := range views.precise6 {
|
||||
if !m.matchAddr(ipx) {
|
||||
matched = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if matched {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type ipViews struct {
|
||||
buckets4, buckets6 map[[9]byte]netip.Addr
|
||||
precise4, precise6 []netip.Addr
|
||||
}
|
||||
|
||||
func (v *ipViews) ensureForMatcher(m *HeuristicGeoIPMatcher, ips []net.IP) bool {
|
||||
needHeur4 := m.ipset.max4 <= 24 && v.buckets4 == nil
|
||||
needHeur6 := m.ipset.max6 <= 64 && v.buckets6 == nil
|
||||
needPrec4 := m.ipset.max4 > 24 && v.precise4 == nil
|
||||
needPrec6 := m.ipset.max6 > 64 && v.precise6 == nil
|
||||
|
||||
if !needHeur4 && !needHeur6 && !needPrec4 && !needPrec6 {
|
||||
return true
|
||||
}
|
||||
|
||||
if needHeur4 {
|
||||
v.buckets4 = make(map[[9]byte]netip.Addr, len(ips))
|
||||
}
|
||||
if needHeur6 {
|
||||
v.buckets6 = make(map[[9]byte]netip.Addr, len(ips))
|
||||
}
|
||||
if needPrec4 {
|
||||
v.precise4 = make([]netip.Addr, 0, len(ips))
|
||||
}
|
||||
if needPrec6 {
|
||||
v.precise6 = make([]netip.Addr, 0, len(ips))
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
key, ok := prefixKeyFromIP(ip)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
switch key[0] {
|
||||
case 4:
|
||||
var ipx netip.Addr
|
||||
if needHeur4 {
|
||||
if _, exists := v.buckets4[key]; !exists {
|
||||
ipx, ok = netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
v.buckets4[key] = ipx
|
||||
}
|
||||
}
|
||||
if needPrec4 {
|
||||
if !ipx.IsValid() {
|
||||
ipx, ok = netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
v.precise4 = append(v.precise4, ipx)
|
||||
}
|
||||
case 6:
|
||||
var ipx netip.Addr
|
||||
if needHeur6 {
|
||||
if _, exists := v.buckets6[key]; !exists {
|
||||
ipx, ok = netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
v.buckets6[key] = ipx
|
||||
}
|
||||
}
|
||||
if needPrec6 {
|
||||
if !ipx.IsValid() {
|
||||
ipx, ok = netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
v.precise6 = append(v.precise6, ipx)
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// FilterIPs implements GeoIPMatcher.
|
||||
func (mm *HeuristicMultiGeoIPMatcher) FilterIPs(ips []net.IP) (matched []net.IP, unmatched []net.IP) {
|
||||
n := len(ips)
|
||||
if n == 0 {
|
||||
return []net.IP{}, []net.IP{}
|
||||
}
|
||||
|
||||
if n == 1 {
|
||||
ipx, ok := netipx.FromStdIP(ips[0])
|
||||
if !ok {
|
||||
return []net.IP{}, []net.IP{}
|
||||
}
|
||||
for _, m := range mm.matchers {
|
||||
if m.matchAddr(ipx) {
|
||||
return ips, []net.IP{}
|
||||
}
|
||||
}
|
||||
return []net.IP{}, ips
|
||||
}
|
||||
|
||||
var views ipBucketViews
|
||||
|
||||
matched = make([]net.IP, 0, n)
|
||||
for _, m := range mm.matchers {
|
||||
views.ensureForMatcher(m, ips)
|
||||
|
||||
if m.ipset.max4 <= 24 {
|
||||
for key, b := range views.buckets4 {
|
||||
if b == nil {
|
||||
continue
|
||||
}
|
||||
if m.matchAddr(b.rep) {
|
||||
views.buckets4[key] = nil
|
||||
matched = append(matched, b.ips...)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for ipx, ip := range views.precise4 {
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
if m.matchAddr(ipx) {
|
||||
views.precise4[ipx] = nil
|
||||
matched = append(matched, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if m.ipset.max6 <= 64 {
|
||||
for key, b := range views.buckets6 {
|
||||
if b == nil {
|
||||
continue
|
||||
}
|
||||
if m.matchAddr(b.rep) {
|
||||
views.buckets6[key] = nil
|
||||
matched = append(matched, b.ips...)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for ipx, ip := range views.precise6 {
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
if m.matchAddr(ipx) {
|
||||
views.precise6[ipx] = nil
|
||||
matched = append(matched, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unmatched = make([]net.IP, 0, n-len(matched))
|
||||
if views.buckets4 != nil {
|
||||
for _, b := range views.buckets4 {
|
||||
if b == nil {
|
||||
continue
|
||||
}
|
||||
unmatched = append(unmatched, b.ips...)
|
||||
}
|
||||
}
|
||||
if views.precise4 != nil {
|
||||
for _, ip := range views.precise4 {
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
unmatched = append(unmatched, ip)
|
||||
}
|
||||
}
|
||||
if views.buckets6 != nil {
|
||||
for _, b := range views.buckets6 {
|
||||
if b == nil {
|
||||
continue
|
||||
}
|
||||
unmatched = append(unmatched, b.ips...)
|
||||
}
|
||||
}
|
||||
if views.precise6 != nil {
|
||||
for _, ip := range views.precise6 {
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
unmatched = append(unmatched, ip)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type ipBucketViews struct {
|
||||
buckets4, buckets6 map[[9]byte]*ipBucket
|
||||
precise4, precise6 map[netip.Addr]net.IP
|
||||
}
|
||||
|
||||
func (v *ipBucketViews) ensureForMatcher(m *HeuristicGeoIPMatcher, ips []net.IP) {
|
||||
needHeur4 := m.ipset.max4 <= 24 && v.buckets4 == nil
|
||||
needHeur6 := m.ipset.max6 <= 64 && v.buckets6 == nil
|
||||
needPrec4 := m.ipset.max4 > 24 && v.precise4 == nil
|
||||
needPrec6 := m.ipset.max6 > 64 && v.precise6 == nil
|
||||
|
||||
if !needHeur4 && !needHeur6 && !needPrec4 && !needPrec6 {
|
||||
return
|
||||
}
|
||||
|
||||
if needHeur4 {
|
||||
v.buckets4 = make(map[[9]byte]*ipBucket, len(ips))
|
||||
}
|
||||
if needHeur6 {
|
||||
v.buckets6 = make(map[[9]byte]*ipBucket, len(ips))
|
||||
}
|
||||
if needPrec4 {
|
||||
v.precise4 = make(map[netip.Addr]net.IP, len(ips))
|
||||
}
|
||||
if needPrec6 {
|
||||
v.precise6 = make(map[netip.Addr]net.IP, len(ips))
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
key, ok := prefixKeyFromIP(ip)
|
||||
if !ok {
|
||||
continue // illegal ip, ignore
|
||||
}
|
||||
|
||||
switch key[0] {
|
||||
case 4:
|
||||
var ipx netip.Addr
|
||||
if needHeur4 {
|
||||
b, exists := v.buckets4[key]
|
||||
if !exists {
|
||||
// build bucket
|
||||
ipx, ok = netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
continue // illegal ip, ignore
|
||||
}
|
||||
b = &ipBucket{
|
||||
rep: ipx,
|
||||
ips: make([]net.IP, 0, 4), // for dns answer
|
||||
}
|
||||
v.buckets4[key] = b
|
||||
}
|
||||
b.ips = append(b.ips, ip)
|
||||
}
|
||||
if needPrec4 {
|
||||
if !ipx.IsValid() {
|
||||
ipx, ok = netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
continue // illegal ip, ignore
|
||||
}
|
||||
}
|
||||
v.precise4[ipx] = ip
|
||||
}
|
||||
case 6:
|
||||
var ipx netip.Addr
|
||||
if needHeur6 {
|
||||
b, exists := v.buckets6[key]
|
||||
if !exists {
|
||||
// build bucket
|
||||
ipx, ok = netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
continue // illegal ip, ignore
|
||||
}
|
||||
b = &ipBucket{
|
||||
rep: ipx,
|
||||
ips: make([]net.IP, 0, 4), // for dns answer
|
||||
}
|
||||
v.buckets6[key] = b
|
||||
}
|
||||
b.ips = append(b.ips, ip)
|
||||
}
|
||||
if needPrec6 {
|
||||
if !ipx.IsValid() {
|
||||
ipx, ok = netipx.FromStdIP(ip)
|
||||
if !ok {
|
||||
continue // illegal ip, ignore
|
||||
}
|
||||
}
|
||||
v.precise6[ipx] = ip
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ToggleReverse implements GeoIPMatcher.
|
||||
func (mm *HeuristicMultiGeoIPMatcher) ToggleReverse() {
|
||||
for _, m := range mm.matchers {
|
||||
m.ToggleReverse()
|
||||
}
|
||||
}
|
||||
|
||||
// SetReverse implements GeoIPMatcher.
|
||||
func (mm *HeuristicMultiGeoIPMatcher) SetReverse(reverse bool) {
|
||||
for _, m := range mm.matchers {
|
||||
m.SetReverse(reverse)
|
||||
}
|
||||
}
|
||||
|
||||
type GeoIPSetFactory struct {
|
||||
sync.Mutex
|
||||
shared map[string]*GeoIPSet // TODO: cleanup
|
||||
}
|
||||
|
||||
var ipsetFactory = GeoIPSetFactory{shared: make(map[string]*GeoIPSet)}
|
||||
|
||||
func (f *GeoIPSetFactory) GetOrCreate(key string, cidrGroups [][]*CIDR) (*GeoIPSet, error) {
|
||||
f.Lock()
|
||||
defer f.Unlock()
|
||||
|
||||
if ipset := f.shared[key]; ipset != nil {
|
||||
return ipset, nil
|
||||
}
|
||||
|
||||
ipset, err := f.Create(cidrGroups...)
|
||||
if err == nil {
|
||||
f.shared[key] = ipset
|
||||
}
|
||||
return ipset, err
|
||||
}
|
||||
|
||||
func (f *GeoIPSetFactory) Create(cidrGroups ...[]*CIDR) (*GeoIPSet, error) {
|
||||
var ipv4Builder, ipv6Builder netipx.IPSetBuilder
|
||||
|
||||
for _, cidrGroup := range cidrGroups {
|
||||
for i, cidrEntry := range cidrGroup {
|
||||
cidrGroup[i] = nil
|
||||
ipBytes := cidrEntry.GetIp()
|
||||
prefixLen := int(cidrEntry.GetPrefix())
|
||||
|
||||
addr, ok := netip.AddrFromSlice(ipBytes)
|
||||
if !ok {
|
||||
errors.LogError(context.Background(), "ignore invalid IP byte slice: ", ipBytes)
|
||||
continue
|
||||
}
|
||||
|
||||
prefix := netip.PrefixFrom(addr, prefixLen)
|
||||
if !prefix.IsValid() {
|
||||
errors.LogError(context.Background(), "ignore created invalid prefix from addr ", addr, " and length ", prefixLen)
|
||||
continue
|
||||
}
|
||||
|
||||
if addr.Is4() {
|
||||
ipv4Builder.AddPrefix(prefix)
|
||||
} else if addr.Is6() {
|
||||
ipv6Builder.AddPrefix(prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ipv4, err := ipv4Builder.IPSet()
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to build IPv4 set").Base(err)
|
||||
}
|
||||
ipv6, err := ipv6Builder.IPSet()
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to build IPv6 set").Base(err)
|
||||
}
|
||||
|
||||
var max4, max6 int
|
||||
|
||||
for _, p := range ipv4.Prefixes() {
|
||||
if b := p.Bits(); b > max4 {
|
||||
max4 = b
|
||||
}
|
||||
}
|
||||
for _, p := range ipv6.Prefixes() {
|
||||
if b := p.Bits(); b > max6 {
|
||||
max6 = b
|
||||
}
|
||||
}
|
||||
|
||||
if max4 == 0 {
|
||||
max4 = 0xff
|
||||
}
|
||||
if max6 == 0 {
|
||||
max6 = 0xff
|
||||
}
|
||||
|
||||
return &GeoIPSet{ipv4: ipv4, ipv6: ipv6, max4: uint8(max4), max6: uint8(max6)}, nil
|
||||
}
|
||||
|
||||
func BuildOptimizedGeoIPMatcher(geoips ...*GeoIP) (GeoIPMatcher, error) {
|
||||
n := len(geoips)
|
||||
if n == 0 {
|
||||
return nil, errors.New("no geoip configs provided")
|
||||
}
|
||||
|
||||
var subs []*HeuristicGeoIPMatcher
|
||||
pos := make([]*GeoIP, 0, n)
|
||||
neg := make([]*GeoIP, 0, n/2)
|
||||
|
||||
for _, geoip := range geoips {
|
||||
if geoip == nil {
|
||||
return nil, errors.New("geoip entry is nil")
|
||||
}
|
||||
if geoip.CountryCode == "" {
|
||||
ipset, err := ipsetFactory.Create(geoip.Cidr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subs = append(subs, &HeuristicGeoIPMatcher{ipset: ipset, reverse: geoip.ReverseMatch})
|
||||
continue
|
||||
}
|
||||
if !geoip.ReverseMatch {
|
||||
pos = append(pos, geoip)
|
||||
} else {
|
||||
neg = append(neg, geoip)
|
||||
}
|
||||
}
|
||||
|
||||
buildIPSet := func(mergeables []*GeoIP) (*GeoIPSet, error) {
|
||||
n := len(mergeables)
|
||||
if n == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sort.Slice(mergeables, func(i, j int) bool {
|
||||
gi, gj := mergeables[i], mergeables[j]
|
||||
return gi.CountryCode < gj.CountryCode
|
||||
})
|
||||
|
||||
var sb strings.Builder
|
||||
sb.Grow(n * 3) // xx,
|
||||
cidrGroups := make([][]*CIDR, 0, n)
|
||||
var last *GeoIP
|
||||
for i, geoip := range mergeables {
|
||||
if i == 0 || (geoip.CountryCode != last.CountryCode) {
|
||||
last = geoip
|
||||
sb.WriteString(geoip.CountryCode)
|
||||
sb.WriteString(",")
|
||||
cidrGroups = append(cidrGroups, geoip.Cidr)
|
||||
}
|
||||
}
|
||||
|
||||
return ipsetFactory.GetOrCreate(sb.String(), cidrGroups)
|
||||
}
|
||||
|
||||
ipset, err := buildIPSet(pos)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ipset != nil {
|
||||
subs = append(subs, &HeuristicGeoIPMatcher{ipset: ipset, reverse: false})
|
||||
}
|
||||
|
||||
ipset, err = buildIPSet(neg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ipset != nil {
|
||||
subs = append(subs, &HeuristicGeoIPMatcher{ipset: ipset, reverse: true})
|
||||
}
|
||||
|
||||
switch len(subs) {
|
||||
case 0:
|
||||
return nil, errors.New("no valid geoip matcher")
|
||||
case 1:
|
||||
return subs[0], nil
|
||||
default:
|
||||
return &HeuristicMultiGeoIPMatcher{matchers: subs}, nil
|
||||
}
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
package router_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/xtls/xray-core/app/router"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/platform"
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func getAssetPath(file string) (string, error) {
|
||||
path := platform.GetAssetLocation(file)
|
||||
_, err := os.Stat(path)
|
||||
if os.IsNotExist(err) {
|
||||
path := filepath.Join("..", "..", "resources", file)
|
||||
_, err := os.Stat(path)
|
||||
if os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("can't find %s in standard asset locations or {project_root}/resources", file)
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can't stat %s: %v", path, err)
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can't stat %s: %v", path, err)
|
||||
}
|
||||
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func TestGeoIPMatcher(t *testing.T) {
|
||||
cidrList := []*router.CIDR{
|
||||
{Ip: []byte{0, 0, 0, 0}, Prefix: 8},
|
||||
{Ip: []byte{10, 0, 0, 0}, Prefix: 8},
|
||||
{Ip: []byte{100, 64, 0, 0}, Prefix: 10},
|
||||
{Ip: []byte{127, 0, 0, 0}, Prefix: 8},
|
||||
{Ip: []byte{169, 254, 0, 0}, Prefix: 16},
|
||||
{Ip: []byte{172, 16, 0, 0}, Prefix: 12},
|
||||
{Ip: []byte{192, 0, 0, 0}, Prefix: 24},
|
||||
{Ip: []byte{192, 0, 2, 0}, Prefix: 24},
|
||||
{Ip: []byte{192, 168, 0, 0}, Prefix: 16},
|
||||
{Ip: []byte{192, 18, 0, 0}, Prefix: 15},
|
||||
{Ip: []byte{198, 51, 100, 0}, Prefix: 24},
|
||||
{Ip: []byte{203, 0, 113, 0}, Prefix: 24},
|
||||
{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
|
||||
{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
|
||||
}
|
||||
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: cidrList,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
testCases := []struct {
|
||||
Input string
|
||||
Output bool
|
||||
}{
|
||||
{
|
||||
Input: "192.168.1.1",
|
||||
Output: true,
|
||||
},
|
||||
{
|
||||
Input: "192.0.0.0",
|
||||
Output: true,
|
||||
},
|
||||
{
|
||||
Input: "192.0.1.0",
|
||||
Output: false,
|
||||
},
|
||||
{
|
||||
Input: "0.1.0.0",
|
||||
Output: true,
|
||||
},
|
||||
{
|
||||
Input: "1.0.0.1",
|
||||
Output: false,
|
||||
},
|
||||
{
|
||||
Input: "8.8.8.7",
|
||||
Output: false,
|
||||
},
|
||||
{
|
||||
Input: "8.8.8.8",
|
||||
Output: true,
|
||||
},
|
||||
{
|
||||
Input: "2001:cdba::3257:9652",
|
||||
Output: false,
|
||||
},
|
||||
{
|
||||
Input: "91.108.255.254",
|
||||
Output: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
ip := net.ParseAddress(testCase.Input).IP()
|
||||
actual := matcher.Match(ip)
|
||||
if actual != testCase.Output {
|
||||
t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeoIPMatcherRegression(t *testing.T) {
|
||||
cidrList := []*router.CIDR{
|
||||
{Ip: []byte{98, 108, 20, 0}, Prefix: 22},
|
||||
{Ip: []byte{98, 108, 20, 0}, Prefix: 23},
|
||||
}
|
||||
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: cidrList,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
testCases := []struct {
|
||||
Input string
|
||||
Output bool
|
||||
}{
|
||||
{
|
||||
Input: "98.108.22.11",
|
||||
Output: true,
|
||||
},
|
||||
{
|
||||
Input: "98.108.25.0",
|
||||
Output: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
ip := net.ParseAddress(testCase.Input).IP()
|
||||
actual := matcher.Match(ip)
|
||||
if actual != testCase.Output {
|
||||
t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeoIPReverseMatcher(t *testing.T) {
|
||||
cidrList := []*router.CIDR{
|
||||
{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
|
||||
{Ip: []byte{91, 108, 4, 0}, Prefix: 16},
|
||||
}
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: cidrList,
|
||||
})
|
||||
common.Must(err)
|
||||
matcher.SetReverse(true) // Reverse match
|
||||
|
||||
testCases := []struct {
|
||||
Input string
|
||||
Output bool
|
||||
}{
|
||||
{
|
||||
Input: "8.8.8.8",
|
||||
Output: false,
|
||||
},
|
||||
{
|
||||
Input: "2001:cdba::3257:9652",
|
||||
Output: true,
|
||||
},
|
||||
{
|
||||
Input: "91.108.255.254",
|
||||
Output: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
ip := net.ParseAddress(testCase.Input).IP()
|
||||
actual := matcher.Match(ip)
|
||||
if actual != testCase.Output {
|
||||
t.Error("expect input", testCase.Input, "to be", testCase.Output, ", but actually", actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeoIPMatcher4CN(t *testing.T) {
|
||||
ips, err := loadGeoIP("CN")
|
||||
common.Must(err)
|
||||
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: ips,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
if matcher.Match([]byte{8, 8, 8, 8}) {
|
||||
t.Error("expect CN geoip doesn't contain 8.8.8.8, but actually does")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeoIPMatcher6US(t *testing.T) {
|
||||
ips, err := loadGeoIP("US")
|
||||
common.Must(err)
|
||||
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: ips,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
if !matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP()) {
|
||||
t.Error("expect US geoip contain 2001:4860:4860::8888, but actually not")
|
||||
}
|
||||
}
|
||||
|
||||
func loadGeoIP(country string) ([]*router.CIDR, error) {
|
||||
path, err := getAssetPath("geoip.dat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
geoipBytes, err := filesystem.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var geoipList router.GeoIPList
|
||||
if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, geoip := range geoipList.Entry {
|
||||
if geoip.CountryCode == country {
|
||||
return geoip.Cidr, nil
|
||||
}
|
||||
}
|
||||
|
||||
panic("country not found: " + country)
|
||||
}
|
||||
|
||||
func BenchmarkGeoIPMatcher4CN(b *testing.B) {
|
||||
ips, err := loadGeoIP("CN")
|
||||
common.Must(err)
|
||||
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: ips,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = matcher.Match([]byte{8, 8, 8, 8})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGeoIPMatcher6US(b *testing.B) {
|
||||
ips, err := loadGeoIP("US")
|
||||
common.Must(err)
|
||||
|
||||
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
|
||||
Cidr: ips,
|
||||
})
|
||||
common.Must(err)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP())
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
package router_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/xtls/xray-core/app/router"
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
)
|
||||
|
||||
func TestDomainMatcherSerialization(t *testing.T) {
|
||||
|
||||
domains := []*router.Domain{
|
||||
{Type: router.Domain_Domain, Value: "google.com"},
|
||||
{Type: router.Domain_Domain, Value: "v2ray.com"},
|
||||
{Type: router.Domain_Full, Value: "full.example.com"},
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := router.SerializeDomainMatcher(domains, &buf); err != nil {
|
||||
t.Fatalf("Serialize failed: %v", err)
|
||||
}
|
||||
|
||||
matcher, err := router.NewDomainMatcherFromBuffer(buf.Bytes())
|
||||
if err != nil {
|
||||
t.Fatalf("Deserialize failed: %v", err)
|
||||
}
|
||||
|
||||
dMatcher := &router.DomainMatcher{
|
||||
Matchers: matcher,
|
||||
}
|
||||
testCases := []struct {
|
||||
Input string
|
||||
Match bool
|
||||
}{
|
||||
{"google.com", true},
|
||||
{"maps.google.com", true},
|
||||
{"v2ray.com", true},
|
||||
{"full.example.com", true},
|
||||
|
||||
{"example.com", false},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if res := dMatcher.ApplyDomain(tc.Input); res != tc.Match {
|
||||
t.Errorf("Match(%s) = %v, want %v", tc.Input, res, tc.Match)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeoSiteSerialization(t *testing.T) {
|
||||
sites := []*router.GeoSite{
|
||||
{
|
||||
CountryCode: "CN",
|
||||
Domain: []*router.Domain{
|
||||
{Type: router.Domain_Domain, Value: "baidu.cn"},
|
||||
{Type: router.Domain_Domain, Value: "qq.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
CountryCode: "US",
|
||||
Domain: []*router.Domain{
|
||||
{Type: router.Domain_Domain, Value: "google.com"},
|
||||
{Type: router.Domain_Domain, Value: "facebook.com"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := router.SerializeGeoSiteList(sites, nil, nil, &buf); err != nil {
|
||||
t.Fatalf("SerializeGeoSiteList failed: %v", err)
|
||||
}
|
||||
|
||||
tmp := t.TempDir()
|
||||
path := filepath.Join(tmp, "matcher.cache")
|
||||
|
||||
f, err := os.Create(path)
|
||||
require.NoError(t, err)
|
||||
_, err = f.Write(buf.Bytes())
|
||||
require.NoError(t, err)
|
||||
f.Close()
|
||||
|
||||
f, err = os.Open(path)
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
|
||||
require.NoError(t, err)
|
||||
data, _ := filesystem.ReadFile(path)
|
||||
|
||||
// cn
|
||||
gp, err := router.LoadGeoSiteMatcher(bytes.NewReader(data), "CN")
|
||||
if err != nil {
|
||||
t.Fatalf("LoadGeoSiteMatcher(CN) failed: %v", err)
|
||||
}
|
||||
|
||||
cnMatcher := &router.DomainMatcher{
|
||||
Matchers: gp,
|
||||
}
|
||||
|
||||
if !cnMatcher.ApplyDomain("baidu.cn") {
|
||||
t.Error("CN matcher should match baidu.cn")
|
||||
}
|
||||
if cnMatcher.ApplyDomain("google.com") {
|
||||
t.Error("CN matcher should NOT match google.com")
|
||||
}
|
||||
|
||||
// us
|
||||
gp, err = router.LoadGeoSiteMatcher(bytes.NewReader(data), "US")
|
||||
if err != nil {
|
||||
t.Fatalf("LoadGeoSiteMatcher(US) failed: %v", err)
|
||||
}
|
||||
|
||||
usMatcher := &router.DomainMatcher{
|
||||
Matchers: gp,
|
||||
}
|
||||
if !usMatcher.ApplyDomain("google.com") {
|
||||
t.Error("US matcher should match google.com")
|
||||
}
|
||||
if usMatcher.ApplyDomain("baidu.cn") {
|
||||
t.Error("US matcher should NOT match baidu.cn")
|
||||
}
|
||||
|
||||
// unknown
|
||||
_, err = router.LoadGeoSiteMatcher(bytes.NewReader(data), "unknown")
|
||||
if err == nil {
|
||||
t.Error("LoadGeoSiteMatcher(unknown) should fail")
|
||||
}
|
||||
}
|
||||
func TestGeoSiteSerializationWithDeps(t *testing.T) {
|
||||
sites := []*router.GeoSite{
|
||||
{
|
||||
CountryCode: "geosite:cn",
|
||||
Domain: []*router.Domain{
|
||||
{Type: router.Domain_Domain, Value: "baidu.cn"},
|
||||
},
|
||||
},
|
||||
{
|
||||
CountryCode: "geosite:google@cn",
|
||||
Domain: []*router.Domain{
|
||||
{Type: router.Domain_Domain, Value: "google.cn"},
|
||||
},
|
||||
},
|
||||
{
|
||||
CountryCode: "rule-1",
|
||||
Domain: []*router.Domain{
|
||||
{Type: router.Domain_Domain, Value: "google.com"},
|
||||
},
|
||||
},
|
||||
}
|
||||
deps := map[string][]string{
|
||||
"rule-1": {"geosite:cn", "geosite:google@cn"},
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err := router.SerializeGeoSiteList(sites, deps, nil, &buf)
|
||||
require.NoError(t, err)
|
||||
|
||||
matcher, err := router.LoadGeoSiteMatcher(bytes.NewReader(buf.Bytes()), "rule-1")
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, matcher.Match("google.com") != nil)
|
||||
require.True(t, matcher.Match("baidu.cn") != nil)
|
||||
require.True(t, matcher.Match("google.cn") != nil)
|
||||
}
|
||||
@@ -1,20 +1,19 @@
|
||||
package router_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
. "github.com/xtls/xray-core/app/router"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/geodata"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
"github.com/xtls/xray-core/common/protocol"
|
||||
"github.com/xtls/xray-core/common/protocol/http"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
routing_session "github.com/xtls/xray-core/features/routing/session"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func withBackground() routing.Context {
|
||||
@@ -45,18 +44,15 @@ func TestRoutingRule(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
rule: &RoutingRule{
|
||||
Domain: []*Domain{
|
||||
Domain: []*geodata.DomainRule{
|
||||
{
|
||||
Value: "example.com",
|
||||
Type: Domain_Plain,
|
||||
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Substr, Value: "example.com"}},
|
||||
},
|
||||
{
|
||||
Value: "google.com",
|
||||
Type: Domain_Domain,
|
||||
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "google.com"}},
|
||||
},
|
||||
{
|
||||
Value: "^facebook\\.com$",
|
||||
Type: Domain_Regex,
|
||||
Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Regex, Value: "^facebook\\.com$"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -93,18 +89,26 @@ func TestRoutingRule(t *testing.T) {
|
||||
},
|
||||
{
|
||||
rule: &RoutingRule{
|
||||
Geoip: []*GeoIP{
|
||||
Ip: []*geodata.IPRule{
|
||||
{
|
||||
Cidr: []*CIDR{
|
||||
{
|
||||
Value: &geodata.IPRule_Custom{
|
||||
Custom: &geodata.CIDR{
|
||||
Ip: []byte{8, 8, 8, 8},
|
||||
Prefix: 32,
|
||||
},
|
||||
{
|
||||
},
|
||||
},
|
||||
{
|
||||
Value: &geodata.IPRule_Custom{
|
||||
Custom: &geodata.CIDR{
|
||||
Ip: []byte{8, 8, 8, 8},
|
||||
Prefix: 32,
|
||||
},
|
||||
{
|
||||
},
|
||||
},
|
||||
{
|
||||
Value: &geodata.IPRule_Custom{
|
||||
Custom: &geodata.CIDR{
|
||||
Ip: net.ParseAddress("2001:0db8:85a3:0000:0000:8a2e:0370:7334").IP(),
|
||||
Prefix: 128,
|
||||
},
|
||||
@@ -133,10 +137,10 @@ func TestRoutingRule(t *testing.T) {
|
||||
},
|
||||
{
|
||||
rule: &RoutingRule{
|
||||
SourceGeoip: []*GeoIP{
|
||||
SourceIp: []*geodata.IPRule{
|
||||
{
|
||||
Cidr: []*CIDR{
|
||||
{
|
||||
Value: &geodata.IPRule_Custom{
|
||||
Custom: &geodata.CIDR{
|
||||
Ip: []byte{192, 168, 0, 0},
|
||||
Prefix: 16,
|
||||
},
|
||||
@@ -300,35 +304,12 @@ func TestRoutingRule(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func loadGeoSite(country string) ([]*Domain, error) {
|
||||
path, err := getAssetPath("geosite.dat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
geositeBytes, err := filesystem.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var geositeList GeoSiteList
|
||||
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, site := range geositeList.Entry {
|
||||
if site.CountryCode == country {
|
||||
return site.Domain, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("country not found: " + country)
|
||||
}
|
||||
|
||||
func TestChinaSites(t *testing.T) {
|
||||
domains, err := loadGeoSite("CN")
|
||||
t.Setenv("xray.location.asset", filepath.Join("..", "..", "resources"))
|
||||
rules, err := geodata.ParseDomainRules([]string{"geosite:cn"}, geodata.Domain_Substr)
|
||||
common.Must(err)
|
||||
|
||||
acMatcher, err := NewMphMatcherGroup(domains)
|
||||
matcher, err := NewDomainMatcher(rules)
|
||||
common.Must(err)
|
||||
|
||||
type TestCase struct {
|
||||
@@ -359,18 +340,19 @@ func TestChinaSites(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
r := acMatcher.ApplyDomain(testCase.Domain)
|
||||
r := matcher.ApplyDomain(testCase.Domain)
|
||||
if r != testCase.Output {
|
||||
t.Error("ACDomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r)
|
||||
t.Error("DomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMphDomainMatcher(b *testing.B) {
|
||||
domains, err := loadGeoSite("CN")
|
||||
b.Setenv("xray.location.asset", filepath.Join("..", "..", "resources"))
|
||||
rules, err := geodata.ParseDomainRules([]string{"geosite:cn"}, geodata.Domain_Substr)
|
||||
common.Must(err)
|
||||
|
||||
matcher, err := NewMphMatcherGroup(domains)
|
||||
matcher, err := NewDomainMatcher(rules)
|
||||
common.Must(err)
|
||||
|
||||
type TestCase struct {
|
||||
@@ -409,45 +391,11 @@ func BenchmarkMphDomainMatcher(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkMultiGeoIPMatcher(b *testing.B) {
|
||||
var geoips []*GeoIP
|
||||
b.Setenv("xray.location.asset", filepath.Join("..", "..", "resources"))
|
||||
rules, err := geodata.ParseIPRules([]string{"geoip:cn", "geoip:jp", "geoip:ca", "geoip:us"})
|
||||
common.Must(err)
|
||||
|
||||
{
|
||||
ips, err := loadGeoIP("CN")
|
||||
common.Must(err)
|
||||
geoips = append(geoips, &GeoIP{
|
||||
CountryCode: "CN",
|
||||
Cidr: ips,
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
ips, err := loadGeoIP("JP")
|
||||
common.Must(err)
|
||||
geoips = append(geoips, &GeoIP{
|
||||
CountryCode: "JP",
|
||||
Cidr: ips,
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
ips, err := loadGeoIP("CA")
|
||||
common.Must(err)
|
||||
geoips = append(geoips, &GeoIP{
|
||||
CountryCode: "CA",
|
||||
Cidr: ips,
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
ips, err := loadGeoIP("US")
|
||||
common.Must(err)
|
||||
geoips = append(geoips, &GeoIP{
|
||||
CountryCode: "US",
|
||||
Cidr: ips,
|
||||
})
|
||||
}
|
||||
|
||||
matcher, err := NewIPMatcher(geoips, MatcherAsType_Target)
|
||||
matcher, err := NewIPMatcher(rules, MatcherAsType_Target)
|
||||
common.Must(err)
|
||||
|
||||
ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.ParseAddress("8.8.8.8"), 80)})
|
||||
|
||||
@@ -3,12 +3,9 @@ package router
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/platform"
|
||||
"github.com/xtls/xray-core/common/platform/filesystem"
|
||||
"github.com/xtls/xray-core/features/outbound"
|
||||
"github.com/xtls/xray-core/features/routing"
|
||||
)
|
||||
@@ -76,60 +73,37 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
|
||||
conds.Add(&AttributeMatcher{configuredKeys})
|
||||
}
|
||||
|
||||
if len(rr.Geoip) > 0 {
|
||||
cond, err := NewIPMatcher(rr.Geoip, MatcherAsType_Target)
|
||||
if len(rr.Ip) > 0 {
|
||||
cond, err := NewIPMatcher(rr.Ip, MatcherAsType_Target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conds.Add(cond)
|
||||
rr.Geoip = nil
|
||||
runtime.GC()
|
||||
}
|
||||
|
||||
if len(rr.SourceGeoip) > 0 {
|
||||
cond, err := NewIPMatcher(rr.SourceGeoip, MatcherAsType_Source)
|
||||
if len(rr.SourceIp) > 0 {
|
||||
cond, err := NewIPMatcher(rr.SourceIp, MatcherAsType_Source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conds.Add(cond)
|
||||
rr.SourceGeoip = nil
|
||||
runtime.GC()
|
||||
}
|
||||
|
||||
if len(rr.LocalGeoip) > 0 {
|
||||
cond, err := NewIPMatcher(rr.LocalGeoip, MatcherAsType_Local)
|
||||
if len(rr.LocalIp) > 0 {
|
||||
cond, err := NewIPMatcher(rr.LocalIp, MatcherAsType_Local)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conds.Add(cond)
|
||||
errors.LogWarning(context.Background(), "Due to some limitations, in UDP connections, localIP is always equal to listen interface IP, so \"localIP\" rule condition does not work properly on UDP inbound connections that listen on all interfaces")
|
||||
rr.LocalGeoip = nil
|
||||
runtime.GC()
|
||||
}
|
||||
|
||||
if len(rr.Domain) > 0 {
|
||||
var matcher *DomainMatcher
|
||||
var err error
|
||||
// Check if domain matcher cache is provided via environment
|
||||
domainMatcherPath := platform.NewEnvFlag(platform.MphCachePath).GetValue(func() string { return "" })
|
||||
|
||||
if domainMatcherPath != "" {
|
||||
matcher, err = GetDomainMatcherWithRuleTag(domainMatcherPath, rr.RuleTag)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to build domain condition from cached MphDomainMatcher").Base(err)
|
||||
}
|
||||
errors.LogDebug(context.Background(), "MphDomainMatcher loaded from cache for ", rr.RuleTag, " rule tag)")
|
||||
|
||||
} else {
|
||||
matcher, err = NewMphMatcherGroup(rr.Domain)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to build domain condition with MphDomainMatcher").Base(err)
|
||||
}
|
||||
errors.LogDebug(context.Background(), "MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)")
|
||||
cond, err := NewDomainMatcher(rr.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conds.Add(matcher)
|
||||
rr.Domain = nil
|
||||
runtime.GC()
|
||||
conds.Add(cond)
|
||||
}
|
||||
|
||||
if len(rr.Process) > 0 {
|
||||
@@ -189,20 +163,3 @@ func (br *BalancingRule) Build(ohm outbound.Manager, dispatcher routing.Dispatch
|
||||
return nil, errors.New("unrecognized balancer type")
|
||||
}
|
||||
}
|
||||
|
||||
func GetDomainMatcherWithRuleTag(domainMatcherPath string, ruleTag string) (*DomainMatcher, error) {
|
||||
f, err := filesystem.NewFileReader(domainMatcherPath)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to load file: ", domainMatcherPath).Base(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
g, err := LoadGeoSiteMatcher(f, ruleTag)
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to load file:", domainMatcherPath).Base(err)
|
||||
}
|
||||
return &DomainMatcher{
|
||||
Matchers: g,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
geodata "github.com/xtls/xray-core/common/geodata"
|
||||
net "github.com/xtls/xray-core/common/net"
|
||||
serial "github.com/xtls/xray-core/common/serial"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
@@ -23,63 +24,6 @@ const (
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// Type of domain value.
|
||||
type Domain_Type int32
|
||||
|
||||
const (
|
||||
// The value is used as is.
|
||||
Domain_Plain Domain_Type = 0
|
||||
// The value is used as a regular expression.
|
||||
Domain_Regex Domain_Type = 1
|
||||
// The value is a root domain.
|
||||
Domain_Domain Domain_Type = 2
|
||||
// The value is a domain.
|
||||
Domain_Full Domain_Type = 3
|
||||
)
|
||||
|
||||
// Enum value maps for Domain_Type.
|
||||
var (
|
||||
Domain_Type_name = map[int32]string{
|
||||
0: "Plain",
|
||||
1: "Regex",
|
||||
2: "Domain",
|
||||
3: "Full",
|
||||
}
|
||||
Domain_Type_value = map[string]int32{
|
||||
"Plain": 0,
|
||||
"Regex": 1,
|
||||
"Domain": 2,
|
||||
"Full": 3,
|
||||
}
|
||||
)
|
||||
|
||||
func (x Domain_Type) Enum() *Domain_Type {
|
||||
p := new(Domain_Type)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x Domain_Type) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (Domain_Type) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_app_router_config_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (Domain_Type) Type() protoreflect.EnumType {
|
||||
return &file_app_router_config_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x Domain_Type) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Domain_Type.Descriptor instead.
|
||||
func (Domain_Type) EnumDescriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{0, 0}
|
||||
}
|
||||
|
||||
type Config_DomainStrategy int32
|
||||
|
||||
const (
|
||||
@@ -116,11 +60,11 @@ func (x Config_DomainStrategy) String() string {
|
||||
}
|
||||
|
||||
func (Config_DomainStrategy) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_app_router_config_proto_enumTypes[1].Descriptor()
|
||||
return file_app_router_config_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (Config_DomainStrategy) Type() protoreflect.EnumType {
|
||||
return &file_app_router_config_proto_enumTypes[1]
|
||||
return &file_app_router_config_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x Config_DomainStrategy) Number() protoreflect.EnumNumber {
|
||||
@@ -129,326 +73,7 @@ func (x Config_DomainStrategy) Number() protoreflect.EnumNumber {
|
||||
|
||||
// Deprecated: Use Config_DomainStrategy.Descriptor instead.
|
||||
func (Config_DomainStrategy) EnumDescriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{11, 0}
|
||||
}
|
||||
|
||||
// Domain for routing decision.
|
||||
type Domain struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// Domain matching type.
|
||||
Type Domain_Type `protobuf:"varint,1,opt,name=type,proto3,enum=xray.app.router.Domain_Type" json:"type,omitempty"`
|
||||
// Domain value.
|
||||
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
|
||||
// Attributes of this domain. May be used for filtering.
|
||||
Attribute []*Domain_Attribute `protobuf:"bytes,3,rep,name=attribute,proto3" json:"attribute,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Domain) Reset() {
|
||||
*x = Domain{}
|
||||
mi := &file_app_router_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Domain) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Domain) ProtoMessage() {}
|
||||
|
||||
func (x *Domain) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Domain.ProtoReflect.Descriptor instead.
|
||||
func (*Domain) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Domain) GetType() Domain_Type {
|
||||
if x != nil {
|
||||
return x.Type
|
||||
}
|
||||
return Domain_Plain
|
||||
}
|
||||
|
||||
func (x *Domain) GetValue() string {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Domain) GetAttribute() []*Domain_Attribute {
|
||||
if x != nil {
|
||||
return x.Attribute
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IP for routing decision, in CIDR form.
|
||||
type CIDR struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// IP address, should be either 4 or 16 bytes.
|
||||
Ip []byte `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"`
|
||||
// Number of leading ones in the network mask.
|
||||
Prefix uint32 `protobuf:"varint,2,opt,name=prefix,proto3" json:"prefix,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *CIDR) Reset() {
|
||||
*x = CIDR{}
|
||||
mi := &file_app_router_config_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CIDR) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CIDR) ProtoMessage() {}
|
||||
|
||||
func (x *CIDR) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CIDR.ProtoReflect.Descriptor instead.
|
||||
func (*CIDR) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *CIDR) GetIp() []byte {
|
||||
if x != nil {
|
||||
return x.Ip
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CIDR) GetPrefix() uint32 {
|
||||
if x != nil {
|
||||
return x.Prefix
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type GeoIP struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
CountryCode string `protobuf:"bytes,1,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"`
|
||||
Cidr []*CIDR `protobuf:"bytes,2,rep,name=cidr,proto3" json:"cidr,omitempty"`
|
||||
ReverseMatch bool `protobuf:"varint,3,opt,name=reverse_match,json=reverseMatch,proto3" json:"reverse_match,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GeoIP) Reset() {
|
||||
*x = GeoIP{}
|
||||
mi := &file_app_router_config_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GeoIP) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GeoIP) ProtoMessage() {}
|
||||
|
||||
func (x *GeoIP) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GeoIP.ProtoReflect.Descriptor instead.
|
||||
func (*GeoIP) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *GeoIP) GetCountryCode() string {
|
||||
if x != nil {
|
||||
return x.CountryCode
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GeoIP) GetCidr() []*CIDR {
|
||||
if x != nil {
|
||||
return x.Cidr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GeoIP) GetReverseMatch() bool {
|
||||
if x != nil {
|
||||
return x.ReverseMatch
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type GeoIPList struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Entry []*GeoIP `protobuf:"bytes,1,rep,name=entry,proto3" json:"entry,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GeoIPList) Reset() {
|
||||
*x = GeoIPList{}
|
||||
mi := &file_app_router_config_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GeoIPList) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GeoIPList) ProtoMessage() {}
|
||||
|
||||
func (x *GeoIPList) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GeoIPList.ProtoReflect.Descriptor instead.
|
||||
func (*GeoIPList) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *GeoIPList) GetEntry() []*GeoIP {
|
||||
if x != nil {
|
||||
return x.Entry
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GeoSite struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
CountryCode string `protobuf:"bytes,1,opt,name=country_code,json=countryCode,proto3" json:"country_code,omitempty"`
|
||||
Domain []*Domain `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GeoSite) Reset() {
|
||||
*x = GeoSite{}
|
||||
mi := &file_app_router_config_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GeoSite) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GeoSite) ProtoMessage() {}
|
||||
|
||||
func (x *GeoSite) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GeoSite.ProtoReflect.Descriptor instead.
|
||||
func (*GeoSite) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *GeoSite) GetCountryCode() string {
|
||||
if x != nil {
|
||||
return x.CountryCode
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GeoSite) GetDomain() []*Domain {
|
||||
if x != nil {
|
||||
return x.Domain
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GeoSiteList struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Entry []*GeoSite `protobuf:"bytes,1,rep,name=entry,proto3" json:"entry,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GeoSiteList) Reset() {
|
||||
*x = GeoSiteList{}
|
||||
mi := &file_app_router_config_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GeoSiteList) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GeoSiteList) ProtoMessage() {}
|
||||
|
||||
func (x *GeoSiteList) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GeoSiteList.ProtoReflect.Descriptor instead.
|
||||
func (*GeoSiteList) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *GeoSiteList) GetEntry() []*GeoSite {
|
||||
if x != nil {
|
||||
return x.Entry
|
||||
}
|
||||
return nil
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{5, 0}
|
||||
}
|
||||
|
||||
type RoutingRule struct {
|
||||
@@ -460,37 +85,35 @@ type RoutingRule struct {
|
||||
TargetTag isRoutingRule_TargetTag `protobuf_oneof:"target_tag"`
|
||||
RuleTag string `protobuf:"bytes,19,opt,name=rule_tag,json=ruleTag,proto3" json:"rule_tag,omitempty"`
|
||||
// List of domains for target domain matching.
|
||||
Domain []*Domain `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"`
|
||||
// List of GeoIPs for target IP address matching. If this entry exists, the
|
||||
// cidr above will have no effect. GeoIP fields with the same country code are
|
||||
// supposed to contain exactly same content. They will be merged during
|
||||
// runtime. For customized GeoIPs, please leave country code empty.
|
||||
Geoip []*GeoIP `protobuf:"bytes,10,rep,name=geoip,proto3" json:"geoip,omitempty"`
|
||||
// List of ports.
|
||||
Domain []*geodata.DomainRule `protobuf:"bytes,2,rep,name=domain,proto3" json:"domain,omitempty"`
|
||||
// List of IPs for target IP address matching.
|
||||
Ip []*geodata.IPRule `protobuf:"bytes,10,rep,name=ip,proto3" json:"ip,omitempty"`
|
||||
// List of ports for target port matching.
|
||||
PortList *net.PortList `protobuf:"bytes,14,opt,name=port_list,json=portList,proto3" json:"port_list,omitempty"`
|
||||
// List of networks for matching.
|
||||
Networks []net.Network `protobuf:"varint,13,rep,packed,name=networks,proto3,enum=xray.common.net.Network" json:"networks,omitempty"`
|
||||
// List of GeoIPs for source IP address matching. If this entry exists, the
|
||||
// source_cidr above will have no effect.
|
||||
SourceGeoip []*GeoIP `protobuf:"bytes,11,rep,name=source_geoip,json=sourceGeoip,proto3" json:"source_geoip,omitempty"`
|
||||
// List of IPs for source IP address matching.
|
||||
SourceIp []*geodata.IPRule `protobuf:"bytes,11,rep,name=source_ip,json=sourceIp,proto3" json:"source_ip,omitempty"`
|
||||
// List of ports for source port matching.
|
||||
SourcePortList *net.PortList `protobuf:"bytes,16,opt,name=source_port_list,json=sourcePortList,proto3" json:"source_port_list,omitempty"`
|
||||
UserEmail []string `protobuf:"bytes,7,rep,name=user_email,json=userEmail,proto3" json:"user_email,omitempty"`
|
||||
InboundTag []string `protobuf:"bytes,8,rep,name=inbound_tag,json=inboundTag,proto3" json:"inbound_tag,omitempty"`
|
||||
Protocol []string `protobuf:"bytes,9,rep,name=protocol,proto3" json:"protocol,omitempty"`
|
||||
Attributes map[string]string `protobuf:"bytes,15,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
LocalGeoip []*GeoIP `protobuf:"bytes,17,rep,name=local_geoip,json=localGeoip,proto3" json:"local_geoip,omitempty"`
|
||||
LocalPortList *net.PortList `protobuf:"bytes,18,opt,name=local_port_list,json=localPortList,proto3" json:"local_port_list,omitempty"`
|
||||
VlessRouteList *net.PortList `protobuf:"bytes,20,opt,name=vless_route_list,json=vlessRouteList,proto3" json:"vless_route_list,omitempty"`
|
||||
Process []string `protobuf:"bytes,21,rep,name=process,proto3" json:"process,omitempty"`
|
||||
Webhook *WebhookConfig `protobuf:"bytes,22,opt,name=webhook,proto3" json:"webhook,omitempty"`
|
||||
// List of IPs for local IP address matching.
|
||||
LocalIp []*geodata.IPRule `protobuf:"bytes,17,rep,name=local_ip,json=localIp,proto3" json:"local_ip,omitempty"`
|
||||
// List of ports for local port matching.
|
||||
LocalPortList *net.PortList `protobuf:"bytes,18,opt,name=local_port_list,json=localPortList,proto3" json:"local_port_list,omitempty"`
|
||||
VlessRouteList *net.PortList `protobuf:"bytes,20,opt,name=vless_route_list,json=vlessRouteList,proto3" json:"vless_route_list,omitempty"`
|
||||
Process []string `protobuf:"bytes,21,rep,name=process,proto3" json:"process,omitempty"`
|
||||
Webhook *WebhookConfig `protobuf:"bytes,22,opt,name=webhook,proto3" json:"webhook,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RoutingRule) Reset() {
|
||||
*x = RoutingRule{}
|
||||
mi := &file_app_router_config_proto_msgTypes[6]
|
||||
mi := &file_app_router_config_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -502,7 +125,7 @@ func (x *RoutingRule) String() string {
|
||||
func (*RoutingRule) ProtoMessage() {}
|
||||
|
||||
func (x *RoutingRule) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[6]
|
||||
mi := &file_app_router_config_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -515,7 +138,7 @@ func (x *RoutingRule) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RoutingRule.ProtoReflect.Descriptor instead.
|
||||
func (*RoutingRule) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{6}
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *RoutingRule) GetTargetTag() isRoutingRule_TargetTag {
|
||||
@@ -550,16 +173,16 @@ func (x *RoutingRule) GetRuleTag() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RoutingRule) GetDomain() []*Domain {
|
||||
func (x *RoutingRule) GetDomain() []*geodata.DomainRule {
|
||||
if x != nil {
|
||||
return x.Domain
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RoutingRule) GetGeoip() []*GeoIP {
|
||||
func (x *RoutingRule) GetIp() []*geodata.IPRule {
|
||||
if x != nil {
|
||||
return x.Geoip
|
||||
return x.Ip
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -578,9 +201,9 @@ func (x *RoutingRule) GetNetworks() []net.Network {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RoutingRule) GetSourceGeoip() []*GeoIP {
|
||||
func (x *RoutingRule) GetSourceIp() []*geodata.IPRule {
|
||||
if x != nil {
|
||||
return x.SourceGeoip
|
||||
return x.SourceIp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -620,9 +243,9 @@ func (x *RoutingRule) GetAttributes() map[string]string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *RoutingRule) GetLocalGeoip() []*GeoIP {
|
||||
func (x *RoutingRule) GetLocalIp() []*geodata.IPRule {
|
||||
if x != nil {
|
||||
return x.LocalGeoip
|
||||
return x.LocalIp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -684,7 +307,7 @@ type WebhookConfig struct {
|
||||
|
||||
func (x *WebhookConfig) Reset() {
|
||||
*x = WebhookConfig{}
|
||||
mi := &file_app_router_config_proto_msgTypes[7]
|
||||
mi := &file_app_router_config_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -696,7 +319,7 @@ func (x *WebhookConfig) String() string {
|
||||
func (*WebhookConfig) ProtoMessage() {}
|
||||
|
||||
func (x *WebhookConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[7]
|
||||
mi := &file_app_router_config_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -709,7 +332,7 @@ func (x *WebhookConfig) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use WebhookConfig.ProtoReflect.Descriptor instead.
|
||||
func (*WebhookConfig) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{7}
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *WebhookConfig) GetUrl() string {
|
||||
@@ -746,7 +369,7 @@ type BalancingRule struct {
|
||||
|
||||
func (x *BalancingRule) Reset() {
|
||||
*x = BalancingRule{}
|
||||
mi := &file_app_router_config_proto_msgTypes[8]
|
||||
mi := &file_app_router_config_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -758,7 +381,7 @@ func (x *BalancingRule) String() string {
|
||||
func (*BalancingRule) ProtoMessage() {}
|
||||
|
||||
func (x *BalancingRule) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[8]
|
||||
mi := &file_app_router_config_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -771,7 +394,7 @@ func (x *BalancingRule) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use BalancingRule.ProtoReflect.Descriptor instead.
|
||||
func (*BalancingRule) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{8}
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *BalancingRule) GetTag() string {
|
||||
@@ -820,7 +443,7 @@ type StrategyWeight struct {
|
||||
|
||||
func (x *StrategyWeight) Reset() {
|
||||
*x = StrategyWeight{}
|
||||
mi := &file_app_router_config_proto_msgTypes[9]
|
||||
mi := &file_app_router_config_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -832,7 +455,7 @@ func (x *StrategyWeight) String() string {
|
||||
func (*StrategyWeight) ProtoMessage() {}
|
||||
|
||||
func (x *StrategyWeight) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[9]
|
||||
mi := &file_app_router_config_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -845,7 +468,7 @@ func (x *StrategyWeight) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use StrategyWeight.ProtoReflect.Descriptor instead.
|
||||
func (*StrategyWeight) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{9}
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *StrategyWeight) GetRegexp() bool {
|
||||
@@ -887,7 +510,7 @@ type StrategyLeastLoadConfig struct {
|
||||
|
||||
func (x *StrategyLeastLoadConfig) Reset() {
|
||||
*x = StrategyLeastLoadConfig{}
|
||||
mi := &file_app_router_config_proto_msgTypes[10]
|
||||
mi := &file_app_router_config_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -899,7 +522,7 @@ func (x *StrategyLeastLoadConfig) String() string {
|
||||
func (*StrategyLeastLoadConfig) ProtoMessage() {}
|
||||
|
||||
func (x *StrategyLeastLoadConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[10]
|
||||
mi := &file_app_router_config_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -912,7 +535,7 @@ func (x *StrategyLeastLoadConfig) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use StrategyLeastLoadConfig.ProtoReflect.Descriptor instead.
|
||||
func (*StrategyLeastLoadConfig) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{10}
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *StrategyLeastLoadConfig) GetCosts() []*StrategyWeight {
|
||||
@@ -961,7 +584,7 @@ type Config struct {
|
||||
|
||||
func (x *Config) Reset() {
|
||||
*x = Config{}
|
||||
mi := &file_app_router_config_proto_msgTypes[11]
|
||||
mi := &file_app_router_config_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -973,7 +596,7 @@ func (x *Config) String() string {
|
||||
func (*Config) ProtoMessage() {}
|
||||
|
||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[11]
|
||||
mi := &file_app_router_config_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -986,7 +609,7 @@ func (x *Config) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||
func (*Config) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{11}
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *Config) GetDomainStrategy() Config_DomainStrategy {
|
||||
@@ -1010,141 +633,21 @@ func (x *Config) GetBalancingRule() []*BalancingRule {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Domain_Attribute struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
// Types that are valid to be assigned to TypedValue:
|
||||
//
|
||||
// *Domain_Attribute_BoolValue
|
||||
// *Domain_Attribute_IntValue
|
||||
TypedValue isDomain_Attribute_TypedValue `protobuf_oneof:"typed_value"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Domain_Attribute) Reset() {
|
||||
*x = Domain_Attribute{}
|
||||
mi := &file_app_router_config_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Domain_Attribute) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Domain_Attribute) ProtoMessage() {}
|
||||
|
||||
func (x *Domain_Attribute) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_app_router_config_proto_msgTypes[12]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Domain_Attribute.ProtoReflect.Descriptor instead.
|
||||
func (*Domain_Attribute) Descriptor() ([]byte, []int) {
|
||||
return file_app_router_config_proto_rawDescGZIP(), []int{0, 0}
|
||||
}
|
||||
|
||||
func (x *Domain_Attribute) GetKey() string {
|
||||
if x != nil {
|
||||
return x.Key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Domain_Attribute) GetTypedValue() isDomain_Attribute_TypedValue {
|
||||
if x != nil {
|
||||
return x.TypedValue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Domain_Attribute) GetBoolValue() bool {
|
||||
if x != nil {
|
||||
if x, ok := x.TypedValue.(*Domain_Attribute_BoolValue); ok {
|
||||
return x.BoolValue
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *Domain_Attribute) GetIntValue() int64 {
|
||||
if x != nil {
|
||||
if x, ok := x.TypedValue.(*Domain_Attribute_IntValue); ok {
|
||||
return x.IntValue
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type isDomain_Attribute_TypedValue interface {
|
||||
isDomain_Attribute_TypedValue()
|
||||
}
|
||||
|
||||
type Domain_Attribute_BoolValue struct {
|
||||
BoolValue bool `protobuf:"varint,2,opt,name=bool_value,json=boolValue,proto3,oneof"`
|
||||
}
|
||||
|
||||
type Domain_Attribute_IntValue struct {
|
||||
IntValue int64 `protobuf:"varint,3,opt,name=int_value,json=intValue,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*Domain_Attribute_BoolValue) isDomain_Attribute_TypedValue() {}
|
||||
|
||||
func (*Domain_Attribute_IntValue) isDomain_Attribute_TypedValue() {}
|
||||
|
||||
var File_app_router_config_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_app_router_config_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x17app/router/config.proto\x12\x0fxray.app.router\x1a!common/serial/typed_message.proto\x1a\x15common/net/port.proto\x1a\x18common/net/network.proto\"\xb3\x02\n" +
|
||||
"\x06Domain\x120\n" +
|
||||
"\x04type\x18\x01 \x01(\x0e2\x1c.xray.app.router.Domain.TypeR\x04type\x12\x14\n" +
|
||||
"\x05value\x18\x02 \x01(\tR\x05value\x12?\n" +
|
||||
"\tattribute\x18\x03 \x03(\v2!.xray.app.router.Domain.AttributeR\tattribute\x1al\n" +
|
||||
"\tAttribute\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x1f\n" +
|
||||
"\n" +
|
||||
"bool_value\x18\x02 \x01(\bH\x00R\tboolValue\x12\x1d\n" +
|
||||
"\tint_value\x18\x03 \x01(\x03H\x00R\bintValueB\r\n" +
|
||||
"\vtyped_value\"2\n" +
|
||||
"\x04Type\x12\t\n" +
|
||||
"\x05Plain\x10\x00\x12\t\n" +
|
||||
"\x05Regex\x10\x01\x12\n" +
|
||||
"\n" +
|
||||
"\x06Domain\x10\x02\x12\b\n" +
|
||||
"\x04Full\x10\x03\".\n" +
|
||||
"\x04CIDR\x12\x0e\n" +
|
||||
"\x02ip\x18\x01 \x01(\fR\x02ip\x12\x16\n" +
|
||||
"\x06prefix\x18\x02 \x01(\rR\x06prefix\"z\n" +
|
||||
"\x05GeoIP\x12!\n" +
|
||||
"\fcountry_code\x18\x01 \x01(\tR\vcountryCode\x12)\n" +
|
||||
"\x04cidr\x18\x02 \x03(\v2\x15.xray.app.router.CIDRR\x04cidr\x12#\n" +
|
||||
"\rreverse_match\x18\x03 \x01(\bR\freverseMatch\"9\n" +
|
||||
"\tGeoIPList\x12,\n" +
|
||||
"\x05entry\x18\x01 \x03(\v2\x16.xray.app.router.GeoIPR\x05entry\"]\n" +
|
||||
"\aGeoSite\x12!\n" +
|
||||
"\fcountry_code\x18\x01 \x01(\tR\vcountryCode\x12/\n" +
|
||||
"\x06domain\x18\x02 \x03(\v2\x17.xray.app.router.DomainR\x06domain\"=\n" +
|
||||
"\vGeoSiteList\x12.\n" +
|
||||
"\x05entry\x18\x01 \x03(\v2\x18.xray.app.router.GeoSiteR\x05entry\"\xbc\a\n" +
|
||||
"\x17app/router/config.proto\x12\x0fxray.app.router\x1a!common/serial/typed_message.proto\x1a\x15common/net/port.proto\x1a\x18common/net/network.proto\x1a\x1bcommon/geodata/geodat.proto\"\xc1\a\n" +
|
||||
"\vRoutingRule\x12\x12\n" +
|
||||
"\x03tag\x18\x01 \x01(\tH\x00R\x03tag\x12%\n" +
|
||||
"\rbalancing_tag\x18\f \x01(\tH\x00R\fbalancingTag\x12\x19\n" +
|
||||
"\brule_tag\x18\x13 \x01(\tR\aruleTag\x12/\n" +
|
||||
"\x06domain\x18\x02 \x03(\v2\x17.xray.app.router.DomainR\x06domain\x12,\n" +
|
||||
"\x05geoip\x18\n" +
|
||||
" \x03(\v2\x16.xray.app.router.GeoIPR\x05geoip\x126\n" +
|
||||
"\brule_tag\x18\x13 \x01(\tR\aruleTag\x127\n" +
|
||||
"\x06domain\x18\x02 \x03(\v2\x1f.xray.common.geodata.DomainRuleR\x06domain\x12+\n" +
|
||||
"\x02ip\x18\n" +
|
||||
" \x03(\v2\x1b.xray.common.geodata.IPRuleR\x02ip\x126\n" +
|
||||
"\tport_list\x18\x0e \x01(\v2\x19.xray.common.net.PortListR\bportList\x124\n" +
|
||||
"\bnetworks\x18\r \x03(\x0e2\x18.xray.common.net.NetworkR\bnetworks\x129\n" +
|
||||
"\fsource_geoip\x18\v \x03(\v2\x16.xray.app.router.GeoIPR\vsourceGeoip\x12C\n" +
|
||||
"\bnetworks\x18\r \x03(\x0e2\x18.xray.common.net.NetworkR\bnetworks\x128\n" +
|
||||
"\tsource_ip\x18\v \x03(\v2\x1b.xray.common.geodata.IPRuleR\bsourceIp\x12C\n" +
|
||||
"\x10source_port_list\x18\x10 \x01(\v2\x19.xray.common.net.PortListR\x0esourcePortList\x12\x1d\n" +
|
||||
"\n" +
|
||||
"user_email\x18\a \x03(\tR\tuserEmail\x12\x1f\n" +
|
||||
@@ -1153,9 +656,8 @@ const file_app_router_config_proto_rawDesc = "" +
|
||||
"\bprotocol\x18\t \x03(\tR\bprotocol\x12L\n" +
|
||||
"\n" +
|
||||
"attributes\x18\x0f \x03(\v2,.xray.app.router.RoutingRule.AttributesEntryR\n" +
|
||||
"attributes\x127\n" +
|
||||
"\vlocal_geoip\x18\x11 \x03(\v2\x16.xray.app.router.GeoIPR\n" +
|
||||
"localGeoip\x12A\n" +
|
||||
"attributes\x126\n" +
|
||||
"\blocal_ip\x18\x11 \x03(\v2\x1b.xray.common.geodata.IPRuleR\alocalIp\x12A\n" +
|
||||
"\x0flocal_port_list\x18\x12 \x01(\v2\x19.xray.common.net.PortListR\rlocalPortList\x12C\n" +
|
||||
"\x10vless_route_list\x18\x14 \x01(\v2\x19.xray.common.net.PortListR\x0evlessRouteList\x12\x18\n" +
|
||||
"\aprocess\x18\x15 \x03(\tR\aprocess\x128\n" +
|
||||
@@ -1187,16 +689,16 @@ const file_app_router_config_proto_rawDesc = "" +
|
||||
"\tbaselines\x18\x03 \x03(\x03R\tbaselines\x12\x1a\n" +
|
||||
"\bexpected\x18\x04 \x01(\x05R\bexpected\x12\x16\n" +
|
||||
"\x06maxRTT\x18\x05 \x01(\x03R\x06maxRTT\x12\x1c\n" +
|
||||
"\ttolerance\x18\x06 \x01(\x02R\ttolerance\"\x90\x02\n" +
|
||||
"\ttolerance\x18\x06 \x01(\x02R\ttolerance\"\x96\x02\n" +
|
||||
"\x06Config\x12O\n" +
|
||||
"\x0fdomain_strategy\x18\x01 \x01(\x0e2&.xray.app.router.Config.DomainStrategyR\x0edomainStrategy\x120\n" +
|
||||
"\x04rule\x18\x02 \x03(\v2\x1c.xray.app.router.RoutingRuleR\x04rule\x12E\n" +
|
||||
"\x0ebalancing_rule\x18\x03 \x03(\v2\x1e.xray.app.router.BalancingRuleR\rbalancingRule\"<\n" +
|
||||
"\x0ebalancing_rule\x18\x03 \x03(\v2\x1e.xray.app.router.BalancingRuleR\rbalancingRule\"B\n" +
|
||||
"\x0eDomainStrategy\x12\b\n" +
|
||||
"\x04AsIs\x10\x00\x12\x10\n" +
|
||||
"\fIpIfNonMatch\x10\x02\x12\x0e\n" +
|
||||
"\n" +
|
||||
"IpOnDemand\x10\x03BO\n" +
|
||||
"IpOnDemand\x10\x03\"\x04\b\x01\x10\x01BO\n" +
|
||||
"\x13com.xray.app.routerP\x01Z$github.com/xtls/xray-core/app/router\xaa\x02\x0fXray.App.Routerb\x06proto3"
|
||||
|
||||
var (
|
||||
@@ -1211,59 +713,47 @@ func file_app_router_config_proto_rawDescGZIP() []byte {
|
||||
return file_app_router_config_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_app_router_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||
var file_app_router_config_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
|
||||
var file_app_router_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_app_router_config_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
||||
var file_app_router_config_proto_goTypes = []any{
|
||||
(Domain_Type)(0), // 0: xray.app.router.Domain.Type
|
||||
(Config_DomainStrategy)(0), // 1: xray.app.router.Config.DomainStrategy
|
||||
(*Domain)(nil), // 2: xray.app.router.Domain
|
||||
(*CIDR)(nil), // 3: xray.app.router.CIDR
|
||||
(*GeoIP)(nil), // 4: xray.app.router.GeoIP
|
||||
(*GeoIPList)(nil), // 5: xray.app.router.GeoIPList
|
||||
(*GeoSite)(nil), // 6: xray.app.router.GeoSite
|
||||
(*GeoSiteList)(nil), // 7: xray.app.router.GeoSiteList
|
||||
(*RoutingRule)(nil), // 8: xray.app.router.RoutingRule
|
||||
(*WebhookConfig)(nil), // 9: xray.app.router.WebhookConfig
|
||||
(*BalancingRule)(nil), // 10: xray.app.router.BalancingRule
|
||||
(*StrategyWeight)(nil), // 11: xray.app.router.StrategyWeight
|
||||
(*StrategyLeastLoadConfig)(nil), // 12: xray.app.router.StrategyLeastLoadConfig
|
||||
(*Config)(nil), // 13: xray.app.router.Config
|
||||
(*Domain_Attribute)(nil), // 14: xray.app.router.Domain.Attribute
|
||||
nil, // 15: xray.app.router.RoutingRule.AttributesEntry
|
||||
nil, // 16: xray.app.router.WebhookConfig.HeadersEntry
|
||||
(*net.PortList)(nil), // 17: xray.common.net.PortList
|
||||
(net.Network)(0), // 18: xray.common.net.Network
|
||||
(*serial.TypedMessage)(nil), // 19: xray.common.serial.TypedMessage
|
||||
(Config_DomainStrategy)(0), // 0: xray.app.router.Config.DomainStrategy
|
||||
(*RoutingRule)(nil), // 1: xray.app.router.RoutingRule
|
||||
(*WebhookConfig)(nil), // 2: xray.app.router.WebhookConfig
|
||||
(*BalancingRule)(nil), // 3: xray.app.router.BalancingRule
|
||||
(*StrategyWeight)(nil), // 4: xray.app.router.StrategyWeight
|
||||
(*StrategyLeastLoadConfig)(nil), // 5: xray.app.router.StrategyLeastLoadConfig
|
||||
(*Config)(nil), // 6: xray.app.router.Config
|
||||
nil, // 7: xray.app.router.RoutingRule.AttributesEntry
|
||||
nil, // 8: xray.app.router.WebhookConfig.HeadersEntry
|
||||
(*geodata.DomainRule)(nil), // 9: xray.common.geodata.DomainRule
|
||||
(*geodata.IPRule)(nil), // 10: xray.common.geodata.IPRule
|
||||
(*net.PortList)(nil), // 11: xray.common.net.PortList
|
||||
(net.Network)(0), // 12: xray.common.net.Network
|
||||
(*serial.TypedMessage)(nil), // 13: xray.common.serial.TypedMessage
|
||||
}
|
||||
var file_app_router_config_proto_depIdxs = []int32{
|
||||
0, // 0: xray.app.router.Domain.type:type_name -> xray.app.router.Domain.Type
|
||||
14, // 1: xray.app.router.Domain.attribute:type_name -> xray.app.router.Domain.Attribute
|
||||
3, // 2: xray.app.router.GeoIP.cidr:type_name -> xray.app.router.CIDR
|
||||
4, // 3: xray.app.router.GeoIPList.entry:type_name -> xray.app.router.GeoIP
|
||||
2, // 4: xray.app.router.GeoSite.domain:type_name -> xray.app.router.Domain
|
||||
6, // 5: xray.app.router.GeoSiteList.entry:type_name -> xray.app.router.GeoSite
|
||||
2, // 6: xray.app.router.RoutingRule.domain:type_name -> xray.app.router.Domain
|
||||
4, // 7: xray.app.router.RoutingRule.geoip:type_name -> xray.app.router.GeoIP
|
||||
17, // 8: xray.app.router.RoutingRule.port_list:type_name -> xray.common.net.PortList
|
||||
18, // 9: xray.app.router.RoutingRule.networks:type_name -> xray.common.net.Network
|
||||
4, // 10: xray.app.router.RoutingRule.source_geoip:type_name -> xray.app.router.GeoIP
|
||||
17, // 11: xray.app.router.RoutingRule.source_port_list:type_name -> xray.common.net.PortList
|
||||
15, // 12: xray.app.router.RoutingRule.attributes:type_name -> xray.app.router.RoutingRule.AttributesEntry
|
||||
4, // 13: xray.app.router.RoutingRule.local_geoip:type_name -> xray.app.router.GeoIP
|
||||
17, // 14: xray.app.router.RoutingRule.local_port_list:type_name -> xray.common.net.PortList
|
||||
17, // 15: xray.app.router.RoutingRule.vless_route_list:type_name -> xray.common.net.PortList
|
||||
9, // 16: xray.app.router.RoutingRule.webhook:type_name -> xray.app.router.WebhookConfig
|
||||
16, // 17: xray.app.router.WebhookConfig.headers:type_name -> xray.app.router.WebhookConfig.HeadersEntry
|
||||
19, // 18: xray.app.router.BalancingRule.strategy_settings:type_name -> xray.common.serial.TypedMessage
|
||||
11, // 19: xray.app.router.StrategyLeastLoadConfig.costs:type_name -> xray.app.router.StrategyWeight
|
||||
1, // 20: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy
|
||||
8, // 21: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule
|
||||
10, // 22: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule
|
||||
23, // [23:23] is the sub-list for method output_type
|
||||
23, // [23:23] is the sub-list for method input_type
|
||||
23, // [23:23] is the sub-list for extension type_name
|
||||
23, // [23:23] is the sub-list for extension extendee
|
||||
0, // [0:23] is the sub-list for field type_name
|
||||
9, // 0: xray.app.router.RoutingRule.domain:type_name -> xray.common.geodata.DomainRule
|
||||
10, // 1: xray.app.router.RoutingRule.ip:type_name -> xray.common.geodata.IPRule
|
||||
11, // 2: xray.app.router.RoutingRule.port_list:type_name -> xray.common.net.PortList
|
||||
12, // 3: xray.app.router.RoutingRule.networks:type_name -> xray.common.net.Network
|
||||
10, // 4: xray.app.router.RoutingRule.source_ip:type_name -> xray.common.geodata.IPRule
|
||||
11, // 5: xray.app.router.RoutingRule.source_port_list:type_name -> xray.common.net.PortList
|
||||
7, // 6: xray.app.router.RoutingRule.attributes:type_name -> xray.app.router.RoutingRule.AttributesEntry
|
||||
10, // 7: xray.app.router.RoutingRule.local_ip:type_name -> xray.common.geodata.IPRule
|
||||
11, // 8: xray.app.router.RoutingRule.local_port_list:type_name -> xray.common.net.PortList
|
||||
11, // 9: xray.app.router.RoutingRule.vless_route_list:type_name -> xray.common.net.PortList
|
||||
2, // 10: xray.app.router.RoutingRule.webhook:type_name -> xray.app.router.WebhookConfig
|
||||
8, // 11: xray.app.router.WebhookConfig.headers:type_name -> xray.app.router.WebhookConfig.HeadersEntry
|
||||
13, // 12: xray.app.router.BalancingRule.strategy_settings:type_name -> xray.common.serial.TypedMessage
|
||||
4, // 13: xray.app.router.StrategyLeastLoadConfig.costs:type_name -> xray.app.router.StrategyWeight
|
||||
0, // 14: xray.app.router.Config.domain_strategy:type_name -> xray.app.router.Config.DomainStrategy
|
||||
1, // 15: xray.app.router.Config.rule:type_name -> xray.app.router.RoutingRule
|
||||
3, // 16: xray.app.router.Config.balancing_rule:type_name -> xray.app.router.BalancingRule
|
||||
17, // [17:17] is the sub-list for method output_type
|
||||
17, // [17:17] is the sub-list for method input_type
|
||||
17, // [17:17] is the sub-list for extension type_name
|
||||
17, // [17:17] is the sub-list for extension extendee
|
||||
0, // [0:17] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_app_router_config_proto_init() }
|
||||
@@ -1271,21 +761,17 @@ func file_app_router_config_proto_init() {
|
||||
if File_app_router_config_proto != nil {
|
||||
return
|
||||
}
|
||||
file_app_router_config_proto_msgTypes[6].OneofWrappers = []any{
|
||||
file_app_router_config_proto_msgTypes[0].OneofWrappers = []any{
|
||||
(*RoutingRule_Tag)(nil),
|
||||
(*RoutingRule_BalancingTag)(nil),
|
||||
}
|
||||
file_app_router_config_proto_msgTypes[12].OneofWrappers = []any{
|
||||
(*Domain_Attribute_BoolValue)(nil),
|
||||
(*Domain_Attribute_IntValue)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_router_config_proto_rawDesc), len(file_app_router_config_proto_rawDesc)),
|
||||
NumEnums: 2,
|
||||
NumMessages: 15,
|
||||
NumEnums: 1,
|
||||
NumMessages: 8,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -9,67 +9,7 @@ option java_multiple_files = true;
|
||||
import "common/serial/typed_message.proto";
|
||||
import "common/net/port.proto";
|
||||
import "common/net/network.proto";
|
||||
|
||||
// Domain for routing decision.
|
||||
message Domain {
|
||||
// Type of domain value.
|
||||
enum Type {
|
||||
// The value is used as is.
|
||||
Plain = 0;
|
||||
// The value is used as a regular expression.
|
||||
Regex = 1;
|
||||
// The value is a root domain.
|
||||
Domain = 2;
|
||||
// The value is a domain.
|
||||
Full = 3;
|
||||
}
|
||||
|
||||
// Domain matching type.
|
||||
Type type = 1;
|
||||
|
||||
// Domain value.
|
||||
string value = 2;
|
||||
|
||||
message Attribute {
|
||||
string key = 1;
|
||||
|
||||
oneof typed_value {
|
||||
bool bool_value = 2;
|
||||
int64 int_value = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Attributes of this domain. May be used for filtering.
|
||||
repeated Attribute attribute = 3;
|
||||
}
|
||||
|
||||
// IP for routing decision, in CIDR form.
|
||||
message CIDR {
|
||||
// IP address, should be either 4 or 16 bytes.
|
||||
bytes ip = 1;
|
||||
|
||||
// Number of leading ones in the network mask.
|
||||
uint32 prefix = 2;
|
||||
}
|
||||
|
||||
message GeoIP {
|
||||
string country_code = 1;
|
||||
repeated CIDR cidr = 2;
|
||||
bool reverse_match = 3;
|
||||
}
|
||||
|
||||
message GeoIPList {
|
||||
repeated GeoIP entry = 1;
|
||||
}
|
||||
|
||||
message GeoSite {
|
||||
string country_code = 1;
|
||||
repeated Domain domain = 2;
|
||||
}
|
||||
|
||||
message GeoSiteList {
|
||||
repeated GeoSite entry = 1;
|
||||
}
|
||||
import "common/geodata/geodat.proto";
|
||||
|
||||
message RoutingRule {
|
||||
oneof target_tag {
|
||||
@@ -79,26 +19,23 @@ message RoutingRule {
|
||||
// Tag of routing balancer.
|
||||
string balancing_tag = 12;
|
||||
}
|
||||
string rule_tag = 19;
|
||||
|
||||
string rule_tag = 19;
|
||||
|
||||
// List of domains for target domain matching.
|
||||
repeated Domain domain = 2;
|
||||
repeated xray.common.geodata.DomainRule domain = 2;
|
||||
|
||||
// List of GeoIPs for target IP address matching. If this entry exists, the
|
||||
// cidr above will have no effect. GeoIP fields with the same country code are
|
||||
// supposed to contain exactly same content. They will be merged during
|
||||
// runtime. For customized GeoIPs, please leave country code empty.
|
||||
repeated GeoIP geoip = 10;
|
||||
// List of IPs for target IP address matching.
|
||||
repeated xray.common.geodata.IPRule ip = 10;
|
||||
|
||||
// List of ports.
|
||||
// List of ports for target port matching.
|
||||
xray.common.net.PortList port_list = 14;
|
||||
|
||||
// List of networks for matching.
|
||||
repeated xray.common.net.Network networks = 13;
|
||||
|
||||
// List of GeoIPs for source IP address matching. If this entry exists, the
|
||||
// source_cidr above will have no effect.
|
||||
repeated GeoIP source_geoip = 11;
|
||||
// List of IPs for source IP address matching.
|
||||
repeated xray.common.geodata.IPRule source_ip = 11;
|
||||
|
||||
// List of ports for source port matching.
|
||||
xray.common.net.PortList source_port_list = 16;
|
||||
@@ -109,10 +46,14 @@ message RoutingRule {
|
||||
|
||||
map<string, string> attributes = 15;
|
||||
|
||||
repeated GeoIP local_geoip = 17;
|
||||
// List of IPs for local IP address matching.
|
||||
repeated xray.common.geodata.IPRule local_ip = 17;
|
||||
|
||||
// List of ports for local port matching.
|
||||
xray.common.net.PortList local_port_list = 18;
|
||||
|
||||
xray.common.net.PortList vless_route_list = 20;
|
||||
|
||||
repeated string process = 21;
|
||||
WebhookConfig webhook = 22;
|
||||
}
|
||||
@@ -155,8 +96,7 @@ message Config {
|
||||
// Use domain as is.
|
||||
AsIs = 0;
|
||||
|
||||
// [Deprecated] Always resolve IP for domains.
|
||||
// UseIp = 1;
|
||||
reserved 1;
|
||||
|
||||
// Resolve to IP if the domain doesn't match any rules.
|
||||
IpIfNonMatch = 2;
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"io"
|
||||
"runtime"
|
||||
|
||||
"github.com/xtls/xray-core/common/strmatcher"
|
||||
)
|
||||
|
||||
type geoSiteListGob struct {
|
||||
Sites map[string][]byte
|
||||
Deps map[string][]string
|
||||
Hosts map[string][]string
|
||||
}
|
||||
|
||||
func SerializeGeoSiteList(sites []*GeoSite, deps map[string][]string, hosts map[string][]string, w io.Writer) error {
|
||||
data := geoSiteListGob{
|
||||
Sites: make(map[string][]byte),
|
||||
Deps: deps,
|
||||
Hosts: hosts,
|
||||
}
|
||||
|
||||
for _, site := range sites {
|
||||
if site == nil {
|
||||
continue
|
||||
}
|
||||
var buf bytesWriter
|
||||
if err := SerializeDomainMatcher(site.Domain, &buf); err != nil {
|
||||
return err
|
||||
}
|
||||
data.Sites[site.CountryCode] = buf.Bytes()
|
||||
}
|
||||
|
||||
return gob.NewEncoder(w).Encode(data)
|
||||
}
|
||||
|
||||
type bytesWriter struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (w *bytesWriter) Write(p []byte) (n int, err error) {
|
||||
w.data = append(w.data, p...)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (w *bytesWriter) Bytes() []byte {
|
||||
return w.data
|
||||
}
|
||||
|
||||
func LoadGeoSiteMatcher(r io.Reader, countryCode string) (strmatcher.IndexMatcher, error) {
|
||||
var data geoSiteListGob
|
||||
if err := gob.NewDecoder(r).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return loadWithDeps(&data, countryCode, make(map[string]bool))
|
||||
}
|
||||
|
||||
func loadWithDeps(data *geoSiteListGob, code string, visited map[string]bool) (strmatcher.IndexMatcher, error) {
|
||||
if visited[code] {
|
||||
return nil, errors.New("cyclic dependency")
|
||||
}
|
||||
visited[code] = true
|
||||
|
||||
var matchers []strmatcher.IndexMatcher
|
||||
|
||||
if siteData, ok := data.Sites[code]; ok {
|
||||
m, err := NewDomainMatcherFromBuffer(siteData)
|
||||
if err == nil {
|
||||
matchers = append(matchers, m)
|
||||
}
|
||||
}
|
||||
|
||||
if deps, ok := data.Deps[code]; ok {
|
||||
for _, dep := range deps {
|
||||
m, err := loadWithDeps(data, dep, visited)
|
||||
if err == nil {
|
||||
matchers = append(matchers, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(matchers) == 0 {
|
||||
return nil, errors.New("matcher not found for: " + code)
|
||||
}
|
||||
if len(matchers) == 1 {
|
||||
return matchers[0], nil
|
||||
}
|
||||
runtime.GC()
|
||||
return &strmatcher.IndexMatcherGroup{Matchers: matchers}, nil
|
||||
}
|
||||
func LoadGeoSiteHosts(r io.Reader) (map[string][]string, error) {
|
||||
var data geoSiteListGob
|
||||
if err := gob.NewDecoder(r).Decode(&data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data.Hosts, nil
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/golang/mock/gomock"
|
||||
. "github.com/xtls/xray-core/app/router"
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/geodata"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
"github.com/xtls/xray-core/common/session"
|
||||
"github.com/xtls/xray-core/features/dns"
|
||||
@@ -155,10 +156,10 @@ func TestIPOnDemand(t *testing.T) {
|
||||
TargetTag: &RoutingRule_Tag{
|
||||
Tag: "test",
|
||||
},
|
||||
Geoip: []*GeoIP{
|
||||
Ip: []*geodata.IPRule{
|
||||
{
|
||||
Cidr: []*CIDR{
|
||||
{
|
||||
Value: &geodata.IPRule_Custom{
|
||||
Custom: &geodata.CIDR{
|
||||
Ip: []byte{192, 168, 0, 0},
|
||||
Prefix: 16,
|
||||
},
|
||||
@@ -200,10 +201,10 @@ func TestIPIfNonMatchDomain(t *testing.T) {
|
||||
TargetTag: &RoutingRule_Tag{
|
||||
Tag: "test",
|
||||
},
|
||||
Geoip: []*GeoIP{
|
||||
Ip: []*geodata.IPRule{
|
||||
{
|
||||
Cidr: []*CIDR{
|
||||
{
|
||||
Value: &geodata.IPRule_Custom{
|
||||
Custom: &geodata.CIDR{
|
||||
Ip: []byte{192, 168, 0, 0},
|
||||
Prefix: 16,
|
||||
},
|
||||
@@ -245,10 +246,10 @@ func TestIPIfNonMatchIP(t *testing.T) {
|
||||
TargetTag: &RoutingRule_Tag{
|
||||
Tag: "test",
|
||||
},
|
||||
Geoip: []*GeoIP{
|
||||
Ip: []*geodata.IPRule{
|
||||
{
|
||||
Cidr: []*CIDR{
|
||||
{
|
||||
Value: &geodata.IPRule_Custom{
|
||||
Custom: &geodata.CIDR{
|
||||
Ip: []byte{127, 0, 0, 0},
|
||||
Prefix: 8,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user