config.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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/filesystem"
  9. "github.com/xtls/xray-core/features/outbound"
  10. "github.com/xtls/xray-core/features/routing"
  11. "google.golang.org/protobuf/proto"
  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. geoip := rr.Geoip
  64. if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
  65. var err error
  66. geoip, err = GetGeoIPList(rr.Geoip)
  67. if err != nil {
  68. return nil, errors.New("failed to build geoip from mmap").Base(err)
  69. }
  70. }
  71. cond, err := NewIPMatcher(geoip, MatcherAsType_Target)
  72. if err != nil {
  73. return nil, err
  74. }
  75. conds.Add(cond)
  76. }
  77. if len(rr.SourceGeoip) > 0 {
  78. cond, err := NewIPMatcher(rr.SourceGeoip, MatcherAsType_Source)
  79. if err != nil {
  80. return nil, err
  81. }
  82. conds.Add(cond)
  83. }
  84. if len(rr.LocalGeoip) > 0 {
  85. cond, err := NewIPMatcher(rr.LocalGeoip, MatcherAsType_Local)
  86. if err != nil {
  87. return nil, err
  88. }
  89. conds.Add(cond)
  90. 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")
  91. }
  92. if len(rr.Domain) > 0 {
  93. domains := rr.Domain
  94. if runtime.GOOS != "windows" && runtime.GOOS != "wasm" {
  95. var err error
  96. domains, err = GetDomainList(rr.Domain)
  97. if err != nil {
  98. return nil, errors.New("failed to build domains from mmap").Base(err)
  99. }
  100. }
  101. matcher, err := NewMphMatcherGroup(domains)
  102. if err != nil {
  103. return nil, errors.New("failed to build domain condition with MphDomainMatcher").Base(err)
  104. }
  105. errors.LogDebug(context.Background(), "MphDomainMatcher is enabled for ", len(domains), " domain rule(s)")
  106. conds.Add(matcher)
  107. }
  108. if len(rr.Process) > 0 {
  109. conds.Add(NewProcessNameMatcher(rr.Process))
  110. }
  111. if conds.Len() == 0 {
  112. return nil, errors.New("this rule has no effective fields").AtWarning()
  113. }
  114. return conds, nil
  115. }
  116. // Build builds the balancing rule
  117. func (br *BalancingRule) Build(ohm outbound.Manager, dispatcher routing.Dispatcher) (*Balancer, error) {
  118. switch strings.ToLower(br.Strategy) {
  119. case "leastping":
  120. return &Balancer{
  121. selectors: br.OutboundSelector,
  122. strategy: &LeastPingStrategy{},
  123. fallbackTag: br.FallbackTag,
  124. ohm: ohm,
  125. }, nil
  126. case "roundrobin":
  127. return &Balancer{
  128. selectors: br.OutboundSelector,
  129. strategy: &RoundRobinStrategy{FallbackTag: br.FallbackTag},
  130. fallbackTag: br.FallbackTag,
  131. ohm: ohm,
  132. }, nil
  133. case "leastload":
  134. i, err := br.StrategySettings.GetInstance()
  135. if err != nil {
  136. return nil, err
  137. }
  138. s, ok := i.(*StrategyLeastLoadConfig)
  139. if !ok {
  140. return nil, errors.New("not a StrategyLeastLoadConfig").AtError()
  141. }
  142. leastLoadStrategy := NewLeastLoadStrategy(s)
  143. return &Balancer{
  144. selectors: br.OutboundSelector,
  145. ohm: ohm,
  146. fallbackTag: br.FallbackTag,
  147. strategy: leastLoadStrategy,
  148. }, nil
  149. case "random":
  150. fallthrough
  151. case "":
  152. return &Balancer{
  153. selectors: br.OutboundSelector,
  154. ohm: ohm,
  155. fallbackTag: br.FallbackTag,
  156. strategy: &RandomStrategy{FallbackTag: br.FallbackTag},
  157. }, nil
  158. default:
  159. return nil, errors.New("unrecognized balancer type")
  160. }
  161. }
  162. func GetGeoIPList(ips []*GeoIP) ([]*GeoIP, error) {
  163. geoipList := []*GeoIP{}
  164. for _, ip := range ips {
  165. if ip.CountryCode != "" {
  166. val := strings.Split(ip.CountryCode, "_")
  167. fileName := "geoip.dat"
  168. if len(val) == 2 {
  169. fileName = strings.ToLower(val[0])
  170. }
  171. bs, err := filesystem.ReadAsset(fileName)
  172. if err != nil {
  173. return nil, errors.New("failed to load file: ", fileName).Base(err)
  174. }
  175. bs = filesystem.Find(bs, []byte(ip.CountryCode))
  176. var geoip GeoIP
  177. if err := proto.Unmarshal(bs, &geoip); err != nil {
  178. return nil, errors.New("failed Unmarshal :").Base(err)
  179. }
  180. geoipList = append(geoipList, &geoip)
  181. } else {
  182. geoipList = append(geoipList, ip)
  183. }
  184. }
  185. return geoipList, nil
  186. }
  187. func GetDomainList(domains []*Domain) ([]*Domain, error) {
  188. domainList := []*Domain{}
  189. for _, domain := range domains {
  190. val := strings.Split(domain.Value, "_")
  191. if len(val) >= 2 {
  192. fileName := val[0]
  193. code := val[1]
  194. bs, err := filesystem.ReadAsset(fileName)
  195. if err != nil {
  196. return nil, errors.New("failed to load file: ", fileName).Base(err)
  197. }
  198. bs = filesystem.Find(bs, []byte(code))
  199. var geosite GeoSite
  200. if err := proto.Unmarshal(bs, &geosite); err != nil {
  201. return nil, errors.New("failed Unmarshal :").Base(err)
  202. }
  203. // parse attr
  204. if len(val) == 3 {
  205. siteWithAttr := strings.Split(val[2], ",")
  206. attrs := ParseAttrs(siteWithAttr)
  207. if !attrs.IsEmpty() {
  208. filteredDomains := make([]*Domain, 0, len(domains))
  209. for _, domain := range geosite.Domain {
  210. if attrs.Match(domain) {
  211. filteredDomains = append(filteredDomains, domain)
  212. }
  213. }
  214. geosite.Domain = filteredDomains
  215. }
  216. }
  217. domainList = append(domainList, geosite.Domain...)
  218. } else {
  219. domainList = append(domainList, domain)
  220. }
  221. }
  222. return domainList, nil
  223. }