mihomo/dns/resolver.go

341 lines
7.1 KiB
Go
Raw Normal View History

2019-06-28 12:29:08 +08:00
package dns
import (
"context"
"crypto/tls"
"errors"
2020-04-16 18:31:40 +08:00
"fmt"
"math/rand"
2019-06-28 12:29:08 +08:00
"net"
"strings"
"time"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/common/picker"
"github.com/Dreamacro/clash/component/fakeip"
"github.com/Dreamacro/clash/component/resolver"
2019-06-28 12:29:08 +08:00
D "github.com/miekg/dns"
2019-07-14 19:29:58 +08:00
"golang.org/x/sync/singleflight"
2019-06-28 12:29:08 +08:00
)
var (
globalSessionCache = tls.NewLRUClientSessionCache(64)
)
type dnsClient interface {
2019-06-28 12:29:08 +08:00
Exchange(m *D.Msg) (msg *D.Msg, err error)
ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error)
}
type result struct {
Msg *D.Msg
Error error
}
type Resolver struct {
2019-09-15 13:36:45 +08:00
ipv6 bool
mapping bool
fakeip bool
pool *fakeip.Pool
main []dnsClient
fallback []dnsClient
2019-09-15 13:36:45 +08:00
fallbackFilters []fallbackFilter
group singleflight.Group
lruCache *cache.LruCache
2019-06-28 12:29:08 +08:00
}
2020-02-17 20:11:46 +08:00
// ResolveIP request with TypeA and TypeAAAA, priority return TypeA
2019-06-28 12:29:08 +08:00
func (r *Resolver) ResolveIP(host string) (ip net.IP, err error) {
2020-02-17 20:11:46 +08:00
ch := make(chan net.IP, 1)
2019-06-28 12:29:08 +08:00
go func() {
defer close(ch)
2020-02-17 20:11:46 +08:00
ip, err := r.resolveIP(host, D.TypeAAAA)
2019-06-28 12:29:08 +08:00
if err != nil {
return
}
ch <- ip
}()
2020-02-17 20:11:46 +08:00
ip, err = r.resolveIP(host, D.TypeA)
2019-06-28 12:29:08 +08:00
if err == nil {
return
}
ip, open := <-ch
if !open {
return nil, resolver.ErrIPNotFound
2019-06-28 12:29:08 +08:00
}
return ip, nil
}
// ResolveIPv4 request with TypeA
func (r *Resolver) ResolveIPv4(host string) (ip net.IP, err error) {
2019-09-11 17:00:55 +08:00
return r.resolveIP(host, D.TypeA)
}
2019-06-28 12:29:08 +08:00
2019-09-11 17:00:55 +08:00
// ResolveIPv6 request with TypeAAAA
func (r *Resolver) ResolveIPv6(host string) (ip net.IP, err error) {
return r.resolveIP(host, D.TypeAAAA)
2019-06-28 12:29:08 +08:00
}
2019-09-15 13:36:45 +08:00
func (r *Resolver) shouldFallback(ip net.IP) bool {
for _, filter := range r.fallbackFilters {
if filter.Match(ip) {
return true
}
}
return false
}
2019-06-28 12:29:08 +08:00
// Exchange a batch of dns request, and it use cache
func (r *Resolver) Exchange(m *D.Msg) (msg *D.Msg, err error) {
if len(m.Question) == 0 {
return nil, errors.New("should have one question at least")
}
q := m.Question[0]
cache, expireTime, hit := r.lruCache.GetWithExpire(q.String())
if hit {
now := time.Now()
2019-06-28 12:29:08 +08:00
msg = cache.(*D.Msg).Copy()
if expireTime.Before(now) {
setMsgTTL(msg, uint32(1)) // Continue fetch
go r.exchangeWithoutCache(m)
} else {
setMsgTTL(msg, uint32(expireTime.Sub(time.Now()).Seconds()))
}
2019-06-28 12:29:08 +08:00
return
}
return r.exchangeWithoutCache(m)
}
// ExchangeWithoutCache a batch of dns request, and it do NOT GET from cache
func (r *Resolver) exchangeWithoutCache(m *D.Msg) (msg *D.Msg, err error) {
q := m.Question[0]
2019-06-28 12:29:08 +08:00
defer func() {
if msg == nil {
return
}
putMsgToCache(r.lruCache, q.String(), msg)
if r.mapping || r.fakeip {
2019-06-28 12:29:08 +08:00
ips := r.msgToIP(msg)
for _, ip := range ips {
putMsgToCache(r.lruCache, ip.String(), msg)
2019-06-28 12:29:08 +08:00
}
}
}()
2020-03-24 10:13:53 +08:00
ret, err, shared := r.group.Do(q.String(), func() (interface{}, error) {
2019-07-14 19:29:58 +08:00
isIPReq := isIPRequest(q)
if isIPReq {
2020-03-08 13:00:42 +08:00
return r.fallbackExchange(m)
2019-07-14 19:29:58 +08:00
}
return r.batchExchange(r.main, m)
})
if err == nil {
msg = ret.(*D.Msg)
2020-03-24 10:13:53 +08:00
if shared {
msg = msg.Copy()
}
2019-06-28 12:29:08 +08:00
}
return
}
// IPToHost return fake-ip or redir-host mapping host
func (r *Resolver) IPToHost(ip net.IP) (string, bool) {
if r.fakeip {
record, existed := r.pool.LookBack(ip)
if existed {
return record, true
}
}
cache, _ := r.lruCache.Get(ip.String())
2019-06-28 12:29:08 +08:00
if cache == nil {
return "", false
}
fqdn := cache.(*D.Msg).Question[0].Name
return strings.TrimRight(fqdn, "."), true
}
func (r *Resolver) IsMapping() bool {
return r.mapping
}
// FakeIPEnabled returns if fake-ip is enabled
func (r *Resolver) FakeIPEnabled() bool {
2019-06-28 12:29:08 +08:00
return r.fakeip
}
// IsFakeIP determine if given ip is a fake-ip
func (r *Resolver) IsFakeIP(ip net.IP) bool {
if r.FakeIPEnabled() {
return r.pool.Exist(ip)
}
return false
}
func (r *Resolver) batchExchange(clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
fast, ctx := picker.WithTimeout(context.Background(), time.Second*5)
2019-08-02 09:22:09 +08:00
for _, client := range clients {
r := client
2019-07-02 19:18:03 +08:00
fast.Go(func() (interface{}, error) {
2020-02-25 21:51:48 +08:00
m, err := r.ExchangeContext(ctx, m)
if err != nil {
return nil, err
2020-03-08 13:00:42 +08:00
} else if m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused {
2020-02-25 21:51:48 +08:00
return nil, errors.New("server failure")
}
return m, nil
2019-07-02 19:18:03 +08:00
})
2019-06-28 12:29:08 +08:00
}
elm := fast.Wait()
2019-07-02 19:18:03 +08:00
if elm == nil {
2020-04-16 18:31:40 +08:00
err := errors.New("All DNS requests failed")
if fErr := fast.Error(); fErr != nil {
err = fmt.Errorf("%w, first error: %s", err, fErr.Error())
}
return nil, err
2019-06-28 12:29:08 +08:00
}
msg = elm.(*D.Msg)
return
}
func (r *Resolver) fallbackExchange(m *D.Msg) (msg *D.Msg, err error) {
msgCh := r.asyncExchange(r.main, m)
if r.fallback == nil {
res := <-msgCh
msg, err = res.Msg, res.Error
return
}
fallbackMsg := r.asyncExchange(r.fallback, m)
res := <-msgCh
if res.Error == nil {
if ips := r.msgToIP(res.Msg); len(ips) != 0 {
2020-03-13 00:11:54 +08:00
if !r.shouldFallback(ips[0]) {
2019-06-28 12:29:08 +08:00
msg = res.Msg
2020-03-08 13:00:42 +08:00
err = res.Error
2019-06-28 12:29:08 +08:00
return msg, err
}
}
}
res = <-fallbackMsg
msg, err = res.Msg, res.Error
return
}
func (r *Resolver) resolveIP(host string, dnsType uint16) (ip net.IP, err error) {
2019-09-11 17:00:55 +08:00
ip = net.ParseIP(host)
if ip != nil {
2019-09-27 15:26:07 +08:00
isIPv4 := ip.To4() != nil
if dnsType == D.TypeAAAA && !isIPv4 {
return ip, nil
2019-09-27 15:26:07 +08:00
} else if dnsType == D.TypeA && isIPv4 {
return ip, nil
2020-02-17 20:11:46 +08:00
} else {
return nil, resolver.ErrIPVersion
2019-09-11 17:00:55 +08:00
}
}
2019-06-28 12:29:08 +08:00
query := &D.Msg{}
query.SetQuestion(D.Fqdn(host), dnsType)
msg, err := r.Exchange(query)
if err != nil {
return nil, err
}
ips := r.msgToIP(msg)
ipLength := len(ips)
if ipLength == 0 {
return nil, resolver.ErrIPNotFound
2019-06-28 12:29:08 +08:00
}
ip = ips[rand.Intn(ipLength)]
2019-06-28 12:29:08 +08:00
return
}
func (r *Resolver) msgToIP(msg *D.Msg) []net.IP {
ips := []net.IP{}
for _, answer := range msg.Answer {
switch ans := answer.(type) {
case *D.AAAA:
ips = append(ips, ans.AAAA)
case *D.A:
ips = append(ips, ans.A)
}
}
return ips
}
func (r *Resolver) asyncExchange(client []dnsClient, msg *D.Msg) <-chan *result {
2020-03-13 00:11:54 +08:00
ch := make(chan *result, 1)
2019-06-28 12:29:08 +08:00
go func() {
res, err := r.batchExchange(client, msg)
ch <- &result{Msg: res, Error: err}
}()
return ch
}
type NameServer struct {
Net string
Addr string
}
2019-09-15 13:36:45 +08:00
type FallbackFilter struct {
GeoIP bool
IPCIDR []*net.IPNet
}
2019-06-28 12:29:08 +08:00
type Config struct {
Main, Fallback []NameServer
Default []NameServer
2019-06-28 12:29:08 +08:00
IPv6 bool
EnhancedMode EnhancedMode
2019-09-15 13:36:45 +08:00
FallbackFilter FallbackFilter
2019-06-28 12:29:08 +08:00
Pool *fakeip.Pool
}
func New(config Config) *Resolver {
defaultResolver := &Resolver{
main: transform(config.Default, nil),
lruCache: cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true)),
}
2019-06-28 12:29:08 +08:00
r := &Resolver{
ipv6: config.IPv6,
main: transform(config.Main, defaultResolver),
lruCache: cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true)),
mapping: config.EnhancedMode == MAPPING,
fakeip: config.EnhancedMode == FAKEIP,
pool: config.Pool,
2019-06-28 12:29:08 +08:00
}
2019-09-15 13:36:45 +08:00
2019-06-28 12:29:08 +08:00
if len(config.Fallback) != 0 {
r.fallback = transform(config.Fallback, defaultResolver)
2019-06-28 12:29:08 +08:00
}
2019-09-15 13:36:45 +08:00
fallbackFilters := []fallbackFilter{}
if config.FallbackFilter.GeoIP {
fallbackFilters = append(fallbackFilters, &geoipFilter{})
}
for _, ipnet := range config.FallbackFilter.IPCIDR {
fallbackFilters = append(fallbackFilters, &ipnetFilter{ipnet: ipnet})
}
r.fallbackFilters = fallbackFilters
2019-06-28 12:29:08 +08:00
return r
}