From 6b6974c804298def050300eec2397d2e29258590 Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Wed, 20 Oct 2021 01:15:49 -0400 Subject: [PATCH] Fakedns improvements (#731) Co-authored-by: Shelikhoo Co-authored-by: sixg0000d Co-authored-by: Loyalsoldier <10487845+Loyalsoldier@users.noreply.github.com> --- app/dispatcher/default.go | 13 ++-- app/dispatcher/fakednssniffer.go | 68 ++++++++++++++++++ app/dispatcher/sniffer.go | 9 +++ app/dns/dns.go | 4 +- app/dns/fakedns/fake.go | 120 +++++++++++++++++++++++++++++-- app/dns/fakedns/fakedns.pb.go | 96 +++++++++++++++++++++---- app/dns/fakedns/fakedns.proto | 4 ++ app/dns/fakedns/fakedns_test.go | 82 ++++++++++++++++++++- app/dns/nameserver_fakedns.go | 14 +++- features/dns/fakedns.go | 12 ++-- infra/conf/fakedns.go | 113 ++++++++++++++++++++++------- infra/conf/xray.go | 2 + 12 files changed, 478 insertions(+), 59 deletions(-) diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 026c5f27..306cd7b0 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -193,10 +193,15 @@ func shouldOverride(ctx context.Context, result SniffResult, request session.Sni if strings.HasPrefix(protocolString, p) { return true } - if fakeDNSEngine != nil && protocolString != "bittorrent" && p == "fakedns" && - destination.Address.Family().IsIP() && fakeDNSEngine.GetFakeIPRange().Contains(destination.Address.IP()) { - newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx)) - return true + if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok && protocolString != "bittorrent" && p == "fakedns" && + destination.Address.Family().IsIP() && fkr0.IsIPInIPPool(destination.Address) { + newError("Using sniffer ", protocolString, " since the fake DNS missed").WriteToLog(session.ExportIDToError(ctx)) + return true + } + if resultSubset, ok := result.(SnifferIsProtoSubsetOf); ok { + if resultSubset.IsProtoSubsetOf(p) { + return true + } } } diff --git a/app/dispatcher/fakednssniffer.go b/app/dispatcher/fakednssniffer.go index 7d26d975..9f91e7f0 100644 --- a/app/dispatcher/fakednssniffer.go +++ b/app/dispatcher/fakednssniffer.go @@ -2,6 +2,7 @@ package dispatcher import ( "context" + "strings" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/net" @@ -32,6 +33,15 @@ func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error) 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 }, metadataSniffer: true}, nil } @@ -47,3 +57,61 @@ func (fakeDNSSniffResult) Protocol() string { func (f fakeDNSSniffResult) Domain() string { 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 { + return strings.HasPrefix(protocolName, f.protocolOriginalName) +} + +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) { // nolint: unparam + // ctx may be used in the future + _ = ctx + 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 { + 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 + } + newError("ip address not in fake dns range, return as is").AtDebug().WriteToLog() + return nil, common.ErrNoClue + } + newError("fake dns sniffer did not set address in range option, assume false.").AtWarning().WriteToLog() + return nil, common.ErrNoClue + }, + metadataSniffer: false, + }, nil +} diff --git a/app/dispatcher/sniffer.go b/app/dispatcher/sniffer.go index 988d3079..4cb83041 100644 --- a/app/dispatcher/sniffer.go +++ b/app/dispatcher/sniffer.go @@ -37,7 +37,12 @@ func NewSniffer(ctx context.Context) *Sniffer { }, } if sniffer, err := newFakeDNSSniffer(ctx); err == nil { + others := ret.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 } @@ -121,3 +126,7 @@ func (c compositeResult) ProtocolForDomainResult() string { type SnifferResultComposite interface { ProtocolForDomainResult() string } + +type SnifferIsProtoSubsetOf interface { + IsProtoSubsetOf(protocolName string) bool +} diff --git a/app/dns/dns.go b/app/dns/dns.go index 112c14b6..a669bf10 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -29,6 +29,7 @@ type DNS struct { ipOption *dns.IPOption hosts *StaticHosts clients []*Client + ctx context.Context domainMatcher strmatcher.IndexMatcher matcherInfos []*DomainMatcherInfo } @@ -136,6 +137,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) { hosts: hosts, ipOption: ipOption, clients: clients, + ctx: ctx, domainMatcher: domainMatcher, matcherInfos: matcherInfos, disableCache: config.DisableCache, @@ -199,7 +201,7 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) { // Name servers lookup errs := []error{} - ctx := session.ContextWithInbound(context.Background(), &session.Inbound{Tag: s.tag}) + ctx := session.ContextWithInbound(s.ctx, &session.Inbound{Tag: s.tag}) for _, client := range s.sortClients(domain) { if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") { newError("skip DNS resolution for domain ", domain, " at server ", client.Name()).AtDebug().WriteToLog() diff --git a/app/dns/fakedns/fake.go b/app/dns/fakedns/fake.go index 1b5ec7d4..01cee465 100644 --- a/app/dns/fakedns/fake.go +++ b/app/dns/fakedns/fake.go @@ -20,12 +20,30 @@ type Holder struct { config *FakeDnsPool } +func (fkdns *Holder) IsIPInIPPool(ip net.Address) bool { + if ip.Family().IsDomain() { + return false + } + 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{} { return (*dns.FakeDNSEngine)(nil) } func (fkdns *Holder) Start() error { - return fkdns.initializeFromConfig() + if fkdns.config != nil && fkdns.config.IpPool != "" && fkdns.config.LruSize != 0 { + return fkdns.initializeFromConfig() + } + return newError("invalid fakeDNS setting") } func (fkdns *Holder) Close() error { @@ -41,7 +59,7 @@ func NewFakeDNSHolder() (*Holder, error) { if fkdns, err = NewFakeDNSHolderConfigOnly(nil); err != nil { return nil, newError("Unable to create Fake Dns Engine").Base(err).AtError() } - err = fkdns.initialize(dns.FakeIPPool, 65535) + err = fkdns.initialize(dns.FakeIPv4Pool, 65535) if err != nil { return nil, err } @@ -117,9 +135,92 @@ func (fkdns *Holder) GetDomainFromFakeDNS(ip net.Address) string { return "" } -// GetFakeIPRange return fake IP range from configuration -func (fkdns *Holder) GetFakeIPRange() *gonet.IPNet { - return fkdns.ipRange +type HolderMulti struct { + holders []*Holder + + config *FakeDnsPoolMulti +} + +func (h *HolderMulti) IsIPInIPPool(ip net.Address) bool { + if ip.Family().IsDomain() { + return false + } + 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 { + if v.config != nil && v.config.IpPool != "" && v.config.LruSize != 0 { + if err := v.Start(); err != nil { + return newError("Cannot start all fake dns pools").Base(err) + } + } else { + return newError("invalid fakeDNS setting") + } + } + return nil +} + +func (h *HolderMulti) Close() error { + for _, v := range h.holders { + if err := v.Close(); 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() { @@ -131,4 +232,13 @@ func init() { } 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 + })) } diff --git a/app/dns/fakedns/fakedns.pb.go b/app/dns/fakedns/fakedns.pb.go index ba1fb9ef..674dd497 100644 --- a/app/dns/fakedns/fakedns.pb.go +++ b/app/dns/fakedns/fakedns.pb.go @@ -75,6 +75,53 @@ func (x *FakeDnsPool) GetLruSize() int64 { 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_rawDesc = []byte{ @@ -85,13 +132,18 @@ var file_app_dns_fakedns_fakedns_proto_rawDesc = []byte{ 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, 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, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, - 0x64, 0x6e, 0x73, 0x50, 0x01, 0x5a, 0x29, 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, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, - 0xaa, 0x02, 0x14, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x44, 0x6e, 0x73, 0x2e, - 0x46, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6c, 0x72, 0x75, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x4b, 0x0a, 0x10, 0x46, 0x61, 0x6b, 0x65, 0x44, + 0x6e, 0x73, 0x50, 0x6f, 0x6f, 0x6c, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x12, 0x37, 0x0a, 0x05, 0x70, + 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x72, 0x61, + 0x79, 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, 0x5e, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, + 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, + 0x50, 0x01, 0x5a, 0x29, 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, 0x2f, 0x66, 0x61, 0x6b, 0x65, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x14, + 0x58, 0x72, 0x61, 0x79, 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 ( @@ -106,16 +158,18 @@ func file_app_dns_fakedns_fakedns_proto_rawDescGZIP() []byte { 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{}{ - (*FakeDnsPool)(nil), // 0: xray.app.dns.fakedns.FakeDnsPool + (*FakeDnsPool)(nil), // 0: xray.app.dns.fakedns.FakeDnsPool + (*FakeDnsPoolMulti)(nil), // 1: xray.app.dns.fakedns.FakeDnsPoolMulti } 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: xray.app.dns.fakedns.FakeDnsPoolMulti.pools:type_name -> xray.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() } @@ -136,6 +190,18 @@ func file_app_dns_fakedns_fakedns_proto_init() { 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{} out := protoimpl.TypeBuilder{ @@ -143,7 +209,7 @@ func file_app_dns_fakedns_fakedns_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_app_dns_fakedns_fakedns_proto_rawDesc, NumEnums: 0, - NumMessages: 1, + NumMessages: 2, NumExtensions: 0, NumServices: 0, }, diff --git a/app/dns/fakedns/fakedns.proto b/app/dns/fakedns/fakedns.proto index 69a344ac..aa168aaf 100644 --- a/app/dns/fakedns/fakedns.proto +++ b/app/dns/fakedns/fakedns.proto @@ -9,4 +9,8 @@ option java_multiple_files = true; message FakeDnsPool{ 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 +} + +message FakeDnsPoolMulti{ + repeated FakeDnsPool pools = 1; } \ No newline at end of file diff --git a/app/dns/fakedns/fakedns_test.go b/app/dns/fakedns/fakedns_test.go index 434c4451..a5553170 100644 --- a/app/dns/fakedns/fakedns_test.go +++ b/app/dns/fakedns/fakedns_test.go @@ -3,6 +3,8 @@ package fakedns import ( "testing" + gonet "net" + "github.com/stretchr/testify/assert" "github.com/xtls/xray-core/common" @@ -11,7 +13,7 @@ import ( "github.com/xtls/xray-core/features/dns" ) -var ipPrefix = "198.18." +var ipPrefix = "198.1" func TestNewFakeDnsHolder(_ *testing.T) { _, err := NewFakeDNSHolder() @@ -67,7 +69,7 @@ func TestFakeDnsHolderCreateMappingManySingleDomain(t *testing.T) { func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) { fkdns, err := NewFakeDNSHolderConfigOnly(&FakeDnsPool{ - IpPool: dns.FakeIPPool, + IpPool: dns.FakeIPv4Pool, LruSize: 256, }) common.Must(err) @@ -101,3 +103,79 @@ func TestFakeDnsHolderCreateMappingAndRollOver(t *testing.T) { } } } + +func TestFakeDNSMulti(t *testing.T) { + fakeMulti, err := NewFakeDNSHolderMulti(&FakeDnsPoolMulti{ + Pools: []*FakeDnsPool{{ + IpPool: "240.0.0.0/12", + LruSize: 256, + }, { + IpPool: "fddd:c5b4:ff5f:f4f0::/64", + LruSize: 256, + }}, + }, + ) + common.Must(err) + + 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()) + }) + }) +} diff --git a/app/dns/nameserver_fakedns.go b/app/dns/nameserver_fakedns.go index 54476ac2..44dcb01c 100644 --- a/app/dns/nameserver_fakedns.go +++ b/app/dns/nameserver_fakedns.go @@ -20,7 +20,7 @@ func (FakeDNSServer) Name() string { 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 err := core.RequireFeatures(ctx, func(fd dns.FakeDNSEngine) { f.fakeDNSEngine = fd @@ -28,7 +28,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() } } - 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) if err != nil { @@ -37,5 +42,8 @@ func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, _ newError(f.Name(), " got answer: ", domain, " -> ", ips).AtInfo().WriteToLog() - return netIP, nil + if len(netIP) > 0 { + return netIP, nil + } + return nil, dns.ErrEmptyResponse } diff --git a/features/dns/fakedns.go b/features/dns/fakedns.go index 12a7b41d..462bcccf 100644 --- a/features/dns/fakedns.go +++ b/features/dns/fakedns.go @@ -1,8 +1,6 @@ package dns import ( - gonet "net" - "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/features" ) @@ -11,7 +9,13 @@ type FakeDNSEngine interface { features.Feature GetFakeIPForDomain(domain string) []net.Address GetDomainFromFakeDNS(ip net.Address) string - GetFakeIPRange() *gonet.IPNet } -var FakeIPPool = "198.18.0.0/16" +var FakeIPv4Pool = "198.18.0.0/15" +var FakeIPv6Pool = "fc00::/18" + +type FakeDNSEngineRev0 interface { + FakeDNSEngine + IsIPInIPPool(ip net.Address) bool + GetFakeIPForDomain3(domain string, IPv4, IPv6 bool) []net.Address +} diff --git a/infra/conf/fakedns.go b/infra/conf/fakedns.go index 10f63e89..e730acdf 100644 --- a/infra/conf/fakedns.go +++ b/infra/conf/fakedns.go @@ -1,65 +1,128 @@ package conf import ( - "github.com/golang/protobuf/proto" + "encoding/json" + "strings" "github.com/xtls/xray-core/app/dns/fakedns" "github.com/xtls/xray-core/features/dns" ) -type FakeDNSConfig struct { +type FakeDNSPoolElementConfig struct { IPPool string `json:"ipPool"` - LruSize int64 `json:"poolSize"` + LRUSize int64 `json:"poolSize"` } -func (f FakeDNSConfig) Build() (proto.Message, error) { - return &fakedns.FakeDnsPool{ - IpPool: f.IPPool, - LruSize: f.LruSize, - }, nil +type FakeDNSConfig struct { + pool *FakeDNSPoolElementConfig + pools []*FakeDNSPoolElementConfig +} + +// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON +func (f *FakeDNSConfig) UnmarshalJSON(data []byte) error { + var pool FakeDNSPoolElementConfig + var pools []*FakeDNSPoolElementConfig + switch { + case json.Unmarshal(data, &pool) == nil: + f.pool = &pool + case json.Unmarshal(data, &pools) == nil: + f.pools = pools + default: + return newError("invalid fakedns config") + } + return nil +} + +func (f *FakeDNSConfig) Build() (*fakedns.FakeDnsPoolMulti, error) { + fakeDNSPool := fakedns.FakeDnsPoolMulti{} + + if f.pool != nil { + fakeDNSPool.Pools = append(fakeDNSPool.Pools, &fakedns.FakeDnsPool{ + IpPool: f.pool.IPPool, + LruSize: f.pool.LRUSize, + }) + return &fakeDNSPool, nil + } + + if f.pools != nil { + for _, v := range f.pools { + fakeDNSPool.Pools = append(fakeDNSPool.Pools, &fakedns.FakeDnsPool{IpPool: v.IPPool, LruSize: v.LRUSize}) + } + return &fakeDNSPool, nil + } + + return nil, newError("no valid FakeDNS config") } type FakeDNSPostProcessingStage struct{} -func (FakeDNSPostProcessingStage) Process(conf *Config) error { - var fakeDNSInUse bool +func (FakeDNSPostProcessingStage) Process(config *Config) error { + fakeDNSInUse := false + isIPv4Enable, isIPv6Enable := true, true - if conf.DNSConfig != nil { - for _, v := range conf.DNSConfig.Servers { - if v.Address.Family().IsDomain() { - if v.Address.Domain() == "fakedns" { - fakeDNSInUse = true - } + if config.DNSConfig != nil { + for _, v := range config.DNSConfig.Servers { + if v.Address.Family().IsDomain() && strings.EqualFold(v.Address.Domain(), "fakedns") { + fakeDNSInUse = true } } + + switch strings.ToLower(config.DNSConfig.QueryStrategy) { + case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": + isIPv4Enable, isIPv6Enable = true, false + case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": + isIPv4Enable, isIPv6Enable = false, true + } } if fakeDNSInUse { - if conf.FakeDNS == nil { - // Add a Fake DNS Config if there is none - conf.FakeDNS = &FakeDNSConfig{ - IPPool: dns.FakeIPPool, - LruSize: 65535, + // Add a Fake DNS Config if there is none + if config.FakeDNS == nil { + config.FakeDNS = &FakeDNSConfig{} + switch { + case isIPv4Enable && isIPv6Enable: + config.FakeDNS.pools = []*FakeDNSPoolElementConfig{ + { + IPPool: dns.FakeIPv4Pool, + LRUSize: 32768, + }, + { + IPPool: dns.FakeIPv6Pool, + LRUSize: 32768, + }, + } + case !isIPv4Enable && isIPv6Enable: + config.FakeDNS.pool = &FakeDNSPoolElementConfig{ + IPPool: dns.FakeIPv6Pool, + LRUSize: 65535, + } + case isIPv4Enable && !isIPv6Enable: + config.FakeDNS.pool = &FakeDNSPoolElementConfig{ + IPPool: dns.FakeIPv4Pool, + LRUSize: 65535, + } } } + found := false // Check if there is a Outbound with necessary sniffer on var inbounds []InboundDetourConfig - if len(conf.InboundConfigs) > 0 { - inbounds = append(inbounds, conf.InboundConfigs...) + if len(config.InboundConfigs) > 0 { + inbounds = append(inbounds, config.InboundConfigs...) } for _, v := range inbounds { if v.SniffingConfig != nil && v.SniffingConfig.Enabled && v.SniffingConfig.DestOverride != nil { for _, dov := range *v.SniffingConfig.DestOverride { - if dov == "fakedns" { + if strings.EqualFold(dov, "fakedns") || strings.EqualFold(dov, "fakedns+others") { found = true + break } } } } if !found { - newError("Defined Fake DNS but haven't enabled fake dns sniffing at any inbound.").AtWarning().WriteToLog() + newError("Defined FakeDNS but haven't enabled FakeDNS destOverride at any inbound.").AtWarning().WriteToLog() } } diff --git a/infra/conf/xray.go b/infra/conf/xray.go index 9ea77828..8e7290a0 100644 --- a/infra/conf/xray.go +++ b/infra/conf/xray.go @@ -78,6 +78,8 @@ func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) { p = append(p, "tls") case "fakedns": p = append(p, "fakedns") + case "fakedns+others": + p = append(p, "fakedns+others") default: return nil, newError("unknown protocol: ", protocol) }