浏览代码

Merge branch 'dns' into dns-geo

秋のかえで 4 年之前
父节点
当前提交
f8b8e8a53a
共有 7 个文件被更改,包括 110 次插入35 次删除
  1. 1 2
      app/dns/config.pb.go
  2. 1 2
      app/dns/config.proto
  3. 4 4
      app/dns/dns.go
  4. 4 8
      app/dns/hosts.go
  5. 41 0
      app/dns/hosts_test.go
  6. 48 14
      infra/conf/dns.go
  7. 11 5
      infra/conf/dns_test.go

+ 1 - 2
app/dns/config.pb.go

@@ -404,8 +404,7 @@ type Config_HostMapping struct {
 	Domain string              `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
 	Ip     [][]byte            `protobuf:"bytes,3,rep,name=ip,proto3" json:"ip,omitempty"`
 	// ProxiedDomain indicates the mapped domain has the same IP address on this
-	// domain. Xray will use this domain for IP queries. This field is only
-	// effective if ip is empty.
+	// domain. Xray will use this domain for IP queries.
 	ProxiedDomain string `protobuf:"bytes,4,opt,name=proxied_domain,json=proxiedDomain,proto3" json:"proxied_domain,omitempty"`
 }
 

+ 1 - 2
app/dns/config.proto

@@ -62,8 +62,7 @@ message Config {
     repeated bytes ip = 3;
 
     // ProxiedDomain indicates the mapped domain has the same IP address on this
-    // domain. Xray will use this domain for IP queries. This field is only
-    // effective if ip is empty.
+    // domain. Xray will use this domain for IP queries.
     string proxied_domain = 4;
   }
 

+ 4 - 4
app/dns/dns.go

@@ -23,7 +23,7 @@ import (
 type DNS struct {
 	sync.Mutex
 	tag             string
-	cs              CacheStrategy
+	cacheStrategy   CacheStrategy
 	disableFallback bool
 	ipOption        *dns.IPOption
 	hosts           *StaticHosts
@@ -139,7 +139,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
 		ctx:             ctx,
 		domainMatcher:   domainMatcher,
 		matcherInfos:    matcherInfos,
-		cs:              config.CacheStrategy,
+		cacheStrategy:   config.CacheStrategy,
 		disableFallback: config.DisableFallback,
 	}, nil
 }
@@ -222,7 +222,7 @@ func (s *DNS) lookupIPInternal(domain string, option *dns.IPOption) ([]net.IP, e
 		// Successfully found ip records in static host.
 		// Skip hosts mapping result in FakeDNS query.
 		if isIPQuery(option) {
-			newError("returning ", len(addrs), " IPs for domain ", domain).WriteToLog()
+			newError("returning ", len(addrs), " IP(s) for domain ", domain, " -> ", addrs).WriteToLog()
 			return toNetIP(addrs)
 		}
 	}
@@ -231,7 +231,7 @@ func (s *DNS) lookupIPInternal(domain string, option *dns.IPOption) ([]net.IP, e
 	errs := []error{}
 	ctx := session.ContextWithInbound(s.ctx, &session.Inbound{Tag: s.tag})
 	for _, client := range s.sortClients(domain, option) {
-		ips, err := client.QueryIP(ctx, domain, *option, s.cs)
+		ips, err := client.QueryIP(ctx, domain, *option, s.cacheStrategy)
 		if len(ips) > 0 {
 			return ips, nil
 		}

+ 4 - 8
app/dns/hosts.go

@@ -47,6 +47,9 @@ func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDoma
 		id := g.Add(matcher)
 		ips := make([]net.Address, 0, len(mapping.Ip)+1)
 		switch {
+		case len(mapping.ProxiedDomain) > 0:
+			ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
+
 		case len(mapping.Ip) > 0:
 			for _, ip := range mapping.Ip {
 				addr := net.IPAddress(ip)
@@ -56,18 +59,10 @@ func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDoma
 				ips = append(ips, addr)
 			}
 
-		case len(mapping.ProxiedDomain) > 0:
-			ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
-
 		default:
 			return nil, newError("neither IP address nor proxied domain specified for domain: ", mapping.Domain).AtWarning()
 		}
 
-		// Special handling for localhost IPv6. This is a dirty workaround as JSON config supports only single IP mapping.
-		if len(ips) == 1 && ips[0] == net.LocalHostIP {
-			ips = append(ips, net.LocalHostIPv6)
-		}
-
 		sh.ips[id] = ips
 	}
 
@@ -100,6 +95,7 @@ func (h *StaticHosts) lookup(domain string, option *dns.IPOption, maxDepth int)
 	case len(addrs) == 0: // Not recorded in static hosts, return nil
 		return nil
 	case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain
+		newError("found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it").AtDebug().WriteToLog()
 		if maxDepth > 0 {
 			unwrapped := h.lookup(addrs[0].Domain(), option, maxDepth-1)
 			if unwrapped != nil {

+ 41 - 0
app/dns/hosts_test.go

@@ -21,6 +21,20 @@ func TestStaticHosts(t *testing.T) {
 				{1, 1, 1, 1},
 			},
 		},
+		{
+			Type:   domain.MatchingType_Full,
+			Domain: "proxy.example.com",
+			Ip: [][]byte{
+				{1, 2, 3, 4},
+				{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
+			},
+			ProxiedDomain: "another-proxy.example.com",
+		},
+		{
+			Type:          domain.MatchingType_Full,
+			Domain:        "proxy2.example.com",
+			ProxiedDomain: "proxy.example.com",
+		},
 		{
 			Type:   domain.MatchingType_Subdomain,
 			Domain: "example.cn",
@@ -33,6 +47,7 @@ func TestStaticHosts(t *testing.T) {
 			Domain: "baidu.com",
 			Ip: [][]byte{
 				{127, 0, 0, 1},
+				{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
 			},
 		},
 	}
@@ -53,6 +68,32 @@ func TestStaticHosts(t *testing.T) {
 		}
 	}
 
+	{
+		domain := hosts.Lookup("proxy.example.com", &dns.IPOption{
+			IPv4Enable: true,
+			IPv6Enable: false,
+		})
+		if len(domain) != 1 {
+			t.Error("expect 1 domain, but got ", len(domain))
+		}
+		if diff := cmp.Diff(domain[0].Domain(), "another-proxy.example.com"); diff != "" {
+			t.Error(diff)
+		}
+	}
+
+	{
+		domain := hosts.Lookup("proxy2.example.com", &dns.IPOption{
+			IPv4Enable: true,
+			IPv6Enable: false,
+		})
+		if len(domain) != 1 {
+			t.Error("expect 1 domain, but got ", len(domain))
+		}
+		if diff := cmp.Diff(domain[0].Domain(), "another-proxy.example.com"); diff != "" {
+			t.Error(diff)
+		}
+	}
+
 	{
 		ips := hosts.Lookup("www.example.cn", &dns.IPOption{
 			IPv4Enable: true,

+ 48 - 14
infra/conf/dns.go

@@ -105,25 +105,59 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
 
 // DNSConfig is a JSON serializable object for dns.Config.
 type DNSConfig struct {
-	Servers         []*NameServerConfig `json:"servers"`
-	Hosts           map[string]*Address `json:"hosts"`
-	ClientIP        *Address            `json:"clientIp"`
-	Tag             string              `json:"tag"`
-	QueryStrategy   string              `json:"queryStrategy"`
-	CacheStrategy   string              `json:"cacheStrategy"`
-	DisableCache    bool                `json:"disableCache"`
-	DisableFallback bool                `json:"disableFallback"`
+	Servers         []*NameServerConfig     `json:"servers"`
+	Hosts           map[string]*HostAddress `json:"hosts"`
+	ClientIP        *Address                `json:"clientIp"`
+	Tag             string                  `json:"tag"`
+	QueryStrategy   string                  `json:"queryStrategy"`
+	CacheStrategy   string                  `json:"cacheStrategy"`
+	DisableCache    bool                    `json:"disableCache"`
+	DisableFallback bool                    `json:"disableFallback"`
 }
 
-func getHostMapping(addr *Address) *dns.Config_HostMapping {
-	if addr.Family().IsIP() {
-		return &dns.Config_HostMapping{
-			Ip: [][]byte{[]byte(addr.IP())},
+type HostAddress struct {
+	addr  *Address
+	addrs []*Address
+}
+
+// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON
+func (h *HostAddress) UnmarshalJSON(data []byte) error {
+	addr := new(Address)
+	var addrs []*Address
+	switch {
+	case json.Unmarshal(data, &addr) == nil:
+		h.addr = addr
+	case json.Unmarshal(data, &addrs) == nil:
+		h.addrs = addrs
+	default:
+		return newError("invalid address")
+	}
+	return nil
+}
+
+func getHostMapping(ha *HostAddress) *dns.Config_HostMapping {
+	if ha.addr != nil {
+		if ha.addr.Family().IsDomain() {
+			return &dns.Config_HostMapping{
+				ProxiedDomain: ha.addr.Domain(),
+			}
 		}
-	} else {
 		return &dns.Config_HostMapping{
-			ProxiedDomain: addr.Domain(),
+			Ip: [][]byte{ha.addr.IP()},
+		}
+	}
+
+	ips := make([][]byte, 0, len(ha.addrs))
+	for _, addr := range ha.addrs {
+		if addr.Family().IsDomain() {
+			return &dns.Config_HostMapping{
+				ProxiedDomain: addr.Domain(),
+			}
 		}
+		ips = append(ips, []byte(addr.IP()))
+	}
+	return &dns.Config_HostMapping{
+		Ip: ips,
 	}
 }
 

+ 11 - 5
infra/conf/dns_test.go

@@ -75,14 +75,15 @@ func TestDNSConfigParsing(t *testing.T) {
 				}],
 				"hosts": {
 					"example.com": "127.0.0.1",
+					"xtls.github.io": ["1.2.3.4", "5.6.7.8"],
 					"domain:example.com": "google.com",
-					"geosite:test": "10.0.0.1",
-					"keyword:google": "8.8.8.8",
+					"geosite:test": ["127.0.0.1", "127.0.0.2"],
+					"keyword:google": ["8.8.8.8", "8.8.4.4"],
 					"regexp:.*\\.com": "8.8.4.4"
 				},
 				"clientIp": "10.0.0.1",
 				"queryStrategy": "UseIPv4",
-				"disableCache": true,
+				"cacheStrategy": "disable",
 				"disableFallback": true
 			}`,
 			Parser: parserCreator(),
@@ -128,18 +129,23 @@ func TestDNSConfigParsing(t *testing.T) {
 					{
 						Type:   domain.MatchingType_Full,
 						Domain: "example.com",
-						Ip:     [][]byte{{10, 0, 0, 1}},
+						Ip:     [][]byte{{127, 0, 0, 1}, {127, 0, 0, 2}},
 					},
 					{
 						Type:   domain.MatchingType_Keyword,
 						Domain: "google",
-						Ip:     [][]byte{{8, 8, 8, 8}},
+						Ip:     [][]byte{{8, 8, 8, 8}, {8, 8, 4, 4}},
 					},
 					{
 						Type:   domain.MatchingType_Regex,
 						Domain: ".*\\.com",
 						Ip:     [][]byte{{8, 8, 4, 4}},
 					},
+					{
+						Type:   domain.MatchingType_Full,
+						Domain: "xtls.github.io",
+						Ip:     [][]byte{{1, 2, 3, 4}, {5, 6, 7, 8}},
+					},
 				},
 				ClientIp:        []byte{10, 0, 0, 1},
 				QueryStrategy:   dns.QueryStrategy_USE_IP4,