github.com/metacubex/mihomo@v1.18.5/config/utils.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"net/netip"
     7  	"strings"
     8  
     9  	"github.com/metacubex/mihomo/adapter/outboundgroup"
    10  	"github.com/metacubex/mihomo/common/structure"
    11  )
    12  
    13  func trimArr(arr []string) (r []string) {
    14  	for _, e := range arr {
    15  		r = append(r, strings.Trim(e, " "))
    16  	}
    17  	return
    18  }
    19  
    20  // Check if ProxyGroups form DAG(Directed Acyclic Graph), and sort all ProxyGroups by dependency order.
    21  // Meanwhile, record the original index in the config file.
    22  // If loop is detected, return an error with location of loop.
    23  func proxyGroupsDagSort(groupsConfig []map[string]any) error {
    24  	type graphNode struct {
    25  		indegree int
    26  		// topological order
    27  		topo int
    28  		// the original data in `groupsConfig`
    29  		data map[string]any
    30  		// `outdegree` and `from` are used in loop locating
    31  		outdegree int
    32  		option    *outboundgroup.GroupCommonOption
    33  		from      []string
    34  	}
    35  
    36  	decoder := structure.NewDecoder(structure.Option{TagName: "group", WeaklyTypedInput: true})
    37  	graph := make(map[string]*graphNode)
    38  
    39  	// Step 1.1 build dependency graph
    40  	for _, mapping := range groupsConfig {
    41  		option := &outboundgroup.GroupCommonOption{}
    42  		if err := decoder.Decode(mapping, option); err != nil {
    43  			return fmt.Errorf("ProxyGroup %s: %s", option.Name, err.Error())
    44  		}
    45  
    46  		groupName := option.Name
    47  		if node, ok := graph[groupName]; ok {
    48  			if node.data != nil {
    49  				return fmt.Errorf("ProxyGroup %s: duplicate group name", groupName)
    50  			}
    51  			node.data = mapping
    52  			node.option = option
    53  		} else {
    54  			graph[groupName] = &graphNode{0, -1, mapping, 0, option, nil}
    55  		}
    56  
    57  		for _, proxy := range option.Proxies {
    58  			if node, ex := graph[proxy]; ex {
    59  				node.indegree++
    60  			} else {
    61  				graph[proxy] = &graphNode{1, -1, nil, 0, nil, nil}
    62  			}
    63  		}
    64  	}
    65  	// Step 1.2 Topological Sort
    66  	// topological index of **ProxyGroup**
    67  	index := 0
    68  	queue := make([]string, 0)
    69  	for name, node := range graph {
    70  		// in the beginning, put nodes that have `node.indegree == 0` into queue.
    71  		if node.indegree == 0 {
    72  			queue = append(queue, name)
    73  		}
    74  	}
    75  	// every element in queue have indegree == 0
    76  	for ; len(queue) > 0; queue = queue[1:] {
    77  		name := queue[0]
    78  		node := graph[name]
    79  		if node.option != nil {
    80  			index++
    81  			groupsConfig[len(groupsConfig)-index] = node.data
    82  			if len(node.option.Proxies) == 0 {
    83  				delete(graph, name)
    84  				continue
    85  			}
    86  
    87  			for _, proxy := range node.option.Proxies {
    88  				child := graph[proxy]
    89  				child.indegree--
    90  				if child.indegree == 0 {
    91  					queue = append(queue, proxy)
    92  				}
    93  			}
    94  		}
    95  		delete(graph, name)
    96  	}
    97  
    98  	// no loop is detected, return sorted ProxyGroup
    99  	if len(graph) == 0 {
   100  		return nil
   101  	}
   102  
   103  	// if loop is detected, locate the loop and throw an error
   104  	// Step 2.1 rebuild the graph, fill `outdegree` and `from` filed
   105  	for name, node := range graph {
   106  		if node.option == nil {
   107  			continue
   108  		}
   109  
   110  		if len(node.option.Proxies) == 0 {
   111  			continue
   112  		}
   113  
   114  		for _, proxy := range node.option.Proxies {
   115  			node.outdegree++
   116  			child := graph[proxy]
   117  			if child.from == nil {
   118  				child.from = make([]string, 0, child.indegree)
   119  			}
   120  			child.from = append(child.from, name)
   121  		}
   122  	}
   123  	// Step 2.2 remove nodes outside the loop. so that we have only the loops remain in `graph`
   124  	queue = make([]string, 0)
   125  	// initialize queue with node have outdegree == 0
   126  	for name, node := range graph {
   127  		if node.outdegree == 0 {
   128  			queue = append(queue, name)
   129  		}
   130  	}
   131  	// every element in queue have outdegree == 0
   132  	for ; len(queue) > 0; queue = queue[1:] {
   133  		name := queue[0]
   134  		node := graph[name]
   135  		for _, f := range node.from {
   136  			graph[f].outdegree--
   137  			if graph[f].outdegree == 0 {
   138  				queue = append(queue, f)
   139  			}
   140  		}
   141  		delete(graph, name)
   142  	}
   143  	// Step 2.3 report the elements in loop
   144  	loopElements := make([]string, 0, len(graph))
   145  	for name := range graph {
   146  		loopElements = append(loopElements, name)
   147  		delete(graph, name)
   148  	}
   149  	return fmt.Errorf("loop is detected in ProxyGroup, please check following ProxyGroups: %v", loopElements)
   150  }
   151  
   152  func verifyIP6() bool {
   153  	if iAddrs, err := net.InterfaceAddrs(); err == nil {
   154  		for _, addr := range iAddrs {
   155  			if prefix, err := netip.ParsePrefix(addr.String()); err == nil {
   156  				if addr := prefix.Addr().Unmap(); addr.Is6() && addr.IsGlobalUnicast() {
   157  					return true
   158  				}
   159  			}
   160  		}
   161  	}
   162  	return false
   163  }