Browse Source

Merge pull request #98 from Vigilans/vigilans/dns-server-matcher-info

Refactor & Log rules matching of built-in DNS's domain matcher
Kslr 5 years ago
parent
commit
dabefc6338

+ 169 - 78
app/dns/config.pb.go

@@ -1,3 +1,9 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.25.0
+// 	protoc        (unknown)
+// source: v2ray.com/core/app/dns/config.proto
+
 package dns
 
 import (
@@ -81,6 +87,7 @@ type NameServer struct {
 	Address           *net.Endpoint                `protobuf:"bytes,1,opt,name=address,proto3" json:"address,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"`
 }
 
 func (x *NameServer) Reset() {
@@ -136,6 +143,13 @@ func (x *NameServer) GetGeoip() []*router.GeoIP {
 	return nil
 }
 
+func (x *NameServer) GetOriginalRules() []*NameServer_OriginalRule {
+	if x != nil {
+		return x.OriginalRules
+	}
+	return nil
+}
+
 type Config struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -145,14 +159,14 @@ type Config struct {
 	// A special value 'localhost' as a domain address can be set to use DNS on local system.
 	//
 	// Deprecated: Do not use.
-	NameServers []*net.Endpoint `protobuf:"bytes,1,rep,name=NameServers,proto3" json:"NameServers,omitempty"`
+	NameServers []*net.Endpoint `protobuf:"bytes,1,rep,name=NameServers,json=nameServers,proto3" json:"NameServers,omitempty"`
 	// NameServer list used by this DNS client.
 	NameServer []*NameServer `protobuf:"bytes,5,rep,name=name_server,json=nameServer,proto3" json:"name_server,omitempty"`
 	// Static hosts. Domain to IP.
 	// Deprecated. Use static_hosts.
 	//
 	// Deprecated: Do not use.
-	Hosts map[string]*net.IPOrDomain `protobuf:"bytes,2,rep,name=Hosts,proto3" json:"Hosts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+	Hosts map[string]*net.IPOrDomain `protobuf:"bytes,2,rep,name=Hosts,json=hosts,proto3" json:"Hosts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
 	// Client IP for EDNS client subnet. Must be 4 bytes (IPv4) or 16 bytes (IPv6).
 	ClientIp    []byte                `protobuf:"bytes,3,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"`
 	StaticHosts []*Config_HostMapping `protobuf:"bytes,4,rep,name=static_hosts,json=staticHosts,proto3" json:"static_hosts,omitempty"`
@@ -291,6 +305,61 @@ func (x *NameServer_PriorityDomain) GetDomain() string {
 	return ""
 }
 
+type NameServer_OriginalRule struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Rule string `protobuf:"bytes,1,opt,name=rule,proto3" json:"rule,omitempty"`
+	Size uint32 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"`
+}
+
+func (x *NameServer_OriginalRule) Reset() {
+	*x = NameServer_OriginalRule{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_v2ray_com_core_app_dns_config_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *NameServer_OriginalRule) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*NameServer_OriginalRule) ProtoMessage() {}
+
+func (x *NameServer_OriginalRule) ProtoReflect() protoreflect.Message {
+	mi := &file_v2ray_com_core_app_dns_config_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use NameServer_OriginalRule.ProtoReflect.Descriptor instead.
+func (*NameServer_OriginalRule) Descriptor() ([]byte, []int) {
+	return file_v2ray_com_core_app_dns_config_proto_rawDescGZIP(), []int{0, 1}
+}
+
+func (x *NameServer_OriginalRule) GetRule() string {
+	if x != nil {
+		return x.Rule
+	}
+	return ""
+}
+
+func (x *NameServer_OriginalRule) GetSize() uint32 {
+	if x != nil {
+		return x.Size
+	}
+	return 0
+}
+
 type Config_HostMapping struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -307,7 +376,7 @@ type Config_HostMapping struct {
 func (x *Config_HostMapping) Reset() {
 	*x = Config_HostMapping{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_v2ray_com_core_app_dns_config_proto_msgTypes[4]
+		mi := &file_v2ray_com_core_app_dns_config_proto_msgTypes[5]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -320,7 +389,7 @@ func (x *Config_HostMapping) String() string {
 func (*Config_HostMapping) ProtoMessage() {}
 
 func (x *Config_HostMapping) ProtoReflect() protoreflect.Message {
-	mi := &file_v2ray_com_core_app_dns_config_proto_msgTypes[4]
+	mi := &file_v2ray_com_core_app_dns_config_proto_msgTypes[5]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -378,7 +447,7 @@ var file_v2ray_com_core_app_dns_config_proto_rawDesc = []byte{
 	0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
 	0x26, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f,
 	0x61, 0x70, 0x70, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbf, 0x02, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65,
+	0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcb, 0x03, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65,
 	0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
 	0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e,
 	0x63, 0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e,
@@ -392,58 +461,66 @@ var file_v2ray_com_core_app_dns_config_proto_rawDesc = []byte{
 	0x32, 0x0a, 0x05, 0x67, 0x65, 0x6f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c,
 	0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e,
 	0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x67, 0x65,
-	0x6f, 0x69, 0x70, 0x1a, 0x64, 0x0a, 0x0e, 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x44,
-	0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65,
-	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, 0x22, 0xc3, 0x04, 0x0a, 0x06, 0x43, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x12, 0x45, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76,
-	0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, 0x32, 0x72, 0x61,
-	0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65,
-	0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b,
-	0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x3f, 0x0a, 0x0b, 0x6e,
-	0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b,
-	0x32, 0x1e, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x3f, 0x0a, 0x05,
-	0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x76, 0x32,
-	0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73,
-	0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74,
-	0x72, 0x79, 0x42, 0x02, 0x18, 0x01, 0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 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, 0x49, 0x0a, 0x0c, 0x73, 0x74,
-	0x61, 0x74, 0x69, 0x63, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b,
-	0x32, 0x26, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x1a, 0x5b, 0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x73,
-	0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x37, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63,
-	0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49,
-	0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
-	0x3a, 0x02, 0x38, 0x01, 0x1a, 0x98, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70,
-	0x70, 0x69, 0x6e, 0x67, 0x12, 0x3a, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x0e, 0x32, 0x26, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 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, 0x42, 0x34, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32,
+	0x6f, 0x69, 0x70, 0x12, 0x52, 0x0a, 0x0e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f,
+	0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x76, 0x32,
 	0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73,
-	0x50, 0x01, 0x5a, 0x03, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x12, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e,
-	0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x33,
+	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, 0x1a, 0x64, 0x0a, 0x0e, 0x50, 0x72, 0x69, 0x6f, 0x72,
+	0x69, 0x74, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x04, 0x74, 0x79, 0x70,
+	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e,
+	0x63, 0x6f, 0x72, 0x65, 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, 0xc3, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x12, 0x45, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18,
+	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
+	0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e,
+	0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65,
+	0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x3f, 0x0a, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
+	0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x76,
+	0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x3f, 0x0a, 0x05, 0x48, 0x6f, 0x73, 0x74,
+	0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e,
+	0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x02,
+	0x18, 0x01, 0x52, 0x05, 0x68, 0x6f, 0x73, 0x74, 0x73, 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, 0x49, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63,
+	0x5f, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x76,
+	0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 0x1a, 0x5b, 0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72,
+	0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
+	0x6b, 0x65, 0x79, 0x12, 0x37, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
+	0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44,
+	0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
+	0x1a, 0x98, 0x01, 0x0a, 0x0b, 0x48, 0x6f, 0x73, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67,
+	0x12, 0x3a, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26,
+	0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 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, 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, 0x42, 0x34, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e,
+	0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x50, 0x01, 0x5a, 0x03,
+	0x64, 0x6e, 0x73, 0xaa, 0x02, 0x12, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65,
+	0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -459,34 +536,36 @@ func file_v2ray_com_core_app_dns_config_proto_rawDescGZIP() []byte {
 }
 
 var file_v2ray_com_core_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
-var file_v2ray_com_core_app_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
+var file_v2ray_com_core_app_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
 var file_v2ray_com_core_app_dns_config_proto_goTypes = []interface{}{
 	(DomainMatchingType)(0),           // 0: v2ray.core.app.dns.DomainMatchingType
 	(*NameServer)(nil),                // 1: v2ray.core.app.dns.NameServer
 	(*Config)(nil),                    // 2: v2ray.core.app.dns.Config
 	(*NameServer_PriorityDomain)(nil), // 3: v2ray.core.app.dns.NameServer.PriorityDomain
-	nil,                               // 4: v2ray.core.app.dns.Config.HostsEntry
-	(*Config_HostMapping)(nil),        // 5: v2ray.core.app.dns.Config.HostMapping
-	(*net.Endpoint)(nil),              // 6: v2ray.core.common.net.Endpoint
-	(*router.GeoIP)(nil),              // 7: v2ray.core.app.router.GeoIP
-	(*net.IPOrDomain)(nil),            // 8: v2ray.core.common.net.IPOrDomain
+	(*NameServer_OriginalRule)(nil),   // 4: v2ray.core.app.dns.NameServer.OriginalRule
+	nil,                               // 5: v2ray.core.app.dns.Config.HostsEntry
+	(*Config_HostMapping)(nil),        // 6: v2ray.core.app.dns.Config.HostMapping
+	(*net.Endpoint)(nil),              // 7: v2ray.core.common.net.Endpoint
+	(*router.GeoIP)(nil),              // 8: v2ray.core.app.router.GeoIP
+	(*net.IPOrDomain)(nil),            // 9: v2ray.core.common.net.IPOrDomain
 }
 var file_v2ray_com_core_app_dns_config_proto_depIdxs = []int32{
-	6,  // 0: v2ray.core.app.dns.NameServer.address:type_name -> v2ray.core.common.net.Endpoint
+	7,  // 0: v2ray.core.app.dns.NameServer.address:type_name -> v2ray.core.common.net.Endpoint
 	3,  // 1: v2ray.core.app.dns.NameServer.prioritized_domain:type_name -> v2ray.core.app.dns.NameServer.PriorityDomain
-	7,  // 2: v2ray.core.app.dns.NameServer.geoip:type_name -> v2ray.core.app.router.GeoIP
-	6,  // 3: v2ray.core.app.dns.Config.NameServers:type_name -> v2ray.core.common.net.Endpoint
-	1,  // 4: v2ray.core.app.dns.Config.name_server:type_name -> v2ray.core.app.dns.NameServer
-	4,  // 5: v2ray.core.app.dns.Config.Hosts:type_name -> v2ray.core.app.dns.Config.HostsEntry
-	5,  // 6: v2ray.core.app.dns.Config.static_hosts:type_name -> v2ray.core.app.dns.Config.HostMapping
-	0,  // 7: v2ray.core.app.dns.NameServer.PriorityDomain.type:type_name -> v2ray.core.app.dns.DomainMatchingType
-	8,  // 8: v2ray.core.app.dns.Config.HostsEntry.value:type_name -> v2ray.core.common.net.IPOrDomain
-	0,  // 9: v2ray.core.app.dns.Config.HostMapping.type:type_name -> v2ray.core.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,  // 2: v2ray.core.app.dns.NameServer.geoip:type_name -> v2ray.core.app.router.GeoIP
+	4,  // 3: v2ray.core.app.dns.NameServer.original_rules:type_name -> v2ray.core.app.dns.NameServer.OriginalRule
+	7,  // 4: v2ray.core.app.dns.Config.NameServers:type_name -> v2ray.core.common.net.Endpoint
+	1,  // 5: v2ray.core.app.dns.Config.name_server:type_name -> v2ray.core.app.dns.NameServer
+	5,  // 6: v2ray.core.app.dns.Config.Hosts:type_name -> v2ray.core.app.dns.Config.HostsEntry
+	6,  // 7: v2ray.core.app.dns.Config.static_hosts:type_name -> v2ray.core.app.dns.Config.HostMapping
+	0,  // 8: v2ray.core.app.dns.NameServer.PriorityDomain.type:type_name -> v2ray.core.app.dns.DomainMatchingType
+	9,  // 9: v2ray.core.app.dns.Config.HostsEntry.value:type_name -> v2ray.core.common.net.IPOrDomain
+	0,  // 10: v2ray.core.app.dns.Config.HostMapping.type:type_name -> v2ray.core.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_v2ray_com_core_app_dns_config_proto_init() }
@@ -531,7 +610,19 @@ func file_v2ray_com_core_app_dns_config_proto_init() {
 				return nil
 			}
 		}
-		file_v2ray_com_core_app_dns_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_v2ray_com_core_app_dns_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*NameServer_OriginalRule); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_v2ray_com_core_app_dns_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*Config_HostMapping); i {
 			case 0:
 				return &v.state
@@ -550,7 +641,7 @@ func file_v2ray_com_core_app_dns_config_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_v2ray_com_core_app_dns_config_proto_rawDesc,
 			NumEnums:      1,
-			NumMessages:   5,
+			NumMessages:   6,
 			NumExtensions: 0,
 			NumServices:   0,
 		},

+ 6 - 0
app/dns/config.proto

@@ -18,8 +18,14 @@ message NameServer {
     string domain = 2;
   }
 
+  message OriginalRule {
+    string rule = 1;
+    uint32 size = 2;
+  }
+
   repeated PriorityDomain prioritized_domain = 2;
   repeated v2ray.core.app.router.GeoIP geoip = 3;
+  repeated OriginalRule original_rules = 4;
 }
 
 enum DomainMatchingType {

+ 75 - 24
app/dns/server.go

@@ -6,6 +6,7 @@ package dns
 
 import (
 	"context"
+	"fmt"
 	"log"
 	"net/url"
 	"strings"
@@ -28,13 +29,20 @@ import (
 // Server is a DNS rely server.
 type Server struct {
 	sync.Mutex
-	hosts          *StaticHosts
-	clients        []Client
-	clientIP       net.IP
-	domainMatcher  strmatcher.IndexMatcher
-	domainIndexMap map[uint32]uint32
-	ipIndexMap     map[uint32]*MultiGeoIPMatcher
-	tag            string
+	hosts         *StaticHosts
+	clientIP      net.IP
+	clients       []Client             // clientIdx -> Client
+	ipIndexMap    []*MultiGeoIPMatcher // clientIdx -> *MultiGeoIPMatcher
+	domainRules   [][]string           // clientIdx -> domainRuleIdx -> DomainRule
+	domainMatcher strmatcher.IndexMatcher
+	matcherInfos  []DomainMatcherInfo // matcherIdx -> DomainMatcherInfo
+	tag           string
+}
+
+// DomainMatcherInfo contains information attached to index returned by Server.domainMatcher
+type DomainMatcherInfo struct {
+	clientIdx     uint16
+	domainRuleIdx uint16
 }
 
 // MultiGeoIPMatcher for match
@@ -139,6 +147,7 @@ func New(ctx context.Context, config *Config) (*Server, error) {
 				}))
 			}
 		}
+		server.ipIndexMap = append(server.ipIndexMap, nil)
 		return len(server.clients) - 1
 	}
 
@@ -150,22 +159,54 @@ func New(ctx context.Context, config *Config) (*Server, error) {
 	}
 
 	if len(config.NameServer) > 0 {
-		domainMatcher := &strmatcher.MatcherGroup{}
-		domainIndexMap := make(map[uint32]uint32)
-		ipIndexMap := make(map[uint32]*MultiGeoIPMatcher)
-		var geoIPMatcherContainer router.GeoIPMatcherContainer
-
+		clientIndices := []int{}
+		domainRuleCount := 0
 		for _, ns := range config.NameServer {
 			idx := addNameServer(ns)
+			clientIndices = append(clientIndices, idx)
+			domainRuleCount += len(ns.PrioritizedDomain)
+		}
+
+		domainRules := make([][]string, len(server.clients))
+		domainMatcher := &strmatcher.MatcherGroup{}
+		matcherInfos := make([]DomainMatcherInfo, domainRuleCount+1) // matcher index starts from 1
+		var geoIPMatcherContainer router.GeoIPMatcherContainer
+		for nidx, ns := range config.NameServer {
+			idx := clientIndices[nidx]
 
+			// Establish domain rule matcher
+			rules := []string{}
+			ruleCurr := 0
+			ruleIter := 0
 			for _, domain := range ns.PrioritizedDomain {
 				matcher, err := toStrMatcher(domain.Type, domain.Domain)
 				if err != nil {
 					return nil, newError("failed to create prioritized domain").Base(err).AtWarning()
 				}
 				midx := domainMatcher.Add(matcher)
-				domainIndexMap[midx] = uint32(idx)
+				if midx >= uint32(len(matcherInfos)) { // This rarely happens according to current matcher's implementation
+					newError("expanding domain matcher info array to size ", midx, " when adding ", matcher).AtDebug().WriteToLog()
+					matcherInfos = append(matcherInfos, make([]DomainMatcherInfo, midx-uint32(len(matcherInfos))+1)...)
+				}
+				info := &matcherInfos[midx]
+				info.clientIdx = uint16(idx)
+				if ruleCurr < len(ns.OriginalRules) {
+					info.domainRuleIdx = uint16(ruleCurr)
+					rule := ns.OriginalRules[ruleCurr]
+					if ruleCurr >= len(rules) {
+						rules = append(rules, rule.Rule)
+					}
+					ruleIter++
+					if ruleIter >= int(rule.Size) {
+						ruleIter = 0
+						ruleCurr++
+					}
+				} else { // No original rule, generate one according to current domain matcher (majorly for compability with tests)
+					info.domainRuleIdx = uint16(len(rules))
+					rules = append(rules, matcher.String())
+				}
 			}
+			domainRules[idx] = rules
 
 			// only add to ipIndexMap if GeoIP is configured
 			if len(ns.Geoip) > 0 {
@@ -178,13 +219,12 @@ func New(ctx context.Context, config *Config) (*Server, error) {
 					matchers = append(matchers, matcher)
 				}
 				matcher := &MultiGeoIPMatcher{matchers: matchers}
-				ipIndexMap[uint32(idx)] = matcher
+				server.ipIndexMap[idx] = matcher
 			}
 		}
-
+		server.domainRules = domainRules
 		server.domainMatcher = domainMatcher
-		server.domainIndexMap = domainIndexMap
-		server.ipIndexMap = ipIndexMap
+		server.matcherInfos = matcherInfos
 	}
 
 	if len(server.clients) == 0 {
@@ -215,9 +255,9 @@ func (s *Server) IsOwnLink(ctx context.Context) bool {
 }
 
 // Match check dns ip match geoip
-func (s *Server) Match(idx uint32, client Client, domain string, ips []net.IP) ([]net.IP, error) {
-	matcher, exist := s.ipIndexMap[idx]
-	if !exist {
+func (s *Server) Match(idx int, client Client, domain string, ips []net.IP) ([]net.IP, error) {
+	matcher := s.ipIndexMap[idx]
+	if matcher == nil {
 		return ips, nil
 	}
 
@@ -239,7 +279,7 @@ func (s *Server) Match(idx uint32, client Client, domain string, ips []net.IP) (
 	return newIps, nil
 }
 
-func (s *Server) queryIPTimeout(idx uint32, client Client, domain string, option IPOption) ([]net.IP, error) {
+func (s *Server) queryIPTimeout(idx int, client Client, domain string, option IPOption) ([]net.IP, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second*4)
 	if len(s.tag) > 0 {
 		ctx = session.ContextWithInbound(ctx, &session.Inbound{
@@ -331,9 +371,20 @@ func (s *Server) lookupIPInternal(domain string, option IPOption) ([]net.IP, err
 	var matchedClient Client
 	if s.domainMatcher != nil {
 		indices := s.domainMatcher.Match(domain)
+		domainRules := []string{}
+		matchingDNS := []string{}
+		for _, idx := range indices {
+			info := s.matcherInfos[idx]
+			rule := s.domainRules[info.clientIdx][info.domainRuleIdx]
+			domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", rule, info.clientIdx))
+			matchingDNS = append(matchingDNS, s.clients[info.clientIdx].Name())
+		}
+		newError("domain ", domain, " matching following rules: ", domainRules).AtDebug().WriteToLog()
+		newError("domain ", domain, " uses following DNS first: ", matchingDNS).AtDebug().WriteToLog()
 		for _, idx := range indices {
-			matchedClient = s.clients[s.domainIndexMap[idx]]
-			ips, err := s.queryIPTimeout(s.domainIndexMap[idx], matchedClient, domain, option)
+			clientIdx := int(s.matcherInfos[idx].clientIdx)
+			matchedClient = s.clients[clientIdx]
+			ips, err := s.queryIPTimeout(clientIdx, matchedClient, domain, option)
 			if len(ips) > 0 {
 				return ips, nil
 			}
@@ -353,7 +404,7 @@ func (s *Server) lookupIPInternal(domain string, option IPOption) ([]net.IP, err
 			continue
 		}
 
-		ips, err := s.queryIPTimeout(uint32(idx), client, domain, option)
+		ips, err := s.queryIPTimeout(idx, client, domain, option)
 		if len(ips) > 0 {
 			return ips, nil
 		}

+ 16 - 0
common/strmatcher/matchers.go

@@ -11,12 +11,20 @@ func (m fullMatcher) Match(s string) bool {
 	return string(m) == s
 }
 
+func (m fullMatcher) String() string {
+	return "full:" + string(m)
+}
+
 type substrMatcher string
 
 func (m substrMatcher) Match(s string) bool {
 	return strings.Contains(s, string(m))
 }
 
+func (m substrMatcher) String() string {
+	return "keyword:" + string(m)
+}
+
 type domainMatcher string
 
 func (m domainMatcher) Match(s string) bool {
@@ -27,6 +35,10 @@ func (m domainMatcher) Match(s string) bool {
 	return len(s) == len(pattern) || s[len(s)-len(pattern)-1] == '.'
 }
 
+func (m domainMatcher) String() string {
+	return "domain:" + string(m)
+}
+
 type regexMatcher struct {
 	pattern *regexp.Regexp
 }
@@ -34,3 +46,7 @@ type regexMatcher struct {
 func (m *regexMatcher) Match(s string) bool {
 	return m.pattern.MatchString(s)
 }
+
+func (m *regexMatcher) String() string {
+	return "regexp:" + m.pattern.String()
+}

+ 1 - 0
common/strmatcher/strmatcher.go

@@ -8,6 +8,7 @@ import (
 type Matcher interface {
 	// Match returns true if the given string matches a predefined pattern.
 	Match(string) bool
+	String() string
 }
 
 // Type is the type of the matcher.

+ 22 - 3
infra/conf/dns.go

@@ -62,11 +62,12 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
 	}
 
 	var domains []*dns.NameServer_PriorityDomain
+	var originalRules []*dns.NameServer_OriginalRule
 
-	for _, d := range c.Domains {
-		parsedDomain, err := parseDomainRule(d)
+	for _, rule := range c.Domains {
+		parsedDomain, err := parseDomainRule(rule)
 		if err != nil {
-			return nil, newError("invalid domain rule: ", d).Base(err)
+			return nil, newError("invalid domain rule: ", rule).Base(err)
 		}
 
 		for _, pd := range parsedDomain {
@@ -75,6 +76,10 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
 				Domain: pd.Value,
 			})
 		}
+		originalRules = append(originalRules, &dns.NameServer_OriginalRule{
+			Rule: rule,
+			Size: uint32(len(parsedDomain)),
+		})
 	}
 
 	geoipList, err := toCidrList(c.ExpectIPs)
@@ -90,6 +95,7 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) {
 		},
 		PrioritizedDomain: domains,
 		Geoip:             geoipList,
+		OriginalRules:     originalRules,
 	}, nil
 }
 
@@ -186,6 +192,19 @@ func (c *DnsConfig) Build() (*dns.Config, error) {
 				mapping.Domain = domain[5:]
 
 				mappings = append(mappings, mapping)
+			} else if strings.HasPrefix(domain, "dotless:") {
+				mapping := getHostMapping(addr)
+				mapping.Type = dns.DomainMatchingType_Regex
+				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)
 			} else if strings.HasPrefix(domain, "ext:") {
 				kv := strings.Split(domain[4:], ":")
 				if len(kv) != 2 {

+ 6 - 0
infra/conf/dns_test.go

@@ -94,6 +94,12 @@ func TestDnsConfigParsing(t *testing.T) {
 								Domain: "v2ray.com",
 							},
 						},
+						OriginalRules: []*dns.NameServer_OriginalRule{
+							{
+								Rule: "domain:v2ray.com",
+								Size: 1,
+							},
+						},
 					},
 				},
 				StaticHosts: []*dns.Config_HostMapping{

+ 1 - 1
infra/conf/router.go

@@ -307,7 +307,7 @@ func parseDomainRule(domain string) ([]*router.Domain, error) {
 		case !strings.Contains(substr, "."):
 			domainRule.Value = "^[^.]*" + substr + "[^.]*$"
 		default:
-			return nil, newError("Substr in dotless rule should not contain a dot: ", substr)
+			return nil, newError("substr in dotless rule should not contain a dot: ", substr)
 		}
 	default:
 		domainRule.Type = router.Domain_Plain