mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-22 09:36:49 +08:00
Outbound: Add targetStrategy
; Fix mux does not close link.Reader
; Fix origin
does not work on UDP; Add logs (#5006)
This commit is contained in:
@@ -42,12 +42,15 @@ func (r *IPRecord) getIPs() ([]net.IP, uint32, error) {
|
|||||||
if r == nil {
|
if r == nil {
|
||||||
return nil, 0, errRecordNotFound
|
return nil, 0, errRecordNotFound
|
||||||
}
|
}
|
||||||
untilExpire := time.Until(r.Expire)
|
untilExpire := time.Until(r.Expire).Seconds()
|
||||||
if untilExpire <= 0 {
|
if untilExpire <= 0 {
|
||||||
return nil, 0, errRecordNotFound
|
return nil, 0, errRecordNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
ttl := uint32(untilExpire/time.Second) + uint32(1)
|
ttl := uint32(untilExpire) + 1
|
||||||
|
if ttl == 1 {
|
||||||
|
r.Expire = time.Now().Add(time.Second) // To ensure that two consecutive requests get the same result
|
||||||
|
}
|
||||||
if r.RCode != dnsmessage.RCodeSuccess {
|
if r.RCode != dnsmessage.RCodeSuccess {
|
||||||
return nil, ttl, dns_feature.RCodeError(r.RCode)
|
return nil, ttl, dns_feature.RCodeError(r.RCode)
|
||||||
}
|
}
|
||||||
|
@@ -449,11 +449,12 @@ type SenderConfig struct {
|
|||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
// Send traffic through the given IP. Only IP is allowed.
|
// Send traffic through the given IP. Only IP is allowed.
|
||||||
Via *net.IPOrDomain `protobuf:"bytes,1,opt,name=via,proto3" json:"via,omitempty"`
|
Via *net.IPOrDomain `protobuf:"bytes,1,opt,name=via,proto3" json:"via,omitempty"`
|
||||||
StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
|
StreamSettings *internet.StreamConfig `protobuf:"bytes,2,opt,name=stream_settings,json=streamSettings,proto3" json:"stream_settings,omitempty"`
|
||||||
ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
|
ProxySettings *internet.ProxyConfig `protobuf:"bytes,3,opt,name=proxy_settings,json=proxySettings,proto3" json:"proxy_settings,omitempty"`
|
||||||
MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"`
|
MultiplexSettings *MultiplexingConfig `protobuf:"bytes,4,opt,name=multiplex_settings,json=multiplexSettings,proto3" json:"multiplex_settings,omitempty"`
|
||||||
ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,omitempty"`
|
ViaCidr string `protobuf:"bytes,5,opt,name=via_cidr,json=viaCidr,proto3" json:"via_cidr,omitempty"`
|
||||||
|
TargetStrategy internet.DomainStrategy `protobuf:"varint,6,opt,name=target_strategy,json=targetStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"target_strategy,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *SenderConfig) Reset() {
|
func (x *SenderConfig) Reset() {
|
||||||
@@ -521,6 +522,13 @@ func (x *SenderConfig) GetViaCidr() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *SenderConfig) GetTargetStrategy() internet.DomainStrategy {
|
||||||
|
if x != nil {
|
||||||
|
return x.TargetStrategy
|
||||||
|
}
|
||||||
|
return internet.DomainStrategy(0)
|
||||||
|
}
|
||||||
|
|
||||||
type MultiplexingConfig struct {
|
type MultiplexingConfig struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -779,7 +787,7 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
|
|||||||
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65,
|
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70, 0x65,
|
||||||
0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53,
|
0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53,
|
||||||
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f,
|
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x10, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f,
|
||||||
0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xcb, 0x02, 0x0a, 0x0c, 0x53, 0x65,
|
0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x9d, 0x03, 0x0a, 0x0c, 0x53, 0x65,
|
||||||
0x6e, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a, 0x03, 0x76, 0x69,
|
0x6e, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2d, 0x0a, 0x03, 0x76, 0x69,
|
||||||
0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63,
|
0x61, 0x18, 0x01, 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,
|
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f,
|
||||||
@@ -800,23 +808,28 @@ var file_app_proxyman_config_proto_rawDesc = []byte{
|
|||||||
0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69,
|
0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69,
|
||||||
0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19, 0x0a, 0x08,
|
0x70, 0x6c, 0x65, 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19, 0x0a, 0x08,
|
||||||
0x76, 0x69, 0x61, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
0x76, 0x69, 0x61, 0x5f, 0x63, 0x69, 0x64, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
||||||
0x76, 0x69, 0x61, 0x43, 0x69, 0x64, 0x72, 0x22, 0xa4, 0x01, 0x0a, 0x12, 0x4d, 0x75, 0x6c, 0x74,
|
0x76, 0x69, 0x61, 0x43, 0x69, 0x64, 0x72, 0x12, 0x50, 0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65,
|
||||||
0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18,
|
0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e,
|
||||||
0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
|
0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||||
0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x63,
|
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
||||||
0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x63,
|
0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x74, 0x61, 0x72, 0x67, 0x65,
|
||||||
0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75,
|
0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x22, 0xa4, 0x01, 0x0a, 0x12, 0x4d, 0x75,
|
||||||
0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x03, 0x20,
|
0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
0x01, 0x28, 0x05, 0x52, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72,
|
0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||||
0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78,
|
0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f,
|
||||||
0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x78,
|
0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52,
|
||||||
0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x42, 0x55,
|
0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f,
|
||||||
0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70,
|
0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18,
|
||||||
0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75,
|
0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x63, 0x75,
|
||||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d,
|
0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72,
|
||||||
0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61,
|
0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x72, 0x6f,
|
0x0f, 0x78, 0x75, 0x64, 0x70, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x44, 0x50, 0x34, 0x34, 0x33,
|
||||||
0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x42, 0x55, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
|
||||||
|
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, 0x01, 0x5a, 0x26, 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, 0x70, 0x72, 0x6f, 0x78, 0x79,
|
||||||
|
0x6d, 0x61, 0x6e, 0xaa, 0x02, 0x11, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50,
|
||||||
|
0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -850,6 +863,7 @@ var file_app_proxyman_config_proto_goTypes = []any{
|
|||||||
(*internet.StreamConfig)(nil), // 13: xray.transport.internet.StreamConfig
|
(*internet.StreamConfig)(nil), // 13: xray.transport.internet.StreamConfig
|
||||||
(*serial.TypedMessage)(nil), // 14: xray.common.serial.TypedMessage
|
(*serial.TypedMessage)(nil), // 14: xray.common.serial.TypedMessage
|
||||||
(*internet.ProxyConfig)(nil), // 15: xray.transport.internet.ProxyConfig
|
(*internet.ProxyConfig)(nil), // 15: xray.transport.internet.ProxyConfig
|
||||||
|
(internet.DomainStrategy)(0), // 16: xray.transport.internet.DomainStrategy
|
||||||
}
|
}
|
||||||
var file_app_proxyman_config_proto_depIdxs = []int32{
|
var file_app_proxyman_config_proto_depIdxs = []int32{
|
||||||
0, // 0: xray.app.proxyman.AllocationStrategy.type:type_name -> xray.app.proxyman.AllocationStrategy.Type
|
0, // 0: xray.app.proxyman.AllocationStrategy.type:type_name -> xray.app.proxyman.AllocationStrategy.Type
|
||||||
@@ -866,11 +880,12 @@ var file_app_proxyman_config_proto_depIdxs = []int32{
|
|||||||
13, // 11: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
|
13, // 11: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
|
||||||
15, // 12: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig
|
15, // 12: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig
|
||||||
8, // 13: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig
|
8, // 13: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig
|
||||||
14, // [14:14] is the sub-list for method output_type
|
16, // 14: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy
|
||||||
14, // [14:14] is the sub-list for method input_type
|
15, // [15:15] is the sub-list for method output_type
|
||||||
14, // [14:14] is the sub-list for extension type_name
|
15, // [15:15] is the sub-list for method input_type
|
||||||
14, // [14:14] is the sub-list for extension extendee
|
15, // [15:15] is the sub-list for extension type_name
|
||||||
0, // [0:14] is the sub-list for field type_name
|
15, // [15:15] is the sub-list for extension extendee
|
||||||
|
0, // [0:15] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_app_proxyman_config_proto_init() }
|
func init() { file_app_proxyman_config_proto_init() }
|
||||||
|
@@ -84,6 +84,7 @@ message SenderConfig {
|
|||||||
xray.transport.internet.ProxyConfig proxy_settings = 3;
|
xray.transport.internet.ProxyConfig proxy_settings = 3;
|
||||||
MultiplexingConfig multiplex_settings = 4;
|
MultiplexingConfig multiplex_settings = 4;
|
||||||
string via_cidr = 5;
|
string via_cidr = 5;
|
||||||
|
xray.transport.internet.DomainStrategy target_strategy = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message MultiplexingConfig {
|
message MultiplexingConfig {
|
||||||
|
@@ -325,7 +325,7 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
|
|||||||
|
|
||||||
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||||||
Source: source,
|
Source: source,
|
||||||
Local: net.DestinationFromAddr(w.hub.Addr()),
|
Local: net.DestinationFromAddr(w.hub.Addr()), // Due to some limitations, in UDP connections, localIP is always equal to listen interface IP
|
||||||
Gateway: net.UDPDestination(w.address, w.port),
|
Gateway: net.UDPDestination(w.address, w.port),
|
||||||
Tag: w.tag,
|
Tag: w.tag,
|
||||||
})
|
})
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
goerrors "errors"
|
goerrors "errors"
|
||||||
|
"github.com/xtls/xray-core/common/dice"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
gonet "net"
|
gonet "net"
|
||||||
@@ -177,6 +178,25 @@ func (h *Handler) Tag() string {
|
|||||||
func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
|
func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
|
||||||
outbounds := session.OutboundsFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
ob := outbounds[len(outbounds)-1]
|
ob := outbounds[len(outbounds)-1]
|
||||||
|
content := session.ContentFromContext(ctx)
|
||||||
|
if h.senderSettings != nil && h.senderSettings.TargetStrategy.HasStrategy() && ob.Target.Address.Family().IsDomain() && (content == nil || !content.SkipDNSResolve) {
|
||||||
|
ips, err := internet.LookupForIP(ob.Target.Address.Domain(), h.senderSettings.TargetStrategy, nil)
|
||||||
|
if err != nil {
|
||||||
|
errors.LogInfoInner(ctx, err, "failed to resolve ip for target ", ob.Target.Address.Domain())
|
||||||
|
if h.senderSettings.TargetStrategy.ForceIP() {
|
||||||
|
err := errors.New("failed to resolve ip for target ", ob.Target.Address.Domain()).Base(err)
|
||||||
|
session.SubmitOutboundErrorToOriginator(ctx, err)
|
||||||
|
common.Interrupt(link.Writer)
|
||||||
|
common.Interrupt(link.Reader)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
unchangedDomain := ob.Target.Address.Domain()
|
||||||
|
ob.Target.Address = net.IPAddress(ips[dice.Roll(len(ips))])
|
||||||
|
errors.LogInfo(ctx, "target: ", unchangedDomain, " resolved to: ", ob.Target.Address.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
if ob.Target.Network == net.Network_UDP && ob.OriginalTarget.Address != nil && ob.OriginalTarget.Address != ob.Target.Address {
|
if ob.Target.Network == net.Network_UDP && ob.OriginalTarget.Address != nil && ob.OriginalTarget.Address != ob.Target.Address {
|
||||||
link.Reader = &buf.EndpointOverrideReader{Reader: link.Reader, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
|
link.Reader = &buf.EndpointOverrideReader{Reader: link.Reader, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
|
||||||
link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
|
link.Writer = &buf.EndpointOverrideWriter{Writer: link.Writer, Dest: ob.Target.Address, OriginalDest: ob.OriginalTarget.Address}
|
||||||
@@ -188,6 +208,7 @@ func (h *Handler) Dispatch(ctx context.Context, link *transport.Link) {
|
|||||||
session.SubmitOutboundErrorToOriginator(ctx, err)
|
session.SubmitOutboundErrorToOriginator(ctx, err)
|
||||||
errors.LogInfo(ctx, err.Error())
|
errors.LogInfo(ctx, err.Error())
|
||||||
common.Interrupt(link.Writer)
|
common.Interrupt(link.Writer)
|
||||||
|
common.Interrupt(link.Reader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ob.Target.Network == net.Network_UDP && ob.Target.Port == 443 {
|
if ob.Target.Network == net.Network_UDP && ob.Target.Port == 443 {
|
||||||
@@ -287,26 +308,18 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (stat.Connecti
|
|||||||
ob.Gateway = ParseRandomIP(addr, h.senderSettings.ViaCidr)
|
ob.Gateway = ParseRandomIP(addr, h.senderSettings.ViaCidr)
|
||||||
|
|
||||||
case domain == "origin":
|
case domain == "origin":
|
||||||
|
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||||
if inbound.Conn != nil {
|
if inbound.Local.IsValid() && inbound.Local.Address.Family().IsIP() {
|
||||||
origin, _, err := net.SplitHostPort(inbound.Conn.LocalAddr().String())
|
ob.Gateway = inbound.Local.Address
|
||||||
if err == nil {
|
errors.LogDebug(ctx, "use inbound local ip as sendthrough: ", inbound.Local.Address.String())
|
||||||
ob.Gateway = net.ParseAddress(origin)
|
|
||||||
errors.LogDebug(ctx, "use receive package ip as snedthrough: ", origin)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case domain == "srcip":
|
case domain == "srcip":
|
||||||
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
if inbound := session.InboundFromContext(ctx); inbound != nil {
|
||||||
if inbound.Conn != nil {
|
if inbound.Source.IsValid() && inbound.Source.Address.Family().IsIP() {
|
||||||
clientaddr, _, err := net.SplitHostPort(inbound.Conn.RemoteAddr().String())
|
ob.Gateway = inbound.Source.Address
|
||||||
if err == nil {
|
errors.LogDebug(ctx, "use inbound source ip as sendthrough: ", inbound.Source.Address.String())
|
||||||
ob.Gateway = net.ParseAddress(clientaddr)
|
|
||||||
errors.LogDebug(ctx, "use client src ip as snedthrough: ", clientaddr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
//case addr.Family().IsDomain():
|
//case addr.Family().IsDomain():
|
||||||
default:
|
default:
|
||||||
|
@@ -260,13 +260,14 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type OutboundDetourConfig struct {
|
type OutboundDetourConfig struct {
|
||||||
Protocol string `json:"protocol"`
|
Protocol string `json:"protocol"`
|
||||||
SendThrough *string `json:"sendThrough"`
|
SendThrough *string `json:"sendThrough"`
|
||||||
Tag string `json:"tag"`
|
Tag string `json:"tag"`
|
||||||
Settings *json.RawMessage `json:"settings"`
|
Settings *json.RawMessage `json:"settings"`
|
||||||
StreamSetting *StreamConfig `json:"streamSettings"`
|
StreamSetting *StreamConfig `json:"streamSettings"`
|
||||||
ProxySettings *ProxyConfig `json:"proxySettings"`
|
ProxySettings *ProxyConfig `json:"proxySettings"`
|
||||||
MuxSettings *MuxConfig `json:"mux"`
|
MuxSettings *MuxConfig `json:"mux"`
|
||||||
|
TargetStrategy string `json:"targetStrategy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OutboundDetourConfig) checkChainProxyConfig() error {
|
func (c *OutboundDetourConfig) checkChainProxyConfig() error {
|
||||||
@@ -282,6 +283,32 @@ func (c *OutboundDetourConfig) checkChainProxyConfig() error {
|
|||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
|
func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
|
||||||
senderSettings := &proxyman.SenderConfig{}
|
senderSettings := &proxyman.SenderConfig{}
|
||||||
|
switch strings.ToLower(c.TargetStrategy) {
|
||||||
|
case "asis", "":
|
||||||
|
senderSettings.TargetStrategy = internet.DomainStrategy_AS_IS
|
||||||
|
case "useip":
|
||||||
|
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP
|
||||||
|
case "useipv4":
|
||||||
|
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP4
|
||||||
|
case "useipv6":
|
||||||
|
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP6
|
||||||
|
case "useipv4v6":
|
||||||
|
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP46
|
||||||
|
case "useipv6v4":
|
||||||
|
senderSettings.TargetStrategy = internet.DomainStrategy_USE_IP64
|
||||||
|
case "forceip":
|
||||||
|
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP
|
||||||
|
case "forceipv4":
|
||||||
|
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP4
|
||||||
|
case "forceipv6":
|
||||||
|
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP6
|
||||||
|
case "forceipv4v6":
|
||||||
|
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP46
|
||||||
|
case "forceipv6v4":
|
||||||
|
senderSettings.TargetStrategy = internet.DomainStrategy_FORCE_IP64
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unsupported target domain strategy: ", c.TargetStrategy)
|
||||||
|
}
|
||||||
if err := c.checkChainProxyConfig(); err != nil {
|
if err := c.checkChainProxyConfig(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -100,30 +100,30 @@ func (m SocketConfig_TProxyMode) IsEnabled() bool {
|
|||||||
return m != SocketConfig_Off
|
return m != SocketConfig_Off
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s DomainStrategy) hasStrategy() bool {
|
func (s DomainStrategy) HasStrategy() bool {
|
||||||
return strategy[s][0] != 0
|
return strategy[s][0] != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s DomainStrategy) forceIP() bool {
|
func (s DomainStrategy) ForceIP() bool {
|
||||||
return strategy[s][0] == 2
|
return strategy[s][0] == 2
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s DomainStrategy) preferIP4() bool {
|
func (s DomainStrategy) PreferIP4() bool {
|
||||||
return strategy[s][1] == 4 || strategy[s][1] == 0
|
return strategy[s][1] == 4 || strategy[s][1] == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s DomainStrategy) preferIP6() bool {
|
func (s DomainStrategy) PreferIP6() bool {
|
||||||
return strategy[s][1] == 6 || strategy[s][1] == 0
|
return strategy[s][1] == 6 || strategy[s][1] == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s DomainStrategy) hasFallback() bool {
|
func (s DomainStrategy) HasFallback() bool {
|
||||||
return strategy[s][2] != 0
|
return strategy[s][2] != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s DomainStrategy) fallbackIP4() bool {
|
func (s DomainStrategy) FallbackIP4() bool {
|
||||||
return strategy[s][2] == 4
|
return strategy[s][2] == 4
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s DomainStrategy) fallbackIP6() bool {
|
func (s DomainStrategy) FallbackIP6() bool {
|
||||||
return strategy[s][2] == 6
|
return strategy[s][2] == 6
|
||||||
}
|
}
|
||||||
|
@@ -85,20 +85,20 @@ var (
|
|||||||
obm outbound.Manager
|
obm outbound.Manager
|
||||||
)
|
)
|
||||||
|
|
||||||
func lookupIP(domain string, strategy DomainStrategy, localAddr net.Address) ([]net.IP, error) {
|
func LookupForIP(domain string, strategy DomainStrategy, localAddr net.Address) ([]net.IP, error) {
|
||||||
if dnsClient == nil {
|
if dnsClient == nil {
|
||||||
return nil, errors.New("DNS client not initialized").AtError()
|
return nil, errors.New("DNS client not initialized").AtError()
|
||||||
}
|
}
|
||||||
|
|
||||||
ips, _, err := dnsClient.LookupIP(domain, dns.IPOption{
|
ips, _, err := dnsClient.LookupIP(domain, dns.IPOption{
|
||||||
IPv4Enable: (localAddr == nil || localAddr.Family().IsIPv4()) && strategy.preferIP4(),
|
IPv4Enable: (localAddr == nil || localAddr.Family().IsIPv4()) && strategy.PreferIP4(),
|
||||||
IPv6Enable: (localAddr == nil || localAddr.Family().IsIPv6()) && strategy.preferIP6(),
|
IPv6Enable: (localAddr == nil || localAddr.Family().IsIPv6()) && strategy.PreferIP6(),
|
||||||
})
|
})
|
||||||
{ // Resolve fallback
|
{ // Resolve fallback
|
||||||
if (len(ips) == 0 || err != nil) && strategy.hasFallback() && localAddr == nil {
|
if (len(ips) == 0 || err != nil) && strategy.HasFallback() && localAddr == nil {
|
||||||
ips, _, err = dnsClient.LookupIP(domain, dns.IPOption{
|
ips, _, err = dnsClient.LookupIP(domain, dns.IPOption{
|
||||||
IPv4Enable: strategy.fallbackIP4(),
|
IPv4Enable: strategy.FallbackIP4(),
|
||||||
IPv6Enable: strategy.fallbackIP6(),
|
IPv6Enable: strategy.FallbackIP6(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ func canLookupIP(dst net.Destination, sockopt *SocketConfig) bool {
|
|||||||
if dst.Address.Family().IsIP() {
|
if dst.Address.Family().IsIP() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return sockopt.DomainStrategy.hasStrategy()
|
return sockopt.DomainStrategy.HasStrategy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func redirect(ctx context.Context, dst net.Destination, obt string, h outbound.Handler) net.Conn {
|
func redirect(ctx context.Context, dst net.Destination, obt string, h outbound.Handler) net.Conn {
|
||||||
@@ -249,17 +249,17 @@ func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
if canLookupIP(dest, sockopt) {
|
if canLookupIP(dest, sockopt) {
|
||||||
ips, err := lookupIP(dest.Address.String(), sockopt.DomainStrategy, src)
|
ips, err := LookupForIP(dest.Address.String(), sockopt.DomainStrategy, src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogErrorInner(ctx, err, "failed to resolve ip")
|
errors.LogErrorInner(ctx, err, "failed to resolve ip")
|
||||||
if sockopt.DomainStrategy.forceIP() {
|
if sockopt.DomainStrategy.ForceIP() {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else if sockopt.HappyEyeballs == nil || sockopt.HappyEyeballs.TryDelayMs == 0 || sockopt.HappyEyeballs.MaxConcurrentTry == 0 || len(ips) < 2 || len(sockopt.DialerProxy) > 0 || dest.Network != net.Network_TCP {
|
} else if sockopt.HappyEyeballs == nil || sockopt.HappyEyeballs.TryDelayMs == 0 || sockopt.HappyEyeballs.MaxConcurrentTry == 0 || len(ips) < 2 || len(sockopt.DialerProxy) > 0 || dest.Network != net.Network_TCP {
|
||||||
dest.Address = net.IPAddress(ips[dice.Roll(len(ips))])
|
dest.Address = net.IPAddress(ips[dice.Roll(len(ips))])
|
||||||
errors.LogInfo(ctx, "replace destination with "+dest.String())
|
errors.LogInfo(ctx, "replace destination with "+dest.String())
|
||||||
} else {
|
} else {
|
||||||
return TcpRaceDial(ctx, src, ips, dest.Port, sockopt)
|
return TcpRaceDial(ctx, src, ips, dest.Port, sockopt, dest.Address.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,6 +2,7 @@ package internet
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -12,7 +13,7 @@ type result struct {
|
|||||||
index int
|
index int
|
||||||
}
|
}
|
||||||
|
|
||||||
func TcpRaceDial(ctx context.Context, src net.Address, ips []net.IP, port net.Port, sockopt *SocketConfig) (net.Conn, error) {
|
func TcpRaceDial(ctx context.Context, src net.Address, ips []net.IP, port net.Port, sockopt *SocketConfig, domain string) (net.Conn, error) {
|
||||||
if len(ips) < 2 {
|
if len(ips) < 2 {
|
||||||
panic("at least 2 ips is required to race dial")
|
panic("at least 2 ips is required to race dial")
|
||||||
}
|
}
|
||||||
@@ -30,6 +31,7 @@ func TcpRaceDial(ctx context.Context, src net.Address, ips []net.IP, port net.Po
|
|||||||
activeNum := uint32(0)
|
activeNum := uint32(0)
|
||||||
timer := time.NewTimer(0)
|
timer := time.NewTimer(0)
|
||||||
var winConn net.Conn
|
var winConn net.Conn
|
||||||
|
errors.LogDebug(ctx, "happy eyeballs racing dial for ", domain, " with IPs ", ips)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case r := <-resultCh:
|
case r := <-resultCh:
|
||||||
@@ -54,6 +56,7 @@ func TcpRaceDial(ctx context.Context, src net.Address, ips []net.IP, port net.Po
|
|||||||
timer.Stop()
|
timer.Stop()
|
||||||
if winConn == nil {
|
if winConn == nil {
|
||||||
winConn = r.conn
|
winConn = r.conn
|
||||||
|
errors.LogDebug(ctx, "happy eyeballs established connection for ", domain, " with IP ", ips[r.index])
|
||||||
} else {
|
} else {
|
||||||
r.conn.Close()
|
r.conn.Close()
|
||||||
}
|
}
|
||||||
@@ -69,6 +72,7 @@ func TcpRaceDial(ctx context.Context, src net.Address, ips []net.IP, port net.Po
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if activeNum == 0 {
|
if activeNum == 0 {
|
||||||
|
errors.LogDebugInner(ctx, r.err, "happy eyeballs no connection established for ", domain)
|
||||||
return nil, r.err
|
return nil, r.err
|
||||||
}
|
}
|
||||||
timer.Stop()
|
timer.Stop()
|
||||||
|
Reference in New Issue
Block a user