github.com/v2fly/v2ray-core/v4@v4.45.2/app/dns/hosts.go (about)

     1  //go:build !confonly
     2  // +build !confonly
     3  
     4  package dns
     5  
     6  import (
     7  	"github.com/v2fly/v2ray-core/v4/common"
     8  	"github.com/v2fly/v2ray-core/v4/common/net"
     9  	"github.com/v2fly/v2ray-core/v4/common/strmatcher"
    10  	"github.com/v2fly/v2ray-core/v4/features"
    11  	"github.com/v2fly/v2ray-core/v4/features/dns"
    12  )
    13  
    14  // StaticHosts represents static domain-ip mapping in DNS server.
    15  type StaticHosts struct {
    16  	ips      [][]net.Address
    17  	matchers *strmatcher.MatcherGroup
    18  }
    19  
    20  // NewStaticHosts creates a new StaticHosts instance.
    21  func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDomain) (*StaticHosts, error) {
    22  	g := new(strmatcher.MatcherGroup)
    23  	sh := &StaticHosts{
    24  		ips:      make([][]net.Address, len(hosts)+len(legacy)+16),
    25  		matchers: g,
    26  	}
    27  
    28  	if legacy != nil {
    29  		features.PrintDeprecatedFeatureWarning("simple host mapping")
    30  
    31  		for domain, ip := range legacy {
    32  			matcher, err := strmatcher.Full.New(domain)
    33  			common.Must(err)
    34  			id := g.Add(matcher)
    35  
    36  			address := ip.AsAddress()
    37  			if address.Family().IsDomain() {
    38  				return nil, newError("invalid domain address in static hosts: ", address.Domain()).AtWarning()
    39  			}
    40  
    41  			sh.ips[id] = []net.Address{address}
    42  		}
    43  	}
    44  
    45  	for _, mapping := range hosts {
    46  		matcher, err := toStrMatcher(mapping.Type, mapping.Domain)
    47  		if err != nil {
    48  			return nil, newError("failed to create domain matcher").Base(err)
    49  		}
    50  		id := g.Add(matcher)
    51  		ips := make([]net.Address, 0, len(mapping.Ip)+1)
    52  		switch {
    53  		case len(mapping.ProxiedDomain) > 0:
    54  			ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
    55  		case len(mapping.Ip) > 0:
    56  			for _, ip := range mapping.Ip {
    57  				addr := net.IPAddress(ip)
    58  				if addr == nil {
    59  					return nil, newError("invalid IP address in static hosts: ", ip).AtWarning()
    60  				}
    61  				ips = append(ips, addr)
    62  			}
    63  		default:
    64  			return nil, newError("neither IP address nor proxied domain specified for domain: ", mapping.Domain).AtWarning()
    65  		}
    66  
    67  		sh.ips[id] = ips
    68  	}
    69  
    70  	return sh, nil
    71  }
    72  
    73  func filterIP(ips []net.Address, option dns.IPOption) []net.Address {
    74  	filtered := make([]net.Address, 0, len(ips))
    75  	for _, ip := range ips {
    76  		if (ip.Family().IsIPv4() && option.IPv4Enable) || (ip.Family().IsIPv6() && option.IPv6Enable) {
    77  			filtered = append(filtered, ip)
    78  		}
    79  	}
    80  	return filtered
    81  }
    82  
    83  func (h *StaticHosts) lookupInternal(domain string) []net.Address {
    84  	var ips []net.Address
    85  	for _, id := range h.matchers.Match(domain) {
    86  		ips = append(ips, h.ips[id]...)
    87  	}
    88  	return ips
    89  }
    90  
    91  func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) []net.Address {
    92  	switch addrs := h.lookupInternal(domain); {
    93  	case len(addrs) == 0: // Not recorded in static hosts, return nil
    94  		return nil
    95  	case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain
    96  		newError("found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it").AtDebug().WriteToLog()
    97  		if maxDepth > 0 {
    98  			unwrapped := h.lookup(addrs[0].Domain(), option, maxDepth-1)
    99  			if unwrapped != nil {
   100  				return unwrapped
   101  			}
   102  		}
   103  		return addrs
   104  	default: // IP record found, return a non-nil IP array
   105  		return filterIP(addrs, option)
   106  	}
   107  }
   108  
   109  // Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts.
   110  func (h *StaticHosts) Lookup(domain string, option dns.IPOption) []net.Address {
   111  	return h.lookup(domain, option, 5)
   112  }