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