glider/proxy/vmess/client.go

321 lines
6.4 KiB
Go
Raw Normal View History

2018-07-03 00:31:43 +08:00
package vmess
import (
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/md5"
"encoding/binary"
"errors"
"hash/fnv"
"io"
"math/rand"
"net"
"runtime"
"strings"
2018-07-03 00:31:43 +08:00
"time"
2018-07-11 00:26:05 +08:00
"golang.org/x/crypto/chacha20poly1305"
2020-10-02 19:09:12 +08:00
"github.com/nadoo/glider/pool"
2018-07-03 00:31:43 +08:00
)
// Request Options
const (
2018-07-11 00:26:05 +08:00
OptBasicFormat byte = 0
OptChunkStream byte = 1
// OptReuseTCPConnection byte = 2
// OptMetadataObfuscate byte = 4
2018-07-03 00:31:43 +08:00
)
// Security types
2018-07-03 00:31:43 +08:00
const (
SecurityAES128GCM byte = 3
SecurityChacha20Poly1305 byte = 4
SecurityNone byte = 5
2018-07-03 00:31:43 +08:00
)
// CmdType is the type of vmess cmd
type CmdType byte
// Cmd Types
2018-07-03 00:31:43 +08:00
const (
CmdTCP CmdType = 1
CmdUDP CmdType = 2
2018-07-03 00:31:43 +08:00
)
// Client is a vmess client.
2018-07-03 00:31:43 +08:00
type Client struct {
users []*User
count int
2018-07-09 23:42:33 +08:00
opt byte
security byte
2018-07-03 14:30:56 +08:00
}
// Conn is a connection to vmess server.
2018-07-03 14:30:56 +08:00
type Conn struct {
user *User
2018-07-09 23:42:33 +08:00
opt byte
security byte
2018-07-03 14:30:56 +08:00
2018-07-07 11:07:38 +08:00
atyp Atyp
addr Addr
port Port
2018-07-03 00:31:43 +08:00
reqBodyIV [16]byte
reqBodyKey [16]byte
reqRespV byte
respBodyIV [16]byte
2018-07-11 00:26:05 +08:00
respBodyKey [16]byte
2018-07-03 00:31:43 +08:00
net.Conn
2018-07-09 23:42:33 +08:00
dataReader io.Reader
dataWriter io.Writer
2018-07-03 00:31:43 +08:00
}
// NewClient returns a new vmess client.
func NewClient(uuidStr, security string, alterID int) (*Client, error) {
2018-07-03 14:30:56 +08:00
uuid, err := StrToUUID(uuidStr)
2018-07-03 00:31:43 +08:00
if err != nil {
return nil, err
}
2018-07-03 14:30:56 +08:00
c := &Client{}
user := NewUser(uuid)
c.users = append(c.users, user)
c.users = append(c.users, user.GenAlterIDUsers(alterID)...)
c.count = len(c.users)
2018-07-10 00:53:15 +08:00
c.opt = OptChunkStream
2018-07-09 23:42:33 +08:00
security = strings.ToLower(security)
switch security {
case "aes-128-gcm":
c.security = SecurityAES128GCM
case "chacha20-poly1305":
c.security = SecurityChacha20Poly1305
2018-07-11 00:26:05 +08:00
case "none":
c.security = SecurityNone
case "":
2021-03-19 15:57:16 +08:00
c.security = SecurityChacha20Poly1305
if runtime.GOARCH == "amd64" || runtime.GOARCH == "s390x" || runtime.GOARCH == "arm64" {
c.security = SecurityAES128GCM
}
2018-07-11 00:26:05 +08:00
default:
return nil, errors.New("unknown security type: " + security)
}
// NOTE: give rand a new seed to avoid the same sequence of values
rand.Seed(time.Now().UnixNano())
2018-07-03 14:30:56 +08:00
return c, nil
}
// NewConn returns a new vmess conn.
func (c *Client) NewConn(rc net.Conn, target string, cmd CmdType) (*Conn, error) {
2018-07-03 14:30:56 +08:00
r := rand.Intn(c.count)
2020-04-19 23:20:15 +08:00
conn := &Conn{user: c.users[r], opt: c.opt, security: c.security, Conn: rc}
2018-07-03 00:31:43 +08:00
2018-07-03 14:30:56 +08:00
var err error
2018-07-07 11:07:38 +08:00
conn.atyp, conn.addr, conn.port, err = ParseAddr(target)
2018-07-03 00:31:43 +08:00
if err != nil {
return nil, err
}
2020-04-19 23:20:15 +08:00
randBytes := pool.GetBuffer(32)
2018-07-03 00:31:43 +08:00
rand.Read(randBytes)
2018-07-03 14:30:56 +08:00
copy(conn.reqBodyIV[:], randBytes[:16])
copy(conn.reqBodyKey[:], randBytes[16:32])
2020-04-19 23:20:15 +08:00
pool.PutBuffer(randBytes)
conn.reqRespV = byte(rand.Intn(1 << 8))
2018-07-03 00:31:43 +08:00
2018-07-03 14:30:56 +08:00
conn.respBodyIV = md5.Sum(conn.reqBodyIV[:])
conn.respBodyKey = md5.Sum(conn.reqBodyKey[:])
2018-07-03 00:31:43 +08:00
2020-04-19 23:20:15 +08:00
// Auth
err = conn.Auth()
2018-07-03 14:30:56 +08:00
if err != nil {
return nil, err
}
2020-04-19 23:20:15 +08:00
// Request
err = conn.Request(cmd)
if err != nil {
return nil, err
}
2018-07-03 14:30:56 +08:00
return conn, nil
2018-07-03 00:31:43 +08:00
}
// Auth send auth info: HMAC("md5", UUID, UTC).
2020-04-19 23:20:15 +08:00
func (c *Conn) Auth() error {
ts := pool.GetBuffer(8)
defer pool.PutBuffer(ts)
2018-07-03 00:31:43 +08:00
binary.BigEndian.PutUint64(ts, uint64(time.Now().UTC().Unix()))
h := hmac.New(md5.New, c.user.UUID[:])
h.Write(ts)
2020-04-19 23:20:15 +08:00
_, err := c.Conn.Write(h.Sum(nil))
return err
2018-07-03 00:31:43 +08:00
}
2020-04-19 23:20:15 +08:00
// Request sends request to server.
func (c *Conn) Request(cmd CmdType) error {
2020-11-03 22:52:50 +08:00
buf := pool.GetBytesBuffer()
defer pool.PutBytesBuffer(buf)
2018-07-03 00:31:43 +08:00
// Request
buf.WriteByte(1) // Ver
buf.Write(c.reqBodyIV[:]) // IV
buf.Write(c.reqBodyKey[:]) // Key
buf.WriteByte(c.reqRespV) // V
2018-07-09 23:42:33 +08:00
buf.WriteByte(c.opt) // Opt
2018-07-03 00:31:43 +08:00
// pLen and Sec
paddingLen := rand.Intn(16)
pSec := byte(paddingLen<<4) | c.security // P(4bit) and Sec(4bit)
2018-07-03 00:31:43 +08:00
buf.WriteByte(pSec)
buf.WriteByte(0) // reserved
buf.WriteByte(byte(cmd)) // cmd
2018-07-03 00:31:43 +08:00
// target
2020-04-19 23:20:15 +08:00
err := binary.Write(buf, binary.BigEndian, uint16(c.port)) // port
if err != nil {
2020-04-19 23:20:15 +08:00
return err
}
buf.WriteByte(byte(c.atyp)) // atyp
buf.Write(c.addr) // addr
2018-07-03 00:31:43 +08:00
// padding
if paddingLen > 0 {
2020-04-19 23:20:15 +08:00
padding := pool.GetBuffer(paddingLen)
2018-07-03 00:31:43 +08:00
rand.Read(padding)
buf.Write(padding)
2020-04-19 23:20:15 +08:00
pool.PutBuffer(padding)
2018-07-03 00:31:43 +08:00
}
// F
fnv1a := fnv.New32a()
_, err = fnv1a.Write(buf.Bytes())
if err != nil {
2020-04-19 23:20:15 +08:00
return err
}
2018-07-03 00:31:43 +08:00
buf.Write(fnv1a.Sum(nil))
2018-07-03 01:07:28 +08:00
block, err := aes.NewCipher(c.user.CmdKey[:])
2018-07-03 00:31:43 +08:00
if err != nil {
2020-04-19 23:20:15 +08:00
return err
2018-07-03 00:31:43 +08:00
}
2018-07-03 01:07:28 +08:00
stream := cipher.NewCFBEncrypter(block, TimestampHash(time.Now().UTC()))
2018-07-03 00:31:43 +08:00
stream.XORKeyStream(buf.Bytes(), buf.Bytes())
2020-04-19 23:20:15 +08:00
_, err = c.Conn.Write(buf.Bytes())
return err
2018-07-03 00:31:43 +08:00
}
// DecodeRespHeader decodes response header.
2018-07-03 14:30:56 +08:00
func (c *Conn) DecodeRespHeader() error {
2018-07-03 00:31:43 +08:00
block, err := aes.NewCipher(c.respBodyKey[:])
if err != nil {
return err
}
stream := cipher.NewCFBDecrypter(block, c.respBodyIV[:])
2018-11-21 20:28:46 +08:00
2020-04-19 23:20:15 +08:00
b := pool.GetBuffer(4)
defer pool.PutBuffer(b)
_, err = io.ReadFull(c.Conn, b)
2018-11-21 20:28:46 +08:00
if err != nil {
return err
}
2018-11-21 20:28:46 +08:00
2020-04-19 23:20:15 +08:00
stream.XORKeyStream(b, b)
2018-07-03 00:31:43 +08:00
2020-04-19 23:20:15 +08:00
if b[0] != c.reqRespV {
2018-07-03 00:31:43 +08:00
return errors.New("unexpected response header")
}
2018-07-05 20:44:19 +08:00
// TODO: Dynamic port support
2020-04-19 23:20:15 +08:00
if b[2] != 0 {
2018-07-05 20:44:19 +08:00
// dataLen := int32(buf[3])
return errors.New("dynamic port is not supported now")
}
2018-07-03 00:31:43 +08:00
return nil
}
2018-07-11 00:26:05 +08:00
func (c *Conn) Write(b []byte) (n int, err error) {
if c.dataWriter != nil {
return c.dataWriter.Write(b)
2018-07-03 00:31:43 +08:00
}
2018-07-11 00:26:05 +08:00
c.dataWriter = c.Conn
if c.opt&OptChunkStream == OptChunkStream {
switch c.security {
case SecurityNone:
c.dataWriter = ChunkedWriter(c.Conn)
case SecurityAES128GCM:
block, _ := aes.NewCipher(c.reqBodyKey[:])
aead, _ := cipher.NewGCM(block)
c.dataWriter = AEADWriter(c.Conn, aead, c.reqBodyIV[:])
case SecurityChacha20Poly1305:
2020-04-19 23:20:15 +08:00
key := pool.GetBuffer(32)
2018-07-11 08:23:26 +08:00
t := md5.Sum(c.reqBodyKey[:])
copy(key, t[:])
t = md5.Sum(key[:16])
copy(key[16:], t[:])
2018-07-11 00:26:05 +08:00
aead, _ := chacha20poly1305.New(key)
c.dataWriter = AEADWriter(c.Conn, aead, c.reqBodyIV[:])
2020-04-19 23:20:15 +08:00
pool.PutBuffer(key)
2018-07-09 23:42:33 +08:00
}
2018-07-11 00:26:05 +08:00
}
2018-07-09 23:42:33 +08:00
2018-07-11 00:26:05 +08:00
return c.dataWriter.Write(b)
}
func (c *Conn) Read(b []byte) (n int, err error) {
if c.dataReader != nil {
2018-07-09 23:42:33 +08:00
return c.dataReader.Read(b)
}
2018-07-11 00:26:05 +08:00
err = c.DecodeRespHeader()
if err != nil {
return 0, err
}
2018-07-03 00:31:43 +08:00
2018-07-11 00:26:05 +08:00
c.dataReader = c.Conn
if c.opt&OptChunkStream == OptChunkStream {
switch c.security {
case SecurityNone:
c.dataReader = ChunkedReader(c.Conn)
case SecurityAES128GCM:
block, _ := aes.NewCipher(c.respBodyKey[:])
aead, _ := cipher.NewGCM(block)
c.dataReader = AEADReader(c.Conn, aead, c.respBodyIV[:])
case SecurityChacha20Poly1305:
2020-04-19 23:20:15 +08:00
key := pool.GetBuffer(32)
2018-07-11 08:23:26 +08:00
t := md5.Sum(c.respBodyKey[:])
copy(key, t[:])
t = md5.Sum(key[:16])
copy(key[16:], t[:])
2018-07-11 00:26:05 +08:00
aead, _ := chacha20poly1305.New(key)
c.dataReader = AEADReader(c.Conn, aead, c.respBodyIV[:])
2020-04-19 23:20:15 +08:00
pool.PutBuffer(key)
2018-07-09 23:42:33 +08:00
}
}
2018-07-11 00:26:05 +08:00
return c.dataReader.Read(b)
2018-07-03 00:31:43 +08:00
}