Compare commits

..
Author SHA1 Message Date
copilot-swe-agent[bot]andRPRX 7792ca405e Fix WireGuard inbound domain-based routing by storing sniffing config at init time
The root cause of issue #4760 was that the sniffing configuration and inbound tag
were being stored during Process() instead of at initialization time. This caused
a race condition where concurrent connections would overwrite each other's session
information, breaking domain-based routing.

The fix follows the same pattern as the TUN handler:
- Save tag and sniffingRequest during NewServer (at creation time)
- Use these saved values in forwardConnection for each new connection

This ensures that each connection uses the correct sniffing configuration that was
set for the inbound, regardless of concurrent connections from multiple WireGuard peers.

Co-authored-by: RPRX <63339210+RPRX@users.noreply.github.com>
2026-01-16 16:18:23 +00:00
copilot-swe-agent[bot] 81db73e320 Initial plan 2026-01-16 16:11:04 +00:00
47 changed files with 979 additions and 1456 deletions
+1 -2
View File
@@ -95,12 +95,11 @@
## GUI Clients
- OpenWrt
- [PassWall](https://github.com/Openwrt-Passwall/openwrt-passwall), [PassWall 2](https://github.com/Openwrt-Passwall/openwrt-passwall2)
- [PassWall](https://github.com/xiaorouji/openwrt-passwall), [PassWall 2](https://github.com/xiaorouji/openwrt-passwall2)
- [ShadowSocksR Plus+](https://github.com/fw876/helloworld)
- [luci-app-xray](https://github.com/yichya/luci-app-xray) ([openwrt-xray](https://github.com/yichya/openwrt-xray))
- Asuswrt-Merlin
- [XRAYUI](https://github.com/DanielLavrushin/asuswrt-merlin-xrayui)
- [fancyss](https://github.com/hq450/fancyss)
- Windows
- [v2rayN](https://github.com/2dust/v2rayN)
- [Furious](https://github.com/LorenEteval/Furious)
-5
View File
@@ -1,5 +0,0 @@
# Security Policy
If you found an issue related to security vulnerability or protocol-identification problem, please report it to us via "[Report a vulnerability](https://github.com/XTLS/Xray-core/security/advisories/new)" privately, instead of publish it publicly before we release the fixed version.
Thanks for your contribution to the FREE Internet!
+95
View File
@@ -12,12 +12,15 @@ import (
"sync"
"time"
router "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/net"
"github.com/xtls/xray-core/common/platform/filesystem"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/strmatcher"
"github.com/xtls/xray-core/features/dns"
"google.golang.org/protobuf/proto"
)
// DNS is a DNS rely server.
@@ -97,6 +100,25 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
}
for _, ns := range config.NameServer {
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
err := parseDomains(ns)
if err != nil {
return nil, errors.New("failed to parse dns domain rules: ").Base(err)
}
expectedGeoip, err := router.GetGeoIPList(ns.ExpectedGeoip)
if err != nil {
return nil, errors.New("failed to parse dns expectIPs rules: ").Base(err)
}
ns.ExpectedGeoip = expectedGeoip
unexpectedGeoip, err := router.GetGeoIPList(ns.UnexpectedGeoip)
if err != nil {
return nil, errors.New("failed to parse dns unexpectedGeoip rules: ").Base(err)
}
ns.UnexpectedGeoip = unexpectedGeoip
}
domainRuleCount += len(ns.PrioritizedDomain)
}
@@ -580,3 +602,76 @@ func detectGUIPlatform() bool {
}
return false
}
func parseDomains(ns *NameServer) error {
pureDomains := []*router.Domain{}
// convert to pure domain
for _, pd := range ns.PrioritizedDomain {
pureDomains = append(pureDomains, &router.Domain{
Type: router.Domain_Type(pd.Type),
Value: pd.Domain,
})
}
domainList := []*router.Domain{}
for _, domain := range pureDomains {
val := strings.Split(domain.Value, "_")
if len(val) >= 2 {
fileName := val[0]
code := val[1]
bs, err := filesystem.ReadAsset(fileName)
if err != nil {
return errors.New("failed to load file: ", fileName).Base(err)
}
bs = filesystem.Find(bs, []byte(code))
var geosite router.GeoSite
if err := proto.Unmarshal(bs, &geosite); err != nil {
return errors.New("failed Unmarshal :").Base(err)
}
// parse attr
if len(val) == 3 {
siteWithAttr := strings.Split(val[2], ",")
attrs := router.ParseAttrs(siteWithAttr)
if !attrs.IsEmpty() {
filteredDomains := make([]*router.Domain, 0, len(pureDomains))
for _, domain := range geosite.Domain {
if attrs.Match(domain) {
filteredDomains = append(filteredDomains, domain)
}
}
geosite.Domain = filteredDomains
}
}
domainList = append(domainList, geosite.Domain...)
// update ns.OriginalRules Size
ruleTag := strings.Join(val, ":")
for i, oRule := range ns.OriginalRules {
if oRule.Rule == strings.ToLower(ruleTag) {
ns.OriginalRules[i].Size = uint32(len(geosite.Domain))
}
}
} else {
domainList = append(domainList, domain)
}
}
// convert back to NameServer_PriorityDomain
ns.PrioritizedDomain = []*NameServer_PriorityDomain{}
for _, pd := range domainList {
ns.PrioritizedDomain = append(ns.PrioritizedDomain, &NameServer_PriorityDomain{
Type: ToDomainMatchingType(pd.Type),
Domain: pd.Value,
})
}
return nil
}
+4 -4
View File
@@ -541,7 +541,7 @@ func TestIPMatch(t *testing.T) {
},
ExpectedGeoip: []*router.GeoIP{
{
CountryCode: "local",
// local
Cidr: []*router.CIDR{
{
// inner ip, will not match
@@ -565,7 +565,7 @@ func TestIPMatch(t *testing.T) {
},
ExpectedGeoip: []*router.GeoIP{
{
CountryCode: "test",
// test
Cidr: []*router.CIDR{
{
Ip: []byte{8, 8, 8, 8},
@@ -574,7 +574,7 @@ func TestIPMatch(t *testing.T) {
},
},
{
CountryCode: "test",
// test
Cidr: []*router.CIDR{
{
Ip: []byte{8, 8, 8, 4},
@@ -669,7 +669,7 @@ func TestLocalDomain(t *testing.T) {
},
ExpectedGeoip: []*router.GeoIP{
{ // Will match localhost, localhost-a and localhost-b,
CountryCode: "local",
// local
Cidr: []*router.CIDR{
{Ip: []byte{127, 0, 0, 2}, Prefix: 32},
{Ip: []byte{127, 0, 0, 3}, Prefix: 32},
+15
View File
@@ -297,3 +297,18 @@ func ResolveIpOptionOverride(queryStrategy QueryStrategy, ipOption dns.IPOption)
return ipOption
}
}
func ToDomainMatchingType(t router.Domain_Type) DomainMatchingType {
switch t {
case router.Domain_Domain:
return DomainMatchingType_Subdomain
case router.Domain_Full:
return DomainMatchingType_Full
case router.Domain_Plain:
return DomainMatchingType_Keyword
case router.Domain_Regex:
return DomainMatchingType_Regex
default:
panic("unknown domain type")
}
}
+33 -83
View File
@@ -2,9 +2,8 @@ package log
import (
"context"
"net"
"fmt"
"regexp"
"strconv"
"strings"
"sync"
@@ -21,23 +20,14 @@ type Instance struct {
errorLogger log.Handler
active bool
dns bool
mask4 int
mask6 int
}
// New creates a new log.Instance based on the given config.
func New(ctx context.Context, config *Config) (*Instance, error) {
m4, m6, err := ParseMaskAddress(config.MaskAddress)
if err != nil {
return nil, err
}
g := &Instance{
config: config,
active: false,
dns: config.EnableDnsLog,
mask4: m4,
mask6: m6,
}
log.RegisterHandler(g)
@@ -114,11 +104,7 @@ func (g *Instance) Handle(msg log.Message) {
var Msg log.Message
if g.config.MaskAddress != "" {
Msg = &MaskedMsgWrapper{
Message: msg,
Mask4: g.mask4,
Mask6: g.mask6,
}
Msg = &MaskedMsgWrapper{Message: msg, config: g.config}
} else {
Msg = msg
}
@@ -163,87 +149,51 @@ func (g *Instance) Close() error {
return nil
}
func ParseMaskAddress(c string) (int, int, error) {
var m4, m6 int
switch c {
case "half":
m4, m6 = 16, 32
case "quarter":
m4, m6 = 8, 16
case "full":
m4, m6 = 0, 0
case "":
// do nothing
default:
if parts := strings.Split(c, "+"); len(parts) > 0 {
if len(parts) >= 1 && parts[0] != "" {
i, err := strconv.Atoi(strings.TrimPrefix(parts[0], "/"))
if err != nil {
return 32, 128, err
}
m4 = i
}
if len(parts) >= 2 && parts[1] != "" {
i, err := strconv.Atoi(strings.TrimPrefix(parts[1], "/"))
if err != nil {
return 32, 128, err
}
m6 = i
}
}
}
if m4%8 != 0 || m4 > 32 || m4 < 0 {
return 32, 128, errors.New("Log Mask: ipv4 mask must be divisible by 8 and between 0-32")
}
return m4, m6, nil
}
// MaskedMsgWrapper is to wrap the string() method to mask IP addresses in the log.
type MaskedMsgWrapper struct {
log.Message
Mask4 int
Mask6 int
config *Config
}
var (
ipv4Regex = regexp.MustCompile(`(\d{1,3}\.){3}\d{1,3}`)
ipv6Regex = regexp.MustCompile(`(?:[\da-fA-F]{0,4}:[\da-fA-F]{0,4}){2,7}`)
)
func (m *MaskedMsgWrapper) String() string {
str := m.Message.String()
// Process ipv4
maskedMsg := ipv4Regex.ReplaceAllStringFunc(str, func(s string) string {
if m.Mask4 == 32 {
return s
}
if m.Mask4 == 0 {
return "[Masked IPv4]"
}
ipv4Regex := regexp.MustCompile(`(\d{1,3}\.){3}\d{1,3}`)
ipv6Regex := regexp.MustCompile(`((?:[\da-fA-F]{0,4}:[\da-fA-F]{0,4}){2,7})(?:[\/\\%](\d{1,3}))?`)
parts := strings.Split(s, ".")
for i := m.Mask4 / 8; i < 4; i++ {
parts[i] = "*"
// Process ipv4
maskedMsg := ipv4Regex.ReplaceAllStringFunc(str, func(ip string) string {
parts := strings.Split(ip, ".")
switch m.config.MaskAddress {
case "half":
return fmt.Sprintf("%s.%s.*.*", parts[0], parts[1])
case "quarter":
return fmt.Sprintf("%s.*.*.*", parts[0])
case "full":
return "[Masked IPv4]"
default:
return ip
}
return strings.Join(parts, ".")
})
// process ipv6
maskedMsg = ipv6Regex.ReplaceAllStringFunc(maskedMsg, func(s string) string {
if m.Mask6 == 128 {
return s
maskedMsg = ipv6Regex.ReplaceAllStringFunc(maskedMsg, func(ip string) string {
parts := strings.Split(ip, ":")
switch m.config.MaskAddress {
case "half":
if len(parts) >= 2 {
return fmt.Sprintf("%s:%s::/32", parts[0], parts[1])
}
case "quarter":
if len(parts) >= 1 {
return fmt.Sprintf("%s::/16", parts[0])
}
case "full":
return "Masked IPv6" // Do not use [Masked IPv6] like ipv4, or you will get "[[Masked IPv6]]" (v6 address already has [])
default:
return ip
}
if m.Mask6 == 0 {
return "Masked IPv6"
}
ip := net.ParseIP(s)
if ip == nil {
return s
}
return ip.Mask(net.CIDRMask(m.Mask6, 128)).String() + "/" + strconv.Itoa(m.Mask6)
return ip
})
return maskedMsg
-37
View File
@@ -2,7 +2,6 @@ package log_test
import (
"context"
"net"
"testing"
"github.com/golang/mock/gomock"
@@ -51,39 +50,3 @@ func TestCustomLogHandler(t *testing.T) {
common.Must(logger.Close())
}
func TestMaskAddress(t *testing.T) {
m4, m6, err := log.ParseMaskAddress("half")
if err != nil {
t.Fatal(err)
}
maskedAddr := log.MaskedMsgWrapper{
Mask4: m4,
Mask6: m6,
}
maskedAddr.Message = net.ParseIP("11.45.1.4")
if maskedAddr.String() != "11.45.*.*" {
t.Fatal("expected '11.45.*.*', but actually ", maskedAddr.String())
}
maskedAddr.Message = net.ParseIP("11:45:14:19:19:81:0::")
if maskedAddr.String() != "11:45::/32" {
t.Fatal("expected '11:45::/32', but actually", maskedAddr.String())
}
m4, m6, err = log.ParseMaskAddress("/16+/64")
if err != nil {
t.Fatal(err)
}
maskedAddr = log.MaskedMsgWrapper{
Mask4: m4,
Mask6: m6,
}
maskedAddr.Message = net.ParseIP("11.45.1.4")
if maskedAddr.String() != "11.45.*.*" {
t.Fatal("expected '11.45.*.*', but actually ", maskedAddr.String())
}
maskedAddr.Message = net.ParseIP("11:45:14:19:19:81:0::")
if maskedAddr.String() != "11:45:14:19::/64" {
t.Fatal("expected '11:45:14:19::/64', but actually", maskedAddr.String())
}
}
-15
View File
@@ -60,7 +60,6 @@ func (s *routingServer) AddRule(ctx context.Context, request *AddRuleRequest) (*
return nil, errors.New("unsupported router implementation")
}
func (s *routingServer) RemoveRule(ctx context.Context, request *RemoveRuleRequest) (*RemoveRuleResponse, error) {
if bo, ok := s.router.(routing.Router); ok {
return &RemoveRuleResponse{}, bo.RemoveRule(request.RuleTag)
@@ -68,20 +67,6 @@ func (s *routingServer) RemoveRule(ctx context.Context, request *RemoveRuleReque
return nil, errors.New("unsupported router implementation")
}
func (s *routingServer) ListRule(ctx context.Context, request *ListRuleRequest) (*ListRuleResponse, error) {
if bo, ok := s.router.(routing.Router); ok {
response := &ListRuleResponse{}
for _, v := range bo.ListRule() {
response.Rules = append(response.Rules, &ListRuleItem{
Tag: v.GetOutboundTag(),
RuleTag: v.GetRuleTag(),
})
}
return response, nil
}
return nil, errors.New("unsupported router implementation")
}
// NewRoutingServer creates a statistics service with statistics manager.
func NewRoutingServer(router routing.Router, routingStats stats.Channel) RoutingServiceServer {
return &routingServer{
+272 -315
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v6.33.2
// protoc-gen-go v1.35.1
// protoc v5.28.2
// source: app/router/command/command.proto
package command
@@ -13,7 +13,6 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
@@ -27,24 +26,25 @@ const (
// It conforms to the structure of xray.features.routing.Context and
// xray.features.routing.Route.
type RoutingContext struct {
state protoimpl.MessageState `protogen:"open.v1"`
InboundTag string `protobuf:"bytes,1,opt,name=InboundTag,proto3" json:"InboundTag,omitempty"`
Network net.Network `protobuf:"varint,2,opt,name=Network,proto3,enum=xray.common.net.Network" json:"Network,omitempty"`
SourceIPs [][]byte `protobuf:"bytes,3,rep,name=SourceIPs,proto3" json:"SourceIPs,omitempty"`
TargetIPs [][]byte `protobuf:"bytes,4,rep,name=TargetIPs,proto3" json:"TargetIPs,omitempty"`
SourcePort uint32 `protobuf:"varint,5,opt,name=SourcePort,proto3" json:"SourcePort,omitempty"`
TargetPort uint32 `protobuf:"varint,6,opt,name=TargetPort,proto3" json:"TargetPort,omitempty"`
TargetDomain string `protobuf:"bytes,7,opt,name=TargetDomain,proto3" json:"TargetDomain,omitempty"`
Protocol string `protobuf:"bytes,8,opt,name=Protocol,proto3" json:"Protocol,omitempty"`
User string `protobuf:"bytes,9,opt,name=User,proto3" json:"User,omitempty"`
Attributes map[string]string `protobuf:"bytes,10,rep,name=Attributes,proto3" json:"Attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
OutboundGroupTags []string `protobuf:"bytes,11,rep,name=OutboundGroupTags,proto3" json:"OutboundGroupTags,omitempty"`
OutboundTag string `protobuf:"bytes,12,opt,name=OutboundTag,proto3" json:"OutboundTag,omitempty"`
LocalIPs [][]byte `protobuf:"bytes,13,rep,name=LocalIPs,proto3" json:"LocalIPs,omitempty"`
LocalPort uint32 `protobuf:"varint,14,opt,name=LocalPort,proto3" json:"LocalPort,omitempty"`
VlessRoute uint32 `protobuf:"varint,15,opt,name=VlessRoute,proto3" json:"VlessRoute,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
InboundTag string `protobuf:"bytes,1,opt,name=InboundTag,proto3" json:"InboundTag,omitempty"`
Network net.Network `protobuf:"varint,2,opt,name=Network,proto3,enum=xray.common.net.Network" json:"Network,omitempty"`
SourceIPs [][]byte `protobuf:"bytes,3,rep,name=SourceIPs,proto3" json:"SourceIPs,omitempty"`
TargetIPs [][]byte `protobuf:"bytes,4,rep,name=TargetIPs,proto3" json:"TargetIPs,omitempty"`
SourcePort uint32 `protobuf:"varint,5,opt,name=SourcePort,proto3" json:"SourcePort,omitempty"`
TargetPort uint32 `protobuf:"varint,6,opt,name=TargetPort,proto3" json:"TargetPort,omitempty"`
TargetDomain string `protobuf:"bytes,7,opt,name=TargetDomain,proto3" json:"TargetDomain,omitempty"`
Protocol string `protobuf:"bytes,8,opt,name=Protocol,proto3" json:"Protocol,omitempty"`
User string `protobuf:"bytes,9,opt,name=User,proto3" json:"User,omitempty"`
Attributes map[string]string `protobuf:"bytes,10,rep,name=Attributes,proto3" json:"Attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
OutboundGroupTags []string `protobuf:"bytes,11,rep,name=OutboundGroupTags,proto3" json:"OutboundGroupTags,omitempty"`
OutboundTag string `protobuf:"bytes,12,opt,name=OutboundTag,proto3" json:"OutboundTag,omitempty"`
LocalIPs [][]byte `protobuf:"bytes,13,rep,name=LocalIPs,proto3" json:"LocalIPs,omitempty"`
LocalPort uint32 `protobuf:"varint,14,opt,name=LocalPort,proto3" json:"LocalPort,omitempty"`
VlessRoute uint32 `protobuf:"varint,15,opt,name=VlessRoute,proto3" json:"VlessRoute,omitempty"`
}
func (x *RoutingContext) Reset() {
@@ -201,10 +201,11 @@ func (x *RoutingContext) GetVlessRoute() uint32 {
//
// * If FieldSelectors is left empty, all fields will be returned.
type SubscribeRoutingStatsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
FieldSelectors []string `protobuf:"bytes,1,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
FieldSelectors []string `protobuf:"bytes,1,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"`
}
func (x *SubscribeRoutingStatsRequest) Reset() {
@@ -252,12 +253,13 @@ func (x *SubscribeRoutingStatsRequest) GetFieldSelectors() []string {
// * PublishResult broadcasts the routing result to routing statistics channel
// if set true.
type TestRouteRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
RoutingContext *RoutingContext `protobuf:"bytes,1,opt,name=RoutingContext,proto3" json:"RoutingContext,omitempty"`
FieldSelectors []string `protobuf:"bytes,2,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"`
PublishResult bool `protobuf:"varint,3,opt,name=PublishResult,proto3" json:"PublishResult,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
RoutingContext *RoutingContext `protobuf:"bytes,1,opt,name=RoutingContext,proto3" json:"RoutingContext,omitempty"`
FieldSelectors []string `protobuf:"bytes,2,rep,name=FieldSelectors,proto3" json:"FieldSelectors,omitempty"`
PublishResult bool `protobuf:"varint,3,opt,name=PublishResult,proto3" json:"PublishResult,omitempty"`
}
func (x *TestRouteRequest) Reset() {
@@ -312,10 +314,11 @@ func (x *TestRouteRequest) GetPublishResult() bool {
}
type PrincipleTargetInfo struct {
state protoimpl.MessageState `protogen:"open.v1"`
Tag []string `protobuf:"bytes,1,rep,name=tag,proto3" json:"tag,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tag []string `protobuf:"bytes,1,rep,name=tag,proto3" json:"tag,omitempty"`
}
func (x *PrincipleTargetInfo) Reset() {
@@ -356,10 +359,11 @@ func (x *PrincipleTargetInfo) GetTag() []string {
}
type OverrideInfo struct {
state protoimpl.MessageState `protogen:"open.v1"`
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
}
func (x *OverrideInfo) Reset() {
@@ -400,11 +404,12 @@ func (x *OverrideInfo) GetTarget() string {
}
type BalancerMsg struct {
state protoimpl.MessageState `protogen:"open.v1"`
Override *OverrideInfo `protobuf:"bytes,5,opt,name=override,proto3" json:"override,omitempty"`
PrincipleTarget *PrincipleTargetInfo `protobuf:"bytes,6,opt,name=principle_target,json=principleTarget,proto3" json:"principle_target,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Override *OverrideInfo `protobuf:"bytes,5,opt,name=override,proto3" json:"override,omitempty"`
PrincipleTarget *PrincipleTargetInfo `protobuf:"bytes,6,opt,name=principle_target,json=principleTarget,proto3" json:"principle_target,omitempty"`
}
func (x *BalancerMsg) Reset() {
@@ -452,10 +457,11 @@ func (x *BalancerMsg) GetPrincipleTarget() *PrincipleTargetInfo {
}
type GetBalancerInfoRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
}
func (x *GetBalancerInfoRequest) Reset() {
@@ -496,10 +502,11 @@ func (x *GetBalancerInfoRequest) GetTag() string {
}
type GetBalancerInfoResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Balancer *BalancerMsg `protobuf:"bytes,1,opt,name=balancer,proto3" json:"balancer,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Balancer *BalancerMsg `protobuf:"bytes,1,opt,name=balancer,proto3" json:"balancer,omitempty"`
}
func (x *GetBalancerInfoResponse) Reset() {
@@ -540,11 +547,12 @@ func (x *GetBalancerInfoResponse) GetBalancer() *BalancerMsg {
}
type OverrideBalancerTargetRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
BalancerTag string `protobuf:"bytes,1,opt,name=balancerTag,proto3" json:"balancerTag,omitempty"`
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
BalancerTag string `protobuf:"bytes,1,opt,name=balancerTag,proto3" json:"balancerTag,omitempty"`
Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"`
}
func (x *OverrideBalancerTargetRequest) Reset() {
@@ -592,9 +600,9 @@ func (x *OverrideBalancerTargetRequest) GetTarget() string {
}
type OverrideBalancerTargetResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *OverrideBalancerTargetResponse) Reset() {
@@ -628,11 +636,12 @@ func (*OverrideBalancerTargetResponse) Descriptor() ([]byte, []int) {
}
type AddRuleRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Config *serial.TypedMessage `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"`
ShouldAppend bool `protobuf:"varint,2,opt,name=shouldAppend,proto3" json:"shouldAppend,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Config *serial.TypedMessage `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"`
ShouldAppend bool `protobuf:"varint,2,opt,name=shouldAppend,proto3" json:"shouldAppend,omitempty"`
}
func (x *AddRuleRequest) Reset() {
@@ -680,9 +689,9 @@ func (x *AddRuleRequest) GetShouldAppend() bool {
}
type AddRuleResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *AddRuleResponse) Reset() {
@@ -716,10 +725,11 @@ func (*AddRuleResponse) Descriptor() ([]byte, []int) {
}
type RemoveRuleRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
RuleTag string `protobuf:"bytes,1,opt,name=ruleTag,proto3" json:"ruleTag,omitempty"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
RuleTag string `protobuf:"bytes,1,opt,name=ruleTag,proto3" json:"ruleTag,omitempty"`
}
func (x *RemoveRuleRequest) Reset() {
@@ -760,9 +770,9 @@ func (x *RemoveRuleRequest) GetRuleTag() string {
}
type RemoveRuleResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *RemoveRuleResponse) Reset() {
@@ -795,147 +805,15 @@ func (*RemoveRuleResponse) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{13}
}
type ListRuleRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListRuleRequest) Reset() {
*x = ListRuleRequest{}
mi := &file_app_router_command_command_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListRuleRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListRuleRequest) ProtoMessage() {}
func (x *ListRuleRequest) ProtoReflect() protoreflect.Message {
mi := &file_app_router_command_command_proto_msgTypes[14]
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 ListRuleRequest.ProtoReflect.Descriptor instead.
func (*ListRuleRequest) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{14}
}
type ListRuleItem struct {
state protoimpl.MessageState `protogen:"open.v1"`
Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
RuleTag string `protobuf:"bytes,2,opt,name=ruleTag,proto3" json:"ruleTag,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListRuleItem) Reset() {
*x = ListRuleItem{}
mi := &file_app_router_command_command_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListRuleItem) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListRuleItem) ProtoMessage() {}
func (x *ListRuleItem) ProtoReflect() protoreflect.Message {
mi := &file_app_router_command_command_proto_msgTypes[15]
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 ListRuleItem.ProtoReflect.Descriptor instead.
func (*ListRuleItem) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{15}
}
func (x *ListRuleItem) GetTag() string {
if x != nil {
return x.Tag
}
return ""
}
func (x *ListRuleItem) GetRuleTag() string {
if x != nil {
return x.RuleTag
}
return ""
}
type ListRuleResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Rules []*ListRuleItem `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListRuleResponse) Reset() {
*x = ListRuleResponse{}
mi := &file_app_router_command_command_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListRuleResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListRuleResponse) ProtoMessage() {}
func (x *ListRuleResponse) ProtoReflect() protoreflect.Message {
mi := &file_app_router_command_command_proto_msgTypes[16]
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 ListRuleResponse.ProtoReflect.Descriptor instead.
func (*ListRuleResponse) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{16}
}
func (x *ListRuleResponse) GetRules() []*ListRuleItem {
if x != nil {
return x.Rules
}
return nil
}
type Config struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *Config) Reset() {
*x = Config{}
mi := &file_app_router_command_command_proto_msgTypes[17]
mi := &file_app_router_command_command_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -947,7 +825,7 @@ func (x *Config) String() string {
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_app_router_command_command_proto_msgTypes[17]
mi := &file_app_router_command_command_proto_msgTypes[14]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -960,103 +838,187 @@ func (x *Config) ProtoReflect() protoreflect.Message {
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
return file_app_router_command_command_proto_rawDescGZIP(), []int{17}
return file_app_router_command_command_proto_rawDescGZIP(), []int{14}
}
var File_app_router_command_command_proto protoreflect.FileDescriptor
const file_app_router_command_command_proto_rawDesc = "" +
"\n" +
" app/router/command/command.proto\x12\x17xray.app.router.command\x1a\x18common/net/network.proto\x1a!common/serial/typed_message.proto\"\xf6\x04\n" +
"\x0eRoutingContext\x12\x1e\n" +
"\n" +
"InboundTag\x18\x01 \x01(\tR\n" +
"InboundTag\x122\n" +
"\aNetwork\x18\x02 \x01(\x0e2\x18.xray.common.net.NetworkR\aNetwork\x12\x1c\n" +
"\tSourceIPs\x18\x03 \x03(\fR\tSourceIPs\x12\x1c\n" +
"\tTargetIPs\x18\x04 \x03(\fR\tTargetIPs\x12\x1e\n" +
"\n" +
"SourcePort\x18\x05 \x01(\rR\n" +
"SourcePort\x12\x1e\n" +
"\n" +
"TargetPort\x18\x06 \x01(\rR\n" +
"TargetPort\x12\"\n" +
"\fTargetDomain\x18\a \x01(\tR\fTargetDomain\x12\x1a\n" +
"\bProtocol\x18\b \x01(\tR\bProtocol\x12\x12\n" +
"\x04User\x18\t \x01(\tR\x04User\x12W\n" +
"\n" +
"Attributes\x18\n" +
" \x03(\v27.xray.app.router.command.RoutingContext.AttributesEntryR\n" +
"Attributes\x12,\n" +
"\x11OutboundGroupTags\x18\v \x03(\tR\x11OutboundGroupTags\x12 \n" +
"\vOutboundTag\x18\f \x01(\tR\vOutboundTag\x12\x1a\n" +
"\bLocalIPs\x18\r \x03(\fR\bLocalIPs\x12\x1c\n" +
"\tLocalPort\x18\x0e \x01(\rR\tLocalPort\x12\x1e\n" +
"\n" +
"VlessRoute\x18\x0f \x01(\rR\n" +
"VlessRoute\x1a=\n" +
"\x0fAttributesEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"F\n" +
"\x1cSubscribeRoutingStatsRequest\x12&\n" +
"\x0eFieldSelectors\x18\x01 \x03(\tR\x0eFieldSelectors\"\xb1\x01\n" +
"\x10TestRouteRequest\x12O\n" +
"\x0eRoutingContext\x18\x01 \x01(\v2'.xray.app.router.command.RoutingContextR\x0eRoutingContext\x12&\n" +
"\x0eFieldSelectors\x18\x02 \x03(\tR\x0eFieldSelectors\x12$\n" +
"\rPublishResult\x18\x03 \x01(\bR\rPublishResult\"'\n" +
"\x13PrincipleTargetInfo\x12\x10\n" +
"\x03tag\x18\x01 \x03(\tR\x03tag\"&\n" +
"\fOverrideInfo\x12\x16\n" +
"\x06target\x18\x02 \x01(\tR\x06target\"\xa9\x01\n" +
"\vBalancerMsg\x12A\n" +
"\boverride\x18\x05 \x01(\v2%.xray.app.router.command.OverrideInfoR\boverride\x12W\n" +
"\x10principle_target\x18\x06 \x01(\v2,.xray.app.router.command.PrincipleTargetInfoR\x0fprincipleTarget\"*\n" +
"\x16GetBalancerInfoRequest\x12\x10\n" +
"\x03tag\x18\x01 \x01(\tR\x03tag\"[\n" +
"\x17GetBalancerInfoResponse\x12@\n" +
"\bbalancer\x18\x01 \x01(\v2$.xray.app.router.command.BalancerMsgR\bbalancer\"Y\n" +
"\x1dOverrideBalancerTargetRequest\x12 \n" +
"\vbalancerTag\x18\x01 \x01(\tR\vbalancerTag\x12\x16\n" +
"\x06target\x18\x02 \x01(\tR\x06target\" \n" +
"\x1eOverrideBalancerTargetResponse\"n\n" +
"\x0eAddRuleRequest\x128\n" +
"\x06config\x18\x01 \x01(\v2 .xray.common.serial.TypedMessageR\x06config\x12\"\n" +
"\fshouldAppend\x18\x02 \x01(\bR\fshouldAppend\"\x11\n" +
"\x0fAddRuleResponse\"-\n" +
"\x11RemoveRuleRequest\x12\x18\n" +
"\aruleTag\x18\x01 \x01(\tR\aruleTag\"\x14\n" +
"\x12RemoveRuleResponse\"\x11\n" +
"\x0fListRuleRequest\":\n" +
"\fListRuleItem\x12\x10\n" +
"\x03tag\x18\x01 \x01(\tR\x03tag\x12\x18\n" +
"\aruleTag\x18\x02 \x01(\tR\aruleTag\"O\n" +
"\x10ListRuleResponse\x12;\n" +
"\x05rules\x18\x01 \x03(\v2%.xray.app.router.command.ListRuleItemR\x05rules\"\b\n" +
"\x06Config2\xa2\x06\n" +
"\x0eRoutingService\x12{\n" +
"\x15SubscribeRoutingStats\x125.xray.app.router.command.SubscribeRoutingStatsRequest\x1a'.xray.app.router.command.RoutingContext\"\x000\x01\x12a\n" +
"\tTestRoute\x12).xray.app.router.command.TestRouteRequest\x1a'.xray.app.router.command.RoutingContext\"\x00\x12v\n" +
"\x0fGetBalancerInfo\x12/.xray.app.router.command.GetBalancerInfoRequest\x1a0.xray.app.router.command.GetBalancerInfoResponse\"\x00\x12\x8b\x01\n" +
"\x16OverrideBalancerTarget\x126.xray.app.router.command.OverrideBalancerTargetRequest\x1a7.xray.app.router.command.OverrideBalancerTargetResponse\"\x00\x12^\n" +
"\aAddRule\x12'.xray.app.router.command.AddRuleRequest\x1a(.xray.app.router.command.AddRuleResponse\"\x00\x12g\n" +
"\n" +
"RemoveRule\x12*.xray.app.router.command.RemoveRuleRequest\x1a+.xray.app.router.command.RemoveRuleResponse\"\x00\x12a\n" +
"\bListRule\x12(.xray.app.router.command.ListRuleRequest\x1a).xray.app.router.command.ListRuleResponse\"\x00Bg\n" +
"\x1bcom.xray.app.router.commandP\x01Z,github.com/xtls/xray-core/app/router/command\xaa\x02\x17Xray.App.Router.Commandb\x06proto3"
var file_app_router_command_command_proto_rawDesc = []byte{
0x0a, 0x20, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6d,
0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x12, 0x17, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75,
0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x18, 0x63, 0x6f, 0x6d,
0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x73, 0x65,
0x72, 0x69, 0x61, 0x6c, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf6, 0x04, 0x0a, 0x0e, 0x52, 0x6f, 0x75,
0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x49,
0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0a, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x32, 0x0a, 0x07, 0x4e,
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e,
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12,
0x1c, 0x0a, 0x09, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x50, 0x73, 0x18, 0x03, 0x20, 0x03,
0x28, 0x0c, 0x52, 0x09, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x50, 0x73, 0x12, 0x1c, 0x0a,
0x09, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x50, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c,
0x52, 0x09, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x50, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x53,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x54,
0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x0a, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x54,
0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0c, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12,
0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x55,
0x73, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12,
0x57, 0x0a, 0x0a, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x0a, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x6f,
0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x41, 0x74, 0x74,
0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x41, 0x74,
0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x62,
0x6f, 0x75, 0x6e, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x18, 0x0b, 0x20,
0x03, 0x28, 0x09, 0x52, 0x11, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x47, 0x72, 0x6f,
0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75,
0x6e, 0x64, 0x54, 0x61, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4f, 0x75, 0x74,
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61,
0x6c, 0x49, 0x50, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x4c, 0x6f, 0x63, 0x61,
0x6c, 0x49, 0x50, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f, 0x72,
0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x6f,
0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f, 0x75, 0x74, 0x65,
0x18, 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x52, 0x6f, 0x75,
0x74, 0x65, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
0x01, 0x22, 0x46, 0x0a, 0x1c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x6f,
0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64,
0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x22, 0xb1, 0x01, 0x0a, 0x10, 0x54, 0x65,
0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f,
0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52,
0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12,
0x26, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72,
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65,
0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69,
0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d,
0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x27, 0x0a,
0x13, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74,
0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28,
0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x26, 0x0a, 0x0c, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69,
0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0xa9,
0x01, 0x0a, 0x0b, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x4d, 0x73, 0x67, 0x12, 0x41,
0x0a, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72,
0x69, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64,
0x65, 0x12, 0x57, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x74,
0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x78, 0x72,
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x6c, 0x65, 0x54,
0x61, 0x72, 0x67, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x6e, 0x63,
0x69, 0x70, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x2a, 0x0a, 0x16, 0x47, 0x65,
0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x5b, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c,
0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x40, 0x0a, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x42, 0x61,
0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x4d, 0x73, 0x67, 0x52, 0x08, 0x62, 0x61, 0x6c, 0x61, 0x6e,
0x63, 0x65, 0x72, 0x22, 0x59, 0x0a, 0x1d, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42,
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72,
0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x61, 0x6c, 0x61, 0x6e,
0x63, 0x65, 0x72, 0x54, 0x61, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x22, 0x20,
0x0a, 0x1e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x6e, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x4d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x22, 0x0a, 0x0c,
0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x08, 0x52, 0x0c, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64,
0x22, 0x11, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x2d, 0x0a, 0x11, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65,
0x54, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x54,
0x61, 0x67, 0x22, 0x14, 0x0a, 0x12, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x32, 0xbf, 0x05, 0x0a, 0x0e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7b, 0x0a, 0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
0x62, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x35,
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
0x62, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e,
0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x00,
0x30, 0x01, 0x12, 0x61, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12,
0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52, 0x6f,
0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x78, 0x72, 0x61,
0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74,
0x65, 0x78, 0x74, 0x22, 0x00, 0x12, 0x76, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61,
0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49, 0x6e,
0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x49,
0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x8b, 0x01,
0x0a, 0x16, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63,
0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x36, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61,
0x6e, 0x64, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e,
0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x37, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x72,
0x69, 0x64, 0x65, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x07, 0x41,
0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
0x2e, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,
0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x75, 0x6c,
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x67, 0x0a, 0x0a, 0x52,
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e,
0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x00, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
0x2f, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6d, 0x6d,
0x61, 0x6e, 0x64, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x52,
0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_app_router_command_command_proto_rawDescOnce sync.Once
file_app_router_command_command_proto_rawDescData []byte
file_app_router_command_command_proto_rawDescData = file_app_router_command_command_proto_rawDesc
)
func file_app_router_command_command_proto_rawDescGZIP() []byte {
file_app_router_command_command_proto_rawDescOnce.Do(func() {
file_app_router_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_app_router_command_command_proto_rawDesc), len(file_app_router_command_command_proto_rawDesc)))
file_app_router_command_command_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_router_command_command_proto_rawDescData)
})
return file_app_router_command_command_proto_rawDescData
}
var file_app_router_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
var file_app_router_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 16)
var file_app_router_command_command_proto_goTypes = []any{
(*RoutingContext)(nil), // 0: xray.app.router.command.RoutingContext
(*SubscribeRoutingStatsRequest)(nil), // 1: xray.app.router.command.SubscribeRoutingStatsRequest
@@ -1072,42 +1034,36 @@ var file_app_router_command_command_proto_goTypes = []any{
(*AddRuleResponse)(nil), // 11: xray.app.router.command.AddRuleResponse
(*RemoveRuleRequest)(nil), // 12: xray.app.router.command.RemoveRuleRequest
(*RemoveRuleResponse)(nil), // 13: xray.app.router.command.RemoveRuleResponse
(*ListRuleRequest)(nil), // 14: xray.app.router.command.ListRuleRequest
(*ListRuleItem)(nil), // 15: xray.app.router.command.ListRuleItem
(*ListRuleResponse)(nil), // 16: xray.app.router.command.ListRuleResponse
(*Config)(nil), // 17: xray.app.router.command.Config
nil, // 18: xray.app.router.command.RoutingContext.AttributesEntry
(net.Network)(0), // 19: xray.common.net.Network
(*serial.TypedMessage)(nil), // 20: xray.common.serial.TypedMessage
(*Config)(nil), // 14: xray.app.router.command.Config
nil, // 15: xray.app.router.command.RoutingContext.AttributesEntry
(net.Network)(0), // 16: xray.common.net.Network
(*serial.TypedMessage)(nil), // 17: xray.common.serial.TypedMessage
}
var file_app_router_command_command_proto_depIdxs = []int32{
19, // 0: xray.app.router.command.RoutingContext.Network:type_name -> xray.common.net.Network
18, // 1: xray.app.router.command.RoutingContext.Attributes:type_name -> xray.app.router.command.RoutingContext.AttributesEntry
16, // 0: xray.app.router.command.RoutingContext.Network:type_name -> xray.common.net.Network
15, // 1: xray.app.router.command.RoutingContext.Attributes:type_name -> xray.app.router.command.RoutingContext.AttributesEntry
0, // 2: xray.app.router.command.TestRouteRequest.RoutingContext:type_name -> xray.app.router.command.RoutingContext
4, // 3: xray.app.router.command.BalancerMsg.override:type_name -> xray.app.router.command.OverrideInfo
3, // 4: xray.app.router.command.BalancerMsg.principle_target:type_name -> xray.app.router.command.PrincipleTargetInfo
5, // 5: xray.app.router.command.GetBalancerInfoResponse.balancer:type_name -> xray.app.router.command.BalancerMsg
20, // 6: xray.app.router.command.AddRuleRequest.config:type_name -> xray.common.serial.TypedMessage
15, // 7: xray.app.router.command.ListRuleResponse.rules:type_name -> xray.app.router.command.ListRuleItem
1, // 8: xray.app.router.command.RoutingService.SubscribeRoutingStats:input_type -> xray.app.router.command.SubscribeRoutingStatsRequest
2, // 9: xray.app.router.command.RoutingService.TestRoute:input_type -> xray.app.router.command.TestRouteRequest
6, // 10: xray.app.router.command.RoutingService.GetBalancerInfo:input_type -> xray.app.router.command.GetBalancerInfoRequest
8, // 11: xray.app.router.command.RoutingService.OverrideBalancerTarget:input_type -> xray.app.router.command.OverrideBalancerTargetRequest
10, // 12: xray.app.router.command.RoutingService.AddRule:input_type -> xray.app.router.command.AddRuleRequest
12, // 13: xray.app.router.command.RoutingService.RemoveRule:input_type -> xray.app.router.command.RemoveRuleRequest
14, // 14: xray.app.router.command.RoutingService.ListRule:input_type -> xray.app.router.command.ListRuleRequest
0, // 15: xray.app.router.command.RoutingService.SubscribeRoutingStats:output_type -> xray.app.router.command.RoutingContext
0, // 16: xray.app.router.command.RoutingService.TestRoute:output_type -> xray.app.router.command.RoutingContext
7, // 17: xray.app.router.command.RoutingService.GetBalancerInfo:output_type -> xray.app.router.command.GetBalancerInfoResponse
9, // 18: xray.app.router.command.RoutingService.OverrideBalancerTarget:output_type -> xray.app.router.command.OverrideBalancerTargetResponse
11, // 19: xray.app.router.command.RoutingService.AddRule:output_type -> xray.app.router.command.AddRuleResponse
13, // 20: xray.app.router.command.RoutingService.RemoveRule:output_type -> xray.app.router.command.RemoveRuleResponse
16, // 21: xray.app.router.command.RoutingService.ListRule:output_type -> xray.app.router.command.ListRuleResponse
15, // [15:22] is the sub-list for method output_type
8, // [8:15] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
17, // 6: xray.app.router.command.AddRuleRequest.config:type_name -> xray.common.serial.TypedMessage
1, // 7: xray.app.router.command.RoutingService.SubscribeRoutingStats:input_type -> xray.app.router.command.SubscribeRoutingStatsRequest
2, // 8: xray.app.router.command.RoutingService.TestRoute:input_type -> xray.app.router.command.TestRouteRequest
6, // 9: xray.app.router.command.RoutingService.GetBalancerInfo:input_type -> xray.app.router.command.GetBalancerInfoRequest
8, // 10: xray.app.router.command.RoutingService.OverrideBalancerTarget:input_type -> xray.app.router.command.OverrideBalancerTargetRequest
10, // 11: xray.app.router.command.RoutingService.AddRule:input_type -> xray.app.router.command.AddRuleRequest
12, // 12: xray.app.router.command.RoutingService.RemoveRule:input_type -> xray.app.router.command.RemoveRuleRequest
0, // 13: xray.app.router.command.RoutingService.SubscribeRoutingStats:output_type -> xray.app.router.command.RoutingContext
0, // 14: xray.app.router.command.RoutingService.TestRoute:output_type -> xray.app.router.command.RoutingContext
7, // 15: xray.app.router.command.RoutingService.GetBalancerInfo:output_type -> xray.app.router.command.GetBalancerInfoResponse
9, // 16: xray.app.router.command.RoutingService.OverrideBalancerTarget:output_type -> xray.app.router.command.OverrideBalancerTargetResponse
11, // 17: xray.app.router.command.RoutingService.AddRule:output_type -> xray.app.router.command.AddRuleResponse
13, // 18: xray.app.router.command.RoutingService.RemoveRule:output_type -> xray.app.router.command.RemoveRuleResponse
13, // [13:19] is the sub-list for method output_type
7, // [7:13] is the sub-list for method input_type
7, // [7:7] is the sub-list for extension type_name
7, // [7:7] is the sub-list for extension extendee
0, // [0:7] is the sub-list for field type_name
}
func init() { file_app_router_command_command_proto_init() }
@@ -1119,9 +1075,9 @@ func file_app_router_command_command_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_app_router_command_command_proto_rawDesc), len(file_app_router_command_command_proto_rawDesc)),
RawDescriptor: file_app_router_command_command_proto_rawDesc,
NumEnums: 0,
NumMessages: 19,
NumMessages: 16,
NumExtensions: 0,
NumServices: 1,
},
@@ -1130,6 +1086,7 @@ func file_app_router_command_command_proto_init() {
MessageInfos: file_app_router_command_command_proto_msgTypes,
}.Build()
File_app_router_command_command_proto = out.File
file_app_router_command_command_proto_rawDesc = nil
file_app_router_command_command_proto_goTypes = nil
file_app_router_command_command_proto_depIdxs = nil
}
-13
View File
@@ -104,17 +104,6 @@ message RemoveRuleRequest {
message RemoveRuleResponse {}
message ListRuleRequest {}
message ListRuleItem {
string tag = 1;
string ruleTag = 2;
}
message ListRuleResponse{
repeated ListRuleItem rules = 1;
}
service RoutingService {
rpc SubscribeRoutingStats(SubscribeRoutingStatsRequest)
returns (stream RoutingContext) {}
@@ -125,8 +114,6 @@ service RoutingService {
rpc AddRule(AddRuleRequest) returns (AddRuleResponse) {}
rpc RemoveRule(RemoveRuleRequest) returns (RemoveRuleResponse) {}
rpc ListRule(ListRuleRequest) returns (ListRuleResponse) {}
}
message Config {}
+9 -47
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.0
// - protoc v6.33.2
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.28.2
// source: app/router/command/command.proto
package command
@@ -25,7 +25,6 @@ const (
RoutingService_OverrideBalancerTarget_FullMethodName = "/xray.app.router.command.RoutingService/OverrideBalancerTarget"
RoutingService_AddRule_FullMethodName = "/xray.app.router.command.RoutingService/AddRule"
RoutingService_RemoveRule_FullMethodName = "/xray.app.router.command.RoutingService/RemoveRule"
RoutingService_ListRule_FullMethodName = "/xray.app.router.command.RoutingService/ListRule"
)
// RoutingServiceClient is the client API for RoutingService service.
@@ -38,7 +37,6 @@ type RoutingServiceClient interface {
OverrideBalancerTarget(ctx context.Context, in *OverrideBalancerTargetRequest, opts ...grpc.CallOption) (*OverrideBalancerTargetResponse, error)
AddRule(ctx context.Context, in *AddRuleRequest, opts ...grpc.CallOption) (*AddRuleResponse, error)
RemoveRule(ctx context.Context, in *RemoveRuleRequest, opts ...grpc.CallOption) (*RemoveRuleResponse, error)
ListRule(ctx context.Context, in *ListRuleRequest, opts ...grpc.CallOption) (*ListRuleResponse, error)
}
type routingServiceClient struct {
@@ -118,16 +116,6 @@ func (c *routingServiceClient) RemoveRule(ctx context.Context, in *RemoveRuleReq
return out, nil
}
func (c *routingServiceClient) ListRule(ctx context.Context, in *ListRuleRequest, opts ...grpc.CallOption) (*ListRuleResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListRuleResponse)
err := c.cc.Invoke(ctx, RoutingService_ListRule_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// RoutingServiceServer is the server API for RoutingService service.
// All implementations must embed UnimplementedRoutingServiceServer
// for forward compatibility.
@@ -138,7 +126,6 @@ type RoutingServiceServer interface {
OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error)
AddRule(context.Context, *AddRuleRequest) (*AddRuleResponse, error)
RemoveRule(context.Context, *RemoveRuleRequest) (*RemoveRuleResponse, error)
ListRule(context.Context, *ListRuleRequest) (*ListRuleResponse, error)
mustEmbedUnimplementedRoutingServiceServer()
}
@@ -150,25 +137,22 @@ type RoutingServiceServer interface {
type UnimplementedRoutingServiceServer struct{}
func (UnimplementedRoutingServiceServer) SubscribeRoutingStats(*SubscribeRoutingStatsRequest, grpc.ServerStreamingServer[RoutingContext]) error {
return status.Error(codes.Unimplemented, "method SubscribeRoutingStats not implemented")
return status.Errorf(codes.Unimplemented, "method SubscribeRoutingStats not implemented")
}
func (UnimplementedRoutingServiceServer) TestRoute(context.Context, *TestRouteRequest) (*RoutingContext, error) {
return nil, status.Error(codes.Unimplemented, "method TestRoute not implemented")
return nil, status.Errorf(codes.Unimplemented, "method TestRoute not implemented")
}
func (UnimplementedRoutingServiceServer) GetBalancerInfo(context.Context, *GetBalancerInfoRequest) (*GetBalancerInfoResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetBalancerInfo not implemented")
return nil, status.Errorf(codes.Unimplemented, "method GetBalancerInfo not implemented")
}
func (UnimplementedRoutingServiceServer) OverrideBalancerTarget(context.Context, *OverrideBalancerTargetRequest) (*OverrideBalancerTargetResponse, error) {
return nil, status.Error(codes.Unimplemented, "method OverrideBalancerTarget not implemented")
return nil, status.Errorf(codes.Unimplemented, "method OverrideBalancerTarget not implemented")
}
func (UnimplementedRoutingServiceServer) AddRule(context.Context, *AddRuleRequest) (*AddRuleResponse, error) {
return nil, status.Error(codes.Unimplemented, "method AddRule not implemented")
return nil, status.Errorf(codes.Unimplemented, "method AddRule not implemented")
}
func (UnimplementedRoutingServiceServer) RemoveRule(context.Context, *RemoveRuleRequest) (*RemoveRuleResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RemoveRule not implemented")
}
func (UnimplementedRoutingServiceServer) ListRule(context.Context, *ListRuleRequest) (*ListRuleResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListRule not implemented")
return nil, status.Errorf(codes.Unimplemented, "method RemoveRule not implemented")
}
func (UnimplementedRoutingServiceServer) mustEmbedUnimplementedRoutingServiceServer() {}
func (UnimplementedRoutingServiceServer) testEmbeddedByValue() {}
@@ -181,7 +165,7 @@ type UnsafeRoutingServiceServer interface {
}
func RegisterRoutingServiceServer(s grpc.ServiceRegistrar, srv RoutingServiceServer) {
// If the following call panics, it indicates UnimplementedRoutingServiceServer was
// If the following call pancis, it indicates UnimplementedRoutingServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
@@ -292,24 +276,6 @@ func _RoutingService_RemoveRule_Handler(srv interface{}, ctx context.Context, de
return interceptor(ctx, in, info, handler)
}
func _RoutingService_ListRule_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListRuleRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RoutingServiceServer).ListRule(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: RoutingService_ListRule_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RoutingServiceServer).ListRule(ctx, req.(*ListRuleRequest))
}
return interceptor(ctx, in, info, handler)
}
// RoutingService_ServiceDesc is the grpc.ServiceDesc for RoutingService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -337,10 +303,6 @@ var RoutingService_ServiceDesc = grpc.ServiceDesc{
MethodName: "RemoveRule",
Handler: _RoutingService_RemoveRule_Handler,
},
{
MethodName: "ListRule",
Handler: _RoutingService_ListRule_Handler,
},
},
Streams: []grpc.StreamDesc{
{
+43 -4
View File
@@ -309,6 +309,48 @@ func (m *AttributeMatcher) Apply(ctx routing.Context) bool {
return m.Match(attributes)
}
// Geo attribute
type GeoAttributeMatcher interface {
Match(*Domain) bool
}
type GeoBooleanMatcher string
func (m GeoBooleanMatcher) Match(domain *Domain) bool {
for _, attr := range domain.Attribute {
if attr.Key == string(m) {
return true
}
}
return false
}
type GeoAttributeList struct {
Matcher []GeoAttributeMatcher
}
func (al *GeoAttributeList) Match(domain *Domain) bool {
for _, matcher := range al.Matcher {
if !matcher.Match(domain) {
return false
}
}
return true
}
func (al *GeoAttributeList) IsEmpty() bool {
return len(al.Matcher) == 0
}
func ParseAttrs(attrs []string) *GeoAttributeList {
al := new(GeoAttributeList)
for _, attr := range attrs {
lc := strings.ToLower(attr)
al.Matcher = append(al.Matcher, GeoBooleanMatcher(lc))
}
return al
}
type ProcessNameMatcher struct {
ProcessNames []string
AbsPaths []string
@@ -358,9 +400,6 @@ func NewProcessNameMatcher(names []string) *ProcessNameMatcher {
}
func (m *ProcessNameMatcher) Apply(ctx routing.Context) bool {
if len(ctx.GetSourceIPs()) == 0 {
return false
}
srcPort := ctx.GetSourcePort().String()
srcIP := ctx.GetSourceIPs()[0].String()
var network string
@@ -400,4 +439,4 @@ func (m *ProcessNameMatcher) Apply(ctx routing.Context) bool {
}
}
return false
}
}
+27 -56
View File
@@ -1,40 +1,17 @@
package router_test
import (
"fmt"
"os"
"path/filepath"
"runtime"
"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"
"github.com/xtls/xray-core/infra/conf"
)
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},
@@ -182,12 +159,11 @@ func TestGeoIPReverseMatcher(t *testing.T) {
}
func TestGeoIPMatcher4CN(t *testing.T) {
ips, err := loadGeoIP("CN")
geo := "geoip:cn"
geoip, err := loadGeoIP(geo)
common.Must(err)
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
Cidr: ips,
})
matcher, err := router.BuildOptimizedGeoIPMatcher(geoip)
common.Must(err)
if matcher.Match([]byte{8, 8, 8, 8}) {
@@ -196,12 +172,11 @@ func TestGeoIPMatcher4CN(t *testing.T) {
}
func TestGeoIPMatcher6US(t *testing.T) {
ips, err := loadGeoIP("US")
geo := "geoip:us"
geoip, err := loadGeoIP(geo)
common.Must(err)
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
Cidr: ips,
})
matcher, err := router.BuildOptimizedGeoIPMatcher(geoip)
common.Must(err)
if !matcher.Match(net.ParseAddress("2001:4860:4860::8888").IP()) {
@@ -209,37 +184,34 @@ func TestGeoIPMatcher6US(t *testing.T) {
}
}
func loadGeoIP(country string) ([]*router.CIDR, error) {
path, err := getAssetPath("geoip.dat")
if err != nil {
return nil, err
}
geoipBytes, err := filesystem.ReadFile(path)
func loadGeoIP(geo string) (*router.GeoIP, error) {
os.Setenv("XRAY_LOCATION_ASSET", filepath.Join("..", "..", "resources"))
geoip, err := conf.ToCidrList([]string{geo})
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
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
geoip, err = router.GetGeoIPList(geoip)
if err != nil {
return nil, err
}
}
panic("country not found: " + country)
if len(geoip) == 0 {
panic("country not found: " + geo)
}
return geoip[0], nil
}
func BenchmarkGeoIPMatcher4CN(b *testing.B) {
ips, err := loadGeoIP("CN")
geo := "geoip:cn"
geoip, err := loadGeoIP(geo)
common.Must(err)
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
Cidr: ips,
})
matcher, err := router.BuildOptimizedGeoIPMatcher(geoip)
common.Must(err)
b.ResetTimer()
@@ -250,12 +222,11 @@ func BenchmarkGeoIPMatcher4CN(b *testing.B) {
}
func BenchmarkGeoIPMatcher6US(b *testing.B) {
ips, err := loadGeoIP("US")
geo := "geoip:us"
geoip, err := loadGeoIP(geo)
common.Must(err)
matcher, err := router.BuildOptimizedGeoIPMatcher(&router.GeoIP{
Cidr: ips,
})
matcher, err := router.BuildOptimizedGeoIPMatcher(geoip)
common.Must(err)
b.ResetTimer()
+65 -28
View File
@@ -1,20 +1,22 @@
package router_test
import (
"os"
"path/filepath"
"runtime"
"strconv"
"testing"
"github.com/xtls/xray-core/app/router"
. "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/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"
"github.com/xtls/xray-core/infra/conf"
)
func withBackground() routing.Context {
@@ -300,32 +302,25 @@ 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)
func loadGeoSiteDomains(geo string) ([]*Domain, error) {
os.Setenv("XRAY_LOCATION_ASSET", filepath.Join("..", "..", "resources"))
domains, err := conf.ParseDomainRule(geo)
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
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
domains, err = router.GetDomainList(domains)
if err != nil {
return nil, err
}
}
return nil, errors.New("country not found: " + country)
return domains, nil
}
func TestChinaSites(t *testing.T) {
domains, err := loadGeoSite("CN")
domains, err := loadGeoSiteDomains("geosite:cn")
common.Must(err)
acMatcher, err := NewMphMatcherGroup(domains)
@@ -366,8 +361,50 @@ func TestChinaSites(t *testing.T) {
}
}
func TestChinaSitesWithAttrs(t *testing.T) {
domains, err := loadGeoSiteDomains("geosite:google@cn")
common.Must(err)
acMatcher, err := NewMphMatcherGroup(domains)
common.Must(err)
type TestCase struct {
Domain string
Output bool
}
testCases := []TestCase{
{
Domain: "google.cn",
Output: true,
},
{
Domain: "recaptcha.net",
Output: true,
},
{
Domain: "164.com",
Output: false,
},
{
Domain: "164.com",
Output: false,
},
}
for i := 0; i < 1024; i++ {
testCases = append(testCases, TestCase{Domain: strconv.Itoa(i) + ".not-exists.com", Output: false})
}
for _, testCase := range testCases {
r := acMatcher.ApplyDomain(testCase.Domain)
if r != testCase.Output {
t.Error("ACDomainMatcher expected output ", testCase.Output, " for domain ", testCase.Domain, " but got ", r)
}
}
}
func BenchmarkMphDomainMatcher(b *testing.B) {
domains, err := loadGeoSite("CN")
domains, err := loadGeoSiteDomains("geosite:cn")
common.Must(err)
matcher, err := NewMphMatcherGroup(domains)
@@ -412,11 +449,11 @@ func BenchmarkMultiGeoIPMatcher(b *testing.B) {
var geoips []*GeoIP
{
ips, err := loadGeoIP("CN")
ips, err := loadGeoIP("geoip:cn")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "CN",
Cidr: ips,
Cidr: ips.Cidr,
})
}
@@ -425,25 +462,25 @@ func BenchmarkMultiGeoIPMatcher(b *testing.B) {
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "JP",
Cidr: ips,
Cidr: ips.Cidr,
})
}
{
ips, err := loadGeoIP("CA")
ips, err := loadGeoIP("geoip:ca")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "CA",
Cidr: ips,
Cidr: ips.Cidr,
})
}
{
ips, err := loadGeoIP("US")
ips, err := loadGeoIP("geoip:us")
common.Must(err)
geoips = append(geoips, &GeoIP{
CountryCode: "US",
Cidr: ips,
Cidr: ips.Cidr,
})
}
+100 -3
View File
@@ -3,11 +3,14 @@ package router
import (
"context"
"regexp"
"runtime"
"strings"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/platform/filesystem"
"github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/features/routing"
"google.golang.org/protobuf/proto"
)
type Rule struct {
@@ -73,7 +76,15 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
}
if len(rr.Geoip) > 0 {
cond, err := NewIPMatcher(rr.Geoip, MatcherAsType_Target)
geoip := rr.Geoip
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
var err error
geoip, err = GetGeoIPList(rr.Geoip)
if err != nil {
return nil, errors.New("failed to build geoip from mmap").Base(err)
}
}
cond, err := NewIPMatcher(geoip, MatcherAsType_Target)
if err != nil {
return nil, err
}
@@ -98,11 +109,20 @@ func (rr *RoutingRule) BuildCondition() (Condition, error) {
}
if len(rr.Domain) > 0 {
matcher, err := NewMphMatcherGroup(rr.Domain)
domains := rr.Domain
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
var err error
domains, err = GetDomainList(rr.Domain)
if err != nil {
return nil, errors.New("failed to build domains from mmap").Base(err)
}
}
matcher, err := NewMphMatcherGroup(domains)
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)")
errors.LogDebug(context.Background(), "MphDomainMatcher is enabled for ", len(domains), " domain rule(s)")
conds.Add(matcher)
}
@@ -163,3 +183,80 @@ func (br *BalancingRule) Build(ohm outbound.Manager, dispatcher routing.Dispatch
return nil, errors.New("unrecognized balancer type")
}
}
func GetGeoIPList(ips []*GeoIP) ([]*GeoIP, error) {
geoipList := []*GeoIP{}
for _, ip := range ips {
if ip.CountryCode != "" {
val := strings.Split(ip.CountryCode, "_")
fileName := "geoip.dat"
if len(val) == 2 {
fileName = strings.ToLower(val[0])
}
bs, err := filesystem.ReadAsset(fileName)
if err != nil {
return nil, errors.New("failed to load file: ", fileName).Base(err)
}
bs = filesystem.Find(bs, []byte(ip.CountryCode))
var geoip GeoIP
if err := proto.Unmarshal(bs, &geoip); err != nil {
return nil, errors.New("failed Unmarshal :").Base(err)
}
geoipList = append(geoipList, &geoip)
} else {
geoipList = append(geoipList, ip)
}
}
return geoipList, nil
}
func GetDomainList(domains []*Domain) ([]*Domain, error) {
domainList := []*Domain{}
for _, domain := range domains {
val := strings.Split(domain.Value, "_")
if len(val) >= 2 {
fileName := val[0]
code := val[1]
bs, err := filesystem.ReadAsset(fileName)
if err != nil {
return nil, errors.New("failed to load file: ", fileName).Base(err)
}
bs = filesystem.Find(bs, []byte(code))
var geosite GeoSite
if err := proto.Unmarshal(bs, &geosite); err != nil {
return nil, errors.New("failed Unmarshal :").Base(err)
}
// parse attr
if len(val) == 3 {
siteWithAttr := strings.Split(val[2], ",")
attrs := ParseAttrs(siteWithAttr)
if !attrs.IsEmpty() {
filteredDomains := make([]*Domain, 0, len(domains))
for _, domain := range geosite.Domain {
if attrs.Match(domain) {
filteredDomains = append(filteredDomains, domain)
}
}
geosite.Domain = filteredDomains
}
}
domainList = append(domainList, geosite.Domain...)
} else {
domainList = append(domainList, domain)
}
}
return domainList, nil
}
+1 -16
View File
@@ -2,7 +2,7 @@ package router
import (
"context"
"sync"
sync "sync"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
@@ -181,21 +181,6 @@ func (r *Router) RemoveRule(tag string) error {
return errors.New("empty tag name!")
}
// ListRule implements routing.Router
func (r *Router) ListRule() []routing.Route {
r.mu.Lock()
defer r.mu.Unlock()
ruleList := make([]routing.Route, 0)
for _, rule := range r.rules {
ruleList = append(ruleList, &Route{
outboundTag: rule.Tag,
ruleTag: rule.RuleTag,
})
}
return ruleList
}
func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) {
// SkipDNSResolve is set from DNS module.
// the DOH remote server maybe a domain name,
+8 -5
View File
@@ -4,19 +4,22 @@ import (
"context"
)
// PrintNonRemovalDeprecatedFeatureWarning prints a warning of the deprecated feature that won't be removed in the near future.
// PrintMigrateFeatureInfo prints a notice of the upcoming feature migration.
// Place it after the source feature related config file pharser code.
// Important note: Only use this when the target migrating feature is under construction.
// Important note: Even when the target migrating feature has finished its construction, this notice can still be used yet before announcing deprecation of the old feature.
// Do not remove this function even there is no reference to it.
func PrintNonRemovalDeprecatedFeatureWarning(sourceFeature string, targetFeature string) {
LogWarning(context.Background(), "The feature "+sourceFeature+" is deprecated, not recommended for using and might be removed. Please migrate to "+targetFeature+" as soon as possible.")
func PrintMigrateFeatureInfo(sourceFeature string, targetFeature string) {
LogInfo(context.Background(), "The feature "+sourceFeature+" will be migrated to "+targetFeature+" in the future.")
}
// PrintDeprecatedFeatureWarning prints a warning for deprecated and going to be removed feature.
// Do not remove this function even there is no reference to it.
func PrintDeprecatedFeatureWarning(feature string, migrateFeature string) {
if len(migrateFeature) > 0 {
LogWarning(context.Background(), "This feature "+feature+" is deprecated, will be removed soon and being migrated to "+migrateFeature+". Please update your config(s) according to release note and documentation before removal.")
LogWarning(context.Background(), "This feature "+feature+" is deprecated and being migrated to "+migrateFeature+". Please update your config(s) according to release note and documentation before removal.")
} else {
LogWarning(context.Background(), "This feature "+feature+" is deprecated and will be removed soon. Please update your config(s) according to release note and documentation before removal.")
LogWarning(context.Background(), "This feature "+feature+" is deprecated. Please update your config(s) according to release note and documentation before removal.")
}
}
+52
View File
@@ -0,0 +1,52 @@
package filesystem
func DecodeVarint(buf []byte) (x uint64, n int) {
for shift := uint(0); shift < 64; shift += 7 {
if n >= len(buf) {
return 0, 0
}
b := uint64(buf[n])
n++
x |= (b & 0x7F) << shift
if (b & 0x80) == 0 {
return x, n
}
}
// The number is too large to represent in a 64-bit value.
return 0, 0
}
func Find(data, code []byte) []byte {
codeL := len(code)
if codeL == 0 {
return nil
}
for {
dataL := len(data)
if dataL < 2 {
return nil
}
x, y := DecodeVarint(data[1:])
if x == 0 && y == 0 {
return nil
}
headL, bodyL := 1+y, int(x)
dataL -= headL
if dataL < bodyL {
return nil
}
data = data[headL:]
if int(data[1]) == codeL {
for i := 0; i < codeL && data[2+i] == code[i]; i++ {
if i+1 == codeL {
return data[:bodyL]
}
}
}
if dataL == bodyL {
return nil
}
data = data[bodyL:]
}
}
+26
View File
@@ -1,9 +1,12 @@
//go:build !windows && !wasm
package filesystem
import (
"io"
"os"
"path/filepath"
"syscall"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/platform"
@@ -16,6 +19,29 @@ var NewFileReader FileReaderFunc = func(path string) (io.ReadCloser, error) {
}
func ReadFile(path string) ([]byte, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
stat, err := file.Stat()
if err != nil {
return nil, err
}
size := stat.Size()
if size == 0 {
return []byte{}, nil
}
// use mmap to save RAM
bs, err := syscall.Mmap(int(file.Fd()), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED)
if err == nil {
return bs, nil
}
// fallback
reader, err := NewFileReader(path)
if err != nil {
return nil, err
+54
View File
@@ -0,0 +1,54 @@
//go:build windows || wasm
package filesystem
import (
"io"
"os"
"path/filepath"
"github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/platform"
)
type FileReaderFunc func(path string) (io.ReadCloser, error)
var NewFileReader FileReaderFunc = func(path string) (io.ReadCloser, error) {
return os.Open(path)
}
func ReadFile(path string) ([]byte, error) {
reader, err := NewFileReader(path)
if err != nil {
return nil, err
}
defer reader.Close()
return buf.ReadAllToBytes(reader)
}
func ReadAsset(file string) ([]byte, error) {
return ReadFile(platform.GetAssetLocation(file))
}
func ReadCert(file string) ([]byte, error) {
if filepath.IsAbs(file) {
return ReadFile(file)
}
return ReadFile(platform.GetCertLocation(file))
}
func CopyFile(dst string, src string) error {
bytes, err := ReadFile(src)
if err != nil {
return err
}
f, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return err
}
defer f.Close()
_, err = f.Write(bytes)
return err
}
+4 -1
View File
@@ -3,7 +3,9 @@
package platform
import "path/filepath"
import (
"path/filepath"
)
func LineSeparator() string {
return "\r\n"
@@ -12,6 +14,7 @@ func LineSeparator() string {
// GetAssetLocation searches for `file` in the env dir and the executable dir
func GetAssetLocation(file string) string {
assetPath := NewEnvFlag(AssetLocation).GetValue(getExecutableDir)
return filepath.Join(assetPath, file)
}
+1 -1
View File
@@ -19,7 +19,7 @@ import (
var (
Version_x byte = 26
Version_y byte = 1
Version_z byte = 23
Version_z byte = 13
)
var (
-6
View File
@@ -16,7 +16,6 @@ type Router interface {
PickRoute(ctx Context) (Route, error)
AddRule(config *serial.TypedMessage, shouldAppend bool) error
RemoveRule(tag string) error
ListRule() []Route
}
// Route is the routing result of Router feature.
@@ -66,11 +65,6 @@ func (DefaultRouter) RemoveRule(tag string) error {
return common.ErrNoClue
}
// ListRule implements Router.
func (DefaultRouter) ListRule() []Route {
return nil
}
// Start implements common.Runnable.
func (DefaultRouter) Start() error {
return nil
+4 -4
View File
@@ -1,17 +1,17 @@
module github.com/xtls/xray-core
go 1.25.6
go 1.25.5
require (
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178
github.com/cloudflare/circl v1.6.3
github.com/cloudflare/circl v1.6.2
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
github.com/golang/mock v1.7.0-rc.1
github.com/google/go-cmp v0.7.0
github.com/gorilla/websocket v1.5.3
github.com/miekg/dns v1.1.72
github.com/miekg/dns v1.1.70
github.com/pelletier/go-toml v1.9.5
github.com/pires/go-proxyproto v0.9.0
github.com/pires/go-proxyproto v0.8.1
github.com/refraction-networking/utls v1.8.2
github.com/sagernet/sing v0.5.1
github.com/sagernet/sing-shadowsocks v0.2.7
+6 -6
View File
@@ -2,8 +2,8 @@ github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sx
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178 h1:bSq8n+gX4oO/qnM3MKf4kroW75n+phO9Qp6nigJKZ1E=
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178/go.mod h1:N1WIjPphkqs4efXWuyDNQ6OjjIK04vM3h+bEgwV+eVU=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/cloudflare/circl v1.6.2 h1:hL7VBpHHKzrV5WTfHCaBsgx/HGbBYlgrwvNXEVDYYsQ=
github.com/cloudflare/circl v1.6.2/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -40,14 +40,14 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
github.com/miekg/dns v1.1.70 h1:DZ4u2AV35VJxdD9Fo9fIWm119BsQL5cZU1cQ9s0LkqA=
github.com/miekg/dns v1.1.70/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
github.com/pires/go-proxyproto v0.9.0 h1:3Qg3CLxWx4wJOw5uxhTvc0VrgsJeerDbGTvexu4UK1E=
github.com/pires/go-proxyproto v0.9.0/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
+2 -17
View File
@@ -80,21 +80,6 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
return errors.New("failed to parse name server: ", string(data))
}
func toDomainMatchingType(t router.Domain_Type) dns.DomainMatchingType {
switch t {
case router.Domain_Domain:
return dns.DomainMatchingType_Subdomain
case router.Domain_Full:
return dns.DomainMatchingType_Full
case router.Domain_Plain:
return dns.DomainMatchingType_Keyword
case router.Domain_Regex:
return dns.DomainMatchingType_Regex
default:
panic("unknown domain type")
}
}
func (c *NameServerConfig) Build() (*dns.NameServer, error) {
if c.Address == nil {
return nil, errors.New("NameServer address is not specified.")
@@ -104,14 +89,14 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
var originalRules []*dns.NameServer_OriginalRule
for _, rule := range c.Domains {
parsedDomain, err := parseDomainRule(rule)
parsedDomain, err := ParseDomainRule(rule)
if err != nil {
return nil, errors.New("invalid domain rule: ", rule).Base(err)
}
for _, pd := range parsedDomain {
domains = append(domains, &dns.NameServer_PriorityDomain{
Type: toDomainMatchingType(pd.Type),
Type: dns.ToDomainMatchingType(pd.Type),
Domain: pd.Value,
})
}
+45 -116
View File
@@ -203,17 +203,23 @@ func loadFile(file string) ([]byte, error) {
func loadIP(file, code string) ([]*router.CIDR, error) {
index := file + ":" + code
if IPCache[index] == nil {
bs, err := loadFile(file)
if err != nil {
return nil, errors.New("failed to load file: ", file).Base(err)
}
bs = find(bs, []byte(code))
if bs == nil {
return nil, errors.New("code not found in ", file, ": ", code)
}
var geoip router.GeoIP
if err := proto.Unmarshal(bs, &geoip); err != nil {
return nil, errors.New("error unmarshal IP in ", file, ": ", code).Base(err)
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
// dont pass code becuase we have country code in top level router.GeoIP
geoip = router.GeoIP{Cidr: []*router.CIDR{}}
} else {
bs, err := loadFile(file)
if err != nil {
return nil, errors.New("failed to load file: ", file).Base(err)
}
bs = filesystem.Find(bs, []byte(code))
if bs == nil {
return nil, errors.New("code not found in ", file, ": ", code)
}
if err := proto.Unmarshal(bs, &geoip); err != nil {
return nil, errors.New("error unmarshal IP in ", file, ": ", code).Base(err)
}
}
defer runtime.GC() // or debug.FreeOSMemory()
return geoip.Cidr, nil // do not cache geoip
@@ -225,18 +231,28 @@ func loadIP(file, code string) ([]*router.CIDR, error) {
func loadSite(file, code string) ([]*router.Domain, error) {
index := file + ":" + code
if SiteCache[index] == nil {
bs, err := loadFile(file)
if err != nil {
return nil, errors.New("failed to load file: ", file).Base(err)
}
bs = find(bs, []byte(code))
if bs == nil {
return nil, errors.New("list not found in ", file, ": ", code)
}
var geosite router.GeoSite
if err := proto.Unmarshal(bs, &geosite); err != nil {
return nil, errors.New("error unmarshal Site in ", file, ": ", code).Base(err)
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
// pass file:code so can build optimized matcher later
domain := router.Domain{Value: file + "_" + code}
geosite = router.GeoSite{Domain: []*router.Domain{&domain}}
} else {
bs, err := loadFile(file)
if err != nil {
return nil, errors.New("failed to load file: ", file).Base(err)
}
bs = filesystem.Find(bs, []byte(code))
if bs == nil {
return nil, errors.New("list not found in ", file, ": ", code)
}
if err := proto.Unmarshal(bs, &geosite); err != nil {
return nil, errors.New("error unmarshal Site in ", file, ": ", code).Base(err)
}
}
defer runtime.GC() // or debug.FreeOSMemory()
return geosite.Domain, nil // do not cache geosite
SiteCache[index] = &geosite
@@ -244,105 +260,13 @@ func loadSite(file, code string) ([]*router.Domain, error) {
return SiteCache[index].Domain, nil
}
func DecodeVarint(buf []byte) (x uint64, n int) {
for shift := uint(0); shift < 64; shift += 7 {
if n >= len(buf) {
return 0, 0
}
b := uint64(buf[n])
n++
x |= (b & 0x7F) << shift
if (b & 0x80) == 0 {
return x, n
}
}
// The number is too large to represent in a 64-bit value.
return 0, 0
}
func find(data, code []byte) []byte {
codeL := len(code)
if codeL == 0 {
return nil
}
for {
dataL := len(data)
if dataL < 2 {
return nil
}
x, y := DecodeVarint(data[1:])
if x == 0 && y == 0 {
return nil
}
headL, bodyL := 1+y, int(x)
dataL -= headL
if dataL < bodyL {
return nil
}
data = data[headL:]
if int(data[1]) == codeL {
for i := 0; i < codeL && data[2+i] == code[i]; i++ {
if i+1 == codeL {
return data[:bodyL]
}
}
}
if dataL == bodyL {
return nil
}
data = data[bodyL:]
}
}
type AttributeMatcher interface {
Match(*router.Domain) bool
}
type BooleanMatcher string
func (m BooleanMatcher) Match(domain *router.Domain) bool {
for _, attr := range domain.Attribute {
if attr.Key == string(m) {
return true
}
}
return false
}
type AttributeList struct {
matcher []AttributeMatcher
}
func (al *AttributeList) Match(domain *router.Domain) bool {
for _, matcher := range al.matcher {
if !matcher.Match(domain) {
return false
}
}
return true
}
func (al *AttributeList) IsEmpty() bool {
return len(al.matcher) == 0
}
func parseAttrs(attrs []string) *AttributeList {
al := new(AttributeList)
for _, attr := range attrs {
lc := strings.ToLower(attr)
al.matcher = append(al.matcher, BooleanMatcher(lc))
}
return al
}
func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) {
parts := strings.Split(siteWithAttr, "@")
if len(parts) == 0 {
return nil, errors.New("empty site")
}
country := strings.ToUpper(parts[0])
attrs := parseAttrs(parts[1:])
attrs := router.ParseAttrs(parts[1:])
domains, err := loadSite(file, country)
if err != nil {
return nil, err
@@ -352,6 +276,11 @@ func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, er
return domains, nil
}
if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
domains[0].Value = domains[0].Value + "_" + strings.Join(parts[1:], ",")
return domains, nil
}
filteredDomains := make([]*router.Domain, 0, len(domains))
for _, domain := range domains {
if attrs.Match(domain) {
@@ -362,7 +291,7 @@ func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, er
return filteredDomains, nil
}
func parseDomainRule(domain string) ([]*router.Domain, error) {
func ParseDomainRule(domain string) ([]*router.Domain, error) {
if strings.HasPrefix(domain, "geosite:") {
country := strings.ToUpper(domain[8:])
domains, err := loadGeositeWithAttr("geosite.dat", country)
@@ -560,7 +489,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
if rawFieldRule.Domain != nil {
for _, domain := range *rawFieldRule.Domain {
rules, err := parseDomainRule(domain)
rules, err := ParseDomainRule(domain)
if err != nil {
return nil, errors.New("failed to parse domain rule: ", domain).Base(err)
}
@@ -570,7 +499,7 @@ func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
if rawFieldRule.Domains != nil {
for _, domain := range *rawFieldRule.Domains {
rules, err := parseDomainRule(domain)
rules, err := ParseDomainRule(domain)
if err != nil {
return nil, errors.New("failed to parse domain rule: ", domain).Base(err)
}
+1 -5
View File
@@ -50,8 +50,6 @@ type ShadowsocksServerConfig struct {
}
func (v *ShadowsocksServerConfig) Build() (proto.Message, error) {
errors.PrintNonRemovalDeprecatedFeatureWarning("Shadowsocks (with no Forward Secrecy, etc.)", "VLESS Encryption")
if C.Contains(shadowaead_2022.List, v.Cipher) {
return buildShadowsocks2022(v)
}
@@ -187,8 +185,6 @@ type ShadowsocksClientConfig struct {
}
func (v *ShadowsocksClientConfig) Build() (proto.Message, error) {
errors.PrintNonRemovalDeprecatedFeatureWarning("Shadowsocks (with no Forward Secrecy, etc.)", "VLESS Encryption")
if v.Address != nil {
v.Servers = []*ShadowsocksServerTarget{
{
@@ -259,7 +255,7 @@ func (v *ShadowsocksClientConfig) Build() (proto.Message, error) {
ss := &protocol.ServerEndpoint{
Address: server.Address.Build(),
Port: uint32(server.Port),
User: &protocol.User{
User: &protocol.User{
Level: uint32(server.Level),
Email: server.Email,
Account: serial.ToTypedMessage(account),
+8 -20
View File
@@ -391,12 +391,11 @@ type UdpHop struct {
}
type HysteriaConfig struct {
Version int32 `json:"version"`
Auth string `json:"auth"`
Congestion string `json:"congestion"`
Up Bandwidth `json:"up"`
Down Bandwidth `json:"down"`
UdpHop UdpHop `json:"udphop"`
Version int32 `json:"version"`
Auth string `json:"auth"`
Up Bandwidth `json:"up"`
Down Bandwidth `json:"down"`
UdpHop UdpHop `json:"udphop"`
InitStreamReceiveWindow uint64 `json:"initStreamReceiveWindow"`
MaxStreamReceiveWindow uint64 `json:"maxStreamReceiveWindow"`
@@ -411,7 +410,6 @@ func (c *HysteriaConfig) Build() (proto.Message, error) {
if c.Version != 2 {
return nil, errors.New("version != 2")
}
up, err := c.Up.Bps()
if err != nil {
return nil, err
@@ -420,12 +418,6 @@ func (c *HysteriaConfig) Build() (proto.Message, error) {
if err != nil {
return nil, err
}
c.Congestion = strings.ToLower(c.Congestion)
if c.Congestion == "force-brutal" && up == 0 {
return nil, errors.New("force-brutal require up")
}
var hop *PortList
if err := json.Unmarshal(c.UdpHop.PortList, &hop); err != nil {
hop = &PortList{}
@@ -463,7 +455,6 @@ func (c *HysteriaConfig) Build() (proto.Message, error) {
config := &hysteria.Config{}
config.Version = c.Version
config.Auth = c.Auth
config.Congestion = c.Congestion
config.Up = up
config.Down = down
config.Ports = hop.Build().Ports()
@@ -596,9 +587,6 @@ func (c *TLSConfig) Build() (proto.Message, error) {
}
serverName := c.ServerName
config.AllowInsecure = c.Insecure
if config.AllowInsecure {
errors.PrintDeprecatedFeatureWarning("allowInsecure", "pinnedPeerCertSha256")
}
if len(c.ServerName) > 0 {
config.ServerName = serverName
}
@@ -903,13 +891,13 @@ func (p TransportProtocol) Build() (string, error) {
case "kcp", "mkcp":
return "mkcp", nil
case "grpc":
errors.PrintNonRemovalDeprecatedFeatureWarning("gRPC transport (with unnecessary costs, etc.)", "XHTTP stream-up H2")
errors.PrintDeprecatedFeatureWarning("gRPC transport (with unnecessary costs, etc.)", "XHTTP stream-up H2")
return "grpc", nil
case "ws", "websocket":
errors.PrintNonRemovalDeprecatedFeatureWarning("WebSocket transport (with ALPN http/1.1, etc.)", "XHTTP H2 & H3")
errors.PrintDeprecatedFeatureWarning("WebSocket transport (with ALPN http/1.1, etc.)", "XHTTP H2 & H3")
return "websocket", nil
case "httpupgrade":
errors.PrintNonRemovalDeprecatedFeatureWarning("HTTPUpgrade transport (with ALPN http/1.1, etc.)", "XHTTP H2 & H3")
errors.PrintDeprecatedFeatureWarning("HTTPUpgrade transport (with ALPN http/1.1, etc.)", "XHTTP H2 & H3")
return "httpupgrade", nil
case "h2", "h3", "http":
return "", errors.PrintRemovedFeatureError("HTTP transport (without header padding, etc.)", "XHTTP stream-one H2 & H3")
+1 -5
View File
@@ -39,8 +39,6 @@ type TrojanClientConfig struct {
// Build implements Buildable
func (c *TrojanClientConfig) Build() (proto.Message, error) {
errors.PrintNonRemovalDeprecatedFeatureWarning("Trojan (with no Flow, etc.)", "VLESS with Flow & Seed")
if c.Address != nil {
c.Servers = []*TrojanServerTarget{
{
@@ -76,7 +74,7 @@ func (c *TrojanClientConfig) Build() (proto.Message, error) {
config.Server = &protocol.ServerEndpoint{
Address: rec.Address.Build(),
Port: uint32(rec.Port),
User: &protocol.User{
User: &protocol.User{
Level: uint32(rec.Level),
Email: rec.Email,
Account: serial.ToTypedMessage(&trojan.Account{
@@ -117,8 +115,6 @@ type TrojanServerConfig struct {
// Build implements Buildable
func (c *TrojanServerConfig) Build() (proto.Message, error) {
errors.PrintNonRemovalDeprecatedFeatureWarning("Trojan (with no Flow, etc.)", "VLESS with Flow & Seed")
config := &trojan.ServerConfig{
Users: make([]*protocol.User, len(c.Clients)),
}
+5 -6
View File
@@ -42,6 +42,8 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
config := new(inbound.Config)
config.Clients = make([]*protocol.User, len(c.Clients))
switch c.Flow {
case vless.None:
c.Flow = ""
case "", vless.XRV:
default:
return nil, errors.New(`VLESS "settings.flow" doesn't support "` + c.Flow + `" in this version`)
@@ -65,13 +67,12 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
switch account.Flow {
case "":
account.Flow = c.Flow
case vless.None:
account.Flow = ""
case vless.XRV:
default:
return nil, errors.New(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`)
}
if account.Flow == "" {
errors.PrintNonRemovalDeprecatedFeatureWarning("VLESS (with no Flow, etc.)", "VLESS with Flow & Seed")
}
if len(account.Testseed) < 4 {
account.Testseed = c.Testseed
@@ -279,9 +280,7 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
account.Id = u.String()
switch account.Flow {
case "":
errors.PrintNonRemovalDeprecatedFeatureWarning("VLESS (with no Flow, etc.)", "VLESS with Flow & Seed")
case vless.XRV, vless.XRV + "-udp443":
case "", vless.XRV, vless.XRV + "-udp443":
default:
return nil, errors.New(`VLESS users: "flow" doesn't support "` + account.Flow + `" in this version`)
}
+2 -6
View File
@@ -58,14 +58,12 @@ func (c *VMessDefaultConfig) Build() *inbound.DefaultConfig {
}
type VMessInboundConfig struct {
Users []json.RawMessage `json:"clients"`
Defaults *VMessDefaultConfig `json:"default"`
Users []json.RawMessage `json:"clients"`
Defaults *VMessDefaultConfig `json:"default"`
}
// Build implements Buildable
func (c *VMessInboundConfig) Build() (proto.Message, error) {
errors.PrintNonRemovalDeprecatedFeatureWarning("VMess (with no Forward Secrecy, etc.)", "VLESS Encryption")
config := &inbound.Config{}
if c.Defaults != nil {
@@ -115,8 +113,6 @@ type VMessOutboundConfig struct {
// Build implements Buildable
func (c *VMessOutboundConfig) Build() (proto.Message, error) {
errors.PrintNonRemovalDeprecatedFeatureWarning("VMess (with no Forward Secrecy, etc.)", "VLESS Encryption")
config := new(outbound.Config)
if c.Address != nil {
c.Receivers = []*VMessOutboundTarget{
-1
View File
@@ -29,7 +29,6 @@ var CmdAPI = &base.Command{
cmdInboundUserCount,
cmdAddRules,
cmdRemoveRules,
cmdListRules,
cmdSourceIpBlock,
cmdOnlineStats,
cmdOnlineStatsIpList,
-43
View File
@@ -1,43 +0,0 @@
package api
import (
routerService "github.com/xtls/xray-core/app/router/command"
"github.com/xtls/xray-core/main/commands/base"
)
var cmdListRules = &base.Command{
CustomFlags: true,
UsageLine: "{{.Exec}} api lsrules [--server=127.0.0.1:8080]",
Short: "List routing rules",
Long: `
List routing rules in Xray.
Arguments:
-s, -server <server:port>
The API server address. Default 127.0.0.1:8080
-t, -timeout <seconds>
Timeout in seconds for calling API. Default 3
Example:
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080
`,
Run: executeListRules,
}
func executeListRules(cmd *base.Command, args []string) {
setSharedFlags(cmd)
cmd.Flag.Parse(args)
conn, ctx, close := dialAPIServer()
defer close()
client := routerService.NewRoutingServiceClient(conn)
resp, err := client.ListRule(ctx, &routerService.ListRuleRequest{})
if err != nil {
base.Fatalf("failed to list rules: %s", err)
}
showJSONResponse(resp)
}
+1 -2
View File
@@ -17,7 +17,6 @@ import (
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features/policy"
hyCtx "github.com/xtls/xray-core/proxy/hysteria/ctx"
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/hysteria"
@@ -56,7 +55,7 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter
ob.CanSpliceCopy = 3
target := ob.Target
conn, err := dialer.Dial(hyCtx.ContextWithRequireDatagram(ctx, target.Network == net.Network_UDP), c.server.Destination)
conn, err := dialer.Dial(ctx, c.server.Destination)
if err != nil {
return errors.New("failed to find an available destination").AtWarning().Base(err)
}
-23
View File
@@ -1,23 +0,0 @@
package ctx
import (
"context"
)
type key int
const (
requireDatagram key = iota
)
func ContextWithRequireDatagram(ctx context.Context, udp bool) context.Context {
if !udp {
return ctx
}
return context.WithValue(ctx, requireDatagram, struct{}{})
}
func RequireDatagramFromContext(ctx context.Context) bool {
_, ok := ctx.Value(requireDatagram).(struct{})
return ok
}
+2 -3
View File
@@ -106,9 +106,8 @@ func (t *Handler) HandleConnection(conn net.Conn, destination net.Destination) {
// to close, send completion packets back to the network, and cleanup
defer conn.Close()
ctx, cancel := context.WithCancel(t.ctx)
defer cancel()
ctx = c.ContextWithID(ctx, session.NewID())
sid := session.NewID()
ctx := c.ContextWithID(t.ctx, sid)
source := net.DestinationFromAddr(conn.RemoteAddr())
inbound := session.Inbound{
-169
View File
@@ -1,169 +0,0 @@
//go:build darwin
package tun
import (
"errors"
"fmt"
"strings"
"unsafe"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
const (
utunControlName = "com.apple.net.utun_control"
utunOptIfName = 2
sysprotoControl = 2
)
type DarwinTun struct {
tunFd int
name string
options TunOptions
}
var _ Tun = (*DarwinTun)(nil)
var _ GVisorTun = (*DarwinTun)(nil)
func NewTun(options TunOptions) (Tun, error) {
tunFd, name, err := openUTun(options.Name)
if err != nil {
return nil, err
}
return &DarwinTun{
tunFd: tunFd,
name: name,
options: options,
}, nil
}
func (t *DarwinTun) Start() error {
if t.options.MTU > 0 {
if err := setMTU(t.name, int(t.options.MTU)); err != nil {
return err
}
}
return setState(t.name, true)
}
func (t *DarwinTun) Close() error {
_ = setState(t.name, false)
return unix.Close(t.tunFd)
}
func (t *DarwinTun) newEndpoint() (stack.LinkEndpoint, error) {
return newDarwinEndpoint(t.tunFd, t.options.MTU), nil
}
func openUTun(name string) (int, string, error) {
fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, sysprotoControl)
if err != nil {
return -1, "", err
}
ctlInfo := &unix.CtlInfo{}
copy(ctlInfo.Name[:], utunControlName)
if err := unix.IoctlCtlInfo(fd, ctlInfo); err != nil {
_ = unix.Close(fd)
return -1, "", err
}
sockaddr := &unix.SockaddrCtl{
ID: ctlInfo.Id,
Unit: parseUTunUnit(name),
}
if err := unix.Connect(fd, sockaddr); err != nil {
_ = unix.Close(fd)
return -1, "", err
}
if err := unix.SetNonblock(fd, true); err != nil {
_ = unix.Close(fd)
return -1, "", err
}
tunName, err := unix.GetsockoptString(fd, sysprotoControl, utunOptIfName)
if err != nil {
_ = unix.Close(fd)
return -1, "", err
}
tunName = strings.TrimRight(tunName, "\x00")
if tunName == "" {
_ = unix.Close(fd)
return -1, "", errors.New("empty utun name")
}
return fd, tunName, nil
}
func parseUTunUnit(name string) uint32 {
var unit uint32
if _, err := fmt.Sscanf(name, "utun%d", &unit); err != nil {
return 0
}
return unit + 1
}
type ifreqMTU struct {
Name [unix.IFNAMSIZ]byte
MTU int32
_ [12]byte
}
type ifreqFlags struct {
Name [unix.IFNAMSIZ]byte
Flags int16
_ [14]byte
}
func setMTU(name string, mtu int) error {
if mtu <= 0 {
return nil
}
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
if err != nil {
return err
}
defer func() { _ = unix.Close(fd) }()
ifr := ifreqMTU{MTU: int32(mtu)}
copy(ifr.Name[:], name)
return ioctlPtr(fd, unix.SIOCSIFMTU, unsafe.Pointer(&ifr))
}
func setState(name string, up bool) error {
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
if err != nil {
return err
}
defer func() { _ = unix.Close(fd) }()
ifr := ifreqFlags{}
copy(ifr.Name[:], name)
if err := ioctlPtr(fd, unix.SIOCGIFFLAGS, unsafe.Pointer(&ifr)); err != nil {
return err
}
if up {
ifr.Flags |= unix.IFF_UP
} else {
ifr.Flags &^= unix.IFF_UP
}
return ioctlPtr(fd, unix.SIOCSIFFLAGS, unsafe.Pointer(&ifr))
}
func ioctlPtr(fd int, req uint, arg unsafe.Pointer) error {
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg))
if errno != 0 {
return errno
}
return nil
}
-201
View File
@@ -1,201 +0,0 @@
//go:build darwin
package tun
import (
"context"
"encoding/binary"
"errors"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
const utunHeaderSize = 4
var ErrUnsupportedNetworkProtocol = errors.New("unsupported ip version")
// DarwinEndpoint implements GVisor stack.LinkEndpoint
var _ stack.LinkEndpoint = (*DarwinEndpoint)(nil)
type DarwinEndpoint struct {
tunFd int
mtu uint32
dispatcherCancel context.CancelFunc
}
func newDarwinEndpoint(tunFd int, mtu uint32) *DarwinEndpoint {
return &DarwinEndpoint{
tunFd: tunFd,
mtu: mtu,
}
}
func (e *DarwinEndpoint) MTU() uint32 {
return e.mtu
}
func (e *DarwinEndpoint) SetMTU(_ uint32) {
// not Implemented, as it is not expected GVisor will be asking tun device to be modified
}
func (e *DarwinEndpoint) MaxHeaderLength() uint16 {
return 0
}
func (e *DarwinEndpoint) LinkAddress() tcpip.LinkAddress {
return ""
}
func (e *DarwinEndpoint) SetLinkAddress(_ tcpip.LinkAddress) {
// not Implemented, as it is not expected GVisor will be asking tun device to be modified
}
func (e *DarwinEndpoint) Capabilities() stack.LinkEndpointCapabilities {
return stack.CapabilityRXChecksumOffload
}
func (e *DarwinEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
if e.dispatcherCancel != nil {
e.dispatcherCancel()
e.dispatcherCancel = nil
}
if dispatcher != nil {
ctx, cancel := context.WithCancel(context.Background())
go e.dispatchLoop(ctx, dispatcher)
e.dispatcherCancel = cancel
}
}
func (e *DarwinEndpoint) IsAttached() bool {
return e.dispatcherCancel != nil
}
func (e *DarwinEndpoint) Wait() {
}
func (e *DarwinEndpoint) ARPHardwareType() header.ARPHardwareType {
return header.ARPHardwareNone
}
func (e *DarwinEndpoint) AddHeader(buffer *stack.PacketBuffer) {
// tun interface doesn't have link layer header, it will be added by the OS
}
func (e *DarwinEndpoint) ParseHeader(ptr *stack.PacketBuffer) bool {
return true
}
func (e *DarwinEndpoint) Close() {
if e.dispatcherCancel != nil {
e.dispatcherCancel()
e.dispatcherCancel = nil
}
}
func (e *DarwinEndpoint) SetOnCloseAction(_ func()) {
}
func (e *DarwinEndpoint) WritePackets(packetBufferList stack.PacketBufferList) (int, tcpip.Error) {
var n int
for _, packetBuffer := range packetBufferList.AsSlice() {
family, err := ipFamilyFromPacket(packetBuffer)
if err != nil {
return n, &tcpip.ErrAborted{}
}
var headerBytes [utunHeaderSize]byte
binary.BigEndian.PutUint32(headerBytes[:], family)
writeSlices := append([][]byte{headerBytes[:]}, packetBuffer.AsSlices()...)
if _, err := unix.Writev(e.tunFd, writeSlices); err != nil {
if errors.Is(err, unix.EAGAIN) {
return n, &tcpip.ErrWouldBlock{}
}
return n, &tcpip.ErrAborted{}
}
n++
}
return n, nil
}
func (e *DarwinEndpoint) dispatchLoop(ctx context.Context, dispatcher stack.NetworkDispatcher) {
readSize := int(e.mtu)
if readSize <= 0 {
readSize = 65535
}
readSize += utunHeaderSize
buf := make([]byte, readSize)
for ctx.Err() == nil {
n, err := unix.Read(e.tunFd, buf)
if err != nil {
if errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EINTR) {
continue
}
e.Attach(nil)
return
}
if n <= utunHeaderSize {
continue
}
networkProtocol, packet, err := parseUTunPacket(buf[:n])
if errors.Is(err, ErrUnsupportedNetworkProtocol) {
continue
}
if err != nil {
e.Attach(nil)
return
}
dispatcher.DeliverNetworkPacket(networkProtocol, packet)
packet.DecRef()
}
}
func parseUTunPacket(packet []byte) (tcpip.NetworkProtocolNumber, *stack.PacketBuffer, error) {
if len(packet) <= utunHeaderSize {
return 0, nil, errors.New("packet too short")
}
family := binary.BigEndian.Uint32(packet[:utunHeaderSize])
var networkProtocol tcpip.NetworkProtocolNumber
switch family {
case uint32(unix.AF_INET):
networkProtocol = header.IPv4ProtocolNumber
case uint32(unix.AF_INET6):
networkProtocol = header.IPv6ProtocolNumber
default:
return 0, nil, ErrUnsupportedNetworkProtocol
}
payload := packet[utunHeaderSize:]
packetBuffer := buffer.MakeWithData(payload)
return networkProtocol, stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: packetBuffer,
IsForwardedPacket: true,
}), nil
}
func ipFamilyFromPacket(packetBuffer *stack.PacketBuffer) (uint32, error) {
for _, slice := range packetBuffer.AsSlices() {
if len(slice) == 0 {
continue
}
switch header.IPVersion(slice) {
case header.IPv4Version:
return uint32(unix.AF_INET), nil
case header.IPv6Version:
return uint32(unix.AF_INET6), nil
default:
return 0, ErrUnsupportedNetworkProtocol
}
}
return 0, errors.New("empty packet")
}
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build !linux && !windows && !android && !darwin
//go:build !linux && !windows && !android
package tun
+27 -21
View File
@@ -26,15 +26,15 @@ var nullDestination = net.TCPDestination(net.AnyIP, 0)
type Server struct {
bindServer *netBindServer
info routingInfo
policyManager policy.Manager
info routingInfo
policyManager policy.Manager
tag string
sniffingRequest session.SniffingRequest
}
type routingInfo struct {
ctx context.Context
dispatcher routing.Dispatcher
inboundTag *session.Inbound
contentTag *session.Content
ctx context.Context
dispatcher routing.Dispatcher
}
func NewServer(ctx context.Context, conf *DeviceConfig) (*Server, error) {
@@ -58,6 +58,14 @@ func NewServer(ctx context.Context, conf *DeviceConfig) (*Server, error) {
policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager),
}
// Retrieve tag and sniffing config from context (set by AlwaysOnInboundHandler)
if inbound := session.InboundFromContext(ctx); inbound != nil {
server.tag = inbound.Tag
}
if content := session.ContentFromContext(ctx); content != nil {
server.sniffingRequest = content.SniffingRequest
}
tun, err := conf.createTun()(endpoints, int(conf.Mtu), server.forwardConnection)
if err != nil {
return nil, err
@@ -81,8 +89,6 @@ func (s *Server) Process(ctx context.Context, network net.Network, conn stat.Con
s.info = routingInfo{
ctx: ctx,
dispatcher: dispatcher,
inboundTag: session.InboundFromContext(ctx),
contentTag: session.ContentFromContext(ctx),
}
ep, err := s.bindServer.ParseEndpoint(conn.RemoteAddr().String())
@@ -129,21 +135,21 @@ func (s *Server) forwardConnection(dest net.Destination, conn net.Conn) {
ctx, cancel := context.WithCancel(core.ToBackgroundDetachedContext(s.info.ctx))
sid := session.NewID()
ctx = c.ContextWithID(ctx, sid)
inbound := session.Inbound{} // since promiscuousModeHandler mixed-up context, we shallow copy inbound (tag) and content (configs)
if s.info.inboundTag != nil {
inbound = *s.info.inboundTag
}
inbound.Name = "wireguard"
inbound.CanSpliceCopy = 3
// overwrite the source to use the tun address for each sub context.
// Since gvisor.ForwarderRequest doesn't provide any info to associate the sub-context with the Parent context
// Currently we have no way to link to the original source address
inbound.Source = net.DestinationFromAddr(conn.RemoteAddr())
ctx = session.ContextWithInbound(ctx, &inbound)
if s.info.contentTag != nil {
ctx = session.ContextWithContent(ctx, s.info.contentTag)
inbound := session.Inbound{
Name: "wireguard",
Tag: s.tag,
CanSpliceCopy: 3,
// overwrite the source to use the tun address for each sub context.
// Since gvisor.ForwarderRequest doesn't provide any info to associate the sub-context with the Parent context
// Currently we have no way to link to the original source address
Source: net.DestinationFromAddr(conn.RemoteAddr()),
}
ctx = session.ContextWithInbound(ctx, &inbound)
ctx = session.ContextWithContent(ctx, &session.Content{
SniffingRequest: s.sniffingRequest,
})
ctx = session.SubContextFromMuxInbound(ctx)
plcy := s.policyManager.ForLevel(0)
+18 -31
View File
@@ -213,40 +213,27 @@ func testTCPConn2(conn net.Conn, payloadSize int, timeout time.Duration) func()
"\tSys =", units.ByteSize(m.Sys).String(),
"\tNumGC =", m.NumGC)
}()
singleWrite := func(length int) error {
payload := make([]byte, length)
common.Must2(rand.Read(payload))
payload := make([]byte, payloadSize)
common.Must2(rand.Read(payload))
nBytes, err := conn.Write(payload)
if err != nil {
return err
}
if nBytes != len(payload) {
return errors.New("expect ", len(payload), " written, but actually ", nBytes)
}
response, err := readFrom2(conn, timeout, length)
if err != nil {
return err
}
_ = response
if r := bytes.Compare(response, xor(payload)); r != 0 {
return errors.New(r)
}
return nil
nBytes, err := conn.Write(payload)
if err != nil {
return err
}
for payloadSize > 0 {
sizeToWrite := 1024
if payloadSize < 1024 {
sizeToWrite = payloadSize
}
if err := singleWrite(sizeToWrite); err != nil {
return err
}
payloadSize -= sizeToWrite
if nBytes != len(payload) {
return errors.New("expect ", len(payload), " written, but actually ", nBytes)
}
response, err := readFrom2(conn, timeout, payloadSize)
if err != nil {
return err
}
_ = response
if r := bytes.Compare(response, xor(payload)); r != 0 {
return errors.New(r)
}
return nil
}
}
+25 -36
View File
@@ -25,18 +25,17 @@ type Config struct {
state protoimpl.MessageState `protogen:"open.v1"`
Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
Auth string `protobuf:"bytes,2,opt,name=auth,proto3" json:"auth,omitempty"`
Congestion string `protobuf:"bytes,3,opt,name=congestion,proto3" json:"congestion,omitempty"`
Up uint64 `protobuf:"varint,4,opt,name=up,proto3" json:"up,omitempty"`
Down uint64 `protobuf:"varint,5,opt,name=down,proto3" json:"down,omitempty"`
Ports []uint32 `protobuf:"varint,6,rep,packed,name=ports,proto3" json:"ports,omitempty"`
Interval int64 `protobuf:"varint,7,opt,name=interval,proto3" json:"interval,omitempty"`
InitStreamReceiveWindow uint64 `protobuf:"varint,8,opt,name=init_stream_receive_window,json=initStreamReceiveWindow,proto3" json:"init_stream_receive_window,omitempty"`
MaxStreamReceiveWindow uint64 `protobuf:"varint,9,opt,name=max_stream_receive_window,json=maxStreamReceiveWindow,proto3" json:"max_stream_receive_window,omitempty"`
InitConnReceiveWindow uint64 `protobuf:"varint,10,opt,name=init_conn_receive_window,json=initConnReceiveWindow,proto3" json:"init_conn_receive_window,omitempty"`
MaxConnReceiveWindow uint64 `protobuf:"varint,11,opt,name=max_conn_receive_window,json=maxConnReceiveWindow,proto3" json:"max_conn_receive_window,omitempty"`
MaxIdleTimeout int64 `protobuf:"varint,12,opt,name=max_idle_timeout,json=maxIdleTimeout,proto3" json:"max_idle_timeout,omitempty"`
KeepAlivePeriod int64 `protobuf:"varint,13,opt,name=keep_alive_period,json=keepAlivePeriod,proto3" json:"keep_alive_period,omitempty"`
DisablePathMtuDiscovery bool `protobuf:"varint,14,opt,name=disable_path_mtu_discovery,json=disablePathMtuDiscovery,proto3" json:"disable_path_mtu_discovery,omitempty"`
Up uint64 `protobuf:"varint,3,opt,name=up,proto3" json:"up,omitempty"`
Down uint64 `protobuf:"varint,4,opt,name=down,proto3" json:"down,omitempty"`
Ports []uint32 `protobuf:"varint,5,rep,packed,name=ports,proto3" json:"ports,omitempty"`
Interval int64 `protobuf:"varint,6,opt,name=interval,proto3" json:"interval,omitempty"`
InitStreamReceiveWindow uint64 `protobuf:"varint,7,opt,name=init_stream_receive_window,json=initStreamReceiveWindow,proto3" json:"init_stream_receive_window,omitempty"`
MaxStreamReceiveWindow uint64 `protobuf:"varint,8,opt,name=max_stream_receive_window,json=maxStreamReceiveWindow,proto3" json:"max_stream_receive_window,omitempty"`
InitConnReceiveWindow uint64 `protobuf:"varint,9,opt,name=init_conn_receive_window,json=initConnReceiveWindow,proto3" json:"init_conn_receive_window,omitempty"`
MaxConnReceiveWindow uint64 `protobuf:"varint,10,opt,name=max_conn_receive_window,json=maxConnReceiveWindow,proto3" json:"max_conn_receive_window,omitempty"`
MaxIdleTimeout int64 `protobuf:"varint,11,opt,name=max_idle_timeout,json=maxIdleTimeout,proto3" json:"max_idle_timeout,omitempty"`
KeepAlivePeriod int64 `protobuf:"varint,12,opt,name=keep_alive_period,json=keepAlivePeriod,proto3" json:"keep_alive_period,omitempty"`
DisablePathMtuDiscovery bool `protobuf:"varint,13,opt,name=disable_path_mtu_discovery,json=disablePathMtuDiscovery,proto3" json:"disable_path_mtu_discovery,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -85,13 +84,6 @@ func (x *Config) GetAuth() string {
return ""
}
func (x *Config) GetCongestion() string {
if x != nil {
return x.Congestion
}
return ""
}
func (x *Config) GetUp() uint64 {
if x != nil {
return x.Up
@@ -173,25 +165,22 @@ var File_transport_internet_hysteria_config_proto protoreflect.FileDescriptor
const file_transport_internet_hysteria_config_proto_rawDesc = "" +
"\n" +
"(transport/internet/hysteria/config.proto\x12 xray.transport.internet.hysteria\"\xa7\x04\n" +
"(transport/internet/hysteria/config.proto\x12 xray.transport.internet.hysteria\"\x87\x04\n" +
"\x06Config\x12\x18\n" +
"\aversion\x18\x01 \x01(\x05R\aversion\x12\x12\n" +
"\x04auth\x18\x02 \x01(\tR\x04auth\x12\x1e\n" +
"\n" +
"congestion\x18\x03 \x01(\tR\n" +
"congestion\x12\x0e\n" +
"\x02up\x18\x04 \x01(\x04R\x02up\x12\x12\n" +
"\x04down\x18\x05 \x01(\x04R\x04down\x12\x14\n" +
"\x05ports\x18\x06 \x03(\rR\x05ports\x12\x1a\n" +
"\binterval\x18\a \x01(\x03R\binterval\x12;\n" +
"\x1ainit_stream_receive_window\x18\b \x01(\x04R\x17initStreamReceiveWindow\x129\n" +
"\x19max_stream_receive_window\x18\t \x01(\x04R\x16maxStreamReceiveWindow\x127\n" +
"\x18init_conn_receive_window\x18\n" +
" \x01(\x04R\x15initConnReceiveWindow\x125\n" +
"\x17max_conn_receive_window\x18\v \x01(\x04R\x14maxConnReceiveWindow\x12(\n" +
"\x10max_idle_timeout\x18\f \x01(\x03R\x0emaxIdleTimeout\x12*\n" +
"\x11keep_alive_period\x18\r \x01(\x03R\x0fkeepAlivePeriod\x12;\n" +
"\x1adisable_path_mtu_discovery\x18\x0e \x01(\bR\x17disablePathMtuDiscoveryB\x82\x01\n" +
"\x04auth\x18\x02 \x01(\tR\x04auth\x12\x0e\n" +
"\x02up\x18\x03 \x01(\x04R\x02up\x12\x12\n" +
"\x04down\x18\x04 \x01(\x04R\x04down\x12\x14\n" +
"\x05ports\x18\x05 \x03(\rR\x05ports\x12\x1a\n" +
"\binterval\x18\x06 \x01(\x03R\binterval\x12;\n" +
"\x1ainit_stream_receive_window\x18\a \x01(\x04R\x17initStreamReceiveWindow\x129\n" +
"\x19max_stream_receive_window\x18\b \x01(\x04R\x16maxStreamReceiveWindow\x127\n" +
"\x18init_conn_receive_window\x18\t \x01(\x04R\x15initConnReceiveWindow\x125\n" +
"\x17max_conn_receive_window\x18\n" +
" \x01(\x04R\x14maxConnReceiveWindow\x12(\n" +
"\x10max_idle_timeout\x18\v \x01(\x03R\x0emaxIdleTimeout\x12*\n" +
"\x11keep_alive_period\x18\f \x01(\x03R\x0fkeepAlivePeriod\x12;\n" +
"\x1adisable_path_mtu_discovery\x18\r \x01(\bR\x17disablePathMtuDiscoveryB\x82\x01\n" +
"$com.xray.transport.internet.hysteriaP\x01Z5github.com/xtls/xray-core/transport/internet/hysteria\xaa\x02 Xray.Transport.Internet.Hysteriab\x06proto3"
var (
+11 -12
View File
@@ -9,18 +9,17 @@ option java_multiple_files = true;
message Config {
int32 version = 1;
string auth = 2;
string congestion = 3;
uint64 up = 4;
uint64 down = 5;
repeated uint32 ports = 6;
int64 interval = 7;
uint64 up = 3;
uint64 down = 4;
repeated uint32 ports = 5;
int64 interval = 6;
uint64 init_stream_receive_window = 8;
uint64 max_stream_receive_window = 9;
uint64 init_conn_receive_window = 10;
uint64 max_conn_receive_window = 11;
int64 max_idle_timeout = 12;
int64 keep_alive_period = 13;
bool disable_path_mtu_discovery = 14;
uint64 init_stream_receive_window = 7;
uint64 max_stream_receive_window = 8;
uint64 init_conn_receive_window = 9;
uint64 max_conn_receive_window = 10;
int64 max_idle_timeout = 11;
int64 keep_alive_period = 12;
bool disable_path_mtu_discovery = 13;
}
-4
View File
@@ -24,7 +24,6 @@ func (i *interConn) Write(b []byte) (int, error) {
}
func (i *interConn) Close() error {
i.stream.CancelRead(0)
return i.stream.Close()
}
@@ -65,9 +64,6 @@ func (i *InterUdpConn) Read(p []byte) (int, error) {
return 0, io.EOF
}
n := copy(p, b)
if n != len(b) {
return 0, io.ErrShortBuffer
}
return n, nil
}
+10 -23
View File
@@ -16,8 +16,8 @@ import (
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/common/task"
hyCtx "github.com/xtls/xray-core/proxy/hysteria/ctx"
"github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/finalmask"
"github.com/xtls/xray-core/transport/internet/hysteria/congestion"
@@ -115,8 +115,8 @@ type client struct {
conn *quic.Conn
config *Config
tlsConfig *go_tls.Config
socketConfig *internet.SocketConfig
udpmaskManager *finalmask.UdpmaskManager
socketConfig *internet.SocketConfig
udpSM *udpSessionManager
mutex sync.Mutex
}
@@ -243,25 +243,10 @@ func (c *client) dial() error {
serverAuto := resp.Header.Get(CommonHeaderCCRX)
serverDown, _ := strconv.ParseUint(serverAuto, 10, 64)
switch c.config.Congestion {
case "reno":
errors.LogDebug(c.ctx, "congestion reno")
case "bbr":
errors.LogDebug(c.ctx, "congestion bbr")
if serverAuto == "auto" || c.config.Up == 0 || serverDown == 0 {
congestion.UseBBR(quicConn)
case "brutal", "":
if serverAuto == "auto" || c.config.Up == 0 || serverDown == 0 {
errors.LogDebug(c.ctx, "congestion bbr")
congestion.UseBBR(quicConn)
} else {
errors.LogDebug(c.ctx, "congestion brutal bytes per second ", min(c.config.Up, serverDown))
congestion.UseBrutal(quicConn, min(c.config.Up, serverDown))
}
case "force-brutal":
errors.LogDebug(c.ctx, "congestion brutal bytes per second ", c.config.Up)
congestion.UseBrutal(quicConn, c.config.Up)
default:
errors.LogDebug(c.ctx, "congestion reno")
} else {
congestion.UseBrutal(quicConn, min(c.config.Up, serverDown))
}
c.pktConn = pktConn
@@ -378,7 +363,6 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
return nil, errors.New("tls config is nil")
}
requireDatagram := hyCtx.RequireDatagramFromContext(ctx)
addr := dest.NetAddr()
config := streamSettings.ProtocolSettings.(*Config)
@@ -391,15 +375,18 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
dest: dest,
config: config,
tlsConfig: tlsConfig.GetTLSConfig(),
socketConfig: streamSettings.SocketSettings,
udpmaskManager: streamSettings.UdpmaskManager,
socketConfig: streamSettings.SocketSettings,
}
manger.m[addr] = c
}
c.setCtx(ctx)
manger.mutex.Unlock()
if requireDatagram {
outbounds := session.OutboundsFromContext(ctx)
targetUdp := len(outbounds) > 0 && outbounds[len(outbounds)-1].Target.Network == net.Network_UDP
if targetUdp {
return c.udp()
}
return c.tcp()
-60
View File
@@ -1,15 +1,12 @@
package tls
import (
"crypto/tls"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"testing"
"github.com/stretchr/testify/assert"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/protocol/tls/cert"
)
func TestCalculateCertHash(t *testing.T) {
@@ -98,60 +95,3 @@ uI6HqHFD3iEct8fBkYfQiwH2e1eu9OwgujiWHsutyK8VvzVB3/YnhQ/TzciRjPqz
assert.Equal(t, fingerprint, hash)
})
}
func TestVerifyPeerLeafCert(t *testing.T) {
leafCert := cert.MustGenerate(nil, cert.DNSNames("example.com"))
leaf := common.Must2(x509.ParseCertificate(leafCert.Certificate))
caHash := GenerateCertHash(leafCert.Certificate)
r := &RandCarrier{
Config: &tls.Config{
ServerName: "example.com",
},
PinnedPeerCertSha256: [][]byte{caHash},
}
rawCerts := [][]byte{leaf.Raw}
err := r.verifyPeerCert(rawCerts, nil)
if err != nil {
t.Fatal("expected to verify leaf cert signed by pinned CA, but got error:", err)
}
// make the pinned hash incorrect
r.PinnedPeerCertSha256[0][0] += 1
err = r.verifyPeerCert(rawCerts, nil)
if err == nil {
t.Fatal("expected to fail verifying leaf cert with incorrect pinned CA hash, but got no error")
}
}
func TestVerifyPeerCACert(t *testing.T) {
caCert := cert.MustGenerate(nil, cert.Authority(true), cert.KeyUsage(x509.KeyUsageCertSign))
ca := common.Must2(x509.ParseCertificate(caCert.Certificate))
leafCert := cert.MustGenerate(caCert, cert.DNSNames("example.com"))
leaf := common.Must2(x509.ParseCertificate(leafCert.Certificate))
caHash := GenerateCertHash(ca)
r := &RandCarrier{
Config: &tls.Config{
ServerName: "example.com",
},
PinnedPeerCertSha256: [][]byte{caHash},
}
rawCerts := [][]byte{leaf.Raw, ca.Raw}
err := r.verifyPeerCert(rawCerts, nil)
if err != nil {
t.Fatal("expected to verify leaf cert signed by pinned CA, but got error:", err)
}
// make the pinned hash incorrect
r.PinnedPeerCertSha256[0][0] += 1
err = r.verifyPeerCert(rawCerts, nil)
if err == nil {
t.Fatal("expected to fail verifying leaf cert with incorrect pinned CA hash, but got no error")
}
}