From 726a72201929c28ac1b32abcd9d84958fb9b9c54 Mon Sep 17 00:00:00 2001 From: JimhHan <50871214+JimhHan@users.noreply.github.com> Date: Fri, 9 Apr 2021 23:36:36 +0800 Subject: [PATCH 1/5] Refine DNS strategies --- app/dns/config.pb.go | 140 ++++++++++++++++++++-------- app/dns/config.proto | 8 +- app/dns/dns.go | 75 ++++++++------- app/dns/nameserver.go | 6 +- app/dns/nameserver_doh.go | 12 ++- app/dns/nameserver_doh_test.go | 6 +- app/dns/nameserver_fakedns.go | 6 +- app/dns/nameserver_local.go | 2 +- app/dns/nameserver_local_test.go | 2 +- app/dns/nameserver_quic.go | 12 ++- app/dns/nameserver_quic_test.go | 6 +- app/dns/nameserver_udp.go | 12 ++- app/dns/options.go | 24 +++++ features/dns/client.go | 25 +++-- features/dns/localdns/client.go | 5 + features/routing/dns/context.go | 32 +------ infra/conf/dns.go | 18 +++- infra/conf/dns_test.go | 2 +- proxy/dns/dns.go | 29 ++---- proxy/freedom/freedom.go | 19 +--- transport/internet/system_dialer.go | 22 ++--- 21 files changed, 255 insertions(+), 208 deletions(-) create mode 100644 app/dns/options.go diff --git a/app/dns/config.pb.go b/app/dns/config.pb.go index 1f812c43..b7035c1e 100644 --- a/app/dns/config.pb.go +++ b/app/dns/config.pb.go @@ -128,6 +128,55 @@ func (QueryStrategy) EnumDescriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{1} } +type CacheStrategy int32 + +const ( + CacheStrategy_Cache_ALL CacheStrategy = 0 + CacheStrategy_Cache_NOERROR CacheStrategy = 1 + CacheStrategy_Cache_DISABLE CacheStrategy = 2 +) + +// Enum value maps for CacheStrategy. +var ( + CacheStrategy_name = map[int32]string{ + 0: "Cache_ALL", + 1: "Cache_NOERROR", + 2: "Cache_DISABLE", + } + CacheStrategy_value = map[string]int32{ + "Cache_ALL": 0, + "Cache_NOERROR": 1, + "Cache_DISABLE": 2, + } +) + +func (x CacheStrategy) Enum() *CacheStrategy { + p := new(CacheStrategy) + *p = x + return p +} + +func (x CacheStrategy) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (CacheStrategy) Descriptor() protoreflect.EnumDescriptor { + return file_app_dns_config_proto_enumTypes[2].Descriptor() +} + +func (CacheStrategy) Type() protoreflect.EnumType { + return &file_app_dns_config_proto_enumTypes[2] +} + +func (x CacheStrategy) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use CacheStrategy.Descriptor instead. +func (CacheStrategy) EnumDescriptor() ([]byte, []int) { + return file_app_dns_config_proto_rawDescGZIP(), []int{2} +} + type NameServer struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -232,7 +281,7 @@ type Config struct { // Tag is the inbound tag of DNS client. Tag string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"` // DisableCache Disable DNS cache - DisableCache bool `protobuf:"varint,8,opt,name=disableCache,proto3" json:"disableCache,omitempty"` + CacheStrategy CacheStrategy `protobuf:"varint,8,opt,name=cache_strategy,json=cacheStrategy,proto3,enum=xray.app.dns.CacheStrategy" json:"cache_strategy,omitempty"` QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"` } @@ -312,11 +361,11 @@ func (x *Config) GetTag() string { return "" } -func (x *Config) GetDisableCache() bool { +func (x *Config) GetCacheStrategy() CacheStrategy { if x != nil { - return x.DisableCache + return x.CacheStrategy } - return false + return CacheStrategy_Cache_ALL } func (x *Config) GetQueryStrategy() QueryStrategy { @@ -549,7 +598,7 @@ var file_app_dns_config_proto_rawDesc = []byte{ 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, 0x8d, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3f, 0x0a, + 0x7a, 0x65, 0x22, 0xad, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3f, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x02, 0x18, @@ -568,9 +617,11 @@ var file_app_dns_config_proto_rawDesc = []byte{ 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, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x42, 0x0a, 0x0e, 0x63, 0x61, 0x63, 0x68, + 0x65, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, + 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x63, + 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 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, @@ -598,11 +649,16 @@ var file_app_dns_config_proto_rawDesc = []byte{ 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, + 0x2a, 0x44, 0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x00, + 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x4e, 0x4f, 0x45, 0x52, 0x52, 0x4f, + 0x52, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x44, 0x49, 0x53, + 0x41, 0x42, 0x4c, 0x45, 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, } var ( @@ -617,39 +673,41 @@ func file_app_dns_config_proto_rawDescGZIP() []byte { return file_app_dns_config_proto_rawDescData } -var file_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3) var file_app_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_app_dns_config_proto_goTypes = []interface{}{ (DomainMatchingType)(0), // 0: xray.app.dns.DomainMatchingType (QueryStrategy)(0), // 1: xray.app.dns.QueryStrategy - (*NameServer)(nil), // 2: xray.app.dns.NameServer - (*Config)(nil), // 3: xray.app.dns.Config - (*NameServer_PriorityDomain)(nil), // 4: xray.app.dns.NameServer.PriorityDomain - (*NameServer_OriginalRule)(nil), // 5: xray.app.dns.NameServer.OriginalRule - nil, // 6: xray.app.dns.Config.HostsEntry - (*Config_HostMapping)(nil), // 7: xray.app.dns.Config.HostMapping - (*net.Endpoint)(nil), // 8: xray.common.net.Endpoint - (*router.GeoIP)(nil), // 9: xray.app.router.GeoIP - (*net.IPOrDomain)(nil), // 10: xray.common.net.IPOrDomain + (CacheStrategy)(0), // 2: xray.app.dns.CacheStrategy + (*NameServer)(nil), // 3: xray.app.dns.NameServer + (*Config)(nil), // 4: xray.app.dns.Config + (*NameServer_PriorityDomain)(nil), // 5: xray.app.dns.NameServer.PriorityDomain + (*NameServer_OriginalRule)(nil), // 6: xray.app.dns.NameServer.OriginalRule + nil, // 7: xray.app.dns.Config.HostsEntry + (*Config_HostMapping)(nil), // 8: xray.app.dns.Config.HostMapping + (*net.Endpoint)(nil), // 9: xray.common.net.Endpoint + (*router.GeoIP)(nil), // 10: xray.app.router.GeoIP + (*net.IPOrDomain)(nil), // 11: xray.common.net.IPOrDomain } var file_app_dns_config_proto_depIdxs = []int32{ - 8, // 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 - 9, // 2: xray.app.dns.NameServer.geoip:type_name -> xray.app.router.GeoIP - 5, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule - 8, // 4: xray.app.dns.Config.NameServers:type_name -> xray.common.net.Endpoint - 2, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer - 6, // 6: xray.app.dns.Config.Hosts:type_name -> xray.app.dns.Config.HostsEntry - 7, // 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 - 10, // 10: xray.app.dns.Config.HostsEntry.value:type_name -> xray.common.net.IPOrDomain - 0, // 11: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType - 12, // [12:12] is the sub-list for method output_type - 12, // [12:12] is the sub-list for method input_type - 12, // [12:12] is the sub-list for extension type_name - 12, // [12:12] is the sub-list for extension extendee - 0, // [0:12] is the sub-list for field type_name + 9, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint + 5, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain + 10, // 2: xray.app.dns.NameServer.geoip:type_name -> xray.app.router.GeoIP + 6, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule + 9, // 4: xray.app.dns.Config.NameServers:type_name -> xray.common.net.Endpoint + 3, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer + 7, // 6: xray.app.dns.Config.Hosts:type_name -> xray.app.dns.Config.HostsEntry + 8, // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping + 2, // 8: xray.app.dns.Config.cache_strategy:type_name -> xray.app.dns.CacheStrategy + 1, // 9: xray.app.dns.Config.query_strategy:type_name -> xray.app.dns.QueryStrategy + 0, // 10: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType + 11, // 11: xray.app.dns.Config.HostsEntry.value:type_name -> xray.common.net.IPOrDomain + 0, // 12: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType + 13, // [13:13] is the sub-list for method output_type + 13, // [13:13] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name } func init() { file_app_dns_config_proto_init() } @@ -724,7 +782,7 @@ func file_app_dns_config_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_app_dns_config_proto_rawDesc, - NumEnums: 2, + NumEnums: 3, NumMessages: 6, NumExtensions: 0, NumServices: 0, diff --git a/app/dns/config.proto b/app/dns/config.proto index 5ba5c4c2..21b898d1 100644 --- a/app/dns/config.proto +++ b/app/dns/config.proto @@ -42,6 +42,12 @@ enum QueryStrategy { USE_IP6 = 2; } +enum CacheStrategy { + Cache_ALL = 0; + Cache_NOERROR = 1; + Cache_DISABLE = 2; +} + message Config { // Nameservers used by this DNS. Only traditional UDP servers are support at // the moment. A special value 'localhost' as a domain address can be set to @@ -79,7 +85,7 @@ message Config { reserved 7; // DisableCache Disable DNS cache - bool disableCache = 8; + CacheStrategy cache_strategy = 8; QueryStrategy query_strategy = 9; } diff --git a/app/dns/dns.go b/app/dns/dns.go index 90c342bc..4f7407a0 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -23,7 +23,7 @@ import ( type DNS struct { sync.Mutex tag string - disableCache bool + cs CacheStrategy ipOption *dns.IPOption hosts *StaticHosts clients []*Client @@ -138,7 +138,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) { ctx: ctx, domainMatcher: domainMatcher, matcherInfos: matcherInfos, - disableCache: config.DisableCache, + cs: config.CacheStrategy, }, nil } @@ -165,11 +165,12 @@ func (s *DNS) IsOwnLink(ctx context.Context) bool { // LookupIP implements dns.Client. func (s *DNS) LookupIP(domain string) ([]net.IP, error) { - return s.lookupIPInternal(domain, dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + return s.lookupIPInternal(domain, *s.ipOption) +} + +// LookupOptions implements dns.Client. +func (s *DNS) LookupOptions(domain string, opt dns.IPOption) ([]net.IP, error) { + return s.lookupIPInternal(domain, opt) } // LookupIPv4 implements dns.IPv4Lookup. @@ -194,6 +195,9 @@ func (s *DNS) lookupIPInternal(domain string, option dns.IPOption) ([]net.IP, er if domain == "" { return nil, newError("empty domain name") } + if isQuery(option) { + return nil, newError("empty option: I'm pretty sure it shouldn't happened.") + } // Normalize the FQDN form query if strings.HasSuffix(domain, ".") { @@ -210,19 +214,18 @@ func (s *DNS) lookupIPInternal(domain string, option dns.IPOption) ([]net.IP, er newError("domain replaced: ", domain, " -> ", addrs[0].Domain()).WriteToLog() domain = addrs[0].Domain() default: // Successfully found ip records in static host - newError("returning ", len(addrs), " IPs for domain ", domain).WriteToLog() - return toNetIP(addrs) + if isIPQuery(option) { + // maybe our client prefer to query fake dns -_- + newError("returning ", len(addrs), " IPs for domain ", domain).WriteToLog() + return toNetIP(addrs) + } } // Name servers lookup errs := []error{} 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() - continue - } - ips, err := client.QueryIP(ctx, domain, option, s.disableCache) + for _, client := range s.sortClients(domain, option) { + ips, err := client.QueryIP(ctx, domain, option, s.cs) if len(ips) > 0 { return ips, nil } @@ -238,33 +241,31 @@ func (s *DNS) lookupIPInternal(domain string, option dns.IPOption) ([]net.IP, er return nil, newError("returning nil for domain ", domain).Base(errors.Combine(errs...)) } -// GetIPOption implements ClientWithIPOption. -func (s *DNS) GetIPOption() *dns.IPOption { - return s.ipOption -} - -// SetQueryOption implements ClientWithIPOption. -func (s *DNS) SetQueryOption(isIPv4Enable, isIPv6Enable bool) { - s.ipOption.IPv4Enable = isIPv4Enable - s.ipOption.IPv6Enable = isIPv6Enable -} - -// SetFakeDNSOption implements ClientWithIPOption. -func (s *DNS) SetFakeDNSOption(isFakeEnable bool) { - s.ipOption.FakeEnable = isFakeEnable -} - -func (s *DNS) sortClients(domain string) []*Client { +func (s *DNS) sortClients(domain string, option dns.IPOption) []*Client { clients := make([]*Client, 0, len(s.clients)) clientUsed := make([]bool, len(s.clients)) clientNames := make([]string, 0, len(s.clients)) domainRules := []string{} + defer func() { + if len(domainRules) > 0 { + newError("domain ", domain, " matches following rules: ", domainRules).AtDebug().WriteToLog() + } + if len(clientNames) > 0 { + newError("domain ", domain, " will use DNS in order: ", clientNames).AtDebug().WriteToLog() + } + }() + // Priority domain matching for _, match := range s.domainMatcher.Match(domain) { info := s.matcherInfos[match] client := s.clients[info.clientIdx] domainRule := client.domains[info.domainRuleIdx] + if !canQueryOnClient(option, client) { + newError("skipping the client " + client.Name()).AtDebug().WriteToLog() + continue + } + domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", domainRule, info.clientIdx)) if clientUsed[info.clientIdx] { continue @@ -279,17 +280,15 @@ func (s *DNS) sortClients(domain string) []*Client { if clientUsed[idx] { continue } + if !canQueryOnClient(option, client) { + newError("skipping the client " + client.Name()).AtDebug().WriteToLog() + continue + } clientUsed[idx] = true clients = append(clients, client) clientNames = append(clientNames, client.Name()) } - if len(domainRules) > 0 { - newError("domain ", domain, " matches following rules: ", domainRules).AtDebug().WriteToLog() - } - if len(clientNames) > 0 { - newError("domain ", domain, " will use DNS in order: ", clientNames).AtDebug().WriteToLog() - } return clients } diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go index 36341f65..14c41993 100644 --- a/app/dns/nameserver.go +++ b/app/dns/nameserver.go @@ -20,7 +20,7 @@ type Server interface { // Name of the Client. Name() string // QueryIP sends IP queries to its configured server. - QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption, disableCache bool) ([]net.IP, error) + QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption, cs CacheStrategy) ([]net.IP, error) } // Client is the interface for DNS client. @@ -177,9 +177,9 @@ func (c *Client) Name() string { } // QueryIP send DNS query to the name server with the client's IP. -func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption, disableCache bool) ([]net.IP, error) { +func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption, cs CacheStrategy) ([]net.IP, error) { ctx, cancel := context.WithTimeout(ctx, 4*time.Second) - ips, err := c.server.QueryIP(ctx, domain, c.clientIP, option, disableCache) + ips, err := c.server.QueryIP(ctx, domain, c.clientIP, option, cs) cancel() if err != nil { diff --git a/app/dns/nameserver_doh.go b/app/dns/nameserver_doh.go index bbde1bf8..2b38a87d 100644 --- a/app/dns/nameserver_doh.go +++ b/app/dns/nameserver_doh.go @@ -369,17 +369,19 @@ func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOpt } // QueryIP implements Server. -func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { // nolint: dupl +func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, cs CacheStrategy) ([]net.IP, error) { // nolint: dupl fqdn := Fqdn(domain) - if disableCache { + if cs == CacheStrategy_Cache_DISABLE { newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog() } else { ips, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { - newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog() - log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err}) - return ips, err + if cs == CacheStrategy_Cache_NOERROR && err == nil { + newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog() + log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err}) + return ips, err + } } } diff --git a/app/dns/nameserver_doh_test.go b/app/dns/nameserver_doh_test.go index cc12c7a5..f4c24194 100644 --- a/app/dns/nameserver_doh_test.go +++ b/app/dns/nameserver_doh_test.go @@ -23,7 +23,7 @@ func TestDOHNameServer(t *testing.T) { ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, - }, false) + }, CacheStrategy_Cache_ALL) cancel() common.Must(err) if len(ips) == 0 { @@ -40,7 +40,7 @@ func TestDOHNameServerWithCache(t *testing.T) { ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, - }, false) + }, CacheStrategy_Cache_ALL) cancel() common.Must(err) if len(ips) == 0 { @@ -51,7 +51,7 @@ func TestDOHNameServerWithCache(t *testing.T) { ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, - }, true) + }, CacheStrategy_Cache_ALL) cancel() common.Must(err) if r := cmp.Diff(ips2, ips); r != "" { diff --git a/app/dns/nameserver_fakedns.go b/app/dns/nameserver_fakedns.go index 54476ac2..382f8317 100644 --- a/app/dns/nameserver_fakedns.go +++ b/app/dns/nameserver_fakedns.go @@ -16,11 +16,13 @@ func NewFakeDNSServer() *FakeDNSServer { return &FakeDNSServer{} } +const FakeDNSName = "FakeDNS" + func (FakeDNSServer) Name() string { - return "FakeDNS" + return FakeDNSName } -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, _ dns.IPOption, _ CacheStrategy) ([]net.IP, error) { if f.fakeDNSEngine == nil { if err := core.RequireFeatures(ctx, func(fd dns.FakeDNSEngine) { f.fakeDNSEngine = fd diff --git a/app/dns/nameserver_local.go b/app/dns/nameserver_local.go index e1f859d2..a8a5732e 100644 --- a/app/dns/nameserver_local.go +++ b/app/dns/nameserver_local.go @@ -14,7 +14,7 @@ type LocalNameServer struct { } // QueryIP implements Server. -func (s *LocalNameServer) QueryIP(_ context.Context, domain string, _ net.IP, option dns.IPOption, _ bool) ([]net.IP, error) { +func (s *LocalNameServer) QueryIP(_ context.Context, domain string, _ net.IP, option dns.IPOption, _ CacheStrategy) ([]net.IP, error) { var ips []net.IP var err error diff --git a/app/dns/nameserver_local_test.go b/app/dns/nameserver_local_test.go index d1995a63..ab89c9fb 100644 --- a/app/dns/nameserver_local_test.go +++ b/app/dns/nameserver_local_test.go @@ -17,7 +17,7 @@ func TestLocalNameServer(t *testing.T) { ips, err := s.QueryIP(ctx, "google.com", net.IP{}, dns.IPOption{ IPv4Enable: true, IPv6Enable: true, - }, false) + }, CacheStrategy_Cache_ALL) cancel() common.Must(err) if len(ips) == 0 { diff --git a/app/dns/nameserver_quic.go b/app/dns/nameserver_quic.go index 05cfd8e7..f7f08927 100644 --- a/app/dns/nameserver_quic.go +++ b/app/dns/nameserver_quic.go @@ -268,17 +268,19 @@ func (s *QUICNameServer) findIPsForDomain(domain string, option dns_feature.IPOp } // QueryIP is called from dns.Server->queryIPTimeout -func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { +func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, cs CacheStrategy) ([]net.IP, error) { fqdn := Fqdn(domain) - if disableCache { + if cs == CacheStrategy_Cache_DISABLE { newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog() } else { ips, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { - newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog() - log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err}) - return ips, err + if cs == CacheStrategy_Cache_NOERROR && err == nil { + newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog() + log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err}) + return ips, err + } } } diff --git a/app/dns/nameserver_quic_test.go b/app/dns/nameserver_quic_test.go index 5a4beba9..51f655bd 100644 --- a/app/dns/nameserver_quic_test.go +++ b/app/dns/nameserver_quic_test.go @@ -23,7 +23,7 @@ func TestQUICNameServer(t *testing.T) { ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, - }, false) + }, CacheStrategy_Cache_ALL) cancel() common.Must(err) if len(ips) == 0 { @@ -40,7 +40,7 @@ func TestQUICNameServerWithCache(t *testing.T) { ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, - }, false) + }, CacheStrategy_Cache_ALL) cancel() common.Must(err) if len(ips) == 0 { @@ -51,7 +51,7 @@ func TestQUICNameServerWithCache(t *testing.T) { ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, - }, true) + }, CacheStrategy_Cache_ALL) cancel() common.Must(err) if r := cmp.Diff(ips2, ips); r != "" { diff --git a/app/dns/nameserver_udp.go b/app/dns/nameserver_udp.go index 6f9b6404..fa6327c5 100644 --- a/app/dns/nameserver_udp.go +++ b/app/dns/nameserver_udp.go @@ -245,17 +245,19 @@ func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.I } // QueryIP implements Server. -func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { +func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, cs CacheStrategy) ([]net.IP, error) { fqdn := Fqdn(domain) - if disableCache { + if cs == CacheStrategy_Cache_DISABLE { newError("DNS cache is disabled. Querying IP for ", domain, " at ", s.name).AtDebug().WriteToLog() } else { ips, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { - newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog() - log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err}) - return ips, err + if cs == CacheStrategy_Cache_NOERROR && err == nil { + newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog() + log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err}) + return ips, err + } } } diff --git a/app/dns/options.go b/app/dns/options.go new file mode 100644 index 00000000..2bedbe9f --- /dev/null +++ b/app/dns/options.go @@ -0,0 +1,24 @@ +package dns + +import "github.com/xtls/xray-core/features/dns" + +type Option interface { + queryIPv4() bool + queryIPv6() bool + queryIP() bool + queryFake() bool + canDoQuery(c *Client) bool +} + +func isIPQuery(o dns.IPOption) bool { + return o.IPv4Enable || o.IPv6Enable +} + +func canQueryOnClient(o dns.IPOption, c *Client) bool { + isIPClient := !(c.Name() == FakeDNSName) + return isIPClient && isIPQuery(o) +} + +func isQuery(o dns.IPOption) bool { + return !(o.IPv4Enable || o.IPv6Enable || o.FakeEnable) +} diff --git a/features/dns/client.go b/features/dns/client.go index 2855da38..93f8a68b 100644 --- a/features/dns/client.go +++ b/features/dns/client.go @@ -22,6 +22,9 @@ type Client interface { // LookupIP returns IP address for the given domain. IPs may contain IPv4 and/or IPv6 addresses. LookupIP(domain string) ([]net.IP, error) + + // LookupOptions query IP address for domain with IPOption. + LookupOptions(domain string, opt IPOption) ([]net.IP, error) } // IPv4Lookup is an optional feature for querying IPv4 addresses only. @@ -38,20 +41,6 @@ type IPv6Lookup interface { LookupIPv6(domain string) ([]net.IP, error) } -// ClientWithIPOption is an optional feature for querying DNS information. -// -// xray:api:beta -type ClientWithIPOption interface { - // GetIPOption returns IPOption for the DNS client. - GetIPOption() *IPOption - - // SetQueryOption sets IPv4Enable and IPv6Enable for the DNS client. - SetQueryOption(isIPv4Enable, isIPv6Enable bool) - - // SetFakeDNSOption sets FakeEnable option for DNS client. - SetFakeDNSOption(isFakeEnable bool) -} - // ClientType returns the type of Client interface. Can be used for implementing common.HasType. // // xray:api:beta @@ -78,3 +67,11 @@ func RCodeFromError(err error) uint16 { } return 0 } + +var ( + LookupIPv4 = IPOption{IPv4Enable: true} + LookupIPv6 = IPOption{IPv6Enable: true} + LookupIP = IPOption{IPv4Enable: true, IPv6Enable: true} + LookupFake = IPOption{FakeEnable: true} + LookupAll = IPOption{true, true, true} +) diff --git a/features/dns/localdns/client.go b/features/dns/localdns/client.go index e8b367ca..47d3590e 100644 --- a/features/dns/localdns/client.go +++ b/features/dns/localdns/client.go @@ -38,6 +38,11 @@ func (*Client) LookupIP(host string) ([]net.IP, error) { return parsedIPs, nil } +// LookupOptions implements Client. +func (c *Client) LookupOptions(host string, _ dns.IPOption) ([]net.IP, error) { + return c.LookupIP(host) +} + // LookupIPv4 implements IPv4Lookup. func (c *Client) LookupIPv4(host string) ([]net.IP, error) { ips, err := c.LookupIP(host) diff --git a/features/routing/dns/context.go b/features/routing/dns/context.go index 0f61ab3a..9912332a 100644 --- a/features/routing/dns/context.go +++ b/features/routing/dns/context.go @@ -26,40 +26,12 @@ func (ctx *ResolvableContext) GetTargetIPs() []net.IP { } if domain := ctx.GetTargetDomain(); len(domain) != 0 { - var lookupFunc func(string) ([]net.IP, error) = ctx.dnsClient.LookupIP - ipOption := &dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - } - - if c, ok := ctx.dnsClient.(dns.ClientWithIPOption); ok { - ipOption = c.GetIPOption() - c.SetFakeDNSOption(false) // Skip FakeDNS. - } else { - newError("ctx.dnsClient doesn't implement ClientWithIPOption").AtDebug().WriteToLog() - } - - switch { - case ipOption.IPv4Enable && !ipOption.IPv6Enable: - if lookupIPv4, ok := ctx.dnsClient.(dns.IPv4Lookup); ok { - lookupFunc = lookupIPv4.LookupIPv4 - } else { - newError("ctx.dnsClient doesn't implement IPv4Lookup. Use LookupIP instead.").AtDebug().WriteToLog() - } - case !ipOption.IPv4Enable && ipOption.IPv6Enable: - if lookupIPv6, ok := ctx.dnsClient.(dns.IPv6Lookup); ok { - lookupFunc = lookupIPv6.LookupIPv6 - } else { - newError("ctx.dnsClient doesn't implement IPv6Lookup. Use LookupIP instead.").AtDebug().WriteToLog() - } - } - - ips, err := lookupFunc(domain) + ips, err := ctx.dnsClient.LookupIP(domain) if err == nil { ctx.resolvedIPs = ips return ips } - newError("resolve ip for ", domain).Base(err).WriteToLog() + newError("failed to resolve ip for ", domain).Base(err).WriteToLog() } return nil diff --git a/infra/conf/dns.go b/infra/conf/dns.go index 15402a23..003c1579 100644 --- a/infra/conf/dns.go +++ b/infra/conf/dns.go @@ -124,6 +124,7 @@ type DNSConfig struct { ClientIP *Address `json:"clientIp"` Tag string `json:"tag"` QueryStrategy string `json:"queryStrategy"` + CacheStrategy string `json:"cacheStrategy"` DisableCache bool `json:"disableCache"` } @@ -142,8 +143,12 @@ func getHostMapping(addr *Address) *dns.Config_HostMapping { // Build implements Buildable func (c *DNSConfig) Build() (*dns.Config, error) { config := &dns.Config{ - Tag: c.Tag, - DisableCache: c.DisableCache, + Tag: c.Tag, + CacheStrategy: dns.CacheStrategy_Cache_ALL, + } + + if c.DisableCache { + config.CacheStrategy = dns.CacheStrategy_Cache_DISABLE } if c.ClientIP != nil { @@ -163,6 +168,15 @@ func (c *DNSConfig) Build() (*dns.Config, error) { config.QueryStrategy = dns.QueryStrategy_USE_IP6 } + switch strings.ToLower(c.CacheStrategy) { + case "noerror": + config.CacheStrategy = dns.CacheStrategy_Cache_NOERROR + case "all": + config.CacheStrategy = dns.CacheStrategy_Cache_ALL + case "disable", "none": + config.CacheStrategy = dns.CacheStrategy_Cache_DISABLE + } + for _, server := range c.Servers { ns, err := server.Build() if err != nil { diff --git a/infra/conf/dns_test.go b/infra/conf/dns_test.go index c3025726..e2032831 100644 --- a/infra/conf/dns_test.go +++ b/infra/conf/dns_test.go @@ -139,7 +139,7 @@ func TestDNSConfigParsing(t *testing.T) { }, ClientIp: []byte{10, 0, 0, 1}, QueryStrategy: dns.QueryStrategy_USE_IP4, - DisableCache: true, + CacheStrategy: dns.CacheStrategy_Cache_DISABLE, }, }, }) diff --git a/proxy/dns/dns.go b/proxy/dns/dns.go index e38b0cd9..109af830 100644 --- a/proxy/dns/dns.go +++ b/proxy/dns/dns.go @@ -37,8 +37,6 @@ type ownLinkVerifier interface { type Handler struct { client dns.Client - ipv4Lookup dns.IPv4Lookup - ipv6Lookup dns.IPv6Lookup ownLinkVerifier ownLinkVerifier server net.Destination } @@ -46,18 +44,6 @@ type Handler struct { func (h *Handler) Init(config *Config, dnsClient dns.Client) error { h.client = dnsClient - if ipv4lookup, ok := dnsClient.(dns.IPv4Lookup); ok { - h.ipv4Lookup = ipv4lookup - } else { - return newError("dns.Client doesn't implement IPv4Lookup") - } - - if ipv6lookup, ok := dnsClient.(dns.IPv6Lookup); ok { - h.ipv6Lookup = ipv6lookup - } else { - return newError("dns.Client doesn't implement IPv6Lookup") - } - if v, ok := dnsClient.(ownLinkVerifier); ok { h.ownLinkVerifier = v } @@ -213,21 +199,18 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, var err error var ttl uint32 = 600 - - // Do NOT skip FakeDNS - if c, ok := h.client.(dns.ClientWithIPOption); ok { - c.SetFakeDNSOption(true) - } else { - newError("dns.Client doesn't implement ClientWithIPOption") - } + var opt = dns.LookupIP switch qType { case dnsmessage.TypeA: - ips, err = h.ipv4Lookup.LookupIPv4(domain) + opt = dns.LookupIPv4 case dnsmessage.TypeAAAA: - ips, err = h.ipv6Lookup.LookupIPv6(domain) + opt = dns.LookupIPv6 } + opt.FakeEnable = true + + ips, err = h.client.LookupOptions(domain, opt) rcode := dns.RCodeFromError(err) if rcode == 0 && len(ips) == 0 && err != dns.ErrEmptyResponse { newError("ip query").Base(err).WriteToLog() diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index 0da6f4da..fe6db062 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -59,24 +59,15 @@ func (h *Handler) policy() policy.Session { } func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address { - if c, ok := h.dns.(dns.ClientWithIPOption); ok { - c.SetFakeDNSOption(false) // Skip FakeDNS - } else { - newError("DNS client doesn't implement ClientWithIPOption") - } - - var lookupFunc func(string) ([]net.IP, error) = h.dns.LookupIP + var opt = dns.LookupIP if h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()) { - if lookupIPv4, ok := h.dns.(dns.IPv4Lookup); ok { - lookupFunc = lookupIPv4.LookupIPv4 - } + opt = dns.LookupIPv4 } else if h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()) { - if lookupIPv6, ok := h.dns.(dns.IPv6Lookup); ok { - lookupFunc = lookupIPv6.LookupIPv6 - } + opt = dns.LookupIPv6 } + opt.FakeEnable = true - ips, err := lookupFunc(domain) + ips, err := h.dns.LookupOptions(domain, opt) if err != nil { newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx)) } diff --git a/transport/internet/system_dialer.go b/transport/internet/system_dialer.go index 605fd7dc..7cc48645 100644 --- a/transport/internet/system_dialer.go +++ b/transport/internet/system_dialer.go @@ -63,30 +63,20 @@ func (d *DefaultSystemDialer) lookupIP(domain string, strategy DomainStrategy, l return nil, nil } - if c, ok := d.dns.(dns.ClientWithIPOption); ok { - c.SetFakeDNSOption(false) // Skip FakeDNS - } else { - newError("DNS client doesn't implement ClientWithIPOption") - } - - var lookupFunc func(string) ([]net.IP, error) = d.dns.LookupIP + var opt = dns.LookupIP switch { case strategy == DomainStrategy_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()): - if lookupIPv4, ok := d.dns.(dns.IPv4Lookup); ok { - lookupFunc = lookupIPv4.LookupIPv4 - } + opt = dns.LookupIPv4 case strategy == DomainStrategy_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()): - if lookupIPv6, ok := d.dns.(dns.IPv6Lookup); ok { - lookupFunc = lookupIPv6.LookupIPv6 - } + opt = dns.LookupIPv6 case strategy == DomainStrategy_AS_IS: return nil, nil } - return lookupFunc(domain) + return d.dns.LookupOptions(domain, opt) } -func (d *DefaultSystemDialer) doLookupIP(ctx context.Context, dst net.Destination, sockopt *SocketConfig) bool { +func (d *DefaultSystemDialer) canLookupIP(ctx context.Context, dst net.Destination, sockopt *SocketConfig) bool { if sockopt == nil || dst.Address.Family().IsIP() || d.dns == nil { return false } @@ -121,7 +111,7 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne } } - if d.doLookupIP(ctx, dest, sockopt) { + if d.canLookupIP(ctx, dest, sockopt) { ips, err := d.lookupIP(dest.Address.String(), sockopt.DomainStrategy, src) if err == nil && len(ips) > 0 { dest.Address = net.IPAddress(ips[dice.Roll(len(ips))]) From 70b63e21a56fe9bb1c0feefc5f22131593dcc096 Mon Sep 17 00:00:00 2001 From: JimhHan <50871214+JimhHan@users.noreply.github.com> Date: Fri, 9 Apr 2021 23:36:48 +0800 Subject: [PATCH 2/5] Fix testing --- testing/mocks/dns.go | 18 +++++++++++++- testing/mocks/io.go | 27 +++++++++++---------- testing/mocks/log.go | 15 ++++++------ testing/mocks/mux.go | 15 ++++++------ testing/mocks/outbound.go | 51 ++++++++++++++++++++------------------- testing/mocks/proxy.go | 31 ++++++++++++------------ 6 files changed, 89 insertions(+), 68 deletions(-) diff --git a/testing/mocks/dns.go b/testing/mocks/dns.go index 8fa59372..21ccaeae 100644 --- a/testing/mocks/dns.go +++ b/testing/mocks/dns.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/xray/xray-core/v4/features/dns (interfaces: Client) +// Source: github.com/xtls/xray-core/features/dns (interfaces: Client) // Package mocks is a generated GoMock package. package mocks @@ -9,6 +9,7 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" + dns "github.com/xtls/xray-core/features/dns" ) // DNSClient is a mock of Client interface. @@ -63,6 +64,21 @@ func (mr *DNSClientMockRecorder) LookupIP(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupIP", reflect.TypeOf((*DNSClient)(nil).LookupIP), arg0) } +// LookupOptions mocks base method. +func (m *DNSClient) LookupOptions(arg0 string, arg1 dns.IPOption) ([]net.IP, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LookupOptions", arg0, arg1) + ret0, _ := ret[0].([]net.IP) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LookupOptions indicates an expected call of LookupOptions. +func (mr *DNSClientMockRecorder) LookupOptions(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupOptions", reflect.TypeOf((*DNSClient)(nil).LookupOptions), arg0, arg1) +} + // Start mocks base method. func (m *DNSClient) Start() error { m.ctrl.T.Helper() diff --git a/testing/mocks/io.go b/testing/mocks/io.go index d1f92cea..a24e4f7c 100644 --- a/testing/mocks/io.go +++ b/testing/mocks/io.go @@ -5,34 +5,35 @@ package mocks import ( - gomock "github.com/golang/mock/gomock" reflect "reflect" + + gomock "github.com/golang/mock/gomock" ) -// Reader is a mock of Reader interface +// Reader is a mock of Reader interface. type Reader struct { ctrl *gomock.Controller recorder *ReaderMockRecorder } -// ReaderMockRecorder is the mock recorder for Reader +// ReaderMockRecorder is the mock recorder for Reader. type ReaderMockRecorder struct { mock *Reader } -// NewReader creates a new mock instance +// NewReader creates a new mock instance. func NewReader(ctrl *gomock.Controller) *Reader { mock := &Reader{ctrl: ctrl} mock.recorder = &ReaderMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *Reader) EXPECT() *ReaderMockRecorder { return m.recorder } -// Read mocks base method +// Read mocks base method. func (m *Reader) Read(arg0 []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Read", arg0) @@ -41,36 +42,36 @@ func (m *Reader) Read(arg0 []byte) (int, error) { return ret0, ret1 } -// Read indicates an expected call of Read +// Read indicates an expected call of Read. func (mr *ReaderMockRecorder) Read(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*Reader)(nil).Read), arg0) } -// Writer is a mock of Writer interface +// Writer is a mock of Writer interface. type Writer struct { ctrl *gomock.Controller recorder *WriterMockRecorder } -// WriterMockRecorder is the mock recorder for Writer +// WriterMockRecorder is the mock recorder for Writer. type WriterMockRecorder struct { mock *Writer } -// NewWriter creates a new mock instance +// NewWriter creates a new mock instance. func NewWriter(ctrl *gomock.Controller) *Writer { mock := &Writer{ctrl: ctrl} mock.recorder = &WriterMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *Writer) EXPECT() *WriterMockRecorder { return m.recorder } -// Write mocks base method +// Write mocks base method. func (m *Writer) Write(arg0 []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Write", arg0) @@ -79,7 +80,7 @@ func (m *Writer) Write(arg0 []byte) (int, error) { return ret0, ret1 } -// Write indicates an expected call of Write +// Write indicates an expected call of Write. func (mr *WriterMockRecorder) Write(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*Writer)(nil).Write), arg0) diff --git a/testing/mocks/log.go b/testing/mocks/log.go index 77c821f5..b408d964 100644 --- a/testing/mocks/log.go +++ b/testing/mocks/log.go @@ -5,41 +5,42 @@ package mocks import ( + reflect "reflect" + gomock "github.com/golang/mock/gomock" log "github.com/xtls/xray-core/common/log" - reflect "reflect" ) -// LogHandler is a mock of Handler interface +// LogHandler is a mock of Handler interface. type LogHandler struct { ctrl *gomock.Controller recorder *LogHandlerMockRecorder } -// LogHandlerMockRecorder is the mock recorder for LogHandler +// LogHandlerMockRecorder is the mock recorder for LogHandler. type LogHandlerMockRecorder struct { mock *LogHandler } -// NewLogHandler creates a new mock instance +// NewLogHandler creates a new mock instance. func NewLogHandler(ctrl *gomock.Controller) *LogHandler { mock := &LogHandler{ctrl: ctrl} mock.recorder = &LogHandlerMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *LogHandler) EXPECT() *LogHandlerMockRecorder { return m.recorder } -// Handle mocks base method +// Handle mocks base method. func (m *LogHandler) Handle(arg0 log.Message) { m.ctrl.T.Helper() m.ctrl.Call(m, "Handle", arg0) } -// Handle indicates an expected call of Handle +// Handle indicates an expected call of Handle. func (mr *LogHandlerMockRecorder) Handle(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Handle", reflect.TypeOf((*LogHandler)(nil).Handle), arg0) diff --git a/testing/mocks/mux.go b/testing/mocks/mux.go index 07d4c4ad..2e295099 100644 --- a/testing/mocks/mux.go +++ b/testing/mocks/mux.go @@ -5,35 +5,36 @@ package mocks import ( + reflect "reflect" + gomock "github.com/golang/mock/gomock" mux "github.com/xtls/xray-core/common/mux" - reflect "reflect" ) -// MuxClientWorkerFactory is a mock of ClientWorkerFactory interface +// MuxClientWorkerFactory is a mock of ClientWorkerFactory interface. type MuxClientWorkerFactory struct { ctrl *gomock.Controller recorder *MuxClientWorkerFactoryMockRecorder } -// MuxClientWorkerFactoryMockRecorder is the mock recorder for MuxClientWorkerFactory +// MuxClientWorkerFactoryMockRecorder is the mock recorder for MuxClientWorkerFactory. type MuxClientWorkerFactoryMockRecorder struct { mock *MuxClientWorkerFactory } -// NewMuxClientWorkerFactory creates a new mock instance +// NewMuxClientWorkerFactory creates a new mock instance. func NewMuxClientWorkerFactory(ctrl *gomock.Controller) *MuxClientWorkerFactory { mock := &MuxClientWorkerFactory{ctrl: ctrl} mock.recorder = &MuxClientWorkerFactoryMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *MuxClientWorkerFactory) EXPECT() *MuxClientWorkerFactoryMockRecorder { return m.recorder } -// Create mocks base method +// Create mocks base method. func (m *MuxClientWorkerFactory) Create() (*mux.ClientWorker, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Create") @@ -42,7 +43,7 @@ func (m *MuxClientWorkerFactory) Create() (*mux.ClientWorker, error) { return ret0, ret1 } -// Create indicates an expected call of Create +// Create indicates an expected call of Create. func (mr *MuxClientWorkerFactoryMockRecorder) Create() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MuxClientWorkerFactory)(nil).Create)) diff --git a/testing/mocks/outbound.go b/testing/mocks/outbound.go index 9cab8c34..2271aa75 100644 --- a/testing/mocks/outbound.go +++ b/testing/mocks/outbound.go @@ -6,35 +6,36 @@ package mocks import ( context "context" + reflect "reflect" + gomock "github.com/golang/mock/gomock" outbound "github.com/xtls/xray-core/features/outbound" - reflect "reflect" ) -// OutboundManager is a mock of Manager interface +// OutboundManager is a mock of Manager interface. type OutboundManager struct { ctrl *gomock.Controller recorder *OutboundManagerMockRecorder } -// OutboundManagerMockRecorder is the mock recorder for OutboundManager +// OutboundManagerMockRecorder is the mock recorder for OutboundManager. type OutboundManagerMockRecorder struct { mock *OutboundManager } -// NewOutboundManager creates a new mock instance +// NewOutboundManager creates a new mock instance. func NewOutboundManager(ctrl *gomock.Controller) *OutboundManager { mock := &OutboundManager{ctrl: ctrl} mock.recorder = &OutboundManagerMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *OutboundManager) EXPECT() *OutboundManagerMockRecorder { return m.recorder } -// AddHandler mocks base method +// AddHandler mocks base method. func (m *OutboundManager) AddHandler(arg0 context.Context, arg1 outbound.Handler) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddHandler", arg0, arg1) @@ -42,13 +43,13 @@ func (m *OutboundManager) AddHandler(arg0 context.Context, arg1 outbound.Handler return ret0 } -// AddHandler indicates an expected call of AddHandler +// AddHandler indicates an expected call of AddHandler. func (mr *OutboundManagerMockRecorder) AddHandler(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddHandler", reflect.TypeOf((*OutboundManager)(nil).AddHandler), arg0, arg1) } -// Close mocks base method +// Close mocks base method. func (m *OutboundManager) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") @@ -56,13 +57,13 @@ func (m *OutboundManager) Close() error { return ret0 } -// Close indicates an expected call of Close +// Close indicates an expected call of Close. func (mr *OutboundManagerMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*OutboundManager)(nil).Close)) } -// GetDefaultHandler mocks base method +// GetDefaultHandler mocks base method. func (m *OutboundManager) GetDefaultHandler() outbound.Handler { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDefaultHandler") @@ -70,13 +71,13 @@ func (m *OutboundManager) GetDefaultHandler() outbound.Handler { return ret0 } -// GetDefaultHandler indicates an expected call of GetDefaultHandler +// GetDefaultHandler indicates an expected call of GetDefaultHandler. func (mr *OutboundManagerMockRecorder) GetDefaultHandler() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultHandler", reflect.TypeOf((*OutboundManager)(nil).GetDefaultHandler)) } -// GetHandler mocks base method +// GetHandler mocks base method. func (m *OutboundManager) GetHandler(arg0 string) outbound.Handler { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHandler", arg0) @@ -84,13 +85,13 @@ func (m *OutboundManager) GetHandler(arg0 string) outbound.Handler { return ret0 } -// GetHandler indicates an expected call of GetHandler +// GetHandler indicates an expected call of GetHandler. func (mr *OutboundManagerMockRecorder) GetHandler(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHandler", reflect.TypeOf((*OutboundManager)(nil).GetHandler), arg0) } -// RemoveHandler mocks base method +// RemoveHandler mocks base method. func (m *OutboundManager) RemoveHandler(arg0 context.Context, arg1 string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RemoveHandler", arg0, arg1) @@ -98,13 +99,13 @@ func (m *OutboundManager) RemoveHandler(arg0 context.Context, arg1 string) error return ret0 } -// RemoveHandler indicates an expected call of RemoveHandler +// RemoveHandler indicates an expected call of RemoveHandler. func (mr *OutboundManagerMockRecorder) RemoveHandler(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveHandler", reflect.TypeOf((*OutboundManager)(nil).RemoveHandler), arg0, arg1) } -// Start mocks base method +// Start mocks base method. func (m *OutboundManager) Start() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start") @@ -112,13 +113,13 @@ func (m *OutboundManager) Start() error { return ret0 } -// Start indicates an expected call of Start +// Start indicates an expected call of Start. func (mr *OutboundManagerMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*OutboundManager)(nil).Start)) } -// Type mocks base method +// Type mocks base method. func (m *OutboundManager) Type() interface{} { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Type") @@ -126,36 +127,36 @@ func (m *OutboundManager) Type() interface{} { return ret0 } -// Type indicates an expected call of Type +// Type indicates an expected call of Type. func (mr *OutboundManagerMockRecorder) Type() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Type", reflect.TypeOf((*OutboundManager)(nil).Type)) } -// OutboundHandlerSelector is a mock of HandlerSelector interface +// OutboundHandlerSelector is a mock of HandlerSelector interface. type OutboundHandlerSelector struct { ctrl *gomock.Controller recorder *OutboundHandlerSelectorMockRecorder } -// OutboundHandlerSelectorMockRecorder is the mock recorder for OutboundHandlerSelector +// OutboundHandlerSelectorMockRecorder is the mock recorder for OutboundHandlerSelector. type OutboundHandlerSelectorMockRecorder struct { mock *OutboundHandlerSelector } -// NewOutboundHandlerSelector creates a new mock instance +// NewOutboundHandlerSelector creates a new mock instance. func NewOutboundHandlerSelector(ctrl *gomock.Controller) *OutboundHandlerSelector { mock := &OutboundHandlerSelector{ctrl: ctrl} mock.recorder = &OutboundHandlerSelectorMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *OutboundHandlerSelector) EXPECT() *OutboundHandlerSelectorMockRecorder { return m.recorder } -// Select mocks base method +// Select mocks base method. func (m *OutboundHandlerSelector) Select(arg0 []string) []string { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Select", arg0) @@ -163,7 +164,7 @@ func (m *OutboundHandlerSelector) Select(arg0 []string) []string { return ret0 } -// Select indicates an expected call of Select +// Select indicates an expected call of Select. func (mr *OutboundHandlerSelectorMockRecorder) Select(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Select", reflect.TypeOf((*OutboundHandlerSelector)(nil).Select), arg0) diff --git a/testing/mocks/proxy.go b/testing/mocks/proxy.go index cba5b3ca..d0499b88 100644 --- a/testing/mocks/proxy.go +++ b/testing/mocks/proxy.go @@ -6,38 +6,39 @@ package mocks import ( context "context" + reflect "reflect" + gomock "github.com/golang/mock/gomock" net "github.com/xtls/xray-core/common/net" routing "github.com/xtls/xray-core/features/routing" transport "github.com/xtls/xray-core/transport" internet "github.com/xtls/xray-core/transport/internet" - reflect "reflect" ) -// ProxyInbound is a mock of Inbound interface +// ProxyInbound is a mock of Inbound interface. type ProxyInbound struct { ctrl *gomock.Controller recorder *ProxyInboundMockRecorder } -// ProxyInboundMockRecorder is the mock recorder for ProxyInbound +// ProxyInboundMockRecorder is the mock recorder for ProxyInbound. type ProxyInboundMockRecorder struct { mock *ProxyInbound } -// NewProxyInbound creates a new mock instance +// NewProxyInbound creates a new mock instance. func NewProxyInbound(ctrl *gomock.Controller) *ProxyInbound { mock := &ProxyInbound{ctrl: ctrl} mock.recorder = &ProxyInboundMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *ProxyInbound) EXPECT() *ProxyInboundMockRecorder { return m.recorder } -// Network mocks base method +// Network mocks base method. func (m *ProxyInbound) Network() []net.Network { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Network") @@ -45,13 +46,13 @@ func (m *ProxyInbound) Network() []net.Network { return ret0 } -// Network indicates an expected call of Network +// Network indicates an expected call of Network. func (mr *ProxyInboundMockRecorder) Network() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Network", reflect.TypeOf((*ProxyInbound)(nil).Network)) } -// Process mocks base method +// Process mocks base method. func (m *ProxyInbound) Process(arg0 context.Context, arg1 net.Network, arg2 internet.Connection, arg3 routing.Dispatcher) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Process", arg0, arg1, arg2, arg3) @@ -59,36 +60,36 @@ func (m *ProxyInbound) Process(arg0 context.Context, arg1 net.Network, arg2 inte return ret0 } -// Process indicates an expected call of Process +// Process indicates an expected call of Process. func (mr *ProxyInboundMockRecorder) Process(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Process", reflect.TypeOf((*ProxyInbound)(nil).Process), arg0, arg1, arg2, arg3) } -// ProxyOutbound is a mock of Outbound interface +// ProxyOutbound is a mock of Outbound interface. type ProxyOutbound struct { ctrl *gomock.Controller recorder *ProxyOutboundMockRecorder } -// ProxyOutboundMockRecorder is the mock recorder for ProxyOutbound +// ProxyOutboundMockRecorder is the mock recorder for ProxyOutbound. type ProxyOutboundMockRecorder struct { mock *ProxyOutbound } -// NewProxyOutbound creates a new mock instance +// NewProxyOutbound creates a new mock instance. func NewProxyOutbound(ctrl *gomock.Controller) *ProxyOutbound { mock := &ProxyOutbound{ctrl: ctrl} mock.recorder = &ProxyOutboundMockRecorder{mock} return mock } -// EXPECT returns an object that allows the caller to indicate expected use +// EXPECT returns an object that allows the caller to indicate expected use. func (m *ProxyOutbound) EXPECT() *ProxyOutboundMockRecorder { return m.recorder } -// Process mocks base method +// Process mocks base method. func (m *ProxyOutbound) Process(arg0 context.Context, arg1 *transport.Link, arg2 internet.Dialer) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Process", arg0, arg1, arg2) @@ -96,7 +97,7 @@ func (m *ProxyOutbound) Process(arg0 context.Context, arg1 *transport.Link, arg2 return ret0 } -// Process indicates an expected call of Process +// Process indicates an expected call of Process. func (mr *ProxyOutboundMockRecorder) Process(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Process", reflect.TypeOf((*ProxyOutbound)(nil).Process), arg0, arg1, arg2) From 6e902b24aebfc98b40f8e1ad601a75bffdb6eb06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E3=81=AE=E3=81=8B=E3=81=88=E3=81=A7?= Date: Sat, 10 Apr 2021 00:07:08 +0800 Subject: [PATCH 3/5] Feat: add disableFallback & skipFallback option for DNS client (#489) --- app/dns/config.pb.go | 197 +++++++++++++++++++++++------------------ app/dns/config.proto | 5 +- app/dns/dns.go | 66 ++++++++------ app/dns/nameserver.go | 10 ++- infra/conf/dns.go | 44 +++++---- infra/conf/dns_test.go | 14 +-- 6 files changed, 191 insertions(+), 145 deletions(-) diff --git a/app/dns/config.pb.go b/app/dns/config.pb.go index b7035c1e..f9c0c181 100644 --- a/app/dns/config.pb.go +++ b/app/dns/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.25.0 -// protoc v3.15.6 +// protoc v3.15.8 // source: app/dns/config.proto package dns @@ -184,6 +184,7 @@ type NameServer struct { 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"` @@ -235,6 +236,13 @@ func (x *NameServer) GetClientIp() []byte { return nil } +func (x *NameServer) GetSkipFallback() bool { + if x != nil { + return x.SkipFallback + } + return false +} + func (x *NameServer) GetPrioritizedDomain() []*NameServer_PriorityDomain { if x != nil { return x.PrioritizedDomain @@ -280,9 +288,10 @@ type Config struct { StaticHosts []*Config_HostMapping `protobuf:"bytes,4,rep,name=static_hosts,json=staticHosts,proto3" json:"static_hosts,omitempty"` // Tag is the inbound tag of DNS client. Tag string `protobuf:"bytes,6,opt,name=tag,proto3" json:"tag,omitempty"` - // DisableCache Disable DNS cache - CacheStrategy CacheStrategy `protobuf:"varint,8,opt,name=cache_strategy,json=cacheStrategy,proto3,enum=xray.app.dns.CacheStrategy" json:"cache_strategy,omitempty"` - QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"` + // DisableCache disables DNS cache + CacheStrategy CacheStrategy `protobuf:"varint,8,opt,name=cache_strategy,json=cacheStrategy,proto3,enum=xray.app.dns.CacheStrategy" json:"cache_strategy,omitempty"` + QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"` + DisableFallback bool `protobuf:"varint,10,opt,name=disableFallback,proto3" json:"disableFallback,omitempty"` } func (x *Config) Reset() { @@ -375,6 +384,13 @@ func (x *Config) GetQueryStrategy() QueryStrategy { return QueryStrategy_USE_IP } +func (x *Config) GetDisableFallback() bool { + if x != nil { + return x.DisableFallback + } + return false +} + type NameServer_PriorityDomain struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -569,96 +585,101 @@ var file_app_dns_config_proto_rawDesc = []byte{ 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, 0xca, 0x03, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xee, 0x03, 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, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x56, 0x0a, 0x12, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, - 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x27, 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, 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, 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, 0xad, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3f, 0x0a, - 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 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, 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, 0x39, 0x0a, 0x05, 0x48, 0x6f, 0x73, - 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 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, 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, 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, 0x42, 0x0a, 0x0e, 0x63, 0x61, 0x63, 0x68, - 0x65, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x64, 0x6e, 0x73, 0x2e, - 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x63, - 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x42, 0x0a, 0x0e, - 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x09, + 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x6b, 0x69, 0x70, 0x46, 0x61, + 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x73, 0x6b, + 0x69, 0x70, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x56, 0x0a, 0x12, 0x70, 0x72, + 0x69, 0x6f, 0x72, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 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, 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, 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, 0xd7, 0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x3f, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x78, 0x72, 0x61, 0x79, 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, 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, 0x39, 0x0a, + 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 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, 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, 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, 0x42, 0x0a, 0x0e, + 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x08, 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, - 0x1a, 0x55, 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, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 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, 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, - 0x2a, 0x44, 0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, - 0x79, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x00, - 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x4e, 0x4f, 0x45, 0x52, 0x52, 0x4f, - 0x52, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x44, 0x49, 0x53, - 0x41, 0x42, 0x4c, 0x45, 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, + 0x64, 0x6e, 0x73, 0x2e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x52, 0x0d, 0x63, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, + 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, 0x1a, 0x55, + 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, 0x31, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x78, 0x72, 0x61, 0x79, 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, 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, 0x2a, 0x44, + 0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, + 0x0d, 0x0a, 0x09, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x00, 0x12, 0x11, + 0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x4e, 0x4f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, + 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x5f, 0x44, 0x49, 0x53, 0x41, 0x42, + 0x4c, 0x45, 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, } var ( diff --git a/app/dns/config.proto b/app/dns/config.proto index 21b898d1..108a3429 100644 --- a/app/dns/config.proto +++ b/app/dns/config.proto @@ -13,6 +13,7 @@ import "app/router/config.proto"; message NameServer { xray.common.net.Endpoint address = 1; bytes client_ip = 5; + bool skipFallback = 6; message PriorityDomain { DomainMatchingType type = 1; @@ -84,8 +85,10 @@ message Config { reserved 7; - // DisableCache Disable DNS cache + // DisableCache disables DNS cache CacheStrategy cache_strategy = 8; QueryStrategy query_strategy = 9; + + bool disableFallback = 10; } diff --git a/app/dns/dns.go b/app/dns/dns.go index 4f7407a0..fc2d7829 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -22,14 +22,15 @@ import ( // DNS is a DNS rely server. type DNS struct { sync.Mutex - tag string - cs CacheStrategy - ipOption *dns.IPOption - hosts *StaticHosts - clients []*Client - ctx context.Context - domainMatcher strmatcher.IndexMatcher - matcherInfos []DomainMatcherInfo + tag string + cs CacheStrategy + disableFallback bool + ipOption *dns.IPOption + hosts *StaticHosts + clients []*Client + ctx context.Context + domainMatcher strmatcher.IndexMatcher + matcherInfos []DomainMatcherInfo } // DomainMatcherInfo contains information attached to index returned by Server.domainMatcher @@ -131,14 +132,15 @@ func New(ctx context.Context, config *Config) (*DNS, error) { } return &DNS{ - tag: tag, - hosts: hosts, - ipOption: ipOption, - clients: clients, - ctx: ctx, - domainMatcher: domainMatcher, - matcherInfos: matcherInfos, - cs: config.CacheStrategy, + tag: tag, + hosts: hosts, + ipOption: ipOption, + clients: clients, + ctx: ctx, + domainMatcher: domainMatcher, + matcherInfos: matcherInfos, + cs: config.CacheStrategy, + disableFallback: config.DisableFallback, }, nil } @@ -254,6 +256,11 @@ func (s *DNS) sortClients(domain string, option dns.IPOption) []*Client { if len(clientNames) > 0 { newError("domain ", domain, " will use DNS in order: ", clientNames).AtDebug().WriteToLog() } + if len(clients) == 0 { + clients = append(clients, s.clients[0]) + clientNames = append(clientNames, s.clients[0].Name()) + newError("domain ", domain, " will use the first DNS: ", clientNames).AtDebug().WriteToLog() + } }() // Priority domain matching @@ -265,7 +272,6 @@ func (s *DNS) sortClients(domain string, option dns.IPOption) []*Client { newError("skipping the client " + client.Name()).AtDebug().WriteToLog() continue } - domainRules = append(domainRules, fmt.Sprintf("%s(DNS idx:%d)", domainRule, info.clientIdx)) if clientUsed[info.clientIdx] { continue @@ -275,18 +281,22 @@ func (s *DNS) sortClients(domain string, option dns.IPOption) []*Client { clientNames = append(clientNames, client.Name()) } - // Default round-robin query - for idx, client := range s.clients { - if clientUsed[idx] { - continue + if !s.disableFallback { + // Default round-robin query + for idx, client := range s.clients { + if clientUsed[idx] || client.skipFallback { + continue + } + + if !canQueryOnClient(option, client) { + newError("skipping the client " + client.Name()).AtDebug().WriteToLog() + continue + } + + clientUsed[idx] = true + clients = append(clients, client) + clientNames = append(clientNames, client.Name()) } - if !canQueryOnClient(option, client) { - newError("skipping the client " + client.Name()).AtDebug().WriteToLog() - continue - } - clientUsed[idx] = true - clients = append(clients, client) - clientNames = append(clientNames, client.Name()) } return clients diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go index 14c41993..f0e546bb 100644 --- a/app/dns/nameserver.go +++ b/app/dns/nameserver.go @@ -25,10 +25,11 @@ type Server interface { // Client is the interface for DNS client. type Client struct { - server Server - clientIP net.IP - domains []string - expectIPs []*router.GeoIPMatcher + server Server + clientIP net.IP + skipFallback bool + domains []string + expectIPs []*router.GeoIPMatcher } var errExpectedIPNonMatch = errors.New("expectIPs not match") @@ -65,6 +66,7 @@ func NewServer(dest net.Destination, dispatcher routing.Dispatcher) (Server, err // NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs. func NewClient(ctx context.Context, ns *NameServer, clientIP net.IP, container router.GeoIPMatcherContainer, matcherInfos *[]DomainMatcherInfo, updateDomainRule func(strmatcher.Matcher, int, []DomainMatcherInfo) error) (*Client, error) { client := &Client{} + err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error { // Create a new server for each client for now server, err := NewServer(ns.Address.AsDestination(), dispatcher) diff --git a/infra/conf/dns.go b/infra/conf/dns.go index 003c1579..8ebf7c8e 100644 --- a/infra/conf/dns.go +++ b/infra/conf/dns.go @@ -11,11 +11,12 @@ import ( ) type NameServerConfig struct { - Address *Address - ClientIP *Address - Port uint16 - Domains []string - ExpectIPs StringList + Address *Address + ClientIP *Address + Port uint16 + SkipFallback bool + Domains []string + ExpectIPs StringList } func (c *NameServerConfig) UnmarshalJSON(data []byte) error { @@ -26,16 +27,18 @@ func (c *NameServerConfig) UnmarshalJSON(data []byte) error { } var advanced struct { - Address *Address `json:"address"` - ClientIP *Address `json:"clientIp"` - Port uint16 `json:"port"` - Domains []string `json:"domains"` - ExpectIPs StringList `json:"expectIps"` + Address *Address `json:"address"` + ClientIP *Address `json:"clientIp"` + Port uint16 `json:"port"` + SkipFallback bool `json:"skipFallback"` + Domains []string `json:"domains"` + ExpectIPs StringList `json:"expectIps"` } if err := json.Unmarshal(data, &advanced); err == nil { c.Address = advanced.Address c.ClientIP = advanced.ClientIP c.Port = advanced.Port + c.SkipFallback = advanced.SkipFallback c.Domains = advanced.Domains c.ExpectIPs = advanced.ExpectIPs return nil @@ -104,6 +107,7 @@ func (c *NameServerConfig) Build() (*dns.NameServer, error) { Port: uint32(c.Port), }, ClientIp: myClientIP, + SkipFallback: c.SkipFallback, PrioritizedDomain: domains, Geoip: geoipList, OriginalRules: originalRules, @@ -119,13 +123,14 @@ var typeMap = map[router.Domain_Type]dns.DomainMatchingType{ // DNSConfig is a JSON serializable object for dns.Config. type DNSConfig struct { - Servers []*NameServerConfig `json:"servers"` - Hosts map[string]*Address `json:"hosts"` - ClientIP *Address `json:"clientIp"` - Tag string `json:"tag"` - QueryStrategy string `json:"queryStrategy"` - CacheStrategy string `json:"cacheStrategy"` - DisableCache bool `json:"disableCache"` + Servers []*NameServerConfig `json:"servers"` + Hosts map[string]*Address `json:"hosts"` + ClientIP *Address `json:"clientIp"` + Tag string `json:"tag"` + QueryStrategy string `json:"queryStrategy"` + CacheStrategy string `json:"cacheStrategy"` + DisableCache bool `json:"disableCache"` + DisableFallback bool `json:"disableFallback"` } func getHostMapping(addr *Address) *dns.Config_HostMapping { @@ -143,8 +148,9 @@ func getHostMapping(addr *Address) *dns.Config_HostMapping { // Build implements Buildable func (c *DNSConfig) Build() (*dns.Config, error) { config := &dns.Config{ - Tag: c.Tag, - CacheStrategy: dns.CacheStrategy_Cache_ALL, + Tag: c.Tag, + CacheStrategy: dns.CacheStrategy_Cache_ALL, + DisableFallback: c.DisableFallback, } if c.DisableCache { diff --git a/infra/conf/dns_test.go b/infra/conf/dns_test.go index e2032831..8240d3ec 100644 --- a/infra/conf/dns_test.go +++ b/infra/conf/dns_test.go @@ -69,6 +69,7 @@ func TestDNSConfigParsing(t *testing.T) { "address": "8.8.8.8", "clientIp": "10.0.0.1", "port": 5353, + "skipFallback": true, "domains": ["domain:example.com"] }], "hosts": { @@ -80,7 +81,8 @@ func TestDNSConfigParsing(t *testing.T) { }, "clientIp": "10.0.0.1", "queryStrategy": "UseIPv4", - "disableCache": true + "disableCache": true, + "disableFallback": true }`, Parser: parserCreator(), Output: &dns.Config{ @@ -95,7 +97,8 @@ func TestDNSConfigParsing(t *testing.T) { Network: net.Network_UDP, Port: 5353, }, - ClientIp: []byte{10, 0, 0, 1}, + ClientIp: []byte{10, 0, 0, 1}, + SkipFallback: true, PrioritizedDomain: []*dns.NameServer_PriorityDomain{ { Type: dns.DomainMatchingType_Subdomain, @@ -137,9 +140,10 @@ func TestDNSConfigParsing(t *testing.T) { Ip: [][]byte{{8, 8, 4, 4}}, }, }, - ClientIp: []byte{10, 0, 0, 1}, - QueryStrategy: dns.QueryStrategy_USE_IP4, - CacheStrategy: dns.CacheStrategy_Cache_DISABLE, + ClientIp: []byte{10, 0, 0, 1}, + QueryStrategy: dns.QueryStrategy_USE_IP4, + CacheStrategy: dns.CacheStrategy_Cache_DISABLE, + DisableFallback: true, }, }, }) From f20c44597484e50220d4614b3e192be0bb24ace2 Mon Sep 17 00:00:00 2001 From: JimhHan <50871214+JimhHan@users.noreply.github.com> Date: Sat, 10 Apr 2021 00:33:55 +0800 Subject: [PATCH 4/5] Fix typo --- app/dns/dns.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/dns/dns.go b/app/dns/dns.go index fc2d7829..1ad75ada 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -198,7 +198,7 @@ func (s *DNS) lookupIPInternal(domain string, option dns.IPOption) ([]net.IP, er return nil, newError("empty domain name") } if isQuery(option) { - return nil, newError("empty option: I'm pretty sure it shouldn't happened.") + return nil, newError("empty option: Impossible.").AtWarning() } // Normalize the FQDN form query @@ -215,9 +215,10 @@ func (s *DNS) lookupIPInternal(domain string, option dns.IPOption) ([]net.IP, er case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Domain replacement newError("domain replaced: ", domain, " -> ", addrs[0].Domain()).WriteToLog() domain = addrs[0].Domain() - default: // Successfully found ip records in static host + default: + // Successfully found ip records in static host. + // Skip hosts mapping result in FakeDNS query. if isIPQuery(option) { - // maybe our client prefer to query fake dns -_- newError("returning ", len(addrs), " IPs for domain ", domain).WriteToLog() return toNetIP(addrs) } From 217844cc3763874dc37474070c19e1f2375bc0a8 Mon Sep 17 00:00:00 2001 From: JimhHan <50871214+JimhHan@users.noreply.github.com> Date: Sat, 10 Apr 2021 11:36:58 +0800 Subject: [PATCH 5/5] Refine DNS Options --- app/dns/dns.go | 25 ++++++++-------- app/dns/hosts.go | 6 ++-- app/dns/hosts_test.go | 6 ++-- app/dns/options.go | 14 ++------- features/dns/client.go | 44 ++++++++++++++++++++++++----- features/dns/localdns/client.go | 2 +- proxy/dns/dns.go | 10 +++---- proxy/freedom/freedom.go | 9 +++--- testing/mocks/dns.go | 13 ++++++--- transport/internet/system_dialer.go | 8 +++--- 10 files changed, 82 insertions(+), 55 deletions(-) diff --git a/app/dns/dns.go b/app/dns/dns.go index 1ad75ada..b76db087 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -167,33 +167,36 @@ func (s *DNS) IsOwnLink(ctx context.Context) bool { // LookupIP implements dns.Client. func (s *DNS) LookupIP(domain string) ([]net.IP, error) { - return s.lookupIPInternal(domain, *s.ipOption) + return s.lookupIPInternal(domain, s.ipOption.Copy()) } // LookupOptions implements dns.Client. -func (s *DNS) LookupOptions(domain string, opt dns.IPOption) ([]net.IP, error) { +func (s *DNS) LookupOptions(domain string, opts ...dns.Option) ([]net.IP, error) { + opt := s.ipOption.Copy() + for _, o := range opts { + if o != nil { + o(opt) + } + } + return s.lookupIPInternal(domain, opt) } // LookupIPv4 implements dns.IPv4Lookup. func (s *DNS) LookupIPv4(domain string) ([]net.IP, error) { - return s.lookupIPInternal(domain, dns.IPOption{ + return s.lookupIPInternal(domain, &dns.IPOption{ IPv4Enable: true, - IPv6Enable: false, - FakeEnable: false, }) } // LookupIPv6 implements dns.IPv6Lookup. func (s *DNS) LookupIPv6(domain string) ([]net.IP, error) { - return s.lookupIPInternal(domain, dns.IPOption{ - IPv4Enable: false, + return s.lookupIPInternal(domain, &dns.IPOption{ IPv6Enable: true, - FakeEnable: false, }) } -func (s *DNS) lookupIPInternal(domain string, option dns.IPOption) ([]net.IP, error) { +func (s *DNS) lookupIPInternal(domain string, option *dns.IPOption) ([]net.IP, error) { if domain == "" { return nil, newError("empty domain name") } @@ -228,7 +231,7 @@ func (s *DNS) lookupIPInternal(domain string, option dns.IPOption) ([]net.IP, er errs := []error{} ctx := session.ContextWithInbound(s.ctx, &session.Inbound{Tag: s.tag}) for _, client := range s.sortClients(domain, option) { - ips, err := client.QueryIP(ctx, domain, option, s.cs) + ips, err := client.QueryIP(ctx, domain, *option, s.cs) if len(ips) > 0 { return ips, nil } @@ -244,7 +247,7 @@ func (s *DNS) lookupIPInternal(domain string, option dns.IPOption) ([]net.IP, er return nil, newError("returning nil for domain ", domain).Base(errors.Combine(errs...)) } -func (s *DNS) sortClients(domain string, option dns.IPOption) []*Client { +func (s *DNS) sortClients(domain string, option *dns.IPOption) []*Client { clients := make([]*Client, 0, len(s.clients)) clientUsed := make([]bool, len(s.clients)) clientNames := make([]string, 0, len(s.clients)) diff --git a/app/dns/hosts.go b/app/dns/hosts.go index af315803..44b79c47 100644 --- a/app/dns/hosts.go +++ b/app/dns/hosts.go @@ -74,7 +74,7 @@ func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDoma return sh, nil } -func filterIP(ips []net.Address, option dns.IPOption) []net.Address { +func filterIP(ips []net.Address, option *dns.IPOption) []net.Address { filtered := make([]net.Address, 0, len(ips)) for _, ip := range ips { if (ip.Family().IsIPv4() && option.IPv4Enable) || (ip.Family().IsIPv6() && option.IPv6Enable) { @@ -95,7 +95,7 @@ func (h *StaticHosts) lookupInternal(domain string) []net.Address { return ips } -func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) []net.Address { +func (h *StaticHosts) lookup(domain string, option *dns.IPOption, maxDepth int) []net.Address { switch addrs := h.lookupInternal(domain); { case len(addrs) == 0: // Not recorded in static hosts, return nil return nil @@ -113,6 +113,6 @@ func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) [ } // Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts. -func (h *StaticHosts) Lookup(domain string, option dns.IPOption) []net.Address { +func (h *StaticHosts) Lookup(domain string, option *dns.IPOption) []net.Address { return h.lookup(domain, option, 5) } diff --git a/app/dns/hosts_test.go b/app/dns/hosts_test.go index 87e487bc..786aeca3 100644 --- a/app/dns/hosts_test.go +++ b/app/dns/hosts_test.go @@ -40,7 +40,7 @@ func TestStaticHosts(t *testing.T) { common.Must(err) { - ips := hosts.Lookup("example.com", dns.IPOption{ + ips := hosts.Lookup("example.com", &dns.IPOption{ IPv4Enable: true, IPv6Enable: true, }) @@ -53,7 +53,7 @@ func TestStaticHosts(t *testing.T) { } { - ips := hosts.Lookup("www.example.cn", dns.IPOption{ + ips := hosts.Lookup("www.example.cn", &dns.IPOption{ IPv4Enable: true, IPv6Enable: true, }) @@ -66,7 +66,7 @@ func TestStaticHosts(t *testing.T) { } { - ips := hosts.Lookup("baidu.com", dns.IPOption{ + ips := hosts.Lookup("baidu.com", &dns.IPOption{ IPv4Enable: false, IPv6Enable: true, }) diff --git a/app/dns/options.go b/app/dns/options.go index 2bedbe9f..06d93e29 100644 --- a/app/dns/options.go +++ b/app/dns/options.go @@ -2,23 +2,15 @@ package dns import "github.com/xtls/xray-core/features/dns" -type Option interface { - queryIPv4() bool - queryIPv6() bool - queryIP() bool - queryFake() bool - canDoQuery(c *Client) bool -} - -func isIPQuery(o dns.IPOption) bool { +func isIPQuery(o *dns.IPOption) bool { return o.IPv4Enable || o.IPv6Enable } -func canQueryOnClient(o dns.IPOption, c *Client) bool { +func canQueryOnClient(o *dns.IPOption, c *Client) bool { isIPClient := !(c.Name() == FakeDNSName) return isIPClient && isIPQuery(o) } -func isQuery(o dns.IPOption) bool { +func isQuery(o *dns.IPOption) bool { return !(o.IPv4Enable || o.IPv6Enable || o.FakeEnable) } diff --git a/features/dns/client.go b/features/dns/client.go index 93f8a68b..3fc9dce0 100644 --- a/features/dns/client.go +++ b/features/dns/client.go @@ -14,6 +14,12 @@ type IPOption struct { FakeEnable bool } +func (p *IPOption) Copy() *IPOption { + return &IPOption{p.IPv4Enable, p.IPv6Enable, p.FakeEnable} +} + +type Option func(dopt *IPOption) *IPOption + // Client is a Xray feature for querying DNS information. // // xray:api:stable @@ -23,8 +29,8 @@ type Client interface { // LookupIP returns IP address for the given domain. IPs may contain IPv4 and/or IPv6 addresses. LookupIP(domain string) ([]net.IP, error) - // LookupOptions query IP address for domain with IPOption. - LookupOptions(domain string, opt IPOption) ([]net.IP, error) + // LookupOptions query IP address for domain with *IPOption. + LookupOptions(domain string, opt ...Option) ([]net.IP, error) } // IPv4Lookup is an optional feature for querying IPv4 addresses only. @@ -69,9 +75,33 @@ func RCodeFromError(err error) uint16 { } var ( - LookupIPv4 = IPOption{IPv4Enable: true} - LookupIPv6 = IPOption{IPv6Enable: true} - LookupIP = IPOption{IPv4Enable: true, IPv6Enable: true} - LookupFake = IPOption{FakeEnable: true} - LookupAll = IPOption{true, true, true} + LookupIPv4Only = func(d *IPOption) *IPOption { + d.IPv4Enable = true + d.IPv6Enable = false + return d + } + LookupIPv6Only = func(d *IPOption) *IPOption { + d.IPv4Enable = false + d.IPv6Enable = true + return d + } + LookupIP = func(d *IPOption) *IPOption { + d.IPv4Enable = true + d.IPv6Enable = true + return d + } + LookupFake = func(d *IPOption) *IPOption { + d.FakeEnable = true + return d + } + LookupNoFake = func(d *IPOption) *IPOption { + d.FakeEnable = false + return d + } + + LookupAll = func(d *IPOption) *IPOption { + LookupIP(d) + LookupFake(d) + return d + } ) diff --git a/features/dns/localdns/client.go b/features/dns/localdns/client.go index 47d3590e..d47d29c0 100644 --- a/features/dns/localdns/client.go +++ b/features/dns/localdns/client.go @@ -39,7 +39,7 @@ func (*Client) LookupIP(host string) ([]net.IP, error) { } // LookupOptions implements Client. -func (c *Client) LookupOptions(host string, _ dns.IPOption) ([]net.IP, error) { +func (c *Client) LookupOptions(host string, _ ...dns.Option) ([]net.IP, error) { return c.LookupIP(host) } diff --git a/proxy/dns/dns.go b/proxy/dns/dns.go index 109af830..8b9fd957 100644 --- a/proxy/dns/dns.go +++ b/proxy/dns/dns.go @@ -199,18 +199,16 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, var err error var ttl uint32 = 600 - var opt = dns.LookupIP + var opt dns.Option switch qType { case dnsmessage.TypeA: - opt = dns.LookupIPv4 + opt = dns.LookupIPv4Only case dnsmessage.TypeAAAA: - opt = dns.LookupIPv6 + opt = dns.LookupIPv6Only } - opt.FakeEnable = true - - ips, err = h.client.LookupOptions(domain, opt) + ips, err = h.client.LookupOptions(domain, opt, dns.LookupFake) rcode := dns.RCodeFromError(err) if rcode == 0 && len(ips) == 0 && err != dns.ErrEmptyResponse { newError("ip query").Base(err).WriteToLog() diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index fe6db062..de01e072 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -59,15 +59,14 @@ func (h *Handler) policy() policy.Session { } func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address { - var opt = dns.LookupIP + var opt dns.Option if h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()) { - opt = dns.LookupIPv4 + opt = dns.LookupIPv4Only } else if h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()) { - opt = dns.LookupIPv6 + opt = dns.LookupIPv6Only } - opt.FakeEnable = true - ips, err := h.dns.LookupOptions(domain, opt) + ips, err := h.dns.LookupOptions(domain, opt, dns.LookupNoFake) if err != nil { newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx)) } diff --git a/testing/mocks/dns.go b/testing/mocks/dns.go index 21ccaeae..1178790c 100644 --- a/testing/mocks/dns.go +++ b/testing/mocks/dns.go @@ -65,18 +65,23 @@ func (mr *DNSClientMockRecorder) LookupIP(arg0 interface{}) *gomock.Call { } // LookupOptions mocks base method. -func (m *DNSClient) LookupOptions(arg0 string, arg1 dns.IPOption) ([]net.IP, error) { +func (m *DNSClient) LookupOptions(arg0 string, arg1 ...dns.Option) ([]net.IP, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LookupOptions", arg0, arg1) + varargs := []interface{}{arg0} + for _, a := range arg1 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "LookupOptions", varargs...) ret0, _ := ret[0].([]net.IP) ret1, _ := ret[1].(error) return ret0, ret1 } // LookupOptions indicates an expected call of LookupOptions. -func (mr *DNSClientMockRecorder) LookupOptions(arg0, arg1 interface{}) *gomock.Call { +func (mr *DNSClientMockRecorder) LookupOptions(arg0 interface{}, arg1 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupOptions", reflect.TypeOf((*DNSClient)(nil).LookupOptions), arg0, arg1) + varargs := append([]interface{}{arg0}, arg1...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupOptions", reflect.TypeOf((*DNSClient)(nil).LookupOptions), varargs...) } // Start mocks base method. diff --git a/transport/internet/system_dialer.go b/transport/internet/system_dialer.go index 7cc48645..22a9de8d 100644 --- a/transport/internet/system_dialer.go +++ b/transport/internet/system_dialer.go @@ -63,17 +63,17 @@ func (d *DefaultSystemDialer) lookupIP(domain string, strategy DomainStrategy, l return nil, nil } - var opt = dns.LookupIP + var opt dns.Option switch { case strategy == DomainStrategy_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()): - opt = dns.LookupIPv4 + opt = dns.LookupIPv4Only case strategy == DomainStrategy_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()): - opt = dns.LookupIPv6 + opt = dns.LookupIPv6Only case strategy == DomainStrategy_AS_IS: return nil, nil } - return d.dns.LookupOptions(domain, opt) + return d.dns.LookupOptions(domain, opt, dns.LookupNoFake) } func (d *DefaultSystemDialer) canLookupIP(ctx context.Context, dst net.Destination, sockopt *SocketConfig) bool {