mirror of https://github.com/XTLS/Xray-core
VLESS Encryption: Add customizable 1-RTT padding parameters; Decrease memory using; Chores
Completes https://github.com/XTLS/Xray-core/pull/5067 --------- Co-authored-by: wwqgtxx <wwqgtxx@gmail.com>pull/5074/head
parent
fbb0ecfb83
commit
e8b02cd664
|
@ -10,6 +10,9 @@ func RandBetween(from int64, to int64) int64 {
|
||||||
if from == to {
|
if from == to {
|
||||||
return from
|
return from
|
||||||
}
|
}
|
||||||
|
if from > to {
|
||||||
|
from, to = to, from
|
||||||
|
}
|
||||||
bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from))
|
bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from))
|
||||||
return from + bigInt.Int64()
|
return from + bigInt.Int64()
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if account.Encryption != "" {
|
if account.Encryption != "" {
|
||||||
return nil, errors.New(`VLESS clients: "encryption" should not in inbound settings`)
|
return nil, errors.New(`VLESS clients: "encryption" should not be in inbound settings`)
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Account = serial.ToTypedMessage(account)
|
user.Account = serial.ToTypedMessage(account)
|
||||||
|
@ -107,12 +107,21 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||||
}
|
}
|
||||||
config.Seconds = uint32(i)
|
config.Seconds = uint32(i)
|
||||||
}
|
}
|
||||||
for i := 3; i < len(s); i++ {
|
padding := 0
|
||||||
if b, _ := base64.RawURLEncoding.DecodeString(s[i]); len(b) != 32 && len(b) != 64 {
|
for _, r := range s[3:] {
|
||||||
|
if len(r) < 20 {
|
||||||
|
padding += len(r) + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if b, _ := base64.RawURLEncoding.DecodeString(r); len(b) != 32 && len(b) != 64 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
config.Decryption = config.Decryption[27+len(s[2]):]
|
config.Decryption = config.Decryption[27+len(s[2]):]
|
||||||
|
if padding > 0 {
|
||||||
|
config.Padding = config.Decryption[:padding-1]
|
||||||
|
config.Decryption = config.Decryption[padding:]
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}() && config.Decryption != "none" {
|
}() && config.Decryption != "none" {
|
||||||
if config.Decryption == "" {
|
if config.Decryption == "" {
|
||||||
|
@ -121,6 +130,10 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
||||||
return nil, errors.New(`VLESS settings: unsupported "decryption": ` + config.Decryption)
|
return nil, errors.New(`VLESS settings: unsupported "decryption": ` + config.Decryption)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.Decryption != "none" && c.Fallbacks != nil {
|
||||||
|
return nil, errors.New(`VLESS settings: "fallbacks" can not be used together with "decryption"`)
|
||||||
|
}
|
||||||
|
|
||||||
for _, fb := range c.Fallbacks {
|
for _, fb := range c.Fallbacks {
|
||||||
var i uint16
|
var i uint16
|
||||||
var s string
|
var s string
|
||||||
|
@ -250,12 +263,21 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) {
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for i := 3; i < len(s); i++ {
|
padding := 0
|
||||||
if b, _ := base64.RawURLEncoding.DecodeString(s[i]); len(b) != 32 && len(b) != 1184 {
|
for _, r := range s[3:] {
|
||||||
|
if len(r) < 20 {
|
||||||
|
padding += len(r) + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if b, _ := base64.RawURLEncoding.DecodeString(r); len(b) != 32 && len(b) != 1184 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
account.Encryption = account.Encryption[27+len(s[2]):]
|
account.Encryption = account.Encryption[27+len(s[2]):]
|
||||||
|
if padding > 0 {
|
||||||
|
account.Padding = account.Encryption[:padding-1]
|
||||||
|
account.Encryption = account.Encryption[padding:]
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}() && account.Encryption != "none" {
|
}() && account.Encryption != "none" {
|
||||||
if account.Encryption == "" {
|
if account.Encryption == "" {
|
||||||
|
|
|
@ -20,6 +20,7 @@ func (a *Account) AsAccount() (protocol.Account, error) {
|
||||||
Encryption: a.Encryption, // needs parser here?
|
Encryption: a.Encryption, // needs parser here?
|
||||||
XorMode: a.XorMode,
|
XorMode: a.XorMode,
|
||||||
Seconds: a.Seconds,
|
Seconds: a.Seconds,
|
||||||
|
Padding: a.Padding,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +34,7 @@ type MemoryAccount struct {
|
||||||
Encryption string
|
Encryption string
|
||||||
XorMode uint32
|
XorMode uint32
|
||||||
Seconds uint32
|
Seconds uint32
|
||||||
|
Padding string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals implements protocol.Account.Equals().
|
// Equals implements protocol.Account.Equals().
|
||||||
|
@ -51,5 +53,6 @@ func (a *MemoryAccount) ToProto() proto.Message {
|
||||||
Encryption: a.Encryption,
|
Encryption: a.Encryption,
|
||||||
XorMode: a.XorMode,
|
XorMode: a.XorMode,
|
||||||
Seconds: a.Seconds,
|
Seconds: a.Seconds,
|
||||||
|
Padding: a.Padding,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ type Account struct {
|
||||||
Encryption string `protobuf:"bytes,3,opt,name=encryption,proto3" json:"encryption,omitempty"`
|
Encryption string `protobuf:"bytes,3,opt,name=encryption,proto3" json:"encryption,omitempty"`
|
||||||
XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"`
|
XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"`
|
||||||
Seconds uint32 `protobuf:"varint,5,opt,name=seconds,proto3" json:"seconds,omitempty"`
|
Seconds uint32 `protobuf:"varint,5,opt,name=seconds,proto3" json:"seconds,omitempty"`
|
||||||
|
Padding string `protobuf:"bytes,6,opt,name=padding,proto3" json:"padding,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Account) Reset() {
|
func (x *Account) Reset() {
|
||||||
|
@ -99,12 +100,19 @@ func (x *Account) GetSeconds() uint32 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Account) GetPadding() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Padding
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
var File_proxy_vless_account_proto protoreflect.FileDescriptor
|
var File_proxy_vless_account_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_proxy_vless_account_proto_rawDesc = []byte{
|
var file_proxy_vless_account_proto_rawDesc = []byte{
|
||||||
0x0a, 0x19, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x63,
|
0x0a, 0x19, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x63,
|
||||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61,
|
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61,
|
||||||
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x81, 0x01,
|
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x9b, 0x01,
|
||||||
0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
|
0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f,
|
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f,
|
||||||
0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1e, 0x0a,
|
0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x1e, 0x0a,
|
||||||
|
@ -113,12 +121,14 @@ var file_proxy_vless_account_proto_rawDesc = []byte{
|
||||||
0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
|
0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07,
|
||||||
0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e,
|
0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e,
|
||||||
0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64,
|
0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64,
|
||||||
0x73, 0x42, 0x52, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72,
|
0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01,
|
||||||
0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74,
|
0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x52, 0x0a, 0x14, 0x63,
|
||||||
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61,
|
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c,
|
||||||
0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65,
|
0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
||||||
0x73, 0x73, 0xaa, 0x02, 0x10, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e,
|
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
|
||||||
0x56, 0x6c, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0xaa, 0x02, 0x10, 0x58,
|
||||||
|
0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x62,
|
||||||
|
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -15,4 +15,5 @@ message Account {
|
||||||
string encryption = 3;
|
string encryption = 3;
|
||||||
uint32 xorMode = 4;
|
uint32 xorMode = 4;
|
||||||
uint32 seconds = 5;
|
uint32 seconds = 5;
|
||||||
|
string padding = 6;
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,7 @@ func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*A
|
||||||
}
|
}
|
||||||
|
|
||||||
// XtlsRead filter and read xtls protocol
|
// XtlsRead filter and read xtls protocol
|
||||||
func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, peerCache *[]byte, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error {
|
func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, trafficState *proxy.TrafficState, ob *session.Outbound, isUplink bool, ctx context.Context) error {
|
||||||
err := func() error {
|
err := func() error {
|
||||||
for {
|
for {
|
||||||
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
|
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
|
||||||
|
@ -194,23 +194,17 @@ func XtlsRead(reader buf.Reader, writer buf.Writer, timer *signal.ActivityTimer,
|
||||||
if !buffer.IsEmpty() {
|
if !buffer.IsEmpty() {
|
||||||
timer.Update()
|
timer.Update()
|
||||||
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
|
if isUplink && trafficState.Inbound.UplinkReaderDirectCopy || !isUplink && trafficState.Outbound.DownlinkReaderDirectCopy {
|
||||||
// XTLS Vision processes struct Encryption Conn's peerCache or TLS Conn's input and rawInput
|
// XTLS Vision processes TLS-like conn's input and rawInput
|
||||||
if peerCache != nil {
|
if inputBuffer, err := buf.ReadFrom(input); err == nil && !inputBuffer.IsEmpty() {
|
||||||
if len(*peerCache) != 0 {
|
buffer, _ = buf.MergeMulti(buffer, inputBuffer)
|
||||||
buffer = buf.MergeBytes(buffer, *peerCache)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if inputBuffer, err := buf.ReadFrom(input); err == nil {
|
|
||||||
if !inputBuffer.IsEmpty() {
|
|
||||||
buffer, _ = buf.MergeMulti(buffer, inputBuffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if rawInputBuffer, err := buf.ReadFrom(rawInput); err == nil {
|
|
||||||
if !rawInputBuffer.IsEmpty() {
|
|
||||||
buffer, _ = buf.MergeMulti(buffer, rawInputBuffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if rawInputBuffer, err := buf.ReadFrom(rawInput); err == nil && !rawInputBuffer.IsEmpty() {
|
||||||
|
buffer, _ = buf.MergeMulti(buffer, rawInputBuffer)
|
||||||
|
}
|
||||||
|
*input = bytes.Reader{} // release memory
|
||||||
|
input = nil
|
||||||
|
*rawInput = bytes.Buffer{} // release memory
|
||||||
|
rawInput = nil
|
||||||
}
|
}
|
||||||
if werr := writer.WriteMultiBuffer(buffer); werr != nil {
|
if werr := writer.WriteMultiBuffer(buffer); werr != nil {
|
||||||
return werr
|
return werr
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/crypto"
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/protocol"
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
"lukechampine.com/blake3"
|
"lukechampine.com/blake3"
|
||||||
|
@ -23,6 +22,8 @@ type ClientInstance struct {
|
||||||
RelaysLength int
|
RelaysLength int
|
||||||
XorMode uint32
|
XorMode uint32
|
||||||
Seconds uint32
|
Seconds uint32
|
||||||
|
PaddingLens [][2]int
|
||||||
|
PaddingGaps [][2]int
|
||||||
|
|
||||||
RWLock sync.RWMutex
|
RWLock sync.RWMutex
|
||||||
Expire time.Time
|
Expire time.Time
|
||||||
|
@ -30,15 +31,13 @@ type ClientInstance struct {
|
||||||
Ticket []byte
|
Ticket []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32) (err error) {
|
func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) {
|
||||||
if i.NfsPKeys != nil {
|
if i.NfsPKeys != nil {
|
||||||
err = errors.New("already initialized")
|
return errors.New("already initialized")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
l := len(nfsPKeysBytes)
|
l := len(nfsPKeysBytes)
|
||||||
if l == 0 {
|
if l == 0 {
|
||||||
err = errors.New("empty nfsPKeysBytes")
|
return errors.New("empty nfsPKeysBytes")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
i.NfsPKeys = make([]any, l)
|
i.NfsPKeys = make([]any, l)
|
||||||
i.NfsPKeysBytes = nfsPKeysBytes
|
i.NfsPKeysBytes = nfsPKeysBytes
|
||||||
|
@ -60,7 +59,7 @@ func (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32) (
|
||||||
i.RelaysLength -= 32
|
i.RelaysLength -= 32
|
||||||
i.XorMode = xorMode
|
i.XorMode = xorMode
|
||||||
i.Seconds = seconds
|
i.Seconds = seconds
|
||||||
return
|
return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||||
|
@ -71,7 +70,7 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||||
|
|
||||||
ivAndRealysLength := 16 + i.RelaysLength
|
ivAndRealysLength := 16 + i.RelaysLength
|
||||||
pfsKeyExchangeLength := 18 + 1184 + 32 + 16
|
pfsKeyExchangeLength := 18 + 1184 + 32 + 16
|
||||||
paddingLength := int(crypto.RandBetween(100, 1000))
|
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)
|
||||||
clientHello := make([]byte, ivAndRealysLength+pfsKeyExchangeLength+paddingLength)
|
clientHello := make([]byte, ivAndRealysLength+pfsKeyExchangeLength+paddingLength)
|
||||||
|
|
||||||
iv := clientHello[:16]
|
iv := clientHello[:16]
|
||||||
|
@ -140,10 +139,18 @@ func (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||||
nfsAEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
nfsAEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||||
nfsAEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
nfsAEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||||
|
|
||||||
if _, err := conn.Write(clientHello); err != nil {
|
paddingLens[0] = ivAndRealysLength + pfsKeyExchangeLength + paddingLens[0]
|
||||||
return nil, err
|
for i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||||
|
if l > 0 {
|
||||||
|
if _, err := conn.Write(clientHello[:l]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
clientHello = clientHello[l:]
|
||||||
|
}
|
||||||
|
if len(paddingGaps) > i {
|
||||||
|
time.Sleep(paddingGaps[i])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// padding can be sent in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
|
||||||
|
|
||||||
encryptedPfsPublicKey := make([]byte, 1088+32+16)
|
encryptedPfsPublicKey := make([]byte, 1088+32+16)
|
||||||
if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {
|
if _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {
|
||||||
|
|
|
@ -7,10 +7,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/crypto"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
"lukechampine.com/blake3"
|
"lukechampine.com/blake3"
|
||||||
|
@ -31,15 +33,14 @@ type CommonConn struct {
|
||||||
AEAD *AEAD
|
AEAD *AEAD
|
||||||
PeerAEAD *AEAD
|
PeerAEAD *AEAD
|
||||||
PeerPadding []byte
|
PeerPadding []byte
|
||||||
PeerInBytes []byte
|
rawInput bytes.Buffer
|
||||||
PeerCache []byte
|
input bytes.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCommonConn(conn net.Conn, useAES bool) *CommonConn {
|
func NewCommonConn(conn net.Conn, useAES bool) *CommonConn {
|
||||||
return &CommonConn{
|
return &CommonConn{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
UseAES: useAES,
|
UseAES: useAES,
|
||||||
PeerInBytes: make([]byte, 5+17000), // no need to use sync.Pool, because we are always reading
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,16 +100,14 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||||
}
|
}
|
||||||
c.PeerPadding = nil
|
c.PeerPadding = nil
|
||||||
}
|
}
|
||||||
if len(c.PeerCache) > 0 {
|
if c.input.Len() > 0 {
|
||||||
n := copy(b, c.PeerCache)
|
return c.input.Read(b)
|
||||||
c.PeerCache = c.PeerCache[n:]
|
|
||||||
return n, nil
|
|
||||||
}
|
}
|
||||||
peerHeader := c.PeerInBytes[:5]
|
peerHeader := [5]byte{}
|
||||||
if _, err := io.ReadFull(c.Conn, peerHeader); err != nil {
|
if _, err := io.ReadFull(c.Conn, peerHeader[:]); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
l, err := DecodeHeader(c.PeerInBytes[:5]) // l: 17~17000
|
l, err := DecodeHeader(peerHeader[:]) // l: 17~17000
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if c.Client != nil && strings.Contains(err.Error(), "invalid header: ") { // client's 0-RTT
|
if c.Client != nil && strings.Contains(err.Error(), "invalid header: ") { // client's 0-RTT
|
||||||
c.Client.RWLock.Lock()
|
c.Client.RWLock.Lock()
|
||||||
|
@ -121,7 +120,10 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
c.Client = nil
|
c.Client = nil
|
||||||
peerData := c.PeerInBytes[5 : 5+l]
|
if c.rawInput.Cap() < l {
|
||||||
|
c.rawInput.Grow(l) // no need to use sync.Pool, because we are always reading
|
||||||
|
}
|
||||||
|
peerData := c.rawInput.Bytes()[:l]
|
||||||
if _, err := io.ReadFull(c.Conn, peerData); err != nil {
|
if _, err := io.ReadFull(c.Conn, peerData); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -131,9 +133,9 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||||
}
|
}
|
||||||
var newAEAD *AEAD
|
var newAEAD *AEAD
|
||||||
if bytes.Equal(c.PeerAEAD.Nonce[:], MaxNonce) {
|
if bytes.Equal(c.PeerAEAD.Nonce[:], MaxNonce) {
|
||||||
newAEAD = NewAEAD(c.PeerInBytes[:5+l], c.UnitedKey, c.UseAES)
|
newAEAD = NewAEAD(append(peerHeader[:], peerData...), c.UnitedKey, c.UseAES)
|
||||||
}
|
}
|
||||||
_, err = c.PeerAEAD.Open(dst[:0], nil, peerData, peerHeader)
|
_, err = c.PeerAEAD.Open(dst[:0], nil, peerData, peerHeader[:])
|
||||||
if newAEAD != nil {
|
if newAEAD != nil {
|
||||||
c.PeerAEAD = newAEAD
|
c.PeerAEAD = newAEAD
|
||||||
}
|
}
|
||||||
|
@ -141,7 +143,7 @@ func (c *CommonConn) Read(b []byte) (int, error) {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
if len(dst) > len(b) {
|
if len(dst) > len(b) {
|
||||||
c.PeerCache = dst[copy(b, dst):]
|
c.input.Reset(dst[copy(b, dst):])
|
||||||
dst = b // for len(dst)
|
dst = b // for len(dst)
|
||||||
}
|
}
|
||||||
return len(dst), nil
|
return len(dst), nil
|
||||||
|
@ -213,7 +215,55 @@ func DecodeHeader(h []byte) (l int, err error) {
|
||||||
l = 0
|
l = 0
|
||||||
}
|
}
|
||||||
if l < 17 || l > 17000 { // TODO: TLSv1.3 max length
|
if l < 17 || l > 17000 { // TODO: TLSv1.3 max length
|
||||||
err = errors.New("invalid header: ", fmt.Sprintf("%v", h[:5])) // DO NOT CHANGE: relied by client's Read()
|
err = errors.New("invalid header: " + fmt.Sprintf("%v", h[:5])) // DO NOT CHANGE: relied by client's Read()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParsePadding(padding string, paddingLens, paddingGaps *[][2]int) (err error) {
|
||||||
|
if padding == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
maxLen := 0
|
||||||
|
for i, s := range strings.Split(padding, ".") {
|
||||||
|
x := strings.SplitN(s, "-", 2)
|
||||||
|
if len(x) != 2 || x[0] == "" || x[1] == "" {
|
||||||
|
return errors.New("invalid padding lenth/gap parameter: " + s)
|
||||||
|
}
|
||||||
|
y := [2]int{}
|
||||||
|
if y[0], err = strconv.Atoi(x[0]); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if y[1], err = strconv.Atoi(x[1]); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if i == 0 && (y[0] < 17 || y[1] < 17) {
|
||||||
|
return errors.New("first padding length must be larger than 16")
|
||||||
|
}
|
||||||
|
if i%2 == 0 {
|
||||||
|
*paddingLens = append(*paddingLens, y)
|
||||||
|
maxLen += max(y[0], y[1])
|
||||||
|
} else {
|
||||||
|
*paddingGaps = append(*paddingGaps, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if maxLen > 65535 {
|
||||||
|
return errors.New("total padding length must be smaller than 65536")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreatPadding(paddingLens, paddingGaps [][2]int) (length int, lens []int, gaps []time.Duration) {
|
||||||
|
if len(paddingLens) == 0 {
|
||||||
|
paddingLens = [][2]int{{111, 1111}, {3333, -1234}}
|
||||||
|
paddingGaps = [][2]int{{111, -66}}
|
||||||
|
}
|
||||||
|
for _, l := range paddingLens {
|
||||||
|
lens = append(lens, int(max(0, crypto.RandBetween(int64(l[0]), int64(l[1])))))
|
||||||
|
length += lens[len(lens)-1]
|
||||||
|
}
|
||||||
|
for _, g := range paddingGaps {
|
||||||
|
gaps = append(gaps, time.Duration(max(0, crypto.RandBetween(int64(g[0]), int64(g[1]))))*time.Millisecond)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,21 +30,21 @@ type ServerInstance struct {
|
||||||
RelaysLength int
|
RelaysLength int
|
||||||
XorMode uint32
|
XorMode uint32
|
||||||
Seconds uint32
|
Seconds uint32
|
||||||
|
PaddingLens [][2]int
|
||||||
|
PaddingGaps [][2]int
|
||||||
|
|
||||||
RWLock sync.RWMutex
|
RWLock sync.RWMutex
|
||||||
Sessions map[[16]byte]*ServerSession
|
Sessions map[[16]byte]*ServerSession
|
||||||
Closed bool
|
Closed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32) (err error) {
|
func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) {
|
||||||
if i.NfsSKeys != nil {
|
if i.NfsSKeys != nil {
|
||||||
err = errors.New("already initialized")
|
return errors.New("already initialized")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
l := len(nfsSKeysBytes)
|
l := len(nfsSKeysBytes)
|
||||||
if l == 0 {
|
if l == 0 {
|
||||||
err = errors.New("empty nfsSKeysBytes")
|
return errors.New("empty nfsSKeysBytes")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
i.NfsSKeys = make([]any, l)
|
i.NfsSKeys = make([]any, l)
|
||||||
i.NfsPKeysBytes = make([][]byte, l)
|
i.NfsPKeysBytes = make([][]byte, l)
|
||||||
|
@ -88,7 +88,7 @@ func (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode, seconds uint32) (
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
return
|
return ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ServerInstance) Close() (err error) {
|
func (i *ServerInstance) Close() (err error) {
|
||||||
|
@ -98,7 +98,7 @@ func (i *ServerInstance) Close() (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
func (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn, error) {
|
||||||
if i.NfsSKeys == nil {
|
if i.NfsSKeys == nil {
|
||||||
return nil, errors.New("uninitialized")
|
return nil, errors.New("uninitialized")
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,9 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||||
if _, err := io.ReadFull(conn, ivAndRelays); err != nil {
|
if _, err := io.ReadFull(conn, ivAndRelays); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if fallback != nil {
|
||||||
|
*fallback = append(*fallback, ivAndRelays...)
|
||||||
|
}
|
||||||
iv := ivAndRelays[:16]
|
iv := ivAndRelays[:16]
|
||||||
relays := ivAndRelays[16:]
|
relays := ivAndRelays[16:]
|
||||||
var nfsKey []byte
|
var nfsKey []byte
|
||||||
|
@ -157,6 +160,9 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if fallback != nil {
|
||||||
|
*fallback = append(*fallback, encryptedLength...)
|
||||||
|
}
|
||||||
decryptedLength := make([]byte, 2)
|
decryptedLength := make([]byte, 2)
|
||||||
if _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
if _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil {
|
||||||
c.UseAES = !c.UseAES
|
c.UseAES = !c.UseAES
|
||||||
|
@ -165,6 +171,9 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if fallback != nil {
|
||||||
|
*fallback = nil
|
||||||
|
}
|
||||||
length := DecodeLength(decryptedLength)
|
length := DecodeLength(decryptedLength)
|
||||||
|
|
||||||
if length == 32 {
|
if length == 32 {
|
||||||
|
@ -183,7 +192,7 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||||
s := i.Sessions[[16]byte(ticket)]
|
s := i.Sessions[[16]byte(ticket)]
|
||||||
i.RWLock.RUnlock()
|
i.RWLock.RUnlock()
|
||||||
if s == nil {
|
if s == nil {
|
||||||
noises := make([]byte, crypto.RandBetween(1268, 2268)) // matches 1-RTT's server hello length for "random", though it is not important, just for example
|
noises := make([]byte, crypto.RandBetween(1279, 2279)) // matches 1-RTT's server hello length for "random", though it is not important, just for example
|
||||||
var err error
|
var err error
|
||||||
for err == nil {
|
for err == nil {
|
||||||
rand.Read(noises)
|
rand.Read(noises)
|
||||||
|
@ -237,25 +246,10 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||||
c.UnitedKey = append(pfsKey, nfsKey...)
|
c.UnitedKey = append(pfsKey, nfsKey...)
|
||||||
c.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)
|
c.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)
|
||||||
c.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1184+32], c.UnitedKey, c.UseAES)
|
c.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1184+32], c.UnitedKey, c.UseAES)
|
||||||
|
|
||||||
ticket := make([]byte, 16)
|
ticket := make([]byte, 16)
|
||||||
rand.Read(ticket)
|
rand.Read(ticket)
|
||||||
copy(ticket, EncodeLength(int(i.Seconds*4/5)))
|
copy(ticket, EncodeLength(int(i.Seconds*4/5)))
|
||||||
|
|
||||||
pfsKeyExchangeLength := 1088 + 32 + 16
|
|
||||||
encryptedTicketLength := 32
|
|
||||||
paddingLength := int(crypto.RandBetween(100, 1000))
|
|
||||||
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
|
|
||||||
nfsAEAD.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)
|
|
||||||
c.AEAD.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket, nil)
|
|
||||||
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
|
|
||||||
c.AEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
|
||||||
c.AEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
|
||||||
|
|
||||||
if _, err := conn.Write(serverHello); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// padding can be sent in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
|
||||||
|
|
||||||
if i.Seconds > 0 {
|
if i.Seconds > 0 {
|
||||||
i.RWLock.Lock()
|
i.RWLock.Lock()
|
||||||
i.Sessions[[16]byte(ticket)] = &ServerSession{
|
i.Sessions[[16]byte(ticket)] = &ServerSession{
|
||||||
|
@ -265,6 +259,29 @@ func (i *ServerInstance) Handshake(conn net.Conn) (*CommonConn, error) {
|
||||||
i.RWLock.Unlock()
|
i.RWLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pfsKeyExchangeLength := 1088 + 32 + 16
|
||||||
|
encryptedTicketLength := 32
|
||||||
|
paddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)
|
||||||
|
serverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)
|
||||||
|
nfsAEAD.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)
|
||||||
|
c.AEAD.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket, nil)
|
||||||
|
padding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]
|
||||||
|
c.AEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)
|
||||||
|
c.AEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)
|
||||||
|
|
||||||
|
paddingLens[0] = pfsKeyExchangeLength + encryptedTicketLength + paddingLens[0]
|
||||||
|
for i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control
|
||||||
|
if l > 0 {
|
||||||
|
if _, err := conn.Write(serverHello[:l]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
serverHello = serverHello[l:]
|
||||||
|
}
|
||||||
|
if len(paddingGaps) > i {
|
||||||
|
time.Sleep(paddingGaps[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// important: allows client sends padding slowly, eliminating 1-RTT's traffic pattern
|
// important: allows client sends padding slowly, eliminating 1-RTT's traffic pattern
|
||||||
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
if _, err := io.ReadFull(conn, encryptedLength); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -116,6 +116,7 @@ type Config struct {
|
||||||
Decryption string `protobuf:"bytes,3,opt,name=decryption,proto3" json:"decryption,omitempty"`
|
Decryption string `protobuf:"bytes,3,opt,name=decryption,proto3" json:"decryption,omitempty"`
|
||||||
XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"`
|
XorMode uint32 `protobuf:"varint,4,opt,name=xorMode,proto3" json:"xorMode,omitempty"`
|
||||||
Seconds uint32 `protobuf:"varint,5,opt,name=seconds,proto3" json:"seconds,omitempty"`
|
Seconds uint32 `protobuf:"varint,5,opt,name=seconds,proto3" json:"seconds,omitempty"`
|
||||||
|
Padding string `protobuf:"bytes,6,opt,name=padding,proto3" json:"padding,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
|
@ -183,6 +184,13 @@ func (x *Config) GetSeconds() uint32 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetPadding() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Padding
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
var File_proxy_vless_inbound_config_proto protoreflect.FileDescriptor
|
var File_proxy_vless_inbound_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_proxy_vless_inbound_config_proto_rawDesc = []byte{
|
var file_proxy_vless_inbound_config_proto_rawDesc = []byte{
|
||||||
|
@ -199,7 +207,7 @@ var file_proxy_vless_inbound_config_proto_rawDesc = []byte{
|
||||||
0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20,
|
0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65,
|
0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65,
|
||||||
0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0xd4, 0x01,
|
0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0xee, 0x01,
|
||||||
0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65,
|
0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65,
|
||||||
0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
||||||
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
|
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
|
||||||
|
@ -213,14 +221,16 @@ var file_proxy_vless_inbound_config_proto_rawDesc = []byte{
|
||||||
0x12, 0x18, 0x0a, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
|
0x12, 0x18, 0x0a, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||||
0x0d, 0x52, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65,
|
0x0d, 0x52, 0x07, 0x78, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65,
|
||||||
0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x65, 0x63,
|
0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x65, 0x63,
|
||||||
0x6f, 0x6e, 0x64, 0x73, 0x42, 0x6a, 0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79,
|
0x6f, 0x6e, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x18,
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62,
|
0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x6a,
|
||||||
0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
|
0x0a, 0x1c, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79,
|
||||||
0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72,
|
0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01,
|
||||||
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e,
|
0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c,
|
||||||
0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f,
|
0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78,
|
||||||
0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64,
|
0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa,
|
||||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x02, 0x18, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65,
|
||||||
|
0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -24,4 +24,5 @@ message Config {
|
||||||
string decryption = 3;
|
string decryption = 3;
|
||||||
uint32 xorMode = 4;
|
uint32 xorMode = 4;
|
||||||
uint32 seconds = 5;
|
uint32 seconds = 5;
|
||||||
|
string padding = 6;
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@ func New(ctx context.Context, config *Config, dc dns.Client, validator vless.Val
|
||||||
nfsSKeysBytes = append(nfsSKeysBytes, b)
|
nfsSKeysBytes = append(nfsSKeysBytes, b)
|
||||||
}
|
}
|
||||||
handler.decryption = &encryption.ServerInstance{}
|
handler.decryption = &encryption.ServerInstance{}
|
||||||
if err := handler.decryption.Init(nfsSKeysBytes, config.XorMode, config.Seconds); err != nil {
|
if err := handler.decryption.Init(nfsSKeysBytes, config.XorMode, config.Seconds, config.Padding); err != nil {
|
||||||
return nil, errors.New("failed to use decryption").Base(err).AtError()
|
return nil, errors.New("failed to use decryption").Base(err).AtError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,8 +220,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
||||||
|
|
||||||
if h.decryption != nil {
|
if h.decryption != nil {
|
||||||
var err error
|
var err error
|
||||||
connection, err = h.decryption.Handshake(connection)
|
if connection, err = h.decryption.Handshake(connection, nil); err != nil {
|
||||||
if err != nil {
|
|
||||||
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
|
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -491,7 +490,6 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
||||||
// Flow: requestAddons.Flow,
|
// Flow: requestAddons.Flow,
|
||||||
}
|
}
|
||||||
|
|
||||||
var peerCache *[]byte
|
|
||||||
var input *bytes.Reader
|
var input *bytes.Reader
|
||||||
var rawInput *bytes.Buffer
|
var rawInput *bytes.Buffer
|
||||||
switch requestAddons.Flow {
|
switch requestAddons.Flow {
|
||||||
|
@ -504,16 +502,15 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
||||||
case protocol.RequestCommandMux:
|
case protocol.RequestCommandMux:
|
||||||
fallthrough // we will break Mux connections that contain TCP requests
|
fallthrough // we will break Mux connections that contain TCP requests
|
||||||
case protocol.RequestCommandTCP:
|
case protocol.RequestCommandTCP:
|
||||||
if serverConn, ok := connection.(*encryption.CommonConn); ok {
|
|
||||||
peerCache = &serverConn.PeerCache
|
|
||||||
if _, ok := serverConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransport(iConn) {
|
|
||||||
inbound.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport can not use Linux Splice
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
var t reflect.Type
|
var t reflect.Type
|
||||||
var p uintptr
|
var p uintptr
|
||||||
if tlsConn, ok := iConn.(*tls.Conn); ok {
|
if commonConn, ok := connection.(*encryption.CommonConn); ok {
|
||||||
|
if _, ok := commonConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransport(iConn) {
|
||||||
|
inbound.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport can not use Linux Splice
|
||||||
|
}
|
||||||
|
t = reflect.TypeOf(commonConn).Elem()
|
||||||
|
p = uintptr(unsafe.Pointer(commonConn))
|
||||||
|
} else if tlsConn, ok := iConn.(*tls.Conn); ok {
|
||||||
if tlsConn.ConnectionState().Version != gotls.VersionTLS13 {
|
if tlsConn.ConnectionState().Version != gotls.VersionTLS13 {
|
||||||
return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, tlsConn.ConnectionState().Version).AtWarning()
|
return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, tlsConn.ConnectionState().Version).AtWarning()
|
||||||
}
|
}
|
||||||
|
@ -579,7 +576,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
||||||
if requestAddons.Flow == vless.XRV {
|
if requestAddons.Flow == vless.XRV {
|
||||||
ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice
|
ctx1 := session.ContextWithInbound(ctx, nil) // TODO enable splice
|
||||||
clientReader = proxy.NewVisionReader(clientReader, trafficState, true, ctx1)
|
clientReader = proxy.NewVisionReader(clientReader, trafficState, true, ctx1)
|
||||||
err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, peerCache, input, rawInput, trafficState, nil, true, ctx1)
|
err = encoding.XtlsRead(clientReader, serverWriter, timer, connection, input, rawInput, trafficState, nil, true, ctx1)
|
||||||
} else {
|
} else {
|
||||||
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer
|
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBuffer
|
||||||
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
|
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer))
|
||||||
|
|
|
@ -77,7 +77,7 @@ func New(ctx context.Context, config *Config) (*Handler, error) {
|
||||||
nfsPKeysBytes = append(nfsPKeysBytes, b)
|
nfsPKeysBytes = append(nfsPKeysBytes, b)
|
||||||
}
|
}
|
||||||
handler.encryption = &encryption.ClientInstance{}
|
handler.encryption = &encryption.ClientInstance{}
|
||||||
if err := handler.encryption.Init(nfsPKeysBytes, a.XorMode, a.Seconds); err != nil {
|
if err := handler.encryption.Init(nfsPKeysBytes, a.XorMode, a.Seconds, a.Padding); err != nil {
|
||||||
return nil, errors.New("failed to use encryption").Base(err).AtError()
|
return nil, errors.New("failed to use encryption").Base(err).AtError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,8 +118,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||||
|
|
||||||
if h.encryption != nil {
|
if h.encryption != nil {
|
||||||
var err error
|
var err error
|
||||||
conn, err = h.encryption.Handshake(conn)
|
if conn, err = h.encryption.Handshake(conn); err != nil {
|
||||||
if err != nil {
|
|
||||||
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
|
return errors.New("ML-KEM-768 handshake failed").Base(err).AtInfo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,7 +145,6 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||||
Flow: account.Flow,
|
Flow: account.Flow,
|
||||||
}
|
}
|
||||||
|
|
||||||
var peerCache *[]byte
|
|
||||||
var input *bytes.Reader
|
var input *bytes.Reader
|
||||||
var rawInput *bytes.Buffer
|
var rawInput *bytes.Buffer
|
||||||
allowUDP443 := false
|
allowUDP443 := false
|
||||||
|
@ -165,16 +163,15 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||||
case protocol.RequestCommandMux:
|
case protocol.RequestCommandMux:
|
||||||
fallthrough // let server break Mux connections that contain TCP requests
|
fallthrough // let server break Mux connections that contain TCP requests
|
||||||
case protocol.RequestCommandTCP:
|
case protocol.RequestCommandTCP:
|
||||||
if clientConn, ok := conn.(*encryption.CommonConn); ok {
|
|
||||||
peerCache = &clientConn.PeerCache
|
|
||||||
if _, ok := clientConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransport(iConn) {
|
|
||||||
ob.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport can not use Linux Splice
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
var t reflect.Type
|
var t reflect.Type
|
||||||
var p uintptr
|
var p uintptr
|
||||||
if tlsConn, ok := iConn.(*tls.Conn); ok {
|
if commonConn, ok := conn.(*encryption.CommonConn); ok {
|
||||||
|
if _, ok := commonConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransport(iConn) {
|
||||||
|
ob.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport can not use Linux Splice
|
||||||
|
}
|
||||||
|
t = reflect.TypeOf(commonConn).Elem()
|
||||||
|
p = uintptr(unsafe.Pointer(commonConn))
|
||||||
|
} else if tlsConn, ok := iConn.(*tls.Conn); ok {
|
||||||
t = reflect.TypeOf(tlsConn.Conn).Elem()
|
t = reflect.TypeOf(tlsConn.Conn).Elem()
|
||||||
p = uintptr(unsafe.Pointer(tlsConn.Conn))
|
p = uintptr(unsafe.Pointer(tlsConn.Conn))
|
||||||
} else if utlsConn, ok := iConn.(*tls.UConn); ok {
|
} else if utlsConn, ok := iConn.(*tls.UConn); ok {
|
||||||
|
@ -306,7 +303,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
||||||
}
|
}
|
||||||
|
|
||||||
if requestAddons.Flow == vless.XRV {
|
if requestAddons.Flow == vless.XRV {
|
||||||
err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, peerCache, input, rawInput, trafficState, ob, false, ctx)
|
err = encoding.XtlsRead(serverReader, clientWriter, timer, conn, input, rawInput, trafficState, ob, false, ctx)
|
||||||
} else {
|
} else {
|
||||||
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer
|
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBuffer
|
||||||
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
|
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))
|
||||||
|
|
Loading…
Reference in New Issue