Files
sower/proxy/shadow/shadow.go
T
2019-04-04 10:57:25 +08:00

112 lines
2.4 KiB
Go

package shadow
import (
"crypto/cipher"
"encoding/binary"
"io"
"math/rand"
"net"
)
const MAX_SIZE = 0xFFFF
type conn struct {
maxSize int
aead cipher.AEAD
encryptNonce func() []byte
decryptNonce func() []byte
writeBuf []byte
readBuf []byte
readOffset int
net.Conn
}
func (c *conn) Read(b []byte) (n int, err error) {
// read from buffer
if c.readOffset != 0 {
dataSize := len(c.readBuf) - c.aead.Overhead()
n = copy(b, c.readBuf[c.readOffset:dataSize])
c.readOffset += n
if c.readOffset == dataSize {
c.readOffset = 0
}
return
}
// read from conn
dataSize := 0
{ //read data size
c.readBuf = make([]byte, 2+c.aead.Overhead())
if _, err = io.ReadFull(c.Conn, c.readBuf); err != nil {
return
}
if _, err = c.aead.Open(c.readBuf[:0], c.decryptNonce(), c.readBuf, nil); err != nil {
return
}
dataSize = int(c.readBuf[0])<<8 + int(c.readBuf[1])
}
{ // read data
c.readBuf = make([]byte, dataSize+c.aead.Overhead())
if _, err = io.ReadFull(c.Conn, c.readBuf); err != nil {
return
}
if _, err = c.aead.Open(c.readBuf[:0], c.decryptNonce(), c.readBuf, nil); err != nil {
return
}
}
// buffer extra data
if n = copy(b, c.readBuf[:dataSize]); n < dataSize {
c.readOffset = n
}
return
}
func (c *conn) Write(b []byte) (n int, err error) {
bLen := len(b)
dataSize := MAX_SIZE - (2 + c.aead.Overhead()) - c.aead.Overhead()
if bLen < c.maxSize {
dataSize = bLen
}
// BigEndian
c.writeBuf[0], c.writeBuf[1] = byte(dataSize>>8), byte(dataSize)
c.aead.Seal(c.writeBuf[:0], c.encryptNonce(), c.writeBuf[:2], nil)
c.aead.Seal(c.writeBuf[:2+c.aead.Overhead()], c.encryptNonce(), b[:dataSize], nil)
_, err = c.Conn.Write(c.writeBuf[:dataSize+(2+c.aead.Overhead())+c.aead.Overhead()])
if err != nil {
return 0, err
}
return dataSize, err
}
func Shadow(c net.Conn, cipher, password string) net.Conn {
aead, err := pickCipher(cipher, password)
if err != nil {
panic(err)
}
return &conn{
maxSize: MAX_SIZE - (2 - aead.Overhead()) - aead.Overhead(),
aead: aead,
encryptNonce: newNonce(password, aead.NonceSize()),
decryptNonce: newNonce(password, aead.NonceSize()),
writeBuf: make([]byte, 0xFFFF),
Conn: c,
}
}
func newNonce(password string, size int) func() []byte {
num, _ := binary.Varint([]byte(password))
rnd := rand.New(rand.NewSource(num))
buf := make([]byte, size)
return func() []byte {
rnd.Read(buf)
return buf
}
}