github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/infra/conf/synthetic/router/router.go (about)

     1  package router
     2  
     3  //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
     4  
     5  import (
     6  	"context"
     7  	"encoding/json"
     8  	"strings"
     9  
    10  	"github.com/golang/protobuf/proto"
    11  
    12  	"github.com/v2fly/v2ray-core/v5/app/router"
    13  	"github.com/v2fly/v2ray-core/v5/common/platform"
    14  	"github.com/v2fly/v2ray-core/v5/common/serial"
    15  	"github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon"
    16  	"github.com/v2fly/v2ray-core/v5/infra/conf/geodata"
    17  	rule2 "github.com/v2fly/v2ray-core/v5/infra/conf/rule"
    18  )
    19  
    20  type RouterRulesConfig struct { // nolint: revive
    21  	RuleList       []json.RawMessage `json:"rules"`
    22  	DomainStrategy string            `json:"domainStrategy"`
    23  }
    24  
    25  // StrategyConfig represents a strategy config
    26  type StrategyConfig struct {
    27  	Type     string           `json:"type"`
    28  	Settings *json.RawMessage `json:"settings"`
    29  }
    30  
    31  type BalancingRule struct {
    32  	Tag         string               `json:"tag"`
    33  	Selectors   cfgcommon.StringList `json:"selector"`
    34  	Strategy    StrategyConfig       `json:"strategy"`
    35  	FallbackTag string               `json:"fallbackTag"`
    36  }
    37  
    38  // Build builds the balancing rule
    39  func (r *BalancingRule) Build() (*router.BalancingRule, error) {
    40  	if r.Tag == "" {
    41  		return nil, newError("empty balancer tag")
    42  	}
    43  	if len(r.Selectors) == 0 {
    44  		return nil, newError("empty selector list")
    45  	}
    46  
    47  	var strategy string
    48  	switch strings.ToLower(r.Strategy.Type) {
    49  	case strategyRandom, "":
    50  		r.Strategy.Type = strategyRandom
    51  		strategy = strategyRandom
    52  	case strategyLeastLoad:
    53  		strategy = strategyLeastLoad
    54  	case strategyLeastPing:
    55  		strategy = "leastping"
    56  	default:
    57  		return nil, newError("unknown balancing strategy: " + r.Strategy.Type)
    58  	}
    59  
    60  	settings := []byte("{}")
    61  	if r.Strategy.Settings != nil {
    62  		settings = ([]byte)(*r.Strategy.Settings)
    63  	}
    64  	rawConfig, err := strategyConfigLoader.LoadWithID(settings, r.Strategy.Type)
    65  	if err != nil {
    66  		return nil, newError("failed to parse to strategy config.").Base(err)
    67  	}
    68  	var ts proto.Message
    69  	if builder, ok := rawConfig.(cfgcommon.Buildable); ok {
    70  		ts, err = builder.Build()
    71  		if err != nil {
    72  			return nil, err
    73  		}
    74  	}
    75  
    76  	return &router.BalancingRule{
    77  		Strategy:         strategy,
    78  		StrategySettings: serial.ToTypedMessage(ts),
    79  		FallbackTag:      r.FallbackTag,
    80  		OutboundSelector: r.Selectors,
    81  		Tag:              r.Tag,
    82  	}, nil
    83  }
    84  
    85  type RouterConfig struct { // nolint: revive
    86  	Settings       *RouterRulesConfig `json:"settings"` // Deprecated
    87  	RuleList       []json.RawMessage  `json:"rules"`
    88  	DomainStrategy *string            `json:"domainStrategy"`
    89  	Balancers      []*BalancingRule   `json:"balancers"`
    90  
    91  	DomainMatcher string `json:"domainMatcher"`
    92  
    93  	cfgctx context.Context
    94  }
    95  
    96  func (c *RouterConfig) getDomainStrategy() router.DomainStrategy {
    97  	ds := ""
    98  	if c.DomainStrategy != nil {
    99  		ds = *c.DomainStrategy
   100  	} else if c.Settings != nil {
   101  		ds = c.Settings.DomainStrategy
   102  	}
   103  
   104  	switch strings.ToLower(ds) {
   105  	case "alwaysip", "always_ip", "always-ip":
   106  		return router.DomainStrategy_UseIp
   107  	case "ipifnonmatch", "ip_if_non_match", "ip-if-non-match":
   108  		return router.DomainStrategy_IpIfNonMatch
   109  	case "ipondemand", "ip_on_demand", "ip-on-demand":
   110  		return router.DomainStrategy_IpOnDemand
   111  	default:
   112  		return router.DomainStrategy_AsIs
   113  	}
   114  }
   115  
   116  func (c *RouterConfig) BuildV5(ctx context.Context) (*router.Config, error) {
   117  	c.cfgctx = ctx
   118  	return c.Build()
   119  }
   120  
   121  func (c *RouterConfig) Build() (*router.Config, error) {
   122  	config := new(router.Config)
   123  	config.DomainStrategy = c.getDomainStrategy()
   124  
   125  	if c.cfgctx == nil {
   126  		c.cfgctx = cfgcommon.NewConfigureLoadingContext(context.Background())
   127  
   128  		geoloadername := platform.NewEnvFlag("v2ray.conf.geoloader").GetValue(func() string {
   129  			return "standard"
   130  		})
   131  
   132  		if loader, err := geodata.GetGeoDataLoader(geoloadername); err == nil {
   133  			cfgcommon.SetGeoDataLoader(c.cfgctx, loader)
   134  		} else {
   135  			return nil, newError("unable to create geo data loader ").Base(err)
   136  		}
   137  	}
   138  
   139  	var rawRuleList []json.RawMessage
   140  	if c != nil {
   141  		rawRuleList = c.RuleList
   142  		if c.Settings != nil {
   143  			c.RuleList = append(c.RuleList, c.Settings.RuleList...)
   144  			rawRuleList = c.RuleList
   145  		}
   146  	}
   147  
   148  	for _, rawRule := range rawRuleList {
   149  		rule, err := rule2.ParseRule(c.cfgctx, rawRule)
   150  		if err != nil {
   151  			return nil, err
   152  		}
   153  
   154  		if rule.DomainMatcher == "" {
   155  			rule.DomainMatcher = c.DomainMatcher
   156  		}
   157  
   158  		config.Rule = append(config.Rule, rule)
   159  	}
   160  	for _, rawBalancer := range c.Balancers {
   161  		balancer, err := rawBalancer.Build()
   162  		if err != nil {
   163  			return nil, err
   164  		}
   165  		config.BalancingRule = append(config.BalancingRule, balancer)
   166  	}
   167  	return config, nil
   168  }