Browse Source

DNS & Routing: refine rule parsing process (#528)

Loyalsoldier 4 years ago
parent
commit
7a020d2d71
2 changed files with 110 additions and 91 deletions
  1. 31 11
      infra/conf/dns.go
  2. 79 80
      infra/conf/router.go

+ 31 - 11
infra/conf/dns.go

@@ -87,7 +87,7 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
 
 	geoipList, err := toCidrList(c.ExpectIPs)
 	if err != nil {
-		return nil, newError("invalid ip rule: ", c.ExpectIPs).Base(err)
+		return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err)
 	}
 
 	var myClientIP []byte
@@ -153,7 +153,7 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
 	for _, server := range c.Servers {
 		ns, err := server.Build()
 		if err != nil {
-			return nil, newError("failed to build name server").Base(err)
+			return nil, newError("failed to build nameserver").Base(err)
 		}
 		config.NameServer = append(config.NameServer, ns)
 	}
@@ -170,15 +170,23 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
 			var mappings []*dns.Config_HostMapping
 			switch {
 			case strings.HasPrefix(domain, "domain:"):
+				domainName := domain[7:]
+				if len(domainName) == 0 {
+					return nil, newError("empty domain type of rule: ", domain)
+				}
 				mapping := getHostMapping(addr)
 				mapping.Type = dns.DomainMatchingType_Subdomain
-				mapping.Domain = domain[7:]
+				mapping.Domain = domainName
 				mappings = append(mappings, mapping)
 
 			case strings.HasPrefix(domain, "geosite:"):
-				domains, err := loadGeositeWithAttr("geosite.dat", strings.ToUpper(domain[8:]))
+				listName := domain[8:]
+				if len(listName) == 0 {
+					return nil, newError("empty geosite rule: ", domain)
+				}
+				domains, err := loadGeositeWithAttr("geosite.dat", listName)
 				if err != nil {
-					return nil, newError("invalid geosite settings: ", domain).Base(err)
+					return nil, newError("failed to load geosite: ", listName).Base(err)
 				}
 				for _, d := range domains {
 					mapping := getHostMapping(addr)
@@ -188,21 +196,33 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
 				}
 
 			case strings.HasPrefix(domain, "regexp:"):
+				regexpVal := domain[7:]
+				if len(regexpVal) == 0 {
+					return nil, newError("empty regexp type of rule: ", domain)
+				}
 				mapping := getHostMapping(addr)
 				mapping.Type = dns.DomainMatchingType_Regex
-				mapping.Domain = domain[7:]
+				mapping.Domain = regexpVal
 				mappings = append(mappings, mapping)
 
 			case strings.HasPrefix(domain, "keyword:"):
+				keywordVal := domain[8:]
+				if len(keywordVal) == 0 {
+					return nil, newError("empty keyword type of rule: ", domain)
+				}
 				mapping := getHostMapping(addr)
 				mapping.Type = dns.DomainMatchingType_Keyword
-				mapping.Domain = domain[8:]
+				mapping.Domain = keywordVal
 				mappings = append(mappings, mapping)
 
 			case strings.HasPrefix(domain, "full:"):
+				fullVal := domain[5:]
+				if len(fullVal) == 0 {
+					return nil, newError("empty full domain type of rule: ", domain)
+				}
 				mapping := getHostMapping(addr)
 				mapping.Type = dns.DomainMatchingType_Full
-				mapping.Domain = domain[5:]
+				mapping.Domain = fullVal
 				mappings = append(mappings, mapping)
 
 			case strings.HasPrefix(domain, "dotless:"):
@@ -224,10 +244,10 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
 					return nil, newError("invalid external resource: ", domain)
 				}
 				filename := kv[0]
-				country := kv[1]
-				domains, err := loadGeositeWithAttr(filename, country)
+				list := kv[1]
+				domains, err := loadGeositeWithAttr(filename, list)
 				if err != nil {
-					return nil, newError("failed to load domains: ", country, " from ", filename).Base(err)
+					return nil, newError("failed to load domain list: ", list, " from ", filename).Base(err)
 				}
 				for _, d := range domains {
 					mapping := getHostMapping(addr)

+ 79 - 80
infra/conf/router.go

@@ -6,7 +6,6 @@ import (
 	"strings"
 
 	"github.com/golang/protobuf/proto"
-
 	"v2ray.com/core/app/router"
 	"v2ray.com/core/common/net"
 	"v2ray.com/core/common/platform/filesystem"
@@ -52,11 +51,11 @@ func (c *RouterConfig) getDomainStrategy() router.Config_DomainStrategy {
 	}
 
 	switch strings.ToLower(ds) {
-	case "alwaysip":
+	case "alwaysip", "always_ip", "always-ip":
 		return router.Config_UseIp
-	case "ipifnonmatch":
+	case "ipifnonmatch", "ip_if_non_match", "ip-if-non-match":
 		return router.Config_IpIfNonMatch
-	case "ipondemand":
+	case "ipondemand", "ip_on_demand", "ip-on-demand":
 		return router.Config_IpOnDemand
 	default:
 		return router.Config_AsIs
@@ -162,7 +161,7 @@ func loadIP(filename, country string) ([]*router.CIDR, error) {
 	}
 
 	for _, geoip := range geoipList.Entry {
-		if geoip.CountryCode == country {
+		if strings.EqualFold(geoip.CountryCode, country) {
 			return geoip.Cidr, nil
 		}
 	}
@@ -170,7 +169,7 @@ func loadIP(filename, country string) ([]*router.CIDR, error) {
 	return nil, newError("country not found in ", filename, ": ", country)
 }
 
-func loadSite(filename, country string) ([]*router.Domain, error) {
+func loadSite(filename, list string) ([]*router.Domain, error) {
 	geositeBytes, err := filesystem.ReadAsset(filename)
 	if err != nil {
 		return nil, newError("failed to open file: ", filename).Base(err)
@@ -181,12 +180,12 @@ func loadSite(filename, country string) ([]*router.Domain, error) {
 	}
 
 	for _, site := range geositeList.Entry {
-		if site.CountryCode == country {
+		if strings.EqualFold(site.CountryCode, list) {
 			return site.Domain, nil
 		}
 	}
 
-	return nil, newError("list not found in ", filename, ": ", country)
+	return nil, newError("list not found in ", filename, ": ", list)
 }
 
 type AttributeMatcher interface {
@@ -197,7 +196,7 @@ type BooleanMatcher string
 
 func (m BooleanMatcher) Match(domain *router.Domain) bool {
 	for _, attr := range domain.Attribute {
-		if attr.Key == string(m) {
+		if strings.EqualFold(attr.GetKey(), string(m)) {
 			return true
 		}
 	}
@@ -224,8 +223,11 @@ func (al *AttributeList) IsEmpty() bool {
 func parseAttrs(attrs []string) *AttributeList {
 	al := new(AttributeList)
 	for _, attr := range attrs {
-		lc := strings.ToLower(attr)
-		al.matcher = append(al.matcher, BooleanMatcher(lc))
+		trimmedAttr := strings.ToLower(strings.TrimSpace(attr))
+		if len(trimmedAttr) == 0 {
+			continue
+		}
+		al.matcher = append(al.matcher, BooleanMatcher(trimmedAttr))
 	}
 	return al
 }
@@ -233,38 +235,57 @@ func parseAttrs(attrs []string) *AttributeList {
 func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) {
 	parts := strings.Split(siteWithAttr, "@")
 	if len(parts) == 0 {
-		return nil, newError("empty site")
+		return nil, newError("empty rule")
 	}
-	country := strings.ToUpper(parts[0])
-	attrs := parseAttrs(parts[1:])
-	domains, err := loadSite(file, country)
+	list := strings.TrimSpace(parts[0])
+	attrVal := parts[1:]
+
+	if len(list) == 0 {
+		return nil, newError("empty listname in rule: ", siteWithAttr)
+	}
+
+	domains, err := loadSite(file, list)
 	if err != nil {
 		return nil, err
 	}
 
+	attrs := parseAttrs(attrVal)
 	if attrs.IsEmpty() {
+		if strings.Contains(siteWithAttr, "@") {
+			newError("empty attribute list: ", siteWithAttr)
+		}
 		return domains, nil
 	}
 
 	filteredDomains := make([]*router.Domain, 0, len(domains))
+	hasAttrMatched := false
 	for _, domain := range domains {
 		if attrs.Match(domain) {
+			hasAttrMatched = true
 			filteredDomains = append(filteredDomains, domain)
 		}
 	}
+	if !hasAttrMatched {
+		newError("attribute match no rule: geosite:", siteWithAttr)
+	}
 
 	return filteredDomains, nil
 }
 
 func parseDomainRule(domain string) ([]*router.Domain, error) {
 	if strings.HasPrefix(domain, "geosite:") {
-		country := strings.ToUpper(domain[8:])
-		domains, err := loadGeositeWithAttr("geosite.dat", country)
+		list := domain[8:]
+		if len(list) == 0 {
+			return nil, newError("empty listname in rule: ", domain)
+		}
+		domains, err := loadGeositeWithAttr("geosite.dat", list)
 		if err != nil {
-			return nil, newError("failed to load geosite: ", country).Base(err)
+			return nil, newError("failed to load geosite: ", list).Base(err)
 		}
+
 		return domains, nil
 	}
+
 	var isExtDatFile = 0
 	{
 		const prefix = "ext:"
@@ -276,37 +297,55 @@ func parseDomainRule(domain string) ([]*router.Domain, error) {
 			isExtDatFile = len(prefixQualified)
 		}
 	}
+
 	if isExtDatFile != 0 {
 		kv := strings.Split(domain[isExtDatFile:], ":")
 		if len(kv) != 2 {
 			return nil, newError("invalid external resource: ", domain)
 		}
 		filename := kv[0]
-		country := kv[1]
-		domains, err := loadGeositeWithAttr(filename, country)
+		list := kv[1]
+		domains, err := loadGeositeWithAttr(filename, list)
 		if err != nil {
-			return nil, newError("failed to load external sites: ", country, " from ", filename).Base(err)
+			return nil, newError("failed to load external geosite: ", list, " from ", filename).Base(err)
 		}
+
 		return domains, nil
 	}
 
 	domainRule := new(router.Domain)
 	switch {
 	case strings.HasPrefix(domain, "regexp:"):
+		regexpVal := domain[7:]
+		if len(regexpVal) == 0 {
+			return nil, newError("empty regexp type of rule: ", domain)
+		}
 		domainRule.Type = router.Domain_Regex
-		domainRule.Value = domain[7:]
+		domainRule.Value = regexpVal
 
 	case strings.HasPrefix(domain, "domain:"):
+		domainName := domain[7:]
+		if len(domainName) == 0 {
+			return nil, newError("empty domain type of rule: ", domain)
+		}
 		domainRule.Type = router.Domain_Domain
-		domainRule.Value = domain[7:]
+		domainRule.Value = domainName
 
 	case strings.HasPrefix(domain, "full:"):
+		fullVal := domain[5:]
+		if len(fullVal) == 0 {
+			return nil, newError("empty full domain type of rule: ", domain)
+		}
 		domainRule.Type = router.Domain_Full
-		domainRule.Value = domain[5:]
+		domainRule.Value = fullVal
 
 	case strings.HasPrefix(domain, "keyword:"):
+		keywordVal := domain[8:]
+		if len(keywordVal) == 0 {
+			return nil, newError("empty keyword type of rule: ", domain)
+		}
 		domainRule.Type = router.Domain_Plain
-		domainRule.Value = domain[8:]
+		domainRule.Value = keywordVal
 
 	case strings.HasPrefix(domain, "dotless:"):
 		domainRule.Type = router.Domain_Regex
@@ -333,17 +372,22 @@ func toCidrList(ips StringList) ([]*router.GeoIP, error) {
 	for _, ip := range ips {
 		if strings.HasPrefix(ip, "geoip:") {
 			country := ip[6:]
-			geoip, err := loadGeoIP(strings.ToUpper(country))
+			if len(country) == 0 {
+				return nil, newError("empty country name in rule")
+			}
+			geoip, err := loadGeoIP(country)
 			if err != nil {
-				return nil, newError("failed to load GeoIP: ", country).Base(err)
+				return nil, newError("failed to load geoip: ", country).Base(err)
 			}
 
 			geoipList = append(geoipList, &router.GeoIP{
 				CountryCode: strings.ToUpper(country),
 				Cidr:        geoip,
 			})
+
 			continue
 		}
+
 		var isExtDatFile = 0
 		{
 			const prefix = "ext:"
@@ -355,6 +399,7 @@ func toCidrList(ips StringList) ([]*router.GeoIP, error) {
 				isExtDatFile = len(prefixQualified)
 			}
 		}
+
 		if isExtDatFile != 0 {
 			kv := strings.Split(ip[isExtDatFile:], ":")
 			if len(kv) != 2 {
@@ -363,9 +408,12 @@ func toCidrList(ips StringList) ([]*router.GeoIP, error) {
 
 			filename := kv[0]
 			country := kv[1]
-			geoip, err := loadIP(filename, strings.ToUpper(country))
+			if len(filename) == 0 || len(country) == 0 {
+				return nil, newError("empty filename or empty country in rule")
+			}
+			geoip, err := loadIP(filename, country)
 			if err != nil {
-				return nil, newError("failed to load IPs: ", country, " from ", filename).Base(err)
+				return nil, newError("failed to load geoip: ", country, " from ", filename).Base(err)
 			}
 
 			geoipList = append(geoipList, &router.GeoIP{
@@ -506,62 +554,13 @@ func ParseRule(msg json.RawMessage) (*router.RoutingRule, error) {
 	if err != nil {
 		return nil, newError("invalid router rule").Base(err)
 	}
-	if rawRule.Type == "field" {
+	if strings.EqualFold(rawRule.Type, "field") {
 		fieldrule, err := parseFieldRule(msg)
 		if err != nil {
 			return nil, newError("invalid field rule").Base(err)
 		}
 		return fieldrule, nil
 	}
-	if rawRule.Type == "chinaip" {
-		chinaiprule, err := parseChinaIPRule(msg)
-		if err != nil {
-			return nil, newError("invalid chinaip rule").Base(err)
-		}
-		return chinaiprule, nil
-	}
-	if rawRule.Type == "chinasites" {
-		chinasitesrule, err := parseChinaSitesRule(msg)
-		if err != nil {
-			return nil, newError("invalid chinasites rule").Base(err)
-		}
-		return chinasitesrule, nil
-	}
-	return nil, newError("unknown router rule type: ", rawRule.Type)
-}
 
-func parseChinaIPRule(data []byte) (*router.RoutingRule, error) {
-	rawRule := new(RouterRule)
-	err := json.Unmarshal(data, rawRule)
-	if err != nil {
-		return nil, newError("invalid router rule").Base(err)
-	}
-	chinaIPs, err := loadGeoIP("CN")
-	if err != nil {
-		return nil, newError("failed to load geoip:cn").Base(err)
-	}
-	return &router.RoutingRule{
-		TargetTag: &router.RoutingRule_Tag{
-			Tag: rawRule.OutboundTag,
-		},
-		Cidr: chinaIPs,
-	}, nil
-}
-
-func parseChinaSitesRule(data []byte) (*router.RoutingRule, error) {
-	rawRule := new(RouterRule)
-	err := json.Unmarshal(data, rawRule)
-	if err != nil {
-		return nil, newError("invalid router rule").Base(err).AtError()
-	}
-	domains, err := loadGeositeWithAttr("geosite.dat", "CN")
-	if err != nil {
-		return nil, newError("failed to load geosite:cn.").Base(err)
-	}
-	return &router.RoutingRule{
-		TargetTag: &router.RoutingRule_Tag{
-			Tag: rawRule.OutboundTag,
-		},
-		Domain: domains,
-	}, nil
+	return nil, newError("unknown router rule type: ", rawRule.Type)
 }