Browse Source

Fix panic: index out of range (#727)

Loyalsoldier 4 years ago
parent
commit
45740be4af
2 changed files with 17 additions and 7 deletions
  1. 2 5
      app/dns/dns.go
  2. 15 2
      app/dns/nameserver.go

+ 2 - 5
app/dns/dns.go

@@ -66,9 +66,6 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
 	for _, ns := range config.NameServer {
 	for _, ns := range config.NameServer {
 		domainRuleCount += len(ns.PrioritizedDomain)
 		domainRuleCount += len(ns.PrioritizedDomain)
 	}
 	}
-	// Fixes https://github.com/v2fly/v2ray-core/issues/529
-	// Compatible with `localhost` nameserver specified in config file
-	domainRuleCount += len(localTLDsAndDotlessDomains)
 
 
 	// MatcherInfos is ensured to cover the maximum index domainMatcher could return, where matcher's index starts from 1
 	// MatcherInfos is ensured to cover the maximum index domainMatcher could return, where matcher's index starts from 1
 	matcherInfos := make([]DomainMatcherInfo, domainRuleCount+1)
 	matcherInfos := make([]DomainMatcherInfo, domainRuleCount+1)
@@ -86,7 +83,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
 
 
 	for _, ns := range config.NameServer {
 	for _, ns := range config.NameServer {
 		clientIdx := len(clients)
 		clientIdx := len(clients)
-		updateDomain := func(domainRule strmatcher.Matcher, originalRuleIdx int) error {
+		updateDomain := func(domainRule strmatcher.Matcher, originalRuleIdx int, matcherInfos []DomainMatcherInfo) error {
 			midx := domainMatcher.Add(domainRule)
 			midx := domainMatcher.Add(domainRule)
 			matcherInfos[midx] = DomainMatcherInfo{
 			matcherInfos[midx] = DomainMatcherInfo{
 				clientIdx:     uint16(clientIdx),
 				clientIdx:     uint16(clientIdx),
@@ -100,7 +97,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
 		case net.IPv4len, net.IPv6len:
 		case net.IPv4len, net.IPv6len:
 			myClientIP = net.IP(ns.ClientIp)
 			myClientIP = net.IP(ns.ClientIp)
 		}
 		}
-		client, err := NewClient(ctx, ns, myClientIP, geoipContainer, updateDomain)
+		client, err := NewClient(ctx, ns, myClientIP, geoipContainer, &matcherInfos, updateDomain)
 		if err != nil {
 		if err != nil {
 			return nil, newError("failed to create client").Base(err)
 			return nil, newError("failed to create client").Base(err)
 		}
 		}

+ 15 - 2
app/dns/nameserver.go

@@ -65,7 +65,7 @@ func NewServer(dest net.Destination, dispatcher routing.Dispatcher) (Server, err
 }
 }
 
 
 // NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs.
 // NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs.
-func NewClient(ctx context.Context, ns *NameServer, clientIP net.IP, container router.GeoIPMatcherContainer, updateDomainRule func(strmatcher.Matcher, int) error) (*Client, error) {
+func NewClient(ctx context.Context, ns *NameServer, clientIP net.IP, container router.GeoIPMatcherContainer, matcherInfos *[]DomainMatcherInfo, updateDomainRule func(strmatcher.Matcher, int, []DomainMatcherInfo) error) (*Client, error) {
 	client := &Client{}
 	client := &Client{}
 	err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
 	err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
 		// Create a new server for each client for now
 		// Create a new server for each client for now
@@ -78,6 +78,19 @@ func NewClient(ctx context.Context, ns *NameServer, clientIP net.IP, container r
 		if _, isLocalDNS := server.(*LocalNameServer); isLocalDNS {
 		if _, isLocalDNS := server.(*LocalNameServer); isLocalDNS {
 			ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...)
 			ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...)
 			ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule)
 			ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule)
+			// The following lines is a solution to avoid core panics(rule index out of range) when setting `localhost` DNS client in config.
+			// Because the `localhost` DNS client will apend len(localTLDsAndDotlessDomains) rules into matcherInfos to match `geosite:private` default rule.
+			// But `matcherInfos` has no enough length to add rules, which leads to core panics (rule index out of range).
+			// To avoid this, the length of `matcherInfos` must be equal to the expected, so manually append it with Golang default zero value first for later modification.
+			// Related issues:
+			// https://github.com/v2fly/v2ray-core/issues/529
+			// https://github.com/v2fly/v2ray-core/issues/719
+			for i := 0; i < len(localTLDsAndDotlessDomains); i++ {
+				*matcherInfos = append(*matcherInfos, DomainMatcherInfo{
+					clientIdx:     uint16(0),
+					domainRuleIdx: uint16(0),
+				})
+			}
 		}
 		}
 
 
 		// Establish domain rules
 		// Establish domain rules
@@ -104,7 +117,7 @@ func NewClient(ctx context.Context, ns *NameServer, clientIP net.IP, container r
 				rules = append(rules, domainRule.String())
 				rules = append(rules, domainRule.String())
 				ruleCurr++
 				ruleCurr++
 			}
 			}
-			err = updateDomainRule(domainRule, originalRuleIdx)
+			err = updateDomainRule(domainRule, originalRuleIdx, *matcherInfos)
 			if err != nil {
 			if err != nil {
 				return newError("failed to create prioritized domain").Base(err).AtWarning()
 				return newError("failed to create prioritized domain").Base(err).AtWarning()
 			}
 			}