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  }