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