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

     1  package conf
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/v2fly/v2ray-core/v4/app/dns"
    10  	"github.com/v2fly/v2ray-core/v4/app/router"
    11  	"github.com/v2fly/v2ray-core/v4/common/net"
    12  	"github.com/v2fly/v2ray-core/v4/common/platform"
    13  	"github.com/v2fly/v2ray-core/v4/infra/conf/cfgcommon"
    14  	"github.com/v2fly/v2ray-core/v4/infra/conf/geodata"
    15  	rule2 "github.com/v2fly/v2ray-core/v4/infra/conf/rule"
    16  )
    17  
    18  type NameServerConfig struct {
    19  	Address      *cfgcommon.Address
    20  	ClientIP     *cfgcommon.Address
    21  	Port         uint16
    22  	SkipFallback bool
    23  	Domains      []string
    24  	ExpectIPs    cfgcommon.StringList
    25  
    26  	cfgctx context.Context
    27  }
    28  
    29  func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
    30  	var address cfgcommon.Address
    31  	if err := json.Unmarshal(data, &address); err == nil {
    32  		c.Address = &address
    33  		return nil
    34  	}
    35  
    36  	var advanced struct {
    37  		Address      *cfgcommon.Address   `json:"address"`
    38  		ClientIP     *cfgcommon.Address   `json:"clientIp"`
    39  		Port         uint16               `json:"port"`
    40  		SkipFallback bool                 `json:"skipFallback"`
    41  		Domains      []string             `json:"domains"`
    42  		ExpectIPs    cfgcommon.StringList `json:"expectIps"`
    43  	}
    44  	if err := json.Unmarshal(data, &advanced); err == nil {
    45  		c.Address = advanced.Address
    46  		c.ClientIP = advanced.ClientIP
    47  		c.Port = advanced.Port
    48  		c.SkipFallback = advanced.SkipFallback
    49  		c.Domains = advanced.Domains
    50  		c.ExpectIPs = advanced.ExpectIPs
    51  		return nil
    52  	}
    53  
    54  	return newError("failed to parse name server: ", string(data))
    55  }
    56  
    57  func toDomainMatchingType(t router.Domain_Type) dns.DomainMatchingType {
    58  	switch t {
    59  	case router.Domain_Domain:
    60  		return dns.DomainMatchingType_Subdomain
    61  	case router.Domain_Full:
    62  		return dns.DomainMatchingType_Full
    63  	case router.Domain_Plain:
    64  		return dns.DomainMatchingType_Keyword
    65  	case router.Domain_Regex:
    66  		return dns.DomainMatchingType_Regex
    67  	default:
    68  		panic("unknown domain type")
    69  	}
    70  }
    71  
    72  func (c *NameServerConfig) Build() (*dns.NameServer, error) {
    73  	cfgctx := c.cfgctx
    74  
    75  	if c.Address == nil {
    76  		return nil, newError("NameServer address is not specified.")
    77  	}
    78  
    79  	var domains []*dns.NameServer_PriorityDomain
    80  	var originalRules []*dns.NameServer_OriginalRule
    81  
    82  	for _, rule := range c.Domains {
    83  		parsedDomain, err := rule2.ParseDomainRule(cfgctx, rule)
    84  		if err != nil {
    85  			return nil, newError("invalid domain rule: ", rule).Base(err)
    86  		}
    87  
    88  		for _, pd := range parsedDomain {
    89  			domains = append(domains, &dns.NameServer_PriorityDomain{
    90  				Type:   toDomainMatchingType(pd.Type),
    91  				Domain: pd.Value,
    92  			})
    93  		}
    94  		originalRules = append(originalRules, &dns.NameServer_OriginalRule{
    95  			Rule: rule,
    96  			Size: uint32(len(parsedDomain)),
    97  		})
    98  	}
    99  
   100  	geoipList, err := rule2.ToCidrList(cfgctx, c.ExpectIPs)
   101  	if err != nil {
   102  		return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err)
   103  	}
   104  
   105  	var myClientIP []byte
   106  	if c.ClientIP != nil {
   107  		if !c.ClientIP.Family().IsIP() {
   108  			return nil, newError("not an IP address:", c.ClientIP.String())
   109  		}
   110  		myClientIP = []byte(c.ClientIP.IP())
   111  	}
   112  
   113  	return &dns.NameServer{
   114  		Address: &net.Endpoint{
   115  			Network: net.Network_UDP,
   116  			Address: c.Address.Build(),
   117  			Port:    uint32(c.Port),
   118  		},
   119  		ClientIp:          myClientIP,
   120  		SkipFallback:      c.SkipFallback,
   121  		PrioritizedDomain: domains,
   122  		Geoip:             geoipList,
   123  		OriginalRules:     originalRules,
   124  	}, nil
   125  }
   126  
   127  var typeMap = map[router.Domain_Type]dns.DomainMatchingType{
   128  	router.Domain_Full:   dns.DomainMatchingType_Full,
   129  	router.Domain_Domain: dns.DomainMatchingType_Subdomain,
   130  	router.Domain_Plain:  dns.DomainMatchingType_Keyword,
   131  	router.Domain_Regex:  dns.DomainMatchingType_Regex,
   132  }
   133  
   134  // DNSConfig is a JSON serializable object for dns.Config.
   135  type DNSConfig struct {
   136  	Servers                []*NameServerConfig     `json:"servers"`
   137  	Hosts                  map[string]*HostAddress `json:"hosts"`
   138  	ClientIP               *cfgcommon.Address      `json:"clientIp"`
   139  	Tag                    string                  `json:"tag"`
   140  	QueryStrategy          string                  `json:"queryStrategy"`
   141  	DisableCache           bool                    `json:"disableCache"`
   142  	DisableFallback        bool                    `json:"disableFallback"`
   143  	DisableFallbackIfMatch bool                    `json:"disableFallbackIfMatch"`
   144  }
   145  
   146  type HostAddress struct {
   147  	addr  *cfgcommon.Address
   148  	addrs []*cfgcommon.Address
   149  }
   150  
   151  // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON
   152  func (h *HostAddress) UnmarshalJSON(data []byte) error {
   153  	addr := new(cfgcommon.Address)
   154  	var addrs []*cfgcommon.Address
   155  	switch {
   156  	case json.Unmarshal(data, &addr) == nil:
   157  		h.addr = addr
   158  	case json.Unmarshal(data, &addrs) == nil:
   159  		h.addrs = addrs
   160  	default:
   161  		return newError("invalid address")
   162  	}
   163  	return nil
   164  }
   165  
   166  func getHostMapping(ha *HostAddress) *dns.Config_HostMapping {
   167  	if ha.addr != nil {
   168  		if ha.addr.Family().IsDomain() {
   169  			return &dns.Config_HostMapping{
   170  				ProxiedDomain: ha.addr.Domain(),
   171  			}
   172  		}
   173  		return &dns.Config_HostMapping{
   174  			Ip: [][]byte{ha.addr.IP()},
   175  		}
   176  	}
   177  
   178  	ips := make([][]byte, 0, len(ha.addrs))
   179  	for _, addr := range ha.addrs {
   180  		if addr.Family().IsDomain() {
   181  			return &dns.Config_HostMapping{
   182  				ProxiedDomain: addr.Domain(),
   183  			}
   184  		}
   185  		ips = append(ips, []byte(addr.IP()))
   186  	}
   187  	return &dns.Config_HostMapping{
   188  		Ip: ips,
   189  	}
   190  }
   191  
   192  // Build implements Buildable
   193  func (c *DNSConfig) Build() (*dns.Config, error) {
   194  	cfgctx := cfgcommon.NewConfigureLoadingContext(context.Background())
   195  
   196  	geoloadername := platform.NewEnvFlag("v2ray.conf.geoloader").GetValue(func() string {
   197  		return "standard"
   198  	})
   199  
   200  	if loader, err := geodata.GetGeoDataLoader(geoloadername); err == nil {
   201  		cfgcommon.SetGeoDataLoader(cfgctx, loader)
   202  	} else {
   203  		return nil, newError("unable to create geo data loader ").Base(err)
   204  	}
   205  
   206  	cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(cfgctx)
   207  	geoLoader := cfgEnv.GetGeoLoader()
   208  
   209  	config := &dns.Config{
   210  		Tag:                    c.Tag,
   211  		DisableCache:           c.DisableCache,
   212  		DisableFallback:        c.DisableFallback,
   213  		DisableFallbackIfMatch: c.DisableFallbackIfMatch,
   214  	}
   215  
   216  	if c.ClientIP != nil {
   217  		if !c.ClientIP.Family().IsIP() {
   218  			return nil, newError("not an IP address:", c.ClientIP.String())
   219  		}
   220  		config.ClientIp = []byte(c.ClientIP.IP())
   221  	}
   222  
   223  	config.QueryStrategy = dns.QueryStrategy_USE_IP
   224  	switch strings.ToLower(c.QueryStrategy) {
   225  	case "useip", "use_ip", "use-ip":
   226  		config.QueryStrategy = dns.QueryStrategy_USE_IP
   227  	case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4":
   228  		config.QueryStrategy = dns.QueryStrategy_USE_IP4
   229  	case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6":
   230  		config.QueryStrategy = dns.QueryStrategy_USE_IP6
   231  	}
   232  
   233  	for _, server := range c.Servers {
   234  		server.cfgctx = cfgctx
   235  		ns, err := server.Build()
   236  		if err != nil {
   237  			return nil, newError("failed to build nameserver").Base(err)
   238  		}
   239  		config.NameServer = append(config.NameServer, ns)
   240  	}
   241  
   242  	if c.Hosts != nil {
   243  		mappings := make([]*dns.Config_HostMapping, 0, 20)
   244  
   245  		domains := make([]string, 0, len(c.Hosts))
   246  		for domain := range c.Hosts {
   247  			domains = append(domains, domain)
   248  		}
   249  		sort.Strings(domains)
   250  
   251  		for _, domain := range domains {
   252  			switch {
   253  			case strings.HasPrefix(domain, "domain:"):
   254  				domainName := domain[7:]
   255  				if len(domainName) == 0 {
   256  					return nil, newError("empty domain type of rule: ", domain)
   257  				}
   258  				mapping := getHostMapping(c.Hosts[domain])
   259  				mapping.Type = dns.DomainMatchingType_Subdomain
   260  				mapping.Domain = domainName
   261  				mappings = append(mappings, mapping)
   262  
   263  			case strings.HasPrefix(domain, "geosite:"):
   264  				listName := domain[8:]
   265  				if len(listName) == 0 {
   266  					return nil, newError("empty geosite rule: ", domain)
   267  				}
   268  				geositeList, err := geoLoader.LoadGeoSite(listName)
   269  				if err != nil {
   270  					return nil, newError("failed to load geosite: ", listName).Base(err)
   271  				}
   272  				for _, d := range geositeList {
   273  					mapping := getHostMapping(c.Hosts[domain])
   274  					mapping.Type = typeMap[d.Type]
   275  					mapping.Domain = d.Value
   276  					mappings = append(mappings, mapping)
   277  				}
   278  
   279  			case strings.HasPrefix(domain, "regexp:"):
   280  				regexpVal := domain[7:]
   281  				if len(regexpVal) == 0 {
   282  					return nil, newError("empty regexp type of rule: ", domain)
   283  				}
   284  				mapping := getHostMapping(c.Hosts[domain])
   285  				mapping.Type = dns.DomainMatchingType_Regex
   286  				mapping.Domain = regexpVal
   287  				mappings = append(mappings, mapping)
   288  
   289  			case strings.HasPrefix(domain, "keyword:"):
   290  				keywordVal := domain[8:]
   291  				if len(keywordVal) == 0 {
   292  					return nil, newError("empty keyword type of rule: ", domain)
   293  				}
   294  				mapping := getHostMapping(c.Hosts[domain])
   295  				mapping.Type = dns.DomainMatchingType_Keyword
   296  				mapping.Domain = keywordVal
   297  				mappings = append(mappings, mapping)
   298  
   299  			case strings.HasPrefix(domain, "full:"):
   300  				fullVal := domain[5:]
   301  				if len(fullVal) == 0 {
   302  					return nil, newError("empty full domain type of rule: ", domain)
   303  				}
   304  				mapping := getHostMapping(c.Hosts[domain])
   305  				mapping.Type = dns.DomainMatchingType_Full
   306  				mapping.Domain = fullVal
   307  				mappings = append(mappings, mapping)
   308  
   309  			case strings.HasPrefix(domain, "dotless:"):
   310  				mapping := getHostMapping(c.Hosts[domain])
   311  				mapping.Type = dns.DomainMatchingType_Regex
   312  				switch substr := domain[8:]; {
   313  				case substr == "":
   314  					mapping.Domain = "^[^.]*$"
   315  				case !strings.Contains(substr, "."):
   316  					mapping.Domain = "^[^.]*" + substr + "[^.]*$"
   317  				default:
   318  					return nil, newError("substr in dotless rule should not contain a dot: ", substr)
   319  				}
   320  				mappings = append(mappings, mapping)
   321  
   322  			case strings.HasPrefix(domain, "ext:"):
   323  				kv := strings.Split(domain[4:], ":")
   324  				if len(kv) != 2 {
   325  					return nil, newError("invalid external resource: ", domain)
   326  				}
   327  				filename := kv[0]
   328  				list := kv[1]
   329  				geositeList, err := geoLoader.LoadGeoSiteWithAttr(filename, list)
   330  				if err != nil {
   331  					return nil, newError("failed to load domain list: ", list, " from ", filename).Base(err)
   332  				}
   333  				for _, d := range geositeList {
   334  					mapping := getHostMapping(c.Hosts[domain])
   335  					mapping.Type = typeMap[d.Type]
   336  					mapping.Domain = d.Value
   337  					mappings = append(mappings, mapping)
   338  				}
   339  
   340  			default:
   341  				mapping := getHostMapping(c.Hosts[domain])
   342  				mapping.Type = dns.DomainMatchingType_Full
   343  				mapping.Domain = domain
   344  				mappings = append(mappings, mapping)
   345  			}
   346  		}
   347  
   348  		config.StaticHosts = append(config.StaticHosts, mappings...)
   349  	}
   350  
   351  	return config, nil
   352  }