From 44be94a0e77ded91d594d671a465d20f077a0bd9 Mon Sep 17 00:00:00 2001 From: Vigilans Date: Sat, 18 Feb 2023 03:59:20 +0800 Subject: [PATCH 1/3] Add bytespools ownership for buf.Buffer --- common/buf/buffer.go | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/common/buf/buffer.go b/common/buf/buffer.go index b29b0e5c7d2..06c699ae109 100644 --- a/common/buf/buffer.go +++ b/common/buf/buffer.go @@ -13,6 +13,15 @@ const ( var pool = bytespool.GetPool(Size) +// ownership represents the data owner of the buffer. +type ownership uint8 + +const ( + managed ownership = 0 + unmanaged ownership = 1 + bytespools ownership = 2 +) + // Buffer is a recyclable allocation of a byte array. Buffer.Release() recycles // the buffer into an internal buffer pool, in order to recreate a buffer more // quickly. @@ -20,7 +29,7 @@ type Buffer struct { v []byte start int32 end int32 - unmanaged bool + ownership ownership } // New creates a Buffer with 0 length and 2K capacity. @@ -30,12 +39,20 @@ func New() *Buffer { } } +// NewWithSize creates a Buffer with 0 length and capacity with at least the given size. +func NewWithSize(size int32) *Buffer { + return &Buffer{ + v: bytespool.Alloc(size), + ownership: bytespools, + } +} + // FromBytes creates a Buffer with an existed bytearray func FromBytes(data []byte) *Buffer { return &Buffer{ v: data, end: int32(len(data)), - unmanaged: true, + ownership: unmanaged, } } @@ -49,14 +66,19 @@ func StackNew() Buffer { // Release recycles the buffer into an internal buffer pool. func (b *Buffer) Release() { - if b == nil || b.v == nil || b.unmanaged { + if b == nil || b.v == nil || b.ownership == unmanaged { return } p := b.v b.v = nil b.Clear() - pool.Put(p) // nolint: staticcheck + switch b.ownership { + case managed: + pool.Put(p) // nolint: staticcheck + case bytespools: + bytespool.Free(p) // nolint: staticcheck + } } // Clear clears the content of the buffer, results an empty buffer with @@ -151,6 +173,14 @@ func (b *Buffer) Len() int32 { return b.end - b.start } +// Cap returns the capacity of the buffer content. +func (b *Buffer) Cap() int32 { + if b == nil { + return 0 + } + return int32(len(b.v)) +} + // IsEmpty returns true if the buffer is empty. func (b *Buffer) IsEmpty() bool { return b.Len() == 0 From b1d38db30af3188ac84fcfb6f49b0a104d6789f9 Mon Sep 17 00:00:00 2001 From: Vigilans Date: Sat, 18 Feb 2023 02:50:20 +0800 Subject: [PATCH 2/3] Support using custom resolver when dialing domain address --- common/session/session.go | 2 ++ transport/internet/dialer.go | 20 +++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/common/session/session.go b/common/session/session.go index 25ce204c1c9..0c4f1fd9337 100644 --- a/common/session/session.go +++ b/common/session/session.go @@ -51,6 +51,8 @@ type Outbound struct { Target net.Destination // Gateway address Gateway net.Address + // Domain resolver to use when dialing + Resolver func(ctx context.Context, domain string) net.Address } // SniffingRequest controls the behavior of content sniffing. diff --git a/transport/internet/dialer.go b/transport/internet/dialer.go index 0f978fbf9c5..7a9b8981bc0 100644 --- a/transport/internet/dialer.go +++ b/transport/internet/dialer.go @@ -68,8 +68,10 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *MemoryStrea // DialSystem calls system dialer to create a network connection. func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) { + outbound := session.OutboundFromContext(ctx) + var src net.Address - if outbound := session.OutboundFromContext(ctx); outbound != nil { + if outbound != nil { src = outbound.Gateway } @@ -77,6 +79,22 @@ func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig return DialTaggedOutbound(ctx, dest, transportLayerOutgoingTag) } + originalAddr := dest.Address + if outbound != nil && outbound.Resolver != nil && dest.Address.Family().IsDomain() { + if addr := outbound.Resolver(ctx, dest.Address.Domain()); addr != nil { + dest.Address = addr + } + } + + switch { + case src != nil && dest.Address != originalAddr: + newError("dialing to ", dest, " resolved from ", originalAddr, " via ", src).WriteToLog(session.ExportIDToError(ctx)) + case src != nil: + newError("dialing to ", dest, " via ", src).WriteToLog(session.ExportIDToError(ctx)) + case dest.Address != originalAddr: + newError("dialing to ", dest, " resolved from ", originalAddr).WriteToLog(session.ExportIDToError(ctx)) + } + return effectiveSystemDialer.Dial(ctx, src, dest, sockopt) } From 882a363b858f2e2ad30702f7dbae95a9940aef38 Mon Sep 17 00:00:00 2001 From: Vigilans Date: Sat, 18 Feb 2023 05:51:25 +0800 Subject: [PATCH 3/3] DomainStrategy support for all outbounds --- app/proxyman/config.pb.go | 185 +++++++++++++++++++++---------- app/proxyman/config.proto | 8 ++ app/proxyman/outbound/handler.go | 41 +++++++ infra/conf/v4/v2ray.go | 25 +++-- proxy/freedom/freedom.go | 20 +--- 5 files changed, 202 insertions(+), 77 deletions(-) diff --git a/app/proxyman/config.pb.go b/app/proxyman/config.pb.go index fa9dae7fba3..0002af41989 100644 --- a/app/proxyman/config.pb.go +++ b/app/proxyman/config.pb.go @@ -115,6 +115,58 @@ func (AllocationStrategy_Type) EnumDescriptor() ([]byte, []int) { return file_app_proxyman_config_proto_rawDescGZIP(), []int{1, 0} } +type SenderConfig_DomainStrategy int32 + +const ( + SenderConfig_AS_IS SenderConfig_DomainStrategy = 0 + SenderConfig_USE_IP SenderConfig_DomainStrategy = 1 + SenderConfig_USE_IP4 SenderConfig_DomainStrategy = 2 + SenderConfig_USE_IP6 SenderConfig_DomainStrategy = 3 +) + +// Enum value maps for SenderConfig_DomainStrategy. +var ( + SenderConfig_DomainStrategy_name = map[int32]string{ + 0: "AS_IS", + 1: "USE_IP", + 2: "USE_IP4", + 3: "USE_IP6", + } + SenderConfig_DomainStrategy_value = map[string]int32{ + "AS_IS": 0, + "USE_IP": 1, + "USE_IP4": 2, + "USE_IP6": 3, + } +) + +func (x SenderConfig_DomainStrategy) Enum() *SenderConfig_DomainStrategy { + p := new(SenderConfig_DomainStrategy) + *p = x + return p +} + +func (x SenderConfig_DomainStrategy) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SenderConfig_DomainStrategy) Descriptor() protoreflect.EnumDescriptor { + return file_app_proxyman_config_proto_enumTypes[2].Descriptor() +} + +func (SenderConfig_DomainStrategy) Type() protoreflect.EnumType { + return &file_app_proxyman_config_proto_enumTypes[2] +} + +func (x SenderConfig_DomainStrategy) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SenderConfig_DomainStrategy.Descriptor instead. +func (SenderConfig_DomainStrategy) EnumDescriptor() ([]byte, []int) { + return file_app_proxyman_config_proto_rawDescGZIP(), []int{6, 0} +} + type InboundConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -497,10 +549,11 @@ type SenderConfig struct { unknownFields protoimpl.UnknownFields // Send traffic through the given IP. Only IP is allowed. - 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"` - 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"` + 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"` + 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"` + DomainStrategy SenderConfig_DomainStrategy `protobuf:"varint,5,opt,name=domain_strategy,json=domainStrategy,proto3,enum=v2ray.core.app.proxyman.SenderConfig_DomainStrategy" json:"domain_strategy,omitempty"` } func (x *SenderConfig) Reset() { @@ -563,6 +616,13 @@ func (x *SenderConfig) GetMultiplexSettings() *MultiplexingConfig { return nil } +func (x *SenderConfig) GetDomainStrategy() SenderConfig_DomainStrategy { + if x != nil { + return x.DomainStrategy + } + return SenderConfig_AS_IS +} + type MultiplexingConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -811,8 +871,8 @@ var file_app_proxyman_config_proto_rawDesc = []byte{ 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x53, 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, 0xc8, - 0x02, 0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xea, + 0x03, 0x0a, 0x0c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x33, 0x0a, 0x03, 0x76, 0x69, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, @@ -832,21 +892,32 @@ var file_app_proxyman_config_proto_rawDesc = []byte{ 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 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, 0x22, 0x50, 0x0a, 0x12, 0x4d, 0x75, 0x6c, - 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, - 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, - 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x2a, 0x23, 0x0a, 0x0e, 0x4b, - 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x12, 0x08, 0x0a, - 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x4c, 0x53, 0x10, 0x01, - 0x42, 0x66, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x50, - 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, - 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, - 0x35, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0xaa, 0x02, - 0x17, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x2e, - 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x78, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x5d, 0x0a, 0x0f, 0x64, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x34, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x2e, 0x53, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x22, 0x41, 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, 0x22, 0x50, 0x0a, 0x12, 0x4d, + 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x78, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x63, + 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x2a, 0x23, 0x0a, + 0x0e, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x12, + 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x4c, 0x53, + 0x10, 0x01, 0x42, 0x66, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, + 0x6e, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, + 0x2f, 0x76, 0x35, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, + 0xaa, 0x02, 0x17, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x70, + 0x70, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x6d, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -861,48 +932,50 @@ func file_app_proxyman_config_proto_rawDescGZIP() []byte { return file_app_proxyman_config_proto_rawDescData } -var file_app_proxyman_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_app_proxyman_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3) var file_app_proxyman_config_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_app_proxyman_config_proto_goTypes = []interface{}{ (KnownProtocols)(0), // 0: v2ray.core.app.proxyman.KnownProtocols (AllocationStrategy_Type)(0), // 1: v2ray.core.app.proxyman.AllocationStrategy.Type - (*InboundConfig)(nil), // 2: v2ray.core.app.proxyman.InboundConfig - (*AllocationStrategy)(nil), // 3: v2ray.core.app.proxyman.AllocationStrategy - (*SniffingConfig)(nil), // 4: v2ray.core.app.proxyman.SniffingConfig - (*ReceiverConfig)(nil), // 5: v2ray.core.app.proxyman.ReceiverConfig - (*InboundHandlerConfig)(nil), // 6: v2ray.core.app.proxyman.InboundHandlerConfig - (*OutboundConfig)(nil), // 7: v2ray.core.app.proxyman.OutboundConfig - (*SenderConfig)(nil), // 8: v2ray.core.app.proxyman.SenderConfig - (*MultiplexingConfig)(nil), // 9: v2ray.core.app.proxyman.MultiplexingConfig - (*AllocationStrategy_AllocationStrategyConcurrency)(nil), // 10: v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency - (*AllocationStrategy_AllocationStrategyRefresh)(nil), // 11: v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyRefresh - (*net.PortRange)(nil), // 12: v2ray.core.common.net.PortRange - (*net.IPOrDomain)(nil), // 13: v2ray.core.common.net.IPOrDomain - (*internet.StreamConfig)(nil), // 14: v2ray.core.transport.internet.StreamConfig - (*anypb.Any)(nil), // 15: google.protobuf.Any - (*internet.ProxyConfig)(nil), // 16: v2ray.core.transport.internet.ProxyConfig + (SenderConfig_DomainStrategy)(0), // 2: v2ray.core.app.proxyman.SenderConfig.DomainStrategy + (*InboundConfig)(nil), // 3: v2ray.core.app.proxyman.InboundConfig + (*AllocationStrategy)(nil), // 4: v2ray.core.app.proxyman.AllocationStrategy + (*SniffingConfig)(nil), // 5: v2ray.core.app.proxyman.SniffingConfig + (*ReceiverConfig)(nil), // 6: v2ray.core.app.proxyman.ReceiverConfig + (*InboundHandlerConfig)(nil), // 7: v2ray.core.app.proxyman.InboundHandlerConfig + (*OutboundConfig)(nil), // 8: v2ray.core.app.proxyman.OutboundConfig + (*SenderConfig)(nil), // 9: v2ray.core.app.proxyman.SenderConfig + (*MultiplexingConfig)(nil), // 10: v2ray.core.app.proxyman.MultiplexingConfig + (*AllocationStrategy_AllocationStrategyConcurrency)(nil), // 11: v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency + (*AllocationStrategy_AllocationStrategyRefresh)(nil), // 12: v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyRefresh + (*net.PortRange)(nil), // 13: v2ray.core.common.net.PortRange + (*net.IPOrDomain)(nil), // 14: v2ray.core.common.net.IPOrDomain + (*internet.StreamConfig)(nil), // 15: v2ray.core.transport.internet.StreamConfig + (*anypb.Any)(nil), // 16: google.protobuf.Any + (*internet.ProxyConfig)(nil), // 17: v2ray.core.transport.internet.ProxyConfig } var file_app_proxyman_config_proto_depIdxs = []int32{ 1, // 0: v2ray.core.app.proxyman.AllocationStrategy.type:type_name -> v2ray.core.app.proxyman.AllocationStrategy.Type - 10, // 1: v2ray.core.app.proxyman.AllocationStrategy.concurrency:type_name -> v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency - 11, // 2: v2ray.core.app.proxyman.AllocationStrategy.refresh:type_name -> v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyRefresh - 12, // 3: v2ray.core.app.proxyman.ReceiverConfig.port_range:type_name -> v2ray.core.common.net.PortRange - 13, // 4: v2ray.core.app.proxyman.ReceiverConfig.listen:type_name -> v2ray.core.common.net.IPOrDomain - 3, // 5: v2ray.core.app.proxyman.ReceiverConfig.allocation_strategy:type_name -> v2ray.core.app.proxyman.AllocationStrategy - 14, // 6: v2ray.core.app.proxyman.ReceiverConfig.stream_settings:type_name -> v2ray.core.transport.internet.StreamConfig + 11, // 1: v2ray.core.app.proxyman.AllocationStrategy.concurrency:type_name -> v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyConcurrency + 12, // 2: v2ray.core.app.proxyman.AllocationStrategy.refresh:type_name -> v2ray.core.app.proxyman.AllocationStrategy.AllocationStrategyRefresh + 13, // 3: v2ray.core.app.proxyman.ReceiverConfig.port_range:type_name -> v2ray.core.common.net.PortRange + 14, // 4: v2ray.core.app.proxyman.ReceiverConfig.listen:type_name -> v2ray.core.common.net.IPOrDomain + 4, // 5: v2ray.core.app.proxyman.ReceiverConfig.allocation_strategy:type_name -> v2ray.core.app.proxyman.AllocationStrategy + 15, // 6: v2ray.core.app.proxyman.ReceiverConfig.stream_settings:type_name -> v2ray.core.transport.internet.StreamConfig 0, // 7: v2ray.core.app.proxyman.ReceiverConfig.domain_override:type_name -> v2ray.core.app.proxyman.KnownProtocols - 4, // 8: v2ray.core.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> v2ray.core.app.proxyman.SniffingConfig - 15, // 9: v2ray.core.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> google.protobuf.Any - 15, // 10: v2ray.core.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> google.protobuf.Any - 13, // 11: v2ray.core.app.proxyman.SenderConfig.via:type_name -> v2ray.core.common.net.IPOrDomain - 14, // 12: v2ray.core.app.proxyman.SenderConfig.stream_settings:type_name -> v2ray.core.transport.internet.StreamConfig - 16, // 13: v2ray.core.app.proxyman.SenderConfig.proxy_settings:type_name -> v2ray.core.transport.internet.ProxyConfig - 9, // 14: v2ray.core.app.proxyman.SenderConfig.multiplex_settings:type_name -> v2ray.core.app.proxyman.MultiplexingConfig - 15, // [15:15] is the sub-list for method output_type - 15, // [15:15] is the sub-list for method input_type - 15, // [15:15] is the sub-list for extension type_name - 15, // [15:15] is the sub-list for extension extendee - 0, // [0:15] is the sub-list for field type_name + 5, // 8: v2ray.core.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> v2ray.core.app.proxyman.SniffingConfig + 16, // 9: v2ray.core.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> google.protobuf.Any + 16, // 10: v2ray.core.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> google.protobuf.Any + 14, // 11: v2ray.core.app.proxyman.SenderConfig.via:type_name -> v2ray.core.common.net.IPOrDomain + 15, // 12: v2ray.core.app.proxyman.SenderConfig.stream_settings:type_name -> v2ray.core.transport.internet.StreamConfig + 17, // 13: v2ray.core.app.proxyman.SenderConfig.proxy_settings:type_name -> v2ray.core.transport.internet.ProxyConfig + 10, // 14: v2ray.core.app.proxyman.SenderConfig.multiplex_settings:type_name -> v2ray.core.app.proxyman.MultiplexingConfig + 2, // 15: v2ray.core.app.proxyman.SenderConfig.domain_strategy:type_name -> v2ray.core.app.proxyman.SenderConfig.DomainStrategy + 16, // [16:16] is the sub-list for method output_type + 16, // [16:16] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name } func init() { file_app_proxyman_config_proto_init() } @@ -1037,7 +1110,7 @@ func file_app_proxyman_config_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_app_proxyman_config_proto_rawDesc, - NumEnums: 2, + NumEnums: 3, NumMessages: 10, NumExtensions: 0, NumServices: 0, diff --git a/app/proxyman/config.proto b/app/proxyman/config.proto index fd7d631e456..66959ffb5e9 100644 --- a/app/proxyman/config.proto +++ b/app/proxyman/config.proto @@ -86,11 +86,19 @@ message InboundHandlerConfig { message OutboundConfig {} message SenderConfig { + enum DomainStrategy { + AS_IS = 0; + USE_IP = 1; + USE_IP4 = 2; + USE_IP6 = 3; + } + // Send traffic through the given IP. Only IP is allowed. v2ray.core.common.net.IPOrDomain via = 1; v2ray.core.transport.internet.StreamConfig stream_settings = 2; v2ray.core.transport.internet.ProxyConfig proxy_settings = 3; MultiplexingConfig multiplex_settings = 4; + DomainStrategy domain_strategy = 5; } message MultiplexingConfig { diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index 586968cbfbe..1ba81413e6b 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -6,11 +6,13 @@ import ( core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/common" + "github.com/v2fly/v2ray-core/v5/common/dice" "github.com/v2fly/v2ray-core/v5/common/mux" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/net/packetaddr" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/common/session" + "github.com/v2fly/v2ray-core/v5/features/dns" "github.com/v2fly/v2ray-core/v5/features/outbound" "github.com/v2fly/v2ray-core/v5/features/policy" "github.com/v2fly/v2ray-core/v5/features/stats" @@ -56,6 +58,7 @@ type Handler struct { mux *mux.ClientManager uplinkCounter stats.Counter downlinkCounter stats.Counter + dns dns.Client } // NewHandler create a new Handler based on the given configuration. @@ -123,6 +126,16 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou } } + if h.senderSettings != nil && h.senderSettings.DomainStrategy != proxyman.SenderConfig_AS_IS { + err := core.RequireFeatures(ctx, func(d dns.Client) error { + h.dns = d + return nil + }) + if err != nil { + return nil, err + } + } + h.proxy = proxyHandler return h, nil } @@ -208,7 +221,19 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn } outbound.Gateway = h.senderSettings.Via.AsAddress() } + + if h.senderSettings.DomainStrategy != proxyman.SenderConfig_AS_IS { + outbound := session.OutboundFromContext(ctx) + if outbound == nil { + outbound = new(session.Outbound) + ctx = session.ContextWithOutbound(ctx, outbound) + } + outbound.Resolver = func(ctx context.Context, domain string) net.Address { + return h.resolveIP(ctx, domain, h.Address()) + } + } } + enablePacketAddrCapture := true if h.senderSettings != nil && h.senderSettings.ProxySettings != nil && h.senderSettings.ProxySettings.HasTag() && h.senderSettings.ProxySettings.TransportLayerProxy { tag := h.senderSettings.ProxySettings.Tag @@ -230,6 +255,22 @@ func (h *Handler) Dial(ctx context.Context, dest net.Destination) (internet.Conn return h.getStatCouterConnection(conn), err } +func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address { + strategy := h.senderSettings.DomainStrategy + ips, err := dns.LookupIPWithOption(h.dns, domain, dns.IPOption{ + IPv4Enable: strategy == proxyman.SenderConfig_USE_IP || strategy == proxyman.SenderConfig_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()), + IPv6Enable: strategy == proxyman.SenderConfig_USE_IP || strategy == proxyman.SenderConfig_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()), + FakeEnable: false, + }) + if err != nil { + newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx)) + } + if len(ips) == 0 { + return nil + } + return net.IPAddress(ips[dice.Roll(len(ips))]) +} + func (h *Handler) getStatCouterConnection(conn internet.Connection) internet.Connection { if h.uplinkCounter != nil || h.downlinkCounter != nil { return &internet.StatCouterConnection{ diff --git a/infra/conf/v4/v2ray.go b/infra/conf/v4/v2ray.go index 96dbcc5a26c..d68fc6cc4e8 100644 --- a/infra/conf/v4/v2ray.go +++ b/infra/conf/v4/v2ray.go @@ -203,13 +203,14 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) { } type OutboundDetourConfig struct { - Protocol string `json:"protocol"` - SendThrough *cfgcommon.Address `json:"sendThrough"` - Tag string `json:"tag"` - Settings *json.RawMessage `json:"settings"` - StreamSetting *StreamConfig `json:"streamSettings"` - ProxySettings *proxycfg.ProxyConfig `json:"proxySettings"` - MuxSettings *muxcfg.MuxConfig `json:"mux"` + Protocol string `json:"protocol"` + SendThrough *cfgcommon.Address `json:"sendThrough"` + Tag string `json:"tag"` + Settings *json.RawMessage `json:"settings"` + StreamSetting *StreamConfig `json:"streamSettings"` + ProxySettings *proxycfg.ProxyConfig `json:"proxySettings"` + MuxSettings *muxcfg.MuxConfig `json:"mux"` + DomainStrategy string `json:"domainStrategy"` } // Build implements Buildable. @@ -244,6 +245,16 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) { senderSettings.MultiplexSettings = c.MuxSettings.Build() } + senderSettings.DomainStrategy = proxyman.SenderConfig_AS_IS + switch strings.ToLower(c.DomainStrategy) { + case "useip", "use_ip", "use-ip": + senderSettings.DomainStrategy = proxyman.SenderConfig_USE_IP + case "useip4", "useipv4", "use_ip4", "use_ipv4", "use_ip_v4", "use-ip4", "use-ipv4", "use-ip-v4": + senderSettings.DomainStrategy = proxyman.SenderConfig_USE_IP4 + case "useip6", "useipv6", "use_ip6", "use_ipv6", "use_ip_v6", "use-ip6", "use-ipv6", "use-ip-v6": + senderSettings.DomainStrategy = proxyman.SenderConfig_USE_IP6 + } + settings := []byte("{}") if c.Settings != nil { settings = ([]byte)(*c.Settings) diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index 20cfcee65d2..e16e72b8aad 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -104,6 +104,11 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte destination.Port = net.Port(server.Port) } } + if h.config.useIP() { + outbound.Resolver = func(ctx context.Context, domain string) net.Address { + return h.resolveIP(ctx, domain, dialer.Address()) + } + } newError("opening connection to ", destination).WriteToLog(session.ExportIDToError(ctx)) input := link.Reader @@ -111,20 +116,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte var conn internet.Connection err := retry.ExponentialBackoff(5, 100).On(func() error { - dialDest := destination - if h.config.useIP() && dialDest.Address.Family().IsDomain() { - ip := h.resolveIP(ctx, dialDest.Address.Domain(), dialer.Address()) - if ip != nil { - dialDest = net.Destination{ - Network: dialDest.Network, - Address: ip, - Port: dialDest.Port, - } - newError("dialing to ", dialDest).WriteToLog(session.ExportIDToError(ctx)) - } - } - - rawConn, err := dialer.Dial(ctx, dialDest) + rawConn, err := dialer.Dial(ctx, destination) if err != nil { return err }