github.com/yaling888/clash@v1.53.0/component/resolver/resolver.go (about)

     1  package resolver
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"math/rand/v2"
     7  	"net"
     8  	"net/netip"
     9  	"time"
    10  
    11  	"github.com/miekg/dns"
    12  	"github.com/samber/lo"
    13  
    14  	"github.com/yaling888/clash/component/trie"
    15  )
    16  
    17  var (
    18  	// DefaultResolver aim to resolve ip
    19  	DefaultResolver Resolver
    20  
    21  	// DisableIPv6 means don't resolve ipv6 host
    22  	// default value is true
    23  	DisableIPv6 = true
    24  
    25  	// RemoteDnsResolve reports whether TCP/UDP handler should be remote resolve DNS
    26  	// default value is true
    27  	RemoteDnsResolve = true
    28  
    29  	// DefaultHosts aim to resolve hosts
    30  	DefaultHosts = trie.New[netip.Addr]()
    31  
    32  	// DefaultDNSTimeout defined the default dns request timeout
    33  	DefaultDNSTimeout = time.Second * 5
    34  
    35  	needProxyHostIPv6 = false
    36  )
    37  
    38  var (
    39  	ErrIPNotFound   = errors.New("couldn't find ip")
    40  	ErrIPVersion    = errors.New("ip version error")
    41  	ErrIPv6Disabled = errors.New("ipv6 disabled")
    42  )
    43  
    44  const (
    45  	proxyServerHostKey = ipContextKey("key-lookup-proxy-server-ip")
    46  	proxyKey           = ipContextKey("key-lookup-by-proxy")
    47  )
    48  
    49  const (
    50  	typeNone uint16 = 0
    51  	typeA    uint16 = 1
    52  	typeAAAA uint16 = 28
    53  )
    54  
    55  type ipContextKey string
    56  
    57  type Resolver interface {
    58  	LookupIP(ctx context.Context, host string) ([]netip.Addr, error)
    59  	LookupIPv4(ctx context.Context, host string) ([]netip.Addr, error)
    60  	LookupIPv6(ctx context.Context, host string) ([]netip.Addr, error)
    61  	ResolveIP(host string) (ip netip.Addr, err error)
    62  	ResolveIPv4(host string) (ip netip.Addr, err error)
    63  	ResolveIPv6(host string) (ip netip.Addr, err error)
    64  	ExchangeContext(ctx context.Context, m *dns.Msg) (msg *dns.Msg, source string, err error)
    65  	ExchangeContextWithoutCache(ctx context.Context, m *dns.Msg) (msg *dns.Msg, source string, err error)
    66  	RemoveCache(host string)
    67  }
    68  
    69  // LookupIP with a host, return ip list
    70  func LookupIP(ctx context.Context, host string) ([]netip.Addr, error) {
    71  	return LookupIPByResolver(ctx, host, DefaultResolver)
    72  }
    73  
    74  // LookupIPv4 with a host, return ipv4 list
    75  func LookupIPv4(ctx context.Context, host string) ([]netip.Addr, error) {
    76  	return lookupIPByResolverAndType(ctx, host, DefaultResolver, typeA, false)
    77  }
    78  
    79  // LookupIPv6 with a host, return ipv6 list
    80  func LookupIPv6(ctx context.Context, host string) ([]netip.Addr, error) {
    81  	return lookupIPByResolverAndType(ctx, host, DefaultResolver, typeAAAA, false)
    82  }
    83  
    84  // LookupIPByResolver same as ResolveIP, but with a resolver
    85  func LookupIPByResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) {
    86  	return lookupIPByResolverAndType(ctx, host, r, typeNone, false)
    87  }
    88  
    89  // LookupIPByProxy with a host and proxy, reports force combined ipv6 list whether the DisableIPv6 value is true
    90  func LookupIPByProxy(ctx context.Context, host, proxy string) ([]netip.Addr, error) {
    91  	return lookupIPByProxyAndType(ctx, host, proxy, typeNone, true)
    92  }
    93  
    94  // LookupIPv4ByProxy with a host and proxy, reports ipv4 list
    95  func LookupIPv4ByProxy(ctx context.Context, host, proxy string) ([]netip.Addr, error) {
    96  	return lookupIPByProxyAndType(ctx, host, proxy, typeA, false)
    97  }
    98  
    99  // LookupIPv6ByProxy with a host and proxy, reports ipv6 list whether the DisableIPv6 value is true
   100  func LookupIPv6ByProxy(ctx context.Context, host, proxy string) ([]netip.Addr, error) {
   101  	return lookupIPByProxyAndType(ctx, host, proxy, typeAAAA, true)
   102  }
   103  
   104  // ResolveIP with a host, return ip
   105  func ResolveIP(host string) (netip.Addr, error) {
   106  	return resolveIPByType(host, typeNone)
   107  }
   108  
   109  // ResolveIPv4 with a host, return ipv4
   110  func ResolveIPv4(host string) (netip.Addr, error) {
   111  	return resolveIPByType(host, typeA)
   112  }
   113  
   114  // ResolveIPv6 with a host, return ipv6
   115  func ResolveIPv6(host string) (netip.Addr, error) {
   116  	return resolveIPByType(host, typeAAAA)
   117  }
   118  
   119  // ResolveProxyServerHost proxies server host only
   120  func ResolveProxyServerHost(host string) (netip.Addr, error) {
   121  	return resolveProxyServerHostByType(host, typeNone)
   122  }
   123  
   124  // ResolveIPv4ProxyServerHost proxies server host only
   125  func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) {
   126  	return resolveProxyServerHostByType(host, typeA)
   127  }
   128  
   129  // ResolveIPv6ProxyServerHost proxies server host only
   130  func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) {
   131  	return resolveProxyServerHostByType(host, typeAAAA)
   132  }
   133  
   134  // SetDisableIPv6 set DisableIPv6 & needProxyHostIPv6 value
   135  func SetDisableIPv6(v bool) {
   136  	DisableIPv6 = v
   137  	needProxyHostIPv6 = !v
   138  }
   139  
   140  // RemoveCache remove cache by host
   141  func RemoveCache(host string) {
   142  	if DefaultResolver != nil {
   143  		DefaultResolver.RemoveCache(host)
   144  	}
   145  }
   146  
   147  // IsProxyServer reports whether the DefaultResolver should be exchanged by proxyServer DNS client
   148  func IsProxyServer(ctx context.Context) bool {
   149  	return ctx.Value(proxyServerHostKey) != nil
   150  }
   151  
   152  // IsRemote reports whether the DefaultResolver should be exchanged by remote DNS client
   153  func IsRemote(ctx context.Context) bool {
   154  	return ctx.Value(proxyKey) != nil
   155  }
   156  
   157  // GetProxy reports the proxy name used by the DNS client and whether there is a proxy
   158  func GetProxy(ctx context.Context) (string, bool) {
   159  	v, ok := ctx.Value(proxyKey).(string)
   160  	return v, ok
   161  }
   162  
   163  // WithProxy returns a new context with proxy name
   164  func WithProxy(ctx context.Context, proxy string) context.Context {
   165  	return context.WithValue(ctx, proxyKey, proxy)
   166  }
   167  
   168  // CopyCtxValues returns a new context with parent's values
   169  func CopyCtxValues(parent context.Context) context.Context {
   170  	newCtx := context.Background()
   171  	if v, ok := parent.Value(proxyKey).(string); ok {
   172  		newCtx = context.WithValue(newCtx, proxyKey, v)
   173  	}
   174  	if parent.Value(proxyServerHostKey) != nil {
   175  		newCtx = context.WithValue(newCtx, proxyServerHostKey, struct{}{})
   176  	}
   177  	return newCtx
   178  }
   179  
   180  func resolveIPByType(host string, _type uint16) (netip.Addr, error) {
   181  	var (
   182  		ips []netip.Addr
   183  		err error
   184  	)
   185  
   186  	switch _type {
   187  	case typeNone:
   188  		ips, err = LookupIP(context.Background(), host)
   189  	case typeA:
   190  		ips, err = LookupIPv4(context.Background(), host)
   191  	default:
   192  		ips, err = LookupIPv6(context.Background(), host)
   193  	}
   194  
   195  	if err != nil {
   196  		return netip.Addr{}, err
   197  	}
   198  
   199  	return ips[rand.IntN(len(ips))], nil
   200  }
   201  
   202  func resolveProxyServerHostByType(host string, _type uint16) (netip.Addr, error) {
   203  	var (
   204  		ips []netip.Addr
   205  		err error
   206  		ctx = context.WithValue(context.Background(), proxyServerHostKey, struct{}{})
   207  	)
   208  
   209  	ips, err = lookupIPByResolverAndType(ctx, host, DefaultResolver, _type, needProxyHostIPv6)
   210  	if err != nil {
   211  		return netip.Addr{}, err
   212  	}
   213  
   214  	return ips[rand.IntN(len(ips))], nil
   215  }
   216  
   217  func lookupIPByProxyAndType(ctx context.Context, host, proxy string, t uint16, both bool) ([]netip.Addr, error) {
   218  	ctx = context.WithValue(ctx, proxyKey, proxy)
   219  	return lookupIPByResolverAndType(ctx, host, DefaultResolver, t, both)
   220  }
   221  
   222  func lookupIPByResolverAndType(ctx context.Context, host string, r Resolver, t uint16, both bool) ([]netip.Addr, error) {
   223  	if t == typeAAAA && DisableIPv6 && !both {
   224  		return nil, ErrIPv6Disabled
   225  	}
   226  
   227  	if node := DefaultHosts.Search(host); node != nil {
   228  		ip := node.Data
   229  		if t != typeAAAA {
   230  			ip = ip.Unmap()
   231  		}
   232  		if t == typeNone || (t == typeA && ip.Is4()) || (t == typeAAAA && ip.Is6()) {
   233  			return []netip.Addr{ip}, nil
   234  		}
   235  	}
   236  
   237  	if r != nil {
   238  		if t == typeA {
   239  			return r.LookupIPv4(ctx, host)
   240  		} else if t == typeAAAA {
   241  			return r.LookupIPv6(ctx, host)
   242  		}
   243  		if DisableIPv6 && !both {
   244  			return r.LookupIPv4(ctx, host)
   245  		}
   246  		return r.LookupIP(ctx, host)
   247  	} else if t == typeNone && DisableIPv6 {
   248  		return LookupIPv4(ctx, host)
   249  	}
   250  
   251  	if ip, err := netip.ParseAddr(host); err == nil {
   252  		if t != typeAAAA {
   253  			ip = ip.Unmap()
   254  		}
   255  		is4 := ip.Is4()
   256  		if (t == typeA && !is4) || (t == typeAAAA && is4) {
   257  			return nil, ErrIPVersion
   258  		}
   259  		return []netip.Addr{ip}, nil
   260  	}
   261  
   262  	network := "ip"
   263  	if t == typeA {
   264  		network = "ip4"
   265  	} else if t == typeAAAA {
   266  		network = "ip6"
   267  	}
   268  
   269  	ips, err := net.DefaultResolver.LookupIP(ctx, network, host)
   270  	if err != nil {
   271  		return nil, err
   272  	} else if len(ips) == 0 {
   273  		return nil, ErrIPNotFound
   274  	}
   275  
   276  	return lo.Map(ips, func(item net.IP, _ int) netip.Addr {
   277  		ip, _ := netip.AddrFromSlice(item)
   278  		if t != typeAAAA {
   279  			ip = ip.Unmap()
   280  		}
   281  		return ip
   282  	}), nil
   283  }