github.com/kelleygo/clashcore@v1.0.2/component/resolver/host.go (about) 1 package resolver 2 3 import ( 4 "errors" 5 "net/netip" 6 "os" 7 "strconv" 8 "strings" 9 _ "unsafe" 10 11 "github.com/kelleygo/clashcore/common/utils" 12 "github.com/kelleygo/clashcore/component/trie" 13 "github.com/zhangyunhao116/fastrand" 14 ) 15 16 var DisableSystemHosts, _ = strconv.ParseBool(os.Getenv("DISABLE_SYSTEM_HOSTS")) 17 18 type Hosts struct { 19 *trie.DomainTrie[HostValue] 20 } 21 22 func NewHosts(hosts *trie.DomainTrie[HostValue]) Hosts { 23 return Hosts{ 24 hosts, 25 } 26 } 27 28 // lookupStaticHost looks up the addresses and the canonical name for the given host from /etc/hosts. 29 // 30 //go:linkname lookupStaticHost net.lookupStaticHost 31 func lookupStaticHost(host string) ([]string, string) 32 33 // Return the search result and whether to match the parameter `isDomain` 34 func (h *Hosts) Search(domain string, isDomain bool) (*HostValue, bool) { 35 if value := h.DomainTrie.Search(domain); value != nil { 36 hostValue := value.Data() 37 for { 38 if isDomain && hostValue.IsDomain { 39 return &hostValue, true 40 } else { 41 if node := h.DomainTrie.Search(hostValue.Domain); node != nil { 42 hostValue = node.Data() 43 } else { 44 break 45 } 46 } 47 } 48 if isDomain == hostValue.IsDomain { 49 return &hostValue, true 50 } 51 52 return &hostValue, false 53 } 54 if !isDomain && !DisableSystemHosts { 55 addr, _ := lookupStaticHost(domain) 56 if hostValue, err := NewHostValue(addr); err == nil { 57 return &hostValue, true 58 } 59 } 60 return nil, false 61 } 62 63 type HostValue struct { 64 IsDomain bool 65 IPs []netip.Addr 66 Domain string 67 } 68 69 func NewHostValue(value any) (HostValue, error) { 70 isDomain := true 71 ips := make([]netip.Addr, 0) 72 domain := "" 73 if valueArr, err := utils.ToStringSlice(value); err != nil { 74 return HostValue{}, err 75 } else { 76 if len(valueArr) > 1 { 77 isDomain = false 78 for _, str := range valueArr { 79 if ip, err := netip.ParseAddr(str); err == nil { 80 ips = append(ips, ip) 81 } else { 82 return HostValue{}, err 83 } 84 } 85 } else if len(valueArr) == 1 { 86 host := valueArr[0] 87 if ip, err := netip.ParseAddr(host); err == nil { 88 ips = append(ips, ip) 89 isDomain = false 90 } else { 91 domain = host 92 } 93 } 94 } 95 if isDomain { 96 return NewHostValueByDomain(domain) 97 } else { 98 return NewHostValueByIPs(ips) 99 } 100 } 101 102 func NewHostValueByIPs(ips []netip.Addr) (HostValue, error) { 103 if len(ips) == 0 { 104 return HostValue{}, errors.New("ip list is empty") 105 } 106 return HostValue{ 107 IsDomain: false, 108 IPs: ips, 109 }, nil 110 } 111 112 func NewHostValueByDomain(domain string) (HostValue, error) { 113 domain = strings.Trim(domain, ".") 114 item := strings.Split(domain, ".") 115 if len(item) < 2 { 116 return HostValue{}, errors.New("invaild domain") 117 } 118 return HostValue{ 119 IsDomain: true, 120 Domain: domain, 121 }, nil 122 } 123 124 func (hv HostValue) RandIP() (netip.Addr, error) { 125 if hv.IsDomain { 126 return netip.Addr{}, errors.New("value type is error") 127 } 128 return hv.IPs[fastrand.Intn(len(hv.IPs))], nil 129 }