Files
Xray-core/infra/conf/router.go
T

288 lines
7.2 KiB
Go
Raw Normal View History

2020-11-25 19:01:53 +08:00
package conf
import (
"encoding/json"
"strings"
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/app/router"
2024-06-29 14:32:57 -04:00
"github.com/xtls/xray-core/common/errors"
2026-04-14 00:42:29 +08:00
"github.com/xtls/xray-core/common/geodata"
2024-03-20 13:22:56 +08:00
"github.com/xtls/xray-core/common/serial"
2026-04-14 00:42:29 +08:00
"google.golang.org/protobuf/proto"
2020-11-25 19:01:53 +08:00
)
2021-10-26 01:00:31 -04:00
// StrategyConfig represents a strategy config
type StrategyConfig struct {
Type string `json:"type"`
Settings *json.RawMessage `json:"settings"`
}
2020-11-25 19:01:53 +08:00
type BalancingRule struct {
2024-02-17 22:51:37 -05:00
Tag string `json:"tag"`
Selectors StringList `json:"selector"`
Strategy StrategyConfig `json:"strategy"`
FallbackTag string `json:"fallbackTag"`
2020-11-25 19:01:53 +08:00
}
2024-02-17 22:51:37 -05:00
// Build builds the balancing rule
2020-11-25 19:01:53 +08:00
func (r *BalancingRule) Build() (*router.BalancingRule, error) {
if r.Tag == "" {
2024-06-29 14:32:57 -04:00
return nil, errors.New("empty balancer tag")
2020-11-25 19:01:53 +08:00
}
if len(r.Selectors) == 0 {
2024-06-29 14:32:57 -04:00
return nil, errors.New("empty selector list")
2020-11-25 19:01:53 +08:00
}
2024-02-17 22:51:37 -05:00
r.Strategy.Type = strings.ToLower(r.Strategy.Type)
switch r.Strategy.Type {
case "":
r.Strategy.Type = strategyRandom
case strategyRandom, strategyLeastLoad, strategyLeastPing, strategyRoundRobin:
2021-10-26 01:00:31 -04:00
default:
2024-06-29 14:32:57 -04:00
return nil, errors.New("unknown balancing strategy: " + r.Strategy.Type)
2021-10-26 01:00:31 -04:00
}
2024-02-17 22:51:37 -05:00
settings := []byte("{}")
if r.Strategy.Settings != nil {
settings = ([]byte)(*r.Strategy.Settings)
}
rawConfig, err := strategyConfigLoader.LoadWithID(settings, r.Strategy.Type)
if err != nil {
2024-06-29 14:32:57 -04:00
return nil, errors.New("failed to parse to strategy config.").Base(err)
2024-02-17 22:51:37 -05:00
}
var ts proto.Message
if builder, ok := rawConfig.(Buildable); ok {
ts, err = builder.Build()
if err != nil {
return nil, err
}
}
2020-11-25 19:01:53 +08:00
return &router.BalancingRule{
2024-02-17 22:51:37 -05:00
Strategy: r.Strategy.Type,
StrategySettings: serial.ToTypedMessage(ts),
FallbackTag: r.FallbackTag,
OutboundSelector: r.Selectors,
2020-11-25 19:01:53 +08:00
Tag: r.Tag,
}, nil
}
type RouterConfig struct {
2024-11-09 14:16:11 +03:00
RuleList []json.RawMessage `json:"rules"`
DomainStrategy *string `json:"domainStrategy"`
Balancers []*BalancingRule `json:"balancers"`
2020-11-25 19:01:53 +08:00
}
func (c *RouterConfig) getDomainStrategy() router.Config_DomainStrategy {
ds := ""
if c.DomainStrategy != nil {
ds = *c.DomainStrategy
}
switch strings.ToLower(ds) {
case "ipifnonmatch":
return router.Config_IpIfNonMatch
case "ipondemand":
return router.Config_IpOnDemand
default:
return router.Config_AsIs
}
}
func (c *RouterConfig) Build() (*router.Config, error) {
config := new(router.Config)
config.DomainStrategy = c.getDomainStrategy()
var rawRuleList []json.RawMessage
if c != nil {
rawRuleList = c.RuleList
}
for _, rawRule := range rawRuleList {
2026-01-26 18:45:25 +08:00
rule, err := parseRule(rawRule)
2020-11-25 19:01:53 +08:00
if err != nil {
return nil, err
}
config.Rule = append(config.Rule, rule)
}
2026-04-14 00:42:29 +08:00
2020-11-25 19:01:53 +08:00
for _, rawBalancer := range c.Balancers {
balancer, err := rawBalancer.Build()
if err != nil {
return nil, err
}
config.BalancingRule = append(config.BalancingRule, balancer)
}
2026-04-14 00:42:29 +08:00
2020-11-25 19:01:53 +08:00
return config, nil
}
type RouterRule struct {
2024-03-29 19:17:36 +04:00
RuleTag string `json:"ruleTag"`
2020-11-25 19:01:53 +08:00
OutboundTag string `json:"outboundTag"`
BalancerTag string `json:"balancerTag"`
}
2026-03-07 13:49:46 +03:00
type WebhookRuleConfig struct {
URL string `json:"url"`
Deduplication uint32 `json:"deduplication"`
Headers map[string]string `json:"headers"`
}
2020-11-25 19:01:53 +08:00
func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
type RawFieldRule struct {
RouterRule
2026-03-07 13:49:46 +03:00
Domain *StringList `json:"domain"`
Domains *StringList `json:"domains"`
IP *StringList `json:"ip"`
Port *PortList `json:"port"`
Network *NetworkList `json:"network"`
SourceIP *StringList `json:"sourceIP"`
Source *StringList `json:"source"`
SourcePort *PortList `json:"sourcePort"`
User *StringList `json:"user"`
VlessRoute *PortList `json:"vlessRoute"`
InboundTag *StringList `json:"inboundTag"`
Protocols *StringList `json:"protocol"`
Attributes map[string]string `json:"attrs"`
LocalIP *StringList `json:"localIP"`
LocalPort *PortList `json:"localPort"`
Process *StringList `json:"process"`
Webhook *WebhookRuleConfig `json:"webhook"`
2020-11-25 19:01:53 +08:00
}
rawFieldRule := new(RawFieldRule)
err := json.Unmarshal(msg, rawFieldRule)
if err != nil {
return nil, err
}
rule := new(router.RoutingRule)
2024-03-29 19:17:36 +04:00
rule.RuleTag = rawFieldRule.RuleTag
2020-11-25 19:01:53 +08:00
switch {
case len(rawFieldRule.OutboundTag) > 0:
rule.TargetTag = &router.RoutingRule_Tag{
Tag: rawFieldRule.OutboundTag,
}
case len(rawFieldRule.BalancerTag) > 0:
rule.TargetTag = &router.RoutingRule_BalancingTag{
BalancingTag: rawFieldRule.BalancerTag,
}
default:
2024-06-29 14:32:57 -04:00
return nil, errors.New("neither outboundTag nor balancerTag is specified in routing rule")
2020-11-25 19:01:53 +08:00
}
if rawFieldRule.Domain != nil {
2026-04-14 00:42:29 +08:00
rules, err := geodata.ParseDomainRules(*rawFieldRule.Domain, geodata.Domain_Substr)
if err != nil {
return nil, err
2020-11-25 19:01:53 +08:00
}
2026-04-14 00:42:29 +08:00
rule.Domain = rules
2020-11-25 19:01:53 +08:00
}
2021-01-22 11:35:56 +08:00
if rawFieldRule.Domains != nil {
2026-04-14 00:42:29 +08:00
rules, err := geodata.ParseDomainRules(*rawFieldRule.Domains, geodata.Domain_Substr)
if err != nil {
return nil, err
2021-01-22 11:35:56 +08:00
}
2026-04-14 00:42:29 +08:00
rule.Domain = rules
2021-01-22 11:35:56 +08:00
}
2020-11-25 19:01:53 +08:00
if rawFieldRule.IP != nil {
2026-04-14 00:42:29 +08:00
rules, err := geodata.ParseIPRules(*rawFieldRule.IP)
2020-11-25 19:01:53 +08:00
if err != nil {
return nil, err
}
2026-04-14 00:42:29 +08:00
rule.Ip = rules
2020-11-25 19:01:53 +08:00
}
if rawFieldRule.Port != nil {
rule.PortList = rawFieldRule.Port.Build()
}
if rawFieldRule.Network != nil {
rule.Networks = rawFieldRule.Network.Build()
}
if rawFieldRule.SourceIP == nil {
rawFieldRule.SourceIP = rawFieldRule.Source
}
2020-11-25 19:01:53 +08:00
if rawFieldRule.SourceIP != nil {
2026-04-14 00:42:29 +08:00
rules, err := geodata.ParseIPRules(*rawFieldRule.SourceIP)
2020-11-25 19:01:53 +08:00
if err != nil {
return nil, err
}
2026-04-14 00:42:29 +08:00
rule.SourceIp = rules
2020-11-25 19:01:53 +08:00
}
if rawFieldRule.SourcePort != nil {
rule.SourcePortList = rawFieldRule.SourcePort.Build()
}
if rawFieldRule.LocalIP != nil {
2026-04-14 00:42:29 +08:00
rules, err := geodata.ParseIPRules(*rawFieldRule.LocalIP)
if err != nil {
return nil, err
}
2026-04-14 00:42:29 +08:00
rule.LocalIp = rules
}
if rawFieldRule.LocalPort != nil {
rule.LocalPortList = rawFieldRule.LocalPort.Build()
}
2020-11-25 19:01:53 +08:00
if rawFieldRule.User != nil {
for _, s := range *rawFieldRule.User {
rule.UserEmail = append(rule.UserEmail, s)
}
}
if rawFieldRule.VlessRoute != nil {
rule.VlessRouteList = rawFieldRule.VlessRoute.Build()
}
2020-11-25 19:01:53 +08:00
if rawFieldRule.InboundTag != nil {
for _, s := range *rawFieldRule.InboundTag {
rule.InboundTag = append(rule.InboundTag, s)
}
}
if rawFieldRule.Protocols != nil {
for _, s := range *rawFieldRule.Protocols {
rule.Protocol = append(rule.Protocol, s)
}
}
if len(rawFieldRule.Attributes) > 0 {
rule.Attributes = rawFieldRule.Attributes
}
if rawFieldRule.Process != nil && len(*rawFieldRule.Process) > 0 {
rule.Process = *rawFieldRule.Process
2026-01-05 09:12:13 +08:00
}
2026-03-07 13:49:46 +03:00
if rawFieldRule.Webhook != nil && rawFieldRule.Webhook.URL != "" {
rule.Webhook = &router.WebhookConfig{
Url: rawFieldRule.Webhook.URL,
Deduplication: rawFieldRule.Webhook.Deduplication,
Headers: rawFieldRule.Webhook.Headers,
}
}
2020-11-25 19:01:53 +08:00
return rule, nil
}
2026-01-26 18:45:25 +08:00
func parseRule(msg json.RawMessage) (*router.RoutingRule, error) {
2020-11-25 19:01:53 +08:00
rawRule := new(RouterRule)
err := json.Unmarshal(msg, rawRule)
if err != nil {
2024-06-29 14:32:57 -04:00
return nil, errors.New("invalid router rule").Base(err)
2020-11-25 19:01:53 +08:00
}
fieldrule, err := parseFieldRule(msg)
if err != nil {
return nil, errors.New("invalid field rule").Base(err)
2020-11-25 19:01:53 +08:00
}
return fieldrule, nil
2020-11-25 19:01:53 +08:00
}