diff --git a/infra/conf/transport_authenticators.go b/infra/conf/transport_authenticators.go index 703a1366..46be8588 100644 --- a/infra/conf/transport_authenticators.go +++ b/infra/conf/transport_authenticators.go @@ -4,6 +4,7 @@ import ( "sort" "github.com/golang/protobuf/proto" + "github.com/xtls/xray-core/transport/internet/headers/dns" "github.com/xtls/xray-core/transport/internet/headers/http" "github.com/xtls/xray-core/transport/internet/headers/noop" "github.com/xtls/xray-core/transport/internet/headers/srtp" @@ -49,6 +50,19 @@ func (WireguardAuthenticator) Build() (proto.Message, error) { return new(wireguard.WireguardConfig), nil } +type DNSAuthenticator struct { + Domain string `json:"domain"` +} + +func (v *DNSAuthenticator) Build() (proto.Message, error) { + config := new(dns.Config) + config.Domain = "www.baidu.com" + if len(v.Domain) > 0 { + config.Domain = v.Domain + } + return config, nil +} + type DTLSAuthenticator struct{} func (DTLSAuthenticator) Build() (proto.Message, error) { diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index 21cc7e22..92abb688 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -37,6 +37,7 @@ var ( "wechat-video": func() interface{} { return new(WechatVideoAuthenticator) }, "dtls": func() interface{} { return new(DTLSAuthenticator) }, "wireguard": func() interface{} { return new(WireguardAuthenticator) }, + "dns": func() interface{} { return new(DNSAuthenticator) }, }, "type", "") tcpHeaderLoader = NewJSONConfigLoader(ConfigCreatorCache{ diff --git a/transport/internet/headers/dns/config.pb.go b/transport/internet/headers/dns/config.pb.go new file mode 100644 index 00000000..d42f537e --- /dev/null +++ b/transport/internet/headers/dns/config.pb.go @@ -0,0 +1,153 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.12 +// source: transport/internet/headers/dns/config.proto + +package dns + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Config struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Domain string `protobuf:"bytes,1,opt,name=domain,proto3" json:"domain,omitempty"` +} + +func (x *Config) Reset() { + *x = Config{} + if protoimpl.UnsafeEnabled { + mi := &file_transport_internet_headers_dns_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Config) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Config) ProtoMessage() {} + +func (x *Config) ProtoReflect() protoreflect.Message { + mi := &file_transport_internet_headers_dns_config_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Config.ProtoReflect.Descriptor instead. +func (*Config) Descriptor() ([]byte, []int) { + return file_transport_internet_headers_dns_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Config) GetDomain() string { + if x != nil { + return x.Domain + } + return "" +} + +var File_transport_internet_headers_dns_config_proto protoreflect.FileDescriptor + +var file_transport_internet_headers_dns_config_proto_rawDesc = []byte{ + 0x0a, 0x2b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x64, 0x6e, 0x73, + 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x78, + 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x64, + 0x6e, 0x73, 0x22, 0x20, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, + 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x42, 0x8b, 0x01, 0x0a, 0x27, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, + 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x64, 0x6e, 0x73, + 0x50, 0x01, 0x5a, 0x38, 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, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, + 0x2f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2f, 0x64, 0x6e, 0x73, 0xaa, 0x02, 0x23, 0x58, + 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x44, + 0x4e, 0x53, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_transport_internet_headers_dns_config_proto_rawDescOnce sync.Once + file_transport_internet_headers_dns_config_proto_rawDescData = file_transport_internet_headers_dns_config_proto_rawDesc +) + +func file_transport_internet_headers_dns_config_proto_rawDescGZIP() []byte { + file_transport_internet_headers_dns_config_proto_rawDescOnce.Do(func() { + file_transport_internet_headers_dns_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_internet_headers_dns_config_proto_rawDescData) + }) + return file_transport_internet_headers_dns_config_proto_rawDescData +} + +var file_transport_internet_headers_dns_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_transport_internet_headers_dns_config_proto_goTypes = []interface{}{ + (*Config)(nil), // 0: xray.transport.internet.headers.dns.Config +} +var file_transport_internet_headers_dns_config_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_transport_internet_headers_dns_config_proto_init() } +func file_transport_internet_headers_dns_config_proto_init() { + if File_transport_internet_headers_dns_config_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_transport_internet_headers_dns_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_transport_internet_headers_dns_config_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_transport_internet_headers_dns_config_proto_goTypes, + DependencyIndexes: file_transport_internet_headers_dns_config_proto_depIdxs, + MessageInfos: file_transport_internet_headers_dns_config_proto_msgTypes, + }.Build() + File_transport_internet_headers_dns_config_proto = out.File + file_transport_internet_headers_dns_config_proto_rawDesc = nil + file_transport_internet_headers_dns_config_proto_goTypes = nil + file_transport_internet_headers_dns_config_proto_depIdxs = nil +} diff --git a/transport/internet/headers/dns/config.proto b/transport/internet/headers/dns/config.proto new file mode 100644 index 00000000..a9a44ff4 --- /dev/null +++ b/transport/internet/headers/dns/config.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package xray.transport.internet.headers.dns; +option csharp_namespace = "Xray.Transport.Internet.Headers.DNS"; +option go_package = "github.com/xtls/xray-core/transport/internet/headers/dns"; +option java_package = "com.xray.transport.internet.headers.dns"; +option java_multiple_files = true; + +message Config { + string domain = 1; +} + diff --git a/transport/internet/headers/dns/dns.go b/transport/internet/headers/dns/dns.go new file mode 100644 index 00000000..5839bc81 --- /dev/null +++ b/transport/internet/headers/dns/dns.go @@ -0,0 +1,57 @@ +package dns + +import ( + "context" + "encoding/binary" + + "github.com/miekg/dns" + "github.com/xtls/xray-core/common" + "github.com/xtls/xray-core/common/dice" +) + +type DNS struct { + header []byte +} + +func (d DNS) Size() int32 { + return int32(len(d.header)) +} + +// Serialize implements PacketHeader. +func (d DNS) Serialize(b []byte) { + copy(b, d.header) + binary.BigEndian.PutUint16(b[0:], dice.RollUint16()) // random transaction ID +} + +// NewDNS returns a new DNS instance based on given config. +func NewDNS(ctx context.Context, config interface{}) (interface{}, error) { + var header []byte + + header = binary.BigEndian.AppendUint16(header, 0x0000) // Transaction ID + header = binary.BigEndian.AppendUint16(header, 0x0100) // Flags: Standard query + header = binary.BigEndian.AppendUint16(header, 0x0001) // Questions + header = binary.BigEndian.AppendUint16(header, 0x0000) // Answer RRs + header = binary.BigEndian.AppendUint16(header, 0x0000) // Authority RRs + header = binary.BigEndian.AppendUint16(header, 0x0000) // Additional RRs + + buf := make([]byte, 0x100) + + off1, err := dns.PackDomainName(dns.Fqdn(config.(*Config).Domain), buf, 0, nil, false) + + if err != nil { + return nil, err + } + + header = append(header, buf[:off1]...) + + header = binary.BigEndian.AppendUint16(header, 0x0001) // Type: A + header = binary.BigEndian.AppendUint16(header, 0x0001) // Class: IN + + return DNS{ + header: header, + }, nil +} + +func init() { + common.Must(common.RegisterConfig((*Config)(nil), NewDNS)) +}