diff --git a/app/dns/config.pb.go b/app/dns/config.pb.go index ae6b3a6d..1f812c43 100644 --- a/app/dns/config.pb.go +++ b/app/dns/config.pb.go @@ -79,6 +79,55 @@ func (DomainMatchingType) EnumDescriptor() ([]byte, []int) { return file_app_dns_config_proto_rawDescGZIP(), []int{0} } +type QueryStrategy int32 + +const ( + QueryStrategy_USE_IP QueryStrategy = 0 + QueryStrategy_USE_IP4 QueryStrategy = 1 + QueryStrategy_USE_IP6 QueryStrategy = 2 +) + +// Enum value maps for QueryStrategy. +var ( + QueryStrategy_name = map[int32]string{ + 0: "USE_IP", + 1: "USE_IP4", + 2: "USE_IP6", + } + QueryStrategy_value = map[string]int32{ + "USE_IP": 0, + "USE_IP4": 1, + "USE_IP6": 2, + } +) + +func (x QueryStrategy) Enum() *QueryStrategy { + p := new(QueryStrategy) + *p = x + return p +} + +func (x QueryStrategy) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (QueryStrategy) Descriptor() protoreflect.EnumDescriptor { + return file_app_dns_config_proto_enumTypes[1].Descriptor() +} + +func (QueryStrategy) Type() protoreflect.EnumType { + return &file_app_dns_config_proto_enumTypes[1] +} + +func (x QueryStrategy) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use QueryStrategy.Descriptor instead. +func (QueryStrategy) EnumDescriptor() ([]byte, []int) { + return file_app_dns_config_proto_rawDescGZIP(), []int{1} +} + type NameServer struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -183,7 +232,8 @@ 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"` + DisableCache bool `protobuf:"varint,8,opt,name=disableCache,proto3" json:"disableCache,omitempty"` + QueryStrategy QueryStrategy `protobuf:"varint,9,opt,name=query_strategy,json=queryStrategy,proto3,enum=xray.app.dns.QueryStrategy" json:"query_strategy,omitempty"` } func (x *Config) Reset() { @@ -269,6 +319,13 @@ func (x *Config) GetDisableCache() bool { return false } +func (x *Config) GetQueryStrategy() QueryStrategy { + if x != nil { + return x.QueryStrategy + } + return QueryStrategy_USE_IP +} + type NameServer_PriorityDomain struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -492,7 +549,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, 0xc9, 0x04, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3f, 0x0a, + 0x7a, 0x65, 0x22, 0x8d, 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, @@ -513,32 +570,39 @@ var file_app_dns_config_proto_rawDesc = []byte{ 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, 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, 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, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x42, 0x0a, 0x0e, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, + 0x64, 0x6e, 0x73, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x52, 0x0d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, + 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, + 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 ( @@ -553,37 +617,39 @@ 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, 1) +var file_app_dns_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2) 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 - (*NameServer)(nil), // 1: xray.app.dns.NameServer - (*Config)(nil), // 2: xray.app.dns.Config - (*NameServer_PriorityDomain)(nil), // 3: xray.app.dns.NameServer.PriorityDomain - (*NameServer_OriginalRule)(nil), // 4: xray.app.dns.NameServer.OriginalRule - nil, // 5: xray.app.dns.Config.HostsEntry - (*Config_HostMapping)(nil), // 6: xray.app.dns.Config.HostMapping - (*net.Endpoint)(nil), // 7: xray.common.net.Endpoint - (*router.GeoIP)(nil), // 8: xray.app.router.GeoIP - (*net.IPOrDomain)(nil), // 9: xray.common.net.IPOrDomain + (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 } var file_app_dns_config_proto_depIdxs = []int32{ - 7, // 0: xray.app.dns.NameServer.address:type_name -> xray.common.net.Endpoint - 3, // 1: xray.app.dns.NameServer.prioritized_domain:type_name -> xray.app.dns.NameServer.PriorityDomain - 8, // 2: xray.app.dns.NameServer.geoip:type_name -> xray.app.router.GeoIP - 4, // 3: xray.app.dns.NameServer.original_rules:type_name -> xray.app.dns.NameServer.OriginalRule - 7, // 4: xray.app.dns.Config.NameServers:type_name -> xray.common.net.Endpoint - 1, // 5: xray.app.dns.Config.name_server:type_name -> xray.app.dns.NameServer - 5, // 6: xray.app.dns.Config.Hosts:type_name -> xray.app.dns.Config.HostsEntry - 6, // 7: xray.app.dns.Config.static_hosts:type_name -> xray.app.dns.Config.HostMapping - 0, // 8: xray.app.dns.NameServer.PriorityDomain.type:type_name -> xray.app.dns.DomainMatchingType - 9, // 9: xray.app.dns.Config.HostsEntry.value:type_name -> xray.common.net.IPOrDomain - 0, // 10: xray.app.dns.Config.HostMapping.type:type_name -> xray.app.dns.DomainMatchingType - 11, // [11:11] is the sub-list for method output_type - 11, // [11:11] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name + 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 } func init() { file_app_dns_config_proto_init() } @@ -658,7 +724,7 @@ func file_app_dns_config_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_app_dns_config_proto_rawDesc, - NumEnums: 1, + NumEnums: 2, NumMessages: 6, NumExtensions: 0, NumServices: 0, diff --git a/app/dns/config.proto b/app/dns/config.proto index 3004d8d7..5ba5c4c2 100644 --- a/app/dns/config.proto +++ b/app/dns/config.proto @@ -36,6 +36,12 @@ enum DomainMatchingType { Regex = 3; } +enum QueryStrategy { + USE_IP = 0; + USE_IP4 = 1; + USE_IP6 = 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 @@ -73,5 +79,7 @@ message Config { reserved 7; // DisableCache Disable DNS cache - bool disableCache = 8; + bool disableCache = 8; + + QueryStrategy query_strategy = 9; } diff --git a/app/dns/dns.go b/app/dns/dns.go index 5d9cc0e2..90c342bc 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -24,6 +24,7 @@ type DNS struct { sync.Mutex tag string disableCache bool + ipOption *dns.IPOption hosts *StaticHosts clients []*Client ctx context.Context @@ -54,6 +55,28 @@ func New(ctx context.Context, config *Config) (*DNS, error) { return nil, newError("unexpected client IP length ", len(config.ClientIp)) } + var ipOption *dns.IPOption + switch config.QueryStrategy { + case QueryStrategy_USE_IP: + ipOption = &dns.IPOption{ + IPv4Enable: true, + IPv6Enable: true, + FakeEnable: false, + } + case QueryStrategy_USE_IP4: + ipOption = &dns.IPOption{ + IPv4Enable: true, + IPv6Enable: false, + FakeEnable: false, + } + case QueryStrategy_USE_IP6: + ipOption = &dns.IPOption{ + IPv4Enable: false, + IPv6Enable: true, + FakeEnable: false, + } + } + hosts, err := NewStaticHosts(config.StaticHosts, config.Hosts) if err != nil { return nil, newError("failed to create hosts").Base(err) @@ -110,6 +133,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) { return &DNS{ tag: tag, hosts: hosts, + ipOption: ipOption, clients: clients, ctx: ctx, domainMatcher: domainMatcher, @@ -140,7 +164,33 @@ func (s *DNS) IsOwnLink(ctx context.Context) bool { } // LookupIP implements dns.Client. -func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) { +func (s *DNS) LookupIP(domain string) ([]net.IP, error) { + return s.lookupIPInternal(domain, dns.IPOption{ + IPv4Enable: true, + IPv6Enable: true, + FakeEnable: false, + }) +} + +// LookupIPv4 implements dns.IPv4Lookup. +func (s *DNS) LookupIPv4(domain string) ([]net.IP, error) { + 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, + IPv6Enable: true, + FakeEnable: false, + }) +} + +func (s *DNS) lookupIPInternal(domain string, option dns.IPOption) ([]net.IP, error) { if domain == "" { return nil, newError("empty domain name") } @@ -188,6 +238,22 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) { 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 { clients := make([]*Client, 0, len(s.clients)) clientUsed := make([]bool, len(s.clients)) diff --git a/app/dns/dns_test.go b/app/dns/dns_test.go index feb382fa..8c96eeea 100644 --- a/app/dns/dns_test.go +++ b/app/dns/dns_test.go @@ -154,11 +154,7 @@ func TestUDPServerSubnet(t *testing.T) { client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) - ips, err := client.LookupIP("google.com", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } @@ -213,11 +209,7 @@ func TestUDPServer(t *testing.T) { client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) { - ips, err := client.LookupIP("google.com", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } @@ -228,11 +220,7 @@ func TestUDPServer(t *testing.T) { } { - ips, err := client.LookupIP("facebook.com", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("facebook.com") if err != nil { t.Fatal("unexpected error: ", err) } @@ -243,11 +231,7 @@ func TestUDPServer(t *testing.T) { } { - _, err := client.LookupIP("notexist.google.com", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + _, err := client.LookupIP("notexist.google.com") if err == nil { t.Fatal("nil error") } @@ -257,11 +241,8 @@ func TestUDPServer(t *testing.T) { } { - ips, err := client.LookupIP("ipv4only.google.com", feature_dns.IPOption{ - IPv4Enable: false, - IPv6Enable: true, - FakeEnable: false, - }) + clientv6 := client.(feature_dns.IPv6Lookup) + ips, err := clientv6.LookupIPv6("ipv4only.google.com") if err != feature_dns.ErrEmptyResponse { t.Fatal("error: ", err) } @@ -273,11 +254,7 @@ func TestUDPServer(t *testing.T) { dnsServer.Shutdown() { - ips, err := client.LookupIP("google.com", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } @@ -354,11 +331,7 @@ func TestPrioritizedDomain(t *testing.T) { startTime := time.Now() { - ips, err := client.LookupIP("google.com", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } @@ -417,12 +390,9 @@ func TestUDPServerIPv6(t *testing.T) { common.Must(err) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) + client6 := client.(feature_dns.IPv6Lookup) { - ips, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{ - IPv4Enable: false, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client6.LookupIPv6("ipv6.google.com") if err != nil { t.Fatal("unexpected error: ", err) } @@ -485,11 +455,7 @@ func TestStaticHostDomain(t *testing.T) { client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) { - ips, err := client.LookupIP("example.com", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("example.com") if err != nil { t.Fatal("unexpected error: ", err) } @@ -596,11 +562,7 @@ func TestIPMatch(t *testing.T) { startTime := time.Now() { - ips, err := client.LookupIP("google.com", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } @@ -719,11 +681,7 @@ func TestLocalDomain(t *testing.T) { startTime := time.Now() { // Will match dotless: - ips, err := client.LookupIP("hostname", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("hostname") if err != nil { t.Fatal("unexpected error: ", err) } @@ -734,11 +692,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match domain:local - ips, err := client.LookupIP("hostname.local", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("hostname.local") if err != nil { t.Fatal("unexpected error: ", err) } @@ -749,11 +703,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match static ip - ips, err := client.LookupIP("hostnamestatic", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("hostnamestatic") if err != nil { t.Fatal("unexpected error: ", err) } @@ -764,11 +714,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match domain replacing - ips, err := client.LookupIP("hostnamealias", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("hostnamealias") if err != nil { t.Fatal("unexpected error: ", err) } @@ -779,11 +725,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match dotless:localhost, but not expectIPs: 127.0.0.2, 127.0.0.3, then matches at dotless: - ips, err := client.LookupIP("localhost", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("localhost") if err != nil { t.Fatal("unexpected error: ", err) } @@ -794,11 +736,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3 - ips, err := client.LookupIP("localhost-a", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("localhost-a") if err != nil { t.Fatal("unexpected error: ", err) } @@ -809,11 +747,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3 - ips, err := client.LookupIP("localhost-b", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("localhost-b") if err != nil { t.Fatal("unexpected error: ", err) } @@ -824,11 +758,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match dotless: - ips, err := client.LookupIP("Mijia Cloud", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("Mijia Cloud") if err != nil { t.Fatal("unexpected error: ", err) } @@ -990,11 +920,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { startTime := time.Now() { // Will match server 1,2 and server 1 returns expected ip - ips, err := client.LookupIP("google.com", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("google.com") if err != nil { t.Fatal("unexpected error: ", err) } @@ -1005,11 +931,8 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { } { // Will match server 1,2 and server 1 returns unexpected ip, then server 2 returns expected one - ips, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: false, - FakeEnable: false, - }) + clientv4 := client.(feature_dns.IPv4Lookup) + ips, err := clientv4.LookupIPv4("ipv6.google.com") if err != nil { t.Fatal("unexpected error: ", err) } @@ -1020,11 +943,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { } { // Will match server 3,1,2 and server 3 returns expected one - ips, err := client.LookupIP("api.google.com", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("api.google.com") if err != nil { t.Fatal("unexpected error: ", err) } @@ -1035,11 +954,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { } { // Will match server 4,3,1,2 and server 4 returns expected one - ips, err := client.LookupIP("v2.api.google.com", feature_dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }) + ips, err := client.LookupIP("v2.api.google.com") if err != nil { t.Fatal("unexpected error: ", err) } diff --git a/app/dns/nameserver_local.go b/app/dns/nameserver_local.go index 0ebc1ddb..e1f859d2 100644 --- a/app/dns/nameserver_local.go +++ b/app/dns/nameserver_local.go @@ -15,11 +15,23 @@ type LocalNameServer struct { // QueryIP implements Server. func (s *LocalNameServer) QueryIP(_ context.Context, domain string, _ net.IP, option dns.IPOption, _ bool) ([]net.IP, error) { - if option.IPv4Enable || option.IPv6Enable { - return s.client.LookupIP(domain, option) + var ips []net.IP + var err error + + switch { + case option.IPv4Enable && option.IPv6Enable: + ips, err = s.client.LookupIP(domain) + case option.IPv4Enable: + ips, err = s.client.LookupIPv4(domain) + case option.IPv6Enable: + ips, err = s.client.LookupIPv6(domain) } - return nil, newError("neither IPv4 nor IPv6 is enabled") + if len(ips) > 0 { + newError("Localhost got answer: ", domain, " -> ", ips).AtInfo().WriteToLog() + } + + return ips, err } // Name implements Server. diff --git a/app/router/router_test.go b/app/router/router_test.go index 14ce8a3d..cd0b7154 100644 --- a/app/router/router_test.go +++ b/app/router/router_test.go @@ -9,7 +9,6 @@ import ( "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/session" - "github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/outbound" routing_session "github.com/xtls/xray-core/features/routing/session" "github.com/xtls/xray-core/testing/mocks" @@ -116,11 +115,7 @@ func TestIPOnDemand(t *testing.T) { defer mockCtl.Finish() mockDNS := mocks.NewDNSClient(mockCtl) - mockDNS.EXPECT().LookupIP(gomock.Eq("example.com"), dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes() + mockDNS.EXPECT().LookupIP(gomock.Eq("example.com")).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes() r := new(Router) common.Must(r.Init(config, mockDNS, nil)) @@ -155,11 +150,7 @@ func TestIPIfNonMatchDomain(t *testing.T) { defer mockCtl.Finish() mockDNS := mocks.NewDNSClient(mockCtl) - mockDNS.EXPECT().LookupIP(gomock.Eq("example.com"), dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, - }).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes() + mockDNS.EXPECT().LookupIP(gomock.Eq("example.com")).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes() r := new(Router) common.Must(r.Init(config, mockDNS, nil)) diff --git a/features/dns/client.go b/features/dns/client.go index 584e24f8..2855da38 100644 --- a/features/dns/client.go +++ b/features/dns/client.go @@ -21,7 +21,35 @@ type Client interface { features.Feature // LookupIP returns IP address for the given domain. IPs may contain IPv4 and/or IPv6 addresses. - LookupIP(domain string, option IPOption) ([]net.IP, error) + LookupIP(domain string) ([]net.IP, error) +} + +// IPv4Lookup is an optional feature for querying IPv4 addresses only. +// +// xray:api:beta +type IPv4Lookup interface { + LookupIPv4(domain string) ([]net.IP, error) +} + +// IPv6Lookup is an optional feature for querying IPv6 addresses only. +// +// xray:api:beta +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. diff --git a/features/dns/localdns/client.go b/features/dns/localdns/client.go index 92419dfa..e8b367ca 100644 --- a/features/dns/localdns/client.go +++ b/features/dns/localdns/client.go @@ -20,41 +20,59 @@ func (*Client) Start() error { return nil } func (*Client) Close() error { return nil } // LookupIP implements Client. -func (*Client) LookupIP(host string, option dns.IPOption) ([]net.IP, error) { +func (*Client) LookupIP(host string) ([]net.IP, error) { ips, err := net.LookupIP(host) if err != nil { return nil, err } parsedIPs := make([]net.IP, 0, len(ips)) - ipv4 := make([]net.IP, 0, len(ips)) - ipv6 := make([]net.IP, 0, len(ips)) for _, ip := range ips { parsed := net.IPAddress(ip) if parsed != nil { parsedIPs = append(parsedIPs, parsed.IP()) } + } + if len(parsedIPs) == 0 { + return nil, dns.ErrEmptyResponse + } + return parsedIPs, nil +} + +// LookupIPv4 implements IPv4Lookup. +func (c *Client) LookupIPv4(host string) ([]net.IP, error) { + ips, err := c.LookupIP(host) + if err != nil { + return nil, err + } + ipv4 := make([]net.IP, 0, len(ips)) + for _, ip := range ips { if len(ip) == net.IPv4len { ipv4 = append(ipv4, ip) } + } + if len(ipv4) == 0 { + return nil, dns.ErrEmptyResponse + } + return ipv4, nil +} + +// LookupIPv6 implements IPv6Lookup. +func (c *Client) LookupIPv6(host string) ([]net.IP, error) { + ips, err := c.LookupIP(host) + if err != nil { + return nil, err + } + ipv6 := make([]net.IP, 0, len(ips)) + for _, ip := range ips { if len(ip) == net.IPv6len { ipv6 = append(ipv6, ip) } } - switch { - case option.IPv4Enable && option.IPv6Enable: - if len(parsedIPs) > 0 { - return parsedIPs, nil - } - case option.IPv4Enable: - if len(ipv4) > 0 { - return ipv4, nil - } - case option.IPv6Enable: - if len(ipv6) > 0 { - return ipv6, nil - } + if len(ipv6) == 0 { + return nil, dns.ErrEmptyResponse } - return nil, dns.ErrEmptyResponse + + return ipv6, nil } // New create a new dns.Client that queries localhost for DNS. diff --git a/features/routing/dns/context.go b/features/routing/dns/context.go index b4d07717..0f61ab3a 100644 --- a/features/routing/dns/context.go +++ b/features/routing/dns/context.go @@ -26,11 +26,35 @@ func (ctx *ResolvableContext) GetTargetIPs() []net.IP { } if domain := ctx.GetTargetDomain(); len(domain) != 0 { - ips, err := ctx.dnsClient.LookupIP(domain, dns.IPOption{ + var lookupFunc func(string) ([]net.IP, error) = ctx.dnsClient.LookupIP + ipOption := &dns.IPOption{ IPv4Enable: true, IPv6Enable: true, - FakeEnable: false, - }) + } + + 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) if err == nil { ctx.resolvedIPs = ips return ips diff --git a/infra/conf/dns.go b/infra/conf/dns.go index 7e0bd411..b1c98825 100644 --- a/infra/conf/dns.go +++ b/infra/conf/dns.go @@ -108,11 +108,12 @@ 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"` - 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"` + DisableCache bool `json:"disableCache"` } func getHostMapping(addr *Address) *dns.Config_HostMapping { @@ -141,6 +142,16 @@ func (c *DNSConfig) Build() (*dns.Config, error) { config.ClientIp = []byte(c.ClientIP.IP()) } + config.QueryStrategy = dns.QueryStrategy_USE_IP + switch strings.ToLower(c.QueryStrategy) { + case "useip", "use_ip", "use-ip": + config.QueryStrategy = dns.QueryStrategy_USE_IP + case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": + config.QueryStrategy = dns.QueryStrategy_USE_IP4 + case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": + config.QueryStrategy = dns.QueryStrategy_USE_IP6 + } + 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 421fb877..00284fbb 100644 --- a/infra/conf/dns_test.go +++ b/infra/conf/dns_test.go @@ -77,7 +77,9 @@ func TestDNSConfigParsing(t *testing.T) { "keyword:google": "8.8.8.8", "regexp:.*\\.com": "8.8.4.4" }, - "clientIp": "10.0.0.1" + "clientIp": "10.0.0.1", + "queryStrategy": "UseIPv4", + "disableCache": true }`, Parser: parserCreator(), Output: &dns.Config{ @@ -133,7 +135,9 @@ func TestDNSConfigParsing(t *testing.T) { Ip: [][]byte{{8, 8, 4, 4}}, }, }, - ClientIp: []byte{10, 0, 0, 1}, + ClientIp: []byte{10, 0, 0, 1}, + QueryStrategy: dns.QueryStrategy_USE_IP4, + DisableCache: true, }, }, }) diff --git a/infra/conf/freedom.go b/infra/conf/freedom.go index 29e778d2..60dfd5b8 100644 --- a/infra/conf/freedom.go +++ b/infra/conf/freedom.go @@ -22,11 +22,11 @@ func (c *FreedomConfig) Build() (proto.Message, error) { config := new(freedom.Config) config.DomainStrategy = freedom.Config_AS_IS switch strings.ToLower(c.DomainStrategy) { - case "useip", "use_ip": + case "useip", "use_ip", "use-ip": config.DomainStrategy = freedom.Config_USE_IP - case "useip4", "useipv4", "use_ipv4", "use_ip_v4", "use_ip4": + case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": config.DomainStrategy = freedom.Config_USE_IP4 - case "useip6", "useipv6", "use_ipv6", "use_ip_v6", "use_ip6": + case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": config.DomainStrategy = freedom.Config_USE_IP6 } diff --git a/proxy/dns/dns.go b/proxy/dns/dns.go index 01c02af6..32e5fd59 100644 --- a/proxy/dns/dns.go +++ b/proxy/dns/dns.go @@ -37,12 +37,27 @@ type ownLinkVerifier interface { type Handler struct { client dns.Client + ipv4Lookup dns.IPv4Lookup + ipv6Lookup dns.IPv6Lookup ownLinkVerifier ownLinkVerifier server net.Destination } 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 } @@ -199,19 +214,18 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, 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") + } + switch qType { case dnsmessage.TypeA: - ips, err = h.client.LookupIP(domain, dns.IPOption{ - IPv4Enable: true, - IPv6Enable: false, - FakeEnable: true, - }) + ips, err = h.ipv4Lookup.LookupIPv4(domain) case dnsmessage.TypeAAAA: - ips, err = h.client.LookupIP(domain, dns.IPOption{ - IPv4Enable: false, - IPv6Enable: true, - FakeEnable: true, - }) + ips, err = h.ipv6Lookup.LookupIPv6(domain) } rcode := dns.RCodeFromError(err) diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index a1f99cc4..0da6f4da 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -59,26 +59,24 @@ func (h *Handler) policy() policy.Session { } func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address { - var option dns.IPOption = dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, + 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 if h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()) { - option = dns.IPOption{ - IPv4Enable: true, - IPv6Enable: false, - FakeEnable: false, + if lookupIPv4, ok := h.dns.(dns.IPv4Lookup); ok { + lookupFunc = lookupIPv4.LookupIPv4 } } else if h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()) { - option = dns.IPOption{ - IPv4Enable: false, - IPv6Enable: true, - FakeEnable: false, + if lookupIPv6, ok := h.dns.(dns.IPv6Lookup); ok { + lookupFunc = lookupIPv6.LookupIPv6 } } - ips, err := h.dns.LookupIP(domain, option) + ips, err := lookupFunc(domain) 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 73a219ed..8fa59372 100644 --- a/testing/mocks/dns.go +++ b/testing/mocks/dns.go @@ -1,40 +1,40 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/xtls/xray-core/features/dns (interfaces: Client) +// Source: github.com/xray/xray-core/v4/features/dns (interfaces: Client) // Package mocks is a generated GoMock package. package mocks import ( - gomock "github.com/golang/mock/gomock" - dns "github.com/xtls/xray-core/features/dns" net "net" reflect "reflect" + + gomock "github.com/golang/mock/gomock" ) -// DNSClient is a mock of Client interface +// DNSClient is a mock of Client interface. type DNSClient struct { ctrl *gomock.Controller recorder *DNSClientMockRecorder } -// DNSClientMockRecorder is the mock recorder for DNSClient +// DNSClientMockRecorder is the mock recorder for DNSClient. type DNSClientMockRecorder struct { mock *DNSClient } -// NewDNSClient creates a new mock instance +// NewDNSClient creates a new mock instance. func NewDNSClient(ctrl *gomock.Controller) *DNSClient { mock := &DNSClient{ctrl: ctrl} mock.recorder = &DNSClientMockRecorder{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 *DNSClient) EXPECT() *DNSClientMockRecorder { return m.recorder } -// Close mocks base method +// Close mocks base method. func (m *DNSClient) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") @@ -42,28 +42,28 @@ func (m *DNSClient) Close() error { return ret0 } -// Close indicates an expected call of Close +// Close indicates an expected call of Close. func (mr *DNSClientMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*DNSClient)(nil).Close)) } -// LookupIP mocks base method -func (m *DNSClient) LookupIP(arg0 string, arg1 dns.IPOption) ([]net.IP, error) { +// LookupIP mocks base method. +func (m *DNSClient) LookupIP(arg0 string) ([]net.IP, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "LookupIP", arg0, arg1) + ret := m.ctrl.Call(m, "LookupIP", arg0) ret0, _ := ret[0].([]net.IP) ret1, _ := ret[1].(error) return ret0, ret1 } -// LookupIP indicates an expected call of LookupIP -func (mr *DNSClientMockRecorder) LookupIP(arg0, arg1 interface{}) *gomock.Call { +// LookupIP indicates an expected call of LookupIP. +func (mr *DNSClientMockRecorder) LookupIP(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupIP", reflect.TypeOf((*DNSClient)(nil).LookupIP), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupIP", reflect.TypeOf((*DNSClient)(nil).LookupIP), arg0) } -// Start mocks base method +// Start mocks base method. func (m *DNSClient) Start() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Start") @@ -71,13 +71,13 @@ func (m *DNSClient) Start() error { return ret0 } -// Start indicates an expected call of Start +// Start indicates an expected call of Start. func (mr *DNSClientMockRecorder) Start() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*DNSClient)(nil).Start)) } -// Type mocks base method +// Type mocks base method. func (m *DNSClient) Type() interface{} { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Type") @@ -85,7 +85,7 @@ func (m *DNSClient) Type() interface{} { return ret0 } -// Type indicates an expected call of Type +// Type indicates an expected call of Type. func (mr *DNSClientMockRecorder) Type() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Type", reflect.TypeOf((*DNSClient)(nil).Type)) diff --git a/transport/internet/system_dialer.go b/transport/internet/system_dialer.go index 43c9309f..605fd7dc 100644 --- a/transport/internet/system_dialer.go +++ b/transport/internet/system_dialer.go @@ -63,30 +63,27 @@ func (d *DefaultSystemDialer) lookupIP(domain string, strategy DomainStrategy, l return nil, nil } - var option = dns.IPOption{ - IPv4Enable: true, - IPv6Enable: true, - FakeEnable: false, + 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 switch { case strategy == DomainStrategy_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()): - option = dns.IPOption{ - IPv4Enable: true, - IPv6Enable: false, - FakeEnable: false, + if lookupIPv4, ok := d.dns.(dns.IPv4Lookup); ok { + lookupFunc = lookupIPv4.LookupIPv4 } case strategy == DomainStrategy_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()): - option = dns.IPOption{ - IPv4Enable: false, - IPv6Enable: true, - FakeEnable: false, + if lookupIPv6, ok := d.dns.(dns.IPv6Lookup); ok { + lookupFunc = lookupIPv6.LookupIPv6 } case strategy == DomainStrategy_AS_IS: return nil, nil } - return d.dns.LookupIP(domain, option) + return lookupFunc(domain) } func (d *DefaultSystemDialer) doLookupIP(ctx context.Context, dst net.Destination, sockopt *SocketConfig) bool {