Browse Source

Merge branch 'dev-fakednsimprovement'

Shelikhoo 4 years ago
parent
commit
f5d2ddedb4

+ 5 - 0
app/dispatcher/default.go

@@ -186,6 +186,11 @@ func shouldOverride(result SniffResult, domainOverride []string) bool {
 		if strings.HasPrefix(protocolString, p) {
 		if strings.HasPrefix(protocolString, p) {
 			return true
 			return true
 		}
 		}
+		if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok {
+			if resultSubset.IsProtoSubsetOf(p) {
+				return true
+			}
+		}
 	}
 	}
 	return false
 	return false
 }
 }

+ 70 - 0
app/dispatcher/fakednssniffer.go

@@ -4,6 +4,7 @@ package dispatcher
 
 
 import (
 import (
 	"context"
 	"context"
+	"strings"
 
 
 	core "github.com/v2fly/v2ray-core/v4"
 	core "github.com/v2fly/v2ray-core/v4"
 	"github.com/v2fly/v2ray-core/v4/common"
 	"github.com/v2fly/v2ray-core/v4/common"
@@ -34,6 +35,15 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error)
 				return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
 				return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
 			}
 			}
 		}
 		}
+
+		if ipAddressInRangeValueI := ctx.Value(ipAddressInRange); ipAddressInRangeValueI != nil {
+			ipAddressInRangeValue := ipAddressInRangeValueI.(*ipAddressInRangeOpt)
+			if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok {
+				inPool := fkr0.IsIPInIPPool(Target.Address)
+				ipAddressInRangeValue.addressInRange = &inPool
+			}
+		}
+
 		return nil, common.ErrNoClue
 		return nil, common.ErrNoClue
 	}, metadataSniffer: true}, nil
 	}, metadataSniffer: true}, nil
 }
 }
@@ -49,3 +59,63 @@ func (fakeDNSSniffResult) Protocol() string {
 func (f fakeDNSSniffResult) Domain() string {
 func (f fakeDNSSniffResult) Domain() string {
 	return f.domainName
 	return f.domainName
 }
 }
+
+type fakeDnsExtraOpts int
+
+const ipAddressInRange fakeDnsExtraOpts = 1
+
+type ipAddressInRangeOpt struct {
+	addressInRange *bool
+}
+
+type DNSThenOthersSniffResult struct {
+	domainName           string
+	protocolOriginalName string
+}
+
+func (f DNSThenOthersSniffResult) IsProtoSubsetOf(protocolName string) bool {
+	if strings.HasPrefix(protocolName, f.protocolOriginalName) {
+		return true
+	}
+	return false
+}
+
+func (DNSThenOthersSniffResult) Protocol() string {
+	return "fakedns+others"
+}
+
+func (f DNSThenOthersSniffResult) Domain() string {
+	return f.domainName
+}
+
+func newFakeDNSThenOthers(ctx context.Context, fakeDNSSniffer protocolSnifferWithMetadata, others []protocolSnifferWithMetadata) (protocolSnifferWithMetadata, error) {
+	return protocolSnifferWithMetadata{
+		protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
+			ipAddressInRangeValue := &ipAddressInRangeOpt{}
+			ctx = context.WithValue(ctx, ipAddressInRange, ipAddressInRangeValue)
+			result, err := fakeDNSSniffer.protocolSniffer(ctx, bytes)
+			if err == nil {
+				return result, nil
+			}
+			if ipAddressInRangeValue.addressInRange != nil {
+				if *ipAddressInRangeValue.addressInRange == true {
+					for _, v := range others {
+						if v.metadataSniffer || bytes != nil {
+							if result, err := v.protocolSniffer(ctx, bytes); err == nil {
+								return DNSThenOthersSniffResult{domainName: result.Domain(), protocolOriginalName: result.Protocol()}, nil
+							}
+						}
+					}
+					return nil, common.ErrNoClue
+				} else {
+					newError("ip address not in fake dns range, return as is").AtDebug().WriteToLog()
+					return nil, common.ErrNoClue
+				}
+			} else {
+				newError("fake dns sniffer did not set address in range option, assume false.").AtWarning().WriteToLog()
+				return nil, common.ErrNoClue
+			}
+		},
+		metadataSniffer: false,
+	}, nil
+}

+ 9 - 0
app/dispatcher/sniffer.go

@@ -39,7 +39,12 @@ func NewSniffer(ctx context.Context) *Sniffer {
 		},
 		},
 	}
 	}
 	if sniffer, err := newFakeDNSSniffer(ctx); err == nil {
 	if sniffer, err := newFakeDNSSniffer(ctx); err == nil {
+		others := ret.sniffer
 		ret.sniffer = append(ret.sniffer, sniffer)
 		ret.sniffer = append(ret.sniffer, sniffer)
+		fakeDNSThenOthers, err := newFakeDNSThenOthers(ctx, sniffer, others)
+		if err == nil {
+			ret.sniffer = append([]protocolSnifferWithMetadata{fakeDNSThenOthers}, ret.sniffer...)
+		}
 	}
 	}
 	return ret
 	return ret
 }
 }
@@ -123,3 +128,7 @@ func (c compositeResult) ProtocolForDomainResult() string {
 type SnifferResultComposite interface {
 type SnifferResultComposite interface {
 	ProtocolForDomainResult() string
 	ProtocolForDomainResult() string
 }
 }
+
+type SnifferIsProtoSubsetOf interface {
+	IsProtoSubsetOf(protocolName string) bool
+}

+ 104 - 0
app/dns/fakedns/fake.go

@@ -23,6 +23,18 @@ type Holder struct {
 	config *FakeDnsPool
 	config *FakeDnsPool
 }
 }
 
 
+func (fkdns *Holder) IsIPInIPPool(ip net.Address) bool {
+	return fkdns.ipRange.Contains(ip.IP())
+}
+
+func (fkdns *Holder) GetFakeIPForDomain3(domain string, IPv4, IPv6 bool) []net.Address {
+	isIPv6 := fkdns.ipRange.IP.To4() == nil
+	if (isIPv6 && IPv6) || (!isIPv6 && IPv4) {
+		return fkdns.GetFakeIPForDomain(domain)
+	}
+	return []net.Address{}
+}
+
 func (*Holder) Type() interface{} {
 func (*Holder) Type() interface{} {
 	return (*dns.FakeDNSEngine)(nil)
 	return (*dns.FakeDNSEngine)(nil)
 }
 }
@@ -120,6 +132,89 @@ func (fkdns *Holder) GetDomainFromFakeDNS(ip net.Address) string {
 	return ""
 	return ""
 }
 }
 
 
+type HolderMulti struct {
+	holders []*Holder
+
+	config *FakeDnsPoolMulti
+}
+
+func (h *HolderMulti) IsIPInIPPool(ip net.Address) bool {
+	for _, v := range h.holders {
+		if v.IsIPInIPPool(ip) {
+			return true
+		}
+	}
+	return false
+}
+
+func (h *HolderMulti) GetFakeIPForDomain3(domain string, IPv4, IPv6 bool) []net.Address {
+	var ret []net.Address
+	for _, v := range h.holders {
+		ret = append(ret, v.GetFakeIPForDomain3(domain, IPv4, IPv6)...)
+	}
+	return ret
+}
+
+func (h *HolderMulti) GetFakeIPForDomain(domain string) []net.Address {
+	var ret []net.Address
+	for _, v := range h.holders {
+		ret = append(ret, v.GetFakeIPForDomain(domain)...)
+	}
+	return ret
+}
+
+func (h *HolderMulti) GetDomainFromFakeDNS(ip net.Address) string {
+	for _, v := range h.holders {
+		if domain := v.GetDomainFromFakeDNS(ip); domain != "" {
+			return domain
+		}
+	}
+	return ""
+}
+
+func (h *HolderMulti) Type() interface{} {
+	return (*dns.FakeDNSEngine)(nil)
+}
+
+func (h *HolderMulti) Start() error {
+	for _, v := range h.holders {
+		err := v.Start()
+		if err != nil {
+			return newError("Cannot start all fake dns pools").Base(err)
+		}
+	}
+	return nil
+}
+
+func (h *HolderMulti) Close() error {
+	for _, v := range h.holders {
+		err := v.Start()
+		if err != nil {
+			return newError("Cannot close all fake dns pools").Base(err)
+		}
+	}
+	return nil
+}
+
+func (h *HolderMulti) createHolderGroups() error {
+	for _, v := range h.config.Pools {
+		holder, err := NewFakeDNSHolderConfigOnly(v)
+		if err != nil {
+			return err
+		}
+		h.holders = append(h.holders, holder)
+	}
+	return nil
+}
+
+func NewFakeDNSHolderMulti(conf *FakeDnsPoolMulti) (*HolderMulti, error) {
+	holderMulti := &HolderMulti{nil, conf}
+	if err := holderMulti.createHolderGroups(); err != nil {
+		return nil, err
+	}
+	return holderMulti, nil
+}
+
 func init() {
 func init() {
 	common.Must(common.RegisterConfig((*FakeDnsPool)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
 	common.Must(common.RegisterConfig((*FakeDnsPool)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
 		var f *Holder
 		var f *Holder
@@ -129,4 +224,13 @@ func init() {
 		}
 		}
 		return f, nil
 		return f, nil
 	}))
 	}))
+
+	common.Must(common.RegisterConfig((*FakeDnsPoolMulti)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
+		var f *HolderMulti
+		var err error
+		if f, err = NewFakeDNSHolderMulti(config.(*FakeDnsPoolMulti)); err != nil {
+			return nil, err
+		}
+		return f, nil
+	}))
 }
 }

+ 83 - 17
app/dns/fakedns/fakedns.pb.go

@@ -75,6 +75,53 @@ func (x *FakeDnsPool) GetLruSize() int64 {
 	return 0
 	return 0
 }
 }
 
 
+type FakeDnsPoolMulti struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Pools []*FakeDnsPool `protobuf:"bytes,1,rep,name=pools,proto3" json:"pools,omitempty"`
+}
+
+func (x *FakeDnsPoolMulti) Reset() {
+	*x = FakeDnsPoolMulti{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FakeDnsPoolMulti) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FakeDnsPoolMulti) ProtoMessage() {}
+
+func (x *FakeDnsPoolMulti) ProtoReflect() protoreflect.Message {
+	mi := &file_app_dns_fakedns_fakedns_proto_msgTypes[1]
+	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 FakeDnsPoolMulti.ProtoReflect.Descriptor instead.
+func (*FakeDnsPoolMulti) Descriptor() ([]byte, []int) {
+	return file_app_dns_fakedns_fakedns_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *FakeDnsPoolMulti) GetPools() []*FakeDnsPool {
+	if x != nil {
+		return x.Pools
+	}
+	return nil
+}
+
 var File_app_dns_fakedns_fakedns_proto protoreflect.FileDescriptor
 var File_app_dns_fakedns_fakedns_proto protoreflect.FileDescriptor
 
 
 var file_app_dns_fakedns_fakedns_proto_rawDesc = []byte{
 var file_app_dns_fakedns_fakedns_proto_rawDesc = []byte{
@@ -85,15 +132,20 @@ var file_app_dns_fakedns_fakedns_proto_rawDesc = []byte{
 	0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x70,
 	0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x70,
 	0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x70, 0x50,
 	0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x70, 0x50,
 	0x6f, 0x6f, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x02,
 	0x6f, 0x6f, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x6f, 0x0a,
-	0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e,
-	0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x50,
-	0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32,
-	0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76,
-	0x34, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e,
-	0x73, 0xaa, 0x02, 0x1a, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41,
-	0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x2e, 0x46, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x62, 0x06,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x51, 0x0a,
+	0x10, 0x46, 0x61, 0x6b, 0x65, 0x44, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x4d, 0x75, 0x6c, 0x74,
+	0x69, 0x12, 0x3d, 0x0a, 0x05, 0x70, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x27, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70,
+	0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x2e, 0x46, 0x61,
+	0x6b, 0x65, 0x44, 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x52, 0x05, 0x70, 0x6f, 0x6f, 0x6c, 0x73,
+	0x42, 0x6f, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
+	0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64,
+	0x6e, 0x73, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
+	0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72,
+	0x65, 0x2f, 0x76, 0x34, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x64, 0x6e, 0x73, 0x2f, 0x66, 0x61, 0x6b,
+	0x65, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x1a, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72,
+	0x65, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x2e, 0x46, 0x61, 0x6b, 0x65, 0x64, 0x6e,
+	0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 }
 
 
 var (
 var (
@@ -108,16 +160,18 @@ func file_app_dns_fakedns_fakedns_proto_rawDescGZIP() []byte {
 	return file_app_dns_fakedns_fakedns_proto_rawDescData
 	return file_app_dns_fakedns_fakedns_proto_rawDescData
 }
 }
 
 
-var file_app_dns_fakedns_fakedns_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_app_dns_fakedns_fakedns_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
 var file_app_dns_fakedns_fakedns_proto_goTypes = []interface{}{
 var file_app_dns_fakedns_fakedns_proto_goTypes = []interface{}{
-	(*FakeDnsPool)(nil), // 0: v2ray.core.app.dns.fakedns.FakeDnsPool
+	(*FakeDnsPool)(nil),      // 0: v2ray.core.app.dns.fakedns.FakeDnsPool
+	(*FakeDnsPoolMulti)(nil), // 1: v2ray.core.app.dns.fakedns.FakeDnsPoolMulti
 }
 }
 var file_app_dns_fakedns_fakedns_proto_depIdxs = []int32{
 var file_app_dns_fakedns_fakedns_proto_depIdxs = []int32{
-	0, // [0:0] is the sub-list for method output_type
-	0, // [0:0] is the sub-list for method input_type
-	0, // [0:0] is the sub-list for extension type_name
-	0, // [0:0] is the sub-list for extension extendee
-	0, // [0:0] is the sub-list for field type_name
+	0, // 0: v2ray.core.app.dns.fakedns.FakeDnsPoolMulti.pools:type_name -> v2ray.core.app.dns.fakedns.FakeDnsPool
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
 }
 }
 
 
 func init() { file_app_dns_fakedns_fakedns_proto_init() }
 func init() { file_app_dns_fakedns_fakedns_proto_init() }
@@ -138,6 +192,18 @@ func file_app_dns_fakedns_fakedns_proto_init() {
 				return nil
 				return nil
 			}
 			}
 		}
 		}
+		file_app_dns_fakedns_fakedns_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*FakeDnsPoolMulti); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 	}
 	}
 	type x struct{}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
 	out := protoimpl.TypeBuilder{
@@ -145,7 +211,7 @@ func file_app_dns_fakedns_fakedns_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_app_dns_fakedns_fakedns_proto_rawDesc,
 			RawDescriptor: file_app_dns_fakedns_fakedns_proto_rawDesc,
 			NumEnums:      0,
 			NumEnums:      0,
-			NumMessages:   1,
+			NumMessages:   2,
 			NumExtensions: 0,
 			NumExtensions: 0,
 			NumServices:   0,
 			NumServices:   0,
 		},
 		},

+ 4 - 0
app/dns/fakedns/fakedns.proto

@@ -10,3 +10,7 @@ message FakeDnsPool{
   string ip_pool = 1; //CIDR of IP pool used as fake DNS IP
   string ip_pool = 1; //CIDR of IP pool used as fake DNS IP
   int64  lruSize = 2; //Size of Pool for remembering relationship between domain name and IP address
   int64  lruSize = 2; //Size of Pool for remembering relationship between domain name and IP address
 }
 }
+
+message FakeDnsPoolMulti{
+  repeated FakeDnsPool pools = 1;
+}

+ 77 - 0
app/dns/fakedns/fakedns_test.go

@@ -3,6 +3,8 @@ package fakedns
 import (
 import (
 	"testing"
 	"testing"
 
 
+	gonet "net"
+
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 
 
 	"github.com/v2fly/v2ray-core/v4/common"
 	"github.com/v2fly/v2ray-core/v4/common"
@@ -113,3 +115,78 @@ func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) {
 		}
 		}
 	}
 	}
 }
 }
+
+func TestFakeDNSMulti(t *testing.T) {
+	fakeMulti, err := NewFakeDNSHolderMulti(&FakeDnsPoolMulti{
+		Pools: []*FakeDnsPool{&FakeDnsPool{
+			IpPool:  "240.0.0.0/12",
+			LruSize: 256,
+		}, &FakeDnsPool{
+			IpPool:  "fddd:c5b4:ff5f:f4f0::/64",
+			LruSize: 256,
+		}},
+	},
+	)
+
+	err = fakeMulti.Start()
+
+	common.Must(err)
+
+	assert.Nil(t, err, "Should not throw error")
+	_ = fakeMulti
+
+	t.Run("checkInRange", func(t *testing.T) {
+		t.Run("ipv4", func(t *testing.T) {
+			inPool := fakeMulti.IsIPInIPPool(net.IPAddress([]byte{240, 0, 0, 5}))
+			assert.True(t, inPool)
+		})
+		t.Run("ipv6", func(t *testing.T) {
+			ip, err := gonet.ResolveIPAddr("ip", "fddd:c5b4:ff5f:f4f0::5")
+			assert.Nil(t, err)
+			inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP))
+			assert.True(t, inPool)
+		})
+		t.Run("ipv4_inverse", func(t *testing.T) {
+			inPool := fakeMulti.IsIPInIPPool(net.IPAddress([]byte{241, 0, 0, 5}))
+			assert.False(t, inPool)
+		})
+		t.Run("ipv6_inverse", func(t *testing.T) {
+			ip, err := gonet.ResolveIPAddr("ip", "fcdd:c5b4:ff5f:f4f0::5")
+			assert.Nil(t, err)
+			inPool := fakeMulti.IsIPInIPPool(net.IPAddress(ip.IP))
+			assert.False(t, inPool)
+		})
+	})
+
+	t.Run("allocateTwoAddressForTwoPool", func(t *testing.T) {
+		address := fakeMulti.GetFakeIPForDomain("fakednstest.v2fly.org")
+		assert.Len(t, address, 2, "should be 2 address one for each pool")
+		t.Run("eachOfThemShouldResolve:0", func(t *testing.T) {
+			domain := fakeMulti.GetDomainFromFakeDNS(address[0])
+			assert.Equal(t, "fakednstest.v2fly.org", domain)
+		})
+		t.Run("eachOfThemShouldResolve:1", func(t *testing.T) {
+			domain := fakeMulti.GetDomainFromFakeDNS(address[1])
+			assert.Equal(t, "fakednstest.v2fly.org", domain)
+		})
+	})
+
+	t.Run("understandIPTypeSelector", func(t *testing.T) {
+		t.Run("ipv4", func(t *testing.T) {
+			address := fakeMulti.GetFakeIPForDomain3("fakednstestipv4.v2fly.org", true, false)
+			assert.Len(t, address, 1, "should be 1 address")
+			assert.True(t, address[0].Family().IsIPv4())
+		})
+		t.Run("ipv6", func(t *testing.T) {
+			address := fakeMulti.GetFakeIPForDomain3("fakednstestipv6.v2fly.org", false, true)
+			assert.Len(t, address, 1, "should be 1 address")
+			assert.True(t, address[0].Family().IsIPv6())
+		})
+		t.Run("ipv46", func(t *testing.T) {
+			address := fakeMulti.GetFakeIPForDomain3("fakednstestipv46.v2fly.org", true, true)
+			assert.Len(t, address, 2, "should be 2 address")
+			assert.True(t, address[0].Family().IsIPv4())
+			assert.True(t, address[1].Family().IsIPv6())
+		})
+	})
+}

+ 7 - 2
app/dns/nameserver_fakedns.go

@@ -22,7 +22,7 @@ func (FakeDNSServer) Name() string {
 	return "FakeDNS"
 	return "FakeDNS"
 }
 }
 
 
-func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, _ dns.IPOption, _ bool) ([]net.IP, error) {
+func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, opt dns.IPOption, _ bool) ([]net.IP, error) {
 	if f.fakeDNSEngine == nil {
 	if f.fakeDNSEngine == nil {
 		if err := core.RequireFeatures(ctx, func(fd dns.FakeDNSEngine) {
 		if err := core.RequireFeatures(ctx, func(fd dns.FakeDNSEngine) {
 			f.fakeDNSEngine = fd
 			f.fakeDNSEngine = fd
@@ -30,7 +30,12 @@ func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, _
 			return nil, newError("Unable to locate a fake DNS Engine").Base(err).AtError()
 			return nil, newError("Unable to locate a fake DNS Engine").Base(err).AtError()
 		}
 		}
 	}
 	}
-	ips := f.fakeDNSEngine.GetFakeIPForDomain(domain)
+	var ips []net.Address
+	if fkr0, ok := f.fakeDNSEngine.(dns.FakeDNSEngineRev0); ok {
+		ips = fkr0.GetFakeIPForDomain3(domain, opt.IPv4Enable, opt.IPv6Enable)
+	} else {
+		ips = f.fakeDNSEngine.GetFakeIPForDomain(domain)
+	}
 
 
 	netIP, err := toNetIP(ips)
 	netIP, err := toNetIP(ips)
 	if err != nil {
 	if err != nil {

+ 5 - 0
features/dns/fakedns.go

@@ -10,3 +10,8 @@ type FakeDNSEngine interface {
 	GetFakeIPForDomain(domain string) []net.Address
 	GetFakeIPForDomain(domain string) []net.Address
 	GetDomainFromFakeDNS(ip net.Address) string
 	GetDomainFromFakeDNS(ip net.Address) string
 }
 }
+
+type FakeDNSEngineRev0 interface {
+	IsIPInIPPool(ip net.Address) bool
+	GetFakeIPForDomain3(domain string, IPv4, IPv6 bool) []net.Address
+}

+ 15 - 1
infra/conf/fakedns.go

@@ -6,12 +6,26 @@ import (
 	"github.com/v2fly/v2ray-core/v4/app/dns/fakedns"
 	"github.com/v2fly/v2ray-core/v4/app/dns/fakedns"
 )
 )
 
 
-type FakeDNSConfig struct {
+type FakeDNSPoolElementConfig struct {
 	IPPool  string `json:"ipPool"`
 	IPPool  string `json:"ipPool"`
 	LruSize int64  `json:"poolSize"`
 	LruSize int64  `json:"poolSize"`
 }
 }
 
 
+type FakeDNSConfig struct {
+	IPPool  string                      `json:"ipPool"`
+	LruSize int64                       `json:"poolSize"`
+	Pools   *[]FakeDNSPoolElementConfig `json:"pools,omitempty"`
+}
+
 func (f FakeDNSConfig) Build() (proto.Message, error) {
 func (f FakeDNSConfig) Build() (proto.Message, error) {
+	if f.Pools != nil {
+		fakeDNSPool := &fakedns.FakeDnsPoolMulti{}
+		for _, v := range *f.Pools {
+			fakeDNSPool.Pools = append(fakeDNSPool.Pools, &fakedns.FakeDnsPool{IpPool: v.IPPool, LruSize: v.LruSize})
+		}
+		return fakeDNSPool, nil
+	}
+
 	return &fakedns.FakeDnsPool{
 	return &fakedns.FakeDnsPool{
 		IpPool:  f.IPPool,
 		IpPool:  f.IPPool,
 		LruSize: f.LruSize,
 		LruSize: f.LruSize,

+ 2 - 0
infra/conf/v2ray.go

@@ -75,6 +75,8 @@ func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) {
 				p = append(p, "tls")
 				p = append(p, "tls")
 			case "fakedns":
 			case "fakedns":
 				p = append(p, "fakedns")
 				p = append(p, "fakedns")
+			case "fakedns+others":
+				p = append(p, "fakedns+others")
 			default:
 			default:
 				return nil, newError("unknown protocol: ", domainOverride)
 				return nil, newError("unknown protocol: ", domainOverride)
 			}
 			}