Xray-core/infra/conf/dns.go

254 lines
7.0 KiB
Go
Raw Normal View History

2020-11-25 13:01:53 +02:00
package conf
import (
"encoding/json"
"sort"
"strings"
2020-12-04 03:36:16 +02:00
"github.com/xtls/xray-core/app/dns"
2021-03-24 17:01:20 +02:00
dm "github.com/xtls/xray-core/common/matcher/domain"
2021-03-26 09:28:27 +02:00
"github.com/xtls/xray-core/common/matcher/domain/conf"
2021-03-24 17:01:20 +02:00
"github.com/xtls/xray-core/common/matcher/geosite"
2020-12-04 03:36:16 +02:00
"github.com/xtls/xray-core/common/net"
2020-11-25 13:01:53 +02:00
)
type NameServerConfig struct {
Address *Address
Port uint16
Domains []string
ExpectIPs StringList
}
func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
var address Address
if err := json.Unmarshal(data, &address); err == nil {
c.Address = &address
return nil
}
var advanced struct {
Address *Address `json:"address"`
Port uint16 `json:"port"`
Domains []string `json:"domains"`
ExpectIPs StringList `json:"expectIps"`
}
if err := json.Unmarshal(data, &advanced); err == nil {
c.Address = advanced.Address
c.Port = advanced.Port
c.Domains = advanced.Domains
c.ExpectIPs = advanced.ExpectIPs
return nil
}
return newError("failed to parse name server: ", string(data))
}
func (c *NameServerConfig) Build() (*dns.NameServer, error) {
if c.Address == nil {
return nil, newError("NameServer address is not specified.")
}
2021-03-24 17:01:20 +02:00
var domains []*dm.Domain
2020-11-25 13:01:53 +02:00
var originalRules []*dns.NameServer_OriginalRule
for _, rule := range c.Domains {
2021-03-24 17:01:20 +02:00
parsedDomain, err := conf.ParaseDomainRule(rule)
2020-11-25 13:01:53 +02:00
if err != nil {
return nil, newError("invalid domain rule: ", rule).Base(err)
}
for _, pd := range parsedDomain {
2021-03-24 17:01:20 +02:00
domains = append(domains, &dm.Domain{
Type: pd.Type,
Value: pd.Value,
2020-11-25 13:01:53 +02:00
})
}
originalRules = append(originalRules, &dns.NameServer_OriginalRule{
Rule: rule,
Size: uint32(len(parsedDomain)),
})
}
geoipList, err := toCidrList(c.ExpectIPs)
if err != nil {
2020-12-24 21:45:35 +02:00
return nil, newError("invalid IP rule: ", c.ExpectIPs).Base(err)
2020-11-25 13:01:53 +02:00
}
return &dns.NameServer{
Address: &net.Endpoint{
Network: net.Network_UDP,
Address: c.Address.Build(),
Port: uint32(c.Port),
},
PrioritizedDomain: domains,
Geoip: geoipList,
OriginalRules: originalRules,
}, nil
}
// DNSConfig is a JSON serializable object for dns.Config.
type DNSConfig struct {
2021-03-19 17:33:07 +02:00
Servers []*NameServerConfig `json:"servers"`
Hosts map[string]*Address `json:"hosts"`
ClientIP *Address `json:"clientIp"`
Tag string `json:"tag"`
QueryStrategy string `json:"queryStrategy"`
DisableCache bool `json:"disableCache"`
2020-11-25 13:01:53 +02:00
}
func getHostMapping(addr *Address) *dns.Config_HostMapping {
if addr.Family().IsIP() {
return &dns.Config_HostMapping{
Ip: [][]byte{[]byte(addr.IP())},
}
} else {
return &dns.Config_HostMapping{
ProxiedDomain: addr.Domain(),
}
}
}
// Build implements Buildable
func (c *DNSConfig) Build() (*dns.Config, error) {
config := &dns.Config{
2021-03-18 17:24:24 +02:00
Tag: c.Tag,
DisableCache: c.DisableCache,
2020-11-25 13:01:53 +02:00
}
if c.ClientIP != nil {
if !c.ClientIP.Family().IsIP() {
return nil, newError("not an IP address:", c.ClientIP.String())
}
config.ClientIp = []byte(c.ClientIP.IP())
}
2021-03-19 17:33:07 +02:00
config.QueryStrategy = dns.QueryStrategy_USE_IP
switch strings.ToLower(c.QueryStrategy) {
case "useip", "use_ip", "use-ip":
config.QueryStrategy = dns.QueryStrategy_USE_IP
case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4":
config.QueryStrategy = dns.QueryStrategy_USE_IP4
case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6":
config.QueryStrategy = dns.QueryStrategy_USE_IP6
}
2020-11-25 13:01:53 +02:00
for _, server := range c.Servers {
ns, err := server.Build()
if err != nil {
2020-12-24 21:45:35 +02:00
return nil, newError("failed to build nameserver").Base(err)
2020-11-25 13:01:53 +02:00
}
config.NameServer = append(config.NameServer, ns)
}
if c.Hosts != nil && len(c.Hosts) > 0 {
domains := make([]string, 0, len(c.Hosts))
for domain := range c.Hosts {
domains = append(domains, domain)
}
sort.Strings(domains)
for _, domain := range domains {
addr := c.Hosts[domain]
var mappings []*dns.Config_HostMapping
switch {
case strings.HasPrefix(domain, "domain:"):
2020-12-24 21:45:35 +02:00
domainName := domain[7:]
if len(domainName) == 0 {
return nil, newError("empty domain type of rule: ", domain)
}
2020-11-25 13:01:53 +02:00
mapping := getHostMapping(addr)
2021-03-24 17:01:20 +02:00
mapping.Type = dm.MatchingType_Subdomain
2020-12-24 21:45:35 +02:00
mapping.Domain = domainName
2020-11-25 13:01:53 +02:00
mappings = append(mappings, mapping)
case strings.HasPrefix(domain, "geosite:"):
2020-12-24 21:45:35 +02:00
listName := domain[8:]
if len(listName) == 0 {
return nil, newError("empty geosite rule: ", domain)
}
2021-03-24 17:01:20 +02:00
domains, err := geosite.LoadGeositeWithAttr("geosite.dat", listName)
2020-11-25 13:01:53 +02:00
if err != nil {
2020-12-24 21:45:35 +02:00
return nil, newError("failed to load geosite: ", listName).Base(err)
2020-11-25 13:01:53 +02:00
}
for _, d := range domains {
mapping := getHostMapping(addr)
2021-03-24 17:01:20 +02:00
mapping.Type = d.Type
2020-11-25 13:01:53 +02:00
mapping.Domain = d.Value
mappings = append(mappings, mapping)
}
case strings.HasPrefix(domain, "regexp:"):
2020-12-24 21:45:35 +02:00
regexpVal := domain[7:]
if len(regexpVal) == 0 {
return nil, newError("empty regexp type of rule: ", domain)
}
2020-11-25 13:01:53 +02:00
mapping := getHostMapping(addr)
2021-03-24 17:01:20 +02:00
mapping.Type = dm.MatchingType_Regex
2020-12-24 21:45:35 +02:00
mapping.Domain = regexpVal
2020-11-25 13:01:53 +02:00
mappings = append(mappings, mapping)
case strings.HasPrefix(domain, "keyword:"):
2020-12-24 21:45:35 +02:00
keywordVal := domain[8:]
if len(keywordVal) == 0 {
return nil, newError("empty keyword type of rule: ", domain)
}
2020-11-25 13:01:53 +02:00
mapping := getHostMapping(addr)
2021-03-24 17:01:20 +02:00
mapping.Type = dm.MatchingType_Keyword
2020-12-24 21:45:35 +02:00
mapping.Domain = keywordVal
2020-11-25 13:01:53 +02:00
mappings = append(mappings, mapping)
case strings.HasPrefix(domain, "full:"):
2020-12-24 21:45:35 +02:00
fullVal := domain[5:]
if len(fullVal) == 0 {
return nil, newError("empty full domain type of rule: ", domain)
}
2020-11-25 13:01:53 +02:00
mapping := getHostMapping(addr)
2021-03-24 17:01:20 +02:00
mapping.Type = dm.MatchingType_Full
2020-12-24 21:45:35 +02:00
mapping.Domain = fullVal
2020-11-25 13:01:53 +02:00
mappings = append(mappings, mapping)
case strings.HasPrefix(domain, "dotless:"):
mapping := getHostMapping(addr)
2021-03-24 17:01:20 +02:00
mapping.Type = dm.MatchingType_Regex
2020-11-25 13:01:53 +02:00
switch substr := domain[8:]; {
case substr == "":
mapping.Domain = "^[^.]*$"
case !strings.Contains(substr, "."):
mapping.Domain = "^[^.]*" + substr + "[^.]*$"
default:
return nil, newError("substr in dotless rule should not contain a dot: ", substr)
}
mappings = append(mappings, mapping)
case strings.HasPrefix(domain, "ext:"):
kv := strings.Split(domain[4:], ":")
if len(kv) != 2 {
return nil, newError("invalid external resource: ", domain)
}
filename := kv[0]
2020-12-24 21:45:35 +02:00
list := kv[1]
2021-03-24 17:01:20 +02:00
domains, err := geosite.LoadGeositeWithAttr(filename, list)
2020-11-25 13:01:53 +02:00
if err != nil {
2020-12-24 21:45:35 +02:00
return nil, newError("failed to load domain list: ", list, " from ", filename).Base(err)
2020-11-25 13:01:53 +02:00
}
for _, d := range domains {
mapping := getHostMapping(addr)
2021-03-24 17:01:20 +02:00
mapping.Type = d.Type
2020-11-25 13:01:53 +02:00
mapping.Domain = d.Value
mappings = append(mappings, mapping)
}
default:
mapping := getHostMapping(addr)
2021-03-24 17:01:20 +02:00
mapping.Type = dm.MatchingType_Full
2020-11-25 13:01:53 +02:00
mapping.Domain = domain
mappings = append(mappings, mapping)
}
config.StaticHosts = append(config.StaticHosts, mappings...)
}
}
return config, nil
}