config.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. package router
  2. import (
  3. "context"
  4. "regexp"
  5. "runtime"
  6. "strings"
  7. "github.com/xtls/xray-core/common/errors"
  8. "github.com/xtls/xray-core/common/platform"
  9. "github.com/xtls/xray-core/common/platform/filesystem"
  10. "github.com/xtls/xray-core/features/outbound"
  11. "github.com/xtls/xray-core/features/routing"
  12. )
  13. type Rule struct {
  14. Tag string
  15. RuleTag string
  16. Balancer *Balancer
  17. Condition Condition
  18. }
  19. func (r *Rule) GetTag() (string, error) {
  20. if r.Balancer != nil {
  21. return r.Balancer.PickOutbound()
  22. }
  23. return r.Tag, nil
  24. }
  25. // Apply checks rule matching of current routing context.
  26. func (r *Rule) Apply(ctx routing.Context) bool {
  27. return r.Condition.Apply(ctx)
  28. }
  29. func (rr *RoutingRule) BuildCondition() (Condition, error) {
  30. conds := NewConditionChan()
  31. if len(rr.InboundTag) > 0 {
  32. conds.Add(NewInboundTagMatcher(rr.InboundTag))
  33. }
  34. if len(rr.Networks) > 0 {
  35. conds.Add(NewNetworkMatcher(rr.Networks))
  36. }
  37. if len(rr.Protocol) > 0 {
  38. conds.Add(NewProtocolMatcher(rr.Protocol))
  39. }
  40. if rr.PortList != nil {
  41. conds.Add(NewPortMatcher(rr.PortList, MatcherAsType_Target))
  42. }
  43. if rr.SourcePortList != nil {
  44. conds.Add(NewPortMatcher(rr.SourcePortList, MatcherAsType_Source))
  45. }
  46. if rr.LocalPortList != nil {
  47. conds.Add(NewPortMatcher(rr.LocalPortList, MatcherAsType_Local))
  48. }
  49. if rr.VlessRouteList != nil {
  50. conds.Add(NewPortMatcher(rr.VlessRouteList, MatcherAsType_VlessRoute))
  51. }
  52. if len(rr.UserEmail) > 0 {
  53. conds.Add(NewUserMatcher(rr.UserEmail))
  54. }
  55. if len(rr.Attributes) > 0 {
  56. configuredKeys := make(map[string]*regexp.Regexp)
  57. for key, value := range rr.Attributes {
  58. configuredKeys[strings.ToLower(key)] = regexp.MustCompile(value)
  59. }
  60. conds.Add(&AttributeMatcher{configuredKeys})
  61. }
  62. if len(rr.Geoip) > 0 {
  63. cond, err := NewIPMatcher(rr.Geoip, MatcherAsType_Target)
  64. if err != nil {
  65. return nil, err
  66. }
  67. conds.Add(cond)
  68. rr.Geoip = nil
  69. runtime.GC()
  70. }
  71. if len(rr.SourceGeoip) > 0 {
  72. cond, err := NewIPMatcher(rr.SourceGeoip, MatcherAsType_Source)
  73. if err != nil {
  74. return nil, err
  75. }
  76. conds.Add(cond)
  77. rr.SourceGeoip = nil
  78. runtime.GC()
  79. }
  80. if len(rr.LocalGeoip) > 0 {
  81. cond, err := NewIPMatcher(rr.LocalGeoip, MatcherAsType_Local)
  82. if err != nil {
  83. return nil, err
  84. }
  85. conds.Add(cond)
  86. errors.LogWarning(context.Background(), "Due to some limitations, in UDP connections, localIP is always equal to listen interface IP, so \"localIP\" rule condition does not work properly on UDP inbound connections that listen on all interfaces")
  87. rr.LocalGeoip = nil
  88. runtime.GC()
  89. }
  90. if len(rr.Domain) > 0 {
  91. var matcher *DomainMatcher
  92. var err error
  93. // Check if domain matcher cache is provided via environment
  94. domainMatcherPath := platform.NewEnvFlag(platform.MphCachePath).GetValue(func() string { return "" })
  95. if domainMatcherPath != "" {
  96. matcher, err = GetDomainMatcherWithRuleTag(domainMatcherPath, rr.RuleTag)
  97. if err != nil {
  98. return nil, errors.New("failed to build domain condition from cached MphDomainMatcher").Base(err)
  99. }
  100. errors.LogDebug(context.Background(), "MphDomainMatcher loaded from cache for ", rr.RuleTag, " rule tag)")
  101. } else {
  102. matcher, err = NewMphMatcherGroup(rr.Domain)
  103. if err != nil {
  104. return nil, errors.New("failed to build domain condition with MphDomainMatcher").Base(err)
  105. }
  106. errors.LogDebug(context.Background(), "MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)")
  107. }
  108. conds.Add(matcher)
  109. rr.Domain = nil
  110. runtime.GC()
  111. }
  112. if len(rr.Process) > 0 {
  113. conds.Add(NewProcessNameMatcher(rr.Process))
  114. }
  115. if conds.Len() == 0 {
  116. return nil, errors.New("this rule has no effective fields").AtWarning()
  117. }
  118. return conds, nil
  119. }
  120. // Build builds the balancing rule
  121. func (br *BalancingRule) Build(ohm outbound.Manager, dispatcher routing.Dispatcher) (*Balancer, error) {
  122. switch strings.ToLower(br.Strategy) {
  123. case "leastping":
  124. return &Balancer{
  125. selectors: br.OutboundSelector,
  126. strategy: &LeastPingStrategy{},
  127. fallbackTag: br.FallbackTag,
  128. ohm: ohm,
  129. }, nil
  130. case "roundrobin":
  131. return &Balancer{
  132. selectors: br.OutboundSelector,
  133. strategy: &RoundRobinStrategy{FallbackTag: br.FallbackTag},
  134. fallbackTag: br.FallbackTag,
  135. ohm: ohm,
  136. }, nil
  137. case "leastload":
  138. i, err := br.StrategySettings.GetInstance()
  139. if err != nil {
  140. return nil, err
  141. }
  142. s, ok := i.(*StrategyLeastLoadConfig)
  143. if !ok {
  144. return nil, errors.New("not a StrategyLeastLoadConfig").AtError()
  145. }
  146. leastLoadStrategy := NewLeastLoadStrategy(s)
  147. return &Balancer{
  148. selectors: br.OutboundSelector,
  149. ohm: ohm,
  150. fallbackTag: br.FallbackTag,
  151. strategy: leastLoadStrategy,
  152. }, nil
  153. case "random":
  154. fallthrough
  155. case "":
  156. return &Balancer{
  157. selectors: br.OutboundSelector,
  158. ohm: ohm,
  159. fallbackTag: br.FallbackTag,
  160. strategy: &RandomStrategy{FallbackTag: br.FallbackTag},
  161. }, nil
  162. default:
  163. return nil, errors.New("unrecognized balancer type")
  164. }
  165. }
  166. func GetDomainMatcherWithRuleTag(domainMatcherPath string, ruleTag string) (*DomainMatcher, error) {
  167. f, err := filesystem.NewFileReader(domainMatcherPath)
  168. if err != nil {
  169. return nil, errors.New("failed to load file: ", domainMatcherPath).Base(err)
  170. }
  171. defer f.Close()
  172. g, err := LoadGeoSiteMatcher(f, ruleTag)
  173. if err != nil {
  174. return nil, errors.New("failed to load file:", domainMatcherPath).Base(err)
  175. }
  176. return &DomainMatcher{
  177. Matchers: g,
  178. }, nil
  179. }