2020-11-25 13:01:53 +02:00
|
|
|
package conf
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
2020-12-04 03:36:16 +02:00
|
|
|
"github.com/xtls/xray-core/app/router"
|
2021-03-26 09:28:27 +02:00
|
|
|
"github.com/xtls/xray-core/common/matcher/domain/conf"
|
2021-03-24 17:01:20 +02:00
|
|
|
"github.com/xtls/xray-core/common/matcher/geoip"
|
|
|
|
"github.com/xtls/xray-core/common/matcher/geosite"
|
2020-12-04 03:36:16 +02:00
|
|
|
"github.com/xtls/xray-core/common/net"
|
2020-11-25 13:01:53 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
type RouterRulesConfig struct {
|
|
|
|
RuleList []json.RawMessage `json:"rules"`
|
|
|
|
DomainStrategy string `json:"domainStrategy"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type BalancingRule struct {
|
2021-03-27 09:33:20 +02:00
|
|
|
Tag string `json:"tag"`
|
|
|
|
Selectors StringList `json:"selector"`
|
2020-11-25 13:01:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *BalancingRule) Build() (*router.BalancingRule, error) {
|
|
|
|
if r.Tag == "" {
|
|
|
|
return nil, newError("empty balancer tag")
|
|
|
|
}
|
|
|
|
if len(r.Selectors) == 0 {
|
|
|
|
return nil, newError("empty selector list")
|
|
|
|
}
|
|
|
|
|
|
|
|
return &router.BalancingRule{
|
|
|
|
Tag: r.Tag,
|
|
|
|
OutboundSelector: []string(r.Selectors),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type RouterConfig struct {
|
|
|
|
Settings *RouterRulesConfig `json:"settings"` // Deprecated
|
|
|
|
RuleList []json.RawMessage `json:"rules"`
|
|
|
|
DomainStrategy *string `json:"domainStrategy"`
|
|
|
|
Balancers []*BalancingRule `json:"balancers"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *RouterConfig) getDomainStrategy() router.Config_DomainStrategy {
|
|
|
|
ds := ""
|
|
|
|
if c.DomainStrategy != nil {
|
|
|
|
ds = *c.DomainStrategy
|
|
|
|
} else if c.Settings != nil {
|
|
|
|
ds = c.Settings.DomainStrategy
|
|
|
|
}
|
|
|
|
|
|
|
|
switch strings.ToLower(ds) {
|
2021-04-07 17:53:21 +03:00
|
|
|
case "alwaysip", "always_ip", "always-ip":
|
2020-11-25 13:01:53 +02:00
|
|
|
return router.Config_UseIp
|
2021-04-07 17:53:21 +03:00
|
|
|
case "ipifnonmatch", "ip_if_non_match", "ip-if-non-match":
|
2020-11-25 13:01:53 +02:00
|
|
|
return router.Config_IpIfNonMatch
|
2021-04-07 17:53:21 +03:00
|
|
|
case "ipondemand", "ip_on_demand", "ip-on-demand":
|
2020-11-25 13:01:53 +02:00
|
|
|
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
|
|
|
|
if c.Settings != nil {
|
|
|
|
c.RuleList = append(c.RuleList, c.Settings.RuleList...)
|
|
|
|
rawRuleList = c.RuleList
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, rawRule := range rawRuleList {
|
|
|
|
rule, err := ParseRule(rawRule)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
config.Rule = append(config.Rule, rule)
|
|
|
|
}
|
|
|
|
for _, rawBalancer := range c.Balancers {
|
|
|
|
balancer, err := rawBalancer.Build()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
config.BalancingRule = append(config.BalancingRule, balancer)
|
|
|
|
}
|
|
|
|
return config, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type RouterRule struct {
|
|
|
|
Type string `json:"type"`
|
|
|
|
OutboundTag string `json:"outboundTag"`
|
|
|
|
BalancerTag string `json:"balancerTag"`
|
|
|
|
}
|
|
|
|
|
2021-03-24 17:01:20 +02:00
|
|
|
func ParseIP(s string) (*geoip.CIDR, error) {
|
2020-11-25 13:01:53 +02:00
|
|
|
var addr, mask string
|
|
|
|
i := strings.Index(s, "/")
|
|
|
|
if i < 0 {
|
|
|
|
addr = s
|
|
|
|
} else {
|
|
|
|
addr = s[:i]
|
|
|
|
mask = s[i+1:]
|
|
|
|
}
|
|
|
|
ip := net.ParseAddress(addr)
|
|
|
|
switch ip.Family() {
|
|
|
|
case net.AddressFamilyIPv4:
|
|
|
|
bits := uint32(32)
|
|
|
|
if len(mask) > 0 {
|
|
|
|
bits64, err := strconv.ParseUint(mask, 10, 32)
|
|
|
|
if err != nil {
|
|
|
|
return nil, newError("invalid network mask for router: ", mask).Base(err)
|
|
|
|
}
|
|
|
|
bits = uint32(bits64)
|
|
|
|
}
|
|
|
|
if bits > 32 {
|
|
|
|
return nil, newError("invalid network mask for router: ", bits)
|
|
|
|
}
|
2021-03-24 17:01:20 +02:00
|
|
|
return &geoip.CIDR{
|
2020-11-25 13:01:53 +02:00
|
|
|
Ip: []byte(ip.IP()),
|
|
|
|
Prefix: bits,
|
|
|
|
}, nil
|
|
|
|
case net.AddressFamilyIPv6:
|
|
|
|
bits := uint32(128)
|
|
|
|
if len(mask) > 0 {
|
|
|
|
bits64, err := strconv.ParseUint(mask, 10, 32)
|
|
|
|
if err != nil {
|
|
|
|
return nil, newError("invalid network mask for router: ", mask).Base(err)
|
|
|
|
}
|
|
|
|
bits = uint32(bits64)
|
|
|
|
}
|
|
|
|
if bits > 128 {
|
|
|
|
return nil, newError("invalid network mask for router: ", bits)
|
|
|
|
}
|
2021-03-24 17:01:20 +02:00
|
|
|
return &geoip.CIDR{
|
|
|
|
Ip: ip.IP(),
|
2020-11-25 13:01:53 +02:00
|
|
|
Prefix: bits,
|
|
|
|
}, nil
|
|
|
|
default:
|
|
|
|
return nil, newError("unsupported address for router: ", s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
|
|
|
type RawFieldRule struct {
|
|
|
|
RouterRule
|
2021-03-27 09:33:20 +02:00
|
|
|
Domain *StringList `json:"domain"`
|
|
|
|
Domains *StringList `json:"domains"`
|
|
|
|
IP *StringList `json:"ip"`
|
|
|
|
Port *PortList `json:"port"`
|
|
|
|
Network *NetworkList `json:"network"`
|
|
|
|
SourceIP *StringList `json:"source"`
|
|
|
|
SourcePort *PortList `json:"sourcePort"`
|
|
|
|
User *StringList `json:"user"`
|
|
|
|
InboundTag *StringList `json:"inboundTag"`
|
|
|
|
Protocols *StringList `json:"protocol"`
|
|
|
|
Attributes string `json:"attrs"`
|
2020-11-25 13:01:53 +02:00
|
|
|
}
|
|
|
|
rawFieldRule := new(RawFieldRule)
|
|
|
|
err := json.Unmarshal(msg, rawFieldRule)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
rule := new(router.RoutingRule)
|
|
|
|
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:
|
|
|
|
return nil, newError("neither outboundTag nor balancerTag is specified in routing rule")
|
|
|
|
}
|
|
|
|
|
|
|
|
if rawFieldRule.Domain != nil {
|
|
|
|
for _, domain := range *rawFieldRule.Domain {
|
2021-03-26 10:56:43 +02:00
|
|
|
rules, err := conf.ParseDomainRule(domain)
|
2020-11-25 13:01:53 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, newError("failed to parse domain rule: ", domain).Base(err)
|
|
|
|
}
|
|
|
|
rule.Domain = append(rule.Domain, rules...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-22 05:35:56 +02:00
|
|
|
if rawFieldRule.Domains != nil {
|
|
|
|
for _, domain := range *rawFieldRule.Domains {
|
2021-03-26 10:56:43 +02:00
|
|
|
rules, err := conf.ParseDomainRule(domain)
|
2021-01-22 05:35:56 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, newError("failed to parse domain rule: ", domain).Base(err)
|
|
|
|
}
|
|
|
|
rule.Domain = append(rule.Domain, rules...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-25 13:01:53 +02:00
|
|
|
if rawFieldRule.IP != nil {
|
2021-03-26 10:56:43 +02:00
|
|
|
geoipList, err := geoip.ParaseIPList(*rawFieldRule.IP)
|
2020-11-25 13:01:53 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
rule.Geoip = geoipList
|
|
|
|
}
|
|
|
|
|
|
|
|
if rawFieldRule.Port != nil {
|
|
|
|
rule.PortList = rawFieldRule.Port.Build()
|
|
|
|
}
|
|
|
|
|
|
|
|
if rawFieldRule.Network != nil {
|
|
|
|
rule.Networks = rawFieldRule.Network.Build()
|
|
|
|
}
|
|
|
|
|
|
|
|
if rawFieldRule.SourceIP != nil {
|
2021-03-26 10:56:43 +02:00
|
|
|
geoipList, err := geoip.ParaseIPList(*rawFieldRule.SourceIP)
|
2020-11-25 13:01:53 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
rule.SourceGeoip = geoipList
|
|
|
|
}
|
|
|
|
|
|
|
|
if rawFieldRule.SourcePort != nil {
|
|
|
|
rule.SourcePortList = rawFieldRule.SourcePort.Build()
|
|
|
|
}
|
|
|
|
|
|
|
|
if rawFieldRule.User != nil {
|
|
|
|
for _, s := range *rawFieldRule.User {
|
|
|
|
rule.UserEmail = append(rule.UserEmail, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
return rule, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ParseRule(msg json.RawMessage) (*router.RoutingRule, error) {
|
|
|
|
rawRule := new(RouterRule)
|
|
|
|
err := json.Unmarshal(msg, rawRule)
|
|
|
|
if err != nil {
|
|
|
|
return nil, newError("invalid router rule").Base(err)
|
|
|
|
}
|
|
|
|
if rawRule.Type == "field" {
|
|
|
|
fieldrule, err := parseFieldRule(msg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, newError("invalid field rule").Base(err)
|
|
|
|
}
|
|
|
|
return fieldrule, nil
|
|
|
|
}
|
|
|
|
if rawRule.Type == "chinaip" {
|
|
|
|
chinaiprule, err := parseChinaIPRule(msg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, newError("invalid chinaip rule").Base(err)
|
|
|
|
}
|
|
|
|
return chinaiprule, nil
|
|
|
|
}
|
|
|
|
if rawRule.Type == "chinasites" {
|
|
|
|
chinasitesrule, err := parseChinaSitesRule(msg)
|
|
|
|
if err != nil {
|
|
|
|
return nil, newError("invalid chinasites rule").Base(err)
|
|
|
|
}
|
|
|
|
return chinasitesrule, nil
|
|
|
|
}
|
|
|
|
return nil, newError("unknown router rule type: ", rawRule.Type)
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseChinaIPRule(data []byte) (*router.RoutingRule, error) {
|
|
|
|
rawRule := new(RouterRule)
|
|
|
|
err := json.Unmarshal(data, rawRule)
|
|
|
|
if err != nil {
|
|
|
|
return nil, newError("invalid router rule").Base(err)
|
|
|
|
}
|
2021-03-24 17:01:20 +02:00
|
|
|
chinaIPs, err := geoip.LoadGeoIP("CN")
|
2020-11-25 13:01:53 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, newError("failed to load geoip:cn").Base(err)
|
|
|
|
}
|
|
|
|
return &router.RoutingRule{
|
|
|
|
TargetTag: &router.RoutingRule_Tag{
|
|
|
|
Tag: rawRule.OutboundTag,
|
|
|
|
},
|
|
|
|
Cidr: chinaIPs,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseChinaSitesRule(data []byte) (*router.RoutingRule, error) {
|
|
|
|
rawRule := new(RouterRule)
|
|
|
|
err := json.Unmarshal(data, rawRule)
|
|
|
|
if err != nil {
|
|
|
|
return nil, newError("invalid router rule").Base(err).AtError()
|
|
|
|
}
|
2021-03-24 17:01:20 +02:00
|
|
|
domains, err := geosite.LoadGeositeWithAttr("geosite.dat", "CN")
|
2020-11-25 13:01:53 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, newError("failed to load geosite:cn.").Base(err)
|
|
|
|
}
|
|
|
|
return &router.RoutingRule{
|
|
|
|
TargetTag: &router.RoutingRule_Tag{
|
|
|
|
Tag: rawRule.OutboundTag,
|
|
|
|
},
|
|
|
|
Domain: domains,
|
|
|
|
}, nil
|
|
|
|
}
|