rule.go 10.0 KB

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