From f6fae13d888e69ed9257b61226d8caf5411f7f15 Mon Sep 17 00:00:00 2001 From: nadoo <287492+nadoo@users.noreply.github.com> Date: Tue, 3 Jul 2018 14:30:56 +0800 Subject: [PATCH] vmess: support alterID --- proxy/vmess/client.go | 81 +++++++++++++++++++++++++------------------ proxy/vmess/user.go | 39 +++++++++++++++++---- proxy/vmess/vmess.go | 26 ++++++++------ 3 files changed, 95 insertions(+), 51 deletions(-) diff --git a/proxy/vmess/client.go b/proxy/vmess/client.go index c8abcda..8089ebf 100644 --- a/proxy/vmess/client.go +++ b/proxy/vmess/client.go @@ -40,7 +40,14 @@ const ( // Client vmess client type Client struct { - user *User + users []*User + count int +} + +// Conn is a connection to vmess server +type Conn struct { + user *User + atype AType addr Addr port Port @@ -56,15 +63,28 @@ type Client struct { } // NewClient . -func NewClient(uuid, target string) (*Client, error) { - user, err := NewUser(uuid) +func NewClient(uuidStr string, alterID int) (*Client, error) { + uuid, err := StrToUUID(uuidStr) if err != nil { return nil, err } - c := &Client{user: user} + c := &Client{} + user := NewUser(uuid) + c.users = append(c.users, user) + c.users = append(c.users, user.GenAlterIDUsers(alterID)...) + c.count = len(c.users) - c.atype, c.addr, c.port, err = ParseAddr(target) + return c, nil +} + +// NewConn . +func (c *Client) NewConn(rc net.Conn, target string) (*Conn, error) { + r := rand.Intn(c.count) + conn := &Conn{user: c.users[r]} + + var err error + conn.atype, conn.addr, conn.port, err = ParseAddr(target) if err != nil { return nil, err } @@ -72,18 +92,30 @@ func NewClient(uuid, target string) (*Client, error) { randBytes := make([]byte, 33) rand.Read(randBytes) - copy(c.reqBodyIV[:], randBytes[:16]) - copy(c.reqBodyKey[:], randBytes[16:32]) - c.reqRespV = randBytes[32] + copy(conn.reqBodyIV[:], randBytes[:16]) + copy(conn.reqBodyKey[:], randBytes[16:32]) + conn.reqRespV = randBytes[32] - c.respBodyIV = md5.Sum(c.reqBodyIV[:]) - c.respBodyKey = md5.Sum(c.reqBodyKey[:]) + conn.respBodyIV = md5.Sum(conn.reqBodyIV[:]) + conn.respBodyKey = md5.Sum(conn.reqBodyKey[:]) - return c, nil + // AuthInfo + rc.Write(conn.EncodeAuthInfo()) + + // Request + req, err := conn.EncodeRequest() + if err != nil { + return nil, err + } + rc.Write(req) + + conn.Conn = rc + + return conn, nil } // EncodeAuthInfo returns HMAC("md5", UUID, UTC) result -func (c *Client) EncodeAuthInfo() []byte { +func (c *Conn) EncodeAuthInfo() []byte { ts := make([]byte, 8) binary.BigEndian.PutUint64(ts, uint64(time.Now().UTC().Unix())) @@ -94,7 +126,7 @@ func (c *Client) EncodeAuthInfo() []byte { } // EncodeRequest encodes requests to network bytes -func (c *Client) EncodeRequest() ([]byte, error) { +func (c *Conn) EncodeRequest() ([]byte, error) { buf := new(bytes.Buffer) // Request @@ -141,7 +173,7 @@ func (c *Client) EncodeRequest() ([]byte, error) { } // DecodeRespHeader . -func (c *Client) DecodeRespHeader() error { +func (c *Conn) DecodeRespHeader() error { block, err := aes.NewCipher(c.respBodyKey[:]) if err != nil { return err @@ -167,24 +199,7 @@ func (c *Client) DecodeRespHeader() error { } -// NewConn wraps a net.Conn to VMessConn -func (c *Client) NewConn(rc net.Conn) (net.Conn, error) { - // AuthInfo - rc.Write(c.EncodeAuthInfo()) - - // Request - req, err := c.EncodeRequest() - if err != nil { - return nil, err - } - rc.Write(req) - - c.Conn = rc - - return c, err -} - -func (c *Client) Read(b []byte) (n int, err error) { +func (c *Conn) Read(b []byte) (n int, err error) { if !c.connected { c.DecodeRespHeader() } @@ -192,6 +207,6 @@ func (c *Client) Read(b []byte) (n int, err error) { return c.Conn.Read(b) } -func (c *Client) Write(b []byte) (n int, err error) { +func (c *Conn) Write(b []byte) (n int, err error) { return c.Conn.Write(b) } diff --git a/proxy/vmess/user.go b/proxy/vmess/user.go index bd57b9a..e40db4d 100644 --- a/proxy/vmess/user.go +++ b/proxy/vmess/user.go @@ -1,12 +1,15 @@ package vmess import ( + "bytes" "crypto/md5" "encoding/binary" "encoding/hex" "errors" "strings" "time" + + "github.com/nadoo/glider/common/log" ) // User of vmess client @@ -16,16 +19,38 @@ type User struct { } // NewUser . -func NewUser(uuidStr string) (*User, error) { - uuid, err := StrToUUID(uuidStr) - if err != nil { - return nil, err - } - +func NewUser(uuid [16]byte) *User { u := &User{UUID: uuid} copy(u.CmdKey[:], GetKey(uuid)) + return u +} - return u, nil +func nextID(oldID [16]byte) (newID [16]byte) { + md5hash := md5.New() + md5hash.Write(oldID[:]) + md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81")) + for { + md5hash.Sum(newID[:0]) + if !bytes.Equal(oldID[:], newID[:]) { + log.F("oldID: %x, newID: %x", oldID, newID) + return + } + md5hash.Write([]byte("533eff8a-4113-4b10-b5ce-0f5d76b98cd2")) + } +} + +// GenAlterIDUsers generates users according to primary user's id and alterID +func (u *User) GenAlterIDUsers(alterID int) []*User { + users := make([]*User, alterID) + preID := u.UUID + for i := 0; i < alterID; i++ { + newID := nextID(preID) + // NOTE: alterID user is a user which have a different uuid but a same cmdkey with the primary user. + users[i] = &User{UUID: newID, CmdKey: u.CmdKey} + preID = newID + } + + return users } // StrToUUID converts string to uuid. diff --git a/proxy/vmess/vmess.go b/proxy/vmess/vmess.go index 1dc43f6..a3ff203 100644 --- a/proxy/vmess/vmess.go +++ b/proxy/vmess/vmess.go @@ -16,8 +16,10 @@ type VMess struct { addr string uuid string - alertID uint32 + alterID int security string + + client *Client } func init() { @@ -46,13 +48,19 @@ func NewVMess(s string, dialer proxy.Dialer) (*VMess, error) { aid := "0" params, _ := url.ParseQuery(u.RawQuery) - if v, ok := params["alertId"]; ok { + if v, ok := params["alterID"]; ok { aid = v[0] } - alertID, err := strconv.ParseUint(aid, 10, 32) + alterID, err := strconv.ParseUint(aid, 10, 32) if err != nil { - log.F("parse alertID err: %s", err) + log.F("parse alterID err: %s", err) + return nil, err + } + + client, err := NewClient(uuid, int(alterID)) + if err != nil { + log.F("create vmess client err: %s", err) return nil, err } @@ -60,8 +68,9 @@ func NewVMess(s string, dialer proxy.Dialer) (*VMess, error) { dialer: dialer, addr: addr, uuid: uuid, - alertID: uint32(alertID), + alterID: int(alterID), security: security, + client: client, } return p, nil @@ -85,12 +94,7 @@ func (s *VMess) Dial(network, addr string) (net.Conn, error) { return nil, err } - client, err := NewClient(s.uuid, addr) - if err != nil { - return nil, err - } - - return client.NewConn(rc) + return s.client.NewConn(rc, addr) } // DialUDP connects to the given address via the proxy.