Kaynağa Gözat

Sockopt: Add `addressPortStrategy` (query SRV or TXT) (#4416)

Co-authored-by: 风扇滑翔翼 <[email protected]>
j3l11234 10 ay önce
ebeveyn
işleme
b9cb93d3c2

+ 22 - 0
infra/conf/transport_internet.go

@@ -711,6 +711,7 @@ type SocketConfig struct {
 	Interface            string                 `json:"interface"`
 	TcpMptcp             bool                   `json:"tcpMptcp"`
 	CustomSockopt        []*CustomSockoptConfig `json:"customSockopt"`
+	AddressPortStrategy  string                 `json:"addressPortStrategy"`
 }
 
 // Build implements Buildable.
@@ -780,6 +781,26 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
 		customSockopts = append(customSockopts, customSockopt)
 	}
 
+	addressPortStrategy := internet.AddressPortStrategy_None
+	switch strings.ToLower(c.AddressPortStrategy) {
+	case "none", "":
+		addressPortStrategy = internet.AddressPortStrategy_None
+	case "srvportonly":
+		addressPortStrategy = internet.AddressPortStrategy_SrvPortOnly
+	case "srvaddressonly":
+		addressPortStrategy = internet.AddressPortStrategy_SrvAddressOnly
+	case "srvportandaddress":
+		addressPortStrategy = internet.AddressPortStrategy_SrvPortAndAddress
+	case "txtportonly":
+		addressPortStrategy = internet.AddressPortStrategy_TxtPortOnly
+	case "txtaddressonly":
+		addressPortStrategy = internet.AddressPortStrategy_TxtAddressOnly
+	case "txtportandaddress":
+		addressPortStrategy = internet.AddressPortStrategy_TxtPortAndAddress
+	default:
+		return nil, errors.New("unsupported address and port strategy: ", c.AddressPortStrategy)
+	}
+
 	return &internet.SocketConfig{
 		Mark:                 c.Mark,
 		Tfo:                  tfo,
@@ -798,6 +819,7 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
 		Interface:            c.Interface,
 		TcpMptcp:             c.TcpMptcp,
 		CustomSockopt:        customSockopts,
+		AddressPortStrategy:  addressPortStrategy,
 	}, nil
 }
 

+ 151 - 64
transport/internet/config.pb.go

@@ -95,6 +95,67 @@ func (DomainStrategy) EnumDescriptor() ([]byte, []int) {
 	return file_transport_internet_config_proto_rawDescGZIP(), []int{0}
 }
 
+type AddressPortStrategy int32
+
+const (
+	AddressPortStrategy_None              AddressPortStrategy = 0
+	AddressPortStrategy_SrvPortOnly       AddressPortStrategy = 1
+	AddressPortStrategy_SrvAddressOnly    AddressPortStrategy = 2
+	AddressPortStrategy_SrvPortAndAddress AddressPortStrategy = 3
+	AddressPortStrategy_TxtPortOnly       AddressPortStrategy = 4
+	AddressPortStrategy_TxtAddressOnly    AddressPortStrategy = 5
+	AddressPortStrategy_TxtPortAndAddress AddressPortStrategy = 6
+)
+
+// Enum value maps for AddressPortStrategy.
+var (
+	AddressPortStrategy_name = map[int32]string{
+		0: "None",
+		1: "SrvPortOnly",
+		2: "SrvAddressOnly",
+		3: "SrvPortAndAddress",
+		4: "TxtPortOnly",
+		5: "TxtAddressOnly",
+		6: "TxtPortAndAddress",
+	}
+	AddressPortStrategy_value = map[string]int32{
+		"None":              0,
+		"SrvPortOnly":       1,
+		"SrvAddressOnly":    2,
+		"SrvPortAndAddress": 3,
+		"TxtPortOnly":       4,
+		"TxtAddressOnly":    5,
+		"TxtPortAndAddress": 6,
+	}
+)
+
+func (x AddressPortStrategy) Enum() *AddressPortStrategy {
+	p := new(AddressPortStrategy)
+	*p = x
+	return p
+}
+
+func (x AddressPortStrategy) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (AddressPortStrategy) Descriptor() protoreflect.EnumDescriptor {
+	return file_transport_internet_config_proto_enumTypes[1].Descriptor()
+}
+
+func (AddressPortStrategy) Type() protoreflect.EnumType {
+	return &file_transport_internet_config_proto_enumTypes[1]
+}
+
+func (x AddressPortStrategy) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use AddressPortStrategy.Descriptor instead.
+func (AddressPortStrategy) EnumDescriptor() ([]byte, []int) {
+	return file_transport_internet_config_proto_rawDescGZIP(), []int{1}
+}
+
 type SocketConfig_TProxyMode int32
 
 const (
@@ -131,11 +192,11 @@ func (x SocketConfig_TProxyMode) String() string {
 }
 
 func (SocketConfig_TProxyMode) Descriptor() protoreflect.EnumDescriptor {
-	return file_transport_internet_config_proto_enumTypes[1].Descriptor()
+	return file_transport_internet_config_proto_enumTypes[2].Descriptor()
 }
 
 func (SocketConfig_TProxyMode) Type() protoreflect.EnumType {
-	return &file_transport_internet_config_proto_enumTypes[1]
+	return &file_transport_internet_config_proto_enumTypes[2]
 }
 
 func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber {
@@ -434,23 +495,24 @@ type SocketConfig struct {
 	Tproxy SocketConfig_TProxyMode `protobuf:"varint,3,opt,name=tproxy,proto3,enum=xray.transport.internet.SocketConfig_TProxyMode" json:"tproxy,omitempty"`
 	// ReceiveOriginalDestAddress is for enabling IP_RECVORIGDSTADDR socket
 	// option. This option is for UDP only.
-	ReceiveOriginalDestAddress bool             `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"`
-	BindAddress                []byte           `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"`
-	BindPort                   uint32           `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"`
-	AcceptProxyProtocol        bool             `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"`
-	DomainStrategy             DomainStrategy   `protobuf:"varint,8,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"domain_strategy,omitempty"`
-	DialerProxy                string           `protobuf:"bytes,9,opt,name=dialer_proxy,json=dialerProxy,proto3" json:"dialer_proxy,omitempty"`
-	TcpKeepAliveInterval       int32            `protobuf:"varint,10,opt,name=tcp_keep_alive_interval,json=tcpKeepAliveInterval,proto3" json:"tcp_keep_alive_interval,omitempty"`
-	TcpKeepAliveIdle           int32            `protobuf:"varint,11,opt,name=tcp_keep_alive_idle,json=tcpKeepAliveIdle,proto3" json:"tcp_keep_alive_idle,omitempty"`
-	TcpCongestion              string           `protobuf:"bytes,12,opt,name=tcp_congestion,json=tcpCongestion,proto3" json:"tcp_congestion,omitempty"`
-	Interface                  string           `protobuf:"bytes,13,opt,name=interface,proto3" json:"interface,omitempty"`
-	V6Only                     bool             `protobuf:"varint,14,opt,name=v6only,proto3" json:"v6only,omitempty"`
-	TcpWindowClamp             int32            `protobuf:"varint,15,opt,name=tcp_window_clamp,json=tcpWindowClamp,proto3" json:"tcp_window_clamp,omitempty"`
-	TcpUserTimeout             int32            `protobuf:"varint,16,opt,name=tcp_user_timeout,json=tcpUserTimeout,proto3" json:"tcp_user_timeout,omitempty"`
-	TcpMaxSeg                  int32            `protobuf:"varint,17,opt,name=tcp_max_seg,json=tcpMaxSeg,proto3" json:"tcp_max_seg,omitempty"`
-	Penetrate                  bool             `protobuf:"varint,18,opt,name=penetrate,proto3" json:"penetrate,omitempty"`
-	TcpMptcp                   bool             `protobuf:"varint,19,opt,name=tcp_mptcp,json=tcpMptcp,proto3" json:"tcp_mptcp,omitempty"`
-	CustomSockopt              []*CustomSockopt `protobuf:"bytes,20,rep,name=customSockopt,proto3" json:"customSockopt,omitempty"`
+	ReceiveOriginalDestAddress bool                `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"`
+	BindAddress                []byte              `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"`
+	BindPort                   uint32              `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"`
+	AcceptProxyProtocol        bool                `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"`
+	DomainStrategy             DomainStrategy      `protobuf:"varint,8,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"domain_strategy,omitempty"`
+	DialerProxy                string              `protobuf:"bytes,9,opt,name=dialer_proxy,json=dialerProxy,proto3" json:"dialer_proxy,omitempty"`
+	TcpKeepAliveInterval       int32               `protobuf:"varint,10,opt,name=tcp_keep_alive_interval,json=tcpKeepAliveInterval,proto3" json:"tcp_keep_alive_interval,omitempty"`
+	TcpKeepAliveIdle           int32               `protobuf:"varint,11,opt,name=tcp_keep_alive_idle,json=tcpKeepAliveIdle,proto3" json:"tcp_keep_alive_idle,omitempty"`
+	TcpCongestion              string              `protobuf:"bytes,12,opt,name=tcp_congestion,json=tcpCongestion,proto3" json:"tcp_congestion,omitempty"`
+	Interface                  string              `protobuf:"bytes,13,opt,name=interface,proto3" json:"interface,omitempty"`
+	V6Only                     bool                `protobuf:"varint,14,opt,name=v6only,proto3" json:"v6only,omitempty"`
+	TcpWindowClamp             int32               `protobuf:"varint,15,opt,name=tcp_window_clamp,json=tcpWindowClamp,proto3" json:"tcp_window_clamp,omitempty"`
+	TcpUserTimeout             int32               `protobuf:"varint,16,opt,name=tcp_user_timeout,json=tcpUserTimeout,proto3" json:"tcp_user_timeout,omitempty"`
+	TcpMaxSeg                  int32               `protobuf:"varint,17,opt,name=tcp_max_seg,json=tcpMaxSeg,proto3" json:"tcp_max_seg,omitempty"`
+	Penetrate                  bool                `protobuf:"varint,18,opt,name=penetrate,proto3" json:"penetrate,omitempty"`
+	TcpMptcp                   bool                `protobuf:"varint,19,opt,name=tcp_mptcp,json=tcpMptcp,proto3" json:"tcp_mptcp,omitempty"`
+	CustomSockopt              []*CustomSockopt    `protobuf:"bytes,20,rep,name=customSockopt,proto3" json:"customSockopt,omitempty"`
+	AddressPortStrategy        AddressPortStrategy `protobuf:"varint,21,opt,name=address_port_strategy,json=addressPortStrategy,proto3,enum=xray.transport.internet.AddressPortStrategy" json:"address_port_strategy,omitempty"`
 }
 
 func (x *SocketConfig) Reset() {
@@ -623,6 +685,13 @@ func (x *SocketConfig) GetCustomSockopt() []*CustomSockopt {
 	return nil
 }
 
+func (x *SocketConfig) GetAddressPortStrategy() AddressPortStrategy {
+	if x != nil {
+		return x.AddressPortStrategy
+	}
+	return AddressPortStrategy_None
+}
+
 var File_transport_internet_config_proto protoreflect.FileDescriptor
 
 var file_transport_internet_config_proto_rawDesc = []byte{
@@ -678,7 +747,7 @@ var file_transport_internet_config_proto_rawDesc = []byte{
 	0x28, 0x09, 0x52, 0x03, 0x6f, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
 	0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a,
 	0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70,
-	0x65, 0x22, 0x9b, 0x07, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
+	0x65, 0x22, 0xfd, 0x07, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
 	0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
 	0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x18, 0x02, 0x20,
 	0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74, 0x70, 0x72, 0x6f,
@@ -732,28 +801,44 @@ var file_transport_internet_config_proto_rawDesc = []byte{
 	0x74, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 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, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x52,
-	0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x22, 0x2f,
-	0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03,
-	0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10,
-	0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x2a,
-	0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65,
-	0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a,
-	0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45,
-	0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50,
-	0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10,
-	0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x05, 0x12,
-	0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12, 0x0d, 0x0a,
-	0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09,
-	0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x46,
-	0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a, 0x0a, 0x46,
-	0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x42, 0x67, 0x0a, 0x1b, 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, 0x50, 0x01, 0x5a, 0x2c, 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, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61,
-	0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65,
-	0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x12, 0x60,
+	0x0a, 0x15, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73,
+	0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 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, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50,
+	0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x13, 0x61, 0x64, 0x64,
+	0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
+	0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07,
+	0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78,
+	0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10,
+	0x02, 0x2a, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61,
+	0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12,
+	0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55,
+	0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f,
+	0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34,
+	0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10,
+	0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12,
+	0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d,
+	0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a,
+	0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a,
+	0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x2a, 0x97, 0x01,
+	0x0a, 0x13, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72,
+	0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12,
+	0x0f, 0x0a, 0x0b, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x01,
+	0x12, 0x12, 0x0a, 0x0e, 0x53, 0x72, 0x76, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e,
+	0x6c, 0x79, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x41,
+	0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54,
+	0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e,
+	0x54, 0x78, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x05,
+	0x12, 0x15, 0x0a, 0x11, 0x54, 0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x6e, 0x64, 0x41, 0x64,
+	0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x06, 0x42, 0x67, 0x0a, 0x1b, 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, 0x50, 0x01, 0x5a, 0x2c, 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, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72,
+	0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
+	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -768,33 +853,35 @@ func file_transport_internet_config_proto_rawDescGZIP() []byte {
 	return file_transport_internet_config_proto_rawDescData
 }
 
-var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
+var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
 var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
 var file_transport_internet_config_proto_goTypes = []any{
 	(DomainStrategy)(0),          // 0: xray.transport.internet.DomainStrategy
-	(SocketConfig_TProxyMode)(0), // 1: xray.transport.internet.SocketConfig.TProxyMode
-	(*TransportConfig)(nil),      // 2: xray.transport.internet.TransportConfig
-	(*StreamConfig)(nil),         // 3: xray.transport.internet.StreamConfig
-	(*ProxyConfig)(nil),          // 4: xray.transport.internet.ProxyConfig
-	(*CustomSockopt)(nil),        // 5: xray.transport.internet.CustomSockopt
-	(*SocketConfig)(nil),         // 6: xray.transport.internet.SocketConfig
-	(*serial.TypedMessage)(nil),  // 7: xray.common.serial.TypedMessage
-	(*net.IPOrDomain)(nil),       // 8: xray.common.net.IPOrDomain
+	(AddressPortStrategy)(0),     // 1: xray.transport.internet.AddressPortStrategy
+	(SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode
+	(*TransportConfig)(nil),      // 3: xray.transport.internet.TransportConfig
+	(*StreamConfig)(nil),         // 4: xray.transport.internet.StreamConfig
+	(*ProxyConfig)(nil),          // 5: xray.transport.internet.ProxyConfig
+	(*CustomSockopt)(nil),        // 6: xray.transport.internet.CustomSockopt
+	(*SocketConfig)(nil),         // 7: xray.transport.internet.SocketConfig
+	(*serial.TypedMessage)(nil),  // 8: xray.common.serial.TypedMessage
+	(*net.IPOrDomain)(nil),       // 9: xray.common.net.IPOrDomain
 }
 var file_transport_internet_config_proto_depIdxs = []int32{
-	7, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
-	8, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
-	2, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
-	7, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
-	6, // 4: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
-	1, // 5: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
+	8, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
+	9, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
+	3, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
+	8, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
+	7, // 4: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
+	2, // 5: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
 	0, // 6: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
-	5, // 7: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
-	8, // [8:8] is the sub-list for method output_type
-	8, // [8:8] is the sub-list for method input_type
-	8, // [8:8] is the sub-list for extension type_name
-	8, // [8:8] is the sub-list for extension extendee
-	0, // [0:8] is the sub-list for field type_name
+	6, // 7: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
+	1, // 8: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy
+	9, // [9:9] is the sub-list for method output_type
+	9, // [9:9] is the sub-list for method input_type
+	9, // [9:9] is the sub-list for extension type_name
+	9, // [9:9] is the sub-list for extension extendee
+	0, // [0:9] is the sub-list for field type_name
 }
 
 func init() { file_transport_internet_config_proto_init() }
@@ -807,7 +894,7 @@ func file_transport_internet_config_proto_init() {
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_transport_internet_config_proto_rawDesc,
-			NumEnums:      2,
+			NumEnums:      3,
 			NumMessages:   5,
 			NumExtensions: 0,
 			NumServices:   0,

+ 12 - 0
transport/internet/config.proto

@@ -23,6 +23,16 @@ enum DomainStrategy {
   FORCE_IP64 = 10;
 }
 
+enum AddressPortStrategy {
+  None = 0;
+  SrvPortOnly = 1;
+  SrvAddressOnly = 2;
+  SrvPortAndAddress = 3;
+  TxtPortOnly = 4;
+  TxtAddressOnly = 5;
+  TxtPortAndAddress = 6;
+}
+
 message TransportConfig {
   // Transport protocol name.
   string protocol_name = 3;
@@ -116,4 +126,6 @@ message SocketConfig {
   bool tcp_mptcp = 19;
 
   repeated CustomSockopt customSockopt = 20;
+
+  AddressPortStrategy address_port_strategy = 21;
 }

+ 95 - 0
transport/internet/dialer.go

@@ -2,6 +2,9 @@ package internet
 
 import (
 	"context"
+	"fmt"
+	gonet "net"
+	"strings"
 
 	"github.com/xtls/xray-core/common"
 	"github.com/xtls/xray-core/common/dice"
@@ -140,6 +143,93 @@ func redirect(ctx context.Context, dst net.Destination, obt string) net.Conn {
 	return nil
 }
 
+func checkAddressPortStrategy(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (*net.Destination, error) {
+	if sockopt.AddressPortStrategy == AddressPortStrategy_None {
+		return nil, nil
+	}
+	newDest := dest
+	var OverridePort, OverrideAddress bool
+	var OverrideBy string
+	switch sockopt.AddressPortStrategy {
+	case AddressPortStrategy_SrvPortOnly:
+		OverridePort = true
+		OverrideAddress = false
+		OverrideBy = "srv"
+	case AddressPortStrategy_SrvAddressOnly:
+		OverridePort = false
+		OverrideAddress = true
+		OverrideBy = "srv"
+	case AddressPortStrategy_SrvPortAndAddress:
+		OverridePort = true
+		OverrideAddress = true
+		OverrideBy = "srv"
+	case AddressPortStrategy_TxtPortOnly:
+		OverridePort = true
+		OverrideAddress = false
+		OverrideBy = "txt"
+	case AddressPortStrategy_TxtAddressOnly:
+		OverridePort = false
+		OverrideAddress = true
+		OverrideBy = "txt"
+	case AddressPortStrategy_TxtPortAndAddress:
+		OverridePort = true
+		OverrideAddress = true
+		OverrideBy = "txt"
+	default:
+		return nil, errors.New("unknown AddressPortStrategy")
+	}
+
+	if !dest.Address.Family().IsDomain() {
+		return nil, nil
+	}
+
+	if OverrideBy == "srv" {
+		errors.LogDebug(ctx, "query SRV record for "+dest.Address.String())
+		parts := strings.SplitN(dest.Address.String(), ".", 3)
+		if len(parts) != 3 {
+			return nil, errors.New("invalid address format", dest.Address.String())
+		}
+		_, srvRecords, err := gonet.DefaultResolver.LookupSRV(context.Background(), parts[0][1:], parts[1][1:], parts[2])
+		if err != nil {
+			return nil, errors.New("failed to lookup SRV record").Base(err)
+		}
+		errors.LogDebug(ctx, "SRV record: "+fmt.Sprintf("addr=%s, port=%d, priority=%d, weight=%d", srvRecords[0].Target, srvRecords[0].Port, srvRecords[0].Priority, srvRecords[0].Weight))
+		if OverridePort {
+			newDest.Port = net.Port(srvRecords[0].Port)
+		}
+		if OverrideAddress {
+			newDest.Address = net.ParseAddress(srvRecords[0].Target)
+		}
+		return &newDest, nil
+	}
+	if OverrideBy == "txt" {
+		errors.LogDebug(ctx, "query TXT record for "+dest.Address.String())
+		txtRecords, err := gonet.DefaultResolver.LookupTXT(ctx, dest.Address.String())
+		if err != nil {
+			errors.LogError(ctx, "failed to lookup SRV record: "+err.Error())
+			return nil, errors.New("failed to lookup SRV record").Base(err)
+		}
+		for _, txtRecord := range txtRecords {
+			errors.LogDebug(ctx, "TXT record: "+txtRecord)
+			addr_s, port_s, _ := net.SplitHostPort(string(txtRecord))
+			addr := net.ParseAddress(addr_s)
+			port, err := net.PortFromString(port_s)
+			if err != nil {
+				continue
+			}
+
+			if OverridePort {
+				newDest.Port = port
+			}
+			if OverrideAddress {
+				newDest.Address = addr
+			}
+			return &newDest, nil
+		}
+	}
+	return nil, nil
+}
+
 // DialSystem calls system dialer to create a network connection.
 func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) {
 	var src net.Address
@@ -152,6 +242,11 @@ func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig
 		return effectiveSystemDialer.Dial(ctx, src, dest, sockopt)
 	}
 
+	if newDest, err := checkAddressPortStrategy(ctx, dest, sockopt); err == nil && newDest != nil {
+		errors.LogInfo(ctx, "replace destination with "+newDest.String())
+		dest = *newDest
+	}
+
 	if canLookupIP(ctx, dest, sockopt) {
 		ips, err := lookupIP(dest.Address.String(), sockopt.DomainStrategy, src)
 		if err == nil && len(ips) > 0 {