You've already forked v2ray-core
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fefb5c8e01 | ||
|
|
9ec3e40cee | ||
|
|
b64aceabcf | ||
|
|
40222de0f7 | ||
|
|
981c43afc2 | ||
|
|
4b990c49bc | ||
|
|
b7d022702e | ||
|
|
e8901368d7 | ||
|
|
721bea5d54 | ||
|
|
519eb344db | ||
|
|
9a2a19509c | ||
|
|
6c0a1439c4 | ||
|
|
02ab3f3494 | ||
|
|
9dbdcb3a39 | ||
|
|
80258c0542 | ||
|
|
713ebfb203 | ||
|
|
41961dbd60 |
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,3 +1,9 @@
|
||||
[submodule "vendor/h12.me/socks"]
|
||||
path = vendor/h12.me/socks
|
||||
url = https://github.com/h12w/socks
|
||||
[submodule "vendor/github.com/shadowsocks/go-shadowsocks2"]
|
||||
path = vendor/github.com/shadowsocks/go-shadowsocks2
|
||||
url = https://github.com/shadowsocks/go-shadowsocks2
|
||||
[submodule "vendor/github.com/Yawning/chacha20"]
|
||||
path = vendor/github.com/Yawning/chacha20
|
||||
url = https://github.com/Yawning/chacha20
|
||||
|
||||
@@ -91,8 +91,7 @@ func copyInternal(reader Reader, writer Writer, handler *copyHandler) error {
|
||||
buffer.Release()
|
||||
return werr
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,14 +25,15 @@ func NewBytesToBufferReader(reader io.Reader) Reader {
|
||||
|
||||
func (r *BytesToBufferReader) readSmall() (MultiBuffer, error) {
|
||||
b := New()
|
||||
if err := b.Reset(ReadFrom(r.Reader)); err != nil {
|
||||
b.Release()
|
||||
return nil, err
|
||||
}
|
||||
err := b.Reset(ReadFrom(r.Reader))
|
||||
if b.IsFull() {
|
||||
r.buffer = make([]byte, 32*1024)
|
||||
}
|
||||
return NewMultiBufferValue(b), nil
|
||||
if !b.IsEmpty() {
|
||||
return NewMultiBufferValue(b), nil
|
||||
}
|
||||
b.Release()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ReadMultiBuffer implements Reader.
|
||||
@@ -45,7 +46,7 @@ func (r *BytesToBufferReader) ReadMultiBuffer() (MultiBuffer, error) {
|
||||
if nBytes > 0 {
|
||||
mb := NewMultiBufferCap(nBytes/Size + 1)
|
||||
mb.Write(r.buffer[:nBytes])
|
||||
return mb, err
|
||||
return mb, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@@ -58,21 +59,16 @@ var (
|
||||
)
|
||||
|
||||
type BufferedReader struct {
|
||||
stream Reader
|
||||
legacyReader io.Reader
|
||||
leftOver MultiBuffer
|
||||
buffered bool
|
||||
stream Reader
|
||||
leftOver MultiBuffer
|
||||
buffered bool
|
||||
}
|
||||
|
||||
func NewBufferedReader(reader Reader) *BufferedReader {
|
||||
r := &BufferedReader{
|
||||
return &BufferedReader{
|
||||
stream: reader,
|
||||
buffered: true,
|
||||
}
|
||||
if lr, ok := reader.(io.Reader); ok {
|
||||
r.legacyReader = lr
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *BufferedReader) SetBuffered(f bool) {
|
||||
@@ -99,8 +95,10 @@ func (r *BufferedReader) Read(b []byte) (int, error) {
|
||||
return nBytes, nil
|
||||
}
|
||||
|
||||
if !r.buffered && r.legacyReader != nil {
|
||||
return r.legacyReader.Read(b)
|
||||
if !r.buffered {
|
||||
if reader, ok := r.stream.(io.Reader); ok {
|
||||
return reader.Read(b)
|
||||
}
|
||||
}
|
||||
|
||||
mb, err := r.stream.ReadMultiBuffer()
|
||||
@@ -125,17 +123,20 @@ func (r *BufferedReader) ReadMultiBuffer() (MultiBuffer, error) {
|
||||
}
|
||||
|
||||
// ReadAtMost returns a MultiBuffer with at most size.
|
||||
func (r *BufferedReader) ReadAtMost(size int) (mb MultiBuffer, err error) {
|
||||
func (r *BufferedReader) ReadAtMost(size int) (MultiBuffer, error) {
|
||||
if r.leftOver == nil {
|
||||
r.leftOver, err = r.stream.ReadMultiBuffer()
|
||||
}
|
||||
if r.leftOver != nil {
|
||||
mb = r.leftOver.SliceBySize(size)
|
||||
if r.leftOver.IsEmpty() {
|
||||
r.leftOver = nil
|
||||
mb, err := r.stream.ReadMultiBuffer()
|
||||
if mb.IsEmpty() && err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.leftOver = mb
|
||||
}
|
||||
return
|
||||
|
||||
mb := r.leftOver.SliceBySize(size)
|
||||
if r.leftOver.IsEmpty() {
|
||||
r.leftOver = nil
|
||||
}
|
||||
return mb, nil
|
||||
}
|
||||
|
||||
func (r *BufferedReader) writeToInternal(writer io.Writer) (int64, error) {
|
||||
|
||||
@@ -48,23 +48,18 @@ var (
|
||||
|
||||
// BufferedWriter is a Writer with internal buffer.
|
||||
type BufferedWriter struct {
|
||||
writer Writer
|
||||
legacyWriter io.Writer
|
||||
buffer *Buffer
|
||||
buffered bool
|
||||
writer Writer
|
||||
buffer *Buffer
|
||||
buffered bool
|
||||
}
|
||||
|
||||
// NewBufferedWriter creates a new BufferedWriter.
|
||||
func NewBufferedWriter(writer Writer) *BufferedWriter {
|
||||
w := &BufferedWriter{
|
||||
return &BufferedWriter{
|
||||
writer: writer,
|
||||
buffer: New(),
|
||||
buffered: true,
|
||||
}
|
||||
if lw, ok := writer.(io.Writer); ok {
|
||||
w.legacyWriter = lw
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *BufferedWriter) WriteByte(c byte) error {
|
||||
@@ -74,8 +69,10 @@ func (w *BufferedWriter) WriteByte(c byte) error {
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (w *BufferedWriter) Write(b []byte) (int, error) {
|
||||
if !w.buffered && w.legacyWriter != nil {
|
||||
return w.legacyWriter.Write(b)
|
||||
if !w.buffered {
|
||||
if writer, ok := w.writer.(io.Writer); ok {
|
||||
return writer.Write(b)
|
||||
}
|
||||
}
|
||||
|
||||
totalBytes := 0
|
||||
|
||||
@@ -29,6 +29,26 @@ func (v StaticBytesGenerator) Next() []byte {
|
||||
return v.Content
|
||||
}
|
||||
|
||||
type IncreasingAEADNonceGenerator struct {
|
||||
nonce []byte
|
||||
}
|
||||
|
||||
func NewIncreasingAEADNonceGenerator() *IncreasingAEADNonceGenerator {
|
||||
return &IncreasingAEADNonceGenerator{
|
||||
nonce: []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||
}
|
||||
}
|
||||
|
||||
func (g *IncreasingAEADNonceGenerator) Next() []byte {
|
||||
for i := range g.nonce {
|
||||
g.nonce[i]++
|
||||
if g.nonce[i] != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return g.nonce
|
||||
}
|
||||
|
||||
type Authenticator interface {
|
||||
NonceSize() int
|
||||
Overhead() int
|
||||
@@ -48,7 +68,10 @@ func (v *AEADAuthenticator) Open(dst, cipherText []byte) ([]byte, error) {
|
||||
return nil, newError("invalid AEAD nonce size: ", len(iv))
|
||||
}
|
||||
|
||||
additionalData := v.AdditionalDataGenerator.Next()
|
||||
var additionalData []byte
|
||||
if v.AdditionalDataGenerator != nil {
|
||||
additionalData = v.AdditionalDataGenerator.Next()
|
||||
}
|
||||
return v.AEAD.Open(dst, iv, cipherText, additionalData)
|
||||
}
|
||||
|
||||
@@ -58,7 +81,10 @@ func (v *AEADAuthenticator) Seal(dst, plainText []byte) ([]byte, error) {
|
||||
return nil, newError("invalid AEAD nonce size: ", len(iv))
|
||||
}
|
||||
|
||||
additionalData := v.AdditionalDataGenerator.Next()
|
||||
var additionalData []byte
|
||||
if v.AdditionalDataGenerator != nil {
|
||||
additionalData = v.AdditionalDataGenerator.Next()
|
||||
}
|
||||
return v.AEAD.Seal(dst, iv, plainText, additionalData), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,29 @@ func (PlainChunkSizeParser) Decode(b []byte) (uint16, error) {
|
||||
return serial.BytesToUint16(b), nil
|
||||
}
|
||||
|
||||
type AEADChunkSizeParser struct {
|
||||
Auth *AEADAuthenticator
|
||||
}
|
||||
|
||||
func (p *AEADChunkSizeParser) SizeBytes() int {
|
||||
return 2 + p.Auth.Overhead()
|
||||
}
|
||||
|
||||
func (p *AEADChunkSizeParser) Encode(size uint16, b []byte) []byte {
|
||||
b = serial.Uint16ToBytes(size-uint16(p.Auth.Overhead()), b)
|
||||
b, err := p.Auth.Seal(b[:0], b)
|
||||
common.Must(err)
|
||||
return b
|
||||
}
|
||||
|
||||
func (p *AEADChunkSizeParser) Decode(b []byte) (uint16, error) {
|
||||
b, err := p.Auth.Open(b[:0], b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return serial.BytesToUint16(b) + uint16(p.Auth.Overhead()), nil
|
||||
}
|
||||
|
||||
type ChunkStreamReader struct {
|
||||
sizeDecoder ChunkSizeDecoder
|
||||
reader buf.Reader
|
||||
|
||||
@@ -171,7 +171,7 @@ func (s *Server) handleConnect(ctx context.Context, request *http.Request, reade
|
||||
if reader.Buffered() > 0 {
|
||||
payload := buf.New()
|
||||
common.Must(payload.Reset(func(b []byte) (int, error) {
|
||||
return reader.Read(b)
|
||||
return reader.Read(b[:reader.Buffered()])
|
||||
}))
|
||||
if err := ray.InboundInput().WriteMultiBuffer(buf.NewMultiBufferValue(payload)); err != nil {
|
||||
return err
|
||||
@@ -274,7 +274,10 @@ func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, wri
|
||||
|
||||
requestWriter := buf.NewBufferedWriter(ray.InboundInput())
|
||||
common.Must(requestWriter.SetBuffered(false))
|
||||
return request.Write(requestWriter)
|
||||
if err := request.Write(requestWriter); err != nil {
|
||||
return newError("failed to write whole request").Base(err).AtWarning()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
responseDone := signal.ExecuteAsync(func() error {
|
||||
@@ -308,7 +311,7 @@ func (s *Server) handlePlainHTTP(ctx context.Context, request *http.Request, wri
|
||||
response.Header.Set("Proxy-Connection", "close")
|
||||
}
|
||||
if err := response.Write(writer); err != nil {
|
||||
return newError("failed to write response").Base(err)
|
||||
return newError("failed to write response").Base(err).AtWarning()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -105,10 +105,7 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
|
||||
}
|
||||
|
||||
requestDone := signal.ExecuteAsync(func() error {
|
||||
if err := buf.Copy(outboundRay.OutboundInput(), bodyWriter, buf.UpdateActivity(timer)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return buf.Copy(outboundRay.OutboundInput(), bodyWriter, buf.UpdateActivity(timer))
|
||||
})
|
||||
|
||||
responseDone := signal.ExecuteAsync(func() error {
|
||||
@@ -119,11 +116,7 @@ func (v *Client) Process(ctx context.Context, outboundRay ray.OutboundRay, diale
|
||||
return err
|
||||
}
|
||||
|
||||
if err := buf.Copy(responseReader, outboundRay.OutboundOutput(), buf.UpdateActivity(timer)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return buf.Copy(responseReader, outboundRay.OutboundOutput(), buf.UpdateActivity(timer))
|
||||
})
|
||||
|
||||
if err := signal.ErrorOrFinish2(ctx, requestDone, responseDone); err != nil {
|
||||
|
||||
@@ -2,9 +2,17 @@ package shadowsocks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/buf"
|
||||
"v2ray.com/core/common/crypto"
|
||||
"v2ray.com/core/common/protocol"
|
||||
)
|
||||
@@ -22,6 +30,20 @@ func (v *ShadowsocksAccount) Equals(another protocol.Account) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func createAesGcm(key []byte) cipher.AEAD {
|
||||
block, err := aes.NewCipher(key)
|
||||
common.Must(err)
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
common.Must(err)
|
||||
return gcm
|
||||
}
|
||||
|
||||
func createChacha20Poly1305(key []byte) cipher.AEAD {
|
||||
chacha20, err := chacha20poly1305.New(key)
|
||||
common.Must(err)
|
||||
return chacha20
|
||||
}
|
||||
|
||||
func (v *Account) GetCipher() (Cipher, error) {
|
||||
switch v.CipherType {
|
||||
case CipherType_AES_128_CFB:
|
||||
@@ -32,6 +54,24 @@ func (v *Account) GetCipher() (Cipher, error) {
|
||||
return &ChaCha20{IVBytes: 8}, nil
|
||||
case CipherType_CHACHA20_IETF:
|
||||
return &ChaCha20{IVBytes: 12}, nil
|
||||
case CipherType_AES_128_GCM:
|
||||
return &AEADCipher{
|
||||
KeyBytes: 16,
|
||||
IVBytes: 16,
|
||||
AEADAuthCreator: createAesGcm,
|
||||
}, nil
|
||||
case CipherType_AES_256_GCM:
|
||||
return &AEADCipher{
|
||||
KeyBytes: 32,
|
||||
IVBytes: 32,
|
||||
AEADAuthCreator: createAesGcm,
|
||||
}, nil
|
||||
case CipherType_CHACHA20_POLY1305:
|
||||
return &AEADCipher{
|
||||
KeyBytes: 32,
|
||||
IVBytes: 32,
|
||||
AEADAuthCreator: createChacha20Poly1305,
|
||||
}, nil
|
||||
default:
|
||||
return nil, newError("Unsupported cipher.")
|
||||
}
|
||||
@@ -60,14 +100,21 @@ func (v *Account) GetCipherKey() []byte {
|
||||
type Cipher interface {
|
||||
KeySize() int
|
||||
IVSize() int
|
||||
NewEncodingStream(key []byte, iv []byte) (cipher.Stream, error)
|
||||
NewDecodingStream(key []byte, iv []byte) (cipher.Stream, error)
|
||||
NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error)
|
||||
NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error)
|
||||
IsAEAD() bool
|
||||
EncodePacket(key []byte, b *buf.Buffer) error
|
||||
DecodePacket(key []byte, b *buf.Buffer) error
|
||||
}
|
||||
|
||||
type AesCfb struct {
|
||||
KeyBytes int
|
||||
}
|
||||
|
||||
func (*AesCfb) IsAEAD() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (v *AesCfb) KeySize() int {
|
||||
return v.KeyBytes
|
||||
}
|
||||
@@ -76,20 +123,111 @@ func (v *AesCfb) IVSize() int {
|
||||
return 16
|
||||
}
|
||||
|
||||
func (v *AesCfb) NewEncodingStream(key []byte, iv []byte) (cipher.Stream, error) {
|
||||
func (v *AesCfb) NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) {
|
||||
stream := crypto.NewAesEncryptionStream(key, iv)
|
||||
return stream, nil
|
||||
return buf.NewWriter(crypto.NewCryptionWriter(stream, writer)), nil
|
||||
}
|
||||
|
||||
func (v *AesCfb) NewDecodingStream(key []byte, iv []byte) (cipher.Stream, error) {
|
||||
func (v *AesCfb) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) {
|
||||
stream := crypto.NewAesDecryptionStream(key, iv)
|
||||
return stream, nil
|
||||
return buf.NewReader(crypto.NewCryptionReader(stream, reader)), nil
|
||||
}
|
||||
|
||||
func (v *AesCfb) EncodePacket(key []byte, b *buf.Buffer) error {
|
||||
iv := b.BytesTo(v.IVSize())
|
||||
stream := crypto.NewAesEncryptionStream(key, iv)
|
||||
stream.XORKeyStream(b.BytesFrom(v.IVSize()), b.BytesFrom(v.IVSize()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *AesCfb) DecodePacket(key []byte, b *buf.Buffer) error {
|
||||
iv := b.BytesTo(v.IVSize())
|
||||
stream := crypto.NewAesEncryptionStream(key, iv)
|
||||
stream.XORKeyStream(b.BytesFrom(v.IVSize()), b.BytesFrom(v.IVSize()))
|
||||
b.SliceFrom(v.IVSize())
|
||||
return nil
|
||||
}
|
||||
|
||||
type AEADCipher struct {
|
||||
KeyBytes int
|
||||
IVBytes int
|
||||
AEADAuthCreator func(key []byte) cipher.AEAD
|
||||
}
|
||||
|
||||
func (*AEADCipher) IsAEAD() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *AEADCipher) KeySize() int {
|
||||
return c.KeyBytes
|
||||
}
|
||||
|
||||
func (c *AEADCipher) IVSize() int {
|
||||
return c.IVBytes
|
||||
}
|
||||
|
||||
func (c *AEADCipher) createAuthenticator(key []byte, iv []byte) *crypto.AEADAuthenticator {
|
||||
nonce := crypto.NewIncreasingAEADNonceGenerator()
|
||||
subkey := make([]byte, c.KeyBytes)
|
||||
hkdfSHA1(key, iv, subkey)
|
||||
return &crypto.AEADAuthenticator{
|
||||
AEAD: c.AEADAuthCreator(subkey),
|
||||
NonceGenerator: nonce,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AEADCipher) NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) {
|
||||
auth := c.createAuthenticator(key, iv)
|
||||
return crypto.NewAuthenticationWriter(auth, &crypto.AEADChunkSizeParser{
|
||||
Auth: auth,
|
||||
}, writer, protocol.TransferTypeStream), nil
|
||||
}
|
||||
|
||||
func (c *AEADCipher) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) {
|
||||
auth := c.createAuthenticator(key, iv)
|
||||
return crypto.NewAuthenticationReader(auth, &crypto.AEADChunkSizeParser{
|
||||
Auth: auth,
|
||||
}, reader, protocol.TransferTypeStream), nil
|
||||
}
|
||||
|
||||
func (c *AEADCipher) EncodePacket(key []byte, b *buf.Buffer) error {
|
||||
ivLen := c.IVSize()
|
||||
payloadLen := b.Len()
|
||||
auth := c.createAuthenticator(key, b.BytesTo(ivLen))
|
||||
return b.Reset(func(bb []byte) (int, error) {
|
||||
bbb, err := auth.Seal(bb[:ivLen], bb[ivLen:payloadLen])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(bbb), nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *AEADCipher) DecodePacket(key []byte, b *buf.Buffer) error {
|
||||
ivLen := c.IVSize()
|
||||
payloadLen := b.Len()
|
||||
auth := c.createAuthenticator(key, b.BytesTo(ivLen))
|
||||
if err := b.Reset(func(bb []byte) (int, error) {
|
||||
bbb, err := auth.Open(bb[:ivLen], bb[ivLen:payloadLen])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(bbb), nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
b.SliceFrom(ivLen)
|
||||
return nil
|
||||
}
|
||||
|
||||
type ChaCha20 struct {
|
||||
IVBytes int
|
||||
}
|
||||
|
||||
func (*ChaCha20) IsAEAD() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (v *ChaCha20) KeySize() int {
|
||||
return 32
|
||||
}
|
||||
@@ -98,12 +236,29 @@ func (v *ChaCha20) IVSize() int {
|
||||
return v.IVBytes
|
||||
}
|
||||
|
||||
func (v *ChaCha20) NewEncodingStream(key []byte, iv []byte) (cipher.Stream, error) {
|
||||
return crypto.NewChaCha20Stream(key, iv), nil
|
||||
func (v *ChaCha20) NewEncryptionWriter(key []byte, iv []byte, writer io.Writer) (buf.Writer, error) {
|
||||
stream := crypto.NewChaCha20Stream(key, iv)
|
||||
return buf.NewWriter(crypto.NewCryptionWriter(stream, writer)), nil
|
||||
}
|
||||
|
||||
func (v *ChaCha20) NewDecodingStream(key []byte, iv []byte) (cipher.Stream, error) {
|
||||
return crypto.NewChaCha20Stream(key, iv), nil
|
||||
func (v *ChaCha20) NewDecryptionReader(key []byte, iv []byte, reader io.Reader) (buf.Reader, error) {
|
||||
stream := crypto.NewChaCha20Stream(key, iv)
|
||||
return buf.NewReader(crypto.NewCryptionReader(stream, reader)), nil
|
||||
}
|
||||
|
||||
func (v *ChaCha20) EncodePacket(key []byte, b *buf.Buffer) error {
|
||||
iv := b.BytesTo(v.IVSize())
|
||||
stream := crypto.NewChaCha20Stream(key, iv)
|
||||
stream.XORKeyStream(b.BytesFrom(v.IVSize()), b.BytesFrom(v.IVSize()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *ChaCha20) DecodePacket(key []byte, b *buf.Buffer) error {
|
||||
iv := b.BytesTo(v.IVSize())
|
||||
stream := crypto.NewChaCha20Stream(key, iv)
|
||||
stream.XORKeyStream(b.BytesFrom(v.IVSize()), b.BytesFrom(v.IVSize()))
|
||||
b.SliceFrom(v.IVSize())
|
||||
return nil
|
||||
}
|
||||
|
||||
func PasswordToCipherKey(password string, keySize int) []byte {
|
||||
@@ -123,3 +278,8 @@ func PasswordToCipherKey(password string, keySize int) []byte {
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func hkdfSHA1(secret, salt, outkey []byte) {
|
||||
r := hkdf.New(sha1.New, secret, salt, []byte("ss-subkey"))
|
||||
common.Must2(io.ReadFull(r, outkey))
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/bitmask"
|
||||
"v2ray.com/core/common/buf"
|
||||
"v2ray.com/core/common/crypto"
|
||||
"v2ray.com/core/common/net"
|
||||
"v2ray.com/core/common/protocol"
|
||||
"v2ray.com/core/common/serial"
|
||||
@@ -40,11 +39,12 @@ func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHea
|
||||
|
||||
iv := append([]byte(nil), buffer.BytesTo(ivLen)...)
|
||||
|
||||
stream, err := account.Cipher.NewDecodingStream(account.Key, iv)
|
||||
r, err := account.Cipher.NewDecryptionReader(account.Key, iv, reader)
|
||||
if err != nil {
|
||||
return nil, nil, newError("failed to initialize decoding stream").Base(err).AtError()
|
||||
}
|
||||
reader = crypto.NewCryptionReader(stream, reader)
|
||||
br := buf.NewBufferedReader(r)
|
||||
reader = nil
|
||||
|
||||
authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv))
|
||||
request := &protocol.RequestHeader{
|
||||
@@ -53,40 +53,42 @@ func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHea
|
||||
Command: protocol.RequestCommandTCP,
|
||||
}
|
||||
|
||||
if err := buffer.Reset(buf.ReadFullFrom(reader, 1)); err != nil {
|
||||
if err := buffer.Reset(buf.ReadFullFrom(br, 1)); err != nil {
|
||||
return nil, nil, newError("failed to read address type").Base(err)
|
||||
}
|
||||
|
||||
if !account.Cipher.IsAEAD() {
|
||||
if (buffer.Byte(0) & 0x10) == 0x10 {
|
||||
request.Option.Set(RequestOptionOneTimeAuth)
|
||||
}
|
||||
|
||||
if request.Option.Has(RequestOptionOneTimeAuth) && account.OneTimeAuth == Account_Disabled {
|
||||
return nil, nil, newError("rejecting connection with OTA enabled, while server disables OTA")
|
||||
}
|
||||
|
||||
if !request.Option.Has(RequestOptionOneTimeAuth) && account.OneTimeAuth == Account_Enabled {
|
||||
return nil, nil, newError("rejecting connection with OTA disabled, while server enables OTA")
|
||||
}
|
||||
}
|
||||
|
||||
addrType := (buffer.Byte(0) & 0x0F)
|
||||
if (buffer.Byte(0) & 0x10) == 0x10 {
|
||||
request.Option.Set(RequestOptionOneTimeAuth)
|
||||
}
|
||||
|
||||
if request.Option.Has(RequestOptionOneTimeAuth) && account.OneTimeAuth == Account_Disabled {
|
||||
return nil, nil, newError("rejecting connection with OTA enabled, while server disables OTA")
|
||||
}
|
||||
|
||||
if !request.Option.Has(RequestOptionOneTimeAuth) && account.OneTimeAuth == Account_Enabled {
|
||||
return nil, nil, newError("rejecting connection with OTA disabled, while server enables OTA")
|
||||
}
|
||||
|
||||
switch addrType {
|
||||
case AddrTypeIPv4:
|
||||
if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 4)); err != nil {
|
||||
if err := buffer.AppendSupplier(buf.ReadFullFrom(br, 4)); err != nil {
|
||||
return nil, nil, newError("failed to read IPv4 address").Base(err)
|
||||
}
|
||||
request.Address = net.IPAddress(buffer.BytesFrom(-4))
|
||||
case AddrTypeIPv6:
|
||||
if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 16)); err != nil {
|
||||
if err := buffer.AppendSupplier(buf.ReadFullFrom(br, 16)); err != nil {
|
||||
return nil, nil, newError("failed to read IPv6 address").Base(err)
|
||||
}
|
||||
request.Address = net.IPAddress(buffer.BytesFrom(-16))
|
||||
case AddrTypeDomain:
|
||||
if err := buffer.AppendSupplier(buf.ReadFullFrom(reader, 1)); err != nil {
|
||||
if err := buffer.AppendSupplier(buf.ReadFullFrom(br, 1)); err != nil {
|
||||
return nil, nil, newError("failed to read domain lenth.").Base(err)
|
||||
}
|
||||
domainLength := int(buffer.BytesFrom(-1)[0])
|
||||
err = buffer.AppendSupplier(buf.ReadFullFrom(reader, domainLength))
|
||||
err = buffer.AppendSupplier(buf.ReadFullFrom(br, domainLength))
|
||||
if err != nil {
|
||||
return nil, nil, newError("failed to read domain").Base(err)
|
||||
}
|
||||
@@ -95,7 +97,7 @@ func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHea
|
||||
// Check address validity after OTA verification.
|
||||
}
|
||||
|
||||
err = buffer.AppendSupplier(buf.ReadFullFrom(reader, 2))
|
||||
err = buffer.AppendSupplier(buf.ReadFullFrom(br, 2))
|
||||
if err != nil {
|
||||
return nil, nil, newError("failed to read port").Base(err)
|
||||
}
|
||||
@@ -105,7 +107,7 @@ func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHea
|
||||
actualAuth := make([]byte, AuthSize)
|
||||
authenticator.Authenticate(buffer.Bytes())(actualAuth)
|
||||
|
||||
err := buffer.AppendSupplier(buf.ReadFullFrom(reader, AuthSize))
|
||||
err := buffer.AppendSupplier(buf.ReadFullFrom(br, AuthSize))
|
||||
if err != nil {
|
||||
return nil, nil, newError("Failed to read OTA").Base(err)
|
||||
}
|
||||
@@ -119,11 +121,13 @@ func ReadTCPSession(user *protocol.User, reader io.Reader) (*protocol.RequestHea
|
||||
return nil, nil, newError("invalid remote address.")
|
||||
}
|
||||
|
||||
br.SetBuffered(false)
|
||||
|
||||
var chunkReader buf.Reader
|
||||
if request.Option.Has(RequestOptionOneTimeAuth) {
|
||||
chunkReader = NewChunkReader(reader, NewAuthenticator(ChunkKeyGenerator(iv)))
|
||||
chunkReader = NewChunkReader(br, NewAuthenticator(ChunkKeyGenerator(iv)))
|
||||
} else {
|
||||
chunkReader = buf.NewReader(reader)
|
||||
chunkReader = buf.NewReader(br)
|
||||
}
|
||||
|
||||
return request, chunkReader, nil
|
||||
@@ -137,20 +141,22 @@ func WriteTCPRequest(request *protocol.RequestHeader, writer io.Writer) (buf.Wri
|
||||
}
|
||||
account := rawAccount.(*ShadowsocksAccount)
|
||||
|
||||
if account.Cipher.IsAEAD() {
|
||||
request.Option.Clear(RequestOptionOneTimeAuth)
|
||||
}
|
||||
|
||||
iv := make([]byte, account.Cipher.IVSize())
|
||||
rand.Read(iv)
|
||||
common.Must2(rand.Read(iv))
|
||||
_, err = writer.Write(iv)
|
||||
if err != nil {
|
||||
return nil, newError("failed to write IV")
|
||||
}
|
||||
|
||||
stream, err := account.Cipher.NewEncodingStream(account.Key, iv)
|
||||
w, err := account.Cipher.NewEncryptionWriter(account.Key, iv, writer)
|
||||
if err != nil {
|
||||
return nil, newError("failed to create encoding stream").Base(err).AtError()
|
||||
}
|
||||
|
||||
writer = crypto.NewCryptionWriter(stream, writer)
|
||||
|
||||
header := buf.NewLocal(512)
|
||||
|
||||
switch request.Address.Family() {
|
||||
@@ -180,16 +186,15 @@ func WriteTCPRequest(request *protocol.RequestHeader, writer io.Writer) (buf.Wri
|
||||
common.Must(header.AppendSupplier(authenticator.Authenticate(header.Bytes())))
|
||||
}
|
||||
|
||||
_, err = writer.Write(header.Bytes())
|
||||
if err != nil {
|
||||
if err := w.WriteMultiBuffer(buf.NewMultiBufferValue(header)); err != nil {
|
||||
return nil, newError("failed to write header").Base(err)
|
||||
}
|
||||
|
||||
var chunkWriter buf.Writer
|
||||
if request.Option.Has(RequestOptionOneTimeAuth) {
|
||||
chunkWriter = NewChunkWriter(writer, NewAuthenticator(ChunkKeyGenerator(iv)))
|
||||
chunkWriter = NewChunkWriter(w.(io.Writer), NewAuthenticator(ChunkKeyGenerator(iv)))
|
||||
} else {
|
||||
chunkWriter = buf.NewWriter(writer)
|
||||
chunkWriter = w
|
||||
}
|
||||
|
||||
return chunkWriter, nil
|
||||
@@ -208,11 +213,7 @@ func ReadTCPResponse(user *protocol.User, reader io.Reader) (buf.Reader, error)
|
||||
return nil, newError("failed to read IV").Base(err)
|
||||
}
|
||||
|
||||
stream, err := account.Cipher.NewDecodingStream(account.Key, iv)
|
||||
if err != nil {
|
||||
return nil, newError("failed to initialize decoding stream").Base(err).AtError()
|
||||
}
|
||||
return buf.NewReader(crypto.NewCryptionReader(stream, reader)), nil
|
||||
return account.Cipher.NewDecryptionReader(account.Key, iv, reader)
|
||||
}
|
||||
|
||||
func WriteTCPResponse(request *protocol.RequestHeader, writer io.Writer) (buf.Writer, error) {
|
||||
@@ -224,18 +225,13 @@ func WriteTCPResponse(request *protocol.RequestHeader, writer io.Writer) (buf.Wr
|
||||
account := rawAccount.(*ShadowsocksAccount)
|
||||
|
||||
iv := make([]byte, account.Cipher.IVSize())
|
||||
rand.Read(iv)
|
||||
common.Must2(rand.Read(iv))
|
||||
_, err = writer.Write(iv)
|
||||
if err != nil {
|
||||
return nil, newError("failed to write IV.").Base(err)
|
||||
}
|
||||
|
||||
stream, err := account.Cipher.NewEncodingStream(account.Key, iv)
|
||||
if err != nil {
|
||||
return nil, newError("failed to create encoding stream.").Base(err).AtError()
|
||||
}
|
||||
|
||||
return buf.NewWriter(crypto.NewCryptionWriter(stream, writer)), nil
|
||||
return account.Cipher.NewEncryptionWriter(account.Key, iv, writer)
|
||||
}
|
||||
|
||||
func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buffer, error) {
|
||||
@@ -248,7 +244,7 @@ func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buff
|
||||
|
||||
buffer := buf.New()
|
||||
ivLen := account.Cipher.IVSize()
|
||||
buffer.AppendSupplier(buf.ReadFullFrom(rand.Reader, ivLen))
|
||||
common.Must(buffer.Reset(buf.ReadFullFrom(rand.Reader, ivLen)))
|
||||
iv := buffer.Bytes()
|
||||
|
||||
switch request.Address.Family() {
|
||||
@@ -265,22 +261,19 @@ func EncodeUDPPacket(request *protocol.RequestHeader, payload []byte) (*buf.Buff
|
||||
return nil, newError("unsupported address type: ", request.Address.Family()).AtError()
|
||||
}
|
||||
|
||||
buffer.AppendSupplier(serial.WriteUint16(uint16(request.Port)))
|
||||
common.Must(buffer.AppendSupplier(serial.WriteUint16(uint16(request.Port))))
|
||||
buffer.Append(payload)
|
||||
|
||||
if request.Option.Has(RequestOptionOneTimeAuth) {
|
||||
if !account.Cipher.IsAEAD() && request.Option.Has(RequestOptionOneTimeAuth) {
|
||||
authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv))
|
||||
buffer.SetByte(ivLen, buffer.Byte(ivLen)|0x10)
|
||||
|
||||
buffer.AppendSupplier(authenticator.Authenticate(buffer.BytesFrom(ivLen)))
|
||||
common.Must(buffer.AppendSupplier(authenticator.Authenticate(buffer.BytesFrom(ivLen))))
|
||||
}
|
||||
if err := account.Cipher.EncodePacket(account.Key, buffer); err != nil {
|
||||
return nil, newError("failed to encrypt UDP payload").Base(err)
|
||||
}
|
||||
|
||||
stream, err := account.Cipher.NewEncodingStream(account.Key, iv)
|
||||
if err != nil {
|
||||
return nil, newError("failed to create encoding stream").Base(err).AtError()
|
||||
}
|
||||
|
||||
stream.XORKeyStream(buffer.BytesFrom(ivLen), buffer.BytesFrom(ivLen))
|
||||
return buffer, nil
|
||||
}
|
||||
|
||||
@@ -291,51 +284,50 @@ func DecodeUDPPacket(user *protocol.User, payload *buf.Buffer) (*protocol.Reques
|
||||
}
|
||||
account := rawAccount.(*ShadowsocksAccount)
|
||||
|
||||
ivLen := account.Cipher.IVSize()
|
||||
iv := payload.BytesTo(ivLen)
|
||||
payload.SliceFrom(ivLen)
|
||||
|
||||
stream, err := account.Cipher.NewDecodingStream(account.Key, iv)
|
||||
if err != nil {
|
||||
return nil, nil, newError("failed to initialize decoding stream").Base(err).AtError()
|
||||
var authenticator *Authenticator
|
||||
if !account.Cipher.IsAEAD() {
|
||||
authenticator = NewAuthenticator(HeaderKeyGenerator(account.Key, payload.BytesTo(account.Cipher.IVSize())))
|
||||
}
|
||||
|
||||
if err := account.Cipher.DecodePacket(account.Key, payload); err != nil {
|
||||
return nil, nil, newError("failed to decrypt UDP payload").Base(err)
|
||||
}
|
||||
stream.XORKeyStream(payload.Bytes(), payload.Bytes())
|
||||
|
||||
authenticator := NewAuthenticator(HeaderKeyGenerator(account.Key, iv))
|
||||
request := &protocol.RequestHeader{
|
||||
Version: Version,
|
||||
User: user,
|
||||
Command: protocol.RequestCommandUDP,
|
||||
}
|
||||
|
||||
addrType := (payload.Byte(0) & 0x0F)
|
||||
if (payload.Byte(0) & 0x10) == 0x10 {
|
||||
request.Option |= RequestOptionOneTimeAuth
|
||||
}
|
||||
|
||||
if request.Option.Has(RequestOptionOneTimeAuth) && account.OneTimeAuth == Account_Disabled {
|
||||
return nil, nil, newError("rejecting packet with OTA enabled, while server disables OTA").AtWarning()
|
||||
}
|
||||
|
||||
if !request.Option.Has(RequestOptionOneTimeAuth) && account.OneTimeAuth == Account_Enabled {
|
||||
return nil, nil, newError("rejecting packet with OTA disabled, while server enables OTA").AtWarning()
|
||||
}
|
||||
|
||||
if request.Option.Has(RequestOptionOneTimeAuth) {
|
||||
payloadLen := payload.Len() - AuthSize
|
||||
authBytes := payload.BytesFrom(payloadLen)
|
||||
|
||||
actualAuth := make([]byte, AuthSize)
|
||||
authenticator.Authenticate(payload.BytesTo(payloadLen))(actualAuth)
|
||||
if !bytes.Equal(actualAuth, authBytes) {
|
||||
return nil, nil, newError("invalid OTA")
|
||||
if !account.Cipher.IsAEAD() {
|
||||
if (payload.Byte(0) & 0x10) == 0x10 {
|
||||
request.Option |= RequestOptionOneTimeAuth
|
||||
}
|
||||
|
||||
payload.Slice(0, payloadLen)
|
||||
if request.Option.Has(RequestOptionOneTimeAuth) && account.OneTimeAuth == Account_Disabled {
|
||||
return nil, nil, newError("rejecting packet with OTA enabled, while server disables OTA").AtWarning()
|
||||
}
|
||||
|
||||
if !request.Option.Has(RequestOptionOneTimeAuth) && account.OneTimeAuth == Account_Enabled {
|
||||
return nil, nil, newError("rejecting packet with OTA disabled, while server enables OTA").AtWarning()
|
||||
}
|
||||
|
||||
if request.Option.Has(RequestOptionOneTimeAuth) {
|
||||
payloadLen := payload.Len() - AuthSize
|
||||
authBytes := payload.BytesFrom(payloadLen)
|
||||
|
||||
actualAuth := make([]byte, AuthSize)
|
||||
authenticator.Authenticate(payload.BytesTo(payloadLen))(actualAuth)
|
||||
if !bytes.Equal(actualAuth, authBytes) {
|
||||
return nil, nil, newError("invalid OTA")
|
||||
}
|
||||
|
||||
payload.Slice(0, payloadLen)
|
||||
}
|
||||
}
|
||||
|
||||
addrType := (payload.Byte(0) & 0x0F)
|
||||
payload.SliceFrom(1)
|
||||
|
||||
switch addrType {
|
||||
case AddrTypeIPv4:
|
||||
request.Address = net.IPAddress(payload.BytesTo(4))
|
||||
|
||||
@@ -125,6 +125,8 @@ func TestHttpConnectMethod(t *testing.T) {
|
||||
payload := make([]byte, 1024*64)
|
||||
common.Must2(rand.Read(payload))
|
||||
req, err := http.NewRequest("Connect", "http://"+dest.NetAddr()+"/", bytes.NewReader(payload))
|
||||
req.Header.Set("X-a", "b")
|
||||
req.Header.Set("X-b", "d")
|
||||
common.Must(err)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
|
||||
@@ -2,6 +2,7 @@ package scenarios
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"v2ray.com/core"
|
||||
"v2ray.com/core/app/log"
|
||||
"v2ray.com/core/app/proxyman"
|
||||
"v2ray.com/core/common/buf"
|
||||
"v2ray.com/core/common/net"
|
||||
"v2ray.com/core/common/protocol"
|
||||
"v2ray.com/core/common/serial"
|
||||
@@ -18,6 +20,8 @@ import (
|
||||
"v2ray.com/core/testing/servers/tcp"
|
||||
"v2ray.com/core/testing/servers/udp"
|
||||
. "v2ray.com/ext/assert"
|
||||
|
||||
ss "github.com/shadowsocks/go-shadowsocks2/core"
|
||||
)
|
||||
|
||||
func TestShadowsocksAES256TCP(t *testing.T) {
|
||||
@@ -374,3 +378,398 @@ func TestShadowsocksChacha20TCP(t *testing.T) {
|
||||
|
||||
CloseAllServers(servers)
|
||||
}
|
||||
|
||||
func TestShadowsocksAES256GCMTCP(t *testing.T) {
|
||||
assert := With(t)
|
||||
|
||||
tcpServer := tcp.Server{
|
||||
MsgProcessor: xor,
|
||||
}
|
||||
dest, err := tcpServer.Start()
|
||||
assert(err, IsNil)
|
||||
defer tcpServer.Close()
|
||||
|
||||
account := serial.ToTypedMessage(&shadowsocks.Account{
|
||||
Password: "shadowsocks-password",
|
||||
CipherType: shadowsocks.CipherType_AES_256_GCM,
|
||||
})
|
||||
|
||||
serverPort := pickPort()
|
||||
serverConfig := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&log.Config{
|
||||
ErrorLogLevel: log.LogLevel_Debug,
|
||||
ErrorLogType: log.LogType_Console,
|
||||
}),
|
||||
},
|
||||
Inbound: []*proxyman.InboundHandlerConfig{
|
||||
{
|
||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||
PortRange: net.SinglePortRange(serverPort),
|
||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||
}),
|
||||
ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{
|
||||
User: &protocol.User{
|
||||
Account: account,
|
||||
Level: 1,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
Outbound: []*proxyman.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
clientPort := pickPort()
|
||||
clientConfig := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&log.Config{
|
||||
ErrorLogLevel: log.LogLevel_Debug,
|
||||
ErrorLogType: log.LogType_Console,
|
||||
}),
|
||||
},
|
||||
Inbound: []*proxyman.InboundHandlerConfig{
|
||||
{
|
||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||
PortRange: net.SinglePortRange(clientPort),
|
||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||
}),
|
||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||
Address: net.NewIPOrDomain(dest.Address),
|
||||
Port: uint32(dest.Port),
|
||||
NetworkList: &net.NetworkList{
|
||||
Network: []net.Network{net.Network_TCP},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
Outbound: []*proxyman.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&shadowsocks.ClientConfig{
|
||||
Server: []*protocol.ServerEndpoint{
|
||||
{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: []*protocol.User{
|
||||
{
|
||||
Account: account,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
servers, err := InitializeServerConfigs(serverConfig, clientConfig)
|
||||
assert(err, IsNil)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(10)
|
||||
for i := 0; i < 10; i++ {
|
||||
go func() {
|
||||
conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{
|
||||
IP: []byte{127, 0, 0, 1},
|
||||
Port: int(clientPort),
|
||||
})
|
||||
assert(err, IsNil)
|
||||
|
||||
payload := make([]byte, 10240*1024)
|
||||
rand.Read(payload)
|
||||
|
||||
nBytes, err := conn.Write([]byte(payload))
|
||||
assert(err, IsNil)
|
||||
assert(nBytes, Equals, len(payload))
|
||||
|
||||
response := readFrom(conn, time.Second*20, 10240*1024)
|
||||
assert(response, Equals, xor([]byte(payload)))
|
||||
assert(conn.Close(), IsNil)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
CloseAllServers(servers)
|
||||
}
|
||||
|
||||
func TestShadowsocksAES128GCMUDP(t *testing.T) {
|
||||
assert := With(t)
|
||||
|
||||
udpServer := udp.Server{
|
||||
MsgProcessor: xor,
|
||||
}
|
||||
dest, err := udpServer.Start()
|
||||
assert(err, IsNil)
|
||||
defer udpServer.Close()
|
||||
|
||||
account := serial.ToTypedMessage(&shadowsocks.Account{
|
||||
Password: "shadowsocks-password",
|
||||
CipherType: shadowsocks.CipherType_AES_128_GCM,
|
||||
})
|
||||
|
||||
serverPort := pickPort()
|
||||
serverConfig := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&log.Config{
|
||||
ErrorLogLevel: log.LogLevel_Debug,
|
||||
ErrorLogType: log.LogType_Console,
|
||||
}),
|
||||
},
|
||||
Inbound: []*proxyman.InboundHandlerConfig{
|
||||
{
|
||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||
PortRange: net.SinglePortRange(serverPort),
|
||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||
}),
|
||||
ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{
|
||||
UdpEnabled: true,
|
||||
User: &protocol.User{
|
||||
Account: account,
|
||||
Level: 1,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
Outbound: []*proxyman.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
clientPort := pickPort()
|
||||
clientConfig := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&log.Config{
|
||||
ErrorLogLevel: log.LogLevel_Debug,
|
||||
ErrorLogType: log.LogType_Console,
|
||||
}),
|
||||
},
|
||||
Inbound: []*proxyman.InboundHandlerConfig{
|
||||
{
|
||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||
PortRange: net.SinglePortRange(clientPort),
|
||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||
}),
|
||||
ProxySettings: serial.ToTypedMessage(&dokodemo.Config{
|
||||
Address: net.NewIPOrDomain(dest.Address),
|
||||
Port: uint32(dest.Port),
|
||||
NetworkList: &net.NetworkList{
|
||||
Network: []net.Network{net.Network_UDP},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
Outbound: []*proxyman.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&shadowsocks.ClientConfig{
|
||||
Server: []*protocol.ServerEndpoint{
|
||||
{
|
||||
Address: net.NewIPOrDomain(net.LocalHostIP),
|
||||
Port: uint32(serverPort),
|
||||
User: []*protocol.User{
|
||||
{
|
||||
Account: account,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
servers, err := InitializeServerConfigs(serverConfig, clientConfig)
|
||||
assert(err, IsNil)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(10)
|
||||
for i := 0; i < 10; i++ {
|
||||
go func() {
|
||||
conn, err := net.DialUDP("udp", nil, &net.UDPAddr{
|
||||
IP: []byte{127, 0, 0, 1},
|
||||
Port: int(clientPort),
|
||||
})
|
||||
assert(err, IsNil)
|
||||
|
||||
payload := make([]byte, 1024)
|
||||
rand.Read(payload)
|
||||
|
||||
nBytes, err := conn.Write([]byte(payload))
|
||||
assert(err, IsNil)
|
||||
assert(nBytes, Equals, len(payload))
|
||||
|
||||
response := readFrom(conn, time.Second*5, 1024)
|
||||
assert(response, Equals, xor([]byte(payload)))
|
||||
assert(conn.Close(), IsNil)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
CloseAllServers(servers)
|
||||
}
|
||||
|
||||
func TestShadowsocksAES256GCMConformance(t *testing.T) {
|
||||
assert := With(t)
|
||||
|
||||
tcpServer := tcp.Server{
|
||||
MsgProcessor: xor,
|
||||
}
|
||||
dest, err := tcpServer.Start()
|
||||
assert(err, IsNil)
|
||||
defer tcpServer.Close()
|
||||
|
||||
account := serial.ToTypedMessage(&shadowsocks.Account{
|
||||
Password: "ss-password",
|
||||
CipherType: shadowsocks.CipherType_AES_256_GCM,
|
||||
})
|
||||
|
||||
serverPort := pickPort()
|
||||
serverConfig := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&log.Config{
|
||||
ErrorLogLevel: log.LogLevel_Debug,
|
||||
ErrorLogType: log.LogType_Console,
|
||||
}),
|
||||
},
|
||||
Inbound: []*proxyman.InboundHandlerConfig{
|
||||
{
|
||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||
PortRange: net.SinglePortRange(serverPort),
|
||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||
}),
|
||||
ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{
|
||||
User: &protocol.User{
|
||||
Account: account,
|
||||
Level: 1,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
Outbound: []*proxyman.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
servers, err := InitializeServerConfigs(serverConfig)
|
||||
assert(err, IsNil)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(10)
|
||||
for i := 0; i < 10; i++ {
|
||||
go func() {
|
||||
cipher, err := ss.PickCipher("AES-256-GCM", nil, "ss-password")
|
||||
assert(err, IsNil)
|
||||
conn, err := ss.Dial("tcp", fmt.Sprintf(":%d", serverPort), cipher)
|
||||
assert(err, IsNil)
|
||||
_, err = conn.Write([]byte{1, 127, 0, 0, 1})
|
||||
assert(err, IsNil)
|
||||
_, err = conn.Write(serial.Uint16ToBytes(dest.Port.Value(), nil))
|
||||
assert(err, IsNil)
|
||||
|
||||
payload := make([]byte, 10240*1024)
|
||||
rand.Read(payload)
|
||||
|
||||
nBytes, err := conn.Write([]byte(payload))
|
||||
assert(err, IsNil)
|
||||
assert(nBytes, Equals, len(payload))
|
||||
|
||||
response := readFrom(conn, time.Second*30, 10240*1024)
|
||||
assert(response, Equals, xor([]byte(payload)))
|
||||
assert(conn.Close(), IsNil)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
CloseAllServers(servers)
|
||||
}
|
||||
|
||||
func TestShadowsocksChacha20Poly1305UDPConformance(t *testing.T) {
|
||||
assert := With(t)
|
||||
|
||||
udpServer := udp.Server{
|
||||
MsgProcessor: xor,
|
||||
}
|
||||
dest, err := udpServer.Start()
|
||||
assert(err, IsNil)
|
||||
defer udpServer.Close()
|
||||
|
||||
account := serial.ToTypedMessage(&shadowsocks.Account{
|
||||
Password: "ss-password",
|
||||
CipherType: shadowsocks.CipherType_CHACHA20_POLY1305,
|
||||
})
|
||||
|
||||
serverPort := pickPort()
|
||||
serverConfig := &core.Config{
|
||||
App: []*serial.TypedMessage{
|
||||
serial.ToTypedMessage(&log.Config{
|
||||
ErrorLogLevel: log.LogLevel_Debug,
|
||||
ErrorLogType: log.LogType_Console,
|
||||
}),
|
||||
},
|
||||
Inbound: []*proxyman.InboundHandlerConfig{
|
||||
{
|
||||
ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{
|
||||
PortRange: net.SinglePortRange(serverPort),
|
||||
Listen: net.NewIPOrDomain(net.LocalHostIP),
|
||||
}),
|
||||
ProxySettings: serial.ToTypedMessage(&shadowsocks.ServerConfig{
|
||||
UdpEnabled: true,
|
||||
User: &protocol.User{
|
||||
Account: account,
|
||||
Level: 1,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
Outbound: []*proxyman.OutboundHandlerConfig{
|
||||
{
|
||||
ProxySettings: serial.ToTypedMessage(&freedom.Config{}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
servers, err := InitializeServerConfigs(serverConfig)
|
||||
assert(err, IsNil)
|
||||
|
||||
cipher, err := ss.PickCipher("CHACHA20-IETF-POLY1305", nil, "ss-password")
|
||||
assert(err, IsNil)
|
||||
conn, err := ss.ListenPacket("udp", ":0", cipher)
|
||||
assert(err, IsNil)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
|
||||
payload := buf.New()
|
||||
payload.AppendBytes(1, 127, 0, 0, 1)
|
||||
payload.AppendSupplier(serial.WriteUint16(dest.Port.Value()))
|
||||
|
||||
payload.AppendSupplier(buf.ReadFullFrom(rand.Reader, 10))
|
||||
|
||||
nBytes, err := conn.WriteTo(payload.Bytes(), &net.UDPAddr{
|
||||
IP: []byte{127, 0, 0, 1},
|
||||
Port: int(serverPort),
|
||||
})
|
||||
assert(err, IsNil)
|
||||
assert(nBytes, Equals, payload.Len())
|
||||
|
||||
conn.SetReadDeadline(time.Now().Add(time.Second * 10))
|
||||
response := make([]byte, 10240)
|
||||
nBytes, _, err = conn.ReadFrom(response)
|
||||
assert(err, IsNil)
|
||||
assert(response[:7], Equals, payload.BytesTo(7))
|
||||
assert(response[7:nBytes], Equals, xor(payload.BytesFrom(7)))
|
||||
|
||||
}
|
||||
|
||||
assert(conn.Close(), IsNil)
|
||||
|
||||
CloseAllServers(servers)
|
||||
}
|
||||
|
||||
@@ -652,7 +652,7 @@ func TestVMessNone(t *testing.T) {
|
||||
assert(err, IsNil)
|
||||
assert(nBytes, Equals, len(payload))
|
||||
|
||||
response := readFrom(conn, time.Second*20, 1024*1024)
|
||||
response := readFrom(conn, time.Second*30, 1024*1024)
|
||||
|
||||
assert(response, Equals, xor(payload))
|
||||
assert(conn.Close(), IsNil)
|
||||
|
||||
1
vendor/github.com/Yawning/chacha20
generated
vendored
Submodule
1
vendor/github.com/Yawning/chacha20
generated
vendored
Submodule
Submodule vendor/github.com/Yawning/chacha20 added at e3b1f968fc
1
vendor/github.com/shadowsocks/go-shadowsocks2
generated
vendored
Submodule
1
vendor/github.com/shadowsocks/go-shadowsocks2
generated
vendored
Submodule
Submodule vendor/github.com/shadowsocks/go-shadowsocks2 added at 87b55c90ac
Reference in New Issue
Block a user