config.go 5.3 KB

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