github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/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  
     8  	core "github.com/v2fly/v2ray-core/v5"
     9  	"github.com/v2fly/v2ray-core/v5/common"
    10  	"github.com/v2fly/v2ray-core/v5/common/platform"
    11  	"github.com/v2fly/v2ray-core/v5/features/dns"
    12  	"github.com/v2fly/v2ray-core/v5/features/outbound"
    13  	"github.com/v2fly/v2ray-core/v5/features/routing"
    14  	routing_dns "github.com/v2fly/v2ray-core/v5/features/routing/dns"
    15  	"github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon"
    16  	"github.com/v2fly/v2ray-core/v5/infra/conf/geodata"
    17  )
    18  
    19  // Router is an implementation of routing.Router.
    20  type Router struct {
    21  	domainStrategy DomainStrategy
    22  	rules          []*Rule
    23  	balancers      map[string]*Balancer
    24  	dns            dns.Client
    25  }
    26  
    27  // Route is an implementation of routing.Route.
    28  type Route struct {
    29  	routing.Context
    30  	outboundGroupTags []string
    31  	outboundTag       string
    32  }
    33  
    34  // Init initializes the Router.
    35  func (r *Router) Init(ctx context.Context, config *Config, d dns.Client, ohm outbound.Manager, dispatcher routing.Dispatcher) error {
    36  	r.domainStrategy = config.DomainStrategy
    37  	r.dns = d
    38  
    39  	r.balancers = make(map[string]*Balancer, len(config.BalancingRule))
    40  	for _, rule := range config.BalancingRule {
    41  		balancer, err := rule.Build(ohm, dispatcher)
    42  		if err != nil {
    43  			return err
    44  		}
    45  		balancer.InjectContext(ctx)
    46  		r.balancers[rule.Tag] = balancer
    47  	}
    48  
    49  	r.rules = make([]*Rule, 0, len(config.Rule))
    50  	for _, rule := range config.Rule {
    51  		cond, err := rule.BuildCondition()
    52  		if err != nil {
    53  			return err
    54  		}
    55  		rr := &Rule{
    56  			Condition: cond,
    57  			Tag:       rule.GetTag(),
    58  		}
    59  		btag := rule.GetBalancingTag()
    60  		if len(btag) > 0 {
    61  			brule, found := r.balancers[btag]
    62  			if !found {
    63  				return newError("balancer ", btag, " not found")
    64  			}
    65  			rr.Balancer = brule
    66  		}
    67  		r.rules = append(r.rules, rr)
    68  	}
    69  
    70  	return nil
    71  }
    72  
    73  // PickRoute implements routing.Router.
    74  func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) {
    75  	rule, ctx, err := r.pickRouteInternal(ctx)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	tag, err := rule.GetTag()
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	return &Route{Context: ctx, outboundTag: tag}, nil
    84  }
    85  
    86  func (r *Router) pickRouteInternal(ctx routing.Context) (*Rule, routing.Context, error) {
    87  	// SkipDNSResolve is set from DNS module.
    88  	// the DOH remote server maybe a domain name,
    89  	// this prevents cycle resolving dead loop
    90  	skipDNSResolve := ctx.GetSkipDNSResolve()
    91  
    92  	if r.domainStrategy == DomainStrategy_IpOnDemand && !skipDNSResolve {
    93  		ctx = routing_dns.ContextWithDNSClient(ctx, r.dns)
    94  	}
    95  
    96  	for _, rule := range r.rules {
    97  		if rule.Apply(ctx) {
    98  			return rule, ctx, nil
    99  		}
   100  	}
   101  
   102  	if r.domainStrategy != DomainStrategy_IpIfNonMatch || len(ctx.GetTargetDomain()) == 0 || skipDNSResolve {
   103  		return nil, ctx, common.ErrNoClue
   104  	}
   105  
   106  	ctx = routing_dns.ContextWithDNSClient(ctx, r.dns)
   107  
   108  	// Try applying rules again if we have IPs.
   109  	for _, rule := range r.rules {
   110  		if rule.Apply(ctx) {
   111  			return rule, ctx, nil
   112  		}
   113  	}
   114  
   115  	return nil, ctx, common.ErrNoClue
   116  }
   117  
   118  // Start implements common.Runnable.
   119  func (r *Router) Start() error {
   120  	return nil
   121  }
   122  
   123  // Close implements common.Closable.
   124  func (r *Router) Close() error {
   125  	return nil
   126  }
   127  
   128  // Type implements common.HasType.
   129  func (*Router) Type() interface{} {
   130  	return routing.RouterType()
   131  }
   132  
   133  // GetOutboundGroupTags implements routing.Route.
   134  func (r *Route) GetOutboundGroupTags() []string {
   135  	return r.outboundGroupTags
   136  }
   137  
   138  // GetOutboundTag implements routing.Route.
   139  func (r *Route) GetOutboundTag() string {
   140  	return r.outboundTag
   141  }
   142  
   143  func init() {
   144  	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
   145  		r := new(Router)
   146  		if err := core.RequireFeatures(ctx, func(d dns.Client, ohm outbound.Manager, dispatcher routing.Dispatcher) error {
   147  			return r.Init(ctx, config.(*Config), d, ohm, dispatcher)
   148  		}); err != nil {
   149  			return nil, err
   150  		}
   151  		return r, nil
   152  	}))
   153  
   154  	common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
   155  		ctx = cfgcommon.NewConfigureLoadingContext(ctx)
   156  
   157  		geoloadername := platform.NewEnvFlag("v2ray.conf.geoloader").GetValue(func() string {
   158  			return "standard"
   159  		})
   160  
   161  		if loader, err := geodata.GetGeoDataLoader(geoloadername); err == nil {
   162  			cfgcommon.SetGeoDataLoader(ctx, loader)
   163  		} else {
   164  			return nil, newError("unable to create geo data loader ").Base(err)
   165  		}
   166  
   167  		cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(ctx)
   168  		geoLoader := cfgEnv.GetGeoLoader()
   169  
   170  		simplifiedConfig := config.(*SimplifiedConfig)
   171  
   172  		var routingRules []*RoutingRule
   173  
   174  		for _, v := range simplifiedConfig.Rule {
   175  			rule := new(RoutingRule)
   176  
   177  			for _, geo := range v.Geoip {
   178  				if geo.Code != "" {
   179  					filepath := "geoip.dat"
   180  					if geo.FilePath != "" {
   181  						filepath = geo.FilePath
   182  					} else {
   183  						geo.CountryCode = geo.Code
   184  					}
   185  					var err error
   186  					geo.Cidr, err = geoLoader.LoadIP(filepath, geo.Code)
   187  					if err != nil {
   188  						return nil, newError("unable to load geoip").Base(err)
   189  					}
   190  				}
   191  			}
   192  			rule.Geoip = v.Geoip
   193  
   194  			for _, geo := range v.SourceGeoip {
   195  				if geo.Code != "" {
   196  					filepath := "geoip.dat"
   197  					if geo.FilePath != "" {
   198  						filepath = geo.FilePath
   199  					} else {
   200  						geo.CountryCode = geo.Code
   201  					}
   202  					var err error
   203  					geo.Cidr, err = geoLoader.LoadIP(filepath, geo.Code)
   204  					if err != nil {
   205  						return nil, newError("unable to load geoip").Base(err)
   206  					}
   207  				}
   208  			}
   209  			rule.SourceGeoip = v.SourceGeoip
   210  
   211  			for _, geo := range v.GeoDomain {
   212  				if geo.Code != "" {
   213  					filepath := "geosite.dat"
   214  					if geo.FilePath != "" {
   215  						filepath = geo.FilePath
   216  					}
   217  					var err error
   218  					geo.Domain, err = geoLoader.LoadGeoSiteWithAttr(filepath, geo.Code)
   219  					if err != nil {
   220  						return nil, newError("unable to load geodomain").Base(err)
   221  					}
   222  				}
   223  			}
   224  			if v.PortList != "" {
   225  				portList := &cfgcommon.PortList{}
   226  				err := portList.UnmarshalText(v.PortList)
   227  				if err != nil {
   228  					return nil, err
   229  				}
   230  				rule.PortList = portList.Build()
   231  			}
   232  			if v.SourcePortList != "" {
   233  				portList := &cfgcommon.PortList{}
   234  				err := portList.UnmarshalText(v.SourcePortList)
   235  				if err != nil {
   236  					return nil, err
   237  				}
   238  				rule.SourcePortList = portList.Build()
   239  			}
   240  			rule.Domain = v.Domain
   241  			rule.GeoDomain = v.GeoDomain
   242  			rule.Networks = v.Networks.GetNetwork()
   243  			rule.Protocol = v.Protocol
   244  			rule.Attributes = v.Attributes
   245  			rule.UserEmail = v.UserEmail
   246  			rule.InboundTag = v.InboundTag
   247  			rule.DomainMatcher = v.DomainMatcher
   248  			switch s := v.TargetTag.(type) {
   249  			case *SimplifiedRoutingRule_Tag:
   250  				rule.TargetTag = &RoutingRule_Tag{s.Tag}
   251  			case *SimplifiedRoutingRule_BalancingTag:
   252  				rule.TargetTag = &RoutingRule_BalancingTag{s.BalancingTag}
   253  			}
   254  			routingRules = append(routingRules, rule)
   255  		}
   256  
   257  		fullConfig := &Config{
   258  			DomainStrategy: simplifiedConfig.DomainStrategy,
   259  			Rule:           routingRules,
   260  			BalancingRule:  simplifiedConfig.BalancingRule,
   261  		}
   262  		return common.CreateObject(ctx, fullConfig)
   263  	}))
   264  }