Feature: add ssr support (#805)
* Refactor ssr stream cipher to expose iv and key References: https://github.com/Dreamacro/go-shadowsocks2 https://github.com/sh4d0wfiend/go-shadowsocksr2 * Implement ssr obfs Reference: https://github.com/mzz2017/shadowsocksR * Implement ssr protocol References: https://github.com/mzz2017/shadowsocksR https://github.com/shadowsocksRb/shadowsocksr-libev https://github.com/shadowsocksr-backup/shadowsocksr
This commit is contained in:
300
component/ssr/protocol/auth_aes128_md5.go
Normal file
300
component/ssr/protocol/auth_aes128_md5.go
Normal file
@ -0,0 +1,300 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
"github.com/Dreamacro/clash/component/ssr/tools"
|
||||
"github.com/Dreamacro/go-shadowsocks2/core"
|
||||
)
|
||||
|
||||
type authAES128 struct {
|
||||
*Base
|
||||
*recvInfo
|
||||
*authData
|
||||
hasSentHeader bool
|
||||
packID uint32
|
||||
userKey []byte
|
||||
uid [4]byte
|
||||
salt string
|
||||
hmac hmacMethod
|
||||
hashDigest hashDigestMethod
|
||||
}
|
||||
|
||||
func init() {
|
||||
register("auth_aes128_md5", newAuthAES128MD5)
|
||||
}
|
||||
|
||||
func newAuthAES128MD5(b *Base) Protocol {
|
||||
return &authAES128{
|
||||
Base: b,
|
||||
authData: &authData{},
|
||||
salt: "auth_aes128_md5",
|
||||
hmac: tools.HmacMD5,
|
||||
hashDigest: tools.MD5Sum,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *authAES128) initForConn(iv []byte) Protocol {
|
||||
return &authAES128{
|
||||
Base: &Base{
|
||||
IV: iv,
|
||||
Key: a.Key,
|
||||
TCPMss: a.TCPMss,
|
||||
Overhead: a.Overhead,
|
||||
Param: a.Param,
|
||||
},
|
||||
recvInfo: &recvInfo{recvID: 1, buffer: new(bytes.Buffer)},
|
||||
authData: a.authData,
|
||||
packID: 1,
|
||||
salt: a.salt,
|
||||
hmac: a.hmac,
|
||||
hashDigest: a.hashDigest,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *authAES128) GetProtocolOverhead() int {
|
||||
return 9
|
||||
}
|
||||
|
||||
func (a *authAES128) SetOverhead(overhead int) {
|
||||
a.Overhead = overhead
|
||||
}
|
||||
|
||||
func (a *authAES128) Decode(b []byte) ([]byte, int, error) {
|
||||
a.buffer.Reset()
|
||||
bSize := len(b)
|
||||
readSize := 0
|
||||
key := pool.Get(len(a.userKey) + 4)
|
||||
defer pool.Put(key)
|
||||
copy(key, a.userKey)
|
||||
for bSize > 4 {
|
||||
binary.LittleEndian.PutUint32(key[len(key)-4:], a.recvID)
|
||||
|
||||
h := a.hmac(key, b[:2])
|
||||
if !bytes.Equal(h[:2], b[2:4]) {
|
||||
return nil, 0, errAuthAES128HMACError
|
||||
}
|
||||
length := int(binary.LittleEndian.Uint16(b[:2]))
|
||||
if length >= 8192 || length < 8 {
|
||||
return nil, 0, errAuthAES128DataLengthError
|
||||
}
|
||||
if length > bSize {
|
||||
break
|
||||
}
|
||||
a.recvID++
|
||||
pos := int(b[4])
|
||||
if pos < 255 {
|
||||
pos += 4
|
||||
} else {
|
||||
pos = int(binary.LittleEndian.Uint16(b[5:7])) + 4
|
||||
}
|
||||
|
||||
a.buffer.Write(b[pos : length-4])
|
||||
b = b[length:]
|
||||
bSize -= length
|
||||
readSize += length
|
||||
}
|
||||
return a.buffer.Bytes(), readSize, nil
|
||||
}
|
||||
|
||||
func (a *authAES128) Encode(b []byte) ([]byte, error) {
|
||||
a.buffer.Reset()
|
||||
bSize := len(b)
|
||||
offset := 0
|
||||
if bSize > 0 && !a.hasSentHeader {
|
||||
authSize := bSize
|
||||
if authSize > 1200 {
|
||||
authSize = 1200
|
||||
}
|
||||
a.hasSentHeader = true
|
||||
a.buffer.Write(a.packAuthData(b[:authSize]))
|
||||
bSize -= authSize
|
||||
offset += authSize
|
||||
}
|
||||
const blockSize = 4096
|
||||
for bSize > blockSize {
|
||||
packSize, randSize := a.packedDataSize(b[offset : offset+blockSize])
|
||||
pack := pool.Get(packSize)
|
||||
a.packData(b[offset:offset+blockSize], pack, randSize)
|
||||
a.buffer.Write(pack)
|
||||
pool.Put(pack)
|
||||
bSize -= blockSize
|
||||
offset += blockSize
|
||||
}
|
||||
if bSize > 0 {
|
||||
packSize, randSize := a.packedDataSize(b[offset:])
|
||||
pack := pool.Get(packSize)
|
||||
a.packData(b[offset:], pack, randSize)
|
||||
a.buffer.Write(pack)
|
||||
pool.Put(pack)
|
||||
}
|
||||
return a.buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (a *authAES128) DecodePacket(b []byte) ([]byte, int, error) {
|
||||
bSize := len(b)
|
||||
h := a.hmac(a.Key, b[:bSize-4])
|
||||
if !bytes.Equal(h[:4], b[bSize-4:]) {
|
||||
return nil, 0, errAuthAES128HMACError
|
||||
}
|
||||
return b[:bSize-4], bSize - 4, nil
|
||||
}
|
||||
|
||||
func (a *authAES128) EncodePacket(b []byte) ([]byte, error) {
|
||||
a.initUserKeyAndID()
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Write(b)
|
||||
buf.Write(a.uid[:])
|
||||
h := a.hmac(a.userKey, buf.Bytes())
|
||||
buf.Write(h[:4])
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (a *authAES128) initUserKeyAndID() {
|
||||
if a.userKey == nil {
|
||||
params := strings.Split(a.Param, ":")
|
||||
if len(params) >= 2 {
|
||||
if userID, err := strconv.ParseUint(params[0], 10, 32); err == nil {
|
||||
binary.LittleEndian.PutUint32(a.uid[:], uint32(userID))
|
||||
a.userKey = a.hashDigest([]byte(params[1]))
|
||||
}
|
||||
}
|
||||
|
||||
if a.userKey == nil {
|
||||
rand.Read(a.uid[:])
|
||||
a.userKey = make([]byte, len(a.Key))
|
||||
copy(a.userKey, a.Key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *authAES128) packedDataSize(data []byte) (packSize, randSize int) {
|
||||
dataSize := len(data)
|
||||
randSize = 1
|
||||
if dataSize <= 1200 {
|
||||
if a.packID > 4 {
|
||||
randSize += rand.Intn(32)
|
||||
} else {
|
||||
if dataSize > 900 {
|
||||
randSize += rand.Intn(128)
|
||||
} else {
|
||||
randSize += rand.Intn(512)
|
||||
}
|
||||
}
|
||||
}
|
||||
packSize = randSize + dataSize + 8
|
||||
return
|
||||
}
|
||||
|
||||
func (a *authAES128) packData(data, ret []byte, randSize int) {
|
||||
dataSize := len(data)
|
||||
retSize := len(ret)
|
||||
// 0~1, ret_size
|
||||
binary.LittleEndian.PutUint16(ret[0:], uint16(retSize&0xFFFF))
|
||||
// 2~3, hmac
|
||||
key := pool.Get(len(a.userKey) + 4)
|
||||
defer pool.Put(key)
|
||||
copy(key, a.userKey)
|
||||
binary.LittleEndian.PutUint32(key[len(key)-4:], a.packID)
|
||||
h := a.hmac(key, ret[:2])
|
||||
copy(ret[2:4], h[:2])
|
||||
// 4~rand_size+4, rand number
|
||||
rand.Read(ret[4 : 4+randSize])
|
||||
// 4, rand_size
|
||||
if randSize < 128 {
|
||||
ret[4] = byte(randSize & 0xFF)
|
||||
} else {
|
||||
// 4, magic number 0xFF
|
||||
ret[4] = 0xFF
|
||||
// 5~6, rand_size
|
||||
binary.LittleEndian.PutUint16(ret[5:], uint16(randSize&0xFFFF))
|
||||
}
|
||||
// rand_size+4~ret_size-4, data
|
||||
if dataSize > 0 {
|
||||
copy(ret[randSize+4:], data)
|
||||
}
|
||||
a.packID++
|
||||
h = a.hmac(key, ret[:retSize-4])
|
||||
copy(ret[retSize-4:], h[:4])
|
||||
}
|
||||
|
||||
func (a *authAES128) packAuthData(data []byte) (ret []byte) {
|
||||
dataSize := len(data)
|
||||
var randSize int
|
||||
|
||||
if dataSize > 400 {
|
||||
randSize = rand.Intn(512)
|
||||
} else {
|
||||
randSize = rand.Intn(1024)
|
||||
}
|
||||
|
||||
dataOffset := randSize + 16 + 4 + 4 + 7
|
||||
retSize := dataOffset + dataSize + 4
|
||||
ret = make([]byte, retSize)
|
||||
encrypt := make([]byte, 24)
|
||||
key := make([]byte, len(a.IV)+len(a.Key))
|
||||
copy(key, a.IV)
|
||||
copy(key[len(a.IV):], a.Key)
|
||||
|
||||
rand.Read(ret[dataOffset-randSize:])
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
a.connectionID++
|
||||
if a.connectionID > 0xFF000000 {
|
||||
a.clientID = nil
|
||||
}
|
||||
if len(a.clientID) == 0 {
|
||||
a.clientID = make([]byte, 8)
|
||||
rand.Read(a.clientID)
|
||||
b := make([]byte, 4)
|
||||
rand.Read(b)
|
||||
a.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF
|
||||
}
|
||||
copy(encrypt[4:], a.clientID)
|
||||
binary.LittleEndian.PutUint32(encrypt[8:], a.connectionID)
|
||||
|
||||
now := time.Now().Unix()
|
||||
binary.LittleEndian.PutUint32(encrypt[:4], uint32(now))
|
||||
|
||||
binary.LittleEndian.PutUint16(encrypt[12:], uint16(retSize&0xFFFF))
|
||||
binary.LittleEndian.PutUint16(encrypt[14:], uint16(randSize&0xFFFF))
|
||||
|
||||
a.initUserKeyAndID()
|
||||
|
||||
aesCipherKey := core.Kdf(base64.StdEncoding.EncodeToString(a.userKey)+a.salt, 16)
|
||||
block, err := aes.NewCipher(aesCipherKey)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
encryptData := make([]byte, 16)
|
||||
iv := make([]byte, aes.BlockSize)
|
||||
cbc := cipher.NewCBCEncrypter(block, iv)
|
||||
cbc.CryptBlocks(encryptData, encrypt[:16])
|
||||
copy(encrypt[:4], a.uid[:])
|
||||
copy(encrypt[4:4+16], encryptData)
|
||||
|
||||
h := a.hmac(key, encrypt[:20])
|
||||
copy(encrypt[20:], h[:4])
|
||||
|
||||
rand.Read(ret[:1])
|
||||
h = a.hmac(key, ret[:1])
|
||||
copy(ret[1:], h[:7-1])
|
||||
|
||||
copy(ret[7:], encrypt)
|
||||
copy(ret[dataOffset:], data)
|
||||
|
||||
h = a.hmac(a.userKey, ret[:retSize-4])
|
||||
copy(ret[retSize-4:], h[:4])
|
||||
|
||||
return
|
||||
}
|
22
component/ssr/protocol/auth_aes128_sha1.go
Normal file
22
component/ssr/protocol/auth_aes128_sha1.go
Normal file
@ -0,0 +1,22 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/Dreamacro/clash/component/ssr/tools"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("auth_aes128_sha1", newAuthAES128SHA1)
|
||||
}
|
||||
|
||||
func newAuthAES128SHA1(b *Base) Protocol {
|
||||
return &authAES128{
|
||||
Base: b,
|
||||
recvInfo: &recvInfo{buffer: new(bytes.Buffer)},
|
||||
authData: &authData{},
|
||||
salt: "auth_aes128_sha1",
|
||||
hmac: tools.HmacSHA1,
|
||||
hashDigest: tools.SHA1Sum,
|
||||
}
|
||||
}
|
428
component/ssr/protocol/auth_chain_a.go
Normal file
428
component/ssr/protocol/auth_chain_a.go
Normal file
@ -0,0 +1,428 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rc4"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
"github.com/Dreamacro/clash/component/ssr/tools"
|
||||
"github.com/Dreamacro/go-shadowsocks2/core"
|
||||
)
|
||||
|
||||
type authChain struct {
|
||||
*Base
|
||||
*recvInfo
|
||||
*authData
|
||||
randomClient shift128PlusContext
|
||||
randomServer shift128PlusContext
|
||||
enc cipher.Stream
|
||||
dec cipher.Stream
|
||||
headerSent bool
|
||||
lastClientHash []byte
|
||||
lastServerHash []byte
|
||||
userKey []byte
|
||||
uid [4]byte
|
||||
salt string
|
||||
hmac hmacMethod
|
||||
hashDigest hashDigestMethod
|
||||
rnd rndMethod
|
||||
dataSizeList []int
|
||||
dataSizeList2 []int
|
||||
chunkID uint32
|
||||
}
|
||||
|
||||
func init() {
|
||||
register("auth_chain_a", newAuthChainA)
|
||||
}
|
||||
|
||||
func newAuthChainA(b *Base) Protocol {
|
||||
return &authChain{
|
||||
Base: b,
|
||||
authData: &authData{},
|
||||
salt: "auth_chain_a",
|
||||
hmac: tools.HmacMD5,
|
||||
hashDigest: tools.SHA1Sum,
|
||||
rnd: authChainAGetRandLen,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *authChain) initForConn(iv []byte) Protocol {
|
||||
r := &authChain{
|
||||
Base: &Base{
|
||||
IV: iv,
|
||||
Key: a.Key,
|
||||
TCPMss: a.TCPMss,
|
||||
Overhead: a.Overhead,
|
||||
Param: a.Param,
|
||||
},
|
||||
recvInfo: &recvInfo{recvID: 1, buffer: new(bytes.Buffer)},
|
||||
authData: a.authData,
|
||||
salt: a.salt,
|
||||
hmac: a.hmac,
|
||||
hashDigest: a.hashDigest,
|
||||
rnd: a.rnd,
|
||||
}
|
||||
if r.salt == "auth_chain_b" {
|
||||
initDataSize(r)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (a *authChain) GetProtocolOverhead() int {
|
||||
return 4
|
||||
}
|
||||
|
||||
func (a *authChain) SetOverhead(overhead int) {
|
||||
a.Overhead = overhead
|
||||
}
|
||||
|
||||
func (a *authChain) Decode(b []byte) ([]byte, int, error) {
|
||||
a.buffer.Reset()
|
||||
key := pool.Get(len(a.userKey) + 4)
|
||||
defer pool.Put(key)
|
||||
readSize := 0
|
||||
copy(key, a.userKey)
|
||||
for len(b) > 4 {
|
||||
binary.LittleEndian.PutUint32(key[len(a.userKey):], a.recvID)
|
||||
dataLen := (int)((uint(b[1]^a.lastServerHash[15]) << 8) + uint(b[0]^a.lastServerHash[14]))
|
||||
randLen := a.getServerRandLen(dataLen, a.Overhead)
|
||||
length := randLen + dataLen
|
||||
if length >= 4096 {
|
||||
return nil, 0, errAuthChainDataLengthError
|
||||
}
|
||||
length += 4
|
||||
if length > len(b) {
|
||||
break
|
||||
}
|
||||
|
||||
hash := a.hmac(key, b[:length-2])
|
||||
if !bytes.Equal(hash[:2], b[length-2:length]) {
|
||||
return nil, 0, errAuthChainHMACError
|
||||
}
|
||||
var dataPos int
|
||||
if dataLen > 0 && randLen > 0 {
|
||||
dataPos = 2 + getRandStartPos(&a.randomServer, randLen)
|
||||
} else {
|
||||
dataPos = 2
|
||||
}
|
||||
d := pool.Get(dataLen)
|
||||
a.dec.XORKeyStream(d, b[dataPos:dataPos+dataLen])
|
||||
a.buffer.Write(d)
|
||||
pool.Put(d)
|
||||
if a.recvID == 1 {
|
||||
a.TCPMss = int(binary.LittleEndian.Uint16(a.buffer.Next(2)))
|
||||
}
|
||||
a.lastServerHash = hash
|
||||
a.recvID++
|
||||
b = b[length:]
|
||||
readSize += length
|
||||
}
|
||||
return a.buffer.Bytes(), readSize, nil
|
||||
}
|
||||
|
||||
func (a *authChain) Encode(b []byte) ([]byte, error) {
|
||||
a.buffer.Reset()
|
||||
bSize := len(b)
|
||||
offset := 0
|
||||
if bSize > 0 && !a.headerSent {
|
||||
headSize := 1200
|
||||
if headSize > bSize {
|
||||
headSize = bSize
|
||||
}
|
||||
a.buffer.Write(a.packAuthData(b[:headSize]))
|
||||
offset += headSize
|
||||
bSize -= headSize
|
||||
a.headerSent = true
|
||||
}
|
||||
var unitSize = a.TCPMss - a.Overhead
|
||||
for bSize > unitSize {
|
||||
dataLen, randLength := a.packedDataLen(b[offset : offset+unitSize])
|
||||
d := pool.Get(dataLen)
|
||||
a.packData(d, b[offset:offset+unitSize], randLength)
|
||||
a.buffer.Write(d)
|
||||
pool.Put(d)
|
||||
bSize -= unitSize
|
||||
offset += unitSize
|
||||
}
|
||||
if bSize > 0 {
|
||||
dataLen, randLength := a.packedDataLen(b[offset:])
|
||||
d := pool.Get(dataLen)
|
||||
a.packData(d, b[offset:], randLength)
|
||||
a.buffer.Write(d)
|
||||
pool.Put(d)
|
||||
}
|
||||
return a.buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (a *authChain) DecodePacket(b []byte) ([]byte, int, error) {
|
||||
bSize := len(b)
|
||||
if bSize < 9 {
|
||||
return nil, 0, errAuthChainDataLengthError
|
||||
}
|
||||
h := a.hmac(a.userKey, b[:bSize-1])
|
||||
if h[0] != b[bSize-1] {
|
||||
return nil, 0, errAuthChainHMACError
|
||||
}
|
||||
hash := a.hmac(a.Key, b[bSize-8:bSize-1])
|
||||
cipherKey := a.getRC4CipherKey(hash)
|
||||
dec, _ := rc4.NewCipher(cipherKey)
|
||||
randLength := udpGetRandLen(&a.randomServer, hash)
|
||||
bSize -= 8 + randLength
|
||||
dec.XORKeyStream(b, b[:bSize])
|
||||
return b, bSize, nil
|
||||
}
|
||||
|
||||
func (a *authChain) EncodePacket(b []byte) ([]byte, error) {
|
||||
a.initUserKeyAndID()
|
||||
authData := pool.Get(3)
|
||||
defer pool.Put(authData)
|
||||
rand.Read(authData)
|
||||
hash := a.hmac(a.Key, authData)
|
||||
uid := pool.Get(4)
|
||||
defer pool.Put(uid)
|
||||
for i := 0; i < 4; i++ {
|
||||
uid[i] = a.uid[i] ^ hash[i]
|
||||
}
|
||||
|
||||
cipherKey := a.getRC4CipherKey(hash)
|
||||
enc, _ := rc4.NewCipher(cipherKey)
|
||||
var buf bytes.Buffer
|
||||
enc.XORKeyStream(b, b)
|
||||
buf.Write(b)
|
||||
|
||||
randLength := udpGetRandLen(&a.randomClient, hash)
|
||||
randBytes := pool.Get(randLength)
|
||||
defer pool.Put(randBytes)
|
||||
buf.Write(randBytes)
|
||||
|
||||
buf.Write(authData)
|
||||
buf.Write(uid)
|
||||
|
||||
h := a.hmac(a.userKey, buf.Bytes())
|
||||
buf.Write(h[:1])
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (a *authChain) getRC4CipherKey(hash []byte) []byte {
|
||||
base64UserKey := base64.StdEncoding.EncodeToString(a.userKey)
|
||||
return a.calcRC4CipherKey(hash, base64UserKey)
|
||||
}
|
||||
|
||||
func (a *authChain) calcRC4CipherKey(hash []byte, base64UserKey string) []byte {
|
||||
password := pool.Get(len(base64UserKey) + base64.StdEncoding.EncodedLen(16))
|
||||
defer pool.Put(password)
|
||||
copy(password, base64UserKey)
|
||||
base64.StdEncoding.Encode(password[len(base64UserKey):], hash[:16])
|
||||
return core.Kdf(string(password), 16)
|
||||
}
|
||||
|
||||
func (a *authChain) initUserKeyAndID() {
|
||||
if a.userKey == nil {
|
||||
params := strings.Split(a.Param, ":")
|
||||
if len(params) >= 2 {
|
||||
if userID, err := strconv.ParseUint(params[0], 10, 32); err == nil {
|
||||
binary.LittleEndian.PutUint32(a.uid[:], uint32(userID))
|
||||
a.userKey = []byte(params[1])[:len(a.userKey)]
|
||||
}
|
||||
}
|
||||
|
||||
if a.userKey == nil {
|
||||
rand.Read(a.uid[:])
|
||||
a.userKey = make([]byte, len(a.Key))
|
||||
copy(a.userKey, a.Key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *authChain) getClientRandLen(dataLength int, overhead int) int {
|
||||
return a.rnd(dataLength, &a.randomClient, a.lastClientHash, a.dataSizeList, a.dataSizeList2, overhead)
|
||||
}
|
||||
|
||||
func (a *authChain) getServerRandLen(dataLength int, overhead int) int {
|
||||
return a.rnd(dataLength, &a.randomServer, a.lastServerHash, a.dataSizeList, a.dataSizeList2, overhead)
|
||||
}
|
||||
|
||||
func (a *authChain) packedDataLen(data []byte) (chunkLength, randLength int) {
|
||||
dataLength := len(data)
|
||||
randLength = a.getClientRandLen(dataLength, a.Overhead)
|
||||
chunkLength = randLength + dataLength + 2 + 2
|
||||
return
|
||||
}
|
||||
|
||||
func (a *authChain) packData(outData []byte, data []byte, randLength int) {
|
||||
dataLength := len(data)
|
||||
outLength := randLength + dataLength + 2
|
||||
outData[0] = byte(dataLength) ^ a.lastClientHash[14]
|
||||
outData[1] = byte(dataLength>>8) ^ a.lastClientHash[15]
|
||||
|
||||
{
|
||||
if dataLength > 0 {
|
||||
randPart1Length := getRandStartPos(&a.randomClient, randLength)
|
||||
rand.Read(outData[2 : 2+randPart1Length])
|
||||
a.enc.XORKeyStream(outData[2+randPart1Length:], data)
|
||||
rand.Read(outData[2+randPart1Length+dataLength : outLength])
|
||||
} else {
|
||||
rand.Read(outData[2 : 2+randLength])
|
||||
}
|
||||
}
|
||||
|
||||
userKeyLen := uint8(len(a.userKey))
|
||||
key := pool.Get(int(userKeyLen + 4))
|
||||
defer pool.Put(key)
|
||||
copy(key, a.userKey)
|
||||
a.chunkID++
|
||||
binary.LittleEndian.PutUint32(key[userKeyLen:], a.chunkID)
|
||||
a.lastClientHash = a.hmac(key, outData[:outLength])
|
||||
copy(outData[outLength:], a.lastClientHash[:2])
|
||||
return
|
||||
}
|
||||
|
||||
const authHeadLength = 4 + 8 + 4 + 16 + 4
|
||||
|
||||
func (a *authChain) packAuthData(data []byte) (outData []byte) {
|
||||
outData = make([]byte, authHeadLength, authHeadLength+1500)
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
a.connectionID++
|
||||
if a.connectionID > 0xFF000000 {
|
||||
rand.Read(a.clientID)
|
||||
b := make([]byte, 4)
|
||||
rand.Read(b)
|
||||
a.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF
|
||||
}
|
||||
var key = make([]byte, len(a.IV)+len(a.Key))
|
||||
copy(key, a.IV)
|
||||
copy(key[len(a.IV):], a.Key)
|
||||
|
||||
encrypt := make([]byte, 20)
|
||||
t := time.Now().Unix()
|
||||
binary.LittleEndian.PutUint32(encrypt[:4], uint32(t))
|
||||
copy(encrypt[4:8], a.clientID)
|
||||
binary.LittleEndian.PutUint32(encrypt[8:], a.connectionID)
|
||||
binary.LittleEndian.PutUint16(encrypt[12:], uint16(a.Overhead))
|
||||
binary.LittleEndian.PutUint16(encrypt[14:], 0)
|
||||
|
||||
// first 12 bytes
|
||||
{
|
||||
rand.Read(outData[:4])
|
||||
a.lastClientHash = a.hmac(key, outData[:4])
|
||||
copy(outData[4:], a.lastClientHash[:8])
|
||||
}
|
||||
var base64UserKey string
|
||||
// uid & 16 bytes auth data
|
||||
{
|
||||
a.initUserKeyAndID()
|
||||
uid := make([]byte, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
uid[i] = a.uid[i] ^ a.lastClientHash[8+i]
|
||||
}
|
||||
base64UserKey = base64.StdEncoding.EncodeToString(a.userKey)
|
||||
aesCipherKey := core.Kdf(base64UserKey+a.salt, 16)
|
||||
block, err := aes.NewCipher(aesCipherKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
encryptData := make([]byte, 16)
|
||||
iv := make([]byte, aes.BlockSize)
|
||||
cbc := cipher.NewCBCEncrypter(block, iv)
|
||||
cbc.CryptBlocks(encryptData, encrypt[:16])
|
||||
copy(encrypt[:4], uid[:])
|
||||
copy(encrypt[4:4+16], encryptData)
|
||||
}
|
||||
// final HMAC
|
||||
{
|
||||
a.lastServerHash = a.hmac(a.userKey, encrypt[:20])
|
||||
|
||||
copy(outData[12:], encrypt)
|
||||
copy(outData[12+20:], a.lastServerHash[:4])
|
||||
}
|
||||
|
||||
// init cipher
|
||||
cipherKey := a.calcRC4CipherKey(a.lastClientHash, base64UserKey)
|
||||
a.enc, _ = rc4.NewCipher(cipherKey)
|
||||
a.dec, _ = rc4.NewCipher(cipherKey)
|
||||
|
||||
// data
|
||||
chunkLength, randLength := a.packedDataLen(data)
|
||||
if chunkLength <= 1500 {
|
||||
outData = outData[:authHeadLength+chunkLength]
|
||||
} else {
|
||||
newOutData := make([]byte, authHeadLength+chunkLength)
|
||||
copy(newOutData, outData[:authHeadLength])
|
||||
outData = newOutData
|
||||
}
|
||||
a.packData(outData[authHeadLength:], data, randLength)
|
||||
return
|
||||
}
|
||||
|
||||
func getRandStartPos(random *shift128PlusContext, randLength int) int {
|
||||
if randLength > 0 {
|
||||
return int(random.Next() % 8589934609 % uint64(randLength))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func authChainAGetRandLen(dataLength int, random *shift128PlusContext, lastHash []byte, dataSizeList, dataSizeList2 []int, overhead int) int {
|
||||
if dataLength > 1440 {
|
||||
return 0
|
||||
}
|
||||
random.InitFromBinDatalen(lastHash[:16], dataLength)
|
||||
if dataLength > 1300 {
|
||||
return int(random.Next() % 31)
|
||||
}
|
||||
if dataLength > 900 {
|
||||
return int(random.Next() % 127)
|
||||
}
|
||||
if dataLength > 400 {
|
||||
return int(random.Next() % 521)
|
||||
}
|
||||
return int(random.Next() % 1021)
|
||||
}
|
||||
|
||||
func udpGetRandLen(random *shift128PlusContext, lastHash []byte) int {
|
||||
random.InitFromBin(lastHash[:16])
|
||||
return int(random.Next() % 127)
|
||||
}
|
||||
|
||||
type shift128PlusContext struct {
|
||||
v [2]uint64
|
||||
}
|
||||
|
||||
func (ctx *shift128PlusContext) InitFromBin(bin []byte) {
|
||||
var fillBin [16]byte
|
||||
copy(fillBin[:], bin)
|
||||
|
||||
ctx.v[0] = binary.LittleEndian.Uint64(fillBin[:8])
|
||||
ctx.v[1] = binary.LittleEndian.Uint64(fillBin[8:])
|
||||
}
|
||||
|
||||
func (ctx *shift128PlusContext) InitFromBinDatalen(bin []byte, datalen int) {
|
||||
var fillBin [16]byte
|
||||
copy(fillBin[:], bin)
|
||||
binary.LittleEndian.PutUint16(fillBin[:2], uint16(datalen))
|
||||
|
||||
ctx.v[0] = binary.LittleEndian.Uint64(fillBin[:8])
|
||||
ctx.v[1] = binary.LittleEndian.Uint64(fillBin[8:])
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx *shift128PlusContext) Next() uint64 {
|
||||
x := ctx.v[0]
|
||||
y := ctx.v[1]
|
||||
ctx.v[0] = y
|
||||
x ^= x << 23
|
||||
x ^= y ^ (x >> 17) ^ (y >> 26)
|
||||
ctx.v[1] = x
|
||||
return x + y
|
||||
}
|
72
component/ssr/protocol/auth_chain_b.go
Normal file
72
component/ssr/protocol/auth_chain_b.go
Normal file
@ -0,0 +1,72 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/Dreamacro/clash/component/ssr/tools"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("auth_chain_b", newAuthChainB)
|
||||
}
|
||||
|
||||
func newAuthChainB(b *Base) Protocol {
|
||||
return &authChain{
|
||||
Base: b,
|
||||
authData: &authData{},
|
||||
salt: "auth_chain_b",
|
||||
hmac: tools.HmacMD5,
|
||||
hashDigest: tools.SHA1Sum,
|
||||
rnd: authChainBGetRandLen,
|
||||
}
|
||||
}
|
||||
|
||||
func initDataSize(r *authChain) {
|
||||
random := &r.randomServer
|
||||
random.InitFromBin(r.Key)
|
||||
len := random.Next()%8 + 4
|
||||
r.dataSizeList = make([]int, len)
|
||||
for i := 0; i < int(len); i++ {
|
||||
r.dataSizeList[i] = int(random.Next() % 2340 % 2040 % 1440)
|
||||
}
|
||||
sort.Ints(r.dataSizeList)
|
||||
|
||||
len = random.Next()%16 + 8
|
||||
r.dataSizeList2 = make([]int, len)
|
||||
for i := 0; i < int(len); i++ {
|
||||
r.dataSizeList2[i] = int(random.Next() % 2340 % 2040 % 1440)
|
||||
}
|
||||
sort.Ints(r.dataSizeList2)
|
||||
}
|
||||
|
||||
func authChainBGetRandLen(dataLength int, random *shift128PlusContext, lastHash []byte, dataSizeList, dataSizeList2 []int, overhead int) int {
|
||||
if dataLength > 1440 {
|
||||
return 0
|
||||
}
|
||||
random.InitFromBinDatalen(lastHash[:16], dataLength)
|
||||
pos := sort.Search(len(dataSizeList), func(i int) bool { return dataSizeList[i] > dataLength+overhead })
|
||||
finalPos := uint64(pos) + random.Next()%uint64(len(dataSizeList))
|
||||
if finalPos < uint64(len(dataSizeList)) {
|
||||
return dataSizeList[finalPos] - dataLength - overhead
|
||||
}
|
||||
|
||||
pos = sort.Search(len(dataSizeList2), func(i int) bool { return dataSizeList2[i] > dataLength+overhead })
|
||||
finalPos = uint64(pos) + random.Next()%uint64(len(dataSizeList2))
|
||||
if finalPos < uint64(len(dataSizeList2)) {
|
||||
return dataSizeList2[finalPos] - dataLength - overhead
|
||||
}
|
||||
if finalPos < uint64(pos+len(dataSizeList2)-1) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if dataLength > 1300 {
|
||||
return int(random.Next() % 31)
|
||||
}
|
||||
if dataLength > 900 {
|
||||
return int(random.Next() % 127)
|
||||
}
|
||||
if dataLength > 400 {
|
||||
return int(random.Next() % 521)
|
||||
}
|
||||
return int(random.Next() % 1021)
|
||||
}
|
253
component/ssr/protocol/auth_sha1_v4.go
Normal file
253
component/ssr/protocol/auth_sha1_v4.go
Normal file
@ -0,0 +1,253 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"hash/adler32"
|
||||
"hash/crc32"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
"github.com/Dreamacro/clash/component/ssr/tools"
|
||||
)
|
||||
|
||||
type authSHA1V4 struct {
|
||||
*Base
|
||||
*authData
|
||||
headerSent bool
|
||||
buffer bytes.Buffer
|
||||
}
|
||||
|
||||
func init() {
|
||||
register("auth_sha1_v4", newAuthSHA1V4)
|
||||
}
|
||||
|
||||
func newAuthSHA1V4(b *Base) Protocol {
|
||||
return &authSHA1V4{Base: b, authData: &authData{}}
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) initForConn(iv []byte) Protocol {
|
||||
return &authSHA1V4{
|
||||
Base: &Base{
|
||||
IV: iv,
|
||||
Key: a.Key,
|
||||
TCPMss: a.TCPMss,
|
||||
Overhead: a.Overhead,
|
||||
Param: a.Param,
|
||||
},
|
||||
authData: a.authData,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) GetProtocolOverhead() int {
|
||||
return 7
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) SetOverhead(overhead int) {
|
||||
a.Overhead = overhead
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) Decode(b []byte) ([]byte, int, error) {
|
||||
a.buffer.Reset()
|
||||
bSize := len(b)
|
||||
originalSize := bSize
|
||||
for bSize > 4 {
|
||||
crc := crc32.ChecksumIEEE(b[:2]) & 0xFFFF
|
||||
if binary.LittleEndian.Uint16(b[2:4]) != uint16(crc) {
|
||||
return nil, 0, errAuthSHA1v4CRC32Error
|
||||
}
|
||||
length := int(binary.BigEndian.Uint16(b[:2]))
|
||||
if length >= 8192 || length < 8 {
|
||||
return nil, 0, errAuthSHA1v4DataLengthError
|
||||
}
|
||||
if length > bSize {
|
||||
break
|
||||
}
|
||||
|
||||
if adler32.Checksum(b[:length-4]) == binary.LittleEndian.Uint32(b[length-4:]) {
|
||||
pos := int(b[4])
|
||||
if pos != 0xFF {
|
||||
pos += 4
|
||||
} else {
|
||||
pos = int(binary.BigEndian.Uint16(b[5:5+2])) + 4
|
||||
}
|
||||
retSize := length - pos - 4
|
||||
a.buffer.Write(b[pos : pos+retSize])
|
||||
bSize -= length
|
||||
b = b[length:]
|
||||
} else {
|
||||
return nil, 0, errAuthSHA1v4IncorrectChecksum
|
||||
}
|
||||
}
|
||||
return a.buffer.Bytes(), originalSize - bSize, nil
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) Encode(b []byte) ([]byte, error) {
|
||||
a.buffer.Reset()
|
||||
bSize := len(b)
|
||||
offset := 0
|
||||
if !a.headerSent && bSize > 0 {
|
||||
headSize := getHeadSize(b, 30)
|
||||
if headSize > bSize {
|
||||
headSize = bSize
|
||||
}
|
||||
a.buffer.Write(a.packAuthData(b[:headSize]))
|
||||
offset += headSize
|
||||
bSize -= headSize
|
||||
a.headerSent = true
|
||||
}
|
||||
const blockSize = 4096
|
||||
for bSize > blockSize {
|
||||
packSize, randSize := a.packedDataSize(b[offset : offset+blockSize])
|
||||
pack := pool.Get(packSize)
|
||||
a.packData(b[offset:offset+blockSize], pack, randSize)
|
||||
a.buffer.Write(pack)
|
||||
pool.Put(pack)
|
||||
offset += blockSize
|
||||
bSize -= blockSize
|
||||
}
|
||||
if bSize > 0 {
|
||||
packSize, randSize := a.packedDataSize(b[offset:])
|
||||
pack := pool.Get(packSize)
|
||||
a.packData(b[offset:], pack, randSize)
|
||||
a.buffer.Write(pack)
|
||||
pool.Put(pack)
|
||||
}
|
||||
return a.buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) DecodePacket(b []byte) ([]byte, int, error) {
|
||||
return b, len(b), nil
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) EncodePacket(b []byte) ([]byte, error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) packedDataSize(data []byte) (packSize, randSize int) {
|
||||
dataSize := len(data)
|
||||
randSize = 1
|
||||
if dataSize <= 1300 {
|
||||
if dataSize > 400 {
|
||||
randSize += rand.Intn(128)
|
||||
} else {
|
||||
randSize += rand.Intn(1024)
|
||||
}
|
||||
}
|
||||
packSize = randSize + dataSize + 8
|
||||
return
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) packData(data, ret []byte, randSize int) {
|
||||
dataSize := len(data)
|
||||
retSize := len(ret)
|
||||
// 0~1, ret size
|
||||
binary.BigEndian.PutUint16(ret[:2], uint16(retSize&0xFFFF))
|
||||
// 2~3, crc of ret size
|
||||
crc := crc32.ChecksumIEEE(ret[:2]) & 0xFFFF
|
||||
binary.LittleEndian.PutUint16(ret[2:4], uint16(crc))
|
||||
// 4, rand size
|
||||
if randSize < 128 {
|
||||
ret[4] = uint8(randSize & 0xFF)
|
||||
} else {
|
||||
ret[4] = uint8(0xFF)
|
||||
binary.BigEndian.PutUint16(ret[5:7], uint16(randSize&0xFFFF))
|
||||
}
|
||||
// (rand size+4)~(ret size-4), data
|
||||
if dataSize > 0 {
|
||||
copy(ret[randSize+4:], data)
|
||||
}
|
||||
// (ret size-4)~end, adler32 of full data
|
||||
adler := adler32.Checksum(ret[:retSize-4])
|
||||
binary.LittleEndian.PutUint32(ret[retSize-4:], adler)
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) packAuthData(data []byte) (ret []byte) {
|
||||
dataSize := len(data)
|
||||
randSize := 1
|
||||
if dataSize <= 1300 {
|
||||
if dataSize > 400 {
|
||||
randSize += rand.Intn(128)
|
||||
} else {
|
||||
randSize += rand.Intn(1024)
|
||||
}
|
||||
}
|
||||
dataOffset := randSize + 4 + 2
|
||||
retSize := dataOffset + dataSize + 12 + tools.HmacSHA1Len
|
||||
ret = make([]byte, retSize)
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
a.connectionID++
|
||||
if a.connectionID > 0xFF000000 {
|
||||
a.clientID = nil
|
||||
}
|
||||
if len(a.clientID) == 0 {
|
||||
a.clientID = make([]byte, 8)
|
||||
rand.Read(a.clientID)
|
||||
b := make([]byte, 4)
|
||||
rand.Read(b)
|
||||
a.connectionID = binary.LittleEndian.Uint32(b) & 0xFFFFFF
|
||||
}
|
||||
// 0~1, ret size
|
||||
binary.BigEndian.PutUint16(ret[:2], uint16(retSize&0xFFFF))
|
||||
|
||||
// 2~6, crc of (ret size+salt+key)
|
||||
salt := []byte("auth_sha1_v4")
|
||||
crcData := make([]byte, len(salt)+len(a.Key)+2)
|
||||
copy(crcData[:2], ret[:2])
|
||||
copy(crcData[2:], salt)
|
||||
copy(crcData[2+len(salt):], a.Key)
|
||||
crc := crc32.ChecksumIEEE(crcData) & 0xFFFFFFFF
|
||||
// 2~6, crc of (ret size+salt+key)
|
||||
binary.LittleEndian.PutUint32(ret[2:], crc)
|
||||
// 6~(rand size+6), rand numbers
|
||||
rand.Read(ret[dataOffset-randSize : dataOffset])
|
||||
// 6, rand size
|
||||
if randSize < 128 {
|
||||
ret[6] = byte(randSize & 0xFF)
|
||||
} else {
|
||||
// 6, magic number 0xFF
|
||||
ret[6] = 0xFF
|
||||
// 7~8, rand size
|
||||
binary.BigEndian.PutUint16(ret[7:9], uint16(randSize&0xFFFF))
|
||||
}
|
||||
// rand size+6~(rand size+10), time stamp
|
||||
now := time.Now().Unix()
|
||||
binary.LittleEndian.PutUint32(ret[dataOffset:dataOffset+4], uint32(now))
|
||||
// rand size+10~(rand size+14), client ID
|
||||
copy(ret[dataOffset+4:dataOffset+4+4], a.clientID[:4])
|
||||
// rand size+14~(rand size+18), connection ID
|
||||
binary.LittleEndian.PutUint32(ret[dataOffset+8:dataOffset+8+4], a.connectionID)
|
||||
// rand size+18~(rand size+18)+data length, data
|
||||
copy(ret[dataOffset+12:], data)
|
||||
|
||||
key := make([]byte, len(a.IV)+len(a.Key))
|
||||
copy(key, a.IV)
|
||||
copy(key[len(a.IV):], a.Key)
|
||||
|
||||
h := tools.HmacSHA1(key, ret[:retSize-tools.HmacSHA1Len])
|
||||
// (ret size-10)~(ret size)/(rand size)+18+data length~end, hmac
|
||||
copy(ret[retSize-tools.HmacSHA1Len:], h[:tools.HmacSHA1Len])
|
||||
return ret
|
||||
}
|
||||
|
||||
func getHeadSize(data []byte, defaultValue int) int {
|
||||
if data == nil || len(data) < 2 {
|
||||
return defaultValue
|
||||
}
|
||||
headType := data[0] & 0x07
|
||||
switch headType {
|
||||
case 1:
|
||||
// IPv4 1+4+2
|
||||
return 7
|
||||
case 4:
|
||||
// IPv6 1+16+2
|
||||
return 19
|
||||
case 3:
|
||||
// domain name, variant length
|
||||
return 4 + int(data[1])
|
||||
}
|
||||
|
||||
return defaultValue
|
||||
}
|
10
component/ssr/protocol/base.go
Normal file
10
component/ssr/protocol/base.go
Normal file
@ -0,0 +1,10 @@
|
||||
package protocol
|
||||
|
||||
// Base information for protocol
|
||||
type Base struct {
|
||||
IV []byte
|
||||
Key []byte
|
||||
TCPMss int
|
||||
Overhead int
|
||||
Param string
|
||||
}
|
36
component/ssr/protocol/origin.go
Normal file
36
component/ssr/protocol/origin.go
Normal file
@ -0,0 +1,36 @@
|
||||
package protocol
|
||||
|
||||
type origin struct{ *Base }
|
||||
|
||||
func init() {
|
||||
register("origin", newOrigin)
|
||||
}
|
||||
|
||||
func newOrigin(b *Base) Protocol {
|
||||
return &origin{}
|
||||
}
|
||||
|
||||
func (o *origin) initForConn(iv []byte) Protocol { return &origin{} }
|
||||
|
||||
func (o *origin) GetProtocolOverhead() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (o *origin) SetOverhead(overhead int) {
|
||||
}
|
||||
|
||||
func (o *origin) Decode(b []byte) ([]byte, int, error) {
|
||||
return b, len(b), nil
|
||||
}
|
||||
|
||||
func (o *origin) Encode(b []byte) ([]byte, error) {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (o *origin) DecodePacket(b []byte) ([]byte, int, error) {
|
||||
return b, len(b), nil
|
||||
}
|
||||
|
||||
func (o *origin) EncodePacket(b []byte) ([]byte, error) {
|
||||
return b, nil
|
||||
}
|
42
component/ssr/protocol/packet.go
Normal file
42
component/ssr/protocol/packet.go
Normal file
@ -0,0 +1,42 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
)
|
||||
|
||||
// NewPacketConn returns a net.NewPacketConn with protocol decoding/encoding
|
||||
func NewPacketConn(pc net.PacketConn, p Protocol) net.PacketConn {
|
||||
return &PacketConn{PacketConn: pc, Protocol: p.initForConn(nil)}
|
||||
}
|
||||
|
||||
// PacketConn represents a protocol packet connection
|
||||
type PacketConn struct {
|
||||
net.PacketConn
|
||||
Protocol
|
||||
}
|
||||
|
||||
func (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
buf := pool.Get(pool.RelayBufferSize)
|
||||
defer pool.Put(buf)
|
||||
buf, err := c.EncodePacket(b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
_, err = c.PacketConn.WriteTo(buf, addr)
|
||||
return len(b), err
|
||||
}
|
||||
|
||||
func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
n, addr, err := c.PacketConn.ReadFrom(b)
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
bb, length, err := c.DecodePacket(b[:n])
|
||||
if err != nil {
|
||||
return n, addr, err
|
||||
}
|
||||
copy(b, bb)
|
||||
return length, addr, err
|
||||
}
|
61
component/ssr/protocol/protocol.go
Normal file
61
component/ssr/protocol/protocol.go
Normal file
@ -0,0 +1,61 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
errAuthAES128HMACError = errors.New("auth_aes128_* post decrypt hmac error")
|
||||
errAuthAES128DataLengthError = errors.New("auth_aes128_* post decrypt length mismatch")
|
||||
errAuthSHA1v4CRC32Error = errors.New("auth_sha1_v4 post decrypt data crc32 error")
|
||||
errAuthSHA1v4DataLengthError = errors.New("auth_sha1_v4 post decrypt data length error")
|
||||
errAuthSHA1v4IncorrectChecksum = errors.New("auth_sha1_v4 post decrypt incorrect checksum")
|
||||
errAuthChainDataLengthError = errors.New("auth_chain_* post decrypt length mismatch")
|
||||
errAuthChainHMACError = errors.New("auth_chain_* post decrypt hmac error")
|
||||
)
|
||||
|
||||
type authData struct {
|
||||
clientID []byte
|
||||
connectionID uint32
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
type recvInfo struct {
|
||||
recvID uint32
|
||||
buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
type hmacMethod func(key []byte, data []byte) []byte
|
||||
type hashDigestMethod func(data []byte) []byte
|
||||
type rndMethod func(dataSize int, random *shift128PlusContext, lastHash []byte, dataSizeList, dataSizeList2 []int, overhead int) int
|
||||
|
||||
// Protocol provides methods for decoding, encoding and iv setting
|
||||
type Protocol interface {
|
||||
initForConn(iv []byte) Protocol
|
||||
GetProtocolOverhead() int
|
||||
SetOverhead(int)
|
||||
Decode([]byte) ([]byte, int, error)
|
||||
Encode([]byte) ([]byte, error)
|
||||
DecodePacket([]byte) ([]byte, int, error)
|
||||
EncodePacket([]byte) ([]byte, error)
|
||||
}
|
||||
|
||||
type protocolCreator func(b *Base) Protocol
|
||||
|
||||
var protocolList = make(map[string]protocolCreator)
|
||||
|
||||
func register(name string, c protocolCreator) {
|
||||
protocolList[name] = c
|
||||
}
|
||||
|
||||
// PickProtocol returns a protocol of the given name
|
||||
func PickProtocol(name string, b *Base) (Protocol, error) {
|
||||
if protocolCreator, ok := protocolList[strings.ToLower(name)]; ok {
|
||||
return protocolCreator(b), nil
|
||||
}
|
||||
return nil, fmt.Errorf("Protocol %s not supported", name)
|
||||
}
|
68
component/ssr/protocol/stream.go
Normal file
68
component/ssr/protocol/stream.go
Normal file
@ -0,0 +1,68 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
)
|
||||
|
||||
// NewConn wraps a stream-oriented net.Conn with protocol decoding/encoding
|
||||
func NewConn(c net.Conn, p Protocol, iv []byte) net.Conn {
|
||||
return &Conn{Conn: c, Protocol: p.initForConn(iv)}
|
||||
}
|
||||
|
||||
// Conn represents a protocol connection
|
||||
type Conn struct {
|
||||
net.Conn
|
||||
Protocol
|
||||
buf []byte
|
||||
offset int
|
||||
underDecoded bytes.Buffer
|
||||
}
|
||||
|
||||
func (c *Conn) Read(b []byte) (int, error) {
|
||||
if c.buf != nil {
|
||||
n := copy(b, c.buf[c.offset:])
|
||||
c.offset += n
|
||||
if c.offset == len(c.buf) {
|
||||
c.buf = nil
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
buf := pool.Get(pool.RelayBufferSize)
|
||||
defer pool.Put(buf)
|
||||
n, err := c.Conn.Read(buf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
c.underDecoded.Write(buf[:n])
|
||||
underDecoded := c.underDecoded.Bytes()
|
||||
decoded, length, err := c.Decode(underDecoded)
|
||||
if err != nil {
|
||||
c.underDecoded.Reset()
|
||||
return 0, nil
|
||||
}
|
||||
if length == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
c.underDecoded.Next(length)
|
||||
n = copy(b, decoded)
|
||||
if len(decoded) > len(b) {
|
||||
c.buf = decoded
|
||||
c.offset = n
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (c *Conn) Write(b []byte) (int, error) {
|
||||
encoded, err := c.Encode(b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
_, err = c.Conn.Write(encoded)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
Reference in New Issue
Block a user