add transport layer padding support to tlsmirror

This commit is contained in:
Shelikhoo
2025-07-04 17:26:05 +01:00
committed by Xiaokang Wang (Shelikhoo)
parent f59383bb82
commit 284d1891c6
6 changed files with 190 additions and 25 deletions
+10 -9
View File
@@ -168,15 +168,16 @@ func (d *persistentMirrorTLSDialer) handleIncomingCarrierConnection(ctx context.
ctx, cancel := context.WithCancel(ctx)
cconnState := &clientConnState{
ctx: ctx,
done: cancel,
localAddr: conn.LocalAddr(),
remoteAddr: conn.RemoteAddr(),
handler: d.handleIncomingReadyConnection,
primaryKey: d.config.PrimaryKey,
readPipe: make(chan []byte, 1),
firstWrite: true,
firstWriteDelay: firstWriteDelay,
ctx: ctx,
done: cancel,
localAddr: conn.LocalAddr(),
remoteAddr: conn.RemoteAddr(),
handler: d.handleIncomingReadyConnection,
primaryKey: d.config.PrimaryKey,
readPipe: make(chan []byte, 1),
firstWrite: true,
firstWriteDelay: firstWriteDelay,
transportLayerPadding: d.config.TransportLayerPadding,
}
cconnState.mirrorConn = mirrorbase.NewMirroredTLSConn(ctx, conn, forwardConn, cconnState.onC2SMessage, cconnState.onS2CMessage, conn,
@@ -35,6 +35,8 @@ type clientConnState struct {
firstWrite bool
firstWriteDelay time.Duration
transportLayerPadding *TransportLayerPadding
}
func (s *clientConnState) GetConnectionContext() context.Context {
@@ -54,6 +56,14 @@ func (s *clientConnState) Read(b []byte) (n int, err error) {
case <-s.ctx.Done():
return 0, s.ctx.Err()
case data := <-s.readPipe:
if s.transportLayerPadding != nil && s.transportLayerPadding.Enabled {
var padding int
data, padding = Unpack(data)
_ = padding
if data == nil {
return 0, nil
}
}
s.readBuffer = bytes.NewBuffer(data)
n, err = s.readBuffer.Read(b)
if err != nil {
@@ -74,6 +84,9 @@ func (s *clientConnState) Write(b []byte) (n int, err error) {
s.firstWrite = false
}
}
if s.transportLayerPadding != nil && s.transportLayerPadding.Enabled {
b = Pack(b, 0)
}
err = s.WriteMessage(b)
if err != nil {
return 0, err
@@ -69,6 +69,50 @@ func (x *TimeSpec) GetUniformRandomMultiplierNanoseconds() uint64 {
return 0
}
type TransportLayerPadding struct {
state protoimpl.MessageState `protogen:"open.v1"`
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *TransportLayerPadding) Reset() {
*x = TransportLayerPadding{}
mi := &file_transport_internet_tlsmirror_server_config_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *TransportLayerPadding) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TransportLayerPadding) ProtoMessage() {}
func (x *TransportLayerPadding) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_tlsmirror_server_config_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TransportLayerPadding.ProtoReflect.Descriptor instead.
func (*TransportLayerPadding) Descriptor() ([]byte, []int) {
return file_transport_internet_tlsmirror_server_config_proto_rawDescGZIP(), []int{1}
}
func (x *TransportLayerPadding) GetEnabled() bool {
if x != nil {
return x.Enabled
}
return false
}
type Config struct {
state protoimpl.MessageState `protogen:"open.v1"`
ForwardAddress string `protobuf:"bytes,1,opt,name=forward_address,json=forwardAddress,proto3" json:"forward_address,omitempty"`
@@ -79,13 +123,14 @@ type Config struct {
PrimaryKey []byte `protobuf:"bytes,6,opt,name=primary_key,json=primaryKey,proto3" json:"primary_key,omitempty"`
ExplicitNonceCiphersuites []uint32 `protobuf:"varint,7,rep,packed,name=explicit_nonce_ciphersuites,json=explicitNonceCiphersuites,proto3" json:"explicit_nonce_ciphersuites,omitempty"`
DeferFirstPayloadWriteTime *TimeSpec `protobuf:"bytes,8,opt,name=defer_first_payload_write_time,json=deferFirstPayloadWriteTime,proto3" json:"defer_first_payload_write_time,omitempty"`
TransportLayerPadding *TransportLayerPadding `protobuf:"bytes,9,opt,name=transport_layer_padding,json=transportLayerPadding,proto3" json:"transport_layer_padding,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Config) Reset() {
*x = Config{}
mi := &file_transport_internet_tlsmirror_server_config_proto_msgTypes[1]
mi := &file_transport_internet_tlsmirror_server_config_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -97,7 +142,7 @@ func (x *Config) String() string {
func (*Config) ProtoMessage() {}
func (x *Config) ProtoReflect() protoreflect.Message {
mi := &file_transport_internet_tlsmirror_server_config_proto_msgTypes[1]
mi := &file_transport_internet_tlsmirror_server_config_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -110,7 +155,7 @@ func (x *Config) ProtoReflect() protoreflect.Message {
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
func (*Config) Descriptor() ([]byte, []int) {
return file_transport_internet_tlsmirror_server_config_proto_rawDescGZIP(), []int{1}
return file_transport_internet_tlsmirror_server_config_proto_rawDescGZIP(), []int{2}
}
func (x *Config) GetForwardAddress() string {
@@ -169,6 +214,13 @@ func (x *Config) GetDeferFirstPayloadWriteTime() *TimeSpec {
return nil
}
func (x *Config) GetTransportLayerPadding() *TransportLayerPadding {
if x != nil {
return x.TransportLayerPadding
}
return nil
}
var File_transport_internet_tlsmirror_server_config_proto protoreflect.FileDescriptor
const file_transport_internet_tlsmirror_server_config_proto_rawDesc = "" +
@@ -176,7 +228,9 @@ const file_transport_internet_tlsmirror_server_config_proto_rawDesc = "" +
"0transport/internet/tlsmirror/server/config.proto\x12.v2ray.core.transport.internet.tlsmirror.server\x1a common/protoext/extensions.proto\x1a7transport/internet/tlsmirror/tlstrafficgen/config.proto\"\x88\x01\n" +
"\bTimeSpec\x12)\n" +
"\x10base_nanoseconds\x18\x01 \x01(\x04R\x0fbaseNanoseconds\x12Q\n" +
"%uniform_random_multiplier_nanoseconds\x18\x02 \x01(\x04R\"uniformRandomMultiplierNanoseconds\"\xb4\x04\n" +
"%uniform_random_multiplier_nanoseconds\x18\x02 \x01(\x04R\"uniformRandomMultiplierNanoseconds\"1\n" +
"\x15TransportLayerPadding\x12\x18\n" +
"\aenabled\x18\x01 \x01(\bR\aenabled\"\xb3\x05\n" +
"\x06Config\x12'\n" +
"\x0fforward_address\x18\x01 \x01(\tR\x0eforwardAddress\x12!\n" +
"\fforward_port\x18\x02 \x01(\rR\vforwardPort\x12\x1f\n" +
@@ -187,7 +241,8 @@ const file_transport_internet_tlsmirror_server_config_proto_rawDesc = "" +
"\vprimary_key\x18\x06 \x01(\fR\n" +
"primaryKey\x12>\n" +
"\x1bexplicit_nonce_ciphersuites\x18\a \x03(\rR\x19explicitNonceCiphersuites\x12|\n" +
"\x1edefer_first_payload_write_time\x18\b \x01(\v28.v2ray.core.transport.internet.tlsmirror.server.TimeSpecR\x1adeferFirstPayloadWriteTime:+\x82\xb5\x18'\n" +
"\x1edefer_first_payload_write_time\x18\b \x01(\v28.v2ray.core.transport.internet.tlsmirror.server.TimeSpecR\x1adeferFirstPayloadWriteTime\x12}\n" +
"\x17transport_layer_padding\x18\t \x01(\v2E.v2ray.core.transport.internet.tlsmirror.server.TransportLayerPaddingR\x15transportLayerPadding:+\x82\xb5\x18'\n" +
"\ttransport\x12\ttlsmirror\x8a\xff)\ttlsmirror\x90\xff)\x01B\xab\x01\n" +
"2com.v2ray.core.transport.internet.tlsmirror.serverP\x01ZBgithub.com/v2fly/v2ray-core/v5/transport/internet/tlsmirror/server\xaa\x02.V2Ray.Core.Transport.Internet.Tlsmirror.Serverb\x06proto3"
@@ -203,20 +258,22 @@ func file_transport_internet_tlsmirror_server_config_proto_rawDescGZIP() []byte
return file_transport_internet_tlsmirror_server_config_proto_rawDescData
}
var file_transport_internet_tlsmirror_server_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_transport_internet_tlsmirror_server_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_transport_internet_tlsmirror_server_config_proto_goTypes = []any{
(*TimeSpec)(nil), // 0: v2ray.core.transport.internet.tlsmirror.server.TimeSpec
(*Config)(nil), // 1: v2ray.core.transport.internet.tlsmirror.server.Config
(*tlstrafficgen.Config)(nil), // 2: v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Config
(*TimeSpec)(nil), // 0: v2ray.core.transport.internet.tlsmirror.server.TimeSpec
(*TransportLayerPadding)(nil), // 1: v2ray.core.transport.internet.tlsmirror.server.TransportLayerPadding
(*Config)(nil), // 2: v2ray.core.transport.internet.tlsmirror.server.Config
(*tlstrafficgen.Config)(nil), // 3: v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Config
}
var file_transport_internet_tlsmirror_server_config_proto_depIdxs = []int32{
2, // 0: v2ray.core.transport.internet.tlsmirror.server.Config.embedded_traffic_generator:type_name -> v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Config
3, // 0: v2ray.core.transport.internet.tlsmirror.server.Config.embedded_traffic_generator:type_name -> v2ray.core.transport.internet.tlsmirror.tlstrafficgen.Config
0, // 1: v2ray.core.transport.internet.tlsmirror.server.Config.defer_first_payload_write_time:type_name -> v2ray.core.transport.internet.tlsmirror.server.TimeSpec
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
1, // 2: v2ray.core.transport.internet.tlsmirror.server.Config.transport_layer_padding:type_name -> v2ray.core.transport.internet.tlsmirror.server.TransportLayerPadding
3, // [3:3] is the sub-list for method output_type
3, // [3:3] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_transport_internet_tlsmirror_server_config_proto_init() }
@@ -230,7 +287,7 @@ func file_transport_internet_tlsmirror_server_config_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_transport_internet_tlsmirror_server_config_proto_rawDesc), len(file_transport_internet_tlsmirror_server_config_proto_rawDesc)),
NumEnums: 0,
NumMessages: 2,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
@@ -14,6 +14,10 @@ message TimeSpec {
uint64 uniform_random_multiplier_nanoseconds = 2;
}
message TransportLayerPadding {
bool enabled = 1;
}
message Config {
option (v2ray.core.common.protoext.message_opt).type = "transport";
option (v2ray.core.common.protoext.message_opt).short_name = "tlsmirror";
@@ -34,4 +38,6 @@ message Config {
repeated uint32 explicit_nonce_ciphersuites = 7;
TimeSpec defer_first_payload_write_time = 8;
TransportLayerPadding transport_layer_padding = 9;
}
@@ -31,6 +31,8 @@ type connState struct {
readBuffer *bytes.Buffer
protocolVersion [2]byte
transportLayerPadding *TransportLayerPadding
}
func (s *connState) Read(b []byte) (n int, err error) {
@@ -46,6 +48,14 @@ func (s *connState) Read(b []byte) (n int, err error) {
case <-s.ctx.Done():
return 0, s.ctx.Err()
case data := <-s.readPipe:
if s.transportLayerPadding != nil && s.transportLayerPadding.Enabled {
var padding int
data, padding = Unpack(data)
_ = padding
if data == nil {
return 0, nil
}
}
s.readBuffer = bytes.NewBuffer(data)
n, err = s.readBuffer.Read(b)
if err != nil {
@@ -56,6 +66,9 @@ func (s *connState) Read(b []byte) (n int, err error) {
}
func (s *connState) Write(b []byte) (n int, err error) {
if s.transportLayerPadding != nil && s.transportLayerPadding.Enabled {
b = Pack(b, 0)
}
err = s.WriteMessage(b)
if err != nil {
return 0, err
@@ -0,0 +1,75 @@
package server
// this file defines methods to pad packets
// with a given number of bytes, and to unpack the padding from a padded packet.
// The packet format is as follows if the desired output length is greater than
// 4 bytes:
// | data | padding | data length |
// The data length is a 32-bit big-endian integer that represents the length of
// the data in bytes.
// If the desired output length is 4 bytes or less, the packet format is as
// follows:
// | padding |
// No payload will be included in the packet.
import "encoding/binary"
// Pack pads the given data with the given number of bytes, and appends the
// length of the data to the end of the data. The returned byte slice
// contains the padded data.
// This generates a packet with a length of
// len(data_OWNERSHIP_RELINQUISHED) + padding + 2
// @param data_OWNERSHIP_RELINQUISHED - The payload, this reference is consumed and should not be used after this call.
// @param padding - The number of padding bytes to add to the data.
func Pack(data_OWNERSHIP_RELINQUISHED []byte, paddingLength int) []byte {
data := append(data_OWNERSHIP_RELINQUISHED, make([]byte, paddingLength)...)
dataLength := len(data_OWNERSHIP_RELINQUISHED)
data = binary.BigEndian.AppendUint32(data, uint32(dataLength))
return data
}
// Pad returns a padding packet of padding length.
// If the padding length is less than 0, nil is returned.
// @param padding - The number of padding bytes to add to the data.
func Pad(paddingLength int) []byte {
if assertPaddingLengthIsNotNegative := paddingLength < 0; assertPaddingLengthIsNotNegative {
return nil
}
switch paddingLength {
case 0:
return []byte{}
case 1:
return []byte{0}
case 2:
return []byte{0, 0}
case 3:
return []byte{0, 0, 0}
case 4:
return []byte{0, 0, 0, 0}
default:
return append(make([]byte, paddingLength-4), byte(paddingLength>>24), byte(paddingLength>>16), byte(paddingLength>>8), byte(paddingLength))
}
}
// Unpack extracts the data and padding from the given padded data. It
// returns the data and the number of padding bytes.
// the data may be nil.
// @param wrappedData_OWNERSHIP_RELINQUISHED - The packet, this reference is consumed and should not be used after this call.
func Unpack(wrappedData_OWNERSHIP_RELINQUISHED []byte) ([]byte, int) {
dataLength := len(wrappedData_OWNERSHIP_RELINQUISHED)
if dataLength < 4 {
return nil, dataLength
}
dataLen := int(binary.BigEndian.Uint32(wrappedData_OWNERSHIP_RELINQUISHED[dataLength-4:]))
if dataLen > len(wrappedData_OWNERSHIP_RELINQUISHED)-4 {
return nil, 0
}
paddingLength := dataLength - dataLen - 4
if paddingLength < 0 {
return nil, paddingLength
}
return wrappedData_OWNERSHIP_RELINQUISHED[:dataLen], paddingLength
}