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  }