github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/dns/dns.go (about)

     1  //go:build !confonly
     2  // +build !confonly
     3  
     4  // Package dns is an implementation of core.DNS feature.
     5  package dns
     6  
     7  //go:generate go run github.com/v2fly/v2ray-core/v5/common/errors/errorgen
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"strings"
    13  	"sync"
    14  
    15  	core "github.com/v2fly/v2ray-core/v5"
    16  	"github.com/v2fly/v2ray-core/v5/app/dns/fakedns"
    17  	"github.com/v2fly/v2ray-core/v5/app/router"
    18  	"github.com/v2fly/v2ray-core/v5/app/router/routercommon"
    19  	"github.com/v2fly/v2ray-core/v5/common"
    20  	"github.com/v2fly/v2ray-core/v5/common/errors"
    21  	"github.com/v2fly/v2ray-core/v5/common/net"
    22  	"github.com/v2fly/v2ray-core/v5/common/platform"
    23  	"github.com/v2fly/v2ray-core/v5/common/session"
    24  	"github.com/v2fly/v2ray-core/v5/common/strmatcher"
    25  	"github.com/v2fly/v2ray-core/v5/features"
    26  	"github.com/v2fly/v2ray-core/v5/features/dns"
    27  	"github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon"
    28  	"github.com/v2fly/v2ray-core/v5/infra/conf/geodata"
    29  )
    30  
    31  // DNS is a DNS rely server.
    32  type DNS struct {
    33  	sync.Mutex
    34  	hosts         *StaticHosts
    35  	clients       []*Client
    36  	ctx           context.Context
    37  	clientTags    map[string]bool
    38  	fakeDNSEngine *FakeDNSEngine
    39  	domainMatcher strmatcher.IndexMatcher
    40  	matcherInfos  []DomainMatcherInfo
    41  }
    42  
    43  // DomainMatcherInfo contains information attached to index returned by Server.domainMatcher
    44  type DomainMatcherInfo struct {
    45  	clientIdx     uint16
    46  	domainRuleIdx uint16
    47  }
    48  
    49  // New creates a new DNS server with given configuration.
    50  func New(ctx context.Context, config *Config) (*DNS, error) {
    51  	// Create static hosts
    52  	hosts, err := NewStaticHosts(config.StaticHosts, config.Hosts)
    53  	if err != nil {
    54  		return nil, newError("failed to create hosts").Base(err)
    55  	}
    56  
    57  	// Create name servers from legacy configs
    58  	clients := []*Client{}
    59  	for _, endpoint := range config.NameServers {
    60  		features.PrintDeprecatedFeatureWarning("simple DNS server")
    61  		client, err := NewClient(ctx, &NameServer{Address: endpoint}, config)
    62  		if err != nil {
    63  			return nil, newError("failed to create client").Base(err)
    64  		}
    65  		clients = append(clients, client)
    66  	}
    67  
    68  	// Create name servers
    69  	nsClientMap := map[int]int{}
    70  	for nsIdx, ns := range config.NameServer {
    71  		client, err := NewClient(ctx, ns, config)
    72  		if err != nil {
    73  			return nil, newError("failed to create client").Base(err)
    74  		}
    75  		nsClientMap[nsIdx] = len(clients)
    76  		clients = append(clients, client)
    77  	}
    78  
    79  	// If there is no DNS client in config, add a `localhost` DNS client
    80  	if len(clients) == 0 {
    81  		clients = append(clients, NewLocalDNSClient())
    82  	}
    83  
    84  	s := &DNS{
    85  		hosts:   hosts,
    86  		clients: clients,
    87  		ctx:     ctx,
    88  	}
    89  
    90  	// Establish members related to global DNS state
    91  	s.clientTags = make(map[string]bool)
    92  	for _, client := range clients {
    93  		s.clientTags[client.tag] = true
    94  	}
    95  	if err := establishDomainRules(s, config, nsClientMap); err != nil {
    96  		return nil, err
    97  	}
    98  	if err := establishExpectedIPs(s, config, nsClientMap); err != nil {
    99  		return nil, err
   100  	}
   101  	if err := establishFakeDNS(s, config, nsClientMap); err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	return s, nil
   106  }
   107  
   108  func establishDomainRules(s *DNS, config *Config, nsClientMap map[int]int) error {
   109  	domainRuleCount := 0
   110  	for _, ns := range config.NameServer {
   111  		domainRuleCount += len(ns.PrioritizedDomain)
   112  	}
   113  	// MatcherInfos is ensured to cover the maximum index domainMatcher could return, where matcher's index starts from 1
   114  	matcherInfos := make([]DomainMatcherInfo, domainRuleCount+1)
   115  	var domainMatcher strmatcher.IndexMatcher
   116  	switch config.DomainMatcher {
   117  	case "mph", "hybrid":
   118  		newError("using mph domain matcher").AtDebug().WriteToLog()
   119  		domainMatcher = strmatcher.NewMphIndexMatcher()
   120  	case "linear":
   121  		fallthrough
   122  	default:
   123  		newError("using default domain matcher").AtDebug().WriteToLog()
   124  		domainMatcher = strmatcher.NewLinearIndexMatcher()
   125  	}
   126  	for nsIdx, ns := range config.NameServer {
   127  		clientIdx := nsClientMap[nsIdx]
   128  		var rules []string
   129  		ruleCurr := 0
   130  		ruleIter := 0
   131  		for _, domain := range ns.PrioritizedDomain {
   132  			domainRule, err := toStrMatcher(domain.Type, domain.Domain)
   133  			if err != nil {
   134  				return newError("failed to create prioritized domain").Base(err).AtWarning()
   135  			}
   136  			originalRuleIdx := ruleCurr
   137  			if ruleCurr < len(ns.OriginalRules) {
   138  				rule := ns.OriginalRules[ruleCurr]
   139  				if ruleCurr >= len(rules) {
   140  					rules = append(rules, rule.Rule)
   141  				}
   142  				ruleIter++
   143  				if ruleIter >= int(rule.Size) {
   144  					ruleIter = 0
   145  					ruleCurr++
   146  				}
   147  			} else { // No original rule, generate one according to current domain matcher (majorly for compatibility with tests)
   148  				rules = append(rules, domainRule.String())
   149  				ruleCurr++
   150  			}
   151  			midx := domainMatcher.Add(domainRule)
   152  			matcherInfos[midx] = DomainMatcherInfo{
   153  				clientIdx:     uint16(clientIdx),
   154  				domainRuleIdx: uint16(originalRuleIdx),
   155  			}
   156  			if err != nil {
   157  				return newError("failed to create prioritized domain").Base(err).AtWarning()
   158  			}
   159  		}
   160  		s.clients[clientIdx].domains = rules
   161  	}
   162  	if err := domainMatcher.Build(); err != nil {
   163  		return err
   164  	}
   165  	s.domainMatcher = domainMatcher
   166  	s.matcherInfos = matcherInfos
   167  	return nil
   168  }
   169  
   170  func establishExpectedIPs(s *DNS, config *Config, nsClientMap map[int]int) error {
   171  	geoipContainer := router.GeoIPMatcherContainer{}
   172  	for nsIdx, ns := range config.NameServer {
   173  		clientIdx := nsClientMap[nsIdx]
   174  		var matchers []*router.GeoIPMatcher
   175  		for _, geoip := range ns.Geoip {
   176  			matcher, err := geoipContainer.Add(geoip)
   177  			if err != nil {
   178  				return newError("failed to create ip matcher").Base(err).AtWarning()
   179  			}
   180  			matchers = append(matchers, matcher)
   181  		}
   182  		s.clients[clientIdx].expectIPs = matchers
   183  	}
   184  	return nil
   185  }
   186  
   187  func establishFakeDNS(s *DNS, config *Config, nsClientMap map[int]int) error {
   188  	fakeHolders := &fakedns.HolderMulti{}
   189  	fakeDefault := (*fakedns.HolderMulti)(nil)
   190  	if config.FakeDns != nil {
   191  		defaultEngine, err := fakeHolders.AddPoolMulti(config.FakeDns)
   192  		if err != nil {
   193  			return newError("fail to create fake dns").Base(err).AtWarning()
   194  		}
   195  		fakeDefault = defaultEngine
   196  	}
   197  	for nsIdx, ns := range config.NameServer {
   198  		clientIdx := nsClientMap[nsIdx]
   199  		if ns.FakeDns == nil {
   200  			continue
   201  		}
   202  		engine, err := fakeHolders.AddPoolMulti(ns.FakeDns)
   203  		if err != nil {
   204  			return newError("fail to create fake dns").Base(err).AtWarning()
   205  		}
   206  		s.clients[clientIdx].fakeDNS = NewFakeDNSServer(engine)
   207  		s.clients[clientIdx].queryStrategy.FakeEnable = true
   208  	}
   209  	// Do not create FakeDNSEngine feature if no FakeDNS server is configured
   210  	if fakeHolders.IsEmpty() {
   211  		return nil
   212  	}
   213  	// Add FakeDNSEngine feature when DNS feature is added for the first time
   214  	s.fakeDNSEngine = &FakeDNSEngine{dns: s, fakeHolders: fakeHolders, fakeDefault: fakeDefault}
   215  	return core.RequireFeatures(s.ctx, func(client dns.Client) error {
   216  		v := core.MustFromContext(s.ctx)
   217  		if v.GetFeature(dns.FakeDNSEngineType()) != nil {
   218  			return nil
   219  		}
   220  		if client, ok := client.(dns.ClientWithFakeDNS); ok {
   221  			return v.AddFeature(client.AsFakeDNSEngine())
   222  		}
   223  		return nil
   224  	})
   225  }
   226  
   227  // Type implements common.HasType.
   228  func (*DNS) Type() interface{} {
   229  	return dns.ClientType()
   230  }
   231  
   232  // Start implements common.Runnable.
   233  func (s *DNS) Start() error {
   234  	return nil
   235  }
   236  
   237  // Close implements common.Closable.
   238  func (s *DNS) Close() error {
   239  	return nil
   240  }
   241  
   242  // IsOwnLink implements proxy.dns.ownLinkVerifier
   243  func (s *DNS) IsOwnLink(ctx context.Context) bool {
   244  	inbound := session.InboundFromContext(ctx)
   245  	return inbound != nil && s.clientTags[inbound.Tag]
   246  }
   247  
   248  // AsFakeDNSClient implements dns.ClientWithFakeDNS.
   249  func (s *DNS) AsFakeDNSClient() dns.Client {
   250  	return &FakeDNSClient{DNS: s}
   251  }
   252  
   253  // AsFakeDNSEngine implements dns.ClientWithFakeDNS.
   254  func (s *DNS) AsFakeDNSEngine() dns.FakeDNSEngine {
   255  	return s.fakeDNSEngine
   256  }
   257  
   258  // LookupIP implements dns.Client.
   259  func (s *DNS) LookupIP(domain string) ([]net.IP, error) {
   260  	return s.lookupIPInternal(domain, dns.IPOption{IPv4Enable: true, IPv6Enable: true, FakeEnable: false})
   261  }
   262  
   263  // LookupIPv4 implements dns.IPv4Lookup.
   264  func (s *DNS) LookupIPv4(domain string) ([]net.IP, error) {
   265  	return s.lookupIPInternal(domain, dns.IPOption{IPv4Enable: true, FakeEnable: false})
   266  }
   267  
   268  // LookupIPv6 implements dns.IPv6Lookup.
   269  func (s *DNS) LookupIPv6(domain string) ([]net.IP, error) {
   270  	return s.lookupIPInternal(domain, dns.IPOption{IPv6Enable: true, FakeEnable: false})
   271  }
   272  
   273  func (s *DNS) lookupIPInternal(domain string, option dns.IPOption) ([]net.IP, error) {
   274  	if domain == "" {
   275  		return nil, newError("empty domain name")
   276  	}
   277  
   278  	// Normalize the FQDN form query
   279  	domain = strings.TrimSuffix(domain, ".")
   280  
   281  	// Static host lookup
   282  	switch addrs := s.hosts.Lookup(domain, option); {
   283  	case addrs == nil: // Domain not recorded in static host
   284  		break
   285  	case len(addrs) == 0: // Domain recorded, but no valid IP returned (e.g. IPv4 address with only IPv6 enabled)
   286  		return nil, dns.ErrEmptyResponse
   287  	case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Domain replacement
   288  		newError("domain replaced: ", domain, " -> ", addrs[0].Domain()).WriteToLog()
   289  		domain = addrs[0].Domain()
   290  	default: // Successfully found ip records in static host
   291  		newError("returning ", len(addrs), " IP(s) for domain ", domain, " -> ", addrs).WriteToLog()
   292  		return toNetIP(addrs)
   293  	}
   294  
   295  	// Name servers lookup
   296  	errs := []error{}
   297  	for _, client := range s.sortClients(domain, option) {
   298  		ips, err := client.QueryIP(s.ctx, domain, option)
   299  		if len(ips) > 0 {
   300  			return ips, nil
   301  		}
   302  		if err != nil {
   303  			errs = append(errs, err)
   304  		}
   305  		if err != dns.ErrEmptyResponse { // ErrEmptyResponse is not seen as failure, so no failed log
   306  			newError("failed to lookup ip for domain ", domain, " at server ", client.Name()).Base(err).WriteToLog()
   307  		}
   308  		if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch {
   309  			return nil, err // Only continue lookup for certain errors
   310  		}
   311  	}
   312  
   313  	if len(errs) == 0 {
   314  		return nil, dns.ErrEmptyResponse
   315  	}
   316  	return nil, newError("returning nil for domain ", domain).Base(errors.Combine(errs...))
   317  }
   318  
   319  func (s *DNS) sortClients(domain string, option dns.IPOption) []*Client {
   320  	clients := make([]*Client, 0, len(s.clients))
   321  	clientUsed := make([]bool, len(s.clients))
   322  	clientIdxs := make([]int, 0, len(s.clients))
   323  	domainRules := []string{}
   324  
   325  	// Priority domain matching
   326  	for _, match := range s.domainMatcher.Match(domain) {
   327  		info := s.matcherInfos[match]
   328  		client := s.clients[info.clientIdx]
   329  		domainRule := client.domains[info.domainRuleIdx]
   330  		domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", domainRule, info.clientIdx))
   331  		switch {
   332  		case clientUsed[info.clientIdx]:
   333  			continue
   334  		case !option.FakeEnable && isFakeDNS(client.server):
   335  			continue
   336  		}
   337  		clientUsed[info.clientIdx] = true
   338  		clients = append(clients, client)
   339  		clientIdxs = append(clientIdxs, int(info.clientIdx))
   340  	}
   341  
   342  	// Default round-robin query
   343  	hasDomainMatch := len(clients) > 0
   344  	for idx, client := range s.clients {
   345  		switch {
   346  		case clientUsed[idx]:
   347  			continue
   348  		case !option.FakeEnable && isFakeDNS(client.server):
   349  			continue
   350  		case client.fallbackStrategy == FallbackStrategy_Disabled:
   351  			continue
   352  		case client.fallbackStrategy == FallbackStrategy_DisabledIfAnyMatch && hasDomainMatch:
   353  			continue
   354  		}
   355  		clientUsed[idx] = true
   356  		clients = append(clients, client)
   357  		clientIdxs = append(clientIdxs, idx)
   358  	}
   359  
   360  	if len(domainRules) > 0 {
   361  		newError("domain ", domain, " matches following rules: ", domainRules).AtDebug().WriteToLog()
   362  	}
   363  	if len(clientIdxs) > 0 {
   364  		newError("domain ", domain, " will use DNS in order: ", s.formatClientNames(clientIdxs, option), " ", toReqTypes(option)).AtDebug().WriteToLog()
   365  	}
   366  
   367  	return clients
   368  }
   369  
   370  func (s *DNS) formatClientNames(clientIdxs []int, option dns.IPOption) []string {
   371  	clientNames := make([]string, 0, len(clientIdxs))
   372  	counter := make(map[string]uint, len(clientIdxs))
   373  	for _, clientIdx := range clientIdxs {
   374  		client := s.clients[clientIdx]
   375  		var name string
   376  		if option.With(client.queryStrategy).FakeEnable {
   377  			name = fmt.Sprintf("%s(DNS idx:%d)", client.fakeDNS.Name(), clientIdx)
   378  		} else {
   379  			name = client.Name()
   380  		}
   381  		counter[name]++
   382  		clientNames = append(clientNames, name)
   383  	}
   384  	for idx, clientIdx := range clientIdxs {
   385  		name := clientNames[idx]
   386  		if counter[name] > 1 {
   387  			clientNames[idx] = fmt.Sprintf("%s(DNS idx:%d)", name, clientIdx)
   388  		}
   389  	}
   390  	return clientNames
   391  }
   392  
   393  var matcherTypeMap = map[routercommon.Domain_Type]DomainMatchingType{
   394  	routercommon.Domain_Plain:      DomainMatchingType_Keyword,
   395  	routercommon.Domain_Regex:      DomainMatchingType_Regex,
   396  	routercommon.Domain_RootDomain: DomainMatchingType_Subdomain,
   397  	routercommon.Domain_Full:       DomainMatchingType_Full,
   398  }
   399  
   400  func init() {
   401  	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
   402  		return New(ctx, config.(*Config))
   403  	}))
   404  
   405  	common.Must(common.RegisterConfig((*SimplifiedConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
   406  		ctx = cfgcommon.NewConfigureLoadingContext(ctx)
   407  
   408  		geoloadername := platform.NewEnvFlag("v2ray.conf.geoloader").GetValue(func() string {
   409  			return "standard"
   410  		})
   411  
   412  		if loader, err := geodata.GetGeoDataLoader(geoloadername); err == nil {
   413  			cfgcommon.SetGeoDataLoader(ctx, loader)
   414  		} else {
   415  			return nil, newError("unable to create geo data loader ").Base(err)
   416  		}
   417  
   418  		cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(ctx)
   419  		geoLoader := cfgEnv.GetGeoLoader()
   420  
   421  		simplifiedConfig := config.(*SimplifiedConfig)
   422  		for _, v := range simplifiedConfig.NameServer {
   423  			for _, geo := range v.Geoip {
   424  				if geo.Code != "" {
   425  					filepath := "geoip.dat"
   426  					if geo.FilePath != "" {
   427  						filepath = geo.FilePath
   428  					} else {
   429  						geo.CountryCode = geo.Code
   430  					}
   431  					var err error
   432  					geo.Cidr, err = geoLoader.LoadIP(filepath, geo.Code)
   433  					if err != nil {
   434  						return nil, newError("unable to load geoip").Base(err)
   435  					}
   436  				}
   437  			}
   438  			for _, geo := range v.GeoDomain {
   439  				if geo.Code != "" {
   440  					filepath := "geosite.dat"
   441  					if geo.FilePath != "" {
   442  						filepath = geo.FilePath
   443  					}
   444  					var err error
   445  					geo.Domain, err = geoLoader.LoadGeoSiteWithAttr(filepath, geo.Code)
   446  					if err != nil {
   447  						return nil, newError("unable to load geodomain").Base(err)
   448  					}
   449  				}
   450  				for _, domain := range geo.Domain {
   451  					v.PrioritizedDomain = append(v.PrioritizedDomain, &SimplifiedNameServer_PriorityDomain{
   452  						Type:   matcherTypeMap[domain.Type],
   453  						Domain: domain.Value,
   454  					})
   455  				}
   456  			}
   457  		}
   458  
   459  		var nameservers []*NameServer
   460  
   461  		for _, v := range simplifiedConfig.NameServer {
   462  			nameserver := &NameServer{
   463  				Address:          v.Address,
   464  				ClientIp:         net.ParseIP(v.ClientIp),
   465  				Tag:              v.Tag,
   466  				QueryStrategy:    v.QueryStrategy,
   467  				CacheStrategy:    v.CacheStrategy,
   468  				FallbackStrategy: v.FallbackStrategy,
   469  				SkipFallback:     v.SkipFallback,
   470  				Geoip:            v.Geoip,
   471  			}
   472  			for _, prioritizedDomain := range v.PrioritizedDomain {
   473  				nameserver.PrioritizedDomain = append(nameserver.PrioritizedDomain, &NameServer_PriorityDomain{
   474  					Type:   prioritizedDomain.Type,
   475  					Domain: prioritizedDomain.Domain,
   476  				})
   477  			}
   478  			nameservers = append(nameservers, nameserver)
   479  		}
   480  
   481  		var statichosts []*HostMapping
   482  
   483  		for _, v := range simplifiedConfig.StaticHosts {
   484  			statichost := &HostMapping{
   485  				Type:          v.Type,
   486  				Domain:        v.Domain,
   487  				ProxiedDomain: v.ProxiedDomain,
   488  			}
   489  			for _, ip := range v.Ip {
   490  				statichost.Ip = append(statichost.Ip, net.ParseIP(ip))
   491  			}
   492  			statichosts = append(statichosts, statichost)
   493  		}
   494  
   495  		fullConfig := &Config{
   496  			StaticHosts:      statichosts,
   497  			NameServer:       nameservers,
   498  			ClientIp:         net.ParseIP(simplifiedConfig.ClientIp),
   499  			Tag:              simplifiedConfig.Tag,
   500  			DomainMatcher:    simplifiedConfig.DomainMatcher,
   501  			QueryStrategy:    simplifiedConfig.QueryStrategy,
   502  			CacheStrategy:    simplifiedConfig.CacheStrategy,
   503  			FallbackStrategy: simplifiedConfig.FallbackStrategy,
   504  			// Deprecated flags
   505  			DisableCache:           simplifiedConfig.DisableCache,
   506  			DisableFallback:        simplifiedConfig.DisableFallback,
   507  			DisableFallbackIfMatch: simplifiedConfig.DisableFallbackIfMatch,
   508  		}
   509  		return common.CreateObject(ctx, fullConfig)
   510  	}))
   511  }