Feature: add basic support for the VLESS protocol (#2891)

This commit is contained in:
crwnet
2023-08-24 13:24:45 +08:00
committed by GitHub
parent 651a36964e
commit 8a4c46ae77
16 changed files with 893 additions and 25 deletions

View File

@ -34,6 +34,7 @@ type Conn struct {
security byte
option byte
isAead bool
isVless bool
received bool
}
@ -55,6 +56,29 @@ func (vc *Conn) Read(b []byte) (int, error) {
}
func (vc *Conn) sendRequest() error {
if vc.isVless {
buf := protobytes.BytesWriter{}
buf.PutUint8(0) // Protocol Version
buf.PutSlice(vc.id.UUID.Bytes()) // UUID
buf.PutUint8(0) // Addons Length
// buf.PutString("") // Addons Data
// Command
if vc.dst.UDP {
buf.PutUint8(CommandUDP)
} else {
buf.PutUint8(CommandTCP)
}
// Port AddrType Addr
buf.PutUint16be(uint16(vc.dst.Port))
buf.PutUint8(vc.dst.AddrType)
buf.PutSlice(vc.dst.Addr)
_, err := vc.Conn.Write(buf.Bytes())
return err
}
timestamp := time.Now()
mbuf := protobytes.BytesWriter{}
@ -119,6 +143,24 @@ func (vc *Conn) sendRequest() error {
}
func (vc *Conn) recvResponse() error {
if vc.isVless {
var buffer [2]byte
if _, err := io.ReadFull(vc.Conn, buffer[:]); err != nil {
return err
}
if buffer[0] != 0 {
return errors.New("unexpected response version")
}
length := int64(buffer[1])
if length != 0 { // addon data length > 0
io.CopyN(io.Discard, vc.Conn, length) // just discard
}
return nil
}
var buf []byte
if !vc.isAead {
block, err := aes.NewCipher(vc.respBodyKey[:])
@ -194,31 +236,37 @@ func hashTimestamp(t time.Time) []byte {
}
// newConn return a Conn instance
func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool) (*Conn, error) {
randBytes := make([]byte, 33)
rand.Read(randBytes)
reqBodyIV := make([]byte, 16)
reqBodyKey := make([]byte, 16)
copy(reqBodyIV[:], randBytes[:16])
copy(reqBodyKey[:], randBytes[16:32])
respV := randBytes[32]
option := OptionChunkStream
func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool, isVless bool) (*Conn, error) {
var (
reqBodyKey []byte
reqBodyIV []byte
respBodyKey []byte
respBodyIV []byte
respV byte
option byte
)
if isAead {
bodyKey := sha256.Sum256(reqBodyKey)
bodyIV := sha256.Sum256(reqBodyIV)
respBodyKey = bodyKey[:16]
respBodyIV = bodyIV[:16]
} else {
bodyKey := md5.Sum(reqBodyKey)
bodyIV := md5.Sum(reqBodyIV)
respBodyKey = bodyKey[:]
respBodyIV = bodyIV[:]
if !isVless {
randBytes := make([]byte, 33)
rand.Read(randBytes)
reqBodyIV = make([]byte, 16)
reqBodyKey = make([]byte, 16)
copy(reqBodyIV[:], randBytes[:16])
copy(reqBodyKey[:], randBytes[16:32])
respV = randBytes[32]
option = OptionChunkStream
if isAead {
bodyKey := sha256.Sum256(reqBodyKey)
bodyIV := sha256.Sum256(reqBodyIV)
respBodyKey = bodyKey[:16]
respBodyIV = bodyIV[:16]
} else {
bodyKey := md5.Sum(reqBodyKey)
bodyIV := md5.Sum(reqBodyIV)
respBodyKey = bodyKey[:]
respBodyIV = bodyIV[:]
}
}
var writer io.Writer
@ -276,6 +324,7 @@ func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool
security: security,
option: option,
isAead: isAead,
isVless: isVless,
}
if err := c.sendRequest(); err != nil {
return nil, err

View File

@ -56,6 +56,7 @@ type Client struct {
uuid *uuid.UUID
security Security
isAead bool
isVless bool
}
// Config of vmess
@ -66,12 +67,13 @@ type Config struct {
Port string
HostName string
IsAead bool
IsVless bool
}
// StreamConn return a Conn with net.Conn and DstAddr
func (c *Client) StreamConn(conn net.Conn, dst *DstAddr) (net.Conn, error) {
r := rand.Intn(len(c.user))
return newConn(conn, c.user[r], dst, c.security, c.isAead)
return newConn(conn, c.user[r], dst, c.security, c.isAead, c.isVless)
}
// NewClient return Client instance
@ -81,6 +83,11 @@ func NewClient(config Config) (*Client, error) {
return nil, err
}
if config.IsVless {
config.AlterID = 0
config.Security = "zero"
}
var security Security
switch config.Security {
case "aes-128-gcm":
@ -105,5 +112,6 @@ func NewClient(config Config) (*Client, error) {
uuid: &uid,
security: security,
isAead: config.IsAead,
isVless: config.IsVless,
}, nil
}