Преглед изворни кода

DNS New Features: `disableCache`, `finalQuery`, `unexpectedIPs`, `"*"`, `UseSystem-queryStrategy`, `useSystemHosts` (#4666)

patterniha пре 4 месеци
родитељ
комит
6232e230d9
7 измењених фајлова са 442 додато и 187 уклоњено
  1. 148 101
      app/dns/config.pb.go
  2. 7 2
      app/dns/config.proto
  3. 42 3
      app/dns/dns.go
  4. 7 7
      app/dns/dns_test.go
  5. 76 37
      app/dns/nameserver.go
  6. 26 0
      app/router/condition_geoip.go
  7. 136 37
      infra/conf/dns.go

+ 148 - 101
app/dns/config.pb.go

@@ -80,6 +80,7 @@ const (
 	QueryStrategy_USE_IP  QueryStrategy = 0
 	QueryStrategy_USE_IP4 QueryStrategy = 1
 	QueryStrategy_USE_IP6 QueryStrategy = 2
+	QueryStrategy_USE_SYS QueryStrategy = 3
 )
 
 // Enum value maps for QueryStrategy.
@@ -88,11 +89,13 @@ var (
 		0: "USE_IP",
 		1: "USE_IP4",
 		2: "USE_IP6",
+		3: "USE_SYS",
 	}
 	QueryStrategy_value = map[string]int32{
 		"USE_IP":  0,
 		"USE_IP4": 1,
 		"USE_IP6": 2,
+		"USE_SYS": 3,
 	}
 )
 
@@ -128,16 +131,20 @@ type NameServer struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Address            *net.Endpoint                `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
-	ClientIp           []byte                       `protobuf:"bytes,5,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"`
-	SkipFallback       bool                         `protobuf:"varint,6,opt,name=skipFallback,proto3" json:"skipFallback,omitempty"`
-	PrioritizedDomain  []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"`
-	Geoip              []*router.GeoIP              `protobuf:"bytes,3,rep,name=geoip,proto3" json:"geoip,omitempty"`
-	OriginalRules      []*NameServer_OriginalRule   `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"`
-	QueryStrategy      QueryStrategy                `protobuf:"varint,7,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"`
-	AllowUnexpectedIPs bool                         `protobuf:"varint,8,opt,name=allowUnexpectedIPs,proto3" json:"allowUnexpectedIPs,omitempty"`
-	Tag                string                       `protobuf:"bytes,9,opt,name=tag,proto3" json:"tag,omitempty"`
-	TimeoutMs          uint64                       `protobuf:"varint,10,opt,name=timeoutMs,proto3" json:"timeoutMs,omitempty"`
+	Address           *net.Endpoint                `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
+	ClientIp          []byte                       `protobuf:"bytes,5,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"`
+	SkipFallback      bool                         `protobuf:"varint,6,opt,name=skipFallback,proto3" json:"skipFallback,omitempty"`
+	PrioritizedDomain []*NameServer_PriorityDomain `protobuf:"bytes,2,rep,name=prioritized_domain,json=prioritizedDomain,proto3" json:"prioritized_domain,omitempty"`
+	ExpectedGeoip     []*router.GeoIP              `protobuf:"bytes,3,rep,name=expected_geoip,json=expectedGeoip,proto3" json:"expected_geoip,omitempty"`
+	OriginalRules     []*NameServer_OriginalRule   `protobuf:"bytes,4,rep,name=original_rules,json=originalRules,proto3" json:"original_rules,omitempty"`
+	QueryStrategy     QueryStrategy                `protobuf:"varint,7,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"`
+	ActPrior          bool                         `protobuf:"varint,8,opt,name=actPrior,proto3" json:"actPrior,omitempty"`
+	Tag               string                       `protobuf:"bytes,9,opt,name=tag,proto3" json:"tag,omitempty"`
+	TimeoutMs         uint64                       `protobuf:"varint,10,opt,name=timeoutMs,proto3" json:"timeoutMs,omitempty"`
+	DisableCache      bool                         `protobuf:"varint,11,opt,name=disableCache,proto3" json:"disableCache,omitempty"`
+	FinalQuery        bool                         `protobuf:"varint,12,opt,name=finalQuery,proto3" json:"finalQuery,omitempty"`
+	UnexpectedGeoip   []*router.GeoIP              `protobuf:"bytes,13,rep,name=unexpected_geoip,json=unexpectedGeoip,proto3" json:"unexpected_geoip,omitempty"`
+	ActUnprior        bool                         `protobuf:"varint,14,opt,name=actUnprior,proto3" json:"actUnprior,omitempty"`
 }
 
 func (x *NameServer) Reset() {
@@ -198,9 +205,9 @@ func (x *NameServer) GetPrioritizedDomain() []*NameServer_PriorityDomain {
 	return nil
 }
 
-func (x *NameServer) GetGeoip() []*router.GeoIP {
+func (x *NameServer) GetExpectedGeoip() []*router.GeoIP {
 	if x != nil {
-		return x.Geoip
+		return x.ExpectedGeoip
 	}
 	return nil
 }
@@ -219,9 +226,9 @@ func (x *NameServer) GetQueryStrategy() QueryStrategy {
 	return QueryStrategy_USE_IP
 }
 
-func (x *NameServer) GetAllowUnexpectedIPs() bool {
+func (x *NameServer) GetActPrior() bool {
 	if x != nil {
-		return x.AllowUnexpectedIPs
+		return x.ActPrior
 	}
 	return false
 }
@@ -240,6 +247,34 @@ func (x *NameServer) GetTimeoutMs() uint64 {
 	return 0
 }
 
+func (x *NameServer) GetDisableCache() bool {
+	if x != nil {
+		return x.DisableCache
+	}
+	return false
+}
+
+func (x *NameServer) GetFinalQuery() bool {
+	if x != nil {
+		return x.FinalQuery
+	}
+	return false
+}
+
+func (x *NameServer) GetUnexpectedGeoip() []*router.GeoIP {
+	if x != nil {
+		return x.UnexpectedGeoip
+	}
+	return nil
+}
+
+func (x *NameServer) GetActUnprior() bool {
+	if x != nil {
+		return x.ActUnprior
+	}
+	return false
+}
+
 type Config struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -532,7 +567,7 @@ var file_app_dns_config_proto_rawDesc = []byte{
 	0x2e, 0x64, 0x6e, 0x73, 0x1a, 0x1c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74,
 	0x2f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
 	0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x92, 0x05, 0x0a, 0x0a,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb6, 0x06, 0x0a, 0x0a,
 	0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x61, 0x64,
 	0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72,
 	0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e,
@@ -546,81 +581,92 @@ var file_app_dns_config_proto_rawDesc = []byte{
 	0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65,
 	0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44,
 	0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x11, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a,
-	0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x05, 0x67, 0x65, 0x6f, 0x69,
-	0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61,
-	0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52,
-	0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x4c, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e,
-	0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25,
-	0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61,
-	0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61,
-	0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52,
-	0x75, 0x6c, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74,
-	0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78,
-	0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72,
-	0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79,
-	0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x2e, 0x0a, 0x12, 0x61, 0x6c, 0x6c, 0x6f,
-	0x77, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x49, 0x50, 0x73, 0x18, 0x08,
-	0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x55, 0x6e, 0x65, 0x78, 0x70,
-	0x65, 0x63, 0x74, 0x65, 0x64, 0x49, 0x50, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18,
-	0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69,
-	0x6d, 0x65, 0x6f, 0x75, 0x74, 0x4d, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74,
-	0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x4d, 0x73, 0x1a, 0x5e, 0x0a, 0x0e, 0x50, 0x72, 0x69, 0x6f,
-	0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79,
-	0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
-	0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61,
-	0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
-	0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x36, 0x0a, 0x0c, 0x4f, 0x72, 0x69, 0x67,
-	0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x75, 0x6c, 0x65,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04,
-	0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65,
-	0x22, 0x9c, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x0b, 0x6e,
-	0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b,
-	0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e,
-	0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65,
-	0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
-	0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e,
-	0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x68, 0x6f,
-	0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79,
-	0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
-	0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x73, 0x74, 0x61,
-	0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18,
-	0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69,
-	0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08,
-	0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x42,
-	0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
-	0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
-	0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74,
-	0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65,
-	0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c,
-	0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x69, 0x73,
-	0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x36, 0x0a, 0x16,
-	0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49,
-	0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x64, 0x69,
-	0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d,
-	0x61, 0x74, 0x63, 0x68, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70,
-	0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e,
-	0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67,
-	0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f,
-	0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61,
-	0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x02,
-	0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x5f, 0x64, 0x6f,
-	0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78,
-	0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x2a,
-	0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e,
-	0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00, 0x12,
-	0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10, 0x01, 0x12, 0x0b,
-	0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x52,
-	0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a, 0x35, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53,
-	0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49,
-	0x50, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x01,
-	0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x02, 0x42, 0x46, 0x0a,
-	0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e,
-	0x73, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
-	0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61,
-	0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70,
-	0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3d, 0x0a, 0x0e, 0x65, 0x78, 0x70, 0x65,
+	0x63, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x75, 0x74,
+	0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0d, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74,
+	0x65, 0x64, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x4c, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69,
+	0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
+	0x25, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x4e,
+	0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e,
+	0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c,
+	0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73,
+	0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e,
+	0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65,
+	0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72,
+	0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x74,
+	0x50, 0x72, 0x69, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x63, 0x74,
+	0x50, 0x72, 0x69, 0x6f, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x09, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x6f,
+	0x75, 0x74, 0x4d, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65,
+	0x6f, 0x75, 0x74, 0x4d, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,
+	0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73,
+	0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6e,
+	0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66,
+	0x69, 0x6e, 0x61, 0x6c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x41, 0x0a, 0x10, 0x75, 0x6e, 0x65,
+	0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x0d, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x72,
+	0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x0f, 0x75, 0x6e, 0x65,
+	0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x47, 0x65, 0x6f, 0x69, 0x70, 0x12, 0x1e, 0x0a, 0x0a,
+	0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08,
+	0x52, 0x0a, 0x61, 0x63, 0x74, 0x55, 0x6e, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x1a, 0x5e, 0x0a, 0x0e,
+	0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x34,
+	0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78,
+	0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61,
+	0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04,
+	0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x36, 0x0a, 0x0c,
+	0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04,
+	0x72, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x75, 0x6c, 0x65,
+	0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04,
+	0x73, 0x69, 0x7a, 0x65, 0x22, 0x9c, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
+	0x39, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x05,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
+	0x64, 0x6e, 0x73, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0a,
+	0x6e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c,
+	0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63,
+	0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x43, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69,
+	0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e,
+	0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52,
+	0x0b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x10, 0x0a, 0x03,
+	0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x22,
+	0x0a, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x08,
+	0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63,
+	0x68, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61,
+	0x74, 0x65, 0x67, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61,
+	0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53,
+	0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74,
+	0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
+	0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52,
+	0x0f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b,
+	0x12, 0x36, 0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62,
+	0x61, 0x63, 0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08,
+	0x52, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63,
+	0x6b, 0x49, 0x66, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x92, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73,
+	0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70,
+	0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74, 0x63,
+	0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16,
+	0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
+	0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03,
+	0x28, 0x0c, 0x52, 0x02, 0x69, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x65,
+	0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
+	0x70, 0x72, 0x6f, 0x78, 0x69, 0x65, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x04, 0x08,
+	0x07, 0x10, 0x08, 0x2a, 0x45, 0x0a, 0x12, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4d, 0x61, 0x74,
+	0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c,
+	0x6c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
+	0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x77, 0x6f, 0x72, 0x64, 0x10, 0x02, 0x12,
+	0x09, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x0d, 0x51, 0x75,
+	0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0a, 0x0a, 0x06, 0x55,
+	0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49,
+	0x50, 0x34, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10,
+	0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x53, 0x59, 0x53, 0x10, 0x03, 0x42, 0x46,
+	0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64,
+	0x6e, 0x73, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
+	0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f,
+	0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41,
+	0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -651,19 +697,20 @@ var file_app_dns_config_proto_goTypes = []any{
 var file_app_dns_config_proto_depIdxs = []int32{
 	7,  // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint
 	4,  // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain
-	8,  // 2: xray.app.dns.NameServer.geoip:type_name -> xray.app.router.GeoIP
+	8,  // 2: xray.app.dns.NameServer.expected_geoip:type_name -> xray.app.router.GeoIP
 	5,  // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule
 	1,  // 4: xray.app.dns.NameServer.query_strategy:type_name -> xray.app.dns.QueryStrategy
-	2,  // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
-	6,  // 6: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping
-	1,  // 7: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy
-	0,  // 8: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType
-	0,  // 9: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType
-	10, // [10:10] is the sub-list for method output_type
-	10, // [10:10] is the sub-list for method input_type
-	10, // [10:10] is the sub-list for extension type_name
-	10, // [10:10] is the sub-list for extension extendee
-	0,  // [0:10] is the sub-list for field type_name
+	8,  // 5: xray.app.dns.NameServer.unexpected_geoip:type_name -> xray.app.router.GeoIP
+	2,  // 6: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer
+	6,  // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping
+	1,  // 8: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy
+	0,  // 9: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType
+	0,  // 10: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType
+	11, // [11:11] is the sub-list for method output_type
+	11, // [11:11] is the sub-list for method input_type
+	11, // [11:11] is the sub-list for extension type_name
+	11, // [11:11] is the sub-list for extension extendee
+	0,  // [0:11] is the sub-list for field type_name
 }
 
 func init() { file_app_dns_config_proto_init() }

+ 7 - 2
app/dns/config.proto

@@ -25,12 +25,16 @@ message NameServer {
   }
 
   repeated PriorityDomain prioritized_domain = 2;
-  repeated xray.app.router.GeoIP geoip = 3;
+  repeated xray.app.router.GeoIP expected_geoip = 3;
   repeated OriginalRule original_rules = 4;
   QueryStrategy query_strategy = 7;
-  bool allowUnexpectedIPs = 8;
+  bool actPrior = 8;
   string tag = 9;
   uint64 timeoutMs = 10;
+  bool disableCache = 11;
+  bool finalQuery = 12;
+  repeated xray.app.router.GeoIP unexpected_geoip = 13;
+  bool actUnprior = 14;
 }
 
 enum DomainMatchingType {
@@ -44,6 +48,7 @@ enum QueryStrategy {
   USE_IP = 0;
   USE_IP4 = 1;
   USE_IP6 = 2;
+  USE_SYS = 3;
 }
 
 message Config {

+ 42 - 3
app/dns/dns.go

@@ -28,6 +28,7 @@ type DNS struct {
 	ctx                    context.Context
 	domainMatcher          strmatcher.IndexMatcher
 	matcherInfos           []*DomainMatcherInfo
+	checkSystem            bool
 }
 
 // DomainMatcherInfo contains information attached to index returned by Server.domainMatcher
@@ -47,6 +48,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
 	}
 
 	var ipOption dns.IPOption
+	checkSystem := false
 	switch config.QueryStrategy {
 	case QueryStrategy_USE_IP:
 		ipOption = dns.IPOption{
@@ -54,6 +56,13 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
 			IPv6Enable: true,
 			FakeEnable: false,
 		}
+	case QueryStrategy_USE_SYS:
+		ipOption = dns.IPOption{
+			IPv4Enable: true,
+			IPv6Enable: true,
+			FakeEnable: false,
+		}
+		checkSystem = true
 	case QueryStrategy_USE_IP4:
 		ipOption = dns.IPOption{
 			IPv4Enable: true,
@@ -108,7 +117,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
 			myClientIP = net.IP(ns.ClientIp)
 		}
 
-		disableCache := config.DisableCache
+		disableCache := config.DisableCache || ns.DisableCache
 
 		var tag = defaultTag
 		if len(ns.Tag) > 0 {
@@ -118,6 +127,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
 		if !clientIPOption.IPv4Enable && !clientIPOption.IPv6Enable {
 			return nil, errors.New("no QueryStrategy available for ", ns.Address)
 		}
+
 		client, err := NewClient(ctx, ns, myClientIP, disableCache, tag, clientIPOption, &matcherInfos, updateDomain)
 		if err != nil {
 			return nil, errors.New("failed to create client").Base(err)
@@ -139,6 +149,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
 		matcherInfos:           matcherInfos,
 		disableFallback:        config.DisableFallback,
 		disableFallbackIfMatch: config.DisableFallbackIfMatch,
+		checkSystem:            checkSystem,
 	}, nil
 }
 
@@ -179,8 +190,14 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er
 		return nil, 0, errors.New("empty domain name")
 	}
 
-	option.IPv4Enable = option.IPv4Enable && s.ipOption.IPv4Enable
-	option.IPv6Enable = option.IPv6Enable && s.ipOption.IPv6Enable
+	if s.checkSystem {
+		supportIPv4, supportIPv6 := checkSystemNetwork()
+		option.IPv4Enable = option.IPv4Enable && supportIPv4
+		option.IPv6Enable = option.IPv6Enable && supportIPv6
+	} else {
+		option.IPv4Enable = option.IPv4Enable && s.ipOption.IPv4Enable
+		option.IPv6Enable = option.IPv6Enable && s.ipOption.IPv6Enable
+	}
 
 	if !option.IPv4Enable && !option.IPv6Enable {
 		return nil, 0, dns.ErrEmptyResponse
@@ -227,6 +244,9 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er
 		}
 		errs = append(errs, err)
 
+		if client.IsFinalQuery() {
+			break
+		}
 	}
 
 	if len(errs) > 0 {
@@ -302,3 +322,22 @@ func init() {
 		return New(ctx, config.(*Config))
 	}))
 }
+
+func checkSystemNetwork() (supportIPv4 bool, supportIPv6 bool) {
+	conn4, err4 := net.Dial("udp4", "8.8.8.8:53")
+	if err4 != nil {
+		supportIPv4 = false
+	} else {
+		supportIPv4 = true
+		conn4.Close()
+	}
+
+	conn6, err6 := net.Dial("udp6", "[2001:4860:4860::8888]:53")
+	if err6 != nil {
+		supportIPv6 = false
+	} else {
+		supportIPv6 = true
+		conn6.Close()
+	}
+	return
+}

+ 7 - 7
app/dns/dns_test.go

@@ -539,7 +539,7 @@ func TestIPMatch(t *testing.T) {
 							},
 							Port: uint32(port),
 						},
-						Geoip: []*router.GeoIP{
+						ExpectedGeoip: []*router.GeoIP{
 							{
 								CountryCode: "local",
 								Cidr: []*router.CIDR{
@@ -563,7 +563,7 @@ func TestIPMatch(t *testing.T) {
 							},
 							Port: uint32(port),
 						},
-						Geoip: []*router.GeoIP{
+						ExpectedGeoip: []*router.GeoIP{
 							{
 								CountryCode: "test",
 								Cidr: []*router.CIDR{
@@ -667,7 +667,7 @@ func TestLocalDomain(t *testing.T) {
 							// Equivalent of dotless:localhost
 							{Type: DomainMatchingType_Regex, Domain: "^[^.]*localhost[^.]*$"},
 						},
-						Geoip: []*router.GeoIP{
+						ExpectedGeoip: []*router.GeoIP{
 							{ // Will match localhost, localhost-a and localhost-b,
 								CountryCode: "local",
 								Cidr: []*router.CIDR{
@@ -897,7 +897,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
 								Domain: "google.com",
 							},
 						},
-						Geoip: []*router.GeoIP{
+						ExpectedGeoip: []*router.GeoIP{
 							{ // Will only match 8.8.8.8 and 8.8.4.4
 								Cidr: []*router.CIDR{
 									{Ip: []byte{8, 8, 8, 8}, Prefix: 32},
@@ -922,7 +922,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
 								Domain: "google.com",
 							},
 						},
-						Geoip: []*router.GeoIP{
+						ExpectedGeoip: []*router.GeoIP{
 							{ // Will match 8.8.8.8 and 8.8.8.7, etc
 								Cidr: []*router.CIDR{
 									{Ip: []byte{8, 8, 8, 7}, Prefix: 24},
@@ -946,7 +946,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
 								Domain: "api.google.com",
 							},
 						},
-						Geoip: []*router.GeoIP{
+						ExpectedGeoip: []*router.GeoIP{
 							{ // Will only match 8.8.7.7 (api.google.com)
 								Cidr: []*router.CIDR{
 									{Ip: []byte{8, 8, 7, 7}, Prefix: 32},
@@ -970,7 +970,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
 								Domain: "v2.api.google.com",
 							},
 						},
-						Geoip: []*router.GeoIP{
+						ExpectedGeoip: []*router.GeoIP{
 							{ // Will only match 8.8.7.8 (v2.api.google.com)
 								Cidr: []*router.CIDR{
 									{Ip: []byte{8, 8, 7, 8}, Prefix: 32},

+ 76 - 37
app/dns/nameserver.go

@@ -26,14 +26,18 @@ type Server interface {
 
 // Client is the interface for DNS client.
 type Client struct {
-	server             Server
-	skipFallback       bool
-	domains            []string
-	expectedIPs        []*router.GeoIPMatcher
-	allowUnexpectedIPs bool
-	tag                string
-	timeoutMs          time.Duration
-	ipOption           *dns.IPOption
+	server        Server
+	skipFallback  bool
+	domains       []string
+	expectedIPs   []*router.GeoIPMatcher
+	unexpectedIPs []*router.GeoIPMatcher
+	actPrior      bool
+	actUnprior    bool
+	tag           string
+	timeoutMs     time.Duration
+	finalQuery    bool
+	ipOption      *dns.IPOption
+	checkSystem   bool
 }
 
 // NewServer creates a name server object according to the network destination url.
@@ -150,13 +154,23 @@ func NewClient(
 		}
 
 		// Establish expected IPs
-		var matchers []*router.GeoIPMatcher
-		for _, geoip := range ns.Geoip {
+		var expectedMatchers []*router.GeoIPMatcher
+		for _, geoip := range ns.ExpectedGeoip {
 			matcher, err := router.GlobalGeoIPContainer.Add(geoip)
 			if err != nil {
-				return errors.New("failed to create ip matcher").Base(err).AtWarning()
+				return errors.New("failed to create expected ip matcher").Base(err).AtWarning()
 			}
-			matchers = append(matchers, matcher)
+			expectedMatchers = append(expectedMatchers, matcher)
+		}
+
+		// Establish unexpected IPs
+		var unexpectedMatchers []*router.GeoIPMatcher
+		for _, geoip := range ns.UnexpectedGeoip {
+			matcher, err := router.GlobalGeoIPContainer.Add(geoip)
+			if err != nil {
+				return errors.New("failed to create unexpected ip matcher").Base(err).AtWarning()
+			}
+			unexpectedMatchers = append(unexpectedMatchers, matcher)
 		}
 
 		if len(clientIP) > 0 {
@@ -173,14 +187,20 @@ func NewClient(
 			timeoutMs = time.Duration(ns.TimeoutMs) * time.Millisecond
 		}
 
+		checkSystem := ns.QueryStrategy == QueryStrategy_USE_SYS
+
 		client.server = server
 		client.skipFallback = ns.SkipFallback
 		client.domains = rules
-		client.expectedIPs = matchers
-		client.allowUnexpectedIPs = ns.AllowUnexpectedIPs
+		client.expectedIPs = expectedMatchers
+		client.unexpectedIPs = unexpectedMatchers
+		client.actPrior = ns.ActPrior
+		client.actUnprior = ns.ActUnprior
 		client.tag = tag
 		client.timeoutMs = timeoutMs
+		client.finalQuery = ns.FinalQuery
 		client.ipOption = &ipOption
+		client.checkSystem = checkSystem
 		return nil
 	})
 	return client, err
@@ -191,10 +211,21 @@ func (c *Client) Name() string {
 	return c.server.Name()
 }
 
+func (c *Client) IsFinalQuery() bool {
+	return c.finalQuery
+}
+
 // QueryIP sends DNS query to the name server with the client's IP.
 func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, uint32, error) {
-	option.IPv4Enable = option.IPv4Enable && c.ipOption.IPv4Enable
-	option.IPv6Enable = option.IPv6Enable && c.ipOption.IPv6Enable
+	if c.checkSystem {
+		supportIPv4, supportIPv6 := checkSystemNetwork()
+		option.IPv4Enable = option.IPv4Enable && supportIPv4
+		option.IPv6Enable = option.IPv6Enable && supportIPv6
+	} else {
+		option.IPv4Enable = option.IPv4Enable && c.ipOption.IPv4Enable
+		option.IPv6Enable = option.IPv6Enable && c.ipOption.IPv6Enable
+	}
+
 	if !option.IPv4Enable && !option.IPv6Enable {
 		return nil, 0, dns.ErrEmptyResponse
 	}
@@ -212,39 +243,47 @@ func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption
 		return nil, 0, dns.ErrEmptyResponse
 	}
 
-	if len(c.expectedIPs) > 0 {
-		newIps := c.MatchExpectedIPs(domain, ips)
-		if len(newIps) == 0 {
-			if !c.allowUnexpectedIPs {
-				return nil, 0, dns.ErrEmptyResponse
-			}
-		} else {
-			ips = newIps
+	if len(c.expectedIPs) > 0 && !c.actPrior {
+		ips = router.MatchIPs(c.expectedIPs, ips, false)
+		errors.LogDebug(context.Background(), "domain ", domain, " expectedIPs ", ips, " matched at server ", c.Name())
+		if len(ips) == 0 {
+			return nil, 0, dns.ErrEmptyResponse
 		}
 	}
 
-	return ips, ttl, nil
-}
+	if len(c.unexpectedIPs) > 0 && !c.actUnprior {
+		ips = router.MatchIPs(c.unexpectedIPs, ips, true)
+		errors.LogDebug(context.Background(), "domain ", domain, " unexpectedIPs ", ips, " matched at server ", c.Name())
+		if len(ips) == 0 {
+			return nil, 0, dns.ErrEmptyResponse
+		}
+	}
 
-// MatchExpectedIPs matches queried domain IPs with expected IPs and returns matched ones.
-func (c *Client) MatchExpectedIPs(domain string, ips []net.IP) []net.IP {
-	var newIps []net.IP
-	for _, ip := range ips {
-		for _, matcher := range c.expectedIPs {
-			if matcher.Match(ip) {
-				newIps = append(newIps, ip)
-				break
-			}
+	if len(c.expectedIPs) > 0 && c.actPrior {
+		ipsNew := router.MatchIPs(c.expectedIPs, ips, false)
+		if len(ipsNew) > 0 {
+			ips = ipsNew
+			errors.LogDebug(context.Background(), "domain ", domain, " priorIPs ", ips, " matched at server ", c.Name())
 		}
 	}
-	errors.LogDebug(context.Background(), "domain ", domain, " expectedIPs ", newIps, " matched at server ", c.Name())
-	return newIps
+
+	if len(c.unexpectedIPs) > 0 && c.actUnprior {
+		ipsNew := router.MatchIPs(c.unexpectedIPs, ips, true)
+		if len(ipsNew) > 0 {
+			ips = ipsNew
+			errors.LogDebug(context.Background(), "domain ", domain, " unpriorIPs ", ips, " matched at server ", c.Name())
+		}
+	}
+
+	return ips, ttl, nil
 }
 
 func ResolveIpOptionOverride(queryStrategy QueryStrategy, ipOption dns.IPOption) dns.IPOption {
 	switch queryStrategy {
 	case QueryStrategy_USE_IP:
 		return ipOption
+	case QueryStrategy_USE_SYS:
+		return ipOption
 	case QueryStrategy_USE_IP4:
 		return dns.IPOption{
 			IPv4Enable: ipOption.IPv4Enable,

+ 26 - 0
app/router/condition_geoip.go

@@ -116,3 +116,29 @@ func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
 }
 
 var GlobalGeoIPContainer GeoIPMatcherContainer
+
+func MatchIPs(matchers []*GeoIPMatcher, ips []net.IP, reverse bool) []net.IP {
+	if len(matchers) == 0 {
+		panic("GeoIP matchers should not be empty to avoid ambiguity")
+	}
+	newIPs := make([]net.IP, 0, len(ips))
+	var isFound bool
+	for _, ip := range ips {
+		isFound = false
+		for _, matcher := range matchers {
+			if matcher.Match(ip) {
+				isFound = true
+				break
+			}
+		}
+		if isFound && !reverse {
+			newIPs = append(newIPs, ip)
+			continue
+		}
+		if !isFound && reverse {
+			newIPs = append(newIPs, ip)
+			continue
+		}
+	}
+	return newIPs
+}

+ 136 - 37
infra/conf/dns.go

@@ -1,7 +1,11 @@
 package conf
 
 import (
+	"bufio"
 	"encoding/json"
+	"os"
+	"path/filepath"
+	"runtime"
 	"sort"
 	"strings"
 
@@ -12,17 +16,19 @@ import (
 )
 
 type NameServerConfig struct {
-	Address            *Address   `json:"address"`
-	ClientIP           *Address   `json:"clientIp"`
-	Port               uint16     `json:"port"`
-	SkipFallback       bool       `json:"skipFallback"`
-	Domains            []string   `json:"domains"`
-	ExpectedIPs        StringList `json:"expectedIPs"`
-	ExpectIPs          StringList `json:"expectIPs"`
-	QueryStrategy      string     `json:"queryStrategy"`
-	AllowUnexpectedIPs bool       `json:"allowUnexpectedIps"`
-	Tag                string     `json:"tag"`
-	TimeoutMs          uint64     `json:"timeoutMs"`
+	Address       *Address   `json:"address"`
+	ClientIP      *Address   `json:"clientIp"`
+	Port          uint16     `json:"port"`
+	SkipFallback  bool       `json:"skipFallback"`
+	Domains       []string   `json:"domains"`
+	ExpectedIPs   StringList `json:"expectedIPs"`
+	ExpectIPs     StringList `json:"expectIPs"`
+	QueryStrategy string     `json:"queryStrategy"`
+	Tag           string     `json:"tag"`
+	TimeoutMs     uint64     `json:"timeoutMs"`
+	DisableCache  bool       `json:"disableCache"`
+	FinalQuery    bool       `json:"finalQuery"`
+	UnexpectedIPs StringList `json:"unexpectedIPs"`
 }
 
 // UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON
@@ -34,17 +40,19 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
 	}
 
 	var advanced struct {
-		Address            *Address   `json:"address"`
-		ClientIP           *Address   `json:"clientIp"`
-		Port               uint16     `json:"port"`
-		SkipFallback       bool       `json:"skipFallback"`
-		Domains            []string   `json:"domains"`
-		ExpectedIPs        StringList `json:"expectedIPs"`
-		ExpectIPs          StringList `json:"expectIPs"`
-		QueryStrategy      string     `json:"queryStrategy"`
-		AllowUnexpectedIPs bool       `json:"allowUnexpectedIps"`
-		Tag                string     `json:"tag"`
-		TimeoutMs          uint64     `json:"timeoutMs"`
+		Address       *Address   `json:"address"`
+		ClientIP      *Address   `json:"clientIp"`
+		Port          uint16     `json:"port"`
+		SkipFallback  bool       `json:"skipFallback"`
+		Domains       []string   `json:"domains"`
+		ExpectedIPs   StringList `json:"expectedIPs"`
+		ExpectIPs     StringList `json:"expectIPs"`
+		QueryStrategy string     `json:"queryStrategy"`
+		Tag           string     `json:"tag"`
+		TimeoutMs     uint64     `json:"timeoutMs"`
+		DisableCache  bool       `json:"disableCache"`
+		FinalQuery    bool       `json:"finalQuery"`
+		UnexpectedIPs StringList `json:"unexpectedIPs"`
 	}
 	if err := json.Unmarshal(data, &advanced); err == nil {
 		c.Address = advanced.Address
@@ -55,9 +63,11 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error {
 		c.ExpectedIPs = advanced.ExpectedIPs
 		c.ExpectIPs = advanced.ExpectIPs
 		c.QueryStrategy = advanced.QueryStrategy
-		c.AllowUnexpectedIPs = advanced.AllowUnexpectedIPs
 		c.Tag = advanced.Tag
 		c.TimeoutMs = advanced.TimeoutMs
+		c.DisableCache = advanced.DisableCache
+		c.FinalQuery = advanced.FinalQuery
+		c.UnexpectedIPs = advanced.UnexpectedIPs
 		return nil
 	}
 
@@ -105,13 +115,38 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
 		})
 	}
 
-	var expectedIPs = c.ExpectedIPs
-	if len(expectedIPs) == 0 {
-		expectedIPs = c.ExpectIPs
+	if len(c.ExpectedIPs) == 0 {
+		c.ExpectedIPs = c.ExpectIPs
 	}
-	geoipList, err := ToCidrList(expectedIPs)
+
+	actPrior := false
+	var newExpectedIPs StringList
+	for _, s := range c.ExpectedIPs {
+		if s == "*" {
+			actPrior = true
+		} else {
+			newExpectedIPs = append(newExpectedIPs, s)
+		}
+	}
+
+	actUnprior := false
+	var newUnexpectedIPs StringList
+	for _, s := range c.UnexpectedIPs {
+		if s == "*" {
+			actUnprior = true
+		} else {
+			newUnexpectedIPs = append(newUnexpectedIPs, s)
+		}
+	}
+
+	expectedGeoipList, err := ToCidrList(newExpectedIPs)
 	if err != nil {
-		return nil, errors.New("invalid IP rule: ", expectedIPs).Base(err)
+		return nil, errors.New("invalid expected IP rule: ", c.ExpectedIPs).Base(err)
+	}
+
+	unexpectedGeoipList, err := ToCidrList(newUnexpectedIPs)
+	if err != nil {
+		return nil, errors.New("invalid unexpected IP rule: ", c.UnexpectedIPs).Base(err)
 	}
 
 	var myClientIP []byte
@@ -128,15 +163,19 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
 			Address: c.Address.Build(),
 			Port:    uint32(c.Port),
 		},
-		ClientIp:           myClientIP,
-		SkipFallback:       c.SkipFallback,
-		PrioritizedDomain:  domains,
-		Geoip:              geoipList,
-		OriginalRules:      originalRules,
-		QueryStrategy:      resolveQueryStrategy(c.QueryStrategy),
-		AllowUnexpectedIPs: c.AllowUnexpectedIPs,
-		Tag:                c.Tag,
-		TimeoutMs:          c.TimeoutMs,
+		ClientIp:          myClientIP,
+		SkipFallback:      c.SkipFallback,
+		PrioritizedDomain: domains,
+		ExpectedGeoip:     expectedGeoipList,
+		OriginalRules:     originalRules,
+		QueryStrategy:     resolveQueryStrategy(c.QueryStrategy),
+		ActPrior:          actPrior,
+		Tag:               c.Tag,
+		TimeoutMs:         c.TimeoutMs,
+		DisableCache:      c.DisableCache,
+		FinalQuery:        c.FinalQuery,
+		UnexpectedGeoip:   unexpectedGeoipList,
+		ActUnprior:        actUnprior,
 	}, nil
 }
 
@@ -157,6 +196,7 @@ type DNSConfig struct {
 	DisableCache           bool                `json:"disableCache"`
 	DisableFallback        bool                `json:"disableFallback"`
 	DisableFallbackIfMatch bool                `json:"disableFallbackIfMatch"`
+	UseSystemHosts         bool                `json:"useSystemHosts"`
 }
 
 type HostAddress struct {
@@ -378,6 +418,15 @@ func (c *DNSConfig) Build() (*dns.Config, error) {
 		}
 		config.StaticHosts = append(config.StaticHosts, staticHosts...)
 	}
+	if c.UseSystemHosts {
+		systemHosts, err := readSystemHosts()
+		if err != nil {
+			return nil, errors.New("failed to read system hosts").Base(err)
+		}
+		for domain, ips := range systemHosts {
+			config.StaticHosts = append(config.StaticHosts, &dns.Config_HostMapping{Ip: ips, Domain: domain, Type: dns.DomainMatchingType_Full})
+		}
+	}
 
 	return config, nil
 }
@@ -390,7 +439,57 @@ func resolveQueryStrategy(queryStrategy string) dns.QueryStrategy {
 		return dns.QueryStrategy_USE_IP4
 	case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6":
 		return dns.QueryStrategy_USE_IP6
+	case "usesys", "usesystem", "use_sys", "use_system", "use-sys", "use-system":
+		return dns.QueryStrategy_USE_SYS
 	default:
 		return dns.QueryStrategy_USE_IP
 	}
 }
+
+func readSystemHosts() (map[string][][]byte, error) {
+	var hostsPath string
+	switch runtime.GOOS {
+	case "windows":
+		hostsPath = filepath.Join(os.Getenv("SystemRoot"), "System32", "drivers", "etc", "hosts")
+	default:
+		hostsPath = "/etc/hosts"
+	}
+
+	file, err := os.Open(hostsPath)
+	if err != nil {
+		return nil, err
+	}
+	defer file.Close()
+
+	hostsMap := make(map[string][][]byte)
+	scanner := bufio.NewScanner(file)
+	for scanner.Scan() {
+		line := strings.TrimSpace(scanner.Text())
+		if i := strings.IndexByte(line, '#'); i >= 0 {
+			// Discard comments.
+			line = line[0:i]
+		}
+		f := strings.Fields(line)
+		if len(f) < 2 {
+			continue
+		}
+		addr := net.ParseAddress(f[0])
+		if addr.Family().IsDomain() {
+			continue
+		}
+		ip := addr.IP()
+		for i := 1; i < len(f); i++ {
+			domain := strings.TrimSuffix(f[i], ".")
+			domain = strings.ToLower(domain)
+			if v, ok := hostsMap[domain]; ok {
+				hostsMap[domain] = append(v, ip)
+			} else {
+				hostsMap[domain] = [][]byte{ip}
+			}
+		}
+	}
+	if err := scanner.Err(); err != nil {
+		return nil, err
+	}
+	return hostsMap, nil
+}