github.com/Uhtred009/v2ray-core-1@v4.31.2+incompatible/infra/conf/router.go (about) 1 package conf 2 3 import ( 4 "encoding/json" 5 "strconv" 6 "strings" 7 8 "v2ray.com/core/app/router" 9 "v2ray.com/core/common/net" 10 "v2ray.com/core/common/platform/filesystem" 11 12 "github.com/golang/protobuf/proto" 13 ) 14 15 type RouterRulesConfig struct { 16 RuleList []json.RawMessage `json:"rules"` 17 DomainStrategy string `json:"domainStrategy"` 18 } 19 20 type BalancingRule struct { 21 Tag string `json:"tag"` 22 Selectors StringList `json:"selector"` 23 } 24 25 func (r *BalancingRule) Build() (*router.BalancingRule, error) { 26 if r.Tag == "" { 27 return nil, newError("empty balancer tag") 28 } 29 if len(r.Selectors) == 0 { 30 return nil, newError("empty selector list") 31 } 32 33 return &router.BalancingRule{ 34 Tag: r.Tag, 35 OutboundSelector: []string(r.Selectors), 36 }, nil 37 } 38 39 type RouterConfig struct { 40 Settings *RouterRulesConfig `json:"settings"` // Deprecated 41 RuleList []json.RawMessage `json:"rules"` 42 DomainStrategy *string `json:"domainStrategy"` 43 Balancers []*BalancingRule `json:"balancers"` 44 } 45 46 func (c *RouterConfig) getDomainStrategy() router.Config_DomainStrategy { 47 ds := "" 48 if c.DomainStrategy != nil { 49 ds = *c.DomainStrategy 50 } else if c.Settings != nil { 51 ds = c.Settings.DomainStrategy 52 } 53 54 switch strings.ToLower(ds) { 55 case "alwaysip": 56 return router.Config_UseIp 57 case "ipifnonmatch": 58 return router.Config_IpIfNonMatch 59 case "ipondemand": 60 return router.Config_IpOnDemand 61 default: 62 return router.Config_AsIs 63 } 64 } 65 66 func (c *RouterConfig) Build() (*router.Config, error) { 67 config := new(router.Config) 68 config.DomainStrategy = c.getDomainStrategy() 69 70 rawRuleList := c.RuleList 71 if c.Settings != nil { 72 rawRuleList = append(c.RuleList, c.Settings.RuleList...) 73 } 74 for _, rawRule := range rawRuleList { 75 rule, err := ParseRule(rawRule) 76 if err != nil { 77 return nil, err 78 } 79 config.Rule = append(config.Rule, rule) 80 } 81 for _, rawBalancer := range c.Balancers { 82 balancer, err := rawBalancer.Build() 83 if err != nil { 84 return nil, err 85 } 86 config.BalancingRule = append(config.BalancingRule, balancer) 87 } 88 return config, nil 89 } 90 91 type RouterRule struct { 92 Type string `json:"type"` 93 OutboundTag string `json:"outboundTag"` 94 BalancerTag string `json:"balancerTag"` 95 } 96 97 func ParseIP(s string) (*router.CIDR, error) { 98 var addr, mask string 99 i := strings.Index(s, "/") 100 if i < 0 { 101 addr = s 102 } else { 103 addr = s[:i] 104 mask = s[i+1:] 105 } 106 ip := net.ParseAddress(addr) 107 switch ip.Family() { 108 case net.AddressFamilyIPv4: 109 bits := uint32(32) 110 if len(mask) > 0 { 111 bits64, err := strconv.ParseUint(mask, 10, 32) 112 if err != nil { 113 return nil, newError("invalid network mask for router: ", mask).Base(err) 114 } 115 bits = uint32(bits64) 116 } 117 if bits > 32 { 118 return nil, newError("invalid network mask for router: ", bits) 119 } 120 return &router.CIDR{ 121 Ip: []byte(ip.IP()), 122 Prefix: bits, 123 }, nil 124 case net.AddressFamilyIPv6: 125 bits := uint32(128) 126 if len(mask) > 0 { 127 bits64, err := strconv.ParseUint(mask, 10, 32) 128 if err != nil { 129 return nil, newError("invalid network mask for router: ", mask).Base(err) 130 } 131 bits = uint32(bits64) 132 } 133 if bits > 128 { 134 return nil, newError("invalid network mask for router: ", bits) 135 } 136 return &router.CIDR{ 137 Ip: []byte(ip.IP()), 138 Prefix: bits, 139 }, nil 140 default: 141 return nil, newError("unsupported address for router: ", s) 142 } 143 } 144 145 func loadGeoIP(country string) ([]*router.CIDR, error) { 146 return loadIP("geoip.dat", country) 147 } 148 149 func loadIP(filename, country string) ([]*router.CIDR, error) { 150 geoipBytes, err := filesystem.ReadAsset(filename) 151 if err != nil { 152 return nil, newError("failed to open file: ", filename).Base(err) 153 } 154 var geoipList router.GeoIPList 155 if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil { 156 return nil, err 157 } 158 159 for _, geoip := range geoipList.Entry { 160 if geoip.CountryCode == country { 161 return geoip.Cidr, nil 162 } 163 } 164 165 return nil, newError("country not found in ", filename, ": ", country) 166 } 167 168 func loadSite(filename, country string) ([]*router.Domain, error) { 169 geositeBytes, err := filesystem.ReadAsset(filename) 170 if err != nil { 171 return nil, newError("failed to open file: ", filename).Base(err) 172 } 173 var geositeList router.GeoSiteList 174 if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil { 175 return nil, err 176 } 177 178 for _, site := range geositeList.Entry { 179 if site.CountryCode == country { 180 return site.Domain, nil 181 } 182 } 183 184 return nil, newError("list not found in ", filename, ": ", country) 185 } 186 187 type AttributeMatcher interface { 188 Match(*router.Domain) bool 189 } 190 191 type BooleanMatcher string 192 193 func (m BooleanMatcher) Match(domain *router.Domain) bool { 194 for _, attr := range domain.Attribute { 195 if attr.Key == string(m) { 196 return true 197 } 198 } 199 return false 200 } 201 202 type AttributeList struct { 203 matcher []AttributeMatcher 204 } 205 206 func (al *AttributeList) Match(domain *router.Domain) bool { 207 for _, matcher := range al.matcher { 208 if !matcher.Match(domain) { 209 return false 210 } 211 } 212 return true 213 } 214 215 func (al *AttributeList) IsEmpty() bool { 216 return len(al.matcher) == 0 217 } 218 219 func parseAttrs(attrs []string) *AttributeList { 220 al := new(AttributeList) 221 for _, attr := range attrs { 222 lc := strings.ToLower(attr) 223 al.matcher = append(al.matcher, BooleanMatcher(lc)) 224 } 225 return al 226 } 227 228 func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) { 229 parts := strings.Split(siteWithAttr, "@") 230 if len(parts) == 0 { 231 return nil, newError("empty site") 232 } 233 country := strings.ToUpper(parts[0]) 234 attrs := parseAttrs(parts[1:]) 235 domains, err := loadSite(file, country) 236 if err != nil { 237 return nil, err 238 } 239 240 if attrs.IsEmpty() { 241 return domains, nil 242 } 243 244 filteredDomains := make([]*router.Domain, 0, len(domains)) 245 for _, domain := range domains { 246 if attrs.Match(domain) { 247 filteredDomains = append(filteredDomains, domain) 248 } 249 } 250 251 return filteredDomains, nil 252 } 253 254 func parseDomainRule(domain string) ([]*router.Domain, error) { 255 if strings.HasPrefix(domain, "geosite:") { 256 country := strings.ToUpper(domain[8:]) 257 domains, err := loadGeositeWithAttr("geosite.dat", country) 258 if err != nil { 259 return nil, newError("failed to load geosite: ", country).Base(err) 260 } 261 return domains, nil 262 } 263 var isExtDatFile = 0 264 { 265 const prefix = "ext:" 266 if strings.HasPrefix(domain, prefix) { 267 isExtDatFile = len(prefix) 268 } 269 const prefixQualified = "ext-domain:" 270 if strings.HasPrefix(domain, prefixQualified) { 271 isExtDatFile = len(prefixQualified) 272 } 273 } 274 if isExtDatFile != 0 { 275 kv := strings.Split(domain[isExtDatFile:], ":") 276 if len(kv) != 2 { 277 return nil, newError("invalid external resource: ", domain) 278 } 279 filename := kv[0] 280 country := kv[1] 281 domains, err := loadGeositeWithAttr(filename, country) 282 if err != nil { 283 return nil, newError("failed to load external sites: ", country, " from ", filename).Base(err) 284 } 285 return domains, nil 286 } 287 288 domainRule := new(router.Domain) 289 switch { 290 case strings.HasPrefix(domain, "regexp:"): 291 domainRule.Type = router.Domain_Regex 292 domainRule.Value = domain[7:] 293 case strings.HasPrefix(domain, "domain:"): 294 domainRule.Type = router.Domain_Domain 295 domainRule.Value = domain[7:] 296 case strings.HasPrefix(domain, "full:"): 297 domainRule.Type = router.Domain_Full 298 domainRule.Value = domain[5:] 299 case strings.HasPrefix(domain, "keyword:"): 300 domainRule.Type = router.Domain_Plain 301 domainRule.Value = domain[8:] 302 case strings.HasPrefix(domain, "dotless:"): 303 domainRule.Type = router.Domain_Regex 304 switch substr := domain[8:]; { 305 case substr == "": 306 domainRule.Value = "^[^.]*$" 307 case !strings.Contains(substr, "."): 308 domainRule.Value = "^[^.]*" + substr + "[^.]*$" 309 default: 310 return nil, newError("substr in dotless rule should not contain a dot: ", substr) 311 } 312 default: 313 domainRule.Type = router.Domain_Plain 314 domainRule.Value = domain 315 } 316 return []*router.Domain{domainRule}, nil 317 } 318 319 func toCidrList(ips StringList) ([]*router.GeoIP, error) { 320 var geoipList []*router.GeoIP 321 var customCidrs []*router.CIDR 322 323 for _, ip := range ips { 324 if strings.HasPrefix(ip, "geoip:") { 325 country := ip[6:] 326 geoip, err := loadGeoIP(strings.ToUpper(country)) 327 if err != nil { 328 return nil, newError("failed to load GeoIP: ", country).Base(err) 329 } 330 331 geoipList = append(geoipList, &router.GeoIP{ 332 CountryCode: strings.ToUpper(country), 333 Cidr: geoip, 334 }) 335 continue 336 } 337 var isExtDatFile = 0 338 { 339 const prefix = "ext:" 340 if strings.HasPrefix(ip, prefix) { 341 isExtDatFile = len(prefix) 342 } 343 const prefixQualified = "ext-ip:" 344 if strings.HasPrefix(ip, prefixQualified) { 345 isExtDatFile = len(prefixQualified) 346 } 347 } 348 if isExtDatFile != 0 { 349 kv := strings.Split(ip[isExtDatFile:], ":") 350 if len(kv) != 2 { 351 return nil, newError("invalid external resource: ", ip) 352 } 353 354 filename := kv[0] 355 country := kv[1] 356 geoip, err := loadIP(filename, strings.ToUpper(country)) 357 if err != nil { 358 return nil, newError("failed to load IPs: ", country, " from ", filename).Base(err) 359 } 360 361 geoipList = append(geoipList, &router.GeoIP{ 362 CountryCode: strings.ToUpper(filename + "_" + country), 363 Cidr: geoip, 364 }) 365 366 continue 367 } 368 369 ipRule, err := ParseIP(ip) 370 if err != nil { 371 return nil, newError("invalid IP: ", ip).Base(err) 372 } 373 customCidrs = append(customCidrs, ipRule) 374 } 375 376 if len(customCidrs) > 0 { 377 geoipList = append(geoipList, &router.GeoIP{ 378 Cidr: customCidrs, 379 }) 380 } 381 382 return geoipList, nil 383 } 384 385 func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) { 386 type RawFieldRule struct { 387 RouterRule 388 Domain *StringList `json:"domain"` 389 IP *StringList `json:"ip"` 390 Port *PortList `json:"port"` 391 Network *NetworkList `json:"network"` 392 SourceIP *StringList `json:"source"` 393 SourcePort *PortList `json:"sourcePort"` 394 User *StringList `json:"user"` 395 InboundTag *StringList `json:"inboundTag"` 396 Protocols *StringList `json:"protocol"` 397 Attributes string `json:"attrs"` 398 } 399 rawFieldRule := new(RawFieldRule) 400 err := json.Unmarshal(msg, rawFieldRule) 401 if err != nil { 402 return nil, err 403 } 404 405 rule := new(router.RoutingRule) 406 if len(rawFieldRule.OutboundTag) > 0 { 407 rule.TargetTag = &router.RoutingRule_Tag{ 408 Tag: rawFieldRule.OutboundTag, 409 } 410 } else if len(rawFieldRule.BalancerTag) > 0 { 411 rule.TargetTag = &router.RoutingRule_BalancingTag{ 412 BalancingTag: rawFieldRule.BalancerTag, 413 } 414 } else { 415 return nil, newError("neither outboundTag nor balancerTag is specified in routing rule") 416 } 417 418 if rawFieldRule.Domain != nil { 419 for _, domain := range *rawFieldRule.Domain { 420 rules, err := parseDomainRule(domain) 421 if err != nil { 422 return nil, newError("failed to parse domain rule: ", domain).Base(err) 423 } 424 rule.Domain = append(rule.Domain, rules...) 425 } 426 } 427 428 if rawFieldRule.IP != nil { 429 geoipList, err := toCidrList(*rawFieldRule.IP) 430 if err != nil { 431 return nil, err 432 } 433 rule.Geoip = geoipList 434 } 435 436 if rawFieldRule.Port != nil { 437 rule.PortList = rawFieldRule.Port.Build() 438 } 439 440 if rawFieldRule.Network != nil { 441 rule.Networks = rawFieldRule.Network.Build() 442 } 443 444 if rawFieldRule.SourceIP != nil { 445 geoipList, err := toCidrList(*rawFieldRule.SourceIP) 446 if err != nil { 447 return nil, err 448 } 449 rule.SourceGeoip = geoipList 450 } 451 452 if rawFieldRule.SourcePort != nil { 453 rule.SourcePortList = rawFieldRule.SourcePort.Build() 454 } 455 456 if rawFieldRule.User != nil { 457 for _, s := range *rawFieldRule.User { 458 rule.UserEmail = append(rule.UserEmail, s) 459 } 460 } 461 462 if rawFieldRule.InboundTag != nil { 463 for _, s := range *rawFieldRule.InboundTag { 464 rule.InboundTag = append(rule.InboundTag, s) 465 } 466 } 467 468 if rawFieldRule.Protocols != nil { 469 for _, s := range *rawFieldRule.Protocols { 470 rule.Protocol = append(rule.Protocol, s) 471 } 472 } 473 474 if len(rawFieldRule.Attributes) > 0 { 475 rule.Attributes = rawFieldRule.Attributes 476 } 477 478 return rule, nil 479 } 480 481 func ParseRule(msg json.RawMessage) (*router.RoutingRule, error) { 482 rawRule := new(RouterRule) 483 err := json.Unmarshal(msg, rawRule) 484 if err != nil { 485 return nil, newError("invalid router rule").Base(err) 486 } 487 if rawRule.Type == "field" { 488 fieldrule, err := parseFieldRule(msg) 489 if err != nil { 490 return nil, newError("invalid field rule").Base(err) 491 } 492 return fieldrule, nil 493 } 494 if rawRule.Type == "chinaip" { 495 chinaiprule, err := parseChinaIPRule(msg) 496 if err != nil { 497 return nil, newError("invalid chinaip rule").Base(err) 498 } 499 return chinaiprule, nil 500 } 501 if rawRule.Type == "chinasites" { 502 chinasitesrule, err := parseChinaSitesRule(msg) 503 if err != nil { 504 return nil, newError("invalid chinasites rule").Base(err) 505 } 506 return chinasitesrule, nil 507 } 508 return nil, newError("unknown router rule type: ", rawRule.Type) 509 } 510 511 func parseChinaIPRule(data []byte) (*router.RoutingRule, error) { 512 rawRule := new(RouterRule) 513 err := json.Unmarshal(data, rawRule) 514 if err != nil { 515 return nil, newError("invalid router rule").Base(err) 516 } 517 chinaIPs, err := loadGeoIP("CN") 518 if err != nil { 519 return nil, newError("failed to load geoip:cn").Base(err) 520 } 521 return &router.RoutingRule{ 522 TargetTag: &router.RoutingRule_Tag{ 523 Tag: rawRule.OutboundTag, 524 }, 525 Cidr: chinaIPs, 526 }, nil 527 } 528 529 func parseChinaSitesRule(data []byte) (*router.RoutingRule, error) { 530 rawRule := new(RouterRule) 531 err := json.Unmarshal(data, rawRule) 532 if err != nil { 533 return nil, newError("invalid router rule").Base(err).AtError() 534 } 535 domains, err := loadGeositeWithAttr("geosite.dat", "CN") 536 if err != nil { 537 return nil, newError("failed to load geosite:cn.").Base(err) 538 } 539 return &router.RoutingRule{ 540 TargetTag: &router.RoutingRule_Tag{ 541 Tag: rawRule.OutboundTag, 542 }, 543 Domain: domains, 544 }, nil 545 }