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 }