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  }