mirror of
https://github.com/v2fly/v2ray-core.git
synced 2025-01-21 16:56:27 -05:00
more security types in vmess.
This commit is contained in:
parent
6d0915cc8c
commit
85163d4b47
@ -98,6 +98,9 @@ func (v *AuthenticationReader) NextChunk() error {
|
|||||||
if size == v.auth.Overhead() {
|
if size == v.auth.Overhead() {
|
||||||
return io.EOF
|
return io.EOF
|
||||||
}
|
}
|
||||||
|
if size < v.auth.Overhead() {
|
||||||
|
return errors.New("AuthenticationReader: invalid packet size.")
|
||||||
|
}
|
||||||
cipherChunk := v.buffer.BytesRange(2, size+2)
|
cipherChunk := v.buffer.BytesRange(2, size+2)
|
||||||
plainChunk, err := v.auth.Open(cipherChunk, cipherChunk)
|
plainChunk, err := v.auth.Open(cipherChunk, cipherChunk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -176,7 +179,7 @@ func (v *AuthenticationWriter) Write(b []byte) (int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
serial.Uint16ToBytes(uint16(len(cipherChunk)), b[:0])
|
serial.Uint16ToBytes(uint16(len(cipherChunk)), v.buffer[:0])
|
||||||
_, err = v.writer.Write(v.buffer[:2+len(cipherChunk)])
|
_, err = v.writer.Write(v.buffer[:2+len(cipherChunk)])
|
||||||
return len(b), err
|
return len(b), err
|
||||||
}
|
}
|
||||||
|
@ -29,11 +29,14 @@ func NewAdaptiveWriter(writer io.Writer) *AdaptiveWriter {
|
|||||||
// Write implements Writer.Write(). Write() takes ownership of the given buffer.
|
// Write implements Writer.Write(). Write() takes ownership of the given buffer.
|
||||||
func (v *AdaptiveWriter) Write(buffer *alloc.Buffer) error {
|
func (v *AdaptiveWriter) Write(buffer *alloc.Buffer) error {
|
||||||
defer buffer.Release()
|
defer buffer.Release()
|
||||||
for !buffer.IsEmpty() {
|
for {
|
||||||
nBytes, err := v.writer.Write(buffer.Bytes())
|
nBytes, err := v.writer.Write(buffer.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if nBytes == buffer.Len() {
|
||||||
|
break
|
||||||
|
}
|
||||||
buffer.SliceFrom(nBytes)
|
buffer.SliceFrom(nBytes)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -31,13 +31,20 @@ func (v *RequestOption) Clear(option RequestOption) {
|
|||||||
*v = (*v & (^option))
|
*v = (*v & (^option))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Security byte
|
||||||
|
|
||||||
|
func (v Security) Is(t SecurityType) bool {
|
||||||
|
return v == Security(t)
|
||||||
|
}
|
||||||
|
|
||||||
type RequestHeader struct {
|
type RequestHeader struct {
|
||||||
Version byte
|
Version byte
|
||||||
User *User
|
User *User
|
||||||
Command RequestCommand
|
Command RequestCommand
|
||||||
Option RequestOption
|
Option RequestOption
|
||||||
Address v2net.Address
|
Security Security
|
||||||
Port v2net.Port
|
Address v2net.Address
|
||||||
|
Port v2net.Port
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *RequestHeader) Destination() v2net.Destination {
|
func (v *RequestHeader) Destination() v2net.Destination {
|
||||||
|
@ -2,6 +2,7 @@ package encoding
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
|
|
||||||
"v2ray.com/core/common/crypto"
|
"v2ray.com/core/common/crypto"
|
||||||
"v2ray.com/core/common/serial"
|
"v2ray.com/core/common/serial"
|
||||||
)
|
)
|
||||||
|
24
proxy/vmess/encoding/auth_test.go
Normal file
24
proxy/vmess/encoding/auth_test.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package encoding_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "v2ray.com/core/proxy/vmess/encoding"
|
||||||
|
"v2ray.com/core/testing/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFnvAuth(t *testing.T) {
|
||||||
|
assert := assert.On(t)
|
||||||
|
fnvAuth := new(FnvAuthenticator)
|
||||||
|
|
||||||
|
expectedText := make([]byte, 256)
|
||||||
|
rand.Read(expectedText)
|
||||||
|
|
||||||
|
buffer := make([]byte, 512)
|
||||||
|
b := fnvAuth.Seal(buffer, nil, expectedText, nil)
|
||||||
|
b, err := fnvAuth.Open(buffer, nil, b, nil)
|
||||||
|
assert.Error(err).IsNil()
|
||||||
|
assert.Int(len(b)).Equals(256)
|
||||||
|
assert.Bytes(b).Equals(expectedText)
|
||||||
|
}
|
@ -1,15 +1,23 @@
|
|||||||
package encoding
|
package encoding
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
|
|
||||||
"v2ray.com/core/common/crypto"
|
"v2ray.com/core/common/crypto"
|
||||||
|
"v2ray.com/core/common/dice"
|
||||||
|
v2io "v2ray.com/core/common/io"
|
||||||
"v2ray.com/core/common/log"
|
"v2ray.com/core/common/log"
|
||||||
v2net "v2ray.com/core/common/net"
|
v2net "v2ray.com/core/common/net"
|
||||||
"v2ray.com/core/common/protocol"
|
"v2ray.com/core/common/protocol"
|
||||||
|
"v2ray.com/core/common/serial"
|
||||||
"v2ray.com/core/proxy/vmess"
|
"v2ray.com/core/proxy/vmess"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -64,7 +72,10 @@ func (v *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writ
|
|||||||
buffer = append(buffer, Version)
|
buffer = append(buffer, Version)
|
||||||
buffer = append(buffer, v.requestBodyIV...)
|
buffer = append(buffer, v.requestBodyIV...)
|
||||||
buffer = append(buffer, v.requestBodyKey...)
|
buffer = append(buffer, v.requestBodyKey...)
|
||||||
buffer = append(buffer, v.responseHeader, byte(header.Option), byte(0), byte(0), byte(header.Command))
|
buffer = append(buffer, v.responseHeader, byte(header.Option))
|
||||||
|
padingLen := dice.Roll(16)
|
||||||
|
security := byte(padingLen<<4) | byte(header.Security)
|
||||||
|
buffer = append(buffer, security, byte(0), byte(header.Command))
|
||||||
buffer = header.Port.Bytes(buffer)
|
buffer = header.Port.Bytes(buffer)
|
||||||
|
|
||||||
switch header.Address.Family() {
|
switch header.Address.Family() {
|
||||||
@ -79,6 +90,10 @@ func (v *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writ
|
|||||||
buffer = append(buffer, header.Address.Domain()...)
|
buffer = append(buffer, header.Address.Domain()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pading := make([]byte, padingLen)
|
||||||
|
rand.Read(pading)
|
||||||
|
buffer = append(buffer, pading...)
|
||||||
|
|
||||||
fnv1a := fnv.New32a()
|
fnv1a := fnv.New32a()
|
||||||
fnv1a.Write(buffer)
|
fnv1a.Write(buffer)
|
||||||
|
|
||||||
@ -94,9 +109,61 @@ func (v *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writ
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ClientSession) EncodeRequestBody(writer io.Writer) io.Writer {
|
func (v *ClientSession) EncodeRequestBody(request *protocol.RequestHeader, writer io.Writer) v2io.Writer {
|
||||||
aesStream := crypto.NewAesEncryptionStream(v.requestBodyKey, v.requestBodyIV)
|
var authWriter io.Writer
|
||||||
return crypto.NewCryptionWriter(aesStream, writer)
|
if request.Security.Is(protocol.SecurityType_NONE) {
|
||||||
|
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: new(FnvAuthenticator),
|
||||||
|
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authWriter = crypto.NewAuthenticationWriter(auth, writer)
|
||||||
|
} else {
|
||||||
|
authWriter = writer
|
||||||
|
}
|
||||||
|
} else if request.Security.Is(protocol.SecurityType_LEGACY) {
|
||||||
|
aesStream := crypto.NewAesEncryptionStream(v.requestBodyKey, v.requestBodyIV)
|
||||||
|
cryptionWriter := crypto.NewCryptionWriter(aesStream, writer)
|
||||||
|
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: new(FnvAuthenticator),
|
||||||
|
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authWriter = crypto.NewAuthenticationWriter(auth, cryptionWriter)
|
||||||
|
} else {
|
||||||
|
authWriter = cryptionWriter
|
||||||
|
}
|
||||||
|
} else if request.Security.Is(protocol.SecurityType_AES128_GCM) {
|
||||||
|
block, _ := aes.NewCipher(v.responseBodyKey)
|
||||||
|
aead, _ := cipher.NewGCM(block)
|
||||||
|
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: aead,
|
||||||
|
NonceGenerator: &ChunkNonceGenerator{
|
||||||
|
Nonce: append([]byte(nil), v.responseBodyIV...),
|
||||||
|
Size: aead.NonceSize(),
|
||||||
|
},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authWriter = crypto.NewAuthenticationWriter(auth, writer)
|
||||||
|
} else if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) {
|
||||||
|
aead, _ := chacha20poly1305.New(v.responseBodyKey)
|
||||||
|
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: aead,
|
||||||
|
NonceGenerator: &ChunkNonceGenerator{
|
||||||
|
Nonce: append([]byte(nil), v.responseBodyIV...),
|
||||||
|
Size: aead.NonceSize(),
|
||||||
|
},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authWriter = crypto.NewAuthenticationWriter(auth, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v2io.NewAdaptiveWriter(authWriter)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.ResponseHeader, error) {
|
func (v *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.ResponseHeader, error) {
|
||||||
@ -107,7 +174,7 @@ func (v *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.Respon
|
|||||||
|
|
||||||
_, err := io.ReadFull(v.responseReader, buffer[:4])
|
_, err := io.ReadFull(v.responseReader, buffer[:4])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("Raw: Failed to read response header: ", err)
|
log.Info("VMess|Client: Failed to read response header: ", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +191,7 @@ func (v *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.Respon
|
|||||||
dataLen := int(buffer[3])
|
dataLen := int(buffer[3])
|
||||||
_, err := io.ReadFull(v.responseReader, buffer[:dataLen])
|
_, err := io.ReadFull(v.responseReader, buffer[:dataLen])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("Raw: Failed to read response command: ", err)
|
log.Info("VMess|Client: Failed to read response command: ", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
data := buffer[:dataLen]
|
data := buffer[:dataLen]
|
||||||
@ -137,6 +204,69 @@ func (v *ClientSession) DecodeResponseHeader(reader io.Reader) (*protocol.Respon
|
|||||||
return header, nil
|
return header, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ClientSession) DecodeResponseBody(reader io.Reader) io.Reader {
|
func (v *ClientSession) DecodeResponseBody(request *protocol.RequestHeader, reader io.Reader) v2io.Reader {
|
||||||
return v.responseReader
|
aggressive := (request.Command == protocol.RequestCommandTCP)
|
||||||
|
var authReader io.Reader
|
||||||
|
if request.Security.Is(protocol.SecurityType_NONE) {
|
||||||
|
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: new(FnvAuthenticator),
|
||||||
|
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authReader = crypto.NewAuthenticationReader(auth, reader, aggressive)
|
||||||
|
} else {
|
||||||
|
authReader = reader
|
||||||
|
}
|
||||||
|
} else if request.Security.Is(protocol.SecurityType_LEGACY) {
|
||||||
|
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: new(FnvAuthenticator),
|
||||||
|
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authReader = crypto.NewAuthenticationReader(auth, v.responseReader, aggressive)
|
||||||
|
} else {
|
||||||
|
authReader = v.responseReader
|
||||||
|
}
|
||||||
|
} else if request.Security.Is(protocol.SecurityType_AES128_GCM) {
|
||||||
|
block, _ := aes.NewCipher(v.responseBodyKey)
|
||||||
|
aead, _ := cipher.NewGCM(block)
|
||||||
|
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: aead,
|
||||||
|
NonceGenerator: &ChunkNonceGenerator{
|
||||||
|
Nonce: append([]byte(nil), v.responseBodyIV...),
|
||||||
|
Size: aead.NonceSize(),
|
||||||
|
},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authReader = crypto.NewAuthenticationReader(auth, reader, aggressive)
|
||||||
|
} else if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) {
|
||||||
|
aead, _ := chacha20poly1305.New(v.responseBodyKey)
|
||||||
|
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: aead,
|
||||||
|
NonceGenerator: &ChunkNonceGenerator{
|
||||||
|
Nonce: append([]byte(nil), v.responseBodyIV...),
|
||||||
|
Size: aead.NonceSize(),
|
||||||
|
},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authReader = crypto.NewAuthenticationReader(auth, reader, aggressive)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v2io.NewAdaptiveReader(authReader)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChunkNonceGenerator struct {
|
||||||
|
Nonce []byte
|
||||||
|
Size int
|
||||||
|
count uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *ChunkNonceGenerator) Next() []byte {
|
||||||
|
serial.Uint16ToBytes(v.count, v.Nonce[:2])
|
||||||
|
v.count++
|
||||||
|
return v.Nonce[:v.Size]
|
||||||
}
|
}
|
||||||
|
@ -27,12 +27,13 @@ func TestRequestSerialization(t *testing.T) {
|
|||||||
user.Account = loader.NewTypedSettings(account)
|
user.Account = loader.NewTypedSettings(account)
|
||||||
|
|
||||||
expectedRequest := &protocol.RequestHeader{
|
expectedRequest := &protocol.RequestHeader{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
User: user,
|
User: user,
|
||||||
Command: protocol.RequestCommandTCP,
|
Command: protocol.RequestCommandTCP,
|
||||||
Option: protocol.RequestOption(0),
|
Option: protocol.RequestOption(0),
|
||||||
Address: v2net.DomainAddress("www.v2ray.com"),
|
Address: v2net.DomainAddress("www.v2ray.com"),
|
||||||
Port: v2net.Port(443),
|
Port: v2net.Port(443),
|
||||||
|
Security: protocol.Security(protocol.SecurityType_AES128_GCM),
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer := alloc.NewBuffer()
|
buffer := alloc.NewBuffer()
|
||||||
@ -51,4 +52,5 @@ func TestRequestSerialization(t *testing.T) {
|
|||||||
assert.Byte(byte(expectedRequest.Option)).Equals(byte(actualRequest.Option))
|
assert.Byte(byte(expectedRequest.Option)).Equals(byte(actualRequest.Option))
|
||||||
assert.Address(expectedRequest.Address).Equals(actualRequest.Address)
|
assert.Address(expectedRequest.Address).Equals(actualRequest.Address)
|
||||||
assert.Port(expectedRequest.Port).Equals(actualRequest.Port)
|
assert.Port(expectedRequest.Port).Equals(actualRequest.Port)
|
||||||
|
assert.Byte(byte(expectedRequest.Security)).Equals(byte(actualRequest.Security))
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
package encoding
|
package encoding
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
|
|
||||||
"v2ray.com/core/common/crypto"
|
"v2ray.com/core/common/crypto"
|
||||||
"v2ray.com/core/common/errors"
|
"v2ray.com/core/common/errors"
|
||||||
|
v2io "v2ray.com/core/common/io"
|
||||||
"v2ray.com/core/common/log"
|
"v2ray.com/core/common/log"
|
||||||
v2net "v2ray.com/core/common/net"
|
v2net "v2ray.com/core/common/net"
|
||||||
"v2ray.com/core/common/protocol"
|
"v2ray.com/core/common/protocol"
|
||||||
@ -84,7 +90,10 @@ func (v *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
|
|||||||
v.requestBodyIV = append([]byte(nil), buffer[1:17]...) // 16 bytes
|
v.requestBodyIV = append([]byte(nil), buffer[1:17]...) // 16 bytes
|
||||||
v.requestBodyKey = append([]byte(nil), buffer[17:33]...) // 16 bytes
|
v.requestBodyKey = append([]byte(nil), buffer[17:33]...) // 16 bytes
|
||||||
v.responseHeader = buffer[33] // 1 byte
|
v.responseHeader = buffer[33] // 1 byte
|
||||||
request.Option = protocol.RequestOption(buffer[34]) // 1 byte + 2 bytes reserved
|
request.Option = protocol.RequestOption(buffer[34]) // 1 byte
|
||||||
|
padingLen := int(buffer[35] >> 4)
|
||||||
|
request.Security = protocol.Security(buffer[35] & 0x0F)
|
||||||
|
// 1 bytes reserved
|
||||||
request.Command = protocol.RequestCommand(buffer[37])
|
request.Command = protocol.RequestCommand(buffer[37])
|
||||||
|
|
||||||
request.Port = v2net.PortFromBytes(buffer[38:40])
|
request.Port = v2net.PortFromBytes(buffer[38:40])
|
||||||
@ -121,6 +130,14 @@ func (v *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
|
|||||||
request.Address = v2net.DomainAddress(string(buffer[42 : 42+domainLength]))
|
request.Address = v2net.DomainAddress(string(buffer[42 : 42+domainLength]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if padingLen > 0 {
|
||||||
|
_, err = io.ReadFull(decryptor, buffer[bufferLen:bufferLen+padingLen])
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("VMess|Server: Failed to read padding.")
|
||||||
|
}
|
||||||
|
bufferLen += padingLen
|
||||||
|
}
|
||||||
|
|
||||||
_, err = io.ReadFull(decryptor, buffer[bufferLen:bufferLen+4])
|
_, err = io.ReadFull(decryptor, buffer[bufferLen:bufferLen+4])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Base(err).Message("VMess|Server: Failed to read checksum.")
|
return nil, errors.Base(err).Message("VMess|Server: Failed to read checksum.")
|
||||||
@ -138,9 +155,61 @@ func (v *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Request
|
|||||||
return request, nil
|
return request, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ServerSession) DecodeRequestBody(reader io.Reader) io.Reader {
|
func (v *ServerSession) DecodeRequestBody(request *protocol.RequestHeader, reader io.Reader) v2io.Reader {
|
||||||
aesStream := crypto.NewAesDecryptionStream(v.requestBodyKey, v.requestBodyIV)
|
aggressive := (request.Command == protocol.RequestCommandTCP)
|
||||||
return crypto.NewCryptionReader(aesStream, reader)
|
var authReader io.Reader
|
||||||
|
if request.Security.Is(protocol.SecurityType_NONE) {
|
||||||
|
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: new(FnvAuthenticator),
|
||||||
|
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authReader = crypto.NewAuthenticationReader(auth, reader, aggressive)
|
||||||
|
} else {
|
||||||
|
authReader = reader
|
||||||
|
}
|
||||||
|
} else if request.Security.Is(protocol.SecurityType_LEGACY) {
|
||||||
|
aesStream := crypto.NewAesDecryptionStream(v.requestBodyKey, v.requestBodyIV)
|
||||||
|
cryptionReader := crypto.NewCryptionReader(aesStream, reader)
|
||||||
|
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: new(FnvAuthenticator),
|
||||||
|
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authReader = crypto.NewAuthenticationReader(auth, cryptionReader, aggressive)
|
||||||
|
} else {
|
||||||
|
authReader = cryptionReader
|
||||||
|
}
|
||||||
|
} else if request.Security.Is(protocol.SecurityType_AES128_GCM) {
|
||||||
|
block, _ := aes.NewCipher(v.responseBodyKey)
|
||||||
|
aead, _ := cipher.NewGCM(block)
|
||||||
|
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: aead,
|
||||||
|
NonceGenerator: &ChunkNonceGenerator{
|
||||||
|
Nonce: append([]byte(nil), v.responseBodyIV...),
|
||||||
|
Size: aead.NonceSize(),
|
||||||
|
},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authReader = crypto.NewAuthenticationReader(auth, reader, aggressive)
|
||||||
|
} else if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) {
|
||||||
|
aead, _ := chacha20poly1305.New(v.responseBodyKey)
|
||||||
|
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: aead,
|
||||||
|
NonceGenerator: &ChunkNonceGenerator{
|
||||||
|
Nonce: append([]byte(nil), v.responseBodyIV...),
|
||||||
|
Size: aead.NonceSize(),
|
||||||
|
},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authReader = crypto.NewAuthenticationReader(auth, reader, aggressive)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v2io.NewAdaptiveReader(authReader)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, writer io.Writer) {
|
func (v *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, writer io.Writer) {
|
||||||
@ -160,6 +229,56 @@ func (v *ServerSession) EncodeResponseHeader(header *protocol.ResponseHeader, wr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *ServerSession) EncodeResponseBody(writer io.Writer) io.Writer {
|
func (v *ServerSession) EncodeResponseBody(request *protocol.RequestHeader, writer io.Writer) v2io.Writer {
|
||||||
return v.responseWriter
|
var authWriter io.Writer
|
||||||
|
if request.Security.Is(protocol.SecurityType_NONE) {
|
||||||
|
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: new(FnvAuthenticator),
|
||||||
|
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authWriter = crypto.NewAuthenticationWriter(auth, writer)
|
||||||
|
} else {
|
||||||
|
authWriter = writer
|
||||||
|
}
|
||||||
|
} else if request.Security.Is(protocol.SecurityType_LEGACY) {
|
||||||
|
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: new(FnvAuthenticator),
|
||||||
|
NonceGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authWriter = crypto.NewAuthenticationWriter(auth, v.responseWriter)
|
||||||
|
} else {
|
||||||
|
authWriter = v.responseWriter
|
||||||
|
}
|
||||||
|
} else if request.Security.Is(protocol.SecurityType_AES128_GCM) {
|
||||||
|
block, _ := aes.NewCipher(v.responseBodyKey)
|
||||||
|
aead, _ := cipher.NewGCM(block)
|
||||||
|
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: aead,
|
||||||
|
NonceGenerator: &ChunkNonceGenerator{
|
||||||
|
Nonce: append([]byte(nil), v.responseBodyIV...),
|
||||||
|
Size: aead.NonceSize(),
|
||||||
|
},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authWriter = crypto.NewAuthenticationWriter(auth, writer)
|
||||||
|
} else if request.Security.Is(protocol.SecurityType_CHACHA20_POLY1305) {
|
||||||
|
aead, _ := chacha20poly1305.New(v.responseBodyKey)
|
||||||
|
|
||||||
|
auth := &crypto.AEADAuthenticator{
|
||||||
|
AEAD: aead,
|
||||||
|
NonceGenerator: &ChunkNonceGenerator{
|
||||||
|
Nonce: append([]byte(nil), v.responseBodyIV...),
|
||||||
|
Size: aead.NonceSize(),
|
||||||
|
},
|
||||||
|
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
||||||
|
}
|
||||||
|
authWriter = crypto.NewAuthenticationWriter(auth, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v2io.NewAdaptiveWriter(authWriter)
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"v2ray.com/core/app/proxyman"
|
"v2ray.com/core/app/proxyman"
|
||||||
"v2ray.com/core/common"
|
"v2ray.com/core/common"
|
||||||
"v2ray.com/core/common/alloc"
|
"v2ray.com/core/common/alloc"
|
||||||
"v2ray.com/core/common/crypto"
|
|
||||||
"v2ray.com/core/common/errors"
|
"v2ray.com/core/common/errors"
|
||||||
v2io "v2ray.com/core/common/io"
|
v2io "v2ray.com/core/common/io"
|
||||||
"v2ray.com/core/common/loader"
|
"v2ray.com/core/common/loader"
|
||||||
@ -21,7 +20,6 @@ import (
|
|||||||
"v2ray.com/core/proxy/registry"
|
"v2ray.com/core/proxy/registry"
|
||||||
"v2ray.com/core/proxy/vmess"
|
"v2ray.com/core/proxy/vmess"
|
||||||
"v2ray.com/core/proxy/vmess/encoding"
|
"v2ray.com/core/proxy/vmess/encoding"
|
||||||
vmessio "v2ray.com/core/proxy/vmess/io"
|
|
||||||
"v2ray.com/core/transport/internet"
|
"v2ray.com/core/transport/internet"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -187,24 +185,12 @@ func (v *VMessInboundHandler) HandleConnection(connection internet.Connection) {
|
|||||||
reader.SetCached(false)
|
reader.SetCached(false)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
bodyReader := session.DecodeRequestBody(reader)
|
bodyReader := session.DecodeRequestBody(request, reader)
|
||||||
var requestReader v2io.Reader
|
if err := v2io.PipeUntilEOF(bodyReader, input); err != nil {
|
||||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
|
||||||
auth := &crypto.AEADAuthenticator{
|
|
||||||
AEAD: new(encoding.FnvAuthenticator),
|
|
||||||
NonceGenerator: crypto.NoOpBytesGenerator{},
|
|
||||||
AdditionalDataGenerator: crypto.NoOpBytesGenerator{},
|
|
||||||
}
|
|
||||||
authReader := crypto.NewAuthenticationReader(auth, bodyReader, request.Command == protocol.RequestCommandTCP)
|
|
||||||
requestReader = v2io.NewAdaptiveReader(authReader)
|
|
||||||
} else {
|
|
||||||
requestReader = v2io.NewAdaptiveReader(bodyReader)
|
|
||||||
}
|
|
||||||
if err := v2io.PipeUntilEOF(requestReader, input); err != nil {
|
|
||||||
connection.SetReusable(false)
|
connection.SetReusable(false)
|
||||||
}
|
}
|
||||||
|
bodyReader.Release()
|
||||||
|
|
||||||
requestReader.Release()
|
|
||||||
input.Close()
|
input.Close()
|
||||||
readFinish.Unlock()
|
readFinish.Unlock()
|
||||||
}()
|
}()
|
||||||
@ -222,33 +208,29 @@ func (v *VMessInboundHandler) HandleConnection(connection internet.Connection) {
|
|||||||
|
|
||||||
session.EncodeResponseHeader(response, writer)
|
session.EncodeResponseHeader(response, writer)
|
||||||
|
|
||||||
bodyWriter := session.EncodeResponseBody(writer)
|
bodyWriter := session.EncodeResponseBody(request, writer)
|
||||||
var v2writer v2io.Writer = v2io.NewAdaptiveWriter(bodyWriter)
|
|
||||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
|
||||||
v2writer = vmessio.NewAuthChunkWriter(v2writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optimize for small response packet
|
// Optimize for small response packet
|
||||||
if data, err := output.Read(); err == nil {
|
if data, err := output.Read(); err == nil {
|
||||||
if err := v2writer.Write(data); err != nil {
|
if err := bodyWriter.Write(data); err != nil {
|
||||||
connection.SetReusable(false)
|
connection.SetReusable(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.SetCached(false)
|
writer.SetCached(false)
|
||||||
|
|
||||||
if err := v2io.PipeUntilEOF(output, v2writer); err != nil {
|
if err := v2io.PipeUntilEOF(output, bodyWriter); err != nil {
|
||||||
connection.SetReusable(false)
|
connection.SetReusable(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
output.Release()
|
output.Release()
|
||||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||||
if err := v2writer.Write(alloc.NewLocalBuffer(32)); err != nil {
|
if err := bodyWriter.Write(alloc.NewLocalBuffer(32)); err != nil {
|
||||||
connection.SetReusable(false)
|
connection.SetReusable(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writer.Flush()
|
writer.Flush()
|
||||||
v2writer.Release()
|
bodyWriter.Release()
|
||||||
|
|
||||||
readFinish.Lock()
|
readFinish.Lock()
|
||||||
}
|
}
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
package io_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/rand"
|
|
||||||
"io"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"v2ray.com/core/common/alloc"
|
|
||||||
"v2ray.com/core/common/errors"
|
|
||||||
v2io "v2ray.com/core/common/io"
|
|
||||||
"v2ray.com/core/common/serial"
|
|
||||||
. "v2ray.com/core/proxy/vmess/io"
|
|
||||||
"v2ray.com/core/testing/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAuthenticate(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
buffer := alloc.NewBuffer()
|
|
||||||
buffer.AppendBytes(1, 2, 3, 4)
|
|
||||||
Authenticate(buffer)
|
|
||||||
assert.Bytes(buffer.Bytes()).Equals([]byte{0, 8, 87, 52, 168, 125, 1, 2, 3, 4})
|
|
||||||
|
|
||||||
b2, err := NewAuthChunkReader(buffer).Read()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.Bytes(b2.Bytes()).Equals([]byte{1, 2, 3, 4})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSingleIO(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
content := bytes.NewBuffer(make([]byte, 0, 1024*1024))
|
|
||||||
|
|
||||||
writer := NewAuthChunkWriter(v2io.NewAdaptiveWriter(content))
|
|
||||||
b := alloc.NewBuffer()
|
|
||||||
b.AppendFunc(serial.WriteString("abcd"))
|
|
||||||
writer.Write(b)
|
|
||||||
writer.Write(alloc.NewBuffer())
|
|
||||||
writer.Release()
|
|
||||||
|
|
||||||
reader := NewAuthChunkReader(content)
|
|
||||||
buffer, err := reader.Read()
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
assert.String(buffer.String()).Equals("abcd")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLargeIO(t *testing.T) {
|
|
||||||
assert := assert.On(t)
|
|
||||||
|
|
||||||
content := make([]byte, 1024*1024)
|
|
||||||
rand.Read(content)
|
|
||||||
|
|
||||||
chunckContent := bytes.NewBuffer(make([]byte, 0, len(content)*2))
|
|
||||||
writer := NewAuthChunkWriter(v2io.NewAdaptiveWriter(chunckContent))
|
|
||||||
writeSize := 0
|
|
||||||
for {
|
|
||||||
chunkSize := 7 * 1024
|
|
||||||
if chunkSize+writeSize > len(content) {
|
|
||||||
chunkSize = len(content) - writeSize
|
|
||||||
}
|
|
||||||
b := alloc.NewBuffer()
|
|
||||||
b.Append(content[writeSize : writeSize+chunkSize])
|
|
||||||
writer.Write(b)
|
|
||||||
b.Release()
|
|
||||||
|
|
||||||
writeSize += chunkSize
|
|
||||||
if writeSize == len(content) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writer.Write(alloc.NewBuffer())
|
|
||||||
writer.Release()
|
|
||||||
|
|
||||||
actualContent := make([]byte, 0, len(content))
|
|
||||||
reader := NewAuthChunkReader(chunckContent)
|
|
||||||
for {
|
|
||||||
buffer, err := reader.Read()
|
|
||||||
if errors.Cause(err) == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
assert.Error(err).IsNil()
|
|
||||||
actualContent = append(actualContent, buffer.Bytes()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Int(len(actualContent)).Equals(len(content))
|
|
||||||
assert.Bytes(actualContent).Equals(content)
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
package io
|
|
||||||
|
|
||||||
import (
|
|
||||||
"hash"
|
|
||||||
"hash/fnv"
|
|
||||||
"io"
|
|
||||||
"v2ray.com/core/common/alloc"
|
|
||||||
"v2ray.com/core/common/errors"
|
|
||||||
"v2ray.com/core/common/serial"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Private: Visible for testing.
|
|
||||||
type Validator struct {
|
|
||||||
actualAuth hash.Hash32
|
|
||||||
expectedAuth uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewValidator(expectedAuth uint32) *Validator {
|
|
||||||
return &Validator{
|
|
||||||
actualAuth: fnv.New32a(),
|
|
||||||
expectedAuth: expectedAuth,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Validator) Consume(b []byte) {
|
|
||||||
v.actualAuth.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Validator) Validate() bool {
|
|
||||||
return v.actualAuth.Sum32() == v.expectedAuth
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthChunkReader struct {
|
|
||||||
reader io.Reader
|
|
||||||
last *alloc.Buffer
|
|
||||||
chunkLength int
|
|
||||||
validator *Validator
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAuthChunkReader(reader io.Reader) *AuthChunkReader {
|
|
||||||
return &AuthChunkReader{
|
|
||||||
reader: reader,
|
|
||||||
chunkLength: -1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *AuthChunkReader) Read() (*alloc.Buffer, error) {
|
|
||||||
var buffer *alloc.Buffer
|
|
||||||
if v.last != nil {
|
|
||||||
buffer = v.last
|
|
||||||
v.last = nil
|
|
||||||
} else {
|
|
||||||
buffer = alloc.NewBuffer()
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.chunkLength == -1 {
|
|
||||||
for buffer.Len() < 6 {
|
|
||||||
_, err := buffer.FillFrom(v.reader)
|
|
||||||
if err != nil {
|
|
||||||
buffer.Release()
|
|
||||||
return nil, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
length := serial.BytesToUint16(buffer.BytesTo(2))
|
|
||||||
v.chunkLength = int(length) - 4
|
|
||||||
v.validator = NewValidator(serial.BytesToUint32(buffer.BytesRange(2, 6)))
|
|
||||||
buffer.SliceFrom(6)
|
|
||||||
if buffer.Len() < v.chunkLength && v.chunkLength <= 2048 {
|
|
||||||
_, err := buffer.FillFrom(v.reader)
|
|
||||||
if err != nil {
|
|
||||||
buffer.Release()
|
|
||||||
return nil, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if buffer.Len() < v.chunkLength {
|
|
||||||
_, err := buffer.FillFrom(v.reader)
|
|
||||||
if err != nil {
|
|
||||||
buffer.Release()
|
|
||||||
return nil, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.chunkLength == 0 {
|
|
||||||
buffer.Release()
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
if buffer.Len() < v.chunkLength {
|
|
||||||
v.validator.Consume(buffer.Bytes())
|
|
||||||
v.chunkLength -= buffer.Len()
|
|
||||||
} else {
|
|
||||||
v.validator.Consume(buffer.BytesTo(v.chunkLength))
|
|
||||||
if !v.validator.Validate() {
|
|
||||||
buffer.Release()
|
|
||||||
return nil, errors.New("VMess|AuthChunkReader: Invalid auth.")
|
|
||||||
}
|
|
||||||
leftLength := buffer.Len() - v.chunkLength
|
|
||||||
if leftLength > 0 {
|
|
||||||
v.last = alloc.NewBuffer()
|
|
||||||
v.last.Append(buffer.BytesFrom(v.chunkLength))
|
|
||||||
buffer.Slice(0, v.chunkLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
v.chunkLength = -1
|
|
||||||
v.validator = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *AuthChunkReader) Release() {
|
|
||||||
v.reader = nil
|
|
||||||
v.last.Release()
|
|
||||||
v.last = nil
|
|
||||||
v.validator = nil
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package io
|
|
||||||
|
|
||||||
import (
|
|
||||||
"hash/fnv"
|
|
||||||
|
|
||||||
"v2ray.com/core/common/alloc"
|
|
||||||
v2io "v2ray.com/core/common/io"
|
|
||||||
"v2ray.com/core/common/serial"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AuthChunkWriter struct {
|
|
||||||
writer v2io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAuthChunkWriter(writer v2io.Writer) *AuthChunkWriter {
|
|
||||||
return &AuthChunkWriter{
|
|
||||||
writer: writer,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *AuthChunkWriter) Write(buffer *alloc.Buffer) error {
|
|
||||||
Authenticate(buffer)
|
|
||||||
return v.writer.Write(buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *AuthChunkWriter) Release() {
|
|
||||||
v.writer.Release()
|
|
||||||
v.writer = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Authenticate(buffer *alloc.Buffer) {
|
|
||||||
fnvHash := fnv.New32a()
|
|
||||||
fnvHash.Write(buffer.Bytes())
|
|
||||||
buffer.PrependFunc(4, serial.WriteHash(fnvHash))
|
|
||||||
|
|
||||||
buffer.PrependFunc(2, serial.WriteUint16(uint16(buffer.Len())))
|
|
||||||
}
|
|
@ -14,7 +14,6 @@ import (
|
|||||||
"v2ray.com/core/proxy"
|
"v2ray.com/core/proxy"
|
||||||
"v2ray.com/core/proxy/registry"
|
"v2ray.com/core/proxy/registry"
|
||||||
"v2ray.com/core/proxy/vmess/encoding"
|
"v2ray.com/core/proxy/vmess/encoding"
|
||||||
vmessio "v2ray.com/core/proxy/vmess/io"
|
|
||||||
"v2ray.com/core/transport/internet"
|
"v2ray.com/core/transport/internet"
|
||||||
"v2ray.com/core/transport/ray"
|
"v2ray.com/core/transport/ray"
|
||||||
)
|
)
|
||||||
@ -92,13 +91,11 @@ func (v *VMessOutboundHandler) handleRequest(session *encoding.ClientSession, co
|
|||||||
defer writer.Release()
|
defer writer.Release()
|
||||||
session.EncodeRequestHeader(request, writer)
|
session.EncodeRequestHeader(request, writer)
|
||||||
|
|
||||||
bodyWriter := session.EncodeRequestBody(writer)
|
bodyWriter := session.EncodeRequestBody(request, writer)
|
||||||
var streamWriter v2io.Writer = v2io.NewAdaptiveWriter(bodyWriter)
|
defer bodyWriter.Release()
|
||||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
|
||||||
streamWriter = vmessio.NewAuthChunkWriter(streamWriter)
|
|
||||||
}
|
|
||||||
if !payload.IsEmpty() {
|
if !payload.IsEmpty() {
|
||||||
if err := streamWriter.Write(payload); err != nil {
|
if err := bodyWriter.Write(payload); err != nil {
|
||||||
log.Info("VMess|Outbound: Failed to write payload. Disabling connection reuse.", err)
|
log.Info("VMess|Outbound: Failed to write payload. Disabling connection reuse.", err)
|
||||||
conn.SetReusable(false)
|
conn.SetReusable(false)
|
||||||
}
|
}
|
||||||
@ -106,17 +103,16 @@ func (v *VMessOutboundHandler) handleRequest(session *encoding.ClientSession, co
|
|||||||
}
|
}
|
||||||
writer.SetCached(false)
|
writer.SetCached(false)
|
||||||
|
|
||||||
if err := v2io.PipeUntilEOF(input, streamWriter); err != nil {
|
if err := v2io.PipeUntilEOF(input, bodyWriter); err != nil {
|
||||||
conn.SetReusable(false)
|
conn.SetReusable(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
||||||
err := streamWriter.Write(alloc.NewLocalBuffer(32))
|
err := bodyWriter.Write(alloc.NewLocalBuffer(32))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.SetReusable(false)
|
conn.SetReusable(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
streamWriter.Release()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,20 +135,13 @@ func (v *VMessOutboundHandler) handleResponse(session *encoding.ClientSession, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
reader.SetCached(false)
|
reader.SetCached(false)
|
||||||
decryptReader := session.DecodeResponseBody(reader)
|
bodyReader := session.DecodeResponseBody(request, reader)
|
||||||
|
defer bodyReader.Release()
|
||||||
var bodyReader v2io.Reader
|
|
||||||
if request.Option.Has(protocol.RequestOptionChunkStream) {
|
|
||||||
bodyReader = vmessio.NewAuthChunkReader(decryptReader)
|
|
||||||
} else {
|
|
||||||
bodyReader = v2io.NewAdaptiveReader(decryptReader)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v2io.PipeUntilEOF(bodyReader, output); err != nil {
|
if err := v2io.PipeUntilEOF(bodyReader, output); err != nil {
|
||||||
conn.SetReusable(false)
|
conn.SetReusable(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
bodyReader.Release()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user