github.com/v2fly/v2ray-core/v4@v4.45.2/app/router/router.go (about)

     1  //go:build !confonly
     2  // +build !confonly
     3  
     4  package router
     5  
     6  //go:generate go run github.com/v2fly/v2ray-core/v4/common/errors/errorgen
     7  
     8  import (
     9  	"context"
    10  
    11  	core "github.com/v2fly/v2ray-core/v4"
    12  	"github.com/v2fly/v2ray-core/v4/common"
    13  	"github.com/v2fly/v2ray-core/v4/features/dns"
    14  	"github.com/v2fly/v2ray-core/v4/features/outbound"
    15  	"github.com/v2fly/v2ray-core/v4/features/routing"
    16  	routing_dns "github.com/v2fly/v2ray-core/v4/features/routing/dns"
    17  )
    18  
    19  // Router is an implementation of routing.Router.
    20  type Router struct {
    21  	domainStrategy Config_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) 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)
    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 == Config_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 != Config_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 (*Router) Start() error {
   120  	return nil
   121  }
   122  
   123  // Close implements common.Closable.
   124  func (*Router) Close() error {
   125  	return nil
   126  }
   127  
   128  // Type implement 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) error {
   147  			return r.Init(ctx, config.(*Config), d, ohm)
   148  		}); err != nil {
   149  			return nil, err
   150  		}
   151  		return r, nil
   152  	}))
   153  }