github.com/xmplusdev/xray-core@v1.8.10/common/strmatcher/domain_matcher.go (about) 1 package strmatcher 2 3 import "strings" 4 5 func breakDomain(domain string) []string { 6 return strings.Split(domain, ".") 7 } 8 9 type node struct { 10 values []uint32 11 sub map[string]*node 12 } 13 14 // DomainMatcherGroup is a IndexMatcher for a large set of Domain matchers. 15 // Visible for testing only. 16 type DomainMatcherGroup struct { 17 root *node 18 } 19 20 func (g *DomainMatcherGroup) Add(domain string, value uint32) { 21 if g.root == nil { 22 g.root = new(node) 23 } 24 25 current := g.root 26 parts := breakDomain(domain) 27 for i := len(parts) - 1; i >= 0; i-- { 28 part := parts[i] 29 if current.sub == nil { 30 current.sub = make(map[string]*node) 31 } 32 next := current.sub[part] 33 if next == nil { 34 next = new(node) 35 current.sub[part] = next 36 } 37 current = next 38 } 39 40 current.values = append(current.values, value) 41 } 42 43 func (g *DomainMatcherGroup) addMatcher(m domainMatcher, value uint32) { 44 g.Add(string(m), value) 45 } 46 47 func (g *DomainMatcherGroup) Match(domain string) []uint32 { 48 if domain == "" { 49 return nil 50 } 51 52 current := g.root 53 if current == nil { 54 return nil 55 } 56 57 nextPart := func(idx int) int { 58 for i := idx - 1; i >= 0; i-- { 59 if domain[i] == '.' { 60 return i 61 } 62 } 63 return -1 64 } 65 66 matches := [][]uint32{} 67 idx := len(domain) 68 for { 69 if idx == -1 || current.sub == nil { 70 break 71 } 72 73 nidx := nextPart(idx) 74 part := domain[nidx+1 : idx] 75 next := current.sub[part] 76 if next == nil { 77 break 78 } 79 current = next 80 idx = nidx 81 if len(current.values) > 0 { 82 matches = append(matches, current.values) 83 } 84 } 85 switch len(matches) { 86 case 0: 87 return nil 88 case 1: 89 return matches[0] 90 default: 91 result := []uint32{} 92 for idx := range matches { 93 // Insert reversely, the subdomain that matches further ranks higher 94 result = append(result, matches[len(matches)-1-idx]...) 95 } 96 return result 97 } 98 }