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