From f4fd8b8fadaf2814d267ca633accf25b2573cc4a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=A3=8E=E6=89=87=E6=BB=91=E7=BF=94=E7=BF=BC?=
 <Fangliding.fshxy@outlook.com>
Date: Sun, 19 Jan 2025 15:39:54 +0800
Subject: [PATCH] DNS: Implement queryStrategy for "localhost" (#4303)

Fixes https://github.com/XTLS/Xray-core/issues/4302
---
 app/dns/nameserver.go            |  2 +-
 app/dns/nameserver_local.go      | 15 +++++++++++----
 app/dns/nameserver_local_test.go |  2 +-
 3 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go
index 9c2668d9..b145f9eb 100644
--- a/app/dns/nameserver.go
+++ b/app/dns/nameserver.go
@@ -43,7 +43,7 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
 		}
 		switch {
 		case strings.EqualFold(u.String(), "localhost"):
-			return NewLocalNameServer(), nil
+			return NewLocalNameServer(queryStrategy), nil
 		case strings.EqualFold(u.Scheme, "https"): // DOH Remote mode
 			return NewDoHNameServer(u, dispatcher, queryStrategy)
 		case strings.EqualFold(u.Scheme, "https+local"): // DOH Local mode
diff --git a/app/dns/nameserver_local.go b/app/dns/nameserver_local.go
index f204981f..14de8356 100644
--- a/app/dns/nameserver_local.go
+++ b/app/dns/nameserver_local.go
@@ -14,13 +14,19 @@ import (
 
 // LocalNameServer is an wrapper over local DNS feature.
 type LocalNameServer struct {
-	client *localdns.Client
+	client        *localdns.Client
+	queryStrategy QueryStrategy
 }
 
 const errEmptyResponse = "No address associated with hostname"
 
 // QueryIP implements Server.
 func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, _ net.IP, option dns.IPOption, _ bool) (ips []net.IP, err error) {
+	option = ResolveIpOptionOverride(s.queryStrategy, option)
+	if !option.IPv4Enable && !option.IPv6Enable {
+		return nil, dns.ErrEmptyResponse
+	}
+
 	start := time.Now()
 	ips, err = s.client.LookupIP(domain, option)
 
@@ -42,14 +48,15 @@ func (s *LocalNameServer) Name() string {
 }
 
 // NewLocalNameServer creates localdns server object for directly lookup in system DNS.
-func NewLocalNameServer() *LocalNameServer {
+func NewLocalNameServer(queryStrategy QueryStrategy) *LocalNameServer {
 	errors.LogInfo(context.Background(), "DNS: created localhost client")
 	return &LocalNameServer{
-		client: localdns.New(),
+		queryStrategy: queryStrategy,
+		client:        localdns.New(),
 	}
 }
 
 // NewLocalDNSClient creates localdns client object for directly lookup in system DNS.
 func NewLocalDNSClient() *Client {
-	return &Client{server: NewLocalNameServer()}
+	return &Client{server: NewLocalNameServer(QueryStrategy_USE_IP)}
 }
diff --git a/app/dns/nameserver_local_test.go b/app/dns/nameserver_local_test.go
index 3331a333..500962f7 100644
--- a/app/dns/nameserver_local_test.go
+++ b/app/dns/nameserver_local_test.go
@@ -12,7 +12,7 @@ import (
 )
 
 func TestLocalNameServer(t *testing.T) {
-	s := NewLocalNameServer()
+	s := NewLocalNameServer(QueryStrategy_USE_IP)
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
 	ips, err := s.QueryIP(ctx, "google.com", net.IP{}, dns.IPOption{
 		IPv4Enable: true,