github.com/eagleql/xray-core@v1.4.4/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  }