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

     1  package dns
     2  
     3  import (
     4  	"context"
     5  	"net/url"
     6  	"strings"
     7  	"time"
     8  
     9  	core "github.com/v2fly/v2ray-core/v5"
    10  	"github.com/v2fly/v2ray-core/v5/app/dns/fakedns"
    11  	"github.com/v2fly/v2ray-core/v5/app/router"
    12  	"github.com/v2fly/v2ray-core/v5/common/errors"
    13  	"github.com/v2fly/v2ray-core/v5/common/net"
    14  	"github.com/v2fly/v2ray-core/v5/common/session"
    15  	"github.com/v2fly/v2ray-core/v5/features"
    16  	"github.com/v2fly/v2ray-core/v5/features/dns"
    17  	"github.com/v2fly/v2ray-core/v5/features/routing"
    18  )
    19  
    20  // Server is the interface for Name Server.
    21  type Server interface {
    22  	// Name of the Client.
    23  	Name() string
    24  	// QueryIP sends IP queries to its configured server.
    25  	QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption, disableCache bool) ([]net.IP, error)
    26  }
    27  
    28  // Client is the interface for DNS client.
    29  type Client struct {
    30  	server   Server
    31  	clientIP net.IP
    32  	tag      string
    33  
    34  	queryStrategy    dns.IPOption
    35  	cacheStrategy    CacheStrategy
    36  	fallbackStrategy FallbackStrategy
    37  
    38  	domains   []string
    39  	expectIPs []*router.GeoIPMatcher
    40  	fakeDNS   Server
    41  }
    42  
    43  var errExpectedIPNonMatch = errors.New("expectIPs not match")
    44  
    45  // NewServer creates a name server object according to the network destination url.
    46  func NewServer(ctx context.Context, dest net.Destination, onCreated func(Server) error) error {
    47  	onCreatedWithError := func(server Server, err error) error {
    48  		if err != nil {
    49  			return err
    50  		}
    51  		return onCreated(server)
    52  	}
    53  	if address := dest.Address; address.Family().IsDomain() {
    54  		u, err := url.Parse(address.Domain())
    55  		if err != nil {
    56  			return err
    57  		}
    58  		switch {
    59  		case strings.EqualFold(u.String(), "localhost"):
    60  			return onCreated(NewLocalNameServer())
    61  		case strings.EqualFold(u.String(), "fakedns"):
    62  			return core.RequireFeatures(ctx, func(fakedns dns.FakeDNSEngine) error { return onCreated(NewFakeDNSServer(fakedns)) })
    63  		case strings.EqualFold(u.Scheme, "https"): // DOH Remote mode
    64  			return core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { return onCreatedWithError(NewDoHNameServer(u, dispatcher)) })
    65  		case strings.EqualFold(u.Scheme, "https+local"): // DOH Local mode
    66  			return onCreated(NewDoHLocalNameServer(u))
    67  		case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode
    68  			return core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { return onCreatedWithError(NewTCPNameServer(u, dispatcher)) })
    69  		case strings.EqualFold(u.Scheme, "tcp+local"): // DNS-over-TCP Local mode
    70  			return onCreatedWithError(NewTCPLocalNameServer(u))
    71  		case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
    72  			return onCreatedWithError(NewQUICNameServer(u))
    73  		}
    74  	}
    75  	if dest.Network == net.Network_Unknown {
    76  		dest.Network = net.Network_UDP
    77  	}
    78  	if dest.Network == net.Network_UDP { // UDP classic DNS mode
    79  		return core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { return onCreated(NewClassicNameServer(dest, dispatcher)) })
    80  	}
    81  	return newError("No available name server could be created from ", dest).AtWarning()
    82  }
    83  
    84  // NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs.
    85  func NewClient(ctx context.Context, ns *NameServer, dns *Config) (*Client, error) {
    86  	client := &Client{}
    87  
    88  	// Create DNS server instance
    89  	err := NewServer(ctx, ns.Address.AsDestination(), func(server Server) error {
    90  		client.server = server
    91  		return nil
    92  	})
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	// Initialize fields with default values
    98  	if len(ns.Tag) == 0 {
    99  		ns.Tag = dns.Tag
   100  		if len(ns.Tag) == 0 {
   101  			ns.Tag = generateRandomTag()
   102  		}
   103  	}
   104  	if len(ns.ClientIp) == 0 {
   105  		ns.ClientIp = dns.ClientIp
   106  	}
   107  	if ns.QueryStrategy == nil {
   108  		ns.QueryStrategy = &dns.QueryStrategy
   109  	}
   110  	if ns.CacheStrategy == nil {
   111  		ns.CacheStrategy = new(CacheStrategy)
   112  		switch {
   113  		case dns.CacheStrategy != CacheStrategy_CacheEnabled:
   114  			*ns.CacheStrategy = dns.CacheStrategy
   115  		case dns.DisableCache:
   116  			features.PrintDeprecatedFeatureWarning("DNS disableCache settings")
   117  			*ns.CacheStrategy = CacheStrategy_CacheDisabled
   118  		}
   119  	}
   120  	if ns.FallbackStrategy == nil {
   121  		ns.FallbackStrategy = new(FallbackStrategy)
   122  		switch {
   123  		case ns.SkipFallback:
   124  			features.PrintDeprecatedFeatureWarning("DNS server skipFallback settings")
   125  			*ns.FallbackStrategy = FallbackStrategy_Disabled
   126  		case dns.FallbackStrategy != FallbackStrategy_Enabled:
   127  			*ns.FallbackStrategy = dns.FallbackStrategy
   128  		case dns.DisableFallback:
   129  			features.PrintDeprecatedFeatureWarning("DNS disableFallback settings")
   130  			*ns.FallbackStrategy = FallbackStrategy_Disabled
   131  		case dns.DisableFallbackIfMatch:
   132  			features.PrintDeprecatedFeatureWarning("DNS disableFallbackIfMatch settings")
   133  			*ns.FallbackStrategy = FallbackStrategy_DisabledIfAnyMatch
   134  		}
   135  	}
   136  	if (ns.FakeDns != nil && len(ns.FakeDns.Pools) == 0) || // Use globally configured fake ip pool if: 1. `fakedns` config is set, but empty(represents { "fakedns": true } in JSON settings);
   137  		ns.FakeDns == nil && strings.EqualFold(ns.Address.Address.GetDomain(), "fakedns") { // 2. `fakedns` config not set, but server address is `fakedns`(represents { "address": "fakedns" } in JSON settings).
   138  		if dns.FakeDns != nil {
   139  			ns.FakeDns = dns.FakeDns
   140  		} else {
   141  			ns.FakeDns = &fakedns.FakeDnsPoolMulti{}
   142  			queryStrategy := toIPOption(*ns.QueryStrategy)
   143  			if queryStrategy.IPv4Enable {
   144  				ns.FakeDns.Pools = append(ns.FakeDns.Pools, &fakedns.FakeDnsPool{
   145  					IpPool:  "198.18.0.0/15",
   146  					LruSize: 65535,
   147  				})
   148  			}
   149  			if queryStrategy.IPv6Enable {
   150  				ns.FakeDns.Pools = append(ns.FakeDns.Pools, &fakedns.FakeDnsPool{
   151  					IpPool:  "fc00::/18",
   152  					LruSize: 65535,
   153  				})
   154  			}
   155  		}
   156  	}
   157  
   158  	// Priotize local domains with specific TLDs or without any dot to local DNS
   159  	if strings.EqualFold(ns.Address.Address.GetDomain(), "localhost") {
   160  		ns.PrioritizedDomain = append(ns.PrioritizedDomain, localTLDsAndDotlessDomains...)
   161  		ns.OriginalRules = append(ns.OriginalRules, localTLDsAndDotlessDomainsRule)
   162  	}
   163  
   164  	if len(ns.ClientIp) > 0 {
   165  		newError("DNS: client ", ns.Address.Address.AsAddress(), " uses clientIP ", net.IP(ns.ClientIp).String()).AtInfo().WriteToLog()
   166  	}
   167  
   168  	client.clientIP = ns.ClientIp
   169  	client.tag = ns.Tag
   170  	client.queryStrategy = toIPOption(*ns.QueryStrategy)
   171  	client.cacheStrategy = *ns.CacheStrategy
   172  	client.fallbackStrategy = *ns.FallbackStrategy
   173  	return client, nil
   174  }
   175  
   176  // Name returns the server name the client manages.
   177  func (c *Client) Name() string {
   178  	return c.server.Name()
   179  }
   180  
   181  // QueryIP send DNS query to the name server with the client's IP and IP options.
   182  func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, error) {
   183  	queryOption := option.With(c.queryStrategy)
   184  	if !queryOption.IsValid() {
   185  		newError(c.server.Name(), " returns empty answer: ", domain, ". ", toReqTypes(option)).AtInfo().WriteToLog()
   186  		return nil, dns.ErrEmptyResponse
   187  	}
   188  	server := c.server
   189  	if queryOption.FakeEnable && c.fakeDNS != nil {
   190  		server = c.fakeDNS
   191  	}
   192  	disableCache := c.cacheStrategy == CacheStrategy_CacheDisabled
   193  
   194  	ctx = session.ContextWithInbound(ctx, &session.Inbound{Tag: c.tag})
   195  	ctx, cancel := context.WithTimeout(ctx, 4*time.Second)
   196  	ips, err := server.QueryIP(ctx, domain, c.clientIP, queryOption, disableCache)
   197  	cancel()
   198  
   199  	if err != nil || queryOption.FakeEnable {
   200  		return ips, err
   201  	}
   202  	return c.MatchExpectedIPs(domain, ips)
   203  }
   204  
   205  // MatchExpectedIPs matches queried domain IPs with expected IPs and returns matched ones.
   206  func (c *Client) MatchExpectedIPs(domain string, ips []net.IP) ([]net.IP, error) {
   207  	if len(c.expectIPs) == 0 {
   208  		return ips, nil
   209  	}
   210  	newIps := []net.IP{}
   211  	for _, ip := range ips {
   212  		for _, matcher := range c.expectIPs {
   213  			if matcher.Match(ip) {
   214  				newIps = append(newIps, ip)
   215  				break
   216  			}
   217  		}
   218  	}
   219  	if len(newIps) == 0 {
   220  		return nil, errExpectedIPNonMatch
   221  	}
   222  	newError("domain ", domain, " expectIPs ", newIps, " matched at server ", c.Name()).AtDebug().WriteToLog()
   223  	return newIps, nil
   224  }