github.com/v2fly/v2ray-core/v4@v4.45.2/infra/conf/rule/rule.go (about) 1 package rule 2 3 import ( 4 "context" 5 "encoding/json" 6 "strconv" 7 "strings" 8 9 "github.com/v2fly/v2ray-core/v4/app/router" 10 "github.com/v2fly/v2ray-core/v4/common/net" 11 "github.com/v2fly/v2ray-core/v4/infra/conf/cfgcommon" 12 ) 13 14 //go:generate go run github.com/v2fly/v2ray-core/v4/common/errors/errorgen 15 16 func parseDomainRule(ctx context.Context, domain string) ([]*router.Domain, error) { 17 cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(ctx) 18 geoLoader := cfgEnv.GetGeoLoader() 19 20 if strings.HasPrefix(domain, "geosite:") { 21 list := domain[8:] 22 if len(list) == 0 { 23 return nil, newError("empty listname in rule: ", domain) 24 } 25 domains, err := geoLoader.LoadGeoSite(list) 26 if err != nil { 27 return nil, newError("failed to load geosite: ", list).Base(err) 28 } 29 30 return domains, nil 31 } 32 33 isExtDatFile := 0 34 { 35 const prefix = "ext:" 36 if strings.HasPrefix(domain, prefix) { 37 isExtDatFile = len(prefix) 38 } 39 const prefixQualified = "ext-domain:" 40 if strings.HasPrefix(domain, prefixQualified) { 41 isExtDatFile = len(prefixQualified) 42 } 43 } 44 45 if isExtDatFile != 0 { 46 kv := strings.Split(domain[isExtDatFile:], ":") 47 if len(kv) != 2 { 48 return nil, newError("invalid external resource: ", domain) 49 } 50 filename := kv[0] 51 list := kv[1] 52 domains, err := geoLoader.LoadGeoSiteWithAttr(filename, list) 53 if err != nil { 54 return nil, newError("failed to load external geosite: ", list, " from ", filename).Base(err) 55 } 56 57 return domains, nil 58 } 59 60 domainRule := new(router.Domain) 61 switch { 62 case strings.HasPrefix(domain, "regexp:"): 63 regexpVal := domain[7:] 64 if len(regexpVal) == 0 { 65 return nil, newError("empty regexp type of rule: ", domain) 66 } 67 domainRule.Type = router.Domain_Regex 68 domainRule.Value = regexpVal 69 70 case strings.HasPrefix(domain, "domain:"): 71 domainName := domain[7:] 72 if len(domainName) == 0 { 73 return nil, newError("empty domain type of rule: ", domain) 74 } 75 domainRule.Type = router.Domain_Domain 76 domainRule.Value = domainName 77 78 case strings.HasPrefix(domain, "full:"): 79 fullVal := domain[5:] 80 if len(fullVal) == 0 { 81 return nil, newError("empty full domain type of rule: ", domain) 82 } 83 domainRule.Type = router.Domain_Full 84 domainRule.Value = fullVal 85 86 case strings.HasPrefix(domain, "keyword:"): 87 keywordVal := domain[8:] 88 if len(keywordVal) == 0 { 89 return nil, newError("empty keyword type of rule: ", domain) 90 } 91 domainRule.Type = router.Domain_Plain 92 domainRule.Value = keywordVal 93 94 case strings.HasPrefix(domain, "dotless:"): 95 domainRule.Type = router.Domain_Regex 96 switch substr := domain[8:]; { 97 case substr == "": 98 domainRule.Value = "^[^.]*$" 99 case !strings.Contains(substr, "."): 100 domainRule.Value = "^[^.]*" + substr + "[^.]*$" 101 default: 102 return nil, newError("substr in dotless rule should not contain a dot: ", substr) 103 } 104 105 default: 106 domainRule.Type = router.Domain_Plain 107 domainRule.Value = domain 108 } 109 return []*router.Domain{domainRule}, nil 110 } 111 112 func toCidrList(ctx context.Context, ips cfgcommon.StringList) ([]*router.GeoIP, error) { 113 cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(ctx) 114 geoLoader := cfgEnv.GetGeoLoader() 115 116 var geoipList []*router.GeoIP 117 var customCidrs []*router.CIDR 118 119 for _, ip := range ips { 120 if strings.HasPrefix(ip, "geoip:") { 121 country := ip[6:] 122 isReverseMatch := false 123 if strings.HasPrefix(ip, "geoip:!") { 124 country = ip[7:] 125 isReverseMatch = true 126 } 127 if len(country) == 0 { 128 return nil, newError("empty country name in rule") 129 } 130 geoip, err := geoLoader.LoadGeoIP(country) 131 if err != nil { 132 return nil, newError("failed to load geoip: ", country).Base(err) 133 } 134 135 geoipList = append(geoipList, &router.GeoIP{ 136 CountryCode: strings.ToUpper(country), 137 Cidr: geoip, 138 ReverseMatch: isReverseMatch, 139 }) 140 141 continue 142 } 143 144 isExtDatFile := 0 145 { 146 const prefix = "ext:" 147 if strings.HasPrefix(ip, prefix) { 148 isExtDatFile = len(prefix) 149 } 150 const prefixQualified = "ext-ip:" 151 if strings.HasPrefix(ip, prefixQualified) { 152 isExtDatFile = len(prefixQualified) 153 } 154 } 155 156 if isExtDatFile != 0 { 157 kv := strings.Split(ip[isExtDatFile:], ":") 158 if len(kv) != 2 { 159 return nil, newError("invalid external resource: ", ip) 160 } 161 162 filename := kv[0] 163 country := kv[1] 164 if len(filename) == 0 || len(country) == 0 { 165 return nil, newError("empty filename or empty country in rule") 166 } 167 168 isReverseMatch := false 169 if strings.HasPrefix(country, "!") { 170 country = country[1:] 171 isReverseMatch = true 172 } 173 geoip, err := geoLoader.LoadIP(filename, country) 174 if err != nil { 175 return nil, newError("failed to load geoip: ", country, " from ", filename).Base(err) 176 } 177 178 geoipList = append(geoipList, &router.GeoIP{ 179 CountryCode: strings.ToUpper(filename + "_" + country), 180 Cidr: geoip, 181 ReverseMatch: isReverseMatch, 182 }) 183 184 continue 185 } 186 187 ipRule, err := ParseIP(ip) 188 if err != nil { 189 return nil, newError("invalid IP: ", ip).Base(err) 190 } 191 customCidrs = append(customCidrs, ipRule) 192 } 193 194 if len(customCidrs) > 0 { 195 geoipList = append(geoipList, &router.GeoIP{ 196 Cidr: customCidrs, 197 }) 198 } 199 200 return geoipList, nil 201 } 202 203 func parseFieldRule(ctx context.Context, msg json.RawMessage) (*router.RoutingRule, error) { 204 type RawFieldRule struct { 205 RouterRule 206 Domain *cfgcommon.StringList `json:"domain"` 207 Domains *cfgcommon.StringList `json:"domains"` 208 IP *cfgcommon.StringList `json:"ip"` 209 Port *cfgcommon.PortList `json:"port"` 210 Network *cfgcommon.NetworkList `json:"network"` 211 SourceIP *cfgcommon.StringList `json:"source"` 212 SourcePort *cfgcommon.PortList `json:"sourcePort"` 213 User *cfgcommon.StringList `json:"user"` 214 InboundTag *cfgcommon.StringList `json:"inboundTag"` 215 Protocols *cfgcommon.StringList `json:"protocol"` 216 Attributes string `json:"attrs"` 217 } 218 rawFieldRule := new(RawFieldRule) 219 err := json.Unmarshal(msg, rawFieldRule) 220 if err != nil { 221 return nil, err 222 } 223 224 rule := new(router.RoutingRule) 225 switch { 226 case len(rawFieldRule.OutboundTag) > 0: 227 rule.TargetTag = &router.RoutingRule_Tag{ 228 Tag: rawFieldRule.OutboundTag, 229 } 230 case len(rawFieldRule.BalancerTag) > 0: 231 rule.TargetTag = &router.RoutingRule_BalancingTag{ 232 BalancingTag: rawFieldRule.BalancerTag, 233 } 234 default: 235 return nil, newError("neither outboundTag nor balancerTag is specified in routing rule") 236 } 237 238 if rawFieldRule.DomainMatcher != "" { 239 rule.DomainMatcher = rawFieldRule.DomainMatcher 240 } 241 242 if rawFieldRule.Domain != nil { 243 for _, domain := range *rawFieldRule.Domain { 244 rules, err := parseDomainRule(ctx, domain) 245 if err != nil { 246 return nil, newError("failed to parse domain rule: ", domain).Base(err) 247 } 248 rule.Domain = append(rule.Domain, rules...) 249 } 250 } 251 252 if rawFieldRule.Domains != nil { 253 for _, domain := range *rawFieldRule.Domains { 254 rules, err := parseDomainRule(ctx, domain) 255 if err != nil { 256 return nil, newError("failed to parse domain rule: ", domain).Base(err) 257 } 258 rule.Domain = append(rule.Domain, rules...) 259 } 260 } 261 262 if rawFieldRule.IP != nil { 263 geoipList, err := toCidrList(ctx, *rawFieldRule.IP) 264 if err != nil { 265 return nil, err 266 } 267 rule.Geoip = geoipList 268 } 269 270 if rawFieldRule.Port != nil { 271 rule.PortList = rawFieldRule.Port.Build() 272 } 273 274 if rawFieldRule.Network != nil { 275 rule.Networks = rawFieldRule.Network.Build() 276 } 277 278 if rawFieldRule.SourceIP != nil { 279 geoipList, err := toCidrList(ctx, *rawFieldRule.SourceIP) 280 if err != nil { 281 return nil, err 282 } 283 rule.SourceGeoip = geoipList 284 } 285 286 if rawFieldRule.SourcePort != nil { 287 rule.SourcePortList = rawFieldRule.SourcePort.Build() 288 } 289 290 if rawFieldRule.User != nil { 291 for _, s := range *rawFieldRule.User { 292 rule.UserEmail = append(rule.UserEmail, s) 293 } 294 } 295 296 if rawFieldRule.InboundTag != nil { 297 for _, s := range *rawFieldRule.InboundTag { 298 rule.InboundTag = append(rule.InboundTag, s) 299 } 300 } 301 302 if rawFieldRule.Protocols != nil { 303 for _, s := range *rawFieldRule.Protocols { 304 rule.Protocol = append(rule.Protocol, s) 305 } 306 } 307 308 if len(rawFieldRule.Attributes) > 0 { 309 rule.Attributes = rawFieldRule.Attributes 310 } 311 312 return rule, nil 313 } 314 315 func ParseRule(ctx context.Context, msg json.RawMessage) (*router.RoutingRule, error) { 316 rawRule := new(RouterRule) 317 err := json.Unmarshal(msg, rawRule) 318 if err != nil { 319 return nil, newError("invalid router rule").Base(err) 320 } 321 if strings.EqualFold(rawRule.Type, "field") { 322 fieldrule, err := parseFieldRule(ctx, msg) 323 if err != nil { 324 return nil, newError("invalid field rule").Base(err) 325 } 326 return fieldrule, nil 327 } 328 329 return nil, newError("unknown router rule type: ", rawRule.Type) 330 } 331 332 func ParseIP(s string) (*router.CIDR, error) { 333 var addr, mask string 334 i := strings.Index(s, "/") 335 if i < 0 { 336 addr = s 337 } else { 338 addr = s[:i] 339 mask = s[i+1:] 340 } 341 ip := net.ParseAddress(addr) 342 switch ip.Family() { 343 case net.AddressFamilyIPv4: 344 bits := uint32(32) 345 if len(mask) > 0 { 346 bits64, err := strconv.ParseUint(mask, 10, 32) 347 if err != nil { 348 return nil, newError("invalid network mask for router: ", mask).Base(err) 349 } 350 bits = uint32(bits64) 351 } 352 if bits > 32 { 353 return nil, newError("invalid network mask for router: ", bits) 354 } 355 return &router.CIDR{ 356 Ip: []byte(ip.IP()), 357 Prefix: bits, 358 }, nil 359 case net.AddressFamilyIPv6: 360 bits := uint32(128) 361 if len(mask) > 0 { 362 bits64, err := strconv.ParseUint(mask, 10, 32) 363 if err != nil { 364 return nil, newError("invalid network mask for router: ", mask).Base(err) 365 } 366 bits = uint32(bits64) 367 } 368 if bits > 128 { 369 return nil, newError("invalid network mask for router: ", bits) 370 } 371 return &router.CIDR{ 372 Ip: []byte(ip.IP()), 373 Prefix: bits, 374 }, nil 375 default: 376 return nil, newError("unsupported address for router: ", s) 377 } 378 } 379 380 func ParseDomainRule(ctx context.Context, domain string) ([]*router.Domain, error) { 381 return parseDomainRule(ctx, domain) 382 } 383 384 func ToCidrList(ctx context.Context, ips cfgcommon.StringList) ([]*router.GeoIP, error) { 385 return toCidrList(ctx, ips) 386 } 387 388 type RouterRule struct { 389 Type string `json:"type"` 390 OutboundTag string `json:"outboundTag"` 391 BalancerTag string `json:"balancerTag"` 392 393 DomainMatcher string `json:"domainMatcher"` 394 }