Co-authored-by: goomada <madao@DESKTOP-IOEBS0C.localdomain>
This commit is contained in:
@ -6,248 +6,177 @@ import (
|
||||
"hash/adler32"
|
||||
"hash/crc32"
|
||||
"math/rand"
|
||||
"time"
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
"github.com/Dreamacro/clash/component/ssr/tools"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("auth_sha1_v4", newAuthSHA1V4, 7)
|
||||
}
|
||||
|
||||
type authSHA1V4 struct {
|
||||
*Base
|
||||
*authData
|
||||
headerSent bool
|
||||
buffer bytes.Buffer
|
||||
}
|
||||
|
||||
func init() {
|
||||
register("auth_sha1_v4", newAuthSHA1V4)
|
||||
iv []byte
|
||||
hasSentHeader bool
|
||||
rawTrans bool
|
||||
}
|
||||
|
||||
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) StreamConn(c net.Conn, iv []byte) net.Conn {
|
||||
p := &authSHA1V4{Base: a.Base, authData: a.next()}
|
||||
p.iv = iv
|
||||
return &Conn{Conn: c, Protocol: p}
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) PacketConn(c net.PacketConn) net.PacketConn {
|
||||
return c
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) Decode(dst, src *bytes.Buffer) error {
|
||||
if a.rawTrans {
|
||||
dst.ReadFrom(src)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
for src.Len() > 4 {
|
||||
if uint16(crc32.ChecksumIEEE(src.Bytes()[:2])&0xffff) != binary.LittleEndian.Uint16(src.Bytes()[2:4]) {
|
||||
src.Reset()
|
||||
return errAuthSHA1V4CRC32Error
|
||||
}
|
||||
length := int(binary.BigEndian.Uint16(b[:2]))
|
||||
if length >= 8192 || length < 8 {
|
||||
return nil, 0, errAuthSHA1v4DataLengthError
|
||||
|
||||
length := int(binary.BigEndian.Uint16(src.Bytes()[:2]))
|
||||
if length >= 8192 || length < 7 {
|
||||
a.rawTrans = true
|
||||
src.Reset()
|
||||
return errAuthSHA1V4LengthError
|
||||
}
|
||||
if length > bSize {
|
||||
if length > src.Len() {
|
||||
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:]
|
||||
if adler32.Checksum(src.Bytes()[:length-4]) != binary.LittleEndian.Uint32(src.Bytes()[length-4:length]) {
|
||||
a.rawTrans = true
|
||||
src.Reset()
|
||||
return errAuthSHA1V4Adler32Error
|
||||
}
|
||||
|
||||
pos := int(src.Bytes()[4])
|
||||
if pos < 255 {
|
||||
pos += 4
|
||||
} else {
|
||||
return nil, 0, errAuthSHA1v4IncorrectChecksum
|
||||
pos = int(binary.BigEndian.Uint16(src.Bytes()[5:7])) + 4
|
||||
}
|
||||
dst.Write(src.Bytes()[pos : length-4])
|
||||
src.Next(length)
|
||||
}
|
||||
return a.buffer.Bytes(), originalSize - bSize, nil
|
||||
return 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
|
||||
func (a *authSHA1V4) Encode(buf *bytes.Buffer, b []byte) error {
|
||||
if !a.hasSentHeader {
|
||||
dataLength := getDataLength(b)
|
||||
|
||||
a.packAuthData(buf, b[:dataLength])
|
||||
b = b[dataLength:]
|
||||
|
||||
a.hasSentHeader = 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
|
||||
for len(b) > 8100 {
|
||||
a.packData(buf, b[:8100])
|
||||
b = b[8100:]
|
||||
}
|
||||
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)
|
||||
if len(b) > 0 {
|
||||
a.packData(buf, b)
|
||||
}
|
||||
return a.buffer.Bytes(), nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) DecodePacket(b []byte) ([]byte, int, error) {
|
||||
return b, len(b), nil
|
||||
func (a *authSHA1V4) DecodePacket(b []byte) ([]byte, error) { return b, nil }
|
||||
|
||||
func (a *authSHA1V4) EncodePacket(buf *bytes.Buffer, b []byte) error {
|
||||
buf.Write(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) EncodePacket(b []byte) ([]byte, error) {
|
||||
return b, nil
|
||||
func (a *authSHA1V4) packData(poolBuf *bytes.Buffer, data []byte) {
|
||||
dataLength := len(data)
|
||||
randDataLength := a.getRandDataLength(dataLength)
|
||||
/*
|
||||
2: uint16 BigEndian packedDataLength
|
||||
2: uint16 LittleEndian crc32Data & 0xffff
|
||||
3: maxRandDataLengthPrefix (min:1)
|
||||
4: adler32Data
|
||||
*/
|
||||
packedDataLength := 2 + 2 + 3 + randDataLength + dataLength + 4
|
||||
if randDataLength < 128 {
|
||||
packedDataLength -= 2
|
||||
}
|
||||
|
||||
binary.Write(poolBuf, binary.BigEndian, uint16(packedDataLength))
|
||||
binary.Write(poolBuf, binary.LittleEndian, uint16(crc32.ChecksumIEEE(poolBuf.Bytes()[poolBuf.Len()-2:])&0xffff))
|
||||
a.packRandData(poolBuf, randDataLength)
|
||||
poolBuf.Write(data)
|
||||
binary.Write(poolBuf, binary.LittleEndian, adler32.Checksum(poolBuf.Bytes()[poolBuf.Len()-packedDataLength+4:]))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
func (a *authSHA1V4) packAuthData(poolBuf *bytes.Buffer, data []byte) {
|
||||
dataLength := len(data)
|
||||
randDataLength := a.getRandDataLength(12 + dataLength)
|
||||
/*
|
||||
2: uint16 BigEndian packedAuthDataLength
|
||||
4: uint32 LittleEndian crc32Data
|
||||
3: maxRandDataLengthPrefix (min: 1)
|
||||
12: authDataLength
|
||||
10: hmacSHA1DataLength
|
||||
*/
|
||||
packedAuthDataLength := 2 + 4 + 3 + randDataLength + 12 + dataLength + 10
|
||||
if randDataLength < 128 {
|
||||
packedAuthDataLength -= 2
|
||||
}
|
||||
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])
|
||||
crcData := pool.Get(len(salt) + len(a.Key) + 2)
|
||||
defer pool.Put(crcData)
|
||||
binary.BigEndian.PutUint16(crcData, uint16(packedAuthDataLength))
|
||||
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)
|
||||
key := pool.Get(len(a.iv) + len(a.Key))
|
||||
defer pool.Put(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
|
||||
poolBuf.Write(crcData[:2])
|
||||
binary.Write(poolBuf, binary.LittleEndian, crc32.ChecksumIEEE(crcData))
|
||||
a.packRandData(poolBuf, randDataLength)
|
||||
a.putAuthData(poolBuf)
|
||||
poolBuf.Write(data)
|
||||
poolBuf.Write(tools.HmacSHA1(key, poolBuf.Bytes()[poolBuf.Len()-packedAuthDataLength+10:])[:10])
|
||||
}
|
||||
|
||||
func getHeadSize(data []byte, defaultValue int) int {
|
||||
if data == nil || len(data) < 2 {
|
||||
return defaultValue
|
||||
func (a *authSHA1V4) packRandData(poolBuf *bytes.Buffer, size int) {
|
||||
if size < 128 {
|
||||
poolBuf.WriteByte(byte(size + 1))
|
||||
tools.AppendRandBytes(poolBuf, size)
|
||||
return
|
||||
}
|
||||
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
|
||||
poolBuf.WriteByte(255)
|
||||
binary.Write(poolBuf, binary.BigEndian, uint16(size+3))
|
||||
tools.AppendRandBytes(poolBuf, size)
|
||||
}
|
||||
|
||||
func (a *authSHA1V4) getRandDataLength(size int) int {
|
||||
if size > 1200 {
|
||||
return 0
|
||||
}
|
||||
if size > 400 {
|
||||
return rand.Intn(256)
|
||||
}
|
||||
return rand.Intn(512)
|
||||
}
|
||||
|
Reference in New Issue
Block a user