github.com/moqsien/xraycore@v1.8.5/infra/conf/dns.go (about)

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