github.com/moqsien/xraycore@v1.8.5/infra/conf/router.go (about) 1 package conf 2 3 import ( 4 "encoding/json" 5 "runtime" 6 "strconv" 7 "strings" 8 9 "github.com/moqsien/xraycore/app/router" 10 "github.com/moqsien/xraycore/common/net" 11 "github.com/moqsien/xraycore/common/platform/filesystem" 12 "google.golang.org/protobuf/proto" 13 ) 14 15 type RouterRulesConfig struct { 16 RuleList []json.RawMessage `json:"rules"` 17 DomainStrategy string `json:"domainStrategy"` 18 } 19 20 // StrategyConfig represents a strategy config 21 type StrategyConfig struct { 22 Type string `json:"type"` 23 Settings *json.RawMessage `json:"settings"` 24 } 25 26 type BalancingRule struct { 27 Tag string `json:"tag"` 28 Selectors StringList `json:"selector"` 29 Strategy StrategyConfig `json:"strategy"` 30 } 31 32 func (r *BalancingRule) Build() (*router.BalancingRule, error) { 33 if r.Tag == "" { 34 return nil, newError("empty balancer tag") 35 } 36 if len(r.Selectors) == 0 { 37 return nil, newError("empty selector list") 38 } 39 40 var strategy string 41 switch strings.ToLower(r.Strategy.Type) { 42 case strategyRandom, "": 43 strategy = strategyRandom 44 case strategyLeastPing: 45 strategy = "leastPing" 46 default: 47 return nil, newError("unknown balancing strategy: " + r.Strategy.Type) 48 } 49 50 return &router.BalancingRule{ 51 Tag: r.Tag, 52 OutboundSelector: []string(r.Selectors), 53 Strategy: strategy, 54 }, nil 55 } 56 57 type RouterConfig struct { 58 Settings *RouterRulesConfig `json:"settings"` // Deprecated 59 RuleList []json.RawMessage `json:"rules"` 60 DomainStrategy *string `json:"domainStrategy"` 61 Balancers []*BalancingRule `json:"balancers"` 62 63 DomainMatcher string `json:"domainMatcher"` 64 } 65 66 func (c *RouterConfig) getDomainStrategy() router.Config_DomainStrategy { 67 ds := "" 68 if c.DomainStrategy != nil { 69 ds = *c.DomainStrategy 70 } else if c.Settings != nil { 71 ds = c.Settings.DomainStrategy 72 } 73 74 switch strings.ToLower(ds) { 75 case "alwaysip": 76 return router.Config_UseIp 77 case "ipifnonmatch": 78 return router.Config_IpIfNonMatch 79 case "ipondemand": 80 return router.Config_IpOnDemand 81 default: 82 return router.Config_AsIs 83 } 84 } 85 86 func (c *RouterConfig) Build() (*router.Config, error) { 87 config := new(router.Config) 88 config.DomainStrategy = c.getDomainStrategy() 89 90 var rawRuleList []json.RawMessage 91 if c != nil { 92 rawRuleList = c.RuleList 93 if c.Settings != nil { 94 c.RuleList = append(c.RuleList, c.Settings.RuleList...) 95 rawRuleList = c.RuleList 96 } 97 } 98 99 for _, rawRule := range rawRuleList { 100 rule, err := ParseRule(rawRule) 101 if err != nil { 102 return nil, err 103 } 104 105 if rule.DomainMatcher == "" { 106 rule.DomainMatcher = c.DomainMatcher 107 } 108 109 config.Rule = append(config.Rule, rule) 110 } 111 for _, rawBalancer := range c.Balancers { 112 balancer, err := rawBalancer.Build() 113 if err != nil { 114 return nil, err 115 } 116 config.BalancingRule = append(config.BalancingRule, balancer) 117 } 118 return config, nil 119 } 120 121 type RouterRule struct { 122 Type string `json:"type"` 123 OutboundTag string `json:"outboundTag"` 124 BalancerTag string `json:"balancerTag"` 125 126 DomainMatcher string `json:"domainMatcher"` 127 } 128 129 func ParseIP(s string) (*router.CIDR, error) { 130 var addr, mask string 131 i := strings.Index(s, "/") 132 if i < 0 { 133 addr = s 134 } else { 135 addr = s[:i] 136 mask = s[i+1:] 137 } 138 ip := net.ParseAddress(addr) 139 switch ip.Family() { 140 case net.AddressFamilyIPv4: 141 bits := uint32(32) 142 if len(mask) > 0 { 143 bits64, err := strconv.ParseUint(mask, 10, 32) 144 if err != nil { 145 return nil, newError("invalid network mask for router: ", mask).Base(err) 146 } 147 bits = uint32(bits64) 148 } 149 if bits > 32 { 150 return nil, newError("invalid network mask for router: ", bits) 151 } 152 return &router.CIDR{ 153 Ip: []byte(ip.IP()), 154 Prefix: bits, 155 }, nil 156 case net.AddressFamilyIPv6: 157 bits := uint32(128) 158 if len(mask) > 0 { 159 bits64, err := strconv.ParseUint(mask, 10, 32) 160 if err != nil { 161 return nil, newError("invalid network mask for router: ", mask).Base(err) 162 } 163 bits = uint32(bits64) 164 } 165 if bits > 128 { 166 return nil, newError("invalid network mask for router: ", bits) 167 } 168 return &router.CIDR{ 169 Ip: []byte(ip.IP()), 170 Prefix: bits, 171 }, nil 172 default: 173 return nil, newError("unsupported address for router: ", s) 174 } 175 } 176 177 func loadGeoIP(code string) ([]*router.CIDR, error) { 178 return loadIP("geoip.dat", code) 179 } 180 181 var ( 182 FileCache = make(map[string][]byte) 183 IPCache = make(map[string]*router.GeoIP) 184 SiteCache = make(map[string]*router.GeoSite) 185 ) 186 187 func loadFile(file string) ([]byte, error) { 188 if FileCache[file] == nil { 189 bs, err := filesystem.ReadAsset(file) 190 if err != nil { 191 return nil, newError("failed to open file: ", file).Base(err) 192 } 193 if len(bs) == 0 { 194 return nil, newError("empty file: ", file) 195 } 196 // Do not cache file, may save RAM when there 197 // are many files, but consume CPU each time. 198 return bs, nil 199 FileCache[file] = bs 200 } 201 return FileCache[file], nil 202 } 203 204 func loadIP(file, code string) ([]*router.CIDR, error) { 205 index := file + ":" + code 206 if IPCache[index] == nil { 207 bs, err := loadFile(file) 208 if err != nil { 209 return nil, newError("failed to load file: ", file).Base(err) 210 } 211 bs = find(bs, []byte(code)) 212 if bs == nil { 213 return nil, newError("code not found in ", file, ": ", code) 214 } 215 var geoip router.GeoIP 216 if err := proto.Unmarshal(bs, &geoip); err != nil { 217 return nil, newError("error unmarshal IP in ", file, ": ", code).Base(err) 218 } 219 defer runtime.GC() // or debug.FreeOSMemory() 220 return geoip.Cidr, nil // do not cache geoip 221 IPCache[index] = &geoip 222 } 223 return IPCache[index].Cidr, nil 224 } 225 226 func loadSite(file, code string) ([]*router.Domain, error) { 227 index := file + ":" + code 228 if SiteCache[index] == nil { 229 bs, err := loadFile(file) 230 if err != nil { 231 return nil, newError("failed to load file: ", file).Base(err) 232 } 233 bs = find(bs, []byte(code)) 234 if bs == nil { 235 return nil, newError("list not found in ", file, ": ", code) 236 } 237 var geosite router.GeoSite 238 if err := proto.Unmarshal(bs, &geosite); err != nil { 239 return nil, newError("error unmarshal Site in ", file, ": ", code).Base(err) 240 } 241 defer runtime.GC() // or debug.FreeOSMemory() 242 return geosite.Domain, nil // do not cache geosite 243 SiteCache[index] = &geosite 244 } 245 return SiteCache[index].Domain, nil 246 } 247 248 func DecodeVarint(buf []byte) (x uint64, n int) { 249 for shift := uint(0); shift < 64; shift += 7 { 250 if n >= len(buf) { 251 return 0, 0 252 } 253 b := uint64(buf[n]) 254 n++ 255 x |= (b & 0x7F) << shift 256 if (b & 0x80) == 0 { 257 return x, n 258 } 259 } 260 261 // The number is too large to represent in a 64-bit value. 262 return 0, 0 263 } 264 265 func find(data, code []byte) []byte { 266 codeL := len(code) 267 if codeL == 0 { 268 return nil 269 } 270 for { 271 dataL := len(data) 272 if dataL < 2 { 273 return nil 274 } 275 x, y := DecodeVarint(data[1:]) 276 if x == 0 && y == 0 { 277 return nil 278 } 279 headL, bodyL := 1+y, int(x) 280 dataL -= headL 281 if dataL < bodyL { 282 return nil 283 } 284 data = data[headL:] 285 if int(data[1]) == codeL { 286 for i := 0; i < codeL && data[2+i] == code[i]; i++ { 287 if i+1 == codeL { 288 return data[:bodyL] 289 } 290 } 291 } 292 if dataL == bodyL { 293 return nil 294 } 295 data = data[bodyL:] 296 } 297 } 298 299 type AttributeMatcher interface { 300 Match(*router.Domain) bool 301 } 302 303 type BooleanMatcher string 304 305 func (m BooleanMatcher) Match(domain *router.Domain) bool { 306 for _, attr := range domain.Attribute { 307 if attr.Key == string(m) { 308 return true 309 } 310 } 311 return false 312 } 313 314 type AttributeList struct { 315 matcher []AttributeMatcher 316 } 317 318 func (al *AttributeList) Match(domain *router.Domain) bool { 319 for _, matcher := range al.matcher { 320 if !matcher.Match(domain) { 321 return false 322 } 323 } 324 return true 325 } 326 327 func (al *AttributeList) IsEmpty() bool { 328 return len(al.matcher) == 0 329 } 330 331 func parseAttrs(attrs []string) *AttributeList { 332 al := new(AttributeList) 333 for _, attr := range attrs { 334 lc := strings.ToLower(attr) 335 al.matcher = append(al.matcher, BooleanMatcher(lc)) 336 } 337 return al 338 } 339 340 func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) { 341 parts := strings.Split(siteWithAttr, "@") 342 if len(parts) == 0 { 343 return nil, newError("empty site") 344 } 345 country := strings.ToUpper(parts[0]) 346 attrs := parseAttrs(parts[1:]) 347 domains, err := loadSite(file, country) 348 if err != nil { 349 return nil, err 350 } 351 352 if attrs.IsEmpty() { 353 return domains, nil 354 } 355 356 filteredDomains := make([]*router.Domain, 0, len(domains)) 357 for _, domain := range domains { 358 if attrs.Match(domain) { 359 filteredDomains = append(filteredDomains, domain) 360 } 361 } 362 363 return filteredDomains, nil 364 } 365 366 func parseDomainRule(domain string) ([]*router.Domain, error) { 367 if strings.HasPrefix(domain, "geosite:") { 368 country := strings.ToUpper(domain[8:]) 369 domains, err := loadGeositeWithAttr("geosite.dat", country) 370 if err != nil { 371 return nil, newError("failed to load geosite: ", country).Base(err) 372 } 373 return domains, nil 374 } 375 isExtDatFile := 0 376 { 377 const prefix = "ext:" 378 if strings.HasPrefix(domain, prefix) { 379 isExtDatFile = len(prefix) 380 } 381 const prefixQualified = "ext-domain:" 382 if strings.HasPrefix(domain, prefixQualified) { 383 isExtDatFile = len(prefixQualified) 384 } 385 } 386 if isExtDatFile != 0 { 387 kv := strings.Split(domain[isExtDatFile:], ":") 388 if len(kv) != 2 { 389 return nil, newError("invalid external resource: ", domain) 390 } 391 filename := kv[0] 392 country := kv[1] 393 domains, err := loadGeositeWithAttr(filename, country) 394 if err != nil { 395 return nil, newError("failed to load external sites: ", country, " from ", filename).Base(err) 396 } 397 return domains, nil 398 } 399 400 domainRule := new(router.Domain) 401 switch { 402 case strings.HasPrefix(domain, "regexp:"): 403 domainRule.Type = router.Domain_Regex 404 domainRule.Value = domain[7:] 405 406 case strings.HasPrefix(domain, "domain:"): 407 domainRule.Type = router.Domain_Domain 408 domainRule.Value = domain[7:] 409 410 case strings.HasPrefix(domain, "full:"): 411 domainRule.Type = router.Domain_Full 412 domainRule.Value = domain[5:] 413 414 case strings.HasPrefix(domain, "keyword:"): 415 domainRule.Type = router.Domain_Plain 416 domainRule.Value = domain[8:] 417 418 case strings.HasPrefix(domain, "dotless:"): 419 domainRule.Type = router.Domain_Regex 420 switch substr := domain[8:]; { 421 case substr == "": 422 domainRule.Value = "^[^.]*$" 423 case !strings.Contains(substr, "."): 424 domainRule.Value = "^[^.]*" + substr + "[^.]*$" 425 default: 426 return nil, newError("substr in dotless rule should not contain a dot: ", substr) 427 } 428 429 default: 430 domainRule.Type = router.Domain_Plain 431 domainRule.Value = domain 432 } 433 return []*router.Domain{domainRule}, nil 434 } 435 436 func ToCidrList(ips StringList) ([]*router.GeoIP, error) { 437 var geoipList []*router.GeoIP 438 var customCidrs []*router.CIDR 439 440 for _, ip := range ips { 441 if strings.HasPrefix(ip, "geoip:") { 442 country := ip[6:] 443 isReverseMatch := false 444 if strings.HasPrefix(ip, "geoip:!") { 445 country = ip[7:] 446 isReverseMatch = true 447 } 448 if len(country) == 0 { 449 return nil, newError("empty country name in rule") 450 } 451 geoip, err := loadGeoIP(strings.ToUpper(country)) 452 if err != nil { 453 return nil, newError("failed to load GeoIP: ", country).Base(err) 454 } 455 456 geoipList = append(geoipList, &router.GeoIP{ 457 CountryCode: strings.ToUpper(country), 458 Cidr: geoip, 459 ReverseMatch: isReverseMatch, 460 }) 461 continue 462 } 463 isExtDatFile := 0 464 { 465 const prefix = "ext:" 466 if strings.HasPrefix(ip, prefix) { 467 isExtDatFile = len(prefix) 468 } 469 const prefixQualified = "ext-ip:" 470 if strings.HasPrefix(ip, prefixQualified) { 471 isExtDatFile = len(prefixQualified) 472 } 473 } 474 if isExtDatFile != 0 { 475 kv := strings.Split(ip[isExtDatFile:], ":") 476 if len(kv) != 2 { 477 return nil, newError("invalid external resource: ", ip) 478 } 479 480 filename := kv[0] 481 country := kv[1] 482 if len(filename) == 0 || len(country) == 0 { 483 return nil, newError("empty filename or empty country in rule") 484 } 485 486 isReverseMatch := false 487 if strings.HasPrefix(country, "!") { 488 country = country[1:] 489 isReverseMatch = true 490 } 491 geoip, err := loadIP(filename, strings.ToUpper(country)) 492 if err != nil { 493 return nil, newError("failed to load IPs: ", country, " from ", filename).Base(err) 494 } 495 496 geoipList = append(geoipList, &router.GeoIP{ 497 CountryCode: strings.ToUpper(filename + "_" + country), 498 Cidr: geoip, 499 ReverseMatch: isReverseMatch, 500 }) 501 502 continue 503 } 504 505 ipRule, err := ParseIP(ip) 506 if err != nil { 507 return nil, newError("invalid IP: ", ip).Base(err) 508 } 509 customCidrs = append(customCidrs, ipRule) 510 } 511 512 if len(customCidrs) > 0 { 513 geoipList = append(geoipList, &router.GeoIP{ 514 Cidr: customCidrs, 515 }) 516 } 517 518 return geoipList, nil 519 } 520 521 func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) { 522 type RawFieldRule struct { 523 RouterRule 524 Domain *StringList `json:"domain"` 525 Domains *StringList `json:"domains"` 526 IP *StringList `json:"ip"` 527 Port *PortList `json:"port"` 528 Network *NetworkList `json:"network"` 529 SourceIP *StringList `json:"source"` 530 SourcePort *PortList `json:"sourcePort"` 531 User *StringList `json:"user"` 532 InboundTag *StringList `json:"inboundTag"` 533 Protocols *StringList `json:"protocol"` 534 Attributes map[string]string `json:"attrs"` 535 } 536 rawFieldRule := new(RawFieldRule) 537 err := json.Unmarshal(msg, rawFieldRule) 538 if err != nil { 539 return nil, err 540 } 541 542 rule := new(router.RoutingRule) 543 switch { 544 case len(rawFieldRule.OutboundTag) > 0: 545 rule.TargetTag = &router.RoutingRule_Tag{ 546 Tag: rawFieldRule.OutboundTag, 547 } 548 case len(rawFieldRule.BalancerTag) > 0: 549 rule.TargetTag = &router.RoutingRule_BalancingTag{ 550 BalancingTag: rawFieldRule.BalancerTag, 551 } 552 default: 553 return nil, newError("neither outboundTag nor balancerTag is specified in routing rule") 554 } 555 556 if rawFieldRule.DomainMatcher != "" { 557 rule.DomainMatcher = rawFieldRule.DomainMatcher 558 } 559 560 if rawFieldRule.Domain != nil { 561 for _, domain := range *rawFieldRule.Domain { 562 rules, err := parseDomainRule(domain) 563 if err != nil { 564 return nil, newError("failed to parse domain rule: ", domain).Base(err) 565 } 566 rule.Domain = append(rule.Domain, rules...) 567 } 568 } 569 570 if rawFieldRule.Domains != nil { 571 for _, domain := range *rawFieldRule.Domains { 572 rules, err := parseDomainRule(domain) 573 if err != nil { 574 return nil, newError("failed to parse domain rule: ", domain).Base(err) 575 } 576 rule.Domain = append(rule.Domain, rules...) 577 } 578 } 579 580 if rawFieldRule.IP != nil { 581 geoipList, err := ToCidrList(*rawFieldRule.IP) 582 if err != nil { 583 return nil, err 584 } 585 rule.Geoip = geoipList 586 } 587 588 if rawFieldRule.Port != nil { 589 rule.PortList = rawFieldRule.Port.Build() 590 } 591 592 if rawFieldRule.Network != nil { 593 rule.Networks = rawFieldRule.Network.Build() 594 } 595 596 if rawFieldRule.SourceIP != nil { 597 geoipList, err := ToCidrList(*rawFieldRule.SourceIP) 598 if err != nil { 599 return nil, err 600 } 601 rule.SourceGeoip = geoipList 602 } 603 604 if rawFieldRule.SourcePort != nil { 605 rule.SourcePortList = rawFieldRule.SourcePort.Build() 606 } 607 608 if rawFieldRule.User != nil { 609 for _, s := range *rawFieldRule.User { 610 rule.UserEmail = append(rule.UserEmail, s) 611 } 612 } 613 614 if rawFieldRule.InboundTag != nil { 615 for _, s := range *rawFieldRule.InboundTag { 616 rule.InboundTag = append(rule.InboundTag, s) 617 } 618 } 619 620 if rawFieldRule.Protocols != nil { 621 for _, s := range *rawFieldRule.Protocols { 622 rule.Protocol = append(rule.Protocol, s) 623 } 624 } 625 626 if len(rawFieldRule.Attributes) > 0 { 627 rule.Attributes = rawFieldRule.Attributes 628 } 629 630 return rule, nil 631 } 632 633 func ParseRule(msg json.RawMessage) (*router.RoutingRule, error) { 634 rawRule := new(RouterRule) 635 err := json.Unmarshal(msg, rawRule) 636 if err != nil { 637 return nil, newError("invalid router rule").Base(err) 638 } 639 if strings.EqualFold(rawRule.Type, "field") { 640 fieldrule, err := parseFieldRule(msg) 641 if err != nil { 642 return nil, newError("invalid field rule").Base(err) 643 } 644 return fieldrule, nil 645 } 646 if strings.EqualFold(rawRule.Type, "chinaip") { 647 chinaiprule, err := parseChinaIPRule(msg) 648 if err != nil { 649 return nil, newError("invalid chinaip rule").Base(err) 650 } 651 return chinaiprule, nil 652 } 653 if strings.EqualFold(rawRule.Type, "chinasites") { 654 chinasitesrule, err := parseChinaSitesRule(msg) 655 if err != nil { 656 return nil, newError("invalid chinasites rule").Base(err) 657 } 658 return chinasitesrule, nil 659 } 660 return nil, newError("unknown router rule type: ", rawRule.Type) 661 } 662 663 func parseChinaIPRule(data []byte) (*router.RoutingRule, error) { 664 rawRule := new(RouterRule) 665 err := json.Unmarshal(data, rawRule) 666 if err != nil { 667 return nil, newError("invalid router rule").Base(err) 668 } 669 chinaIPs, err := loadGeoIP("CN") 670 if err != nil { 671 return nil, newError("failed to load geoip:cn").Base(err) 672 } 673 return &router.RoutingRule{ 674 TargetTag: &router.RoutingRule_Tag{ 675 Tag: rawRule.OutboundTag, 676 }, 677 Cidr: chinaIPs, 678 }, nil 679 } 680 681 func parseChinaSitesRule(data []byte) (*router.RoutingRule, error) { 682 rawRule := new(RouterRule) 683 err := json.Unmarshal(data, rawRule) 684 if err != nil { 685 return nil, newError("invalid router rule").Base(err).AtError() 686 } 687 domains, err := loadGeositeWithAttr("geosite.dat", "CN") 688 if err != nil { 689 return nil, newError("failed to load geosite:cn.").Base(err) 690 } 691 return &router.RoutingRule{ 692 TargetTag: &router.RoutingRule_Tag{ 693 Tag: rawRule.OutboundTag, 694 }, 695 Domain: domains, 696 }, nil 697 }